20

I'm working on an application that needs to use session id information. My session is stored in cookies. The problem I have is that my session is not immediately available to the controller when a user comes to the site for the first time. I think I may be missing something about how sessions are initialized in Rails. But I'm positve about the fact that the session is not loaded because this is the output of session.inspect:

#<Rack::Session::Abstract::SessionHash:0x15cb970 not yet loaded>

Here is how to reproduce the problem with Rails 3.2.11 and ruby 1.9.3:

Create a new application with a test controller:

rails new my_app
cd my_app/
rails g controller test
rm app/assets/javascripts/test.js.coffee
touch app/views/test/index.html.erb

Try to get the session id in that controller:

class TestController < ApplicationController
  def index
    puts session[:session_id]
    puts session.inspect
  end
end

Add the needed routes:

MyApp::Application.routes.draw do
  resources :test
end

Then access the application and see what it does:

rails server

got to: http://localhost:3000/test

That is the output in the console:

#<Rack::Session::Abstract::SessionHash:0x3fd10f50eea0 not yet loaded>

Then again http://localhost:3000/test and this time we have a session:

400706c0b3d95a5a1e56521e455075ac
{"session_id"=>"400706c0b3d95a5a1e56521e455075ac", "_csrf_token"=>"Euaign8Ptpj/o/8/ucBFMgxGtiH7goKxkxeGctumyGQ="}
joscas
  • 7,474
  • 5
  • 39
  • 59
  • 1
    Try to force session loading: [http://stackoverflow.com/questions/1035933/rails-2-3-session][1] [1]: http://stackoverflow.com/questions/1035933/rails-2-3-session – MiGro Feb 02 '13 at 19:23
  • That is a good hint. I tried a couple of te methods there without success so far. – joscas Feb 02 '13 at 20:06
  • I just tried it in a clean project, and my sessions is always loaded. I dont seem to reproduce your problem. You could try to give us more code to look at. – rik.vanmechelen Feb 03 '13 at 03:04
  • @rik.vanmechelen Thanks for your interest in the problem. I've added the steps to reproduce the situation to my original question. – joscas Feb 03 '13 at 19:04
  • 1
    @joscas ok, i get the error, but only the very first time i run rails server, if i quit the server and start it again i do not get the error anymore. I am going to try it in a production environment and see what happens there. – rik.vanmechelen Feb 03 '13 at 21:38
  • Please remember to clean cookies every time. Maybe this is the reason why the second time it works fine. – joscas Feb 03 '13 at 23:04
  • 1
    You may want to track [this issue](https://github.com/rails/rails/issues/10813). – Franklin Yu Jun 28 '17 at 04:19

6 Answers6

23

I found a way to force initialization of the session. Accessing the session apparently does not force initialization but writing into the session does. What I do in my controller is this now:

class MyController < ApplicationController
  protect_from_forgery
  def index
    session["init"] = true
    do_stuff
  end
end

Still I'm not sure if this should be considered normal behavior in Rails. It doesn't look right to me having to write into the session to force initialization. Reading should be enough.

joscas
  • 7,474
  • 5
  • 39
  • 59
9

I agree with @joscas answer but instead of writing a value, I'd delete it as to not have redundant data.

class MyController < ApplicationController
  protect_from_forgery
  def index
    session.delete 'init'
    do_stuff
  end
end

The session is loaded this way too.

Note: Make sure you don't use the key to be deleted in your application.

Lazarus Lazaridis
  • 5,803
  • 2
  • 21
  • 35
7

Here's some relevant code from ActionDispatch::Session:

 def [](key)
    load_for_read!
    @delegate[key.to_s]
  end

  private

  def load_for_read!
    load! if !loaded? && exists?
  end

Which implies that the session object will be loaded as soon as you access any value by its key via [].

zetetic
  • 47,184
  • 10
  • 111
  • 119
  • That makes sense but then doing something like: session["session_id"] should always return a value because if the session isn't loaded yet it will load it at this moment and this is not the case for me. – joscas Feb 02 '13 at 21:02
  • the problem you're having is really weird to begin with. Have you tried what you want to do in a empty project, so you can be sure it's not cause of some other gems that the loading fails. and what versions are you using (ruby and rails) – rik.vanmechelen Feb 02 '13 at 21:10
  • Rails 3.2.11 and ruby 1.9.3. The session actually does load but not the first time when I try to access it. I will try to do the test without gems but I'm using very few stuff anyway: 'therubyracer', 'couchrest', 'useragent' and 'figaro' – joscas Feb 02 '13 at 22:07
0

I don't really understand your question. If you require a user to register or sign in before being able to access the site there should be no problem. When creating a user his information is immediately stored in a cookie. For example:

User controller: (registering is done through users#new)

def create
   @user = User.new(params[:user])
   if @user.save
     cookies.permanent[:remember_token] = user.remember_token
     redirect_to root_path, notice: "Thank you for registering!"
   else
     render :new
   end
 end

Sessions controller: (signing in is done through sessions#new)

 def create
  user = User.find_by_email(params[:session][:email].downcase)

  if user && user.authenticate(params[:session][:password])
    cookies.permanent[:remember_token] = user.remember_token
    redirect_to root_path, notice: "Logged in."
  else
    flash.now.alert = "Email or password is incorrect."
    render :new
 end
end
Peter de Ridder
  • 2,379
  • 19
  • 18
0

So the problem probably boils down to the fact that the cookie that stores session_id wasn't created yet at the point where you tried to access the session.

When you read session object, Rails calls a private method session.load_for_read!, but, as its name suggests, it only loads session for reading and doesn't instantiates the session if it simply doesn't exist.

On the other hand, calling session.merge!({}) (for example) forces session instantiation.

Alexis
  • 4,317
  • 1
  • 25
  • 34
0

Yo! @zetetic's answer above made me realize that we can just do

session.send(:load!)

and it'll load the session .

Jon Sullivan
  • 193
  • 2
  • 6