nono-redis-store 1.0.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.
Files changed (48) hide show
  1. data/.gitignore +11 -0
  2. data/CHANGELOG +262 -0
  3. data/Gemfile +33 -0
  4. data/Gemfile.lock +194 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.md +191 -0
  7. data/Rakefile +60 -0
  8. data/VERSION +1 -0
  9. data/lib/action_controller/session/redis_session_store.rb +74 -0
  10. data/lib/active_support/cache/redis_store.rb +228 -0
  11. data/lib/cache/merb/redis_store.rb +75 -0
  12. data/lib/cache/sinatra/redis_store.rb +126 -0
  13. data/lib/i18n/backend/redis.rb +67 -0
  14. data/lib/rack/cache/redis_entitystore.rb +51 -0
  15. data/lib/rack/cache/redis_metastore.rb +42 -0
  16. data/lib/rack/session/merb.rb +32 -0
  17. data/lib/rack/session/redis.rb +81 -0
  18. data/lib/redis-store.rb +44 -0
  19. data/lib/redis/distributed_store.rb +35 -0
  20. data/lib/redis/factory.rb +46 -0
  21. data/lib/redis/store.rb +30 -0
  22. data/lib/redis/store/interface.rb +17 -0
  23. data/lib/redis/store/marshalling.rb +41 -0
  24. data/lib/redis/store/namespace.rb +54 -0
  25. data/lib/redis/store/ttl.rb +37 -0
  26. data/lib/redis/store/version.rb +11 -0
  27. data/redis-store.gemspec +103 -0
  28. data/spec/action_controller/session/redis_session_store_spec.rb +121 -0
  29. data/spec/active_support/cache/redis_store_spec.rb +405 -0
  30. data/spec/cache/merb/redis_store_spec.rb +143 -0
  31. data/spec/cache/sinatra/redis_store_spec.rb +192 -0
  32. data/spec/config/master.conf +312 -0
  33. data/spec/config/single.conf +312 -0
  34. data/spec/config/slave.conf +312 -0
  35. data/spec/i18n/backend/redis_spec.rb +56 -0
  36. data/spec/rack/cache/entitystore/pony.jpg +0 -0
  37. data/spec/rack/cache/entitystore/redis_spec.rb +120 -0
  38. data/spec/rack/cache/metastore/redis_spec.rb +255 -0
  39. data/spec/rack/session/redis_spec.rb +234 -0
  40. data/spec/redis/distributed_store_spec.rb +47 -0
  41. data/spec/redis/factory_spec.rb +110 -0
  42. data/spec/redis/store/interface_spec.rb +23 -0
  43. data/spec/redis/store/marshalling_spec.rb +83 -0
  44. data/spec/redis/store/namespace_spec.rb +76 -0
  45. data/spec/redis/store/version_spec.rb +7 -0
  46. data/spec/spec_helper.rb +43 -0
  47. data/tasks/redis.tasks.rb +220 -0
  48. metadata +141 -0
@@ -0,0 +1,44 @@
1
+ require "redis"
2
+ require "redis/distributed"
3
+ require "redis/factory"
4
+ require "redis/store/interface"
5
+ require "redis/store/ttl"
6
+ require "redis/store/namespace"
7
+ require "redis/store/marshalling"
8
+ require "redis/store/version"
9
+ require "redis/store"
10
+ require "redis/distributed_store"
11
+
12
+ # Cache store
13
+ if defined?(Sinatra)
14
+ require "cache/sinatra/redis_store"
15
+ elsif defined?(Merb)
16
+ # HACK for cyclic dependency: redis-store is required before merb-cache
17
+ module Merb; module Cache; class AbstractStore; end end end
18
+ require "cache/merb/redis_store"
19
+ end
20
+
21
+ # Rack::Session
22
+ if defined?(Rack::Session)
23
+ require "rack/session/abstract/id"
24
+ require "rack/session/redis"
25
+ if defined?(Merb)
26
+ require "rack/session/merb"
27
+ end
28
+ end
29
+
30
+ # ActionDispatch::Session
31
+ if ::Redis::Store.rails3?
32
+ require "action_controller/session/redis_session_store"
33
+ end
34
+
35
+ # Rack::Cache
36
+ if defined?(Rack::Cache)
37
+ require "rack/cache/key"
38
+ require "rack/cache/redis_metastore"
39
+ require "rack/cache/redis_entitystore"
40
+ end
41
+
42
+ if defined?(I18n)
43
+ require "i18n/backend/redis"
44
+ end
@@ -0,0 +1,35 @@
1
+ class Redis
2
+ class DistributedStore < Distributed
3
+ attr_reader :ring
4
+
5
+ def initialize(addresses, options = { })
6
+ nodes = addresses.map do |address|
7
+ ::Redis::Store.new address
8
+ end
9
+ _extend_namespace options
10
+ @ring = Redis::HashRing.new nodes
11
+ end
12
+
13
+ def nodes
14
+ ring.nodes
15
+ end
16
+
17
+ def set(key, value, options = nil)
18
+ node_for(key).set(key, value, options)
19
+ end
20
+
21
+ def get(key, options = nil)
22
+ node_for(key).get(key, options)
23
+ end
24
+
25
+ def setnx(key, value, options = nil)
26
+ node_for(key).setnx(key, value, options)
27
+ end
28
+
29
+ private
30
+ def _extend_namespace(options)
31
+ @namespace = options[:namespace]
32
+ extend ::Redis::Store::Namespace if @namespace
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,46 @@
1
+ class Redis
2
+ class Factory
3
+ def self.create(*redis_client_options)
4
+ redis_client_options = redis_client_options.flatten.compact.inject([]) do |result, address|
5
+ result << convert_to_redis_client_options(address)
6
+ result
7
+ end
8
+ if redis_client_options.size > 1
9
+ ::Redis::DistributedStore.new redis_client_options
10
+ else
11
+ ::Redis::Store.new redis_client_options.first || {}
12
+ end
13
+ end
14
+
15
+ def self.convert_to_redis_client_options(address_or_options)
16
+ if address_or_options.is_a?(Hash)
17
+ options = address_or_options.dup
18
+ options[:namespace] ||= options.delete(:key_prefix) # RailsSessionStore
19
+ options
20
+ else
21
+ if address_or_options =~ /redis\:\/\//
22
+ require 'uri'
23
+ uri = URI.parse address_or_options
24
+ _, db, namespace = if uri.path
25
+ uri.path.split /\//
26
+ end
27
+ else
28
+ warn "[DEPRECATION] `#{address_or_options}` is deprecated. Please use `redis://#{address_or_options}` instead."
29
+ address_or_options, password = address_or_options.split(/\@/).reverse
30
+ password = password.gsub(/\:/, "") if password
31
+ host, port = address_or_options.split /\:/
32
+ port, db, namespace = port.split /\// if port
33
+ end
34
+
35
+ options = {}
36
+ options[:host] = host || uri && uri.host
37
+ options[:port] = port || uri && uri.port
38
+ options[:db] = db.to_i if db
39
+ options[:namespace] = namespace if namespace
40
+ options[:password] = password || uri && uri.password
41
+ options[:marshalling] = false if RUBY_VERSION =~ /1\.9/
42
+ options
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,30 @@
1
+ class Redis
2
+ class Store < self
3
+ include Ttl, Interface
4
+
5
+ def initialize(options = { })
6
+ super
7
+ _extend_marshalling options
8
+ _extend_namespace options
9
+ end
10
+
11
+ def self.rails3? #:nodoc:
12
+ defined?(::Rails) && ::Rails::VERSION::MAJOR == 3
13
+ end
14
+
15
+ def to_s
16
+ "Redis Client connected to #{@client.host}:#{@client.port} against DB #{@client.db}"
17
+ end
18
+
19
+ private
20
+ def _extend_marshalling(options)
21
+ @marshalling = !(options[:marshalling] === false) # HACK - TODO delegate to Factory
22
+ extend Marshalling if @marshalling
23
+ end
24
+
25
+ def _extend_namespace(options)
26
+ @namespace = options[:namespace]
27
+ extend Namespace if @namespace
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,17 @@
1
+ class Redis
2
+ class Store < self
3
+ module Interface
4
+ def get(key, options = nil)
5
+ super(key)
6
+ end
7
+
8
+ def set(key, value, options = nil)
9
+ super(key, value)
10
+ end
11
+
12
+ def setnx(key, value, options = nil)
13
+ super(key, value)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,41 @@
1
+ class Redis
2
+ class Store < self
3
+ module Marshalling
4
+ def set(key, value, options = nil)
5
+ _marshal(value, options) { |value| super key, value, options }
6
+ end
7
+
8
+ def setnx(key, value, options = nil)
9
+ _marshal(value, options) { |value| super key, value, options }
10
+ end
11
+
12
+ def get(key, options = nil)
13
+ _unmarshal super(key), options
14
+ end
15
+
16
+ def mget(*keys)
17
+ options = keys.flatten.pop if keys.flatten.last.is_a?(Hash)
18
+ super(*keys).map do |result|
19
+ _unmarshal result, options
20
+ end
21
+ end
22
+
23
+ private
24
+ def _marshal(val, options)
25
+ yield marshal?(options) ? val : Marshal.dump(val)
26
+ end
27
+
28
+ def _unmarshal(val, options)
29
+ unmarshal?(val, options) ? Marshal.load(val) : val
30
+ end
31
+
32
+ def marshal?(options)
33
+ options && options[:raw]
34
+ end
35
+
36
+ def unmarshal?(result, options)
37
+ result && result.size > 0 && !marshal?(options)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,54 @@
1
+ class Redis
2
+ class Store < self
3
+ module Namespace
4
+ def set(key, val, options = nil)
5
+ namespace(key) { |key| super(key, val, options) }
6
+ end
7
+
8
+ def setnx(key, val, options = nil)
9
+ namespace(key) { |key| super(key, val, options) }
10
+ end
11
+
12
+ def get(key, options = nil)
13
+ namespace(key) { |key| super(key, options) }
14
+ end
15
+
16
+ def exists(key)
17
+ namespace(key) { |key| super(key) }
18
+ end
19
+
20
+ def incrby(key, increment)
21
+ namespace(key) { |key| super(key, increment) }
22
+ end
23
+
24
+ def decrby(key, increment)
25
+ namespace(key) { |key| super(key, increment) }
26
+ end
27
+
28
+ def keys(pattern = "*")
29
+ namespace(pattern) { |pattern| super(pattern) }
30
+ end
31
+
32
+ def del(*keys)
33
+ super *keys.map {|key| interpolate(key) }
34
+ end
35
+
36
+ def mget(*keys)
37
+ super *keys.map {|key| interpolate(key) }
38
+ end
39
+
40
+ def to_s
41
+ "#{super} with namespace #{@namespace}"
42
+ end
43
+
44
+ private
45
+ def namespace(key)
46
+ yield interpolate(key)
47
+ end
48
+
49
+ def interpolate(key)
50
+ key.match(%r{^#{@namespace}\:}) ? key : "#{@namespace}:#{key}"
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,37 @@
1
+ class Redis
2
+ class Store < self
3
+ module Ttl
4
+ def set(key, value, options = nil)
5
+ if ttl = expires_in(options)
6
+ setex(key, ttl, value)
7
+ else
8
+ super(key, value)
9
+ end
10
+ end
11
+
12
+ def setnx(key, value, options = nil)
13
+ if ttl = expires_in(options)
14
+ setnx_with_expire(key, value, ttl)
15
+ else
16
+ super(key, value)
17
+ end
18
+ end
19
+
20
+ protected
21
+ def setnx_with_expire(key, value, ttl)
22
+ multi do
23
+ setnx(key, value)
24
+ expire(key, expires_in)
25
+ end
26
+ end
27
+
28
+ private
29
+ def expires_in(options)
30
+ if options
31
+ # Rack::Session Merb Rails/Sinatra
32
+ options[:expire_after] || options[:expires_in] || options[:expire_in]
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,11 @@
1
+ class Redis
2
+ class Store < self
3
+ module VERSION #:nodoc:
4
+ MAJOR = 1
5
+ MINOR = 0
6
+ TINY = 0
7
+
8
+ STRING = [MAJOR, MINOR, TINY].join('.')
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,103 @@
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{nono-redis-store}
8
+ s.version = "1.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Luca Guidi", "Bruno Michel"]
12
+ s.date = %q{2010-11-15}
13
+ s.description = %q{Namespaced Rack::Session, Rack::Cache, I18n and cache Redis stores for Ruby web frameworks.}
14
+ s.email = %q{brmichel@free.fr}
15
+ s.extra_rdoc_files = [
16
+ "README.md"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ "CHANGELOG",
21
+ "Gemfile",
22
+ "Gemfile.lock",
23
+ "MIT-LICENSE",
24
+ "README.md",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "lib/action_controller/session/redis_session_store.rb",
28
+ "lib/active_support/cache/redis_store.rb",
29
+ "lib/cache/merb/redis_store.rb",
30
+ "lib/cache/sinatra/redis_store.rb",
31
+ "lib/i18n/backend/redis.rb",
32
+ "lib/rack/cache/redis_entitystore.rb",
33
+ "lib/rack/cache/redis_metastore.rb",
34
+ "lib/rack/session/merb.rb",
35
+ "lib/rack/session/redis.rb",
36
+ "lib/redis-store.rb",
37
+ "lib/redis/distributed_store.rb",
38
+ "lib/redis/factory.rb",
39
+ "lib/redis/store.rb",
40
+ "lib/redis/store/interface.rb",
41
+ "lib/redis/store/marshalling.rb",
42
+ "lib/redis/store/namespace.rb",
43
+ "lib/redis/store/ttl.rb",
44
+ "lib/redis/store/version.rb",
45
+ "redis-store.gemspec",
46
+ "spec/action_controller/session/redis_session_store_spec.rb",
47
+ "spec/active_support/cache/redis_store_spec.rb",
48
+ "spec/cache/merb/redis_store_spec.rb",
49
+ "spec/cache/sinatra/redis_store_spec.rb",
50
+ "spec/config/master.conf",
51
+ "spec/config/single.conf",
52
+ "spec/config/slave.conf",
53
+ "spec/i18n/backend/redis_spec.rb",
54
+ "spec/rack/cache/entitystore/pony.jpg",
55
+ "spec/rack/cache/entitystore/redis_spec.rb",
56
+ "spec/rack/cache/metastore/redis_spec.rb",
57
+ "spec/rack/session/redis_spec.rb",
58
+ "spec/redis/distributed_store_spec.rb",
59
+ "spec/redis/factory_spec.rb",
60
+ "spec/redis/store/interface_spec.rb",
61
+ "spec/redis/store/marshalling_spec.rb",
62
+ "spec/redis/store/namespace_spec.rb",
63
+ "spec/redis/store/version_spec.rb",
64
+ "spec/spec_helper.rb",
65
+ "tasks/redis.tasks.rb"
66
+ ]
67
+ s.homepage = %q{http://github.com/nono/redis-store/tree/nono}
68
+ s.rdoc_options = ["--charset=UTF-8"]
69
+ s.require_paths = ["lib"]
70
+ s.rubygems_version = %q{1.3.7}
71
+ s.summary = %q{Namespaced Rack::Session, Rack::Cache, I18n and cache Redis stores for Ruby web frameworks.}
72
+ s.test_files = [
73
+ "spec/action_controller/session/redis_session_store_spec.rb",
74
+ "spec/active_support/cache/redis_store_spec.rb",
75
+ "spec/cache/merb/redis_store_spec.rb",
76
+ "spec/cache/sinatra/redis_store_spec.rb",
77
+ "spec/i18n/backend/redis_spec.rb",
78
+ "spec/rack/cache/entitystore/redis_spec.rb",
79
+ "spec/rack/cache/metastore/redis_spec.rb",
80
+ "spec/rack/session/redis_spec.rb",
81
+ "spec/redis/distributed_store_spec.rb",
82
+ "spec/redis/factory_spec.rb",
83
+ "spec/redis/store/interface_spec.rb",
84
+ "spec/redis/store/marshalling_spec.rb",
85
+ "spec/redis/store/namespace_spec.rb",
86
+ "spec/redis/store/version_spec.rb",
87
+ "spec/spec_helper.rb"
88
+ ]
89
+
90
+ if s.respond_to? :specification_version then
91
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
92
+ s.specification_version = 3
93
+
94
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
95
+ s.add_runtime_dependency(%q<redis>, [">= 2.0.0"])
96
+ else
97
+ s.add_dependency(%q<redis>, [">= 2.0.0"])
98
+ end
99
+ else
100
+ s.add_dependency(%q<redis>, [">= 2.0.0"])
101
+ end
102
+ end
103
+
@@ -0,0 +1,121 @@
1
+ require 'spec_helper'
2
+ RAILS_SESSION_STORE_CLASS = ::Redis::Store.rails3? ? ActionDispatch::Session::RedisSessionStore : ActionController::Session::RedisSessionStore
3
+
4
+ describe RAILS_SESSION_STORE_CLASS do
5
+ attr_reader :app
6
+
7
+ before :each do
8
+ @app = Object.new
9
+ @store = RAILS_SESSION_STORE_CLASS.new(app)
10
+ @dstore = RAILS_SESSION_STORE_CLASS.new app, :servers => ["redis://127.0.0.1:6380/1", "redis://127.0.0.1:6381/1"]
11
+ @rabbit = OpenStruct.new :name => "bunny"
12
+ @white_rabbit = OpenStruct.new :color => "white"
13
+ @sid = "rabbit"
14
+ @env = {'rack.session.options' => {:id => @sid}}
15
+ with_store_management do |store|
16
+ class << store
17
+ attr_reader :pool
18
+ public :get_session, :set_session, :destroy
19
+ end
20
+ store.set_session(@env, @sid, @rabbit)
21
+ store.pool.del "counter"
22
+ store.pool.del "rub-a-dub"
23
+ end
24
+ end
25
+
26
+ it "should accept string connection params" do
27
+ redis = instantiate_store
28
+ redis.to_s.should == "Redis Client connected to 127.0.0.1:6379 against DB 0"
29
+
30
+ redis = instantiate_store :servers => "redis://localhost"
31
+ redis.to_s.should == "Redis Client connected to localhost:6379 against DB 0"
32
+
33
+ redis = instantiate_store :servers => "redis://localhost:6380"
34
+ redis.to_s.should == "Redis Client connected to localhost:6380 against DB 0"
35
+
36
+ redis = instantiate_store :servers => "redis://localhost:6380/13"
37
+ redis.to_s.should == "Redis Client connected to localhost:6380 against DB 13"
38
+
39
+ redis = instantiate_store :servers => "redis://localhost:6380/13/theplaylist"
40
+ redis.to_s.should == "Redis Client connected to localhost:6380 against DB 13 with namespace theplaylist"
41
+ end
42
+
43
+ it "should accept hash connection params" do
44
+ redis = instantiate_store :servers => [{ :host => "192.168.0.1" }]
45
+ redis.to_s.should == "Redis Client connected to 192.168.0.1:6379 against DB 0"
46
+
47
+ redis = instantiate_store :servers => [{ :port => "6380" }]
48
+ redis.to_s.should == "Redis Client connected to localhost:6380 against DB 0"
49
+
50
+ redis = instantiate_store :servers => [{ :db => 13 }]
51
+ redis.to_s.should == "Redis Client connected to localhost:6379 against DB 13"
52
+
53
+ redis = instantiate_store :servers => [{ :key_prefix => "theplaylist" }]
54
+ redis.to_s.should == "Redis Client connected to localhost:6379 against DB 0 with namespace theplaylist"
55
+ end
56
+
57
+ it "should instantiate a ring" do
58
+ store = instantiate_store
59
+ store.should be_kind_of(Redis::Store)
60
+ store = instantiate_store :servers => ["redis://127.0.0.1:6379/0", "redis://127.0.0.1:6379/1"]
61
+ store.should be_kind_of(Redis::DistributedStore)
62
+ end
63
+
64
+ it "should read the data" do
65
+ with_store_management do |store|
66
+ store.get_session(@env, @sid).should === [@sid, @rabbit]
67
+ end
68
+ end
69
+
70
+ it "should write the data" do
71
+ with_store_management do |store|
72
+ store.set_session(@env, @sid, @white_rabbit)
73
+ store.get_session(@env, @sid).should === [@sid, @white_rabbit]
74
+ end
75
+ end
76
+
77
+ it "should delete the data" do
78
+ with_store_management do |store|
79
+ store.destroy(@env)
80
+ store.get_session(@env, @sid).should === [@sid, {}]
81
+ end
82
+ end
83
+
84
+ it "should write the data with expiration time" do
85
+ with_store_management do |store|
86
+ @env['rack.session.options'].merge!(:expires_in => 1.second)
87
+ store.set_session(@env, @sid, @white_rabbit)
88
+ store.get_session(@env, @sid).should === [@sid, @white_rabbit]; sleep 2
89
+ store.get_session(@env, @sid).should === [@sid, {}]
90
+ end
91
+ end
92
+
93
+ describe "namespace" do
94
+ before :each do
95
+ @namespace = "theplaylist"
96
+ @store = RAILS_SESSION_STORE_CLASS.new(lambda {|| }, :servers => [{ :namespace => @namespace }])
97
+ @pool = @store.instance_variable_get(:@pool)
98
+ @client = @pool.instance_variable_get(:@client)
99
+ end
100
+
101
+ it "should read the data" do
102
+ @client.should_receive(:call).with(:get, "#{@namespace}:#{@sid}")
103
+ @store.send :get_session, @env, @sid
104
+ end
105
+
106
+ it "should write the data" do
107
+ @client.should_receive(:call).with(:set, "#{@namespace}:#{@sid}", Marshal.dump(@white_rabbit))
108
+ @store.send :set_session, @env, @sid, @white_rabbit
109
+ end
110
+ end
111
+
112
+ private
113
+ def instantiate_store(params = { })
114
+ RAILS_SESSION_STORE_CLASS.new(app, params).instance_variable_get(:@pool)
115
+ end
116
+
117
+ def with_store_management
118
+ yield @store
119
+ yield @dstore
120
+ end
121
+ end