[LRUG] Can't hide instance method if defined in native extension?

Sean O'Halpin sean.ohalpin at gmail.com
Mon Nov 14 06:23:13 PST 2011


On Sun, Nov 13, 2011 at 10:25 PM, Peter Vrabel <kybu at kybu.org> wrote:
> Hi guys,
>
> I recently came across a strange behavior. I would like to measure time
> spent executing Mysql statement, so I tried to hide 'execute' instance
> method of Mysql::Stmt and my first idea was to use my custom module that I
> include into Mysql::Stmt class. But then I found that I can't do that,
> original 'execute' method is always called. Here is a small edited snippet:
>

[snip example]

Hi,

As others have pointed out, including a module inserts methods above
the current class in the inheritance hierarchy so you can't use it to
override methods defined in that class. You can however use extend on
an instance to override methods defined in the class.

I generally use one of the following two methods. The first is a
'hygenic' version of alias_method in that you can use the same
technique in multiple included modules without worrying about whether
the method has been already aliased. It's clean but it's slow (the
bind is costly). The second (which I use more often) is to use extend
with super. You can even combine both methods together.

# 'hygenic' way to override method via include

class Foo
  def foo(*args, &block)
    p [Foo, :foo, args, block]
  end
end

Foo.new.foo(1, 2, 3)

module Override
  def self.included(other)
    original_foo = other.instance_method(:foo)
    other.class_eval {
      define_method :foo do |*args, &block|
        p Override
        args = args.reverse
        original_foo.bind(self).call(*args, &block)
      end
    }
  end
end

class Foo
  include Override
end

Foo.new.foo(1, 2, 3)

# using extend

class Bar
  def foo(*args, &block)
    p [Bar, :foo, args, block]
  end
end

module Extension
  def foo(*args, &block)
    p Extension
    args = args.map{ |x| x.to_s}
    super
  end
end

b = Bar.new
b.extend(Extension)
b.foo(1, 2, 3)

# using both together

f = Foo.new
f.extend(Extension)
f.foo(1, 2, 3)

Running this gives you the following output:

$ ruby override-method.rb
[Foo, :foo, [1, 2, 3], nil]
Override
[Foo, :foo, [3, 2, 1], nil]
Extension
[Bar, :foo, ["1", "2", "3"], nil]
Extension
Override
[Foo, :foo, ["3", "2", "1"], nil]


HTH,
Sean



More information about the Chat mailing list