mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
3692ca5ce7
I want to decouple Rails from the rack ENV as much as possible. We should try to keep as few references to the env as possible
225 lines
5.4 KiB
Ruby
225 lines
5.4 KiB
Ruby
require 'rack/session/abstract/id'
|
||
|
||
module ActionDispatch
|
||
class Request < Rack::Request
|
||
# Session is responsible for lazily loading the session from store.
|
||
class Session # :nodoc:
|
||
ENV_SESSION_KEY = Rack::Session::Abstract::ENV_SESSION_KEY # :nodoc:
|
||
ENV_SESSION_OPTIONS_KEY = Rack::Session::Abstract::ENV_SESSION_OPTIONS_KEY # :nodoc:
|
||
|
||
# Singleton object used to determine if an optional param wasn't specified
|
||
Unspecified = Object.new
|
||
|
||
# Creates a session hash, merging the properties of the previous session if any
|
||
def self.create(store, env, default_options)
|
||
session_was = find env
|
||
session = Request::Session.new(store, env)
|
||
session.merge! session_was if session_was
|
||
|
||
set(env, session)
|
||
Options.set(env, Request::Session::Options.new(store, default_options))
|
||
session
|
||
end
|
||
|
||
def self.find(env)
|
||
env[ENV_SESSION_KEY]
|
||
end
|
||
|
||
def self.set(env, session)
|
||
env[ENV_SESSION_KEY] = session
|
||
end
|
||
|
||
class Options #:nodoc:
|
||
def self.set(env, options)
|
||
env[ENV_SESSION_OPTIONS_KEY] = options
|
||
end
|
||
|
||
def self.find(env)
|
||
env[ENV_SESSION_OPTIONS_KEY]
|
||
end
|
||
|
||
def initialize(by, default_options)
|
||
@by = by
|
||
@delegate = default_options.dup
|
||
end
|
||
|
||
def [](key)
|
||
@delegate[key]
|
||
end
|
||
|
||
def id(env)
|
||
@delegate.fetch(:id) {
|
||
@by.send(:extract_session_id, env)
|
||
}
|
||
end
|
||
|
||
def []=(k,v); @delegate[k] = v; end
|
||
def to_hash; @delegate.dup; end
|
||
def values_at(*args); @delegate.values_at(*args); end
|
||
end
|
||
|
||
def initialize(by, env)
|
||
@by = by
|
||
@env = env
|
||
@delegate = {}
|
||
@loaded = false
|
||
@exists = nil # we haven't checked yet
|
||
end
|
||
|
||
def id
|
||
options.id(@env)
|
||
end
|
||
|
||
def options
|
||
Options.find @env
|
||
end
|
||
|
||
def destroy
|
||
clear
|
||
options = self.options || {}
|
||
@by.send(:destroy_session, @env, options.id(@env), options)
|
||
|
||
# Load the new sid to be written with the response
|
||
@loaded = false
|
||
load_for_write!
|
||
end
|
||
|
||
# Returns value of the key stored in the session or
|
||
# nil if the given key is not found in the session.
|
||
def [](key)
|
||
load_for_read!
|
||
@delegate[key.to_s]
|
||
end
|
||
|
||
# Returns true if the session has the given key or false.
|
||
def has_key?(key)
|
||
load_for_read!
|
||
@delegate.key?(key.to_s)
|
||
end
|
||
alias :key? :has_key?
|
||
alias :include? :has_key?
|
||
|
||
# Returns keys of the session as Array.
|
||
def keys
|
||
@delegate.keys
|
||
end
|
||
|
||
# Returns values of the session as Array.
|
||
def values
|
||
@delegate.values
|
||
end
|
||
|
||
# Writes given value to given key of the session.
|
||
def []=(key, value)
|
||
load_for_write!
|
||
@delegate[key.to_s] = value
|
||
end
|
||
|
||
# Clears the session.
|
||
def clear
|
||
load_for_write!
|
||
@delegate.clear
|
||
end
|
||
|
||
# Returns the session as Hash.
|
||
def to_hash
|
||
load_for_read!
|
||
@delegate.dup.delete_if { |_,v| v.nil? }
|
||
end
|
||
|
||
# Updates the session with given Hash.
|
||
#
|
||
# session.to_hash
|
||
# # => {"session_id"=>"e29b9ea315edf98aad94cc78c34cc9b2"}
|
||
#
|
||
# session.update({ "foo" => "bar" })
|
||
# # => {"session_id"=>"e29b9ea315edf98aad94cc78c34cc9b2", "foo" => "bar"}
|
||
#
|
||
# session.to_hash
|
||
# # => {"session_id"=>"e29b9ea315edf98aad94cc78c34cc9b2", "foo" => "bar"}
|
||
def update(hash)
|
||
load_for_write!
|
||
@delegate.update stringify_keys(hash)
|
||
end
|
||
|
||
# Deletes given key from the session.
|
||
def delete(key)
|
||
load_for_write!
|
||
@delegate.delete key.to_s
|
||
end
|
||
|
||
# Returns value of given key from the session, or raises +KeyError+
|
||
# if can't find given key in case of not setted dafault value.
|
||
# Returns default value if specified.
|
||
#
|
||
# session.fetch(:foo)
|
||
# # => KeyError: key not found: "foo"
|
||
#
|
||
# session.fetch(:foo, :bar)
|
||
# # => :bar
|
||
#
|
||
# session.fetch(:foo) do
|
||
# :bar
|
||
# end
|
||
# # => :bar
|
||
def fetch(key, default=Unspecified, &block)
|
||
load_for_read!
|
||
if default == Unspecified
|
||
@delegate.fetch(key.to_s, &block)
|
||
else
|
||
@delegate.fetch(key.to_s, default, &block)
|
||
end
|
||
end
|
||
|
||
def inspect
|
||
if loaded?
|
||
super
|
||
else
|
||
"#<#{self.class}:0x#{(object_id << 1).to_s(16)} not yet loaded>"
|
||
end
|
||
end
|
||
|
||
def exists?
|
||
return @exists unless @exists.nil?
|
||
@exists = @by.send(:session_exists?, @env)
|
||
end
|
||
|
||
def loaded?
|
||
@loaded
|
||
end
|
||
|
||
def empty?
|
||
load_for_read!
|
||
@delegate.empty?
|
||
end
|
||
|
||
def merge!(other)
|
||
load_for_write!
|
||
@delegate.merge!(other)
|
||
end
|
||
|
||
private
|
||
|
||
def load_for_read!
|
||
load! if !loaded? && exists?
|
||
end
|
||
|
||
def load_for_write!
|
||
load! unless loaded?
|
||
end
|
||
|
||
def load!
|
||
id, session = @by.load_session @env
|
||
options[:id] = id
|
||
@delegate.replace(stringify_keys(session))
|
||
@loaded = true
|
||
end
|
||
|
||
def stringify_keys(other)
|
||
other.each_with_object({}) { |(key, value), hash|
|
||
hash[key.to_s] = value
|
||
}
|
||
end
|
||
end
|
||
end
|
||
end
|