nofxx-tokyo_store 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -8,8 +8,13 @@ Demo:: http://github.com/nofxx/tokyo_webapps under rails/tokyo_store
8
8
 
9
9
  == Require
10
10
 
11
- Tokyo Cabinet/Tyrant:: http://tokyocabinet.sourceforge.net
12
- Rufus Tokyo:: http://github.com/jmettraux/rufus-tokyo
11
+ Tokyo Cabinet and/or Tyrant:: http://tokyocabinet.sourceforge.net
12
+
13
+ Choose an adapter:
14
+
15
+ Tyrant - Pure Ruby:: http://tokyocabinet.sourceforge.net/tyrantrubydoc
16
+ Cabinet - C Bindings:: http://tokyocabinet.sourceforge.net/rubydoc
17
+ RufusTyrant - Rufus FFI:: http://github.com/jmettraux/rufus-tokyo
13
18
 
14
19
 
15
20
  == Install
@@ -19,12 +24,17 @@ Rufus Tokyo:: http://github.com/jmettraux/rufus-tokyo
19
24
  Rails (enviroment.rb)
20
25
 
21
26
  config.gem 'nofxx-tokyo_store', :lib => 'tokyo_store'
22
- ActionController::Base.session_store = Rack::Session::Tokyo
27
+ ActionController::Base.session_store = Rack::Session::<ADAPTER (Tyrant, Cabinet or RufusTyrant)>
28
+
23
29
 
24
- Start Tyrant
30
+ == Tyrant
31
+
32
+ Start the server if you are using a Tyrant based adapter:
25
33
 
26
34
  ttserver data.tch
27
35
 
36
+ Some rake tasks: http://gist.github.com/147413
37
+
28
38
 
29
39
  Have fun!
30
40
 
@@ -33,7 +43,7 @@ Have fun!
33
43
 
34
44
  Mikio Hirabayashi:: tokyo products
35
45
  John Mettraux:: rufus-tokyo
36
- Luca Guidi:: redis-store - Rack code stolen from here =D
46
+ Luca Guidi:: redis-store
37
47
 
38
48
 
39
49
  == Copyright
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.1
1
+ 0.3.0
@@ -26,6 +26,13 @@ TEST = {
26
26
  }
27
27
  bar = "_" * 45
28
28
 
29
+ puts "\n#{bar} SET"
30
+ Benchmark.bmbm do |b|
31
+ TEST.each_pair do |n,s|
32
+ b.report(n) { T.times { Rack::MockRequest.new(s).get("/") }}
33
+ end
34
+ end
35
+
29
36
  puts "\n#{bar} GET"
30
37
  Benchmark.bmbm do |b|
31
38
  TEST.each_pair do |n,s|
@@ -37,13 +44,6 @@ Benchmark.bmbm do |b|
37
44
  end
38
45
  end
39
46
 
40
- puts "\n#{bar} SET"
41
- Benchmark.bmbm do |b|
42
- TEST.each_pair do |n,s|
43
- b.report(n) { T.times { Rack::MockRequest.new(s).get("/") }}
44
- end
45
- end
46
-
47
47
  puts "\n#{bar} EXIST"
48
48
  Benchmark.bmbm do |b|
49
49
  TEST.each_pair do |n,s|
@@ -0,0 +1,70 @@
1
+ require 'tokyocabinet'
2
+ module Rack
3
+ module Session
4
+ class Cabinet < Abstract::ID
5
+ include TokyoCabinet
6
+ attr_reader :mutex, :pool
7
+ DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge :cabinet_file => "/tmp/session.tch"
8
+
9
+ def initialize(app, options = {})
10
+ super
11
+ @mutex = Mutex.new
12
+ @db = options[:cabinet_file] || @default_options[:cabinet_file]
13
+ tokyo_connect
14
+ end
15
+
16
+ private
17
+ def tokyo_connect
18
+ @pool = HDB.new
19
+ unless @pool.open(@db, HDB::OREADER | HDB::OWRITER | HDB::OCREAT)
20
+ warn "Can't open db file '#{@db}', #{@pool.errmsg}."
21
+ end
22
+ end
23
+
24
+ def get_session(env, sid)
25
+ session = Marshal.load(@pool.get(sid)) rescue session if sid && session = @pool.get(sid)
26
+ @mutex.lock if env['rack.multithread']
27
+ unless sid && session
28
+ env['rack.errors'].puts("Session '#{sid.inspect}' not found, initializing...") if $VERBOSE and not sid.nil?
29
+ session = {}
30
+ sid = generate_sid
31
+ ret = @pool.put(sid, Marshal.dump(session))
32
+ raise "Session collision on '#{sid.inspect}'" unless ret
33
+ end
34
+ return [sid, session]
35
+ rescue Rufus::Tokyo::TokyoError => e
36
+ return [nil, {}]
37
+ ensure
38
+ @mutex.unlock if env['rack.multithread']
39
+ end
40
+
41
+ def set_session(env, sid, new_session, options)
42
+ @mutex.lock if env['rack.multithread']
43
+ session = Marshal.load(session) rescue session if session = @pool.get(sid)
44
+ if options[:renew] || options[:drop]
45
+ @pool.out(sid)
46
+ return false if options[:drop]
47
+ sid = generate_sid
48
+ @pool.put(sid, "")
49
+ end
50
+ @pool.put(sid, options && options[:raw] ? new_session : Marshal.dump(new_session))
51
+ return sid
52
+ rescue Rufus::Tokyo::TokyoError => e
53
+ warn "#{self} is unable to find server, error: #{e}"
54
+ warn $!.inspect
55
+ ensure
56
+ @mutex.unlock if env['rack.multithread']
57
+ end
58
+
59
+ def generate_sid
60
+ loop do
61
+ sid = super
62
+ break sid unless @pool.get(sid)
63
+ end
64
+ end
65
+
66
+ end
67
+
68
+ end
69
+
70
+ end
@@ -1,6 +1,8 @@
1
+ require 'rufus/tokyo/tyrant'
2
+
1
3
  module Rack
2
4
  module Session
3
- class Tokyo < Abstract::ID
5
+ class RufusTyrant < Abstract::ID
4
6
  attr_reader :mutex, :pool
5
7
  DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge :tyrant_server => "localhost:1978"
6
8
 
@@ -11,22 +13,22 @@ module Rack
11
13
  # connecting & closing on each get and put
12
14
  # not sure if this is the best option, but otherwise it'll keep
13
15
  # opening connections until tyrant freezes... =/
14
- # tokyo_connect
16
+ tokyo_connect
15
17
  end
16
18
 
17
19
  private
18
20
  def tokyo_connect
19
21
  begin
20
- @pool = Rufus::Tokyo::Tyrant.new(@host, @port.to_i)
22
+ @pool ||= Rufus::Tokyo::Tyrant.new(@host, @port.to_i)
21
23
  rescue Rufus::Tokyo::TokyoError => e
22
24
  warn "Can't connect to Tyrant #{e}"
23
25
  end
24
26
  end
25
27
 
26
28
  def get_session(env, sid)
27
- tokyo_connect
28
- @mutex.lock if env['rack.multithread']
29
+ # tokyo_connect
29
30
  session = Marshal.load(@pool[sid]) rescue session if sid && session = @pool[sid]
31
+ @mutex.lock if env['rack.multithread']
30
32
  unless sid && session
31
33
  env['rack.errors'].puts("Session '#{sid.inspect}' not found, initializing...") if $VERBOSE and not sid.nil?
32
34
  session = {}
@@ -37,14 +39,14 @@ module Rack
37
39
  session.instance_variable_set('@old', {}.merge(session))
38
40
  return [sid, session]
39
41
  rescue Rufus::Tokyo::TokyoError => e
40
- session = {}
42
+ return [nil, {}]
41
43
  ensure
42
44
  @mutex.unlock if env['rack.multithread']
43
- @pool.close
45
+ # @pool.close
44
46
  end
45
47
 
46
48
  def set_session(env, sid, new_session, options)
47
- tokyo_connect
49
+ # tokyo_connect
48
50
  @mutex.lock if env['rack.multithread']
49
51
  session = Marshal.load(session) rescue session if session = @pool[sid]
50
52
  if options[:renew] || options[:drop]
@@ -62,7 +64,7 @@ module Rack
62
64
  warn $!.inspect
63
65
  ensure
64
66
  @mutex.unlock if env['rack.multithread']
65
- @pool.close
67
+ # @pool.close
66
68
  end
67
69
 
68
70
  def generate_sid
@@ -0,0 +1,70 @@
1
+ require 'tokyotyrant'
2
+ module Rack
3
+ module Session
4
+ class Tyrant < Abstract::ID
5
+ include TokyoTyrant
6
+ attr_reader :mutex, :pool
7
+ DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge :tyrant_server => "localhost:1978"
8
+
9
+ def initialize(app, options = {})
10
+ super
11
+ @mutex = Mutex.new
12
+ @host, @port = *(options[:tyrant_server] || @default_options[:tyrant_server]).split(":")
13
+ tokyo_connect
14
+ end
15
+
16
+ private
17
+ def tokyo_connect
18
+ @pool = RDB.new
19
+ unless @pool.open(@host, @port.to_i)
20
+ warn "Can't connect to Tyrant #{@host}:#{@port}"
21
+ end
22
+ end
23
+
24
+ def get_session(env, sid)
25
+ session = Marshal.load(@pool.get(sid)) rescue session if sid && session = @pool.get(sid)
26
+ @mutex.lock if env['rack.multithread']
27
+ unless sid && session
28
+ env['rack.errors'].puts("Session '#{sid.inspect}' not found, initializing...") if $VERBOSE and not sid.nil?
29
+ session = {}
30
+ sid = generate_sid
31
+ ret = @pool.put(sid, Marshal.dump(session))
32
+ raise "Session collision on '#{sid.inspect}'" unless ret
33
+ end
34
+ return [sid, session]
35
+ rescue Rufus::Tokyo::TokyoError => e
36
+ return [nil, {}]
37
+ ensure
38
+ @mutex.unlock if env['rack.multithread']
39
+ end
40
+
41
+ def set_session(env, sid, new_session, options)
42
+ @mutex.lock if env['rack.multithread']
43
+ session = Marshal.load(session) rescue session if session = @pool.get(sid)
44
+ if options[:renew] || options[:drop]
45
+ @pool.delete sid
46
+ return false if options[:drop]
47
+ sid = generate_sid
48
+ @pool.put(sid, "")
49
+ end
50
+ @pool.put(sid, options && options[:raw] ? new_session : Marshal.dump(new_session))
51
+ return sid
52
+ rescue Rufus::Tokyo::TokyoError => e
53
+ warn "#{self} is unable to find server, error: #{e}"
54
+ warn $!.inspect
55
+ ensure
56
+ @mutex.unlock if env['rack.multithread']
57
+ end
58
+
59
+ def generate_sid
60
+ loop do
61
+ sid = super
62
+ break sid unless @pool.get(sid)
63
+ end
64
+ end
65
+
66
+ end
67
+
68
+ end
69
+
70
+ end
@@ -1,9 +1,10 @@
1
- require 'rufus/tokyo/tyrant'
2
1
 
3
2
  # Rack Session
4
3
  if defined?(Rack::Session)
5
4
  require "rack/session/abstract/id"
6
- require "rack/session/tokyo"
5
+ require "rack/session/tyrant"
6
+ #require "rack/session/cabinet"
7
+ #require "rack/session/rufus_tyrant"
7
8
  end
8
9
 
9
10
  # # Cache store
@@ -0,0 +1,242 @@
1
+ # Code from http://github.com/jodosha/redis-store
2
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
3
+
4
+ module Rack
5
+ module Session
6
+ describe "Rack::Session::Cabinet" do
7
+ before(:each) do
8
+ @session_key = Rack::Session::Cabinet::DEFAULT_OPTIONS[:key]
9
+ @session_match = /#{@session_key}=[0-9a-fA-F]+;/
10
+ @incrementor = lambda do |env|
11
+ env["rack.session"]["counter"] ||= 0
12
+ env["rack.session"]["counter"] += 1
13
+ Rack::Response.new(env["rack.session"].inspect).to_a
14
+ end
15
+ @drop_session = proc do |env|
16
+ env['rack.session.options'][:drop] = true
17
+ @incrementor.call(env)
18
+ end
19
+ @renew_session = proc do |env|
20
+ env['rack.session.options'][:renew] = true
21
+ @incrementor.call(env)
22
+ end
23
+ @defer_session = proc do |env|
24
+ env['rack.session.options'][:defer] = true
25
+ @incrementor.call(env)
26
+ end
27
+ end
28
+
29
+ it "should specify connection params" do
30
+ pool = Rack::Session::Cabinet.new(@incrementor, :cabinet_file => "/tmp/some.tch").pool
31
+ pool.should be_kind_of(TokyoCabinet::HDB)
32
+ pool.path.should eql("/tmp/some.tch")
33
+ end
34
+
35
+ it "should raise tokyo error on connect" do
36
+ lambda{ Rack::Session::Tokyo.new(@incrementor, :cabinet_file => "/tmp/session.tch").pool }.
37
+ should_not raise_error(Rufus::Tokyo::TokyoError)
38
+ end
39
+
40
+
41
+ it "creates a new cookie" do
42
+ pool = Rack::Session::Cabinet.new(@incrementor)
43
+ res = Rack::MockRequest.new(pool).get("/")
44
+ res["Set-Cookie"].should match(/#{@session_key}=/)
45
+ res.body.should eql('{"counter"=>1}')
46
+ end
47
+
48
+ it "determines session from a cookie" do
49
+ pool = Rack::Session::Cabinet.new(@incrementor)
50
+ req = Rack::MockRequest.new(pool)
51
+ res = req.get("/")
52
+ cookie = res["Set-Cookie"]
53
+ req.get("/", "HTTP_COOKIE" => cookie).body.should eql('{"counter"=>2}')
54
+ req.get("/", "HTTP_COOKIE" => cookie).body.should eql('{"counter"=>3}')
55
+ end
56
+
57
+ it "survives nonexistant cookies" do
58
+ bad_cookie = "rack.session=blsarghfasel"
59
+ pool = Rack::Session::Cabinet.new(@incrementor)
60
+ res = Rack::MockRequest.new(pool).get("/", "HTTP_COOKIE" => bad_cookie)
61
+ res.body.should eql('{"counter"=>1}')
62
+ cookie = res["Set-Cookie"][@session_match]
63
+ cookie.should_not match(/#{bad_cookie}/)
64
+ end
65
+
66
+ # Expire isn't supported by cabinet. Implement in ruby?
67
+ # it "should maintain freshness" do
68
+ # pool = Rack::Session::Cabinet.new(@incrementor, :expire_after => 3)
69
+ # res = Rack::MockRequest.new(pool).get('/')
70
+ # res.body.should include('"counter"=>1')
71
+ # cookie = res["Set-Cookie"]
72
+ # res = Rack::MockRequest.new(pool).get('/', "HTTP_COOKIE" => cookie)
73
+ # res["Set-Cookie"].should == cookie
74
+ # res.body.should include('"counter"=>2')
75
+ # puts 'Sleeping to expire session' if $DEBUG
76
+ # sleep 4
77
+ # res = Rack::MockRequest.new(pool).get('/', "HTTP_COOKIE" => cookie)
78
+ # res["Set-Cookie"].should_not == cookie
79
+ # res.body.should include('"counter"=>1')
80
+ # end
81
+
82
+ it "deletes cookies with :drop option" do
83
+ pool = Rack::Session::Cabinet.new(@incrementor)
84
+ req = Rack::MockRequest.new(pool)
85
+ drop = Rack::Utils::Context.new(pool, @drop_session)
86
+ dreq = Rack::MockRequest.new(drop)
87
+
88
+ res0 = req.get("/")
89
+ session = (cookie = res0["Set-Cookie"])[@session_match]
90
+ res0.body.should == '{"counter"=>1}'
91
+
92
+ res1 = req.get("/", "HTTP_COOKIE" => cookie)
93
+ res1["Set-Cookie"][@session_match].should == session
94
+ res1.body.should == '{"counter"=>2}'
95
+
96
+ res2 = dreq.get("/", "HTTP_COOKIE" => cookie)
97
+ res2["Set-Cookie"].should be_nil
98
+ res2.body.should == '{"counter"=>3}'
99
+
100
+ res3 = req.get("/", "HTTP_COOKIE" => cookie)
101
+ res3["Set-Cookie"][@session_match].should_not == session
102
+ res3.body.should == '{"counter"=>1}'
103
+ end
104
+
105
+ it "provides new session id with :renew option" do
106
+ pool = Rack::Session::Cabinet.new(@incrementor)
107
+ req = Rack::MockRequest.new(pool)
108
+ renew = Rack::Utils::Context.new(pool, @renew_session)
109
+ rreq = Rack::MockRequest.new(renew)
110
+
111
+ res0 = req.get("/")
112
+ session = (cookie = res0["Set-Cookie"])[@session_match]
113
+ res0.body.should == '{"counter"=>1}'
114
+
115
+ res1 = req.get("/", "HTTP_COOKIE" => cookie)
116
+ res1["Set-Cookie"][@session_match].should == session
117
+ res1.body.should == '{"counter"=>2}'
118
+
119
+ res2 = rreq.get("/", "HTTP_COOKIE" => cookie)
120
+ new_cookie = res2["Set-Cookie"]
121
+ new_session = new_cookie[@session_match]
122
+ new_session.should_not == session
123
+ res2.body.should == '{"counter"=>3}'
124
+
125
+ res3 = req.get("/", "HTTP_COOKIE" => new_cookie)
126
+ res3["Set-Cookie"][@session_match].should == new_session
127
+ res3.body.should == '{"counter"=>4}'
128
+ end
129
+
130
+ specify "omits cookie with :defer option" do
131
+ pool = Rack::Session::Cabinet.new(@incrementor)
132
+ req = Rack::MockRequest.new(pool)
133
+ defer = Rack::Utils::Context.new(pool, @defer_session)
134
+ dreq = Rack::MockRequest.new(defer)
135
+
136
+ res0 = req.get("/")
137
+ session = (cookie = res0["Set-Cookie"])[@session_match]
138
+ res0.body.should == '{"counter"=>1}'
139
+
140
+ res1 = req.get("/", "HTTP_COOKIE" => cookie)
141
+ res1["Set-Cookie"][@session_match].should == session
142
+ res1.body.should == '{"counter"=>2}'
143
+
144
+ res2 = dreq.get("/", "HTTP_COOKIE" => cookie)
145
+ res2["Set-Cookie"].should be_nil
146
+ res2.body.should == '{"counter"=>3}'
147
+
148
+ res3 = req.get("/", "HTTP_COOKIE" => cookie)
149
+ res3["Set-Cookie"][@session_match].should == session
150
+ res3.body.should == '{"counter"=>4}'
151
+ end
152
+
153
+ # anyone know how to do this better?
154
+ specify "multithread: should cleanly merge sessions" do
155
+ next unless $DEBUG
156
+ warn 'Running multithread test for Session::Cabinet'
157
+ pool = Rack::Session::Cabinet.new(@incrementor)
158
+ req = Rack::MockRequest.new(pool)
159
+
160
+ res = req.get('/')
161
+ res.body.should == '{"counter"=>1}'
162
+ cookie = res["Set-Cookie"]
163
+ sess_id = cookie[/#{pool.key}=([^,;]+)/,1]
164
+
165
+ delta_incrementor = lambda do |env|
166
+ # emulate disconjoinment of threading
167
+ env['rack.session'] = env['rack.session'].dup
168
+ Thread.stop
169
+ env['rack.session'][(Time.now.usec*rand).to_i] = true
170
+ @incrementor.call(env)
171
+ end
172
+ tses = Rack::Utils::Context.new pool, delta_incrementor
173
+ treq = Rack::MockRequest.new(tses)
174
+ tnum = rand(7).to_i+5
175
+ r = Array.new(tnum) do
176
+ Thread.new(treq) do |run|
177
+ run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true)
178
+ end
179
+ end.reverse.map{|t| t.run.join.value }
180
+ r.each do |res|
181
+ res['Set-Cookie'].should == cookie
182
+ res.body.should include('"counter"=>2')
183
+ end
184
+
185
+ session = pool.pool.get(sess_id)
186
+ session.size.should == tnum+1 # counter
187
+ session['counter'].should == 2 # meeeh
188
+
189
+ tnum = rand(7).to_i+5
190
+ r = Array.new(tnum) do |i|
191
+ delta_time = proc do |env|
192
+ env['rack.session'][i] = Time.now
193
+ Thread.stop
194
+ env['rack.session'] = env['rack.session'].dup
195
+ env['rack.session'][i] -= Time.now
196
+ @incrementor.call(env)
197
+ end
198
+ app = Rack::Utils::Context.new pool, time_delta
199
+ req = Rack::MockRequest.new app
200
+ Thread.new(req) do |run|
201
+ run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true)
202
+ end
203
+ end.reverse.map{|t| t.run.join.value }
204
+ r.each do |res|
205
+ res['Set-Cookie'].should == cookie
206
+ res.body.should include('"counter"=>3')
207
+ end
208
+
209
+ session = pool.pool.get(sess_id)
210
+ session.size.should == tnum+1
211
+ session['counter'].should == 3
212
+
213
+ drop_counter = proc do |env|
214
+ env['rack.session'].delete 'counter'
215
+ env['rack.session']['foo'] = 'bar'
216
+ [200, {'Content-Type'=>'text/plain'}, env['rack.session'].inspect]
217
+ end
218
+ tses = Rack::Utils::Context.new pool, drop_counter
219
+ treq = Rack::MockRequest.new(tses)
220
+ tnum = rand(7).to_i+5
221
+ r = Array.new(tnum) do
222
+ Thread.new(treq) do |run|
223
+ run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true)
224
+ end
225
+ end.reverse.map{|t| t.run.join.value }
226
+ r.each do |res|
227
+ res['Set-Cookie'].should == cookie
228
+ res.body.should include('"foo"=>"bar"')
229
+ end
230
+
231
+ session = pool.pool.get(sess_id)
232
+ session.size.should == r.size+1
233
+ session['counter'].should be_nil
234
+ session['foo'].should == 'bar'
235
+ end
236
+
237
+ after(:all) do
238
+ Rack::Session::Cabinet.new(@incrementor).pool.clear
239
+ end
240
+ end
241
+ end
242
+ end
@@ -3,12 +3,9 @@ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
3
3
 
4
4
  module Rack
5
5
  module Session
6
- # class Tokyo
7
- # attr_reader :
8
- # end
9
- describe "Rack::Session::Tokyo" do
6
+ describe "Rack::Session::RufusTyrant" do
10
7
  before(:each) do
11
- @session_key = Rack::Session::Tokyo::DEFAULT_OPTIONS[:key]
8
+ @session_key = Rack::Session::RufusTyrant::DEFAULT_OPTIONS[:key]
12
9
  @session_match = /#{@session_key}=[0-9a-fA-F]+;/
13
10
  @incrementor = lambda do |env|
14
11
  env["rack.session"]["counter"] ||= 0
@@ -30,30 +27,30 @@ module Rack
30
27
  end
31
28
 
32
29
  it "should specify connection params" do
33
- pool = Rack::Session::Tokyo.new(@incrementor, :tyrant_server => "127.0.0.1:1978").pool
30
+ pool = Rack::Session::RufusTyrant.new(@incrementor, :tyrant_server => "127.0.0.1:1978").pool
34
31
  pool.should be_kind_of(Rufus::Tokyo::Tyrant)
35
32
  pool.host.should eql("127.0.0.1")
36
33
  pool.port.should eql(1978)
37
34
 
38
- # pool = Rack::Session::Tokyo.new(@incrementor, :tokyo_server => ["localhost:6379", "localhost:6380"]).pool
39
- # pool.should be_kind_of(DistributedMarshaledTokyo)
35
+ # pool = Rack::Session::RufusTyrant.new(@incrementor, :tokyo_server => ["localhost:6379", "localhost:6380"]).pool
36
+ # pool.should be_kind_of(DistributedMarshaledRufusTyrant)
40
37
  end
41
38
 
42
39
  it "should raise tokyo error on connect" do
43
- lambda{ Rack::Session::Tokyo.new(@incrementor, :tyrant_server => "localhost:6380").pool }.
40
+ lambda{ Rack::Session::RufusTyrant.new(@incrementor, :tyrant_server => "localhost:6380").pool }.
44
41
  should_not raise_error(Rufus::Tokyo::TokyoError)
45
42
  end
46
43
 
47
44
 
48
45
  it "creates a new cookie" do
49
- pool = Rack::Session::Tokyo.new(@incrementor)
46
+ pool = Rack::Session::RufusTyrant.new(@incrementor)
50
47
  res = Rack::MockRequest.new(pool).get("/")
51
48
  res["Set-Cookie"].should match(/#{@session_key}=/)
52
49
  res.body.should eql('{"counter"=>1}')
53
50
  end
54
51
 
55
52
  it "determines session from a cookie" do
56
- pool = Rack::Session::Tokyo.new(@incrementor)
53
+ pool = Rack::Session::RufusTyrant.new(@incrementor)
57
54
  req = Rack::MockRequest.new(pool)
58
55
  res = req.get("/")
59
56
  cookie = res["Set-Cookie"]
@@ -63,7 +60,7 @@ module Rack
63
60
 
64
61
  it "survives nonexistant cookies" do
65
62
  bad_cookie = "rack.session=blsarghfasel"
66
- pool = Rack::Session::Tokyo.new(@incrementor)
63
+ pool = Rack::Session::RufusTyrant.new(@incrementor)
67
64
  res = Rack::MockRequest.new(pool).get("/", "HTTP_COOKIE" => bad_cookie)
68
65
  res.body.should eql('{"counter"=>1}')
69
66
  cookie = res["Set-Cookie"][@session_match]
@@ -72,7 +69,7 @@ module Rack
72
69
 
73
70
  # Expire isn't supported by cabinet. Implement in ruby?
74
71
  # it "should maintain freshness" do
75
- # pool = Rack::Session::Tokyo.new(@incrementor, :expire_after => 3)
72
+ # pool = Rack::Session::RufusTyrant.new(@incrementor, :expire_after => 3)
76
73
  # res = Rack::MockRequest.new(pool).get('/')
77
74
  # res.body.should include('"counter"=>1')
78
75
  # cookie = res["Set-Cookie"]
@@ -87,7 +84,7 @@ module Rack
87
84
  # end
88
85
 
89
86
  it "deletes cookies with :drop option" do
90
- pool = Rack::Session::Tokyo.new(@incrementor)
87
+ pool = Rack::Session::RufusTyrant.new(@incrementor)
91
88
  req = Rack::MockRequest.new(pool)
92
89
  drop = Rack::Utils::Context.new(pool, @drop_session)
93
90
  dreq = Rack::MockRequest.new(drop)
@@ -110,7 +107,7 @@ module Rack
110
107
  end
111
108
 
112
109
  it "provides new session id with :renew option" do
113
- pool = Rack::Session::Tokyo.new(@incrementor)
110
+ pool = Rack::Session::RufusTyrant.new(@incrementor)
114
111
  req = Rack::MockRequest.new(pool)
115
112
  renew = Rack::Utils::Context.new(pool, @renew_session)
116
113
  rreq = Rack::MockRequest.new(renew)
@@ -135,7 +132,7 @@ module Rack
135
132
  end
136
133
 
137
134
  specify "omits cookie with :defer option" do
138
- pool = Rack::Session::Tokyo.new(@incrementor)
135
+ pool = Rack::Session::RufusTyrant.new(@incrementor)
139
136
  req = Rack::MockRequest.new(pool)
140
137
  defer = Rack::Utils::Context.new(pool, @defer_session)
141
138
  dreq = Rack::MockRequest.new(defer)
@@ -160,8 +157,8 @@ module Rack
160
157
  # anyone know how to do this better?
161
158
  specify "multithread: should cleanly merge sessions" do
162
159
  next unless $DEBUG
163
- warn 'Running multithread test for Session::Tokyo'
164
- pool = Rack::Session::Tokyo.new(@incrementor)
160
+ warn 'Running multithread test for Session::RufusTyrant'
161
+ pool = Rack::Session::RufusTyrant.new(@incrementor)
165
162
  req = Rack::MockRequest.new(pool)
166
163
 
167
164
  res = req.get('/')
@@ -242,7 +239,7 @@ module Rack
242
239
  end
243
240
 
244
241
  after(:all) do
245
- Rack::Session::Tokyo.new(@incrementor).pool.clear
242
+ Rack::Session::RufusTyrant.new(@incrementor).pool.clear
246
243
  end
247
244
  end
248
245
  end
@@ -0,0 +1,249 @@
1
+ # Code from http://github.com/jodosha/redis-store
2
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
3
+
4
+ module Rack
5
+ module Session
6
+ class Tyrant
7
+ attr_reader :host, :port
8
+ end
9
+ describe "Rack::Session::Tyrant" do
10
+ before(:each) do
11
+ @session_key = Rack::Session::Tyrant::DEFAULT_OPTIONS[:key]
12
+ @session_match = /#{@session_key}=[0-9a-fA-F]+;/
13
+ @incrementor = lambda do |env|
14
+ env["rack.session"]["counter"] ||= 0
15
+ env["rack.session"]["counter"] += 1
16
+ Rack::Response.new(env["rack.session"].inspect).to_a
17
+ end
18
+ @drop_session = proc do |env|
19
+ env['rack.session.options'][:drop] = true
20
+ @incrementor.call(env)
21
+ end
22
+ @renew_session = proc do |env|
23
+ env['rack.session.options'][:renew] = true
24
+ @incrementor.call(env)
25
+ end
26
+ @defer_session = proc do |env|
27
+ env['rack.session.options'][:defer] = true
28
+ @incrementor.call(env)
29
+ end
30
+ end
31
+
32
+ it "should specify connection params" do
33
+ pool = Rack::Session::Tyrant.new(@incrementor, :tyrant_server => "127.0.0.1:1978").pool
34
+ pool.should be_kind_of(TokyoTyrant::RDB)
35
+ #pool.host.should eql("127.0.0.1")
36
+ #pool.port.should eql("1978")
37
+
38
+ # pool = Rack::Session::Tyrant.new(@incrementor, :tokyo_server => ["localhost:6379", "localhost:6380"]).pool
39
+ # pool.should be_kind_of(DistributedMarshaledTyrant)
40
+ end
41
+
42
+ it "should not raise error on connect" do
43
+ lambda{ Rack::Session::Tyrant.new(@incrementor, :tyrant_server => "localhost:6380").pool }.
44
+ should_not raise_error
45
+ end
46
+
47
+
48
+ it "creates a new cookie" do
49
+ pool = Rack::Session::Tyrant.new(@incrementor)
50
+ res = Rack::MockRequest.new(pool).get("/")
51
+ res["Set-Cookie"].should match(/#{@session_key}=/)
52
+ res.body.should eql('{"counter"=>1}')
53
+ end
54
+
55
+ it "determines session from a cookie" do
56
+ pool = Rack::Session::Tyrant.new(@incrementor)
57
+ req = Rack::MockRequest.new(pool)
58
+ res = req.get("/")
59
+ cookie = res["Set-Cookie"]
60
+ req.get("/", "HTTP_COOKIE" => cookie).body.should eql('{"counter"=>2}')
61
+ req.get("/", "HTTP_COOKIE" => cookie).body.should eql('{"counter"=>3}')
62
+ end
63
+
64
+ it "survives nonexistant cookies" do
65
+ bad_cookie = "rack.session=blsarghfasel"
66
+ pool = Rack::Session::Tyrant.new(@incrementor)
67
+ res = Rack::MockRequest.new(pool).get("/", "HTTP_COOKIE" => bad_cookie)
68
+ res.body.should eql('{"counter"=>1}')
69
+ cookie = res["Set-Cookie"][@session_match]
70
+ cookie.should_not match(/#{bad_cookie}/)
71
+ end
72
+
73
+ # Expire isn't supported by cabinet. Implement in ruby?
74
+ # it "should maintain freshness" do
75
+ # pool = Rack::Session::Tyrant.new(@incrementor, :expire_after => 3)
76
+ # res = Rack::MockRequest.new(pool).get('/')
77
+ # res.body.should include('"counter"=>1')
78
+ # cookie = res["Set-Cookie"]
79
+ # res = Rack::MockRequest.new(pool).get('/', "HTTP_COOKIE" => cookie)
80
+ # res["Set-Cookie"].should == cookie
81
+ # res.body.should include('"counter"=>2')
82
+ # puts 'Sleeping to expire session' if $DEBUG
83
+ # sleep 4
84
+ # res = Rack::MockRequest.new(pool).get('/', "HTTP_COOKIE" => cookie)
85
+ # res["Set-Cookie"].should_not == cookie
86
+ # res.body.should include('"counter"=>1')
87
+ # end
88
+
89
+ it "deletes cookies with :drop option" do
90
+ pool = Rack::Session::Tyrant.new(@incrementor)
91
+ req = Rack::MockRequest.new(pool)
92
+ drop = Rack::Utils::Context.new(pool, @drop_session)
93
+ dreq = Rack::MockRequest.new(drop)
94
+
95
+ res0 = req.get("/")
96
+ session = (cookie = res0["Set-Cookie"])[@session_match]
97
+ res0.body.should == '{"counter"=>1}'
98
+
99
+ res1 = req.get("/", "HTTP_COOKIE" => cookie)
100
+ res1["Set-Cookie"][@session_match].should == session
101
+ res1.body.should == '{"counter"=>2}'
102
+
103
+ res2 = dreq.get("/", "HTTP_COOKIE" => cookie)
104
+ res2["Set-Cookie"].should be_nil
105
+ res2.body.should == '{"counter"=>3}'
106
+
107
+ res3 = req.get("/", "HTTP_COOKIE" => cookie)
108
+ res3["Set-Cookie"][@session_match].should_not == session
109
+ res3.body.should == '{"counter"=>1}'
110
+ end
111
+
112
+ it "provides new session id with :renew option" do
113
+ pool = Rack::Session::Tyrant.new(@incrementor)
114
+ req = Rack::MockRequest.new(pool)
115
+ renew = Rack::Utils::Context.new(pool, @renew_session)
116
+ rreq = Rack::MockRequest.new(renew)
117
+
118
+ res0 = req.get("/")
119
+ session = (cookie = res0["Set-Cookie"])[@session_match]
120
+ res0.body.should == '{"counter"=>1}'
121
+
122
+ res1 = req.get("/", "HTTP_COOKIE" => cookie)
123
+ res1["Set-Cookie"][@session_match].should == session
124
+ res1.body.should == '{"counter"=>2}'
125
+
126
+ res2 = rreq.get("/", "HTTP_COOKIE" => cookie)
127
+ new_cookie = res2["Set-Cookie"]
128
+ new_session = new_cookie[@session_match]
129
+ new_session.should_not == session
130
+ res2.body.should == '{"counter"=>3}'
131
+
132
+ res3 = req.get("/", "HTTP_COOKIE" => new_cookie)
133
+ res3["Set-Cookie"][@session_match].should == new_session
134
+ res3.body.should == '{"counter"=>4}'
135
+ end
136
+
137
+ specify "omits cookie with :defer option" do
138
+ pool = Rack::Session::Tyrant.new(@incrementor)
139
+ req = Rack::MockRequest.new(pool)
140
+ defer = Rack::Utils::Context.new(pool, @defer_session)
141
+ dreq = Rack::MockRequest.new(defer)
142
+
143
+ res0 = req.get("/")
144
+ session = (cookie = res0["Set-Cookie"])[@session_match]
145
+ res0.body.should == '{"counter"=>1}'
146
+
147
+ res1 = req.get("/", "HTTP_COOKIE" => cookie)
148
+ res1["Set-Cookie"][@session_match].should == session
149
+ res1.body.should == '{"counter"=>2}'
150
+
151
+ res2 = dreq.get("/", "HTTP_COOKIE" => cookie)
152
+ res2["Set-Cookie"].should be_nil
153
+ res2.body.should == '{"counter"=>3}'
154
+
155
+ res3 = req.get("/", "HTTP_COOKIE" => cookie)
156
+ res3["Set-Cookie"][@session_match].should == session
157
+ res3.body.should == '{"counter"=>4}'
158
+ end
159
+
160
+ # anyone know how to do this better?
161
+ specify "multithread: should cleanly merge sessions" do
162
+ next unless $DEBUG
163
+ warn 'Running multithread test for Session::Tyrant'
164
+ pool = Rack::Session::Tyrant.new(@incrementor)
165
+ req = Rack::MockRequest.new(pool)
166
+
167
+ res = req.get('/')
168
+ res.body.should == '{"counter"=>1}'
169
+ cookie = res["Set-Cookie"]
170
+ sess_id = cookie[/#{pool.key}=([^,;]+)/,1]
171
+
172
+ delta_incrementor = lambda do |env|
173
+ # emulate disconjoinment of threading
174
+ env['rack.session'] = env['rack.session'].dup
175
+ Thread.stop
176
+ env['rack.session'][(Time.now.usec*rand).to_i] = true
177
+ @incrementor.call(env)
178
+ end
179
+ tses = Rack::Utils::Context.new pool, delta_incrementor
180
+ treq = Rack::MockRequest.new(tses)
181
+ tnum = rand(7).to_i+5
182
+ r = Array.new(tnum) do
183
+ Thread.new(treq) do |run|
184
+ run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true)
185
+ end
186
+ end.reverse.map{|t| t.run.join.value }
187
+ r.each do |res|
188
+ res['Set-Cookie'].should == cookie
189
+ res.body.should include('"counter"=>2')
190
+ end
191
+
192
+ session = pool.pool.get(sess_id)
193
+ session.size.should == tnum+1 # counter
194
+ session['counter'].should == 2 # meeeh
195
+
196
+ tnum = rand(7).to_i+5
197
+ r = Array.new(tnum) do |i|
198
+ delta_time = proc do |env|
199
+ env['rack.session'][i] = Time.now
200
+ Thread.stop
201
+ env['rack.session'] = env['rack.session'].dup
202
+ env['rack.session'][i] -= Time.now
203
+ @incrementor.call(env)
204
+ end
205
+ app = Rack::Utils::Context.new pool, time_delta
206
+ req = Rack::MockRequest.new app
207
+ Thread.new(req) do |run|
208
+ run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true)
209
+ end
210
+ end.reverse.map{|t| t.run.join.value }
211
+ r.each do |res|
212
+ res['Set-Cookie'].should == cookie
213
+ res.body.should include('"counter"=>3')
214
+ end
215
+
216
+ session = pool.pool.get(sess_id)
217
+ session.size.should == tnum+1
218
+ session['counter'].should == 3
219
+
220
+ drop_counter = proc do |env|
221
+ env['rack.session'].delete 'counter'
222
+ env['rack.session']['foo'] = 'bar'
223
+ [200, {'Content-Type'=>'text/plain'}, env['rack.session'].inspect]
224
+ end
225
+ tses = Rack::Utils::Context.new pool, drop_counter
226
+ treq = Rack::MockRequest.new(tses)
227
+ tnum = rand(7).to_i+5
228
+ r = Array.new(tnum) do
229
+ Thread.new(treq) do |run|
230
+ run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true)
231
+ end
232
+ end.reverse.map{|t| t.run.join.value }
233
+ r.each do |res|
234
+ res['Set-Cookie'].should == cookie
235
+ res.body.should include('"foo"=>"bar"')
236
+ end
237
+
238
+ session = pool.pool.get(sess_id)
239
+ session.size.should == r.size+1
240
+ session['counter'].should be_nil
241
+ session['foo'].should == 'bar'
242
+ end
243
+
244
+ after(:all) do
245
+ Rack::Session::Tyrant.new(@incrementor).pool.clear
246
+ end
247
+ end
248
+ end
249
+ end
@@ -1,7 +1,9 @@
1
1
  $: << File.join(File.dirname(__FILE__), "/../lib")
2
2
  %W{rubygems spec rack/cache activesupport }.each { |l| require l }
3
3
  require 'tokyo_store'
4
- require 'rack/session/tokyo'
4
+ require 'rack/session/rufus_tyrant'
5
+ require 'rack/session/tyrant'
6
+ require 'rack/session/cabinet'
5
7
  require 'cache/tokyo_store'
6
8
  #require 'rack/cache/tokyo'
7
9
 
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{tokyo_store}
5
- s.version = "0.2.1"
5
+ s.version = "0.3.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Marcos Piccinini"]
9
- s.date = %q{2009-07-18}
9
+ s.date = %q{2009-07-19}
10
10
  s.email = %q{x@nofxx.com}
11
11
  s.extra_rdoc_files = [
12
12
  "LICENSE",
@@ -24,11 +24,15 @@ Gem::Specification.new do |s|
24
24
  "lib/cache/tokyo_store.rb",
25
25
  "lib/rack/cache/tokyo_entitystore.rb",
26
26
  "lib/rack/cache/tokyo_metastore.rb",
27
- "lib/rack/session/tokyo.rb",
27
+ "lib/rack/session/cabinet.rb",
28
+ "lib/rack/session/rufus_tyrant.rb",
29
+ "lib/rack/session/tyrant.rb",
28
30
  "lib/tokyo_store.rb",
29
31
  "spec/cache/tokyo_store_spec.rb",
30
32
  "spec/rack/cache/tokyo_spec.rb",
31
- "spec/rack/session/tokyo_spec.rb",
33
+ "spec/rack/session/cabinet_spec.rb",
34
+ "spec/rack/session/rufus_tyrant_spec.rb",
35
+ "spec/rack/session/tyrant_spec.rb",
32
36
  "spec/spec.opts",
33
37
  "spec/spec_helper.rb",
34
38
  "spec/tokyo_store_spec.rb",
@@ -41,7 +45,9 @@ Gem::Specification.new do |s|
41
45
  s.summary = %q{Tokyo Tyrant rails session store}
42
46
  s.test_files = [
43
47
  "spec/rack/cache/tokyo_spec.rb",
44
- "spec/rack/session/tokyo_spec.rb",
48
+ "spec/rack/session/rufus_tyrant_spec.rb",
49
+ "spec/rack/session/cabinet_spec.rb",
50
+ "spec/rack/session/tyrant_spec.rb",
45
51
  "spec/tokyo_store_spec.rb",
46
52
  "spec/cache/tokyo_store_spec.rb",
47
53
  "spec/spec_helper.rb"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nofxx-tokyo_store
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marcos Piccinini
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-07-18 00:00:00 -07:00
12
+ date: 2009-07-19 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -34,11 +34,15 @@ files:
34
34
  - lib/cache/tokyo_store.rb
35
35
  - lib/rack/cache/tokyo_entitystore.rb
36
36
  - lib/rack/cache/tokyo_metastore.rb
37
- - lib/rack/session/tokyo.rb
37
+ - lib/rack/session/cabinet.rb
38
+ - lib/rack/session/rufus_tyrant.rb
39
+ - lib/rack/session/tyrant.rb
38
40
  - lib/tokyo_store.rb
39
41
  - spec/cache/tokyo_store_spec.rb
40
42
  - spec/rack/cache/tokyo_spec.rb
41
- - spec/rack/session/tokyo_spec.rb
43
+ - spec/rack/session/cabinet_spec.rb
44
+ - spec/rack/session/rufus_tyrant_spec.rb
45
+ - spec/rack/session/tyrant_spec.rb
42
46
  - spec/spec.opts
43
47
  - spec/spec_helper.rb
44
48
  - spec/tokyo_store_spec.rb
@@ -71,7 +75,9 @@ specification_version: 3
71
75
  summary: Tokyo Tyrant rails session store
72
76
  test_files:
73
77
  - spec/rack/cache/tokyo_spec.rb
74
- - spec/rack/session/tokyo_spec.rb
78
+ - spec/rack/session/rufus_tyrant_spec.rb
79
+ - spec/rack/session/cabinet_spec.rb
80
+ - spec/rack/session/tyrant_spec.rb
75
81
  - spec/tokyo_store_spec.rb
76
82
  - spec/cache/tokyo_store_spec.rb
77
83
  - spec/spec_helper.rb