Project

General

Profile

Bug #12206

undef_method on prepended module undefines same-name method on prepending class

Added by EugeneG (Eugene Gilburg) almost 4 years ago. Updated 8 months ago.

Status:
Closed
Priority:
Normal
Assignee:
-
Target version:
-
ruby -v:
ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-darwin12.0]
[ruby-core:74491]

Description

  1. Define a class with a method
  2. Define a module with a same-name method
  3. Prepend the module on the class
  4. Undefine the same-name method on the module, not touching the original method on the class
  5. Observe the class method to also become undefined
class MyClass
  def foo
    "MyClass#foo"
  end
end

module MyMod
  def foo
    "#{super} prepended by MyMod#foo"
  end
end

MyClass.prepend(MyMod)

MyClass.new.foo
# => "MyClass#foo prepended by MyMod#foo"

MyClass.instance_method(:foo) == MyMod.instance_method(:foo)
# => false

MyMod.send(:undef_method, :foo)

# The bug:

MyClass.new.foo
# => NoMethodError: undefined method `foo' for #<MyClass:0x007ffdf29614a8>

Expected behavior: MyClass#foo would still return "MyClass#foo" since I only undefined foo on the module, not on the class.

Real life use case: I'm setting up instrumentation code that can track and measure/benchmark call times of specific methods, but after it's done I want to clean up and restore original code the way it was. For me, it would be even better to support to "unprepend" a module from a class instead of undef_method on the mod, but that's more of a feature request than a bug).

Updated by znz (Kazuhiro NISHIYAMA) almost 4 years ago

How about using remove_method instead of undef_method ?

Updated by EugeneG (Eugene Gilburg) almost 4 years ago

Kazuhiro NISHIYAMA wrote:

How about using remove_method instead of undef_method ?

It worked, thanks! Behavior is still confusing since I undef'd the method on the module rather than the class, but this solves my problem.

Updated by jeremyevans0 (Jeremy Evans) 8 months ago

  • Status changed from Open to Closed

This isn't a bug. undef_method and remove_method mean two different things in Ruby:

  • remove_method: Remove the method with this name from the module/class method table
  • undef_method: Add an entry to the module/class method table, such as Ruby will consider the method not defined if it reaches that entry.

If you prepend MyMod to MyClass, that means that calling MyClass.new.foo looks up methods in the following order:
MyMod -> MyClass -> Object -> Kernel -> BasicObject. So if you undef_method in MyMod, Ruby raises a NoMethodError when it reaches the entry in MyMod's method table, before it reaches the method in MyClass's method table. remove_method works because it removes the entry in the method table inMyMod, so lookup proceeds to MyClass's method table.

Also available in: Atom PDF