jodosha-redis-store 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.textile CHANGED
@@ -21,12 +21,12 @@ Provides a cache store for your Ruby web framework of choice.
21
21
  h3. How to use with Rails
22
22
 
23
23
  config.gem "jodosha-redis-store", :source => "http://gems.github.com", :lib => "redis-store"
24
- require "redis-store" # HACK Rails tries to instantiate cache first, then load configured gems
24
+ require "redis-store"
25
25
  config.cache_store = :redis_store
26
26
 
27
27
  h3. How to use with Merb
28
-
29
- dependency "jodosha-redis-store", "0.2.0"
28
+
29
+ dependency "jodosha-redis-store", "0.3.0"
30
30
  dependency("merb-cache", merb_gems_version) do
31
31
  Merb::Cache.setup do
32
32
  register(:redis, Merb::Cache::RedisStore, :servers => ["127.0.0.1:6379"])
@@ -45,6 +45,38 @@ h3. How to use with Sinatra
45
45
  end
46
46
  end
47
47
 
48
+ h2. Rack::Session
49
+
50
+ Provides a Redis store for Rack::Session. See "http://rack.rubyforge.org/doc/Rack/Session.html":http://rack.rubyforge.org/doc/Rack/Session.html
51
+
52
+ h3. How to use with a generic Rack application
53
+
54
+ require "rubygems"
55
+ require "rack"
56
+ require "jodosha-redis-store"
57
+ require "application"
58
+ use Rack::Session::Redis
59
+ run Application.new
60
+
61
+ h3. How to use with Rails
62
+
63
+ config.gem "jodosha-redis-store", :source => "http://gems.github.com", :lib => "redis-store"
64
+ ActionController::Base.session_store = Rack::Session::Redis
65
+
66
+ h3. How to use with Merb
67
+
68
+ dependency "jodosha-redis-store", "0.3.0"
69
+ Merb::Config.use do |c|
70
+ c[:session_store] = 'redis'
71
+ end
72
+ Merb::BootLoader.before_app_loads do
73
+ Merb::SessionContainer.subclasses << "Merb::RedisSession"
74
+ end
75
+
76
+ h3. How to use with Sinatra
77
+
78
+ Sorry, but Sinatra application boot system "hardcode":http://github.com/sinatra/sinatra/blob/0f02bafe86f8dd9bba9ab425468cb1067caa83ff/lib/sinatra/base.rb#L785 Rack::Session::Cookie
79
+
48
80
  h2. Rack::Cache
49
81
 
50
82
  Provides a Redis store for HTTP caching. See "http://github.com/rtomayko/rack-cache":http://github.com/rtomayko/rack-cache
data/Rakefile CHANGED
@@ -5,7 +5,7 @@ require 'rake/testtask'
5
5
  require 'rake/rdoctask'
6
6
  require 'spec/rake/spectask'
7
7
 
8
- REDIS_STORE_VERSION = "0.2.0"
8
+ REDIS_STORE_VERSION = "0.3.0"
9
9
 
10
10
  task :default => :spec
11
11
 
@@ -10,12 +10,7 @@ module Merb
10
10
  # RedisStore.new :servers => ["example.com:23682/1"] # => host: example.com, port: 23682, db: 1
11
11
  # RedisStore.new :servers => ["localhost:6379/0", "localhost:6380/0"] # => instantiate a cluster
12
12
  def initialize(config = {})
13
- addresses = extract_addresses(config[:servers])
14
- @data = if addresses.size > 1
15
- DistributedMarshaledRedis.new addresses
16
- else
17
- MarshaledRedis.new addresses.first || {}
18
- end
13
+ @data = RedisFactory.create config[:servers]
19
14
  end
20
15
 
21
16
  def writable?(key, parameters = {}, conditions = {})
@@ -58,21 +53,6 @@ module Merb
58
53
  end
59
54
 
60
55
  private
61
- def extract_addresses(addresses) # TODO extract in a module or a class
62
- return [] unless addresses
63
- addresses = addresses.flatten.compact
64
- addresses.inject([]) do |result, address|
65
- host, port = address.split /\:/
66
- port, db = port.split /\// if port
67
- address = {}
68
- address[:host] = host if host
69
- address[:port] = port if port
70
- address[:db] = db.to_i if db
71
- result << address
72
- result
73
- end
74
- end
75
-
76
56
  # Returns cache key calculated from base key
77
57
  # and SHA2 hex from parameters.
78
58
  def normalize(key, parameters = {})
@@ -10,12 +10,7 @@ module ActiveSupport
10
10
  # RedisStore.new "example.com:23682/1" # => host: example.com, port: 23682, db: 1
11
11
  # RedisStore.new "localhost:6379/0", "localhost:6380/0" # => instantiate a cluster
12
12
  def initialize(*addresses)
13
- addresses = extract_addresses(addresses)
14
- @data = if addresses.size > 1
15
- DistributedMarshaledRedis.new addresses
16
- else
17
- MarshaledRedis.new addresses.first || {}
18
- end
13
+ @data = RedisFactory.create(addresses)
19
14
  end
20
15
 
21
16
  def write(key, value, options = nil)
@@ -109,21 +104,6 @@ module ActiveSupport
109
104
  def stats
110
105
  @data.info
111
106
  end
112
-
113
- private
114
- def extract_addresses(addresses)
115
- addresses = addresses.flatten.compact
116
- addresses.inject([]) do |result, address|
117
- host, port = address.split /\:/
118
- port, db = port.split /\// if port
119
- address = {}
120
- address[:host] = host if host
121
- address[:port] = port if port
122
- address[:db] = db.to_i if db
123
- result << address
124
- result
125
- end
126
- end
127
107
  end
128
108
  end
129
109
  end
@@ -16,12 +16,7 @@ module Sinatra
16
16
  # RedisStore.new "example.com:23682/1" # => host: example.com, port: 23682, db: 1
17
17
  # RedisStore.new "localhost:6379/0", "localhost:6380/0" # => instantiate a cluster
18
18
  def initialize(*addresses)
19
- addresses = extract_addresses(addresses)
20
- @data = if addresses.size > 1
21
- DistributedMarshaledRedis.new addresses
22
- else
23
- MarshaledRedis.new addresses.first || {}
24
- end
19
+ @data = RedisFactory.create addresses
25
20
  end
26
21
 
27
22
  def write(key, value, options = nil)
@@ -111,21 +106,6 @@ module Sinatra
111
106
  def stats
112
107
  @data.info
113
108
  end
114
-
115
- private
116
- def extract_addresses(addresses) # TODO extract in a module or a class
117
- addresses = addresses.flatten.compact
118
- addresses.inject([]) do |result, address|
119
- host, port = address.split /\:/
120
- port, db = port.split /\// if port
121
- address = {}
122
- address[:host] = host if host
123
- address[:port] = port if port
124
- address[:db] = db.to_i if db
125
- result << address
126
- result
127
- end
128
- end
129
109
  end
130
110
  end
131
111
  end
@@ -0,0 +1,32 @@
1
+ module Merb
2
+ # HACK for cyclic dependency: redis-store is required before Merb session stores
3
+ class Mash < Hash; end
4
+ class SessionContainer < Mash; class_inheritable_accessor :session_store_type end
5
+ class SessionStoreContainer < SessionContainer; end
6
+
7
+ class RedisSession < SessionStoreContainer
8
+ self.session_store_type = :redis
9
+ end
10
+
11
+ module RedisStore
12
+ def retrieve_session(session_id)
13
+ get("session:#{session_id}")
14
+ end
15
+
16
+ def store_session(session_id, data)
17
+ set("session:#{session_id}", data)
18
+ end
19
+
20
+ def delete_session(session_id)
21
+ delete("session:#{session_id}")
22
+ end
23
+ end
24
+ end
25
+
26
+ module Rack
27
+ module Session
28
+ class Redis
29
+ include Merb::RedisStore
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,81 @@
1
+ module Rack
2
+ module Session
3
+ class Redis < Abstract::ID
4
+ attr_reader :mutex, :pool
5
+ DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge :redis_server => "localhost:6379"
6
+
7
+ def initialize(app, options = {})
8
+ super
9
+ @mutex = Mutex.new
10
+ @pool = RedisFactory.create options[:redis_server] || @default_options[:redis_server]
11
+ end
12
+
13
+ def generate_sid
14
+ loop do
15
+ sid = super
16
+ break sid unless @pool.get(sid)
17
+ end
18
+ end
19
+
20
+ def get_session(env, sid)
21
+ session = @pool.get(sid) if sid
22
+ @mutex.lock if env['rack.multithread']
23
+ unless sid and session
24
+ env['rack.errors'].puts("Session '#{sid.inspect}' not found, initializing...") if $VERBOSE and not sid.nil?
25
+ session = {}
26
+ sid = generate_sid
27
+ ret = @pool.set sid, session
28
+ raise "Session collision on '#{sid.inspect}'" unless ret
29
+ end
30
+ session.instance_variable_set('@old', {}.merge(session))
31
+ return [sid, session]
32
+ rescue RedisError, Errno::ECONNREFUSED
33
+ warn "#{self} is unable to find server."
34
+ warn $!.inspect
35
+ return [ nil, {} ]
36
+ ensure
37
+ @mutex.unlock if env['rack.multithread']
38
+ end
39
+
40
+ def set_session(env, session_id, new_session, options)
41
+ @mutex.lock if env['rack.multithread']
42
+ session = @pool.get(session_id) rescue {}
43
+ if options[:renew] or options[:drop]
44
+ @pool.delete session_id
45
+ return false if options[:drop]
46
+ session_id = generate_sid
47
+ @pool.set session_id, 0
48
+ end
49
+ old_session = new_session.instance_variable_get('@old') || {}
50
+ session = merge_sessions session_id, old_session, new_session, session
51
+ @pool.set session_id, session, options
52
+ return session_id
53
+ rescue RedisError, Errno::ECONNREFUSED
54
+ warn "#{self} is unable to find server."
55
+ warn $!.inspect
56
+ return false
57
+ ensure
58
+ @mutex.unlock if env['rack.multithread']
59
+ end
60
+
61
+ private
62
+ def merge_sessions(sid, old, new, cur=nil)
63
+ cur ||= {}
64
+ unless Hash === old and Hash === new
65
+ warn 'Bad old or new sessions provided.'
66
+ return cur
67
+ end
68
+
69
+ delete = old.keys - new.keys
70
+ warn "//@#{sid}: dropping #{delete*','}" if $DEBUG and not delete.empty?
71
+ delete.each{|k| cur.delete k }
72
+
73
+ update = new.keys.select{|k| new[k] != old[k] }
74
+ warn "//@#{sid}: updating #{update*','}" if $DEBUG and not update.empty?
75
+ update.each{|k| cur[k] = new[k] }
76
+
77
+ cur
78
+ end
79
+ end
80
+ end
81
+ end
data/lib/redis-store.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  require "redis"
2
2
  require "dist_redis"
3
+ require "redis/redis"
4
+ require "redis/redis_factory"
3
5
  require "redis/marshaled_redis"
4
6
  require "redis/distributed_marshaled_redis"
5
7
 
@@ -14,6 +16,15 @@ elsif defined?(Rails)
14
16
  require "cache/rails/redis_store"
15
17
  end
16
18
 
19
+ # Rack::Session
20
+ if defined?(Rack::Session)
21
+ require "rack/session/abstract/id"
22
+ require "rack/session/redis"
23
+ if defined?(Merb)
24
+ require "rack/session/merb"
25
+ end
26
+ end
27
+
17
28
  # Rack::Cache
18
29
  if defined?(Rack::Cache)
19
30
  require "rack/cache/redis_metastore"
@@ -22,7 +22,8 @@ class MarshaledRedis < Redis
22
22
 
23
23
  def expires_in(options)
24
24
  if options
25
- options[:expires_in] || options[:expire_in]
25
+ # Rack::Session Merb Rails/Sinatra
26
+ options[:expire_after] || options[:expires_in] || options[:expire_in]
26
27
  end
27
28
  end
28
29
  end
@@ -0,0 +1,14 @@
1
+ # Foreword compatibility with Redis > 0.0.3
2
+ class Redis
3
+ def set(key, val, expiry=nil)
4
+ write("SET #{key} #{val.to_s.size}\r\n#{val}\r\n")
5
+ s = get_response == OK
6
+ return expire(key, expiry) if s && expiry
7
+ s
8
+ end
9
+
10
+ def expire(key, expiry=nil)
11
+ write("EXPIRE #{key} #{expiry}\r\n")
12
+ get_response == 1
13
+ end
14
+ end
@@ -0,0 +1,27 @@
1
+ class RedisFactory
2
+ class << self
3
+ def create(*addresses)
4
+ addresses = extract_addresses(addresses)
5
+ if addresses.size > 1
6
+ DistributedMarshaledRedis.new addresses
7
+ else
8
+ MarshaledRedis.new addresses.first || {}
9
+ end
10
+ end
11
+
12
+ private
13
+ def extract_addresses(addresses)
14
+ addresses = addresses.flatten.compact
15
+ addresses.inject([]) do |result, address|
16
+ host, port = address.split /\:/
17
+ port, db = port.split /\// if port
18
+ address = {}
19
+ address[:host] = host if host
20
+ address[:port] = port if port
21
+ address[:db] = db.to_i if db
22
+ result << address
23
+ result
24
+ end
25
+ end
26
+ end
27
+ end
data/redis-store.gemspec CHANGED
@@ -1,14 +1,14 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "redis-store"
3
- s.version = "0.2.0"
4
- s.date = "2009-04-30"
5
- s.summary = "Redis cache and session stores for Ruby web frameworks"
3
+ s.version = "0.3.0"
4
+ s.date = "2009-05-03"
5
+ s.summary = "Rack::Session, Rack::Cache and cache Redis stores for Ruby web frameworks."
6
6
  s.author = "Luca Guidi"
7
7
  s.email = "guidi.luca@gmail.com"
8
8
  s.homepage = "http://lucaguidi.com"
9
- s.description = "Redis cache and session stores for Ruby web frameworks"
9
+ s.description = "Rack::Session, Rack::Cache and cache Redis stores for Ruby web frameworks."
10
10
  s.has_rdoc = true
11
- s.files = ["MIT-LICENSE", "README.textile", "Rakefile", "lib/cache/merb/redis_store.rb", "lib/cache/rails/redis_store.rb", "lib/cache/sinatra/redis_store.rb", "lib/rack/cache/redis_entitystore.rb", "lib/rack/cache/redis_metastore.rb", "lib/redis-store.rb", "lib/redis/distributed_marshaled_redis.rb", "lib/redis/marshaled_redis.rb", "redis-store.gemspec", "spec/cache/merb/redis_store_spec.rb", "spec/cache/rails/redis_store_spec.rb", "spec/cache/sinatra/redis_store_spec.rb", "spec/config/master.conf", "spec/config/single.conf", "spec/config/slave.conf", "spec/rack/cache/entitystore/pony.jpg", "spec/rack/cache/entitystore/redis_spec.rb", "spec/rack/cache/metastore/redis_spec.rb", "spec/redis/distributed_marshaled_redis_spec.rb", "spec/redis/marshaled_redis_spec.rb", "spec/spec_helper.rb"]
12
- s.test_files = ["spec/cache/merb/redis_store_spec.rb", "spec/cache/rails/redis_store_spec.rb", "spec/cache/sinatra/redis_store_spec.rb", "spec/rack/cache/entitystore/redis_spec.rb", "spec/rack/cache/metastore/redis_spec.rb", "spec/redis/distributed_marshaled_redis_spec.rb", "spec/redis/marshaled_redis_spec.rb"]
11
+ s.files = ["MIT-LICENSE", "README.textile", "Rakefile", "lib/cache/merb/redis_store.rb", "lib/cache/rails/redis_store.rb", "lib/cache/sinatra/redis_store.rb", "lib/rack/cache/redis_entitystore.rb", "lib/rack/cache/redis_metastore.rb", "lib/rack/session/merb.rb", "lib/rack/session/redis.rb", "lib/redis-store.rb", "lib/redis/distributed_marshaled_redis.rb", "lib/redis/marshaled_redis.rb", "lib/redis/redis.rb", "lib/redis/redis_factory.rb", "redis-store.gemspec", "spec/cache/merb/redis_store_spec.rb", "spec/cache/rails/redis_store_spec.rb", "spec/cache/sinatra/redis_store_spec.rb", "spec/config/master.conf", "spec/config/single.conf", "spec/config/slave.conf", "spec/rack/cache/entitystore/pony.jpg", "spec/rack/cache/entitystore/redis_spec.rb", "spec/rack/cache/metastore/redis_spec.rb", "spec/rack/session/redis_spec.rb", "spec/redis/distributed_marshaled_redis_spec.rb", "spec/redis/marshaled_redis_spec.rb", "spec/redis/redis_factory_spec.rb", "spec/spec_helper.rb"]
12
+ s.test_files = ["spec/cache/merb/redis_store_spec.rb", "spec/cache/rails/redis_store_spec.rb", "spec/cache/sinatra/redis_store_spec.rb", "spec/rack/cache/entitystore/redis_spec.rb", "spec/rack/cache/metastore/redis_spec.rb", "spec/rack/session/redis_spec.rb", "spec/redis/distributed_marshaled_redis_spec.rb", "spec/redis/marshaled_redis_spec.rb", "spec/redis/redis_factory_spec.rb"]
13
13
  s.extra_rdoc_files = ["README.textile"]
14
14
  end
@@ -2,10 +2,10 @@ require File.join(File.dirname(__FILE__), "/../../spec_helper")
2
2
 
3
3
  module Merb
4
4
  module Cache
5
- describe "RedisStore" do
5
+ describe "Merb::Cache::RedisStore" do
6
6
  before(:each) do
7
- @store = RedisStore.new
8
- @dstore = RedisStore.new :servers => ["localhost:6380/1", "localhost:6381/1"]
7
+ @store = Merb::Cache::RedisStore.new
8
+ @dstore = Merb::Cache::RedisStore.new :servers => ["localhost:6380/1", "localhost:6381/1"]
9
9
  @rabbit = OpenStruct.new :name => "bunny"
10
10
  @white_rabbit = OpenStruct.new :color => "white"
11
11
  with_store_management do |store|
@@ -16,9 +16,9 @@ module Merb
16
16
 
17
17
  it "should accept connection params" do
18
18
  redis = instantiate_store
19
- redis.instance_variable_get(:@db).should == 0
20
19
  redis.host.should == "localhost"
21
20
  redis.port.should == "6379"
21
+ redis.db.should == 0
22
22
 
23
23
  redis = instantiate_store "redis.com"
24
24
  redis.host.should == "redis.com"
@@ -28,9 +28,9 @@ module Merb
28
28
  redis.port.should == "6380"
29
29
 
30
30
  redis = instantiate_store "redis.com:6380/23"
31
- redis.instance_variable_get(:@db).should == 23
32
31
  redis.host.should == "redis.com"
33
32
  redis.port.should == "6380"
33
+ redis.db.should == 23
34
34
  end
35
35
 
36
36
  it "should instantiate a ring" do
@@ -72,11 +72,13 @@ module Merb
72
72
  end
73
73
  end
74
74
 
75
- # it "should write the data with expiration time" do
76
- # @store.write "rabbit", @white_rabbit, :expires_in => 1.second
77
- # @store.read("rabbit").should === @white_rabbit ; sleep 2
78
- # @store.read("rabbit").should be_nil
79
- # end
75
+ it "should write the data with expiration time" do
76
+ with_store_management do |store|
77
+ store.write "rabbit", @white_rabbit, {}, :expires_in => 1.second
78
+ store.read("rabbit").should === @white_rabbit ; sleep 2
79
+ store.read("rabbit").should be_nil
80
+ end
81
+ end
80
82
 
81
83
  it "should not write data if :unless_exist option is true" do
82
84
  with_store_management do |store|
@@ -91,9 +93,6 @@ module Merb
91
93
  store.fetch("rub-a-dub").should be_nil
92
94
  store.fetch("rub-a-dub") { "Flora de Cana" }
93
95
  store.fetch("rub-a-dub").should === "Flora de Cana"
94
- # store.fetch("rabbit", {}, :force => true, :expires_in => 1.second) { @white_rabbit }
95
- # store.fetch("rabbit").should === @white_rabbit ; sleep 2
96
- # store.fetch("rabbit").should be_nil
97
96
  end
98
97
  end
99
98
 
@@ -120,7 +119,7 @@ module Merb
120
119
 
121
120
  private
122
121
  def instantiate_store(addresses = nil)
123
- RedisStore.new(:servers => [addresses].flatten).instance_variable_get(:@data)
122
+ Merb::Cache::RedisStore.new(:servers => [addresses].flatten).instance_variable_get(:@data)
124
123
  end
125
124
 
126
125
  def with_store_management
@@ -2,10 +2,10 @@ require File.join(File.dirname(__FILE__), "/../../spec_helper")
2
2
 
3
3
  module ActiveSupport
4
4
  module Cache
5
- describe "RedisStore" do
5
+ describe "ActiveSupport::Cache::RedisStore" do
6
6
  before(:each) do
7
- @store = RedisStore.new
8
- @dstore = RedisStore.new "localhost:6380/1", "localhost:6381/1"
7
+ @store = ActiveSupport::Cache::RedisStore.new
8
+ @dstore = ActiveSupport::Cache::RedisStore.new "localhost:6380/1", "localhost:6381/1"
9
9
  @rabbit = OpenStruct.new :name => "bunny"
10
10
  @white_rabbit = OpenStruct.new :color => "white"
11
11
  with_store_management do |store|
@@ -17,9 +17,9 @@ module ActiveSupport
17
17
 
18
18
  it "should accept connection params" do
19
19
  redis = instantiate_store
20
- redis.instance_variable_get(:@db).should == 0
21
20
  redis.host.should == "localhost"
22
21
  redis.port.should == "6379"
22
+ redis.db.should == 0
23
23
 
24
24
  redis = instantiate_store "redis.com"
25
25
  redis.host.should == "redis.com"
@@ -29,9 +29,9 @@ module ActiveSupport
29
29
  redis.port.should == "6380"
30
30
 
31
31
  redis = instantiate_store "redis.com:6380/23"
32
- redis.instance_variable_get(:@db).should == 23
33
32
  redis.host.should == "redis.com"
34
33
  redis.port.should == "6380"
34
+ redis.db.should == 23
35
35
  end
36
36
 
37
37
  it "should instantiate a ring" do
@@ -54,11 +54,13 @@ module ActiveSupport
54
54
  end
55
55
  end
56
56
 
57
- # it "should write the data with expiration time" do
58
- # @store.write "rabbit", @white_rabbit, :expires_in => 1.second
59
- # @store.read("rabbit").should === @white_rabbit ; sleep 2
60
- # @store.read("rabbit").should be_nil
61
- # end
57
+ it "should write the data with expiration time" do
58
+ with_store_management do |store|
59
+ store.write "rabbit", @white_rabbit, :expires_in => 1.second
60
+ store.read("rabbit").should === @white_rabbit ; sleep 2
61
+ store.read("rabbit").should be_nil
62
+ end
63
+ end
62
64
 
63
65
  it "should not write data if :unless_exist option is true" do
64
66
  with_store_management do |store|
@@ -151,15 +153,15 @@ module ActiveSupport
151
153
  store.fetch("rub-a-dub") { "Flora de Cana" }
152
154
  store.fetch("rub-a-dub").should === "Flora de Cana"
153
155
  store.fetch("rabbit", :force => true).should be_nil # force cache miss
154
- # store.fetch("rabbit", :force => true, :expires_in => 1.second) { @white_rabbit }
155
- # store.fetch("rabbit").should === @white_rabbit ; sleep 2
156
- # store.fetch("rabbit").should be_nil
156
+ store.fetch("rabbit", :force => true, :expires_in => 1.second) { @white_rabbit }
157
+ store.fetch("rabbit").should === @white_rabbit ; sleep 2
158
+ store.fetch("rabbit").should be_nil
157
159
  end
158
160
  end
159
161
 
160
162
  private
161
163
  def instantiate_store(addresses = nil)
162
- RedisStore.new(addresses).instance_variable_get(:@data)
164
+ ActiveSupport::Cache::RedisStore.new(addresses).instance_variable_get(:@data)
163
165
  end
164
166
 
165
167
  def with_store_management
@@ -16,10 +16,10 @@ end
16
16
 
17
17
  module Sinatra
18
18
  module Cache
19
- describe "RedisStore" do
19
+ describe "Sinatra::Cache::RedisStore" do
20
20
  before(:each) do
21
- @store = RedisStore.new
22
- @dstore = RedisStore.new "localhost:6380/1", "localhost:6381/1"
21
+ @store = Sinatra::Cache::RedisStore.new
22
+ @dstore = Sinatra::Cache::RedisStore.new "localhost:6380/1", "localhost:6381/1"
23
23
  @rabbit = OpenStruct.new :name => "bunny"
24
24
  @white_rabbit = OpenStruct.new :color => "white"
25
25
  with_store_management do |store|
@@ -38,9 +38,9 @@ module Sinatra
38
38
 
39
39
  it "should accept connection params" do
40
40
  redis = instantiate_store
41
- redis.instance_variable_get(:@db).should == 0
42
41
  redis.host.should == "localhost"
43
42
  redis.port.should == "6379"
43
+ redis.db.should == 0
44
44
 
45
45
  redis = instantiate_store "redis.com"
46
46
  redis.host.should == "redis.com"
@@ -50,9 +50,9 @@ module Sinatra
50
50
  redis.port.should == "6380"
51
51
 
52
52
  redis = instantiate_store "redis.com:6380/23"
53
- redis.instance_variable_get(:@db).should == 23
54
53
  redis.host.should == "redis.com"
55
54
  redis.port.should == "6380"
55
+ redis.db.should == 23
56
56
  end
57
57
 
58
58
  it "should instantiate a ring" do
@@ -75,11 +75,13 @@ module Sinatra
75
75
  end
76
76
  end
77
77
 
78
- # it "should write the data with expiration time" do
79
- # @store.write "rabbit", @white_rabbit, :expires_in => 1.second
80
- # @store.read("rabbit").should === @white_rabbit ; sleep 2
81
- # @store.read("rabbit").should be_nil
82
- # end
78
+ it "should write the data with expiration time" do
79
+ with_store_management do |store|
80
+ store.write "rabbit", @white_rabbit, :expires_in => 1.second
81
+ store.read("rabbit").should === @white_rabbit ; sleep 2
82
+ store.read("rabbit").should be_nil
83
+ end
84
+ end
83
85
 
84
86
  it "should not write data if :unless_exist option is true" do
85
87
  with_store_management do |store|
@@ -172,15 +174,15 @@ module Sinatra
172
174
  store.fetch("rub-a-dub") { "Flora de Cana" }
173
175
  store.fetch("rub-a-dub").should === "Flora de Cana"
174
176
  store.fetch("rabbit", :force => true).should be_nil # force cache miss
175
- # store.fetch("rabbit", :force => true, :expires_in => 1.second) { @white_rabbit }
176
- # store.fetch("rabbit").should === @white_rabbit ; sleep 2
177
- # store.fetch("rabbit").should be_nil
177
+ store.fetch("rabbit", :force => true, :expires_in => 1.second) { @white_rabbit }
178
+ store.fetch("rabbit").should === @white_rabbit ; sleep 2
179
+ store.fetch("rabbit").should be_nil
178
180
  end
179
181
  end
180
182
 
181
183
  private
182
184
  def instantiate_store(addresses = nil)
183
- RedisStore.new(addresses).instance_variable_get(:@data)
185
+ Sinatra::Cache::RedisStore.new(addresses).instance_variable_get(:@data)
184
186
  end
185
187
 
186
188
  def with_store_management
@@ -2,8 +2,8 @@ require File.join(File.dirname(__FILE__), "/../../../spec_helper")
2
2
 
3
3
  module Rack
4
4
  module Cache
5
- class MetaStore
6
- describe "Redis" do
5
+ class EntityStore
6
+ describe "Rack::Cache::EntityStore::Redis" do
7
7
  before(:each) do
8
8
  @store = Rack::Cache::EntityStore::Redis.new :host => "localhost"
9
9
  end
@@ -13,17 +13,17 @@ module Rack
13
13
  end
14
14
 
15
15
  it "should resolve the connection uri" do
16
- cache = Redis.resolve(uri("redis://127.0.0.1")).cache
17
- cache.should be_kind_of(::MarshaledRedis)
16
+ cache = Rack::Cache::EntityStore::Redis.resolve(uri("redis://127.0.0.1")).cache
17
+ cache.should be_kind_of(::Redis)
18
18
  cache.host.should == "127.0.0.1"
19
19
  cache.port.should == "6379"
20
- cache.instance_variable_get(:@db).should == "0"
20
+ cache.db.should == "0"
21
21
 
22
- cache = Redis.resolve(uri("redis://127.0.0.1:6380")).cache
22
+ cache = Rack::Cache::EntityStore::Redis.resolve(uri("redis://127.0.0.1:6380")).cache
23
23
  cache.port.should == 6380
24
24
 
25
- cache = Redis.resolve(uri("redis://127.0.0.1/11")).cache
26
- cache.instance_variable_get(:@db).should == "11"
25
+ cache = Rack::Cache::EntityStore::Redis.resolve(uri("redis://127.0.0.1/11")).cache
26
+ cache.db.should == "11"
27
27
  end
28
28
 
29
29
  it 'responds to all required messages' do
@@ -3,9 +3,9 @@ require File.join(File.dirname(__FILE__), "/../../../spec_helper")
3
3
  module Rack
4
4
  module Cache
5
5
  class MetaStore
6
- describe "Redis" do
6
+ describe "Rack::Cache::MetaStore::Redis" do
7
7
  before(:each) do
8
- @store = Redis.resolve uri("redis://127.0.0.1")
8
+ @store = Rack::Cache::MetaStore::Redis.resolve uri("redis://127.0.0.1")
9
9
  end
10
10
 
11
11
  it "should have the class referenced by homonym constant" do
@@ -13,17 +13,17 @@ module Rack
13
13
  end
14
14
 
15
15
  it "should resolve the connection uri" do
16
- cache = Redis.resolve(uri("redis://127.0.0.1")).cache
16
+ cache = Rack::Cache::MetaStore::Redis.resolve(uri("redis://127.0.0.1")).cache
17
17
  cache.should be_kind_of(::MarshaledRedis)
18
18
  cache.host.should == "127.0.0.1"
19
19
  cache.port.should == "6379"
20
- cache.instance_variable_get(:@db).should == "0"
20
+ cache.db.should == "0"
21
21
 
22
- cache = Redis.resolve(uri("redis://127.0.0.1:6380")).cache
22
+ cache = Rack::Cache::MetaStore::Redis.resolve(uri("redis://127.0.0.1:6380")).cache
23
23
  cache.port.should == 6380
24
24
 
25
- cache = Redis.resolve(uri("redis://127.0.0.1/11")).cache
26
- cache.instance_variable_get(:@db).should == "11"
25
+ cache = Rack::Cache::MetaStore::Redis.resolve(uri("redis://127.0.0.1/11")).cache
26
+ cache.db.should == "11"
27
27
  end
28
28
 
29
29
  it 'writes a list of negotation tuples with #write' do
@@ -0,0 +1,238 @@
1
+ require File.join(File.dirname(__FILE__), "/../../spec_helper")
2
+
3
+ module Rack
4
+ module Session
5
+ describe "Rack::Session::Redis" do
6
+ before(:each) do
7
+ @session_key = Rack::Session::Redis::DEFAULT_OPTIONS[:key]
8
+ @session_match = /#{@session_key}=[0-9a-fA-F]+;/
9
+ @incrementor = lambda do |env|
10
+ env["rack.session"]["counter"] ||= 0
11
+ env["rack.session"]["counter"] += 1
12
+ Rack::Response.new(env["rack.session"].inspect).to_a
13
+ end
14
+ @drop_session = proc do |env|
15
+ env['rack.session.options'][:drop] = true
16
+ @incrementor.call(env)
17
+ end
18
+ @renew_session = proc do |env|
19
+ env['rack.session.options'][:renew] = true
20
+ @incrementor.call(env)
21
+ end
22
+ @defer_session = proc do |env|
23
+ env['rack.session.options'][:defer] = true
24
+ @incrementor.call(env)
25
+ end
26
+ end
27
+
28
+ it "should specify connection params" do
29
+ pool = Rack::Session::Redis.new(@incrementor, :redis_server => "localhost:6380/1").pool
30
+ pool.should be_kind_of(MarshaledRedis)
31
+ pool.host.should == "localhost"
32
+ pool.port.should == "6380"
33
+ pool.db.should == 1
34
+
35
+ pool = Rack::Session::Redis.new(@incrementor, :redis_server => ["localhost:6379", "localhost:6380"]).pool
36
+ pool.should be_kind_of(DistributedMarshaledRedis)
37
+ end
38
+
39
+ it "creates a new cookie" do
40
+ pool = Rack::Session::Redis.new(@incrementor)
41
+ res = Rack::MockRequest.new(pool).get("/")
42
+ res["Set-Cookie"].should match(/#{@session_key}=/)
43
+ res.body.should == '{"counter"=>1}'
44
+ end
45
+
46
+ it "determines session from a cookie" do
47
+ pool = Rack::Session::Redis.new(@incrementor)
48
+ req = Rack::MockRequest.new(pool)
49
+ res = req.get("/")
50
+ cookie = res["Set-Cookie"]
51
+ req.get("/", "HTTP_COOKIE" => cookie).
52
+ body.should == '{"counter"=>2}'
53
+ req.get("/", "HTTP_COOKIE" => cookie).
54
+ body.should == '{"counter"=>3}'
55
+ end
56
+
57
+ it "survives nonexistant cookies" do
58
+ bad_cookie = "rack.session=blarghfasel"
59
+ pool = Rack::Session::Redis.new(@incrementor)
60
+ res = Rack::MockRequest.new(pool).
61
+ get("/", "HTTP_COOKIE" => bad_cookie)
62
+ res.body.should == '{"counter"=>1}'
63
+ cookie = res["Set-Cookie"][@session_match]
64
+ cookie.should_not match(/#{bad_cookie}/)
65
+ end
66
+
67
+ it "should maintain freshness" do
68
+ pool = Rack::Session::Redis.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::Redis.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::Redis.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::Redis.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::Memcache'
157
+ pool = Rack::Session::Redis.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
+ end
237
+ end
238
+ end
@@ -21,7 +21,7 @@ describe "DistributedMarshaledRedis" do
21
21
  mr = dmr.ring.nodes.first
22
22
  mr.host.should == "redis.com"
23
23
  mr.port.should == "6380"
24
- mr.instance_variable_get(:@db).should == 1
24
+ mr.db.should == 1
25
25
  end
26
26
 
27
27
  it "should set an object" do
@@ -0,0 +1,34 @@
1
+ require File.join(File.dirname(__FILE__), "/../spec_helper")
2
+
3
+ describe "RedisFactory" do
4
+ it "should instantiate a MarshaledRedis store" do
5
+ store = RedisFactory.create
6
+ store.should be_kind_of(MarshaledRedis)
7
+ store.host.should == "localhost"
8
+ store.port.should == "6379"
9
+ store.db.should == 0
10
+ end
11
+
12
+ it "should allow to specify host" do
13
+ store = RedisFactory.create "redis.com"
14
+ store.host.should == "redis.com"
15
+ end
16
+
17
+ it "should allow to specify port" do
18
+ store = RedisFactory.create "redis.com:6380"
19
+ store.host.should == "redis.com"
20
+ store.port.should == "6380"
21
+ end
22
+
23
+ it "should allow to specify db" do
24
+ store = RedisFactory.create "redis.com:6380/23"
25
+ store.host.should == "redis.com"
26
+ store.port.should == "6380"
27
+ store.db.should == 23
28
+ end
29
+
30
+ it "should instantiate a DistributedMarshaledRedis store" do
31
+ store = RedisFactory.create "localhost:6379", "localhost:6380"
32
+ store.should be_kind_of(DistributedMarshaledRedis)
33
+ end
34
+ end
data/spec/spec_helper.rb CHANGED
@@ -11,3 +11,5 @@ require "redis-store"
11
11
  require "activesupport"
12
12
  require "cache/rails/redis_store"
13
13
  require "cache/sinatra/redis_store"
14
+
15
+ class Redis; attr_reader :db end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jodosha-redis-store
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Luca Guidi
@@ -9,11 +9,11 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-04-30 00:00:00 -07:00
12
+ date: 2009-05-03 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
16
- description: Redis cache and session stores for Ruby web frameworks
16
+ description: Rack::Session, Rack::Cache and cache Redis stores for Ruby web frameworks.
17
17
  email: guidi.luca@gmail.com
18
18
  executables: []
19
19
 
@@ -30,9 +30,13 @@ files:
30
30
  - lib/cache/sinatra/redis_store.rb
31
31
  - lib/rack/cache/redis_entitystore.rb
32
32
  - lib/rack/cache/redis_metastore.rb
33
+ - lib/rack/session/merb.rb
34
+ - lib/rack/session/redis.rb
33
35
  - lib/redis-store.rb
34
36
  - lib/redis/distributed_marshaled_redis.rb
35
37
  - lib/redis/marshaled_redis.rb
38
+ - lib/redis/redis.rb
39
+ - lib/redis/redis_factory.rb
36
40
  - redis-store.gemspec
37
41
  - spec/cache/merb/redis_store_spec.rb
38
42
  - spec/cache/rails/redis_store_spec.rb
@@ -43,8 +47,10 @@ files:
43
47
  - spec/rack/cache/entitystore/pony.jpg
44
48
  - spec/rack/cache/entitystore/redis_spec.rb
45
49
  - spec/rack/cache/metastore/redis_spec.rb
50
+ - spec/rack/session/redis_spec.rb
46
51
  - spec/redis/distributed_marshaled_redis_spec.rb
47
52
  - spec/redis/marshaled_redis_spec.rb
53
+ - spec/redis/redis_factory_spec.rb
48
54
  - spec/spec_helper.rb
49
55
  has_rdoc: true
50
56
  homepage: http://lucaguidi.com
@@ -71,12 +77,14 @@ rubyforge_project:
71
77
  rubygems_version: 1.2.0
72
78
  signing_key:
73
79
  specification_version: 2
74
- summary: Redis cache and session stores for Ruby web frameworks
80
+ summary: Rack::Session, Rack::Cache and cache Redis stores for Ruby web frameworks.
75
81
  test_files:
76
82
  - spec/cache/merb/redis_store_spec.rb
77
83
  - spec/cache/rails/redis_store_spec.rb
78
84
  - spec/cache/sinatra/redis_store_spec.rb
79
85
  - spec/rack/cache/entitystore/redis_spec.rb
80
86
  - spec/rack/cache/metastore/redis_spec.rb
87
+ - spec/rack/session/redis_spec.rb
81
88
  - spec/redis/distributed_marshaled_redis_spec.rb
82
89
  - spec/redis/marshaled_redis_spec.rb
90
+ - spec/redis/redis_factory_spec.rb