Philip Nelson
Philip Nelson

Reputation: 859

Sinatra/Rack session.fetch producing unexpected results

I was writing a quick helper in Sinatra for redirect_to_next, where I redirect to the path provided by session[:next] if it exists, or a default.

In Sinatra, session really provided by Rack, and by spec, it is said to provide a hash like interface for fetch. I wrote the following error helper to explain my problem.

error 401 do
  session[:next] = request.path
  puts "get   #{session[:next]}"
  puts "fetch #{session.fetch(:next, '/')}"
  redirect "/login"
end

When I attempt to access /settings when not logged in, I halt 401 which runs the above code. This is what it prints to my terminal:

get   /settings
fetch /

:next exists as a key, so why is it giving me the default as if it does not?

Update

This minimal example shows the same behavior.

require 'sinatra'

set :sessions, true

get '/' do
  session[:testing] = "hello"
  puts "get   #{session[:testing]}"
  puts "fetch #{session.fetch(:testing, 'goodbye')}"
end

Logs

[2012-04-29 14:11:51] INFO  WEBrick::HTTPServer#start: pid=1954 port=9292
get   hello
fetch goodbye
10.0.2.2 - - [29/Apr/2012 14:11:54] "GET / HTTP/1.1" 200 - 0.0485

Software

Upvotes: 2

Views: 347

Answers (1)

matt
matt

Reputation: 79783

The session hash isn’t a normal Ruby Hash, it’s a Rack::Session::Abstract::SessionHash. SessionHash actually inherits from Hash, but it overrides the []= and [] methods, calling to_s on any keys before storing and retrieving them.

Extending your update example:

require 'sinatra'

set :sessions, true

get '/' do
  session[:testing] = "hello"
  puts "get               #{session[:testing]}"
  puts "fetch             #{session.fetch(:testing, 'goodbye')}"
  puts "fetch with string #{session.fetch(:testing.to_s, 'goodbye')}"
end

gives this output:

get               hello
fetch             goodbye
fetch with string hello

When you use Hash#fetch, passing a symbol, the method gets dispatched directly to the parent hash, without being converted to a string, and so the matching key isn’t found.

So, always use Strings as keys in your sessions and everything should work.

Upvotes: 3

Related Questions