将类级动态方法创建移动到ruby中的模块中

时间:2012-01-31 23:36:24

标签: ruby metaprogramming

我正在开发一个应用程序,其中我有一个插件式架构,其中有多个定义API的抽象类,以及具有子类的特定实现,这些子类为抽象类上定义的属性提供方法。可以想象一个通用数据库适配器,它具有连接,表,列等抽象类,以及特定数据库的特定子类。

在尝试DRY时,我将每个抽象类的属性(使用默认值)声明为名为ATTRIBUTES的类常量数组。由于ruby不支持真正的抽象类,因此如果在抽象类上调用异常,我会动态创建引发异常的方法。例如:

module MyApplication
  class Operation
    ATTRIBUTES = { name: '', parameters: [], type: :void }
    ATTRIBUTES.keys.each do |a|
      str = "Method '%s' called on abstract 'MyApplication::Operation' class"
      define_method(a) { raise (str % a) }
      define_method("#{a}=") { |x| raise (str % "#{a}=") }
    end
  end
end

我在消息中硬编码类名,因为我不能使用当前类的名称,因为它可能是抽象Operation类的子类,我希望消息清楚,被调用的方法是在抽象类上。

我有一些以上的抽象类,并且我已经将几乎相同的代码复制到每个类中以创建这些存根方法(只有属性列表和类名称不同)。我已经尝试了很多方法将这些代码放入一个模块中,我可以从每个抽象类中包含(或扩展?),但是我的大脑现在正在试图理清如何获得这个的元编程细节。上班。 :)

有没有人能够将这种常见的,类级别(但是实例制作方法)代码放入模块中以避免重复使用相同的代码?

1 个答案:

答案 0 :(得分:2)

这应该做你想要的:

module PluginHelpers
  def stub_required_methods(*method_names)
    method_names.each do |method_name|
      define_method(method_name) do
        raise NotImplementedError, "Method #{__method__} must be implemented in #{self.class}"
      end
    end
  end
end

class A
  class B
    class C
      extend PluginHelpers
      stub_required_methods(:foo, :bar)
    end
  end
end

# usage
A::B::C.new.foo # => NotImplementedError: Method foo must be implemented in A::B::C