This post originated from an RSS feed registered with Ruby Buzz
by Chad Fowler.
Original Post: The Virtues of Monkey Patching
Feed Title: ChadFowler.com
Feed URL: http://feeds2.feedburner.com/Chadfowlercom
Feed Description: Best practices, worst practices, and some generally obvious stuff about programming.
From a semi-recent post
by Python programmer Ian Bicking:
You can monkeypatch code in Python pretty easily, but we look down
on it enough that we call it "monkeypatching". In Ruby they call it
"opening a class" and think it's a cool feature.
I will assert: we are right, they are wrong.
Today, I was frustrated by a Rails bug affecting pagination with joins. In
short, it doesn’t work. At all.
Miraculously, (no, it wasn’t a coincidence—it was an actual
miracle) Kevin Clark posted a message to
the Rails Core list right in the middle of this episode
complaining about the exact same problem. He said he was working on a fix.
Jeremy Hopple responded a little over an hour later saying that he had
already submitted a patch for the bug.
Visions of the nasty download/patch process started trudging through my
mind, but I was desperate for a fix so I would have dealt with the
ugliness. I hate hacking my local Rails (I’m running off of trunk
using svn:externals) because I don’t like dealing with what happens
when the change actually gets applied to trunk or dealing with merges for
other changes to the same files. It’s not rocket science to get
working but it’s also not fun.
Looking more closely at Jeremy’s email, he’s also distributing
his patch as a Rails plugin. So:
Now my code works. The bug is fixed. The plugin contains essentially
nothing but a file that "monkey patches" some of the Rails core
classes, implementing the changed version of the code.
When the change is merged into the Rails trunk, I’ll just
‘script/plugin remove’ Jeremy’s plugin.
The Rails team is already using plugins like this as a way to get new
functionality into circulation as a kind of proving ground before things
make it into Rails core. And as we see today, it’s also a simple,
painless way to get fixes distributed before they’re reviewed and
committed. Sure, you can do the more obvious thing with plugins and
distribute code that implements new functionality altogether. But this
"monkey patching" thing is seriously powerful and, as an end-usre
in this case, I can say that it makes things better for the users
of Ruby.
So Ian, with respect, no…you’re not right.
UPDATE: Ian sent me the following clarification by email (reprinted with
permission—thanks Ian!):
Hi Chad. Saw your post on monkeypatching. Just thought I'd clarify
that I don't think that kind of use is wrong, and I do that frequently
-- applying a patch at runtime to the objects in memory, instead of the
source code on disk. It can be expedient, and certainly a whole lot
better than trading patches around. The use I object to that I see in
lots of Ruby examples (and maybe isn't indicative of most real Ruby
code) is when people add methods to other classes that aren't meant to
fix anything, but just because they don't have an object of their own to
hang the method off of. I've seen several examples where people add
methods to Array to implement some recursive algorithm, instead of using
a function. Or Rails adding methods to numbers to create time delta
objects (well, it would be good if they *were* time delta objects, but I
understand they actually just return seconds as ints, but that's another
issue entirely).
The term "monkeypatch" is really meant to denote that. It's a patch, a
fix, an adjustment to the code. The monkey part has a history (that
someone left on my post) that I think more-or-less is meant to indicate
a certain subversive intent, or at least playfully but maybe mischievous
manipulation. We do it often in Python, but everytime we do it we know
we are working around the system and not with it. Which is fine too,
but it's not the sort of thing you want as a foundation.