DEV Community

Cover image for Rails going backwards
not-a-patch
not-a-patch

Posted on

Rails going backwards

Introduction

This is a Rails review on how to link back to the page you've just visited from. I had struggled to get a solution, and reading through Stackoverflow I don't think I'm alone in this. This is my take and I hope it is useful or can become useful with the help of others.

Solutions

  1. Solution 1: Back with hardcoding
  2. Solution 2: Back with referer
  3. Solution 3: Back with JavaScript
  4. Solution 4: Back with session

Solution 1: Back with hardcoding

If there's only one route in and out of the page. Hardcoding the path out is a clean option. From here, it gets more interesting...

Solution 2: Back with referer

A popular back solution is to use Rails :back symbol to be passed to link_to. An example is in listing 1.

Listing 1

<%= link_to "my link', :back %>
Enter fullscreen mode Exit fullscreen mode

link_to :back rails code It is described in the docs as:

Using a :back Symbol instead of an options hash will generate a link to the referer (a JavaScript back link will be used in place of a referer if none exists).

What's a referer? MDN give a definition for referer they say:

The Referer HTTP request header contains an absolute or partial address of the page making the request. When following a link, this would be the address of the page containing the link.

So, requests often have a URL of the page you were last on. This is the referer, spelt referer in official documents, and Rails uses it to get back again. However, the referer can be missing if the user typed the URL into the search bar directly, as well as other exceptions.

Checking for the referer

I wanted to see how it works. First, I found a way to get the referer in Firefox. You can check the referer by going to the Firefox inspector and then the network tab and searching for referer. In this example, we have navigated to edit page and checking for referer, which is the show page (FYI: the resource was feedbacks).

What is the referer?

There are two issues with using the referer:
a. Back twice - if you go back more than once, the referer will point to where you were last. Not where you were when you were last on that page!
b. POST - if you perform a POST on the page, the referer is also reset.

a. Back Twice

We can see, in table 1, that back is fine for step 1 but on step 2 going back ends up in a loop between Show to Edit. In other words, the Referer only remembers one hop and can quickly form a loop because it is always pointing to where you were on the last hop.

Table 1: Stepping through an application, and what happens if you go back?
Step Page Referer Back Back again And again And again
0 Home Empty → Empty → Empty → Empty → Empty
1 Show Home → Home → Empty → Empty → Empty
2 Edit Show → Show → Edit ⇾ Show → Edit*
  • It will keep looping between show and edit.

Another way to express this information is in a diagram where we can see the black lines as user clicks and blue lines when the user back clicks.

Back using referer

b. POST

It did not work when you are POST a form on the page and then want to go back. If I had to, POST on the show page, say I added a comment and stayed on the Show page. After you POST the referer gets set to the show page, the current page, and back will take you to the show page. In other words, nowhere.

Back with referer summary

link_to :back is only useful when you want to go back one page. In our case, once it had reached edit, it won't remember the original route it got to show. Further, when you POST on the page, the referer can get reset to the current page and losing the route back.

Solution 3: Back with JavaScript

Another popular class of answers are JavaScript. JavaScript is also used in Rails :back if the referer can't be found. Example of Back with Javascript is in listing 2.

Listing 2

 <%= link_to 'Back', 'javascript:history.back()' %>
Enter fullscreen mode Exit fullscreen mode

JavaScript back uses the browser's page stack. Each time it visits a page, it is added to a stack of URLs. Going back meant popping each page reference off the stack. Here's a Stackoverflow on how a Browser back button works. The downside for this, if the user had gone to the edit and then saved and returned to the show page and then started to use back you would have to go "back" via the edit which might not be what the user expected.

Back using JavaScript

Solution 4: Back with session

The final class of solutions were saving the routes in the session. To get this to work, I added methods to the ApplicationController to set and retrieve the values, as well as code to update the session in the controller and use the session variable in the view's link_to - Listing 3 contains the code.

This is not nearly as nice as the other two solutions, much less cohesion, as there's already 3 files required compared to 1 liners for the other solutions.

I'm not saying this is good code, I'm just saying this is what I had to do to get it to work!

Listing 3 Example of using session to go backwards

# app/controllers/application_controller.rb

class ApplicationController
  ...
  helper_method :retrieve_last_index_page_or_default
  ...
  def store_last_index_page
    session[:last_index_page] = request.fullpath
  end

  def retrieve_last_index_page_or_default(default_path: root_path)
    session[:last_index_page] || default_path
  end
  ...
end
Enter fullscreen mode Exit fullscreen mode

Updating session in controller code

# app/controllers/my_controller.rb
class MyController < ApplicationController
  ...
  def index
    ...
    # this could have been in a before_action
    store_last_index_page
    ...
  end
end
Enter fullscreen mode Exit fullscreen mode

View code

# app/views/my/index.html.erb

# this could have been a variable set in controller
<%= link_to retrieve_last_index_page_or_default do %>
  ...
<% end %>
Enter fullscreen mode Exit fullscreen mode

Summary

There are four ways to go backwards:

# link_to Type Description
1 link_to my_path Hardcoded Clean, limited to one route
2 link_to :back Referer Clean, limited to one hop
3 link_to 'javascript:history.back()' JavaScript Access to Browser back
4 link_to get_session_variable() Session Powerful but complicated

References

link_to Rails docs (6.1.4)
url_helper Rails code (6.1.4)
MDN - Referer
HTTP_referer#Etymology
In what cases will the Referer be empty?
How does the Back button in a web browser work?

Latest comments (3)

Collapse
 
kendallcoolidge profile image
Kendall Coolidge

This was very helpful. I have a project with complex (to me at least) navigation where I can end up in one place from several different paths and I always want to "back up" along those same paths. I found that your "session" solution was exactly what I needed in those cases. Thanks very much.

Collapse
 
superails profile image
Yaroslav Shmarov

Thanks! I was frustrated about why :back is getting me into a loooooooooop. Ended up just doing the <%= link_to 'Back', 'javascript:history.back()' %>

Collapse
 
notapatch profile image
not-a-patch

When you hit the loop you think "I really don't know what I'm doing do I?". Glad to know I helped as I'm always reading and watching your output.