I need some like this:
module One def test; puts 'Test One'; end end module Two def test; puts 'Test Two'; end end class Foo include One include Two include One end
In this case开发者_如何学JAVA I need as a result 'Test One' but obviously it returns 'Test Two'. I need a clean simple way for re-include my module.
Any suggestion?
Thanks!
class Module
def include_again(mod)
mod.instance_methods.each { |m|
self.send(:define_method, m) { |*args|
mod.instance_method(m).bind(self).call(*args)
}
}
end
end
module One
def test(a); puts "test one #{a}"; end
end
module Two
def test; puts "test two"; end
end
class Foo
include One
include Two
end
Foo.new.test #=> "test two"
class Foo
include_again One
end
Foo.new.test(1) #=> "test one 1"
I'm not precisely sure of a workaround other than remove_method
, but the reason why what you've shown doesn't work is the way that Ruby performs method lookup.
Inspecting Foo
's ancestors
during each step of Foo
's creation gives us a big hint:
module One
def test; puts 'Test One'; end
end
module Two
def test; puts 'Test Two'; end
end
class Foo
include One
p ancestors
include Two
p ancestors
include One
p ancestors
end
Output:
[Foo, One, Object, Kernel]
[Foo, Two, One, Object, Kernel]
[Foo, Two, One, Object, Kernel]
If a module is already in a class's ancestry, Ruby doesn't allow you to re-include it again. Thus, when Two
is included after One
, Two
occurs in Foo
's lookup table before One
has a chance, no matter how many times you re-include One
.
You could adjust the behavior of include:
module One
def test; puts 'Test One'; end
end
module Two
def test; puts 'Test Two'; end
end
class Foo
@mods = []
def self.include(mod)
@mods.delet mod
@mods << mod
end
def self.new(*args)
super.tap { |o| @mods.each { |m| o.extend m } }
end
include One
include Two
include One
end
@banister, many thanks for that answer, since I can't comment I'll add here the code to make it work with blocks as well as arguments:
class Module
def include_again(mod)
mod.instance_methods.each{ |m|
self.send(:define_method, m) { |*args, &block|
mod.instance_method(m).bind(self).call(*args, &block)
}
}
end
end
精彩评论