MonkeyPatching Class Methods in Ruby/Rails
April 29, 2008 | code, rails, ruby
I ran into this problem while trying to MonkeyPatch some class methods into existing Rails classes.
Say you have the following Mooable module (as a rails plugin) that you want to dynamically inject into ActiveRecord::Base (or any ruby class for that matter).
# app/models/my_model.rb
class MyModel < ActiveRecord::Base
...
end
# plugins/mooable/lib/mooable.rb
module Mooable
def moo
return "Moo!"
end
end
# plugins/mooable/init.irb
ActiveRecord::Base.send :include, Mooable
# test
MyModel.new.moo # => "Moo!"
But what if you wanted #moo to be a class method on ActiveRecord::Base? I was tempted to do the following:
#plugins/mooable/lib/mooable.rb module Mooable def self.moo return "Moo!" end end # test MyModel.moo # => NoMethodError: undefined method `moo' for MyModel:Class
As you can see, simply defining the module method as a class method is a no-go (I’m sure this is a major nuby mistake, but it’s always worth a try!)
The best way I have found so far to monkeypatch new class methods into rails classes with a plugin is to use the Object#extend method inside of the module’s self.included hook. The new class methods are defined within a sub-module Mooable::ClassMethods for easy inclusion.
# plugins/mooable/lib/mooable.rb
module Mooable
def self.included(klass)
klass.extend ClassMethods
end
module ClassMethods
def moo
return "Moo Static/Class Method"
end
end
def moo
return "Moo Instance Method!"
end
end
# test
MyModel.moo # => "Moo Static/Class Method"
MyModel.new.moo # => "Moo Instance Method!"
This works like a charm.
One Response to “MonkeyPatching Class Methods in Ruby/Rails”
You can also do:
module Mooable
def moo
return “Moo!”
end
end
class Foo
class << self
include Mooable
end
end
Foo.moo # “Moo!”
By Adam Jacob on Apr 29, 2008