views:

400

answers:

2

So I have a method in a reservation model called add_equip. This method does some checking to make sure the added piece of equipment is valid (not conflicting with another reservation).

The checks work. If a added piece of equipment shouldn't be added it isn't, and if it should it is.

The problem is I can't figure out how to send the messages back up to the controller to be put in the flash message? I know I must be missing something here, but I've googled for a few hours now and can't really find any clear explanations how how to pass errors back up the the controller, unless they are validation errors.

add_equip in reservations_controller

    def add_equip
    @reservation = Reservation.find(params[:id])
    @addedEquip = Equip.find(params[:equip_id])

    respond_to do |format|
     if @reservation.add_equip(@addedEquip)
        flash[:notice] = "Equipment was added"
        format.html { redirect_to(edit_reservation_path(@reservation)) }
     else
        flash[:notice] = @reservation.errors
        format.html { redirect_to(edit_reservation_path(@reservation)) }
     end
    end
  end

add_equip in reservation model

def add_equip equip
   if self.reserved.find_by_equip_id(equip.id)
     self.errors.add_to_base("Equipment Already Added")
     return false
   elsif !equip.is_available?(self.start, self.end)
     self.errors.add_to_base("Equipment Already Reserved")
     return false
   else
     r = Reserved.new
     r.reservation = self
     r.equip = equip
     r.save
   end
  end

Any help would be greatly appreciated. I know I'm missing something basic here.

+2  A: 

Using add_to_base to store the error message seems fine to me, you just need to figure out how to get it into the view.

How about:

flash[:notice] = @reservation.errors.full_messages.to_sentence

Assuming you're going to re-display a form, you could also probably use:

<%= f.error_messages %>

Or possibly:

<%= error_messages_for :reservation %>

Also, you might want to use flash[:error], then you can color it differently with a CSS class in your view.

Luke Francl
Thanks adding the ".full_messages.to_sentance" did the trick. I knew I must be missing something silly.
raytiley
Using model.errors.full_messages in a flash message is IMHO a poor way to solve this. 'error_messages_for :model' is the conventional way to display error messages, as you indicated. The proper way to solve the problem would be to put the error on equip_id instead of on base and then just render the edit form action. No redirects needed.
Wes Oldenbeuving
That is a good point, it would be better to re-render the page.
Luke Francl
+1  A: 

I think I can see why errors are not being passed back to the user.

The problem is that you are sending a redirect to the user when the action fails instead of just doing a render, that means you lose any variables you set up to use within the request. Instead of adding errors to the flash, just render the edit page and set the flash to a normal message and everything should be fine.

For example:

def add_equip
  @reservation = Reservation.find(params[:id])
  @addedEquip = Equip.find(params[:equip_id])

  respond_to do |format|
    if @reservation.add_equip(@addedEquip)
      flash[:notice] = "Equipment was added"
      format.html { redirect_to(edit_reservation_path(@reservation)) }
    else
      flash[:error] = 'Error adding equipment'
      format.html { render :action => :edit }
    end
  end
end

Now you can continue to use the normal form helpers for displaying error messages.

Also, just a little suggestion for the model code, try to use i18n when possible (including for flash messages in the controller). Although this is mostly a personal preference, it gives a logical home to all your messages and specific text, and alos allows you to create general or default messages which can be changed in one place instead of duplicating the change in multiple models and controllers.

eg.

def add_equip equip
  if self.reserved.find_by_equip_id(equip.id)
    self.errors.add_to_base(:already_added)
    return false
  elsif !equip.is_available?(self.start, self.end)
    self.errors.add_to_base(:already_reserved)
    return false
  else
    r = Reserved.new
    r.reservation = self
    r.equip = equip
    r.save
  end
end
Josh K
Thanks for the reply. The first post solved the issue without changing the redirect code. The i18n suggestion is a good idea though. Thanks.
raytiley
I would strongly recommend that you change your code to follow my example. The first example is not following the standard convention for displaying errors, and although this is not a bad thing in itself, your problem is not new and a standard way to deal this this workflow is already established. By following convention other developers will be able to understand your code quicker without having to figure out the custom workflow or how to display error messages.
Josh K