dsander-redis-store 0.3.8

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.
@@ -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.exists 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.del 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.del(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.del 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,32 @@
1
+ require "dsander-redis"
2
+ require "dsander-redis/dist_redis"
3
+ require "dsander-redis/redis_factory"
4
+ require "dsander-redis/marshaled_redis"
5
+ require "dsander-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/key"
30
+ require "rack/cache/redis_metastore"
31
+ require "rack/cache/redis_entitystore"
32
+ 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 = Redis::HashRing.new nodes
7
+ end
8
+
9
+ alias_method :flushdb, :delete_cloud!
10
+ end
@@ -0,0 +1,38 @@
1
+ class MarshaledRedis < Redis::Client
2
+ def set(key, val, options = nil)
3
+ if options && expires_in(options)
4
+ set_with_expire key, val, expires_in(options)
5
+ else
6
+ super key, (raw?(options) ? val : Marshal.dump(val) )
7
+ end
8
+ end
9
+
10
+ def setnx(key, val, options = nil)
11
+ val = Marshal.dump val unless raw?(options)
12
+ super key, val
13
+ end
14
+
15
+ def get(key, options = nil)
16
+ result = call_command([:get, key])
17
+ result = Marshal.load result if unmarshal?(result, options)
18
+ result
19
+ end
20
+ def multi(&blk)
21
+ yield(self)
22
+ end
23
+ private
24
+ def unmarshal?(result, options)
25
+ result && result.size > 0 && !raw?(options)
26
+ end
27
+
28
+ def raw?(options)
29
+ options && options[:raw]
30
+ end
31
+
32
+ def expires_in(options)
33
+ if options
34
+ # Rack::Session Merb Rails/Sinatra
35
+ options[:expire_after] || options[:expires_in] || options[:expire_in]
36
+ end
37
+ end
38
+ 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,81 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{redis-store}
8
+ s.version = "0.3.7"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Luca Guidi"]
12
+ s.date = %q{2010-04-21}
13
+ s.description = %q{Rack::Session, Rack::Cache and cache Redis stores for Ruby web frameworks.}
14
+ s.email = %q{guidi.luca@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "README.md"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ "Gemfile",
21
+ "MIT-LICENSE",
22
+ "README.md",
23
+ "Rakefile",
24
+ "VERSION",
25
+ "lib/cache/merb/redis_store.rb",
26
+ "lib/cache/rails/redis_store.rb",
27
+ "lib/cache/sinatra/redis_store.rb",
28
+ "lib/rack/cache/redis_entitystore.rb",
29
+ "lib/rack/cache/redis_metastore.rb",
30
+ "lib/rack/session/merb.rb",
31
+ "lib/rack/session/redis.rb",
32
+ "lib/redis-store.rb",
33
+ "lib/redis/distributed_marshaled_redis.rb",
34
+ "lib/redis/marshaled_redis.rb",
35
+ "lib/redis/redis_factory.rb",
36
+ "redis-store.gemspec",
37
+ "spec/cache/merb/redis_store_spec.rb",
38
+ "spec/cache/rails/redis_store_spec.rb",
39
+ "spec/cache/sinatra/redis_store_spec.rb",
40
+ "spec/config/master.conf",
41
+ "spec/config/single.conf",
42
+ "spec/config/slave.conf",
43
+ "spec/rack/cache/entitystore/pony.jpg",
44
+ "spec/rack/cache/entitystore/redis_spec.rb",
45
+ "spec/rack/cache/metastore/redis_spec.rb",
46
+ "spec/rack/session/redis_spec.rb",
47
+ "spec/redis/distributed_marshaled_redis_spec.rb",
48
+ "spec/redis/marshaled_redis_spec.rb",
49
+ "spec/redis/redis_factory_spec.rb",
50
+ "spec/spec_helper.rb",
51
+ "tasks/redis.tasks.rb"
52
+ ]
53
+ s.homepage = %q{http://github.com/jodosha/redis-store}
54
+ s.rdoc_options = ["--charset=UTF-8"]
55
+ s.require_paths = ["lib"]
56
+ s.rubygems_version = %q{1.3.6}
57
+ s.summary = %q{Rack::Session, Rack::Cache and cache Redis stores for Ruby web frameworks.}
58
+ s.test_files = [
59
+ "spec/rack/session/redis_spec.rb",
60
+ "spec/rack/cache/entitystore/redis_spec.rb",
61
+ "spec/rack/cache/metastore/redis_spec.rb",
62
+ "spec/cache/sinatra/redis_store_spec.rb",
63
+ "spec/cache/rails/redis_store_spec.rb",
64
+ "spec/cache/merb/redis_store_spec.rb",
65
+ "spec/spec_helper.rb",
66
+ "spec/redis/distributed_marshaled_redis_spec.rb",
67
+ "spec/redis/redis_factory_spec.rb",
68
+ "spec/redis/marshaled_redis_spec.rb"
69
+ ]
70
+
71
+ if s.respond_to? :specification_version then
72
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
73
+ s.specification_version = 3
74
+
75
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
76
+ else
77
+ end
78
+ else
79
+ end
80
+ end
81
+
@@ -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 == Marshal.dump(@rabbit)
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("*").flatten.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("*").flatten.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