Ruby Metaprogramming:基于子实例属性动态定义方法

时间:2015-04-26 08:24:27

标签: ruby-on-rails ruby

我正在创建一个presenter基类,它应该包装一个ActiveRecord对象。

class BasePresenter
  def initialize object
    @object = object
  end

  def method_missing(*args, &block)
    @object.send(*args, &block)
  end

  def self.wrap collection
    collection.map { |item| new item }
  end
end

对于每个子类,我希望能够在初始化时基于子属性动态定义方法,因此像ListPresenter一样:

class ListPresenter < BasePresenter
end

应使用包装的List对象的id响应list_id

如何在每个儿童班上定义它?我在def initialize(object)中尝试了以下内容,这两种方法都不起作用。 如果可能的话,我宁愿避免使用基于eval的方法,因为我听到这是一种代码味道。

类方法(将方法添加到BasePresenter,而不是子类)

self.class.send(:define_method, "#{object.class.name.underscore}_id") do
  @object.id
end

元类方法(无法访问实例变量object@object):

class << self
  define_method "#{@object.class.name.underscore}_id" do
    @object.id
  end
end

2 个答案:

答案 0 :(得分:4)

当子类继承BasePresenter时,使用Class#inherited挂钩动态添加方法。

class BasePresenter
  def self.inherited(sub_klass)
    sub_klass.send(:define_method, "#{sub_klass.name.underscore.split("_")[0...-1].join("_")}_id") do
      instance_variable_get("@object").id
    end
  end
end


class ListPresenter < BasePresenter
end

# Sending a rails model object below
l = ListPresenter.new(User.first)
l.list_id #=> 1

答案 1 :(得分:2)

我建议您查看Class#inherited

您可以在基类中定义此方法,并对继承类进行评估。方法...

我可能会在我坐在电脑前编辑这个,但这很简单。