diff options
Diffstat (limited to 'actionpack/lib/action_dispatch/middleware/session/abstract_store.rb')
-rw-r--r-- | actionpack/lib/action_dispatch/middleware/session/abstract_store.rb | 96 |
1 files changed, 85 insertions, 11 deletions
diff --git a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb index 3e8d64b0c6..7623a94234 100644 --- a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb @@ -12,6 +12,37 @@ module ActionDispatch ENV_SESSION_KEY = 'rack.session'.freeze ENV_SESSION_OPTIONS_KEY = 'rack.session.options'.freeze + # thin wrapper around Hash that allows us to lazily + # load session id into session_options + class OptionsHash < Hash + def initialize(by, env, default_options) + @by = by + @env = env + merge!(default_options) + @session_id_loaded = false + end + + alias_method :get_without_session_load, :[] + + def [](key) + if key == :id + load_session_id! unless has_session_id? + end + super(key) + end + + private + + def has_session_id? + get_without_session_load(:id).present? || @session_id_loaded + end + + def load_session_id! + self[:id] = @by.send(:extract_session_id, @env) + @session_id_loaded = true + end + end + class SessionHash < Hash def initialize(by, env) super() @@ -21,45 +52,71 @@ module ActionDispatch end def [](key) - load! unless @loaded + load_for_read! + super(key.to_s) + end + + def has_key?(key) + load_for_read! super(key.to_s) end def []=(key, value) - load! unless @loaded + load_for_write! super(key.to_s, value) end def to_hash + load_for_read! h = {}.replace(self) h.delete_if { |k,v| v.nil? } h end def update(hash) - load! unless @loaded + load_for_write! super(hash.stringify_keys) end def delete(key) - load! unless @loaded + load_for_write! super(key.to_s) end def inspect - load! unless @loaded + load_for_read! super end + def exists? + @by.send(:exists?, @env) + end + def loaded? @loaded end + def destroy + clear + @by.send(:destroy, @env) if @by + @env[ENV_SESSION_OPTIONS_KEY].delete(:id) if @env && @env[ENV_SESSION_OPTIONS_KEY] + @loaded = false + end + private + + def load_for_read! + load! if !loaded? && exists? + end + + def load_for_write! + load! unless loaded? + end + def load! stale_session_check! do id, session = @by.send(:load_session, @env) - (@env[ENV_SESSION_OPTIONS_KEY] ||= {})[:id] = id + @env[ENV_SESSION_OPTIONS_KEY][:id] = id replace(session.stringify_keys) @loaded = true end @@ -75,7 +132,6 @@ module ActionDispatch rescue LoadError, NameError => const_error raise ActionDispatch::Session::SessionRestoreError, "Session contains objects whose class definition isn't available.\nRemember to require the classes for all objects kept in the session.\n(Original exception: #{const_error.message} [#{const_error.class}])\n" end - retry else raise @@ -133,7 +189,7 @@ module ActionDispatch def prepare!(env) env[ENV_SESSION_KEY] = SessionHash.new(self, env) - env[ENV_SESSION_OPTIONS_KEY] = @default_options.dup + env[ENV_SESSION_OPTIONS_KEY] = OptionsHash.new(self, env, @default_options.dup) end def generate_sid @@ -145,13 +201,22 @@ module ActionDispatch end def load_session(env) - request = Rack::Request.new(env) - sid = request.cookies[@key] - sid ||= request.params[@key] unless @cookie_only + sid = current_session_id(env) sid, session = get_session(env, sid) [sid, session] end + def extract_session_id(env) + request = Rack::Request.new(env) + sid = request.cookies[@key] + sid ||= request.params[@key] unless @cookie_only + sid + end + + def current_session_id(env) + env[ENV_SESSION_OPTIONS_KEY][:id] + end + def ensure_session_key! if @key.blank? raise ArgumentError, 'A key is required to write a ' + @@ -161,6 +226,10 @@ module ActionDispatch end end + def exists?(env) + current_session_id(env).present? + end + def get_session(env, sid) raise '#get_session needs to be implemented.' end @@ -169,6 +238,11 @@ module ActionDispatch raise '#set_session needs to be implemented and should return ' << 'the value to be stored in the cookie (usually the sid)' end + + def destroy(env) + raise '#destroy needs to be implemented.' + end + end end end |