Search code examples
rubyruby-on-rails-3monkeypatching

How to monkey patch a class using a module?


I have a couple of files. In one I'd like to extend some Ruby classes, e.g.

module Stylable
  class Array
    def styled
      "\n" << "*"*72 << self.to_s << "*"*72 << "\n"
    end
  end
end

In the other file I define a class and mix in my custom module.

require './stylable'

class Printer
  include Stylable

  def initialize(object)
    @object = object
  end

  def print
    puts @object.styled
  end
end

Printer.new([1,2,3]).print

For some reason, I can't exercise my custom Array#styled method:

$ ruby printer.rb 
printer.rb:10:in `print': undefined method `styled' for [1, 2, 3]:Array (NoMethodError)
  from array_printer.rb:14:in `<main>'

What am I doing wrong?

Edit: While a solution from @MarekLipka below works in Ruby 2+. I have to do this on Ruby 1.9.3 / Rails 3. Is there a way to do this, or I have to use a global class extension without a module wrapper?


Solution

  • You're looking for a feature like refinements:

    stylable.rb:

    module Stylable
      refine Array do
        def styled
          "\n" << "*"*72 << self.to_s << "*"*72 << "\n"
        end
      end
    end
    

    printer.rb:

    require './stylable'
    class Printer
      using Stylable
      # ...
    end
    Printer.new([1,2,3]).print
    

    The advantage of using refinements is that this monkey-patch works only in scope of Printer class, so it's less likely to break something.

    The reason your original approach doesn't work is that instead of monkey-patching ::Array class, you implemented a new Stylable::Array class (note the namespace).

    If you're running Ruby < 2.0 and you don't want to monkey-patch Array globally, you could create custom class that inherits from array:

    class StylableArray < Array
      def styled
        "\n" << "*"*72 << self.to_s << "*"*72 << "\n"
      end
    end
    

    and use it in your Printer file:

    Printer.new(StylableArray.new([1,2,3]))
    

    The other way is monkey-patching Array globaly, which isn't recommended.