redis-store 0.3.6

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of redis-store might be problematic. Click here for more details.

@@ -0,0 +1,51 @@
1
+ module Rack
2
+ module Cache
3
+ class EntityStore
4
+ class RedisBase < EntityStore
5
+ # The underlying ::Redis instance used to communicate with the Redis daemon.
6
+ attr_reader :cache
7
+
8
+ extend Rack::Utils
9
+
10
+ def open(key)
11
+ data = read(key)
12
+ data && [data]
13
+ end
14
+
15
+ def self.resolve(uri)
16
+ db = uri.path.sub(/^\//, '')
17
+ db = "0" if db.empty?
18
+ server = { :host => uri.host, :port => uri.port || "6379", :db => db }
19
+ new server
20
+ end
21
+ end
22
+
23
+ class Redis < RedisBase
24
+ def initialize(server, options = {})
25
+ @cache = ::Redis.new server
26
+ end
27
+
28
+ def exist?(key)
29
+ cache.key? key
30
+ end
31
+
32
+ def read(key)
33
+ cache.get key
34
+ end
35
+
36
+ def write(body)
37
+ buf = StringIO.new
38
+ key, size = slurp(body){|part| buf.write(part) }
39
+ [key, size] if cache.set(key, buf.string)
40
+ end
41
+
42
+ def purge(key)
43
+ cache.delete key
44
+ nil
45
+ end
46
+ end
47
+
48
+ REDIS = Redis
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,42 @@
1
+ module Rack
2
+ module Cache
3
+ class MetaStore
4
+ class RedisBase < MetaStore
5
+ extend Rack::Utils
6
+
7
+ # The ::MarshaledRedis object used to communicate with the Redis daemon.
8
+ attr_reader :cache
9
+
10
+ def self.resolve(uri)
11
+ db = uri.path.sub(/^\//, '')
12
+ db = "0" if db.empty?
13
+ server = { :host => uri.host, :port => uri.port || "6379", :db => db }
14
+ new server
15
+ end
16
+ end
17
+
18
+ class Redis < RedisBase
19
+ def initialize(server, options = {})
20
+ @cache = ::MarshaledRedis.new server
21
+ end
22
+
23
+ def read(key)
24
+ key = hexdigest(key)
25
+ cache.get(key) || []
26
+ end
27
+
28
+ def write(key, entries)
29
+ key = hexdigest(key)
30
+ cache.set(key, entries)
31
+ end
32
+
33
+ def purge(key)
34
+ cache.delete(hexdigest(key))
35
+ nil
36
+ end
37
+ end
38
+
39
+ REDIS = Redis
40
+ end
41
+ end
42
+ 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 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 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
@@ -0,0 +1,31 @@
1
+ require "redis"
2
+ require "dist_redis"
3
+ require "redis/redis_factory"
4
+ require "redis/marshaled_redis"
5
+ require "redis/distributed_marshaled_redis"
6
+
7
+ # Cache store
8
+ if defined?(Sinatra)
9
+ require "cache/sinatra/redis_store"
10
+ elsif defined?(Merb)
11
+ # HACK for cyclic dependency: redis-store is required before merb-cache
12
+ module Merb; module Cache; class AbstractStore; end end end
13
+ require "cache/merb/redis_store"
14
+ elsif defined?(Rails)
15
+ require "cache/rails/redis_store"
16
+ end
17
+
18
+ # Rack::Session
19
+ if defined?(Rack::Session)
20
+ require "rack/session/abstract/id"
21
+ require "rack/session/redis"
22
+ if defined?(Merb)
23
+ require "rack/session/merb"
24
+ end
25
+ end
26
+
27
+ # Rack::Cache
28
+ if defined?(Rack::Cache)
29
+ require "rack/cache/redis_metastore"
30
+ require "rack/cache/redis_entitystore"
31
+ end
@@ -0,0 +1,10 @@
1
+ class DistributedMarshaledRedis < DistRedis
2
+ def initialize(addresses)
3
+ nodes = addresses.map do |address|
4
+ MarshaledRedis.new address
5
+ end
6
+ @ring = HashRing.new nodes
7
+ end
8
+
9
+ alias_method :flush_db, :delete_cloud!
10
+ end
@@ -0,0 +1,33 @@
1
+ class MarshaledRedis < Redis
2
+ def set(key, val, options = nil)
3
+ val = Marshal.dump val unless raw?(options)
4
+ super key, val, expires_in(options)
5
+ end
6
+
7
+ def set_unless_exists(key, val, options = nil)
8
+ val = Marshal.dump val unless raw?(options)
9
+ super key, val
10
+ end
11
+
12
+ def get(key, options = nil)
13
+ result = super key
14
+ result = Marshal.load result if unmarshal?(result, options)
15
+ result
16
+ end
17
+
18
+ private
19
+ def unmarshal?(result, options)
20
+ result && result.size > 0 && !raw?(options)
21
+ end
22
+
23
+ def raw?(options)
24
+ options && options[:raw]
25
+ end
26
+
27
+ def expires_in(options)
28
+ if options
29
+ # Rack::Session Merb Rails/Sinatra
30
+ options[:expire_after] || options[:expires_in] || options[:expire_in]
31
+ end
32
+ end
33
+ 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
@@ -0,0 +1,14 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "redis-store"
3
+ s.version = "0.3.6"
4
+ s.date = "2009-06-18"
5
+ s.summary = "Rack::Session, Rack::Cache and cache Redis stores for Ruby web frameworks."
6
+ s.author = "Luca Guidi"
7
+ s.email = "guidi.luca@gmail.com"
8
+ s.homepage = "http://lucaguidi.com"
9
+ s.description = "Rack::Session, Rack::Cache and cache Redis stores for Ruby web frameworks."
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/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_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
+ s.extra_rdoc_files = ["README.textile"]
14
+ end
@@ -0,0 +1,145 @@
1
+ require File.join(File.dirname(__FILE__), "/../../spec_helper")
2
+
3
+ module Merb
4
+ module Cache
5
+ describe "Merb::Cache::RedisStore" do
6
+ before(:each) do
7
+ @store = Merb::Cache::RedisStore.new
8
+ @dstore = Merb::Cache::RedisStore.new :servers => ["localhost:6380/1", "localhost:6381/1"]
9
+ @rabbit = OpenStruct.new :name => "bunny"
10
+ @white_rabbit = OpenStruct.new :color => "white"
11
+ with_store_management do |store|
12
+ store.write "rabbit", @rabbit
13
+ store.delete "rub-a-dub"
14
+ end
15
+ end
16
+
17
+ it "should accept connection params" do
18
+ redis = instantiate_store
19
+ redis.host.should == "127.0.0.1"
20
+ redis.port.should == 6379
21
+ redis.db.should == 0
22
+
23
+ redis = instantiate_store "localhost"
24
+ redis.host.should == "localhost"
25
+
26
+ redis = instantiate_store "localhost:6380"
27
+ redis.host.should == "localhost"
28
+ redis.port.should == 6380
29
+
30
+ redis = instantiate_store "localhost:6380/13"
31
+ redis.host.should == "localhost"
32
+ redis.port.should == 6380
33
+ redis.db.should == 13
34
+ end
35
+
36
+ it "should instantiate a ring" do
37
+ store = instantiate_store
38
+ store.should be_kind_of(MarshaledRedis)
39
+ store = instantiate_store ["localhost:6379/0", "localhost:6379/1"]
40
+ store.should be_kind_of(DistributedMarshaledRedis)
41
+ end
42
+
43
+ it "should verify if writable" do
44
+ with_store_management do |store|
45
+ store.writable?("rabbit").should be_true
46
+ end
47
+ end
48
+
49
+ it "should read the data" do
50
+ with_store_management do |store|
51
+ store.read("rabbit").should === @rabbit
52
+ end
53
+ end
54
+
55
+ it "should read raw data" do
56
+ with_store_management do |store|
57
+ store.read("rabbit", {}, :raw => true).should == "\004\bU:\017OpenStruct{\006:\tname\"\nbunny"
58
+ end
59
+ end
60
+
61
+ it "should write the data" do
62
+ with_store_management do |store|
63
+ store.write "rabbit", @white_rabbit
64
+ store.read("rabbit").should === @white_rabbit
65
+ end
66
+ end
67
+
68
+ it "should write raw data" do
69
+ with_store_management do |store|
70
+ store.write "rabbit", @white_rabbit, {}, :raw => true
71
+ store.read("rabbit", {}, :raw => true).should == %(#<OpenStruct color="white">)
72
+ end
73
+ end
74
+
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
82
+
83
+ it "should not write data if :unless_exist option is true" do
84
+ with_store_management do |store|
85
+ store.write "rabbit", @white_rabbit, {}, :unless_exist => true
86
+ store.read("rabbit").should === @rabbit
87
+ end
88
+ end
89
+
90
+ it "should write all the data" do
91
+ with_store_management do |store|
92
+ store.write_all "rabbit", @white_rabbit
93
+ store.read("rabbit").should === @white_rabbit
94
+ end
95
+ end
96
+
97
+ it "should fetch data" do
98
+ with_store_management do |store|
99
+ store.fetch("rabbit").should == @rabbit
100
+ store.fetch("rub-a-dub").should be_nil
101
+ store.fetch("rub-a-dub") { "Flora de Cana" }
102
+ store.fetch("rub-a-dub").should === "Flora de Cana"
103
+ end
104
+ end
105
+
106
+ it "should verify existence" do
107
+ with_store_management do |store|
108
+ store.exists?("rabbit").should be_true
109
+ store.exists?("rab-a-dub").should be_false
110
+ end
111
+ end
112
+
113
+ it "should delete data" do
114
+ with_store_management do |store|
115
+ store.delete "rabbit"
116
+ store.read("rabbit").should be_nil
117
+ end
118
+ end
119
+
120
+ it "should delete all the data" do
121
+ with_store_management do |store|
122
+ store.delete_all
123
+ store.instance_variable_get(:@data).keys("*").should be_empty
124
+ end
125
+ end
126
+
127
+ it "should delete all the data with bang method" do
128
+ with_store_management do |store|
129
+ store.delete_all!
130
+ store.instance_variable_get(:@data).keys("*").should be_empty
131
+ end
132
+ end
133
+
134
+ private
135
+ def instantiate_store(addresses = nil)
136
+ Merb::Cache::RedisStore.new(:servers => [addresses].flatten).instance_variable_get(:@data)
137
+ end
138
+
139
+ def with_store_management
140
+ yield @store
141
+ yield @dstore
142
+ end
143
+ end
144
+ end
145
+ end