HOWTO: Monkey Patch Without Annoying Others
Written July 8th, 2006
It’s really very, very simple. Subclass first, then monkey patch the subclass and use that instead.
Update: Here’s an example, from one of the most over-monkey-patched classes ever:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
require 'logger' require File.dirname(__FILE__) + '/core_ext/class/attribute_accessors' class CleanLogger < Logger #:nodoc: cattr_accessor :silencer self.silencer = true # Silences the logger for the duration of the block. def silence(temporary_level = Logger::ERROR) if silencer begin old_logger_level, self.level = level, temporary_level yield self ensure self.level = old_logger_level end else yield self end end private alias old_format_message format_message # Ruby 1.8.3 transposed the msg and progname arguments to format_message. # We can't test RUBY_VERSION because some distributions don't keep Ruby # and its standard library in sync, leading to installations of Ruby 1.8.2 # with Logger from 1.8.3 and vice versa. if method_defined?(:formatter=) def format_message(severity, timestamp, progname, msg) "#{msg}\n" end else def format_message(severity, timestamp, msg, progname) "#{msg}\n" end end end |
Notice the difference? See, that wasn’t so bad now, was it?
Update: I’m certainly not the first to complain about this.
As a n00b I can say that example code would be useful.
I don’t get it. “patching” a subclass has no effect on the superclass. I agree that subclassing is best if you just want to extend a class’s behavior for your own use (how is that “monkey patching”?). But when you want to modify something a class does globally, you need to patch the class itself.
Bob,
Sweet thanks for the example.
Josh,
From what I understood from the title, this is about patching functionality that might break other code if you had it take effect globally. I actually have this situation and have been overriding inside my controllers which I dislike.
josh: What teh n00b said. There are very, very, very few cases where modifying a function’s behavior globally is the correct way of going about things. Which, I imagine, is largely why the Python folks tend to frown on the practice. The only reason I can think of why you would do that is if the function is, in fact, producing incorrect results. And if you apply a monkey patch in that case, people aren’t likely to be annoyed by their broken code suddenly working. However, if you monkey patch when you should have subclassed, like the Rails folks did here, that can get annoying really quick. I’ve personally run afoul of their Logger monkey patch several times now since I have a couple of libraries and apps that use ActiveRecord, but not the rest of Rails. As soon as ActiveRecord is loaded, the logger stops working as expected. I had to subclass and remonkey patch it back to the way it was in one instance.
Additional clarification:
Monkey patching to add new functionality is fine. Changing existing behavior, generally, is not. If you’re going to change behavior, do it in a subclass if at all possible.
Leave a Response