views:

205

answers:

2

Hi,

I've 2 models

class Room < ActiveRecord::Base
  has_many :people
  accepts_nested_attributes_for :people, :reject_if => lambda { |a| a[:person_id].blank? }, :allow_destroy => true
end

class Person < ActiveRecord::Base
  belongs_to :room
end

In '/rooms/new' form I've a select tag containing all Person + an 'other' option tag that allow the user to add dynamicaly a person to the select tag (New name).

So, when I submit my form I can have a person with id = -1 which doesn't exist in database, and of course, I want to create a new Person with the new name.

I'm wondering what is the best way to achieve that?

with a 'before_filter' or a 'rescue ActiveRecord::RecordNotFound' or ...

thanks for your help

+2  A: 

As a general practice, I would not suggest using exception handling as a control for functional logic. So I am advocating for checking for an id of -1, and creating the person in that case, rather than doing so after the fact in a rescue block.

If you are looking for a reason, 2 I think about are performance and clarity.

Exceptions are expensive, and you don't want to incur the processing cost for them if it can be avoided.

Also, exceptions are meant to indicate an error condition, not an expected path in your logic. By using them this way, you are muddying the waters, and making it seem like this is not meant to work his way. By having the check for a non-extant person in the before filter, it is more clear this is supposed to happen sometimes, and clear that this happens before the rest of the save.

Also, if you did this logic in handling the exception, you then have to retry the operation that failed, making your save logic that much more complex by either looping or being recursive or otherwise duplicating the failing save. That will also make your code less clear to the next coder that has to work on it.

Andrew Kuklewicz
I agree with you, exceptions should be use only to catch errors and not for functional logic, thanks for your answer
denisjacquemin
+2  A: 

You don't need any special code. ActiveRecord already includes logic to handle this case.

Read the rdoc at http://github.com/rails/rails/blob/2-3-stable/activerecord/lib/active_record/nested_attributes.rb#L328 or http://github.com/rails/rails/blob/master/activerecord/lib/active_record/nested_attributes.rb#L332 for the details. Essentially, if the Hash is passed with an :id key that record's attributes are updated. If the record has no :id key, a new record is created. If it has an :id key, and a :_destroy key with a true'ish value, the record will be deleted.

Below are the 2-3-stable branch documentation:

# Assigns the given attributes to the collection association.
#
# Hashes with an <tt>:id</tt> value matching an existing associated record
# will update that record. Hashes without an <tt>:id</tt> value will build
# a new record for the association. Hashes with a matching <tt>:id</tt>
# value and a <tt>:_destroy</tt> key set to a truthy value will mark the
# matched record for destruction.
#
# For example:
#
# assign_nested_attributes_for_collection_association(:people, {
# '1' => { :id => '1', :name => 'Peter' },
# '2' => { :name => 'John' },
# '3' => { :id => '2', :_destroy => true }
# })
#
# Will update the name of the Person with ID 1, build a new associated
# person with the name `John', and mark the associatied Person with ID 2
# for destruction.
#
# Also accepts an Array of attribute hashes:
#
# assign_nested_attributes_for_collection_association(:people, [
# { :id => '1', :name => 'Peter' },
# { :name => 'John' },
# { :id => '2', :_destroy => true }
# ])
François Beausoleil