views:

779

answers:

2

Using Rails v2.1, lets say you have an action for a controller that is accessible from more than one location. For example, within the Rails app, you have a link to edit a user from two different views, one on the users index view, and another from another view (lets say from the nav bar on every page).

I'm wondering what is the best way to redirect the user back to the right spot depending on what link they clicked on. For example:

Example 1:

  1. List all users
  2. Click "edit" on a user in the list
  3. User clicks "save" on the form, the controller redirects back to 1.

Example 2:

  1. The user could be on any page within the application, the nav bar shows a link to edit the current user
  2. The user clicks on the link to edit
  3. The user clicks "save" on the form, controller redirects back to whatever page they were on when the user clicked the "edit" link in the nav bar.

I've seen it done in the past by:

  1. Placing a parameter on the original edit link with the original controller/action in which the link appeared. To make this more DRY, you could use @controller.controller_name and @controller.action_name in a helper.
  2. The controller saves the parameters to a session variable.
  3. Once the controller has saved the record, it redirects to the session variable.

What I don't particularly like about this solution is the need to add the parameter to every applicable link in the views. I'm wondering if there's a way to build this all into the controller.

One better way I was thinking was to:

  1. Place a before_filter on the "edit" action to save the referrer (is this reliable enough?) into the session.
  2. When "update" is hit, the controller will redirect to the session variable and then delete the session variable.

Any thoughts on the best way to do this?

+1  A: 

I think that using before_filter on the edit action is the least obtrusive.

The referer should be reliable enough ... simply have a default in the case of no referer being available (say: someone bookmarked the edit page) and you should be fine.

Toby Hede
+2  A: 

Flash: Data which you'll use only for the next request can be stored in the flash. Then you don't have to go about automatically clearing it. It works well for limited bits of application context which you're sure to only need once -- not just for error messages!

I know what you did last HTTP access: If you just need to redirect people to the last URL they were at, then just do that. request.referer will, for most browers which don't block the information, give you the last URL the person was at.

#in edit controller
...
flash[:page_to_redirect_to] = request.referer || "/my/default/path"
...

#in save controller
redirect_to flash[:page_to_redirect_to] || "/my/default/path"

I wouldn't suggest hardcoding those, incidentally.

before_filter: I see a lot of Rails developers using this as their favorite hammer and turning everything else into nails. Filters are useful when you want to expose functionality to all or substantially all methods in a controller. I don't know that is necessarily the case here, but your mileage may vary. You can combine the filter with the above two tricks if warranted.

Patrick McKenzie
Good thoughts, you just helped me think through a different problem I was looking at. Thanks!
PJ
I get ya, but the session variable may still be necessary as it needs to persist across form validation errors. How does your answer cater for this? The before_filter gives better DRY-ness as this behaviour would need to be in a number of actions in the controller.
Dan Harper - Leopard CRM
If you need to persist something saved in the flash for another request (for example, to get it past an error correction step) then you can either choose to explicitly persist it (`flash[:foo] = flash[:foo]`) or, if it will cause you less pain overall, use session and explicitly clear it when done.
Patrick McKenzie
This is the best we've come up with as well, but something still doesn't smell completely right about it. Maybe I'm just yearning for a stateful web, and I shouldn't be.
Abie