I want to override a method from a module A from another module B that will monkey-patch A.
http://codepad.org/LPMCuszt
module A
def foo; puts 'A' end
end
module B
def foo; puts 'B'; super; end
end
A.module_eval { include B } # why no override ???
class C
include A
end
# must print 'A B', but only prints 'A' :(
C.new.foo
module A
def foo
puts 'A'
end
end
module B
def foo
puts 'B'
super
end
end
include A # you need to include module A before you can override method
A.module_eval { include B }
class C
include A
end
C.new.foo # => B A
Including a module places it above the module/class that is including it in the class hierarchy. In other words, A#foo is not super of B#foo but rather the other way round.
If you think of including a module as a way of doing multiple inheritance this makes sense, include SomeModule is a way of saying, "Treat SomeModule like it is a parent class for me".
To get the output you wanted you need to reverse the inclusion so that B includes A:
module A
def foo; puts 'A' end
end
module B
def foo; puts 'B'; super; end
end
B.module_eval { include A } # Reversing the inclusion
class C
include B # not include A
end
puts C.new.foo
Edit in response to comment:
Then either include both A and B in C with B included after A:
# A and B as before without including B in A.
class C
include A
include B
end
or patch A in C itself and don't bother with B.
# A as before, no B.
class C
include A
def foo; puts 'B'; super; end
end
The only way for this to work is if the method lookup on C is C -> B -> A and there is no way to do this without including B into C.
This is also one solution to your question. I am trying to achieve with module hooks included. When you include the module A into class C. included callbacks defined in module A is called and executed. We included the module B on-fly. So our module A method foo is overridden by Module B foo to print the superclass module method just called super.
module A
def self.included klass
klass.send(:include, B)
end
def foo
puts 'A'
end
end
module B
def foo
super
puts 'B'
end
end
class C
include A
end
C.new.foo #out put A,B
Another way to accomplish this is to include module B when module A is included.
module A
def foo
puts "this should never be called!"
"a"
end
end
module B
def foo
"b"
end
end
module A
def self.included(base)
base.class_eval do
include B
end
end
end
class C
include A
end
C.new.foo # "b"
Related
Two modules Foo and Baa respectively define a method with the same name name, and I did include Foo and include Baa in a particular context.
When I call name, how can I disambiguate whether to call the name method of Foo or Baa?
Only the order of modules inclusion decides which one will get called. Can't have both with the same name - the latter will override the former.
Of course, you can do any tricks, just from the top of my head:
module A
def foo
:foo_from_A
end
end
module B
def foo
:foo_from_B
end
end
class C
def initialize(from)
#from = from
end
def foo
from.instance_method(__method__).bind(self).call
end
private
attr_reader :from
end
C.new(A).foo #=> :a_from_A
C.new(B).foo #=> :a_from_B
But that's no good for real life use cases :)
Technically, there is no name collision because the method foo is redefined.
In the following exemple, A.foo is redefined and is never called
module A
def foo
raise "I'm never called"
end
end
module B
def foo
puts :foo_from_B
end
end
class C
include A
include B
end
C.new.foo
# =>
# foo_from_B
If you write A and B module, you can use super to call previous definition of foo. As if it where an inherited method.
module A
def foo
puts :foo_from_A
end
end
module B
def foo
super
puts :foo_from_B
end
end
class C
include A
include B
end
C.new.foo
# =>
# foo_from_A
# foo_from_B
There are side effects and I would not use this but this is doing the trick :
module A
def foo
puts :foo_from_A
end
end
module B
def foo
puts :foo_from_B
end
end
class C
def self.include_with_suffix(m, suffix)
m.instance_methods.each do |method_name|
define_method("#{method_name}#{suffix}", m.instance_method(method_name))
end
end
include_with_suffix A, "_from_A"
include_with_suffix B, "_from_B"
end
c= C.new
c.foo_from_A
c.foo_from_B
begin
c.foo
rescue NoMethodError
puts "foo is not defined"
end
# =>
# foo_from_A
# foo_from_B
# foo is not defined
Provided none of the methods of Foo or Baa call name (which seems a reasonable assumption), one can simply create aliases.
module Foo
def name; "Foo#name"; end
end
module Baa
def name; "Baa#name"; end
end
class C
include Foo
alias :foo_name :name
include Baa
alias :baa_name :name
undef_method :name
end
c = C.new
c.foo_name
#=> "Foo#name"
c.baa_name
#=> "Baa#name"
C.instance_methods & [:foo_name, :baa_name, :name]
#=> [:foo_name, :baa_name]
The keyword alias is documented here. One may alternatively use the method #alias_method. See this blog for a comparison of the two.
Module#undef_method is not strictly necessary. It's just to ensure that an exception is raised if name is called.
You should definetely read about method lookups.
Anyway, I would do it this way:
module Foo
def name
:foo
end
end
module Bar
def name
:bar
end
end
class MyClass
include Foo
include Bar
def foo_name
Foo.instance_method(:name).bind(self).call
end
def bar_name
Bar.instance_method(:name).bind(self).call
end
#
# or even like this: obj.name(Foo)
#
def name(mod)
mod.instance_method(:name).bind(self).call
end
end
BTW if you are using Module#instance_method and UnboundMethod#bind you don't really need to include specific module. This code works:
Foo.instance_method(:name).bind('any object (e.g. string)').call
I'm trying to modify existing ruby code, and ruby is not my first languange. Part of the code is like below:
#someFile1.rb
module A
module B
def somefunction()
end
end
end
class X::Y
include A::B
end
#someFile2.rb
module A
module C
def anotherfunction()
#somefunction() <-- error
end
end
end
class X::Y
include A::C
end
Somehow I can't access method somefunction() in anotherfunction.
How to access method defined in module B in method inside module C? Why it's not working?
Instance methods in modules aren't generally accessible until you mix them into a class and create an object of that class.
module A
module B
def some_method
"foo"
end
end
end
module A
module C
def another_method
some_method
end
end
end
class X
include A::B
include A::C
end
X.new.another_method
# => "foo"
But I'd say it isn't very elegant to have a module that depends upon the fact that some other module has also been mixed into the same object
Class methods in modules, on the other hand, are accessible like this:
module A
module B
def self.somefunction
"foo"
end
end
end
module A
module C
def self.another_function
A::B.somefunction
end
end
end
A::C.another_function
# => "foo"
Assuming you want to call the module functions themselves, you would first need to make them module functions (think static in Java or namespace in C++). Then you can use the :: (namespace resolution) operator. See foo and bar.
If you want to import them into classes, just import both, and both will be visible. See baz and qux.
module A
module B
def self.foo
puts "foo"
end
def baz
puts "baz"
end
end
end
module A
module C
def self.bar
puts "bar"
A::B::foo
end
def qux
puts "qux"
baz
end
end
end
class X
include A::B
include A::C
end
A::C::bar
x = X.new
x.qux
Output:
bar
foo
baz
qux
What are the differences between:
module Mod
def here
puts 'child'
end
end
class A
prepend Mod
def here
puts 'parent'
end
end
and
class A
def here
puts 'parent'
end
end
class B < A
def here
puts 'child'
end
end
Or another way to put it: is derivating a class the same as prepending a module of the child's code?
No, it is not. B can only inherit from one class, but Mod can be prepended to many classes. If you were to call super inside B#here, it would always refer to A#here, but inside of Mod#here, it will refer to the #here instance method of whatever class Mod was prepended to:
module Mod
def here
super + ' Mod'
end
end
class A
prepend Mod
def here
'A'
end
end
class B
prepend Mod
def here
'B'
end
end
A.new.here
# => 'A Mod'
B.new.here
# => 'B Mod'
and
class A
def here
'A'
end
end
class B
def here
'B'
end
end
class C < A
def here
super + ' C'
end
end
C.new.here
# => 'A C'
class C < B
def here
super + ' C'
end
end
# TypeError: superclass mismatch for class C
No, it's totally different.
One can prepend as many modules as he wants.
module A
def foo; "A" end
end
module B
def foo; "B" end
end
class C
prepend A, B # Prepending is done by this line
def foo; "C" end
end
### take a look at it!
C.ancestors # => [A, B, C, Object, Kernel, BasicObject]
C.new.foo # => "A"
Ruby implements prepend and inheritance using different ways.
prepend is internally achieved by including modules, which causes the surprising ancestors.
here is another question about prepend which may be helpful.
I have a module like this
module A
module ClassMethods
def a
"a"
end
def b
a
end
end
def self.included(klass)
klass.extend ClassMethods
end
end
I want to factor b into it's own module because Bs are clearly not As. The b method depends on A, and I would like the methods defined in A to be available to B.
Doing this:
module B
module ClassMethods
extend A
def b
a
end
end
def self.included(klass)
klass.extend(ClassMethods)
end
end
It does not work. Nor does include. I tried moving include or extend as well as the how constants are references. What am I missing here?
I hope I understood what you wanted. I split up module A and module B. When I include those in a new class Klass, I have access to both method a and method b, whereas method b from module B depends on method a from module A:
module A
module ClassMethods
def a
"a"
end
end
def self.included(klass)
klass.extend(ClassMethods)
end
end
module B
module ClassMethods
# include A::ClassMethods here and you can do only 'include B' inside Klass
def b
"Called from B: %s" % a
end
end
def self.included(klass)
klass.extend(ClassMethods)
end
end
class Klass
include A, B
end
p Klass.a # => "a"
p Klass.b # => "Called from B: a"
As I understand you can define the same Module in different places, no need to say include, extend or anything. As you see neither Module A nor Module B uses those keywords, they both just define ClassMethods.
Of course, you will have to include module A wherever you use method b of module B or else the method call to a will fail. You can also do include A::ClassMethods inside B::ClassMethods, that way you only need to include B inside Klass.
Add the extend A action in the included callback of the module B instead of adding it in the ClassMethod definition, like the following code does:
module A
module ClassMethods
def a
"a"
end
end
def self.included(klass)
klass.extend ClassMethods
end
end
module B
module ClassMethods
def b
a
end
end
def self.included(klass)
klass.extend(ClassMethods)
klass.extend(A)
end
end
class C
include B
end
C.b
# => "a"
I have the following configuration:
module A
module B
def foo
puts "foo"
end
end
end
class C
include A
end
c = C.new
c.foo
NoMethodError: undefined method `foo' for #<C:0x8765284>
How do I achieve the above?
Thanks.
Module B is "defined" inside of A, it is not "included" in A. This is why you don't get access to the #foo instance method when you include the A module in C. You could do the following:
class C
include A::B
end
C.new.foo
You can use the included callback to have B include when A is included.
module A
def A.included(klass)
klass.include B
end
module B
def foo
puts "foo"
end
end
end
class C
include A
end
and the following would work
c = C.new
c.foo