Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using ActiveRecord with DelegateClass

Using rails 3.2.8 I have a model setup with a couple attributes

class MyModel < ActiveRecord::Base
  attr_accessible :foo, :bar
end

I have another class setup using the above as a delegate class

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

When I create a new MyModelPresenter like so:

MyModelPresenter.build(Date.today, {:foo => 1})

I get the following back

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'

For some reason the database attributes on the model aren't getting defined (setters or getters). Before upgrading to rails 3.2 this was all working in a rails 3.1 app.

Does anyone have any idea why the model's attribute methods aren't getting defined?

like image 406
nates Avatar asked Nov 12 '22 18:11

nates


1 Answers

Inheriting from Delegator instead of using DelegateClass solved the problem. Here is what the final class looked like:

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

As you can see I also had to add the def __getobj__ method. and set @_sd_obj in the initializer to an instance of the class I'm delegating from.

See the SimpleDelegator example.

like image 89
nates Avatar answered Jan 04 '23 03:01

nates