From 825f9c30e258221714434396bfc1d1e73f7b06c6 Mon Sep 17 00:00:00 2001 From: Gannon McGibbon Date: Fri, 22 Sep 2023 19:22:51 -0500 Subject: [PATCH] Allow ActiveRecordStore.session_class to be evaluated lazily Prevents Active Record from being loaded early in an initializer (where the session_class is being set. This can slow down application boot time. --- .../session/active_record_store.rb | 31 ++++++++++++--- test/session_store_test.rb | 39 +++++++++++++++++++ 2 files changed, 65 insertions(+), 5 deletions(-) create mode 100644 test/session_store_test.rb diff --git a/lib/action_dispatch/session/active_record_store.rb b/lib/action_dispatch/session/active_record_store.rb index ae21d70..1400533 100644 --- a/lib/action_dispatch/session/active_record_store.rb +++ b/lib/action_dispatch/session/active_record_store.rb @@ -39,9 +39,10 @@ module Session # feature-packed Active Record or a bare-metal high-performance SQL # store, by setting # - # ActionDispatch::Session::ActiveRecordStore.session_class = MySessionClass + # ActionDispatch::Session::ActiveRecordStore.session_class = "MySessionClass" # - # You must implement these methods: + # The class may optionally be passed as a string or proc to prevent autoloading + # code early. You must implement these methods: # # self.find_by_session_id(session_id) # initialize(hash_of_session_id_and_data, options_hash = {}) @@ -53,13 +54,33 @@ module Session # The example SqlBypass class is a generic SQL session store. You may # use it as a basis for high-performance database-specific stores. class ActiveRecordStore < ActionDispatch::Session::AbstractSecureStore - # The class used for session storage. Defaults to - # ActiveRecord::SessionStore::Session - class_attribute :session_class + class << self + # The class used for session storage. Defaults to + # ActiveRecord::SessionStore::Session + def session_class + @session_class_instance ||= case @session_class + when Proc + @session_class.call + when String + @session_class.constantize + else + @session_class + end + end + + def session_class=(session_class) + @session_class_instance = nil + @session_class = session_class + end + end SESSION_RECORD_KEY = 'rack.session.record' ENV_SESSION_OPTIONS_KEY = Rack::RACK_SESSION_OPTIONS + def session_class + self.class.session_class + end + private def get_session(request, sid) logger.silence do diff --git a/test/session_store_test.rb b/test/session_store_test.rb new file mode 100644 index 0000000..48f6fdf --- /dev/null +++ b/test/session_store_test.rb @@ -0,0 +1,39 @@ +require "helper" +require "action_dispatch/session/active_record_store" + +module ActionDispatch + module Session + class ActiveRecordStoreTest < ActiveSupport::TestCase + + class Session < ActiveRecord::SessionStore::Session; end + + def test_session_class_as_string + with_session_class("ActionDispatch::Session::ActiveRecordStoreTest::Session") do + assert_equal(Session, ActiveRecordStore.session_class) + end + end + + def test_session_class_as_proc + with_session_class(proc { Session }) do + assert_equal(Session, ActiveRecordStore.session_class) + end + end + + def test_session_class_as_class + with_session_class(Session) do + assert_equal(Session, ActiveRecordStore.session_class) + end + end + + private + + def with_session_class(klass) + old_klass = ActiveRecordStore.session_class + ActiveRecordStore.session_class = klass + yield + ensure + ActiveRecordStore.session_class = old_klass + end + end + end +end