Ruby 2.7+。
我在混合的几个模块中有方法,并使用super
调用。每个模块方法依次调用super
,因此在混合模块中调用该名称的所有方法,尽管可能不是以确定的顺序调用。
我的问题是:一个方法可以告诉编程(而不是硬编码)从什么模块它被混合在?
module A
def initialize(*args, **kwargs)
puts("something magic")
end
end
module B
def initialize(*args, **kwargs)
puts("something magic")
end
end
class C
include A
include B
def initialize(*args, **kwargs)
puts("actual #{class.name} initialize")
super
end
end
运行时,将打印三行。我正在寻找的是像class.name
这样的,特定于每个模块,它标识提供正在运行的initialize
方法的模块。"something magic*
字符串将被替换为这个实际的魔法。🙂
谢谢!
我的第一个尝试是调用super_method
来获取堆栈中的所有初始化式:
module A
def initialize
A # return for comparison
end
end
module B
def initialize
B
end
end
class C
include A
include B
def initialize
C
end
def super_initializers
init = method(:initialize)
while init
print init.call, " == " # get hardcoded module from `initialize`
p init.owner # get the module dynamically
init = init.super_method # keep getting the super method
end
end
end
>> C.new.super_initializers
C == C
B == B
A == A
== BasicObject
第二个想法是使用Module.nesting
,我认为这是你正在寻找的:
module A
def initialize
# i, o, m = method(:initialize), [], Module.nesting[0]
# while i; o << i.owner; i = i.super_method; end
# print "prev "; p o[o.index(m)-1] # previous super
puts "A == #{Module.nesting[0]}"
# print "next "; p o[o.index(m)+1] # next super
super
end
end
module B
def initialize
puts "B == #{Module.nesting[0]}"
super
end
end
# add a class
class Y
def initialize
puts "Y == #{Module.nesting[0]}"
super
end
end
# add some nesting
module X
class Z < Y
def initialize
puts "Z == #{Module.nesting[0]}"
super
end
end
end
class C < X::Z
include A
include B
def initialize
puts "C == #{Module.nesting[0]}"
super
end
end
>> C.new
C == C
B == B
A == A
Z == X::Z
Y == Y
实际上,从来没有想过这可能是有用的,但它工作:
def super_trace m
while m;
p m.owner; m = m.super_method
end
end
>> super_trace User.new.method(:save)
ActiveRecord::Suppressor
ActiveRecord::Transactions
ActiveRecord::Validations
ActiveRecord::Persistence
https://rubyapi.org/3.1/o/module method-c-nesting
https://rubyapi.org/3.1/o/method method-i-super_method