将ActiveRecord与DelegateClass一起使用

nat*_*tes 5 ruby ruby-on-rails-3.2

使用rails 3.2.8我有一个带有几个属性的模型设置

class MyModel < ActiveRecord::Base
  attr_accessible :foo, :bar
end
Run Code Online (Sandbox Code Playgroud)

我使用上面的另一个类设置了一个委托类

class MyModelPresenter < DelegateClass(MyModel)
  def initialize(month, obj)
    @month = month
    super(obj)
  end

  def self.build(month, attributes = { })
    new(month, MyModel.new).tap do |p|
      p.attributes = attributes
    end
  end

  def attributes=(attributes)
    attributes.each { |k, v| send("#{k}=", v) }
  end
end
Run Code Online (Sandbox Code Playgroud)

当我像这样创建新的MyModelPresenter时:

MyModelPresenter.build(Date.today, {:foo => 1})
Run Code Online (Sandbox Code Playgroud)

我得到以下内容

NoMethodError: undefined method `foo=' for #<MyModel:0x1098f31a8>
    from /Users/me/.rbenv/versions/ree-1.8.7-2011.03/lib/ruby/gems/1.8/gems/activemodel-3.2.8/lib/active_model/attribute_methods.rb:404:in `method_missing'
    from /Users/me/.rbenv/versions/ree-1.8.7-2011.03/lib/ruby/gems/1.8/gems/activerecord-3.2.8/lib/active_record/attribute_methods.rb:149:in `method_missing'
    from /Users/me/dev/temp/app/presenters/my_model_presenter.rb:23:in `send'
    from /Users/me/dev/temp/app/presenters/my_model_presenter.rb:23:in `attributes='
    from /Users/me/dev/temp/app/presenters/my_model_presenter.rb:23:in `each'
    from /Users/me/dev/temp/app/presenters/my_model_presenter.rb:23:in `attributes='
    from /Users/me/dev/temp/app/presenters/my_model_presenter.rb:17:in `build'
    from /Users/me/dev/temp/app/presenters/my_model_presenter.rb:15:in `tap'
    from /Users/me/dev/temp/app/presenters/my_model_presenter.rb:15:in `build'
Run Code Online (Sandbox Code Playgroud)

由于某种原因,模型上的数据库属性未定义(setter或getter)。在升级到Rails 3.2之前,这都是在Rails 3.1应用程序中进行的。

有谁知道为什么没有定义模型的属性方法?

nat*_*tes 1

继承Delegator而不是使用DelegateClass解决了这个问题。这是最后一堂课的样子:

class MyModelPresenter < Delegator
  def initialize(month, obj)
    @month = month
    super(obj)
    @_sd_obj = obj
  end

  def __getobj__
    @_sd_obj
  end

  def self.build(month, attributes = { })
    new(month, MyModel.new).tap do |p|
      p.attributes = attributes
    end
  end

  def attributes=(attributes)
    attributes.each { |k, v| send("#{k}=", v) }
  end
end
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,我还必须添加该def __getobj__方法。并@_sd_obj在初始化程序中设置为我所委托的类的实例。

请参阅SimpleDelegator示例。