Eu tenho algum código baseado nesta resposta do SO: https://stackoverflow.com/a/2136117/2158544 . Essencialmente, é assim (nota: no código real, eu não controlo o módulo A) :
module A
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def singleton_test
end
end
end
module B
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def self.extended(base)
puts base.method(:singleton_test).owner
define_method(:singleton_test) do |*args, &blk|
super(*args, &blk)
end
end
end
end
Primeira inclusão:
class C
include A
include B
end
A::ClassMethods # <- output
Segunda inclusão:
class D
include A
include B
end
B::ClassMethods # <- output
Embora a chamada para super
ainda seja roteada corretamente para module A
, estou confuso por que singleton_test
já está "embrulhado" quando é incluído em class D
(owner is B::ClassMethods
). Minha teoria é que quando module B
redefines singleton_test
, ele está redefinindo-o no nível do módulo incluído ( module A
) e, portanto, toda vez que module A
é incluído posteriormente, o método já foi "embrulhado".
Quando você chama um método, Ruby sobe na cadeia ancestral procurando pelo método, se chegar ao topo (BasicObject) e não conseguir encontrar um método correspondente, gerará um erro (a menos que seja definido
method_missing
)Como isso funciona no seu exemplo
Primeira Inclusão
Quando você pesquisa,
singleton_test
ele verifica a cadeia ancestralsingleton_test
Agora no
B::ClassMethods::extended
gancho você está definindosingleton_test
usingdefine_method
. Como você chamou esse método sem um receptor, o receptor implícito éself
eself
no contexto desse método é,B::ClassMethods
em essência, você está chamandoVocê pode ver isso mais claramente como
Saída:
Segunda Inclusão
Acho que você pode ver onde isso está indo
Quando você pesquisa,
singleton_test
ele verifica a cadeia ancestralsingleton_test
Portanto, não é que o método "já esteja empacotado" ou que o método esteja sendo redefinido nele,
A
é que você definiu um novo métodoB::ClassMethods
e uma vez queB
é incluído apósA
sua definição ter prioridade (substitui a deA
neste contexto).