Monkeypatching For Robots
Even though Jeff Atwood believes that monkeypatching will lead to the apocalypse, I’ve discovered three cases where it has proven to be most useful.
Case 1: Dynamically Patching a Plug-in
Let’s face it. The Ruby on Rails wiki is littered with a cadre of unmaintained plugins. One example is file_column. Attachment_fu pretty much replaces this, but for whatever reason, the project that I’m working on is using the old warhorse of file upload packages. Recently, I’ve noticed several problems with file_column. The biggest issue is that it was written pre-Leopard So, file_column doesn’t support the new way of calling OS X’s file command. Combining monkeypatching with the Rails alias_method_chain allows us to patch the underlying code with minimal effort. And, keeping the dynamic patch separate, means that updating the plugin will not overwrite the patch.
# Rails lib/project_system.rb
module FileColumn
def get_content_type_with_leopard_rules(fallback=nil)
if not RUBY_PLATFORM.eql?('universal-darwin9.0')
return get_content_type_for_leopard_without_apple_rules
end
if options[:file_exec]
begin
content_type = `#{@options[:file_exec]} -bI "#{@local_file_path}"`.chomp
content_type = fallback unless $?.success?
content_type.gsub!(/;.+$/,"") if content_type
content_type
rescue
fallback
end
else
fallback
end
end
alias_method_chain :get_content_type, :leopard_rules
end
Case 2: Adding a Feature to a Plug-in
I’m a total Unix snob. Upper-case filenames injure what’s left of my dwindling sanity. File_column recklessly allows the user’s filenames to be saved directly to the filesystem. This has the side-effect of dumping a flagon full of mime-type detection FAIL when you view the files that have been uploaded to the server. Think of users uploading images named me.jpeg, me.JPG and me.Jpeg. Inconsistent capitalization is clearly evil and has to be sanitized before it gets to our precious disks. Using a monkeypatch with alias_method_chain, you can override file_column’s sanitize_filename method to convert the filename to lowercase.
# Rails lib/project_system.rb
module FileColumn
class << self
def sanitize_filename_with_downcase_rules(filename)
self.sanitize_filename_without_downcase_rules(filename).downcase.gsub(/\.jpeg$/,'jpg')
end
alias_method_chain :sanitize_filename, :downcase_rules
end
end
Case 3: Overriding a Native Ruby Class to Facilitate Symbol#to_proc Abuses
Ever since reading Reg Braithwaite’s (1..100).inject(&:+) post, I’ve been mystified and rather infatuated with Rails’ Symbol#to_proc method (which, incidentally, was only possible through Rails’ monkeypatch to the Ruby Symbol class).
Recently, I wrote a method to detect whether any one checkbox or radio button had been selected on a HTML form. This would serve as a form validation, but was complicated due to the fact that radio button values are arbitrary strings and checkbox values are posted as “0″ and “1″. The radio button values had to be converted to boolean by the controller, but ActiveRecord’s value_to_boolean method does not support this. Monkeypatching came to the rescue yet again and allowed me to override Object in an inject-friendly way.
# Rails lib/project_system.rb
class Object
def selected?
return true if self and self.is_a?(String) and self.include?('location')
ActiveRecord::ConnectionAdapters::Column.value_to_boolean(self)
end
end
Rather than writing a massive case statement or nested if/elsifs, we can just write the following in a model.
# Rails app/models/model_name.rb
def selected_place?
places.collect(&:selected?).inject(&:|)
# Logically, the same as places.collect(&:selected?).any?
end
The preceding loop calls our new Object selected? method on each of the location_types column, and the inject ORs all the data together.

I don’t really understand why Jeff is writing these slime pieces on monkeypatching. Jeff’s logic is equivalent to reasoning that driving should be outlawed because cars are dangerous. However, just because a few people drive drunk doesn’t mean that no one should drive! Oddly, I would get behind a law to make drunken monkeypatching a felony.
Trackbacks
Use this link to trackback from your own site.







I’ve always wanted to do this and now I can: I have no idea what you’re talking about, sir! (-:
Is this that cool Rooby language people keep telling me about?
That last Object monkey patch?
I hate it. Intensely. When I see a monkey patch like that, it makes me want to murder people. Usually the author. Keep that sort of code to yourself please.
The inject(&:|) part is nice though.
@Bob: is there another superclass of NilClass, TrueClass, FalseClass, and String other than Object? I guess I could have created one.
@Wilmer: that means a lot to me.