session.rb 5.4 KB
Newer Older
1 2 3
require 'rack/session/abstract/id'

module ActionDispatch
4
  class Request
S
Steve Klabnik 已提交
5
    # Session is responsible for lazily loading the session from store.
6
    class Session # :nodoc:
A
Aaron Patterson 已提交
7 8
      ENV_SESSION_KEY         = Rack::RACK_SESSION # :nodoc:
      ENV_SESSION_OPTIONS_KEY = Rack::RACK_SESSION_OPTIONS # :nodoc:
9

10 11
      # Singleton object used to determine if an optional param wasn't specified
      Unspecified = Object.new
12 13
      
      # Creates a session hash, merging the properties of the previous session if any
14 15 16
      def self.create(store, req, default_options)
        session_was = find req
        session     = Request::Session.new(store, req)
17 18
        session.merge! session_was if session_was

19 20
        set(req, session)
        Options.set(req, Request::Session::Options.new(store, default_options))
21 22 23
        session
      end

24 25
      def self.find(req)
        req.get_header ENV_SESSION_KEY
26 27
      end

28 29
      def self.set(req, session)
        req.set_header ENV_SESSION_KEY, session
30 31 32
      end

      class Options #:nodoc:
33 34
        def self.set(req, options)
          req.set_header ENV_SESSION_OPTIONS_KEY, options
35 36
        end

37 38
        def self.find(req)
          req.get_header ENV_SESSION_OPTIONS_KEY
39 40
        end

41
        def initialize(by, default_options)
42
          @by       = by
43
          @delegate = default_options.dup
44 45 46
        end

        def [](key)
47 48 49
          @delegate[key]
        end

50
        def id(req)
51
          @delegate.fetch(:id) {
52
            @by.send(:extract_session_id, req)
53
          }
54 55 56 57 58 59 60
        end

        def []=(k,v);         @delegate[k] = v; end
        def to_hash;          @delegate.dup; end
        def values_at(*args); @delegate.values_at(*args); end
      end

61
      def initialize(by, req)
62
        @by       = by
63
        @req      = req
64 65 66 67 68
        @delegate = {}
        @loaded   = false
        @exists   = nil # we haven't checked yet
      end

69
      def id
70
        options.id(@req)
71 72
      end

73
      def options
74
        Options.find @req
75 76 77 78 79
      end

      def destroy
        clear
        options = self.options || {}
80
        @by.send(:destroy_session, @req, options.id(@req), options)
81 82

        # Load the new sid to be written with the response
83
        @loaded = false
84
        load_for_write!
85 86
      end

87 88
      # Returns value of the key stored in the session or
      # nil if the given key is not found in the session.
89 90 91 92 93
      def [](key)
        load_for_read!
        @delegate[key.to_s]
      end

94
      # Returns true if the session has the given key or false.
95 96 97 98 99 100 101
      def has_key?(key)
        load_for_read!
        @delegate.key?(key.to_s)
      end
      alias :key? :has_key?
      alias :include? :has_key?

102
      # Returns keys of the session as Array.
103 104 105 106
      def keys
        @delegate.keys
      end

107
      # Returns values of the session as Array.
108 109 110 111
      def values
        @delegate.values
      end

112
      # Writes given value to given key of the session.
113 114 115 116 117
      def []=(key, value)
        load_for_write!
        @delegate[key.to_s] = value
      end

118
      # Clears the session.
119 120 121 122 123
      def clear
        load_for_write!
        @delegate.clear
      end

124
      # Returns the session as Hash.
125 126 127 128 129
      def to_hash
        load_for_read!
        @delegate.dup.delete_if { |_,v| v.nil? }
      end

130 131 132 133 134 135 136 137 138 139
      # 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"}
140 141 142 143 144
      def update(hash)
        load_for_write!
        @delegate.update stringify_keys(hash)
      end

145
      # Deletes given key from the session.
146 147 148 149 150
      def delete(key)
        load_for_write!
        @delegate.delete key.to_s
      end

151 152 153 154 155 156 157 158 159 160 161 162 163 164
      # 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
165 166 167 168
      def fetch(key, default=Unspecified, &block)
        load_for_read!
        if default == Unspecified
          @delegate.fetch(key.to_s, &block)
D
Damien Mathieu 已提交
169
        else
170
          @delegate.fetch(key.to_s, default, &block)
D
Damien Mathieu 已提交
171 172 173
        end
      end

174 175 176 177 178 179 180 181 182 183
      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?
184
        @exists = @by.send(:session_exists?, @req)
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
      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!
212
        id, session = @by.load_session @req
213 214 215 216 217 218 219 220 221 222 223 224 225
        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