ActsAsNestedBy

‘acts_as_nested_by’ is/will be a simple counterpart for ActiveRecord::NestedAttributes ‘accepts_attribute_for’.

Installation

  • as gem

    $ sudo gem install acts_as_nested_by
    
  • or as plugin in your rails app

    $ ./script/plugin/install git://github.com/tehael/acts_as_nested_by.git # rails 2.3.x
    $ rails plugin install git://github.com/tehael/acts_as_nested_by.git # rails 3.0.x
    

Use case example

Since the rails-way of handling referential integrity is to code the logic into you model without using database level foreign key constraints, you might produce code like this.

class Foo < ActiveRecord::Base
  has_many :bars
  validates_associated :bars
  accepts_nested_attributes_for :bars
end

class Bar < ActiveRecord::Base
  belongs_to :foo
  validates_presence_of :foo
end

Using ‘validates_presence_of :foo’ instead of ‘validates_presence_of :foo_id’ ensures that not only a ‘:foo_id’ was given but a Foo object existst in the :foos table.

But on creation of a Foo with nested Bars in one step produces a dead look, since the ‘:foo_id’ for the Bars is not available until the Foo is saved, and the ‘validates_presence_of :foo’ therefore produces an error in the Bars invalidating them, no ‘:foo_id’ no ‘Foo’ in the ‘:foos’ table. And because the ‘Foo’ ‘:validates_associated :bars’ it cannot be saved. Using an ‘:unless => :new_record?’ condition with the ‘:validates_presence_of :foo’ might help, but the validation is also skipped, if a ‘Bar’ is not created in a nested way.

To handle this ‘acts_as_nested_by’ comes in handy.

class Bar < ActiveRecord::Base
  belongs_to :foo
  acts_as_nested_by :foo
  validates_presence_of :foo, :unless => :nested_by_foo?
end

For now just pass a parameter (with a hidden form parameter) ‘:nested_by_foo => true’ when creating ‘Bars’ nested in a ‘Foo’. If the ‘Foo’ itsef is ‘valid?’ it can be saved, the nested ‘Bars’ will be auto saved and consistency is assured. ;)

Nota bene

  • The association must be established before ‘calling acts_as_nested_by :foo’, so it must be called after ‘belongs_to :foo’. An ArgumentError will be raised if the association is not yet established.

  • By now the association must be defined by ‘belongs_to’ macro, otherwise an ArgumentError will be raised.

  • When using ‘:attr_accessible’, ‘:attr_proteced’ and a hidden form parameter you should add ‘:nested_by_foo’ to the mass-assignable attributes.

Next steps/known issues

  • Setting of ‘:nested_by_other’ via mass assignement or by calling ‘nested_by_other = true’ could be exploited to create inconsistent entries.

  • Therefore the mass assignment setter method provided by ‘ActiveRecord#accepts_nested_attributes_for’ should set the ‘:nested_by_other’ flag internaly and ‘ActiveRecord::Base#nested_by_other’ should be defined protected.

Note on Patches/Pull Requests

  • Fork the project.

  • Make your feature addition or bug fix.

  • Add tests for it. This is important so I don’t break it in a future version unintentionally.

  • Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)

  • Send me a pull request. Bonus points for topic branches.

Copyright © 2010 Thomas Limp. See LICENSE for details.