Storing Data Between Sessions

Cookies are useful for keeping track of a piece of information between page changes, but as you may have noticed in Figures 13-3 and 13-5, Rails was already setting a cookie, a session cookie, with each request. Rather than manage cookies yourself, you can let Rails do all of that work and move one step further back from the details. (This example is available in ch13/guestbook012.)

Sessions are a means of keeping track of which user is making a given request. Rails doesn’t know anything specific about the user, except that he has a cookie with a given value. Rails uses that cookie to keep track of users and lets you store a bit of information that survives between page requests.

You can set and retrieve information about the current session from the session object, which is available to your controller and view. Because it’s a better idea in general to put logic into the controller, Example 13-3, which is a new version of the app/controllers/entry_controller.rb file, shows what’s involved in storing an array in the session object, retrieving it, and modifying it to add names to the list. Virtually all of it replaces code that was in Example 13-1, with only the retrieval of the name from the form staying the same.

Example 13-3. Working with an array stored in the session object

class EntryController < ApplicationController
    def sign_in
    #get names array from session
    @names=session[:names]

    #if the array doesn't exist, make one
    unless @names
      @names=[]
    end

    #get the latest name from the form
    @name = params[:visitorName]

    if @name
      # add the new name to the names array
      @names << @name
    end

    # store the new names array in the session
    session[:names]=@names
  end
end

Most of the new code is about working with an array rather than a simple field. It’s not a big problem if a string is empty, whereas trying to add new entries to a nonexistent array is a bigger problem. The sign_in method gets the names array from the session object and puts it in @names. If the session object doesn’t have a names object, it will return nil, so the unless creates a names array if necessary. Then the method retrieves the latest visitorName from the form and adds it to the @names array. The very last line puts the updated version of the @names array back into the session object so that the next call will have access to it.

Note

Example 13-3 is more verbose than it needs to be, as you could work on session[:names] directly. However, it’s a bit clearer to work with the @names instance variable, and this approach lets the view work strictly with instance variables.

The view requires fewer changes—just a test that the list of names exists and a loop to display the names if it does. The changes to app/views/entry/sign_in.html.erb are highlighted in Example 13-4.

Example 13-4. Reporting a set of previous names to the user

<html>
<head><title>Hello <%=h @name %></title></head>

<body>
<h1>Hello <%=h @name %></h1>

<% form_tag :action => 'sign_in' do %>
   <p>Enter your name:
   <%= text_field_tag 'visitorName', @name %></p>

   <%= submit_tag 'Sign in' %>
<% end %>

<% if @names %>
<ul>
  <% @names.each do |name| %>
    <li><%=h name %></li>
  <% end %>
</ul>
<% end %>

</body>
</html>

As Figures 13-7 through 13-9 demonstrate, the application now remembers what names have been entered before.

The first iteration, where no previous names are recorded in the session object

Figure 13-7. The first iteration, where no previous names are recorded in the session object

The second iteration, where one previous name has been recorded in the session object

Figure 13-8. The second iteration, where one previous name has been recorded in the session object

The third iteration, where two previous names have been recorded in the session object

Figure 13-9. The third iteration, where two previous names have been recorded in the session object

If you quit your browser and return, or try a different browser, you’ll get the empty result shown in Figure 13-8 again, as the session changes. This application is very different from the application at the end of Chapter 4, which stored names from everyone in the same database. Because this application relies on the session object, only the names entered in this browser at this time will appear. That session identifier will vanish when the user quits their browser because the session cookie will be deleted, and those names will no longer be accessible.

The session object builds on the cookie functionality described in the previous section, but Rails takes care of all the cookie handling. For simple applications, where you’re just going to store something small in the session, you now know everything you need to know and can skip ahead if you’d like.

There are, of course, more details, more things you can tweak. First of all, you can turn sessions off if you have an application that doesn’t need them and want a little speed boost. Just add session :off at the top of controller classes or, for the whole application, in app/controllers/application.rb. (You can turn individual controllers back on with session :on, and the documentation for ActionController::SessionManagement shows many more options for controlling when sessions are used.)

Just as with cookies, you can limit the use of sessions to secure HTTPS connections. To do so, just start off with session :session_secure => true. Sessions will stop working over regular HTTP connections and only work when HTTPS is in use.

The hard question about sessions is where the data is actually stored. A key reason that HTTP is stateless is that it takes a lot of computing time to look up the state for every single transaction. Those queries can become a bottleneck, especially when you want to do things like distribute an application across multiple servers. Rails offers a number of options for solving those problems. There are only two you should consider when getting started, however. Both are illustrated in Figure 13-10.

Two models for storing data in sessions

Figure 13-10. Two models for storing data in sessions

The first, the CookieStore, is what Rails uses by default. All of the data that goes into the session object for a given session is stored directly in the cookie Rails uses to track the session. In some ways, this is extremely convenient—all the session information comes with the request, and the users’ browsers become a gigantic distributed data storage system for the Rails application. On the other hand, this limits the overall storage to 4K, the limit of cookie size, and it means that all of the session information is constantly transferred back and forth in a simple and easily decrypted hash. If you can accept the size limit and the openness, though, it’s easy.

The second approach, the ActiveRecord SessionStore, stores only an identifier token in the session cookie, and stores the actual session data in the database. To make this work, you need to make a change in the config/environment.rb file. First, find:

# Use the database for sessions instead of the cookie-based default,
# which shouldn't be used to store highly confidential information
# (create the session table with "rake db:sessions:create")
# config.action_controller.session_store = :active_record_store

and then delete the # marked in bold. You’ll also have to, as the comment notes, run rake db:sessions:create to create the necessary database support for session storage. You won’t have to change anything about the way you actually use the session object in your controllers. Rails will automatically switch over to the database approach. The only change you might notice is the removal of that 4K limit.

Even without the 4K limit, you’ll find that it’s much more efficient to store only minimal information in the session, preferably an identifier that can link to the necessary information in your application. It reduces the overhead for every request substantially.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset