[Ruby] Let's examine the inheritance tree while watching the flow of method search

Introduction

When calling a method, even if the method is defined in its own class Defined as a singular method in a superclass, module, or an instance created from a class I think there are various cases. This time, about the flow of method search in those cases I would like to summarize it.

Own method

class Klass
  def hello
    puts "Hello_from_Klass"
  end
end

klass_obj = Klass.new
klass_obj.hello
# => Hello_from_Klass

It seems to be the simplest case. When the hello method is called on the obj object Browse to the direct class, the Klass class, and look for the hello method there. In this case, we were able to find it there, so the search ends there and the method is executed.

スクリーンショット 2020-11-14 22.35.18.png

Superclass method

class Klass
  def hello
    puts "Hello from Klass"
  end
end

class SubKlass < Klass
end

sub_klass_obj = SubKlass.new
sub_klass_obj.hello
# => Hello_from_Klass

For the search direction, first refer to the direct class, and if it is not there That parent class, or even that parent class ... Is equal to the class inheritance order. In this case, it referred to SubKlass and there was no hello method. Because the hello method was defined by referring to its parent class Klass The search ends and the method is executed.

スクリーンショット 2020-11-14 22.50.48.png

Singular method

class Klass
  def hello
    puts "Hello from Klass"
  end
end

obj = Klass.new

def obj.hello
  puts "Hello from singleton"
end

obj.hello
# => Hello from singleton

In this case, instead of the hello method defined in the direct class The hello method defined as a singular method was called. In other words, the singular class has its method searched before the object's class. I understand.

スクリーンショット 2020-11-15 13.31.57.png

Methods mixed in to the class

Define a method with the same name in the superclass and module Try calling that method from an instance of a subclass.

module HelloModule
  def hello
    puts "Hello from HelloModule"
  end
end

class Klass
  def hello
    puts "Hello from Klass"
  end
end

class SubKlass < Klass
  include HelloModule
end


sub_klass_obj = SubKlass.new
sub_klass_obj.hello
# => Hello from HelloModule

The hello method of the included module was called. The included modulue is between the included class and the parent class. It will exist. At this time, an anonymous class that seems to wrap the module is created and embedded in the inheritance tree. By the way, this anonymous class cannot be touched by a Ruby program.  Because the anonymous class is created and embedded in the inheritance tree in this way Method search can also be referenced with the same rules as when normal inheritance is done.

スクリーンショット 2020-11-15 14.03.40.png

Include multiple modules

Earlier, I included only one module, module include is different from class inheritance You can include multiple modules for one class.

Let's actually include multiple modules and call the method.

module FirstModule
  def hello
    puts "Hello from FirstModule"
  end
end

module SecondModule
  def hello
    puts "Hello from SecondModule"
  end
end

class Klass
  def hello
    puts "Hello from Klass"
  end
end

class SubKlass < Klass
  include FirstModule
  include SecondModule
end


sub_klass_obj = SubKlass.new
sub_klass_obj.hello
# => Hello from SecondModule

When multiple modules are included, The method of the module that was included last was called. When multiple modules are included in the same class in this way, Modules are embedded in the inheritance tree of the class in the order they are included.

First, FirstModule is included and becomes the following inheritance tree. スクリーンショット 2020-11-15 14.18.27.png

After that, when the SecondModule described below is included, it will be as follows. スクリーンショット 2020-11-15 14.20.11.png

After including the module, include it again

This time, after reading in the order of FirstModule``SecondModule as before Try loading FirstModule again.

 module FirstModule
  def hello
    puts "Hello from FirstModule"
  end
end

module SecondModule
  def hello
    puts "Hello from SecondModule"
  end
end

class Klass
  def hello
    puts "Hello from Klass"
  end
end

class SubKlass < Klass
  include FirstModule
  include SecondModule
  include FirstModule
end


sub_klass_obj = SubKlass.new
sub_klass_obj.hello
# => Hello from SecondModule

The last include description is FirstModule It was the method of SecondModule that was called.

Actually, when inserting a class that wraps a module in the inheritance tree, Checking for the same module in the inheritance tree. Therefore, when the second include First Module is called, Because FirstModule is already included The include method ends without being plugged in.

prepend module

module PrependModule
  def hello
    puts "Hello from PrependModule"
  end
end

module IncludeModule
  def hello
    puts "Hello from IncludeModule"
  end
end

class Klass
  prepend PrependModule
  include IncludeModule
  def hello
    puts "Hello from Klass"
  end
end

klass_obj = Klass.new
klass_obj.hello
# => Hello from PrependModule

include includes an anonymous class that wraps a module Whereas it is embedded above the included class The prepend is embedded below that class. (The method is called with priority over the prepared class) Therefore, if you call super in the module to prepare, The method with the same name in the prepend class is called.

スクリーンショット 2020-11-15 16.51.41.png

Call a method that does not exist on the object

module HelloModule
  def hello
    puts "Hello from FirstModule"
  end
end

class Klass
  def hello
    puts "Hello from Klass"
  end
end

class SubKlass < Klass
  include HelloModule
end

obj = SubKlass.new
obj.bye
# => undefined method `bye' for #<SubKlass:0x00007fb5fb80c628> (NoMethodError)

I tried calling the bye method that doesn't exist anywhere in the object I spit out NoMethodError.

When the bye method is called, it goes through the inheritance tree in order to find the method. However, this time it is not defined anywhere, so after searching to the top of the inheritance tree, BasicObject Go back to the direct class SubKlass, and this time look for the method_missing methods in the order of the inheritance tree. This time it is the top level because I have not defined the method_missing method myself The method_missing method of BasicObject is called to raise a NoMethodError as an exception.

Therefore, if there is a method_midding method in the middle of the inheritance tree up to BasicObject Call it.

module HelloModule
  def hello
    puts "Hello from FirstModule"
  end
end

class Klass
  def hello
    puts "Hello from Klass"
  end

  def method_missing(method_name, *args)
    puts "#{method_name}?There is no such method"
  end
end

class SubKlass < Klass
  include HelloModule
end

obj = SubKlass.new
obj.bye
# => bye?There is no such method

Summary

As a search flow 1, singular class of receiver 2, direct class 2', module if module is included 3, 2 parent classes 3', module if module is included 4, Repeat 3 until the method is found 5, If you do not search to the top of the inheritance tree (BasicObject), the search with that method ends. 6, Using the method name as an argument, search for the method_missing method from 1 7, If not, method_missing of BasicObject is called and an exception of NoMethodError occurs.

end

Recommended Posts

[Ruby] Let's examine the inheritance tree while watching the flow of method search
Examine the elements in the array using the [Ruby] includes? Method
[Ruby] Class nesting, inheritance, and the basics of self
definition of ruby method
[Ruby] Questions and verification about the number of method arguments
Practice of binary search method
Practice of linear search method
[Ruby] Specify the argument name of the method. Meaning of the colon (:) at the end