xlymian-redis-store 0.3.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,32 @@
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/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 = 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 = call_command([:get, 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,28 @@
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, password = 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
+ address[:password] = password if password
23
+ result << address
24
+ result
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,80 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
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{2009-11-15}
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.5}
57
+ s.summary = %q{Rack::Session, Rack::Cache and cache Redis stores for Ruby web frameworks.}
58
+ s.test_files = [
59
+ "spec/cache/merb/redis_store_spec.rb",
60
+ "spec/cache/rails/redis_store_spec.rb",
61
+ "spec/cache/sinatra/redis_store_spec.rb",
62
+ "spec/rack/cache/entitystore/redis_spec.rb",
63
+ "spec/rack/cache/metastore/redis_spec.rb",
64
+ "spec/rack/session/redis_spec.rb",
65
+ "spec/redis/distributed_marshaled_redis_spec.rb",
66
+ "spec/redis/marshaled_redis_spec.rb",
67
+ "spec/redis/redis_factory_spec.rb",
68
+ "spec/spec_helper.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
@@ -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("*").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