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
- ruby (1.9.3p194)
- rack (1.4.1)
- sinatra (1.3.2)
The session hash isn’t a normal Ruby
Hash, it’s aRack::Session::Abstract::SessionHash.SessionHashactually inherits fromHash, but it overrides the[]=and[]methods, callingto_son any keys before storing and retrieving them.Extending your update example:
gives this output:
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.