私がミックスインを理解する最良の方法は、仮想クラスとしてです。ミックスインは、クラスまたはモジュールの祖先チェーンに挿入された「仮想クラス」です。
「include」を使用してモジュールを渡すと、継承元のクラスの直前の祖先チェーンにモジュールが追加されます。
class Parent
end
module M
end
class Child < Parent
include M
end
Child.ancestors
=> [Child, M, Parent, Object ...
Rubyのすべてのオブジェクトにもシングルトンクラスがあります。このシングルトンクラスに追加されたメソッドは、オブジェクトで直接呼び出すことができるため、「クラス」メソッドとして機能します。オブジェクトで "extend"を使用してオブジェクトをモジュールに渡すと、モジュールのメソッドがオブジェクトのシングルトンクラスに追加されます。
module M
def m
puts 'm'
end
end
class Test
end
Test.extend M
Test.m
シングルトンクラスにアクセスするには、singleton_classメソッドを使用します。
Test.singleton_class.ancestors
=> [#<Class:Test>, M, #<Class:Object>, ...
Rubyは、クラス/モジュールに混合されるときに、モジュールにいくつかのフックを提供します。included
Rubyが提供するフックメソッドで、モジュールをモジュールまたはクラスに含めると呼び出されます。含まれているように、extended
拡張のためのフックが関連付けられています。モジュールが別のモジュールまたはクラスによって拡張されたときに呼び出されます。
module M
def self.included(target)
puts "included into #{target}"
end
def self.extended(target)
puts "extended into #{target}"
end
end
class MyClass
include M
end
class MyClass2
extend M
end
これにより、開発者が使用できる興味深いパターンが作成されます。
module M
def self.included(target)
target.send(:include, InstanceMethods)
target.extend ClassMethods
target.class_eval do
a_class_method
end
end
module InstanceMethods
def an_instance_method
end
end
module ClassMethods
def a_class_method
puts "a_class_method called"
end
end
end
class MyClass
include M
# a_class_method called
end
ご覧のとおり、この単一のモジュールはインスタンスメソッド、「クラス」メソッドを追加し、ターゲットクラスに直接作用しています(この場合はa_class_method()を呼び出しています)。
ActiveSupport :: Concernはこのパターンをカプセル化します。ActiveSupport :: Concernを使用するように書き換えられた同じモジュールを次に示します。
module M
extend ActiveSupport::Concern
included do
a_class_method
end
def an_instance_method
end
module ClassMethods
def a_class_method
puts "a_class_method called"
end
end
end