nested_set icon indicating copy to clipboard operation
nested_set copied to clipboard

allow after_add callback

Open jcm opened this issue 13 years ago • 3 comments

Hi,

What do you think of adding an :after_add option for the has_any :children relation ? It would allow to cleanly set some fields that ibherits from parent when creating children with model.children.build (for a nested form for instance)

jcm avatar Aug 12 '11 16:08 jcm

Does this can't be done with AR callbacks in special case? I am not really understand what do you suggest. Try to be more clear, please. It will much better if you provide an example.

skyeagle avatar Aug 16 '11 07:08 skyeagle

Hi,

Here is a detailed example.

After_add is an AR callback : http://edgeguides.rubyonrails.org/association_basics.html chapter 4.5

It's the only solution to be able to setup some initialization dependant from the parent to a newly created child before a nested form, for instance. A before_create callback is called only when saving a new record, and to prepare objects for a nested form, they need to be only built but not created. I tried too to write an after_initialize method, but there the parent_id (from the relation) is not set and therefore, cannot be accessed to inherit.

Example with a simple Flower model:

> rails new nestedsettest -J -d postgresql
> cd nestedsettest
> echo "gem 'nested_set', '~> 1.6.7'" >> Gemfile
> bundle install
> rails g scaffold Flower ref:string seeded_at:date species_id:integer parent_id:integer lft:integer rgt:integer
> rake db:create
> rake db:migrate
> rails console
>> f1 = Flower.create( :species_id =>1, :ref => "Rose 1", :seeded_at => DateTime.now )
>> f2 = Flower.create( :species_id =>2, :ref => "Camelia 1", :seeded_at => DateTime.now )
>> f3 = f2.children.build
=> #<Flower id: nil, ref: nil, seeded_at: nil, species_id: nil, parent_id: 2, lft: nil, rgt: nil, created_at: nil, updated_at: nil>

I would like to have the species_id inherit from the parent. The only callback to set values in a has_many.build call is :after_add.

Let's change the Flower model:

class Flower < ActiveRecord::Base
#  acts_as_nested_set
  belongs_to :parent, :class_name => 'Flower', :foreign_key => :parent_id
  has_many :children, :class_name => 'Flower', :foreign_key => :parent_id, :after_add => :populate

  def populate(f)
    f.species_id = f.parent.species_id
  end
end

Now in console:

>> f2 = Flower.find 2
>> f3 = f2.children.build
=> #<Flower id: nil, ref: nil, seeded_at: nil, species_id: 2, parent_id: 2, lft: nil, rgt: nil, created_at: nil, updated_at: nil>

The species was correctly set by the callback.

I simply wish I could do it with

class Flower < ActiveRecord::Base
  acts_as_nested_set :after_add => :populate

  def populate(f)
    f.species_id = f.parent.species_id
  end
end

which implies adding an option to pass an after_add method name.

jcm avatar Aug 16 '11 09:08 jcm

Ah.. Ok. But I don't see a big difference with before_save callback. If you need this functionality feel free to provide a pull request and I'll merge it.

skyeagle avatar Aug 16 '11 11:08 skyeagle