How to Set/Get Session Vars in a Rack App

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



Leave a reply



Submit