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.

  1. One Response to “MonkeyPatching Class Methods in Ruby/Rails”

  2. 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

Post a Comment