I’m using the nested_form gem for my AddressBook relation. When the user blanks out the value of an existing Addr, I want to delete that Addr rather than saving with a blank value
class Person < ActiveRecord::Base
has_many :addrs, dependent: :destroy
attr_accessible :name, :addrs_attributes
accepts_nested_attributes_for :addrs, reject_if: :addr_blank, allow_destroy: true
def addr_blank(a)
valid? && a[:id].blank? && a[:value].blank?
end
class Addr < ActiveRecord::Base
belongs_to :person
attr_accessible :kind, :label, :value, :person_id
My :reject_if method works well but it doesn’t give me everything I need
valid?keeps my blank Addrs around through validationa[:id].blank?avoids rejections when the user blanks out and existing record
Now, I need to delete (rather than save) an existing Addr when the user blanks the value. Also, I’m exposing Persons and Addrs via a RESTful API. I see two possible options:
- Post process the
paramshash to add the magical_destroy=1param. IOW, emulate the user activity of pressing the delete button. - Encapsulate this inside the
Addrmodel such that an update with a blankvalueis effectively considered a delete.
Based on the advice here is how I implemented it:
people_controller.rb
def update
@person = Person.find(params[:id])
@person.destroy_blank_addrs(params[:person])
respond_to do |format|
...
person.rb
def destroy_blank_addrs(person_params)
if valid? && person_params[:addrs_attributes]
person_params[:addrs_attributes].each do |addr_params_array|
addr_params= addr_params_array[1]
addr_params[:_destroy] = '1' if !addr_params[:id].blank? && addr_params[:value].blank?
end
end
end
A third alternative would be to add an
before_savecallback on Person that will remove all addresses that are blank. This idea has some merit, but I probably won’t go with it.Out of the two options you present, I will not go with post-processing the params. It will work out, but it is too much work. Besides, to controller code will get a bit messier and I’m a firm believer in a very slim controller.
The easiest option, in my head, is to remove the blank addresses after saving. You can add
Person#remove_blank_addresses()and then call it on successful save. You don’t need to pass in the params – it can just iterate the addresses and remove the blank ones. It has the disadvantage of creating empty addresses and then destroying them, but you would need that for updating people anyway.If we’re talking about the cleanest solution (in my opinion), I would introduce a third class that would handle all that logic and have the controller delegate to it. The controller would be easy enough to test in isolation and then you can write a model spec that checks all the nitty-gritty details. It is a bit more work and I can’t think of a good name right now (
PersonUpdater?), but it might be an idea worth thinking about.