Rails refers to a method of overriding another worry method that doesn't work like regular modules

Say I have the following structure in ruby ​​(no rails)

module Parent def f puts "in parent" end end module Child def f super puts "in child" end end class A include Parent include Child end A.new.f # prints => #in parent #in child 

Now that the use of rails refers to

 module Parent extend ActiveSupport::Concern included do def f puts "In Parent" end end end module Child extend ActiveSupport::Concern included do def f super puts "In Child" end end end class A < ActiveRecord::Base include Parent include Child end A.new.f #exception NoMethodError: super: no superclass method `f' for #<A:0x00000002244490> 

So what am I missing here? I need to use super in issues like regular modules. I searched, but I could not find help on this topic

+6
source share
1 answer

The reason for this is that the included method block is actually evaluated in the context of the class. This means that this method defined in it is defined in the class when the module is on, and as such has an advantage over the included modules.

 module Child1 extend ActiveSupport::Concern included do def foo end end end module Child2 def bar end end class A include Child1 include Child2 end A.new.method(:foo).owner #=> A A.new.method(:bar).owner #=> Child2 

Method Search

In ruby, every time you want to call a method, Ruby must first find it (not knowing if it is a method or a variable). This is done using the so-called method search. If the receiver is not specified (a clean call of type puts ), it first looks for the current region for any variables. When it is not found, it searches for this method in the current self . When a receiver is specified ( foo.bar ), it naturally searches for a method for this receiver.

Now search - in ruby ​​all methods always belong to some module / class. The first to order is the eigenclass recipient, if one exists. If not, the usual receiver class will be the first.

If the method is not found in the class, it then searches for all included modules in the given class in reverse order. If nothing is found there, the next class of the next class is the next. The whole process goes recursively until something is found. When the lookup reaches BasicObject and does not find the method that it issues, and starts searching for the method_missing method, with the default implementation defined in BasicObject.

It is important to note that methods belonging to a class always take precedence over modular methods:

 module M def foo :m_foo end end class MyClass def foo :class_foo end include M end MyClass.new.foo #=> :class_foo 

About super

The search for a super method is very similar - it just tries to find a method with the same name as in the method search:

 module M1 def foo "M1-" + super end end module M2 def foo 'M2-' + super end end module M3 def foo 'M3-' + super end end class Object def foo 'Object' end end class A include M2 include M3 end class B < A def foo 'B-' + super end include M1 end B.new.foo #=> 'B-M1-M3-M2-Object' 

AcitveSupport::Concern#included

included is a very simple method that takes a block and creates a self.included method for the current module. A block is executed using instance_eval , which means that any code in it is actually executed in the context of the class in which this module is included. Therefore, when you define a method in it, this method will belong to the class including the module, and not the module itself.

Each module can contain only one method with a given name, as soon as you try to determine the second with the same name, the previous definition is completely erased, and it cannot be found using the search method ruby. Since in your example you included two modules with the same method definition in the included block, the second definition completely overrides the first, and there is no other definition above in the method search, so the super will fail.

+9
source

All Articles