Replacing ActiveRecordStore::Session with a custom model

Has anyone managed to replace ActiveRecordStore::Session with their
own model?

In the source (http://dev.rubyonrails.org/browser/trunk/actionpack/lib/
action_controller/session/active_record_store.rb) it says you can
override the default by setting
CGI::Session::ActiveRecordStore.session_class = MySessionClass

I have tried doing this in a number of ways but I get all kinds of
weird errors, as varied as the methods I’ve tried.

Before you shoot me down for trying to do something I shouldn’t let me
explain why I want to do this.

I’d like a session model that looks something like:

class Session < ActiveRecord::Base
has_many :product_viewings, :dependent => :destroy
has_many :products, :through => :product_viewings, :limit =>
10, :include => :image

belongs_to :order
belongs_to :user
end

In my controller I would like to do something like

session.model.user = @user

and

@recently_viewed_products = session.model.products

Because I cannot replace ActiveRecordStore::Session with my session
class above I have to do instead:

session.model.user_id = @user.id

and

@recently_viewed_products =
Session.find_by_session_id(session.session_id).products

I don’t think it’s unreasonable to want to treat session.model as a
first class ActiveRecord model, anything else just seems a bit half-
baked.

Any ideas on how I could make this happen would be greatly
appreciated.

Paul Odeon

Ok I finally nailed it…

I found that using CGI::Session::ActiveRecordStore.session_class =
Session worked in production mode but not in dev, I could also make it
work in dev but only by setting config.cache_classes = true

In dev it would work on the first page load but crash with a stack too
deep error on the second and subsequent attempt.

Definitely a dependencies issue then…

All the classes are reloaded on every request, including our custom
Session class. However, CGI::Session::ActiveRecordStore.session_class
is only set on application initialization, and the session_class
variable is left holding a stale version of the Session class. What we
need is a way to update ActiveRecordStore with a fresh Session class
after every request, the magic line of code to do that is at the end
of this post.

So to create your own session model with all the ActiveRecord goodness
follow these steps:

  1. config/initializers/session.rb
    CGI::Session::ActiveRecordStore.session_class = Session

  2. app/models/session.rb
    class Session < CGI::Session::ActiveRecordStore::Session
    has_many :product_viewings, :dependent => :destroy
    has_many :products, :through => :product_viewings, :limit =>
    10, :include => :image
    #Or whatever ActiveRecord stuff you like
    end

  3. config/environment.rb
    Rails::Initializer.run do |config|
    config.action_controller.session_store = :active_record_store
    config.to_prepare { CGI::Session::ActiveRecordStore.session_class =
    Session if ENV[‘RAILS_ENV’] == ‘development’ } # This is the one
    end

config.to_prepare executes the block it is passed before each request
in rails. In it, we pass the freshly reloaded class to the Session
store but only in dev.

I hope this helps someone struggling with the same problem…

Cheers

On May 21, 12:27 pm, “[email protected]

One small glitch with the above solution

Any of the script/* and rake scripts seem to fail with some sort of
dependency error

This is a bug (http://dev.rubyonrails.org/ticket/10520) and is
apparently fixed in 2.0.2, although that’s what I am using and I still
get the error.

As described in the link above, I put the following above the
config.to_prepare block and everything worked perfectly.

#fixes rails bug described here:
http://dev.rubyonrails.org/ticket/10520
#this monkey patch should be removed when problem is fixed in rails
module Rails
class Configuration
def to_prepare(&callback)
after_initialize do
require ‘dispatcher’ unless defined?(::Dispatcher)
Dispatcher.to_prepare(&callback)
end
end
end
end

On May 22, 12:18 am, “[email protected]