Recipe 4.15. Tracking Information with SessionsProblemYou want to maintain state across several web pages of an application without using a database. SolutionUse Rails's built-in sessions to maintain state across multiple pages of a web application, such as the state of an online quiz.
Create an online quiz that consists of a sequence of questions, one per page. As a
Create a
Quiz
Controller
that includes a data structure to store the questions, optional answers, and correct answers for each question. The controller contains
app/controllers/quiz_controller.rb :
class QuizController < ApplicationController
@@quiz = [
{ :question => "What's the square root of 9?",
:options => ['2','3','4'],
:answer => "3" },
{ :question => "What's the square root of 4?",
:options => ['16','2','8'],
:answer => '16' },
{ :question => "How many feet in a mile?",
:options => ['90','130','5,280','23,890'],
:answer => '5,280' },
{ :question => "What's the total area of irrigated land in Nepal?",
:options => ['742 sq km','11,350 sq km','5,000 sq km',
'none of the above'],
:answer => '11,350 sq km' },
]
def index
if session[:count].nil?
session[:count] = 0
end
@step = @@quiz[session[:count]]
end
def check
session[:correct] = 0
if params[:answer] == @@quiz[session[:count]][:answer]
session[:correct] += 1
end
session[:count] += 1
@step = @@quiz[session[:count]]
if @step.nil?
redirect_to :action => "results"
else
redirect_to :action => "index"
end
end
def results
@correct = session[:correct]
@possible = @@quiz.length
end
def start_over
reset_session
redirect_to :action => "index"
end
end
Create a template to display each question along with its optional answers:
app/views/quiz/index.
<h1>Quiz</h1>
<p><%= @step[:question] %></p>
<% form_tag :action => "check" do %>
<% for answer in @step[:options] %>
<%= radio_button_tag("answer", answer, checked = false) %>
<%= answer %>;
<% end %>
<%= submit_tag "Answer" %>
<% end %>
At the end of the quiz, the following view displays the total score along with a link prompting to try again: app/views/quiz/results.rhtml : <h1>Quiz</h1> <p><strong>Results:</strong> You got <%= @correct %> out of <%= @possible %>!</p> <%= link_to "Try again?", :action => "start_over" %> Discussion
The Web is
stateless
, which means that each request from a browser carries all the information that the server needs to make the request. The server never says, "Oh, yes, I remember that your current score is 4 out of 5." Being stateless makes it much easier to write web servers but harder to write complex applications, which often need to remember what went before: they need to remember which questions you've
This problem is
In the case of the quiz, the controller checks the answers to each question and maintains a running total, storing it in the session hash with the :correct key. Another key in the session hash is used to keep track of the current question. This number is used to access questions in the @@quiz class variable, which stores each question, its possible answers, and the correct answer in an array. Each question element consists of a hash containing all the information needed to display that question in the view.
The index view displays a form for each question and submits the user's input to the
check
action of the controller. Using
session[:count]
, the
check
action verifies the answer and
When the question count fails to retrieve an elementor questionfrom the @@quiz array, the quiz is over, and the results view is rendered. The total correct is pulled from the session hash and displayed with the total number of questions, which is determined from the length of the quiz array.
A quiz such as this lends itself reasonably well to the convenience of session storage. Be aware that sessions are
Figure 4-3 shows the four steps of the session-driven online quiz. Figure 4-3. An online quiz saving state with sessions
Rails session support is on by default. As the solution
class NewsController < ActionController::Base session :off, :only => "display" end To turn session support off for your entire application, pass :off to the session method within your ApplicationController definition: class ApplicationController < ActionController::Base session :off end See Also
|