Bug #4307

include fails after undef_method

Added by Adriano Mitre almost 6 years ago. Updated almost 5 years ago.

Target version:
ruby -v:
ruby 1.9.2p136 (2010-12-25 revision 30365) [x86_64-linux]


After using #undef_method on a method "inherited" from a mixin, reincluding the mixin should redefine it, but that is not what happens.

Please take a look at this short IRB transcript (source attached):

module Foo; def foo; 42; end; end
=> nil
Array.class_eval { include Foo }
=> Array
=> 42
Array.class_eval { Foo.instance_methods.each {|m| self.class_eval { undef_method m } } }
=> [:foo]
NoMethodError: undefined method foo' for [1]:Array
from (irb):5
from /home/adriano/.rvm/rubies/ruby-1.9.2-p136/bin/irb:16:in
Array.class_eval { include Foo }
=> Array
NoMethodError: undefined method foo' for [1]:Array
from (irb):7
from /home/adriano/.rvm/rubies/ruby-1.9.2-p136/bin/irb:16:in

The last command should just return 42, instead of raising a NoMethodError exception.

Note that this also applies to the following Ruby implementations:
* ruby 1.8.7 (2010-12-23 patchlevel 330) [x86_64-linux]
* rbx-1.2.0-20101221 [ ]
* jruby-1.5.6 [ amd64-java

Thus, it may be a language specification gap, not just an implementation bug.

issue.rb View (214 Bytes) Adriano Mitre, 01/24/2011 04:14 AM


#1 Updated by Loren Segal almost 6 years ago


On 1/23/2011 2:14 PM, Adriano Mitre wrote:

After using #undef_method on a method "inherited" from a mixin, reincluding the mixin should redefine it, but that is not what happens.

This seems like expected behaviour to me.

Calling #undef_method inside a class marks that method as uncallable in
the class itself. Module inclusion only affects method lookup through
the inheritance tree, it doesn't actually copy methods onto the class.
Therefore, your Array class is saying: "I will no longer accept method
calls to 'foo'". Reincluding (or including another module) does not
change the fact that Array is still blocking any calls to 'foo', it only
changes the inheritance tree. This is actually well documented:

The only way to remove this "undef" is to define the method directly on
the class, not through an included module. You can also look at
remove_method (documented in above link) which will continue to look
for a method in the inheritance tree even if removed directly on the
class. Note that this would require the original method to be defined on
the class itself, not through inclusion (which is not how your example

The other issue with your example is that re-including an already
included module is effectively a noop:

  module Foo
    def foo; 42 end

  class MyClass
    include Foo; p ancestors
    include Foo; p ancestors

  # => [MyClass, Foo, Object, Kernel]
  # => [MyClass, Foo, Object, Kernel]


#2 Updated by Adriano Mitre almost 6 years ago

So it was a misunderstanding of #undef_method that led me to not expect the
actual behaviour. Thanks for the clarification.

I read the documentation and understood the difference between undef_method and
remove_method, but I was unable to see how the latter could be any more useful
than the former in the case in point.

How then I could achieve the desired effects, i.e., include, exclude and then
re-include a mixin? If possible, I would like to dynamically change the
ancestors tree. (The only alternative I can think of would involve extracting
method objects and create methods to dynamically bind them, which seems a bit
awkward even considering this is a fairly atypical scenario.)

#3 Updated by Yui NARUSE over 5 years ago

  • Status changed from Open to Assigned
  • Assignee set to Yukihiro Matsumoto

#4 [ruby-core:44260] Updated by Yukihiro Matsumoto almost 5 years ago

  • Status changed from Assigned to Rejected

lsegal's comment is right. undef_methods defies the definition of upper classes, include mixed modules.
So, this is expected behavior.


Also available in: Atom PDF