jodosha-redis-store 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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