我想在template method挂钩中拨打extended。我的扩展代码看起来像这样
module M
def self.extended(mod)
mod.template_method
end
def template_method
raise 'Please implement me in the concrete class'
end
end
class C1
def self.template_method
puts 'OK, implemented in C1'
end
extend M
end
class C2
extend M
def self.template_method
puts 'OK, implemented in C2'
end
end
运行结果在codepad处可用。 C1中的代码通过了,但C2中的代码触发了异常。 C1和C2的唯一区别在于调用“扩展”方法。似乎“extened”挂钩无法识别“extend”调用后定义的代码。我想知道是否有办法解决这个问题。
来自c#,我习惯将所有"using" detectives放在课程开头。 ruby是否扩展并包含遵循相同的约定?
修改 为了反映@ mudasobwa的评论,我删除了“抽象方法”。代码可以在http://codepad.org/3qvzan0m找到 但是存在同样的问题,我确实认为抽象方法让我有机会提供更有启发性的信息而不是“找不到方法”
编辑2: 我想要实现的是基于包含C类的属性动态创建测试用例 代码段看起来像
def self.extended(mod)
methods = mod.expected_methods
methods.each do |name|
define_method("test_must_implement_#{name}") do
assert_respond_to(@object, name)
end
end
端
希望这能澄清我的动力。
答案 0 :(得分:1)
TL; DR :订单很重要。也就是说,该模块正在被提及的地方进行评估:
module M
def self.extended(mod)
puts 'I AM BEING EXTENDED'
end
end
class C1
puts 'I AM C1'
extend M
end
class C2
extend M
puts 'I AM C2'
end
#⇒ I AM C1
#⇒ I AM BEING EXTENDED
#⇒ I AM BEING EXTENDED
#⇒ I AM C2
这是故意的,因为你可以依赖后面代码中已包含/扩展的东西:
module M
def self.extended(mod)
mod.foo = 'bar'
end·
end
class C2
def self.foo= value
@@foo = value
end
extend M
puts "Foo is: #{@@foo}"
end
#⇒ Foo is: bar
UPD 回复更新:
通常,在目标类未完全加载时执行extended
回调。有一个关于如何等到它被初始化并执行完整类的所有内容的技巧(当然除了可能的未来monkeypatches):
TracePoint
; :end
; 把这一切放在一起:
def self.extended mod
self.postpone_init mod
end
def self.postpone_init mod
TracePoint.new(:end) do |tp|
if tp.self == mod
self.do_stuff
else
warn "TracePoint failed: #{tp}."
end
tp.disable
end.enable
end
def self.do_stuff
# HERE YOU GO
end
希望它有所帮助。