Updated README.rdoc again
[feedcatcher.git] / vendor / rails / actionpack / lib / action_controller / vendor / rack-1.0 / rack / session / pool.rb
1 # AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
2 # THANKS:
3 # apeiros, for session id generation, expiry setup, and threadiness
4 # sergio, threadiness and bugreps
5
6 require 'rack/session/abstract/id'
7 require 'thread'
8
9 module Rack
10 module Session
11 # Rack::Session::Pool provides simple cookie based session management.
12 # Session data is stored in a hash held by @pool.
13 # In the context of a multithreaded environment, sessions being
14 # committed to the pool is done in a merging manner.
15 #
16 # The :drop option is available in rack.session.options if you with to
17 # explicitly remove the session from the session cache.
18 #
19 # Example:
20 # myapp = MyRackApp.new
21 # sessioned = Rack::Session::Pool.new(myapp,
22 # :domain => 'foo.com',
23 # :expire_after => 2592000
24 # )
25 # Rack::Handler::WEBrick.run sessioned
26
27 class Pool < Abstract::ID
28 attr_reader :mutex, :pool
29 DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge :drop => false
30
31 def initialize(app, options={})
32 super
33 @pool = Hash.new
34 @mutex = Mutex.new
35 end
36
37 def generate_sid
38 loop do
39 sid = super
40 break sid unless @pool.key? sid
41 end
42 end
43
44 def get_session(env, sid)
45 session = @pool[sid] if sid
46 @mutex.lock if env['rack.multithread']
47 unless sid and session
48 env['rack.errors'].puts("Session '#{sid.inspect}' not found, initializing...") if $VERBOSE and not sid.nil?
49 session = {}
50 sid = generate_sid
51 @pool.store sid, session
52 end
53 session.instance_variable_set('@old', {}.merge(session))
54 return [sid, session]
55 ensure
56 @mutex.unlock if env['rack.multithread']
57 end
58
59 def set_session(env, session_id, new_session, options)
60 @mutex.lock if env['rack.multithread']
61 session = @pool[session_id]
62 if options[:renew] or options[:drop]
63 @pool.delete session_id
64 return false if options[:drop]
65 session_id = generate_sid
66 @pool.store session_id, 0
67 end
68 old_session = new_session.instance_variable_get('@old') || {}
69 session = merge_sessions session_id, old_session, new_session, session
70 @pool.store session_id, session
71 return session_id
72 rescue
73 warn "#{new_session.inspect} has been lost."
74 warn $!.inspect
75 ensure
76 @mutex.unlock if env['rack.multithread']
77 end
78
79 private
80
81 def merge_sessions sid, old, new, cur=nil
82 cur ||= {}
83 unless Hash === old and Hash === new
84 warn 'Bad old or new sessions provided.'
85 return cur
86 end
87
88 delete = old.keys - new.keys
89 warn "//@#{sid}: dropping #{delete*','}" if $DEBUG and not delete.empty?
90 delete.each{|k| cur.delete k }
91
92 update = new.keys.select{|k| new[k] != old[k] }
93 warn "//@#{sid}: updating #{update*','}" if $DEBUG and not update.empty?
94 update.each{|k| cur[k] = new[k] }
95
96 cur
97 end
98 end
99 end
100 end