Il modo migliore per capire i mixin è come classi virtuali. I mixin sono "classi virtuali" che sono state iniettate nella catena di antenati di una classe o di un modulo.
Quando usiamo "include" e gli passiamo un modulo, questo aggiunge il modulo alla catena degli antenati proprio prima della classe da cui stiamo ereditando:
class Parent
end
module M
end
class Child < Parent
include M
end
Child.ancestors
=> [Child, M, Parent, Object ...
Ogni oggetto in Ruby ha anche una classe singleton. I metodi aggiunti a questa classe singleton possono essere richiamati direttamente sull'oggetto e quindi fungono da metodi "class". Quando usiamo "estende" su un oggetto e passiamo l'oggetto a un modulo, stiamo aggiungendo i metodi del modulo alla classe singleton dell'oggetto:
module M
def m
puts 'm'
end
end
class Test
end
Test.extend M
Test.m
Possiamo accedere alla classe singleton con il metodo singleton_class:
Test.singleton_class.ancestors
=> [#<Class:Test>, M, #<Class:Object>, ...
Ruby fornisce alcuni hook per i moduli quando vengono miscelati in classi / moduli. included
è un metodo hook fornito da Ruby che viene chiamato ogni volta che includi un modulo in qualche modulo o classe. Proprio come incluso, c'è un extended
hook associato per extender. Verrà chiamato quando un modulo viene esteso da un altro modulo o classe.
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
Questo crea un modello interessante che gli sviluppatori potrebbero usare:
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
Come puoi vedere, questo singolo modulo sta aggiungendo metodi di istanza, metodi "class" e agendo direttamente sulla classe target (chiamando a_class_method () in questo caso).
ActiveSupport :: Preoccupazione incapsula questo modello. Ecco lo stesso modulo riscritto per usare ActiveSupport :: Preoccupazione:
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