6

I have a SettingsController with actions account and profile, and also an update that looks like:

  def update
    @player = current_user
    if @player.update_attributes(params[:player])
      flash[:success] = "Profile updated."
      redirect_to :back
    else
      @title = "Edit"
      render
    end
end

Now the profile and account actions each have a corresponding view, with a form to edit some records of the Player model.

When you try to save one of those forms, and it fails, ie. it didn't pass validation, it should render the action that initialized the update again, so it can display appropiate error messages.

But the problem is, how do I know which of both requested the update, and render the right one? Basically some sort of equivalent of redirect_to :back is what I'm looking for here.

Sebastian
  • 509
  • 1
  • 5
  • 14

2 Answers2

19

This is ugly but works :)

render Rails.application.routes.recognize_path(request.referer)[:action]
apneadiving
  • 114,565
  • 26
  • 219
  • 213
  • 1
    ... I guess it's a compliment :) – apneadiving Mar 12 '11 at 21:45
  • You're right, this is ugly..! But if it works... Do you know if anything new has come up since 2011? Thanks!!! – Freddo Mar 04 '16 at 15:57
  • It doesn't work cross-controllers. I have a dashboard `administration#dashboard` whose view is `administration/dashboard.html.erb` which lets admins update employees through `employees#update`, and using your method, I have a missing template error because the dashboard is in the subfolder `/administration/` and rails is only looking inside `/` and `/employees/` – Cyril Duchon-Doris Mar 21 '16 at 01:28
  • Ah actually `Rails.application.routes.recognize_path(request.referer)` returns a hash of `:controller` and `:action`, so you can just join the two by `/` to solve the folder problem. `render Rails.application.routes.recognize_path(request.referer).map{ |k,v| v }.join('/')` – Cyril Duchon-Doris Mar 21 '16 at 01:41
  • it still works in 2022! Not sure if there's a prettier way of doing it? – Felipe Cerda Feb 27 '22 at 10:45
-1

Usually you can fix this by applying a pattern:

def edit
  @title = "Edit"

  # ...
end

def update
  # Update using the exception-generating variant
  @player.update_attributes!(params[:player])

  # ... More actions if successful

rescue ActiveRecord::RecordInvalid
  self.edit
  render(:action => 'edit')
end

This is often simplified by having a before_filter that handles the loading of the model instance for you.

Since this can be used a lot, sometimes you can wrap that into a delegation method:

def fallback_render(action)
  send(action)
  render(:action => action)
end
tadman
  • 208,517
  • 23
  • 234
  • 262