How do I set/get session vars in a Rack app?
session
is a method that is part of some web frameworks, for example Sinatra and Rails both have session
methods. Plain rack
applications don’t have a session
method, unless you add one yourself.
The session hash is stored in the rack env hash under the key rack.session
, so you can access it like this (assuming you’ve named the rack environment to your app env
):
env['rack.session'][:msg]="Hello Rack"
Alternatively, you could use Rack’s built in request
object, like this:
request = Rack::Request.new(env)
request.session[:msg]="Hello Rack"
How do you store and access session data in a rack based application
That's quite an open question. Do you want to store data server side or client side? In the first case, you can store data in cookies:
def call
status, headers, body = @app.call(env)
response = Rack::Response.new body, status, headers
response.set_cookie("foo", {:value => "bar", :path => "/", :expires => Time.now+24*60*60})
response.finish
end
In the latter case, you you'll probably want to store it in a database (you can use ActiveRecord or other object mappers), or just a plain text file.
TL;DR: Look at frameworks for creating and storing sessions.
Sinatra and session variables which are not being set
If you're using Shotgun, add the following line to the configure block:
set :session_secret, "My session secret"
To quote from rkh, Sinatra's current maintainer:
[Shotgun] will restart the server on every request, thereby regenerate the session secret and thus invalidate your sessions. This has been fixed in current master. Simple fix: set the session_secret option.
NOTE: This fix doesn't work if you use Rack::Session::Pool
How to set session[:expires] in rack session (Sinatra)
I tried to do this via an after
filter in Sinatra but it didn't work, I guess it sets the session after after
filters have run, so I knocked up a quick Rack filter and it appears to work.
require 'sinatra'
class SessionExpiryModifier
def initialize(app,options={})
@app,@options = app,options
end
def call(env)
warn env["rack.session.options"].inspect
t = Time.now.to_i.even? ? 10 : 60
env["rack.session.options"].merge! :expire_after => 60 * 60 * t
@app.call env
end
end
configure do
use Rack::Session::Cookie,
:expire_after => 60*60*3,
:secret => 'xxxx' * 10
use SessionExpiryModifier
end
get "/" do
session[:usr] = Time.now
env["rack.session.options"].inspect
end
However, that makes it a lot harder to get a conditional from the Sinatra app into the Rack filter to decide on which branch to take, but that depends on what your condition is. Perhaps inject something into the headers that the filter can read to make the decision.
How do I access the Rack environment from within Rails?
I'm pretty sure you can use the Rack::Request
object for passing request-scope variables:
# middleware:
def call(env)
request = Rack::Request.new(env) # no matter how many times you do 'new' you always get the same object
request[:foo] = 'bar'
@app.call(env)
end
# Controller:
def index
if params[:foo] == 'bar'
...
end
end
Alternatively, you can get at that "env
" object directly:
# middleware:
def call(env)
env['foo'] = 'bar'
@app.call(env)
end
# controller:
def index
if request.env['foo'] == 'bar'
...
end
end
Sinatra + Rack::Test + Rspec2 - Using Sessions?
Rack doesn't support passing in sessions via the request anymore (Rack >= v1.0). Read this post for more detailed information on that.
The best way to set a session variable in your app is to call an action inside of your application that will set the session variable. For instance, if you have a route inside your app that sets a session variable like this:
post '/set_sess_var/:id'
session[:user_id] = params[:id]
end
Let's pretend there's another route that you actually wanted to test which is using the session variable like this:
get '/get_user_attributes'
User.find(session[:user_id]).attributes
end
Then in your tests, you should first call the route which sets the session, then go onto another route which uses it. Here is rspec notation, since that is what I use for testing:
it "should print out user attributes" do
user_id = 1
post '/set_sess_var/' + user_id
get '/get_user_attributes'
last_response.body.should == User.find(user_id).attributes
end
If you were going to be using the route frequently in your tests, then you could write a method to accomplish this in your test file (if you're using Rspec, then this method could go in your spec_helper.rb or in your controller_spec.rb file):
def set_session_var(user_id)
post '/set_sess_var/' + user_id
end
and then call it in your tests when you needed it to be set:
it "should print out user attributes" do
set_session_var(1)
get '/get_user_attributes'
last_response.body.should == User.find(1).attributes
end
Related Topics
Edit Each Line in a File in Ruby
Attr_Accessor Strongly Typed Ruby on Rails
Same Instance Variable for All Actions of a Controller
How to Replace the Unicode Gem on Ruby 1.9
Subtract N Hours from a Datetime in Ruby
How to Check If a Class Is Defined
How to Sort a String's Characters Alphabetically
How to Call an Older Version of a Gem from the Commandline
Ruby on Rails Advanced JSON Serialization
How to Get Rspec-2 to Give the Full Trace Associated with a Test Failure