Browse files

tokyo session on rack&roll

  • Loading branch information...
1 parent af18a11 commit 962a7846aadb580555b6cce64d7af38c5d90293a @nofxx committed Jul 3, 2009
Showing with 556 additions and 371 deletions.
  1. +1 −1 VERSION
  2. +61 −25 lib/rack/session/tokyo.rb
  3. +252 −252 spec/rack/cache/tokyo_cache_spec.rb
  4. +217 −55 spec/rack/session/tokyo_spec.rb
  5. +3 −0 spec/spec.opts
  6. +13 −30 spec/spec_helper.rb
  7. +9 −8 tokyo_store.gemspec
View
2 VERSION
@@ -1 +1 @@
-0.1.3
+0.1.5
View
86 lib/rack/session/tokyo.rb
@@ -2,56 +2,92 @@ module Rack
module Session
class Tokyo < Abstract::ID
attr_reader :mutex, :pool
- #DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge :redis_server => "localhost:6379"
-
+ DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge :tyrant_server => "localhost:1978"
def initialize(app, options = {})
# Support old :expires option
#options[:expire_after] ||= options[:expires]
-
super
-
- @default_options = {
- :namespace => 'rack:session',
- :tyrant_server => 'localhost'
- }.merge(@default_options)
-
- @pool = options[:cache] || Rufus::Tokyo::Tyrant.new(@default_options[:tyrant_server], 1978) # @default_options)
+ @mutex = Mutex.new
+ # @default_options = { # :namespace => 'rack:session', # }.merge(@default_options)
+ host, port = *@default_options[:tyrant_server].split(":") # @default_options) #options[:cache] ||
+ begin
+ @pool = Rufus::Tokyo::Tyrant.new(host, port.to_i)
+ rescue => e
+ "No server avaiable or #{e}"
+ end
# unless @pool.servers.any? { |s| s.alive? }
# raise "#{self} unable to find server during initialization."
# end
- @mutex = Mutex.new
-
- super
end
def generate_sid
loop do
sid = super
- break sid unless @pool.get(sid)
+ break sid unless @pool[sid]
end
end
-
private
def get_session(env, sid)
- sid ||= generate_sid
- begin
- session = @pool[sid] || {}
- rescue #Tokyo::TokyoError, Errno::ECONNREFUSED
+ session = Marshal.load(@pool[sid]) if sid && sid != "" #sid ||= generate_sid
+ @mutex.lock if env['rack.multithread']
+ unless sid and session
+ env['rack.errors'].puts("Session '#{sid.inspect}' not found, initializing...") if $VERBOSE and not sid.nil?
session = {}
+ sid = generate_sid
+ ret = @pool[sid] = Marshal.dump(session)
+ raise "Session collision on '#{sid.inspect}'" unless ret
end
- [sid, session]
+ session.instance_variable_set('@old', {}.merge(session))
+ return [sid, session]
+ rescue => e
+ session = {}
+ ensure
+ @mutex.unlock if env['rack.multithread']
end
- def set_session(env, sid, session_data, options)
- options = env['rack.session.options']
- #expiry = options[:expire_after] || 0
- @pool[sid] = session_data
+ def set_session(env, sid, new_session, options)
+ @mutex.lock if env['rack.multithread']
+ session = Marshal.load(@pool[sid]) rescue {}
+ if options[:renew] or options[:drop]
+ @pool.delete sid
+ return false if options[:drop]
+ sid = generate_sid
+ @pool[sid] = 0
+ end
+ old_session = new_session.instance_variable_get('@old') || {}
+ session = merge_sessions sid, old_session, new_session, session
+ @pool[sid] = Marshal.dump(session) #, options])
return sid
- rescue #Tokyo::TokyoError, Errno::ECONNREFUSED
+ rescue => e
+ warn "#{self} is unable to find server. #{e}"
+ warn $!.inspect
return false
+ ensure
+ @mutex.unlock if env['rack.multithread']
end
+
+ def merge_sessions(sid, old, new, cur=nil)
+ cur ||= {}
+ unless Hash === old and Hash === new
+ warn 'Bad old or new sessions provided.'
+ return cur
+ end
+
+ delete = old.keys - new.keys
+ warn "//@#{sid}: dropping #{delete*','}" if $DEBUG and not delete.empty?
+ delete.each{|k| cur.delete k }
+
+ update = new.keys.select{|k| new[k] != old[k] }
+ warn "//@#{sid}: updating #{update*','}" if $DEBUG and not update.empty?
+ update.each{|k| cur[k] = new[k] }
+
+ cur
+ end
+
end
+
end
+
end
View
504 spec/rack/cache/tokyo_cache_spec.rb
@@ -1,252 +1,252 @@
-require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
-
-describe "TokyoStore" do
- it "should store fragment cache" do
- Rufus::Tokyo::Tyrant.should_receive(:new).and_return(@mock_tyrant = mock("Tyrant"))
- store = ActiveSupport::Cache.lookup_store :tokyo_store, "data.tch"
- store.should be_kind_of ActiveSupport::Cache::TokyoStore
- end
-
- it "should fail" do
- tokyo = Rufus::Tokyo::Tyrant.new('localhost', 1978)
- Rufus::Tokyo::Tyrant.should_not_receive(:new)
- store = ActiveSupport::Cache.lookup_store :tokyo_store, tokyo
- store.should be_kind_of ActiveSupport::Cache::TokyoStore
- end
-
- describe "Similar" do
-
- before(:each) do
- @cache = ActiveSupport::Cache::TokyoStore.new 'localhost:1978'
- @cache.clear
- end
-
- it "should return true on success" do
- @cache.write('foo', 'bar').should be_true
- end
-
- it "should read and write strings" do
- @cache.write('foo', 'bar')
- @cache.read('foo').should eql('bar')
- end
-
- it "should read and write hash" do
- @cache.write('foo', {:a => "b"})
- @cache.read('foo').should eql({:a => "b"})
- end
-
- it "should write integers" do
- @cache.write('foo', 1)
- @cache.read('foo').should eql(1)
- end
-
- it "should write nil" do
- @cache.write('foo', nil)
- @cache.read('foo').should eql(nil)
- end
-
- it "should have a cache miss block" do
- @cache.write('foo', 'bar')
- @cache.fetch('foo') { 'baz' }.should eql('bar')
- end
-
- it "should have a cache miss block" do
- @cache.fetch('foo') { 'baz' }.should eql('baz')
- end
-
- it "should have a forced cache miss block" do
- @cache.fetch('foo', :force => true).should be_nil
- end
-
- it "should read and write hash" do
- @cache.write('foo', {:a => "b", :c => "d"})
- @cache.read('foo').should eql({:a => "b", :c => "d"})
- end
-
- it "should read and write array" do
- @cache.write('foo', [1,2,3])
- @cache.read('foo').should eql([1,2,3])
- end
-
- it "should read and write obj" do
- obj = City.new; obj.name = "Acapulco"; obj.pop = 717766
- @cache.write('foo', obj)
- @cache.read('foo').should be_instance_of City
- @cache.read('foo').name.should eql("Acapulco")
- end
-
- it "should read multiples" do
- @cache.write('a', 1)
- @cache.write('b', 2)
- @cache.read_multi('a','b').should eql({ 'a' => 1, 'b' => 2})
- end
-
- it "should clear all" do
- @cache.write("erase_me", 1).should be_true
- @cache.delete("erase_me")
- @cache.exist?("erase_me").should be_false
- end
-
- it "should check if exists" do
- @cache.exist?("new_one").should be_false
- @cache.write("new_one", 1)
- @cache.exist?("new_one").should be_true
- end
-
- it "should increment value" do
- @cache.write('val', 1, :raw => true)
- @cache.read("val", :raw => true).to_i.should eql 1
- @cache.increment('val')
- @cache.read("val", :raw => true).to_i.should eql 2
- @cache.increment('val')
- @cache.read("val", :raw => true).to_i.should eql 3
- end
-
- it "should decrement value" do
- @cache.write('val', 3, :raw => true)
- @cache.read("val", :raw => true).to_i.should eql 3
- @cache.decrement('val')
- @cache.read("val", :raw => true).to_i.should eql 2
- @cache.decrement('val')
- @cache.read("val", :raw => true).to_i.should eql 1
- end
-
- it "should clear all" do
- @cache.increment("val")
- @cache.exist?("val").should be_true
- @cache.clear
- @cache.exist?("val").should be_false
- end
-
- it "should show some stats" do
- @cache.stats.should be_instance_of Hash #== hash_including({ :type => "hash"})
- end
-
- it "store objects should be immutable" do
- @cache.with_local_cache do
- @cache.write('foo', 'bar')
- @cache.read('foo').gsub!(/.*/, 'baz')# }.should raise_error(ActiveSupport::FrozenObjectError)
- @cache.read('foo').should == 'bar'
- end
- end
-
- it "stored objects should not be frozen" do
- @cache.with_local_cache do
- @cache.write('foo', 'bar')
- end
- @cache.with_local_cache do
- @cache.read('foo').should_not be_frozen
- end
- end
-
- it "should delete matched" do
- @cache.write("val", 1)
- @cache.write("value", 1)
- @cache.write("not", 1)
- @cache.delete_matched('val')
- end
-
- end
-
- describe "backed store" do
- before(:each) do
- @cache = ActiveSupport::Cache.lookup_store(:tokyo_store)
- @data = @cache.instance_variable_get(:@data)
- @cache.clear
- end
-
- it "local_writes_are_persistent_on_the_remote_cache" do
- @cache.with_local_cache do
- @cache.write('foo', 'bar')
- end
-
- @cache.read('foo').should eql('bar')
- end
-
- it "test_clear_also_clears_local_cache" do
- @cache.with_local_cache do
- @cache.write('foo', 'bar')
- @cache.clear
- @cache.read('foo').should be_nil
- end
- end
-
- it "test_local_cache_of_read_and_write" do
- @cache.with_local_cache do
- @cache.write('foo', 'bar')
- @data.clear # Clear remote cache
- @cache.read('foo').should eql('bar')
- end
- end
-
- it "test_local_cache_should_read_and_write_integer" do
- @cache.with_local_cache do
- @cache.write('foo', 1)
- @cache.read('foo').should eql(1)
- end
- end
-
- it "test_local_cache_of_delete" do
- @cache.with_local_cache do
- @cache.write('foo', 'bar')
- @cache.delete('foo')
- @data.clear # Clear remote cache
- @cache.read('foo').should be_nil
- end
- end
-
- it "test_local_cache_of_exist" do
- @cache.with_local_cache do
- @cache.write('foo', 'bar')
- @cache.instance_variable_set(:@data, nil)
- @data.clear # Clear remote cache
- @cache.exist?('foo').should be_true
- end
- end
-
- it "test_local_cache_of_increment" do
- @cache.with_local_cache do
- @cache.write('foo', 1, :raw => true)
- @cache.increment('foo')
- @data.clear # Clear remote cache
- @cache.read('foo', :raw => true).to_i.should eql(2)
- end
- end
-
- it "test_local_cache_of_decrement" do
- @cache.with_local_cache do
- @cache.write('foo', 1, :raw => true)
- @cache.decrement('foo')
- @data.clear # Clear remote cache
- @cache.read('foo', :raw => true).to_i.should be_zero
- end
- end
-
- it "test_exist_with_nulls_cached_locally" do
- @cache.with_local_cache do
- @cache.write('foo', 'bar')
- @cache.delete('foo')
- @cache.exist?('foo').should be_false
- end
- end
-
- it "test_multi_get" do
- @cache.with_local_cache do
- @cache.write('foo', 1)
- @cache.write('goo', 2)
- @cache.read_multi('foo', 'goo').should eql({'foo' => 1, 'goo' => 2})
- end
- end
-
- it "test_middleware" do
- app = lambda { |env|
- result = @cache.write('foo', 'bar')
- @cache.read('foo').should eql('bar') # make sure 'foo' was written
- }
- app = @cache.middleware.new(app)
- app.call({})
- end
-
- end
-
-end
+# require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
+
+# describe "TokyoStore" do
+# it "should store fragment cache" do
+# Rufus::Tokyo::Tyrant.should_receive(:new).and_return(@mock_tyrant = mock("Tyrant"))
+# store = ActiveSupport::Cache.lookup_store :tokyo_store, "data.tch"
+# store.should be_kind_of ActiveSupport::Cache::TokyoStore
+# end
+
+# it "should fail" do
+# tokyo = Rufus::Tokyo::Tyrant.new('localhost', 1978)
+# Rufus::Tokyo::Tyrant.should_not_receive(:new)
+# store = ActiveSupport::Cache.lookup_store :tokyo_store, tokyo
+# store.should be_kind_of ActiveSupport::Cache::TokyoStore
+# end
+
+# describe "Similar" do
+
+# before(:each) do
+# @cache = ActiveSupport::Cache::TokyoStore.new 'localhost:1978'
+# @cache.clear
+# end
+
+# it "should return true on success" do
+# @cache.write('foo', 'bar').should be_true
+# end
+
+# it "should read and write strings" do
+# @cache.write('foo', 'bar')
+# @cache.read('foo').should eql('bar')
+# end
+
+# it "should read and write hash" do
+# @cache.write('foo', {:a => "b"})
+# @cache.read('foo').should eql({:a => "b"})
+# end
+
+# it "should write integers" do
+# @cache.write('foo', 1)
+# @cache.read('foo').should eql(1)
+# end
+
+# it "should write nil" do
+# @cache.write('foo', nil)
+# @cache.read('foo').should eql(nil)
+# end
+
+# it "should have a cache miss block" do
+# @cache.write('foo', 'bar')
+# @cache.fetch('foo') { 'baz' }.should eql('bar')
+# end
+
+# it "should have a cache miss block" do
+# @cache.fetch('foo') { 'baz' }.should eql('baz')
+# end
+
+# it "should have a forced cache miss block" do
+# @cache.fetch('foo', :force => true).should be_nil
+# end
+
+# it "should read and write hash" do
+# @cache.write('foo', {:a => "b", :c => "d"})
+# @cache.read('foo').should eql({:a => "b", :c => "d"})
+# end
+
+# it "should read and write array" do
+# @cache.write('foo', [1,2,3])
+# @cache.read('foo').should eql([1,2,3])
+# end
+
+# it "should read and write obj" do
+# obj = City.new; obj.name = "Acapulco"; obj.pop = 717766
+# @cache.write('foo', obj)
+# @cache.read('foo').should be_instance_of City
+# @cache.read('foo').name.should eql("Acapulco")
+# end
+
+# it "should read multiples" do
+# @cache.write('a', 1)
+# @cache.write('b', 2)
+# @cache.read_multi('a','b').should eql({ 'a' => 1, 'b' => 2})
+# end
+
+# it "should clear all" do
+# @cache.write("erase_me", 1).should be_true
+# @cache.delete("erase_me")
+# @cache.exist?("erase_me").should be_false
+# end
+
+# it "should check if exists" do
+# @cache.exist?("new_one").should be_false
+# @cache.write("new_one", 1)
+# @cache.exist?("new_one").should be_true
+# end
+
+# it "should increment value" do
+# @cache.write('val', 1, :raw => true)
+# @cache.read("val", :raw => true).to_i.should eql 1
+# @cache.increment('val')
+# @cache.read("val", :raw => true).to_i.should eql 2
+# @cache.increment('val')
+# @cache.read("val", :raw => true).to_i.should eql 3
+# end
+
+# it "should decrement value" do
+# @cache.write('val', 3, :raw => true)
+# @cache.read("val", :raw => true).to_i.should eql 3
+# @cache.decrement('val')
+# @cache.read("val", :raw => true).to_i.should eql 2
+# @cache.decrement('val')
+# @cache.read("val", :raw => true).to_i.should eql 1
+# end
+
+# it "should clear all" do
+# @cache.increment("val")
+# @cache.exist?("val").should be_true
+# @cache.clear
+# @cache.exist?("val").should be_false
+# end
+
+# it "should show some stats" do
+# @cache.stats.should be_instance_of Hash #== hash_including({ :type => "hash"})
+# end
+
+# it "store objects should be immutable" do
+# @cache.with_local_cache do
+# @cache.write('foo', 'bar')
+# @cache.read('foo').gsub!(/.*/, 'baz')# }.should raise_error(ActiveSupport::FrozenObjectError)
+# @cache.read('foo').should == 'bar'
+# end
+# end
+
+# it "stored objects should not be frozen" do
+# @cache.with_local_cache do
+# @cache.write('foo', 'bar')
+# end
+# @cache.with_local_cache do
+# @cache.read('foo').should_not be_frozen
+# end
+# end
+
+# it "should delete matched" do
+# @cache.write("val", 1)
+# @cache.write("value", 1)
+# @cache.write("not", 1)
+# @cache.delete_matched('val')
+# end
+
+# end
+
+# describe "backed store" do
+# before(:each) do
+# @cache = ActiveSupport::Cache.lookup_store(:tokyo_store)
+# @data = @cache.instance_variable_get(:@data)
+# @cache.clear
+# end
+
+# it "local_writes_are_persistent_on_the_remote_cache" do
+# @cache.with_local_cache do
+# @cache.write('foo', 'bar')
+# end
+
+# @cache.read('foo').should eql('bar')
+# end
+
+# it "test_clear_also_clears_local_cache" do
+# @cache.with_local_cache do
+# @cache.write('foo', 'bar')
+# @cache.clear
+# @cache.read('foo').should be_nil
+# end
+# end
+
+# it "test_local_cache_of_read_and_write" do
+# @cache.with_local_cache do
+# @cache.write('foo', 'bar')
+# @data.clear # Clear remote cache
+# @cache.read('foo').should eql('bar')
+# end
+# end
+
+# it "test_local_cache_should_read_and_write_integer" do
+# @cache.with_local_cache do
+# @cache.write('foo', 1)
+# @cache.read('foo').should eql(1)
+# end
+# end
+
+# it "test_local_cache_of_delete" do
+# @cache.with_local_cache do
+# @cache.write('foo', 'bar')
+# @cache.delete('foo')
+# @data.clear # Clear remote cache
+# @cache.read('foo').should be_nil
+# end
+# end
+
+# it "test_local_cache_of_exist" do
+# @cache.with_local_cache do
+# @cache.write('foo', 'bar')
+# @cache.instance_variable_set(:@data, nil)
+# @data.clear # Clear remote cache
+# @cache.exist?('foo').should be_true
+# end
+# end
+
+# it "test_local_cache_of_increment" do
+# @cache.with_local_cache do
+# @cache.write('foo', 1, :raw => true)
+# @cache.increment('foo')
+# @data.clear # Clear remote cache
+# @cache.read('foo', :raw => true).to_i.should eql(2)
+# end
+# end
+
+# it "test_local_cache_of_decrement" do
+# @cache.with_local_cache do
+# @cache.write('foo', 1, :raw => true)
+# @cache.decrement('foo')
+# @data.clear # Clear remote cache
+# @cache.read('foo', :raw => true).to_i.should be_zero
+# end
+# end
+
+# it "test_exist_with_nulls_cached_locally" do
+# @cache.with_local_cache do
+# @cache.write('foo', 'bar')
+# @cache.delete('foo')
+# @cache.exist?('foo').should be_false
+# end
+# end
+
+# it "test_multi_get" do
+# @cache.with_local_cache do
+# @cache.write('foo', 1)
+# @cache.write('goo', 2)
+# @cache.read_multi('foo', 'goo').should eql({'foo' => 1, 'goo' => 2})
+# end
+# end
+
+# it "test_middleware" do
+# app = lambda { |env|
+# result = @cache.write('foo', 'bar')
+# @cache.read('foo').should eql('bar') # make sure 'foo' was written
+# }
+# app = @cache.middleware.new(app)
+# app.call({})
+# end
+
+# end
+
+# end
View
272 spec/rack/session/tokyo_spec.rb
@@ -1,75 +1,237 @@
-require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
+require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
-describe "Tokyo Session Store" do
+module Rack
+ module Session
+ describe "Rack::Session::Tokyo" do
+ before(:each) do
+ @session_key = Rack::Session::Tokyo::DEFAULT_OPTIONS[:key]
+ @session_match = /#{@session_key}=[0-9a-fA-F]+;/
+ @incrementor = lambda do |env|
+ env["rack.session"]["counter"] ||= 0
+ env["rack.session"]["counter"] += 1
+ Rack::Response.new(env["rack.session"].inspect).to_a
+ end
+ @drop_session = proc do |env|
+ env['rack.session.options'][:drop] = true
+ @incrementor.call(env)
+ end
+ @renew_session = proc do |env|
+ env['rack.session.options'][:renew] = true
+ @incrementor.call(env)
+ end
+ @defer_session = proc do |env|
+ env['rack.session.options'][:defer] = true
+ @incrementor.call(env)
+ end
+ end
- class TestController < ActionController::Base
- def no_session_access
- head :ok
- end
+ it "should specify connection params" do
+ pool = Rack::Session::Tokyo.new(@incrementor, :tokyo_server => "localhost:6380/1").pool
+ pool.should be_kind_of(Rufus::Tokyo::Tyrant)
+ pool.host.should == "localhost"
+ pool.port.should == 1978
- def set_session_value
- session[:foo] = "bar"
- head :ok
- end
+ # pool = Rack::Session::Tokyo.new(@incrementor, :tokyo_server => ["localhost:6379", "localhost:6380"]).pool
+ # pool.should be_kind_of(DistributedMarshaledTokyo)
+ end
- def get_session_value
- render :text => "foo: #{session[:foo].inspect}"
- end
+ it "creates a new cookie" do
+ pool = Rack::Session::Tokyo.new(@incrementor)
+ res = Rack::MockRequest.new(pool).get("/")
+ res["Set-Cookie"].should match(/#{@session_key}=/)
+ res.body.should == '{"counter"=>1}'
+ end
- def get_session_id
- session[:foo]
- render :text => "#{request.session_options[:id]}"
- end
+ it "determines session from a cookie" do
+ pool = Rack::Session::Tokyo.new(@incrementor)
+ req = Rack::MockRequest.new(pool)
+ res = req.get("/")
+ cookie = res["Set-Cookie"]
+ req.get("/", "HTTP_COOKIE" => cookie).
+ body.should == '{"counter"=>2}'
+ req.get("/", "HTTP_COOKIE" => cookie).
+ body.should == '{"counter"=>3}'
+ end
- def call_reset_session
- session[:bar]
- reset_session
- session[:bar] = "baz"
- head :ok
- end
+ it "survives nonexistant cookies" do
+ bad_cookie = "rack.session=blarghfasel"
+ pool = Rack::Session::Tokyo.new(@incrementor)
+ res = Rack::MockRequest.new(pool).
+ get("/", "HTTP_COOKIE" => bad_cookie)
+ res.body.should == '{"counter"=>1}'
+ cookie = res["Set-Cookie"][@session_match]
+ cookie.should_not match(/#{bad_cookie}/)
+ end
- def rescue_action(e) raise end
- end
+ it "should maintain freshness" do
+ pool = Rack::Session::Tokyo.new(@incrementor, :expire_after => 3)
+ res = Rack::MockRequest.new(pool).get('/')
+ res.body.should include('"counter"=>1')
+ cookie = res["Set-Cookie"]
+ res = Rack::MockRequest.new(pool).get('/', "HTTP_COOKIE" => cookie)
+ res["Set-Cookie"].should == cookie
+ res.body.should include('"counter"=>2')
+ puts 'Sleeping to expire session' if $DEBUG
+ sleep 4
+ res = Rack::MockRequest.new(pool).get('/', "HTTP_COOKIE" => cookie)
+ res["Set-Cookie"].should_not == cookie
+ res.body.should include('"counter"=>1')
+ end
- before(:each) do
+ it "deletes cookies with :drop option" do
+ pool = Rack::Session::Tokyo.new(@incrementor)
+ req = Rack::MockRequest.new(pool)
+ drop = Rack::Utils::Context.new(pool, @drop_session)
+ dreq = Rack::MockRequest.new(drop)
+ res0 = req.get("/")
+ session = (cookie = res0["Set-Cookie"])[@session_match]
+ res0.body.should == '{"counter"=>1}'
- end
+ res1 = req.get("/", "HTTP_COOKIE" => cookie)
+ res1["Set-Cookie"][@session_match].should == session
+ res1.body.should == '{"counter"=>2}'
- it "test_setting_and_getting_session_value" do
- with_test_route_set do
- get '/set_session_value'
- response.should eql(:success)
- cookies['_session_id'].should be_true
+ res2 = dreq.get("/", "HTTP_COOKIE" => cookie)
+ res2["Set-Cookie"].should be_nil
+ res2.body.should == '{"counter"=>3}'
- # get '/get_session_value'
- # assert_response :success
- # assert_equal 'foo: "bar"', response.body
+ res3 = req.get("/", "HTTP_COOKIE" => cookie)
+ res3["Set-Cookie"][@session_match].should_not == session
+ res3.body.should == '{"counter"=>1}'
end
- end
- private
- def with_test_route_set
- with_routing do |set|
- set.draw do |map|
- map.with_options :controller => "mem_cache_store_test/test" do |c|
- c.connect "/:action"
+
+ it "provides new session id with :renew option" do
+ pool = Rack::Session::Tokyo.new(@incrementor)
+ req = Rack::MockRequest.new(pool)
+ renew = Rack::Utils::Context.new(pool, @renew_session)
+ rreq = Rack::MockRequest.new(renew)
+
+ res0 = req.get("/")
+ session = (cookie = res0["Set-Cookie"])[@session_match]
+ res0.body.should == '{"counter"=>1}'
+
+ res1 = req.get("/", "HTTP_COOKIE" => cookie)
+ res1["Set-Cookie"][@session_match].should == session
+ res1.body.should == '{"counter"=>2}'
+
+ res2 = rreq.get("/", "HTTP_COOKIE" => cookie)
+ new_cookie = res2["Set-Cookie"]
+ new_session = new_cookie[@session_match]
+ new_session.should_not == session
+ res2.body.should == '{"counter"=>3}'
+
+ res3 = req.get("/", "HTTP_COOKIE" => new_cookie)
+ res3["Set-Cookie"][@session_match].should == new_session
+ res3.body.should == '{"counter"=>4}'
+ end
+
+ specify "omits cookie with :defer option" do
+ pool = Rack::Session::Tokyo.new(@incrementor)
+ req = Rack::MockRequest.new(pool)
+ defer = Rack::Utils::Context.new(pool, @defer_session)
+ dreq = Rack::MockRequest.new(defer)
+
+ res0 = req.get("/")
+ session = (cookie = res0["Set-Cookie"])[@session_match]
+ res0.body.should == '{"counter"=>1}'
+
+ res1 = req.get("/", "HTTP_COOKIE" => cookie)
+ res1["Set-Cookie"][@session_match].should == session
+ res1.body.should == '{"counter"=>2}'
+
+ res2 = dreq.get("/", "HTTP_COOKIE" => cookie)
+ res2["Set-Cookie"].should be_nil
+ res2.body.should == '{"counter"=>3}'
+
+ res3 = req.get("/", "HTTP_COOKIE" => cookie)
+ res3["Set-Cookie"][@session_match].should == session
+ res3.body.should == '{"counter"=>4}'
+ end
+
+ # anyone know how to do this better?
+ specify "multithread: should cleanly merge sessions" do
+ next unless $DEBUG
+ warn 'Running multithread test for Session::Tokyo'
+ pool = Rack::Session::Tokyo.new(@incrementor)
+ req = Rack::MockRequest.new(pool)
+
+ res = req.get('/')
+ res.body.should == '{"counter"=>1}'
+ cookie = res["Set-Cookie"]
+ sess_id = cookie[/#{pool.key}=([^,;]+)/,1]
+
+ delta_incrementor = lambda do |env|
+ # emulate disconjoinment of threading
+ env['rack.session'] = env['rack.session'].dup
+ Thread.stop
+ env['rack.session'][(Time.now.usec*rand).to_i] = true
+ @incrementor.call(env)
+ end
+ tses = Rack::Utils::Context.new pool, delta_incrementor
+ treq = Rack::MockRequest.new(tses)
+ tnum = rand(7).to_i+5
+ r = Array.new(tnum) do
+ Thread.new(treq) do |run|
+ run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true)
end
+ end.reverse.map{|t| t.run.join.value }
+ r.each do |res|
+ res['Set-Cookie'].should == cookie
+ res.body.should include('"counter"=>2')
+ end
+
+ session = pool.pool.get(sess_id)
+ session.size.should == tnum+1 # counter
+ session['counter'].should == 2 # meeeh
+
+ tnum = rand(7).to_i+5
+ r = Array.new(tnum) do |i|
+ delta_time = proc do |env|
+ env['rack.session'][i] = Time.now
+ Thread.stop
+ env['rack.session'] = env['rack.session'].dup
+ env['rack.session'][i] -= Time.now
+ @incrementor.call(env)
+ end
+ app = Rack::Utils::Context.new pool, time_delta
+ req = Rack::MockRequest.new app
+ Thread.new(req) do |run|
+ run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true)
+ end
+ end.reverse.map{|t| t.run.join.value }
+ r.each do |res|
+ res['Set-Cookie'].should == cookie
+ res.body.should include('"counter"=>3')
end
- yield
- end
- end
- def with_routing
- real_routes = ActionController::Routing::Routes
- ActionController::Routing.module_eval { remove_const :Routes }
- temporary_routes = ActionController::Routing::RouteSet.new
- ActionController::Routing.module_eval { const_set :Routes, temporary_routes }
+ session = pool.pool.get(sess_id)
+ session.size.should == tnum+1
+ session['counter'].should == 3
- yield temporary_routes
- ensure
- if ActionController::Routing.const_defined? :Routes
- ActionController::Routing.module_eval { remove_const :Routes }
+ drop_counter = proc do |env|
+ env['rack.session'].delete 'counter'
+ env['rack.session']['foo'] = 'bar'
+ [200, {'Content-Type'=>'text/plain'}, env['rack.session'].inspect]
+ end
+ tses = Rack::Utils::Context.new pool, drop_counter
+ treq = Rack::MockRequest.new(tses)
+ tnum = rand(7).to_i+5
+ r = Array.new(tnum) do
+ Thread.new(treq) do |run|
+ run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true)
+ end
+ end.reverse.map{|t| t.run.join.value }
+ r.each do |res|
+ res['Set-Cookie'].should == cookie
+ res.body.should include('"foo"=>"bar"')
+ end
+
+ session = pool.pool.get(sess_id)
+ session.size.should == r.size+1
+ session['counter'].should be_nil
+ session['foo'].should == 'bar'
end
- ActionController::Routing.const_set(:Routes, real_routes) if real_routes
end
+ end
end
View
3 spec/spec.opts
@@ -0,0 +1,3 @@
+--colour
+--loadby mtime
+--reverse
View
43 spec/spec_helper.rb
@@ -1,43 +1,26 @@
+$: << File.join(File.dirname(__FILE__), "/../lib")
require 'rubygems'
-#require 'mocha'
-#require 'action_controller'
-ENV["RAILS_ENV"] = "test"
-
-
-
-require 'spec'
-#require 'spec/rails'
require 'spec'
+require 'rack'
+# $LOAD_PATH.unshift(File.dirname(__FILE__))
+# $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
+require 'tokyo_store'
+require 'rack/session/tokyo'
+#require 'rack/cache/tokyo'
+#ENV["RAILS_ENV"] = "test"
require 'activesupport'
require 'active_support'
require 'actionpack'
require 'action_controller'
-require 'action_view'
# require 'action_controller/test_process'
# require 'action_pack'
-# Show backtraces for deprecated behavior for quicker cleanup.
-ActiveSupport::Deprecation.debug = true
-ActionController::Base.logger = Logger.new(STDOUT) #"log/debug.log")
-
-
-$LOAD_PATH.unshift(File.dirname(__FILE__))
-$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
-require 'tokyo_cache_store'
-require 'tokyo_session_store'
-
-
-
-#include TokyoCabinet
-# Rails::Initializer.run do |c|
-# c.action_controller.session_store = :tokyo_store
-# end
-ActionController::Base.session_store = :tokyo_store
-#ActionController::Base.ignore_missing_templates = true
+# ActionController::Base.session_store = :tokyo_store
+# #ActionController::Base.ignore_missing_templates = true
-DispatcherApp = ActionController::Dispatcher.new
- TokyoStoreStoreApp = ActionController::Session::TokyoStore.new(
- DispatcherApp, :key => '_s_id')
+# DispatcherApp = ActionController::Dispatcher.new
+# TokyoStoreStoreApp = ActionController::Session::TokyoStore.new(
+# DispatcherApp, :key => '_s_id')
#Simple class to test marshal
class City
View
17 tokyo_store.gemspec
@@ -2,7 +2,7 @@
Gem::Specification.new do |s|
s.name = %q{tokyo_store}
- s.version = "0.1.3"
+ s.version = "0.1.5"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Marcos Piccinini"]
@@ -20,12 +20,13 @@ Gem::Specification.new do |s|
"Rakefile",
"VERSION",
"benchmark/tokyo_store.rb",
+ "lib/rack/cache/tokyo_cache_store.rb",
"lib/rack/session/tokyo.rb",
- "lib/tokyo_cache_store.rb",
"lib/tokyo_store.rb",
+ "spec/rack/cache/tokyo_cache_spec.rb",
+ "spec/rack/session/tokyo_spec.rb",
+ "spec/spec.opts",
"spec/spec_helper.rb",
- "spec/tokyo_session_store_spec.rb",
- "spec/tokyo_store_rufus_spec.rb",
"spec/tokyo_store_spec.rb",
"tokyo_store.gemspec"
]
@@ -35,10 +36,10 @@ Gem::Specification.new do |s|
s.rubygems_version = %q{1.3.4}
s.summary = %q{Tokyo Tyrant rails session store}
s.test_files = [
- "spec/tokyo_store_spec.rb",
- "spec/spec_helper.rb",
- "spec/tokyo_store_rufus_spec.rb",
- "spec/tokyo_session_store_spec.rb"
+ "spec/rack/cache/tokyo_cache_spec.rb",
+ "spec/rack/session/tokyo_spec.rb",
+ "spec/tokyo_store_spec.rb",
+ "spec/spec_helper.rb"
]
if s.respond_to? :specification_version then

0 comments on commit 962a784

Please sign in to comment.