Override module method from another module - ruby

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

How to work through name collisions in ruby

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

accessing method in module of module ruby

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

Ruby module prepend vs derivation

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.

Using a module inside another module with the same name

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"

calling method in nested module included in class

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

Resources