Ruby扩展钩子与模板方法

时间:2015-04-10 12:30:56

标签: ruby

我想在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

希望这能澄清我的动力。

1 个答案:

答案 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):

  1. 在目标类初始化上声明TracePoint;
  2. 等到:end;
  3. 执行代码。
  4. 把这一切放在一起:

      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
    

    希望它有所帮助。