Thomas Felinae
Thomas Felinae

Reputation: 43

Override module_function inside ruby class with access to original

I'm trying to override a Rails helper method that's defined like this:

class Foo
  module Bar

    def orig
      # orig code
    end

    alias o orig

    module_function :o
    module_function :orig

  end
end

So that I can override and add functionality to orig and o something like this:

def orig
  # new code
  # super (run orig code)
end
alias o orig

I've looked through several different monkey patching methods but they don't seem to work. I believe the module_function is what's throwing it off.

Anyone know how I can achieve this?

Upvotes: 3

Views: 934

Answers (2)

Jörg W Mittag
Jörg W Mittag

Reputation: 369556

Pretty much any circumstance where you would in the past have used monkey-patching can nowadays be solved with inheritance and Module#prepend:

module Foo
  def bar(arg)
    "self=#{self}, arg=#{arg}"
  end
  module_function :bar
end

module FooExtension
  def bar(arg)
    super(arg + 1)
  end
end

[Foo, Foo.singleton_class].each do |mod|
  mod.prepend FooExtension
end

Foo.bar(1) #=> "self=Foo, arg=2"

class MyClass
  include Foo
end

MyClass.new.bar(1) #=> "self=#<MyClass:0x00007fb66a86cbf8>, arg=2"

Upvotes: 4

max pleaner
max pleaner

Reputation: 26778

Here's a workaround. You can re-open the module, make an unbound reference to the original instance method, then redefine it to call the original method (with some altered behavior).

First, the original definition:

module Foo
  def bar(arg)
    "self=#{self}, arg=#{arg}"
  end
  module_function :bar
end

Next, reopening and redefining the method:

module Foo
  OrigBarMethod = instance_method(:bar)
  def bar(arg)
    Foo::OrigBarMethod.bind(self).call(arg + 1)
  end
  module_function :bar
end

puts Foo.bar(1) # => "self=Foo, arg=2"

I use bind(self) so that the original method can still make use of self, for example:

class MyClass
  include Foo
end

MyClass.new.send(:bar, 1) # => "self=#<MyClass:0x00007fb66a86cbf8>, arg=2"

Upvotes: 4

Related Questions