views:

67

answers:

3

In one of my model objects I have an array of objects.

In the view I created a simple form to add additional objects to the array via a selection box.

In the controller I use the append method to add user selected objects to the array:

def add_adjacents
  @site = Site.find(params[:id])

  if request.post?
    @site.adjacents << Site.find(params[:adjacents])
    redirect_to :back
  end    
end

I added a validation to the model to validate_the uniqueness_of :neighbors but using the append method appears to be bypassing the validation.

Is there a way to force the validation? Or a more appropriate way to add an element to the array so that the validation occurs? Been googling all over for this and going over the books, but can't find anything on this.

A: 

Have you tried checking the validity afterwards by calling the ".valid?" method, as shown below?

def add_adjacents
  @site = Site.find(params[:id])
  @site.neighbors << Site.find(params[:neighbors])
  unless @site.valid?
    #it's not valid, do something to fix it!
  end
end
Mike Trpcic
I added the unless @site.valid? line and am getting the following error: NoMethodError in SitesController#add_adjacentsYou have a nil object when you didn't expect it!The error occurred while evaluating nil.text?I also added duplicate entries and tried to use the @site.valid? command in the ruby console and it evaluates to true
Frank
A: 

A couple of comments:

  1. Then only way to guarantee uniqueness is to add a unique constraint on your database. validates_uniqueness_of has it's gotchas when there are many users in the system:
    • Process 1 checks uniqueness, returns true.
    • Process 2 checks uniqueness, returns true.
    • Process 1 saves.
    • Process 2 saves.
    • You're in trouble.
  2. Why do you have to test for request.post?? This should be handled by your routes, so in my view it's logic that is fattening your controller unnecessarily. I'd imagine something like the following in config/routes.rb: map.resources :sites, :member => { :add_adjacents => :post }
  3. Need to know more about your associations to figure out how validates_uniqueness_of should play in with this setup...
hgimenez
The reason I tested for request.post? was because I was unable to get the params[:id] value when I used submit_tag on the form and it went to a second action. I looked up form_tag in the Rails Way (page 379) and the author suggested that as a quick way of handling post-backs (my ultimate goal is to update the view with ajax, but I wanted to start small before complicating it all.As for the association. It's a bit complicated. The Site model has_and_belongs_to_many :neighbors. This is a self-referential relationship, accomplished via a form table. For reference http://tinyurl.com/ommquj
Frank
Since you are telling me that there's a join model, you should be able to put validates_uniqueness_of :site_id, :scope => :adjacent_site_id in it (of course, I'm making up names since you didn't really tell what your associations are). Hope that helps.
hgimenez
A: 

I think you're looking for this:

@site.adjacents.build params[:adjacents]

the build method will accept an array of attribute hashes. These will be validated along with the parent model at save time.

Since you're validating_uniqueness_of, you might get some weirdness when you are saving multiple conflicting records at the same time, depending on the rails implementation for the save and validation phases of the association.

A hacky workaround would be to unique your params when they come in the door, like so:

@site.adjacents.build params[:adjacents].inject([]) do |okay_group, candidate|
  if okay_group.all? { |item| item[:neighbor_id] != candidate[:neighbor_id] }
    okay_group << candidate
  end
  okay_group
end

For extra credit you can factor this operation back into the model.

austinfromboston