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,20 @@
1
+ Copyright (c) 2009 - 2010 Luca Guidi
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,191 @@
1
+ # Namespaced Rack::Session, Rack::Cache, I18n and cache Redis stores for Ruby web frameworks
2
+
3
+ ## Installation
4
+
5
+ Download and install Redis from [http://code.google.com/p/redis/](http://code.google.com/p/redis/)
6
+
7
+ wget http://redis.googlecode.com/files/redis-2.0.0.tar.gz
8
+ tar -zxf redis-2.0.0.tar.gz
9
+ mv redis-2.0.0 redis
10
+ cd redis
11
+ make
12
+
13
+ Install the gem
14
+
15
+ sudo gem install redis-store
16
+
17
+ ## Options
18
+ There are two ways to configure the Redis server options: by an URI string and by an hash.
19
+ By default each store try to connect on `localhost` with the port `6379` and the db `0`.
20
+
21
+ ### String
22
+
23
+ "redis://:secret@192.168.1.100:23682/13/theplaylist"
24
+
25
+ host: 192.168.1.100
26
+ port: 23682
27
+ db: 13
28
+ namespace: theplaylist
29
+ password: secret
30
+
31
+ If you want to specify the `namespace` optional, you have to pass the `db` param too.
32
+ #### __Important__: for now (beta3) `namespace` is only supported for single, non-distributed stores.
33
+
34
+ ### Hash
35
+
36
+ { :host => 192.168.1.100, :port => 23682, :db => 13, :namespace => "theplaylist", :password => "secret" }
37
+
38
+ #### __Important__: for now (beta3) `namespace` is only supported for single, non-distributed stores.
39
+
40
+ ## Cache store
41
+
42
+ Provides a cache store for your Ruby web framework of choice.
43
+
44
+ ### Rails 2.x
45
+
46
+ config.gem "redis-store"
47
+ config.cache_store = :redis_store
48
+
49
+ ### Rails 2.x (with Bundler)
50
+
51
+ # Gemfile
52
+ gem "redis-store"
53
+
54
+ # in your configuration
55
+ config.gem "redis-store"
56
+ config.cache_store = :redis_store
57
+
58
+ ### Rails 3.x
59
+
60
+ # Gemfile
61
+ gem 'rails', '3.0.0'
62
+ gem 'redis'
63
+ gem 'redis-store', '1.0.0.beta3'
64
+
65
+ # config/environments/production.rb
66
+ config.cache_store = :redis_store
67
+
68
+ For advanced configurations scenarios please visit [the wiki](http://wiki.github.com/jodosha/redis-store/rails).
69
+
70
+ ### Merb
71
+
72
+ dependency "redis-store", "1.0.0.beta3"
73
+ dependency("merb-cache", merb_gems_version) do
74
+ Merb::Cache.setup do
75
+ register(:redis, Merb::Cache::RedisStore, :servers => ["127.0.0.1:6379"])
76
+ end
77
+ end
78
+
79
+ ### Sinatra
80
+
81
+ require "sinatra"
82
+ require "redis-store"
83
+ class MyApp < Sinatra::Base
84
+ register Sinatra::Cache
85
+ get "/hi" do
86
+ cache.fetch("greet") { "Hello, World!" }
87
+ end
88
+ end
89
+
90
+ ## Rack::Session
91
+
92
+ Provides a Redis store for Rack::Session. See [http://rack.rubyforge.org/doc/Rack/Session.html](http://rack.rubyforge.org/doc/Rack/Session.html)
93
+
94
+ ### Rack application
95
+
96
+ require "rack"
97
+ require "redis-store"
98
+ require "application"
99
+ use Rack::Session::Redis
100
+ run Application.new
101
+
102
+ ### Rails 2.x
103
+
104
+ config.gem "redis-store"
105
+ ActionController::Base.session_store = :redis_session_store
106
+
107
+ ### Rails 2.x (with Bundler)
108
+
109
+ # Gemfile
110
+ gem "redis-store"
111
+
112
+ # in your configuration
113
+ config.gem "redis-store"
114
+
115
+ # config/initializers/session_store.rb
116
+ ActionController::Base.session_store = :redis_session_store
117
+
118
+ ### Rails 3.x
119
+
120
+ # Gemfile
121
+ gem 'rails', '3.0.0'
122
+ gem 'redis'
123
+ gem 'redis-store', '1.0.0.beta3'
124
+
125
+ # config/initializers/session_store.rb
126
+ MyApp::Application.config.session_store :redis_session_store
127
+
128
+ For advanced configurations scenarios please visit [the wiki](http://wiki.github.com/jodosha/redis-store/rails).
129
+
130
+ ### Merb
131
+
132
+ dependency "redis-store", "1.0.0.beta3"
133
+ Merb::Config.use do |c|
134
+ c[:session_store] = "redis"
135
+ end
136
+ Merb::BootLoader.before_app_loads do
137
+ Merb::SessionContainer.subclasses << "Merb::RedisSession"
138
+ end
139
+
140
+ ### Sinatra
141
+
142
+ require "sinatra"
143
+ require "redis-store"
144
+
145
+ class MyApp < Sinatra::Base
146
+ use Rack::Session::Redis
147
+
148
+ get "/" do
149
+ session[:visited_at] = DateTime.now.to_s # This is stored in Redis
150
+ "Hello, visitor."
151
+ end
152
+ end
153
+
154
+ ## Rack::Cache
155
+
156
+ Provides a Redis store for HTTP caching. See [http://github.com/rtomayko/rack-cache](http://github.com/rtomayko/rack-cache)
157
+
158
+ require "rack"
159
+ require "rack/cache"
160
+ require "redis-store"
161
+ require "application"
162
+ use Rack::Cache,
163
+ :metastore => 'redis://localhost:6379/0',
164
+ :entitystore => 'redis://localhost:6380/1'
165
+ run Application.new
166
+
167
+ ## I18n
168
+
169
+ require "i18n"
170
+ require "redis-store"
171
+ I18n.backend = I18n::Backend::Redis.new
172
+
173
+ The backend accepts the uri string and hash options.
174
+
175
+ ## Running specs
176
+
177
+ gem install jeweler bundler
178
+ git clone git://github.com/jodosha/redis-store.git
179
+ cd redis-store
180
+ bundle install
181
+ REDIS_STORE_ENV=rails3 bundle install # to install Rails 3 gems
182
+ rake dtach:install
183
+ rake redis:install
184
+ rake
185
+ REDIS_STORE_ENV=rails3 rake # to test against Rails 3
186
+
187
+ If you are on **Snow Leopard** you have to run `env ARCHFLAGS="-arch x86_64" bundle install`
188
+
189
+ ## Copyright
190
+
191
+ (c) 2009 - 2010 Luca Guidi - [http://lucaguidi.com](http://lucaguidi.com), released under the MIT license
@@ -0,0 +1,60 @@
1
+ $:.unshift 'lib'
2
+ require 'rubygems'
3
+ require 'rake'
4
+ require 'rake/testtask'
5
+ require 'rake/rdoctask'
6
+ require 'spec/rake/spectask'
7
+
8
+ task :default => "spec:suite"
9
+
10
+ begin
11
+ require "jeweler"
12
+ Jeweler::Tasks.new do |gemspec|
13
+ gemspec.name = "#{ENV["GEM_PREFIX"]}redis-store"
14
+ gemspec.summary = "Namespaced Rack::Session, Rack::Cache, I18n and cache Redis stores for Ruby web frameworks."
15
+ gemspec.description = "Namespaced Rack::Session, Rack::Cache, I18n and cache Redis stores for Ruby web frameworks."
16
+ gemspec.email = "guidi.luca@gmail.com"
17
+ gemspec.homepage = "http://github.com/jodosha/redis-store"
18
+ gemspec.authors = [ "Luca Guidi" ]
19
+ gemspec.executables = [ ]
20
+ gemspec.add_dependency "redis", ">= 2.0.0"
21
+ end
22
+
23
+ Jeweler::GemcutterTasks.new
24
+ rescue LoadError
25
+ puts "Jeweler not available. Install it with: sudo gem install jeweler"
26
+ end
27
+
28
+ namespace :spec do
29
+ desc "Run all the examples by starting a detached Redis instance"
30
+ task :suite => :prepare do
31
+ invoke_with_redis_cluster "spec:run"
32
+ end
33
+
34
+ Spec::Rake::SpecTask.new(:run) do |t|
35
+ t.spec_files = FileList['spec/**/*_spec.rb']
36
+ t.spec_opts = %w(-fs --color)
37
+ end
38
+ end
39
+
40
+ desc "Run all examples with RCov"
41
+ task :rcov => :prepare do
42
+ invoke_with_redis_cluster "rcov_run"
43
+ end
44
+
45
+ Spec::Rake::SpecTask.new(:rcov_run) do |t|
46
+ t.spec_files = FileList['spec/**/*_spec.rb']
47
+ t.rcov = true
48
+ end
49
+
50
+ task :prepare do
51
+ `mkdir -p tmp && rm tmp/*.rdb`
52
+ end
53
+
54
+ namespace :bundle do
55
+ task :clean do
56
+ system "rm -rf ~/.bundle/ ~/.gem/ .bundle/ Gemfile.lock"
57
+ end
58
+ end
59
+
60
+ load "tasks/redis.tasks.rb"
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,74 @@
1
+ require "redis-store"
2
+
3
+ module RedisStore
4
+ module Rack
5
+ module Session
6
+ # Redis session storage for Rails, and for Rails only. Derived from
7
+ # the MemCacheStore code, simply dropping in Redis instead.
8
+ #
9
+ # Options:
10
+ # :key => Same as with the other cookie stores, key name
11
+ # :secret => Encryption secret for the key
12
+ # :host => Redis host name, default is localhost
13
+ # :port => Redis port, default is 6379
14
+ # :db => Database number, defaults to 0. Useful to separate your session storage from other data
15
+ # :key_prefix => Prefix for keys used in Redis, e.g. myapp-. Useful to separate session storage keys visibly from others
16
+ # :expire_after => A number in seconds to set the timeout interval for the session. Will map directly to expiry in Redis
17
+ module Rails
18
+ def initialize(app, options = {})
19
+ # Support old :expires option
20
+ options[:expire_after] ||= options[:expires]
21
+
22
+ super
23
+
24
+ servers = [options[:servers]].flatten.compact.map do |server_options|
25
+ {
26
+ :host => 'localhost',
27
+ :port => '6379',
28
+ :db => 0
29
+ }.update(Redis::Factory.convert_to_redis_client_options(server_options))
30
+ end
31
+
32
+ @pool = Redis::Factory.create(*servers)
33
+ end
34
+
35
+ private
36
+ def get_session(env, sid)
37
+ sid ||= generate_sid
38
+ begin
39
+ session = @pool.get(sid) || {}
40
+ rescue Errno::ECONNREFUSED
41
+ session = {}
42
+ end
43
+ [sid, session]
44
+ end
45
+
46
+ def set_session(env, sid, session_data)
47
+ options = env['rack.session.options']
48
+ @pool.set(sid, session_data, options)
49
+ return(::Redis::Store.rails3? ? sid : true)
50
+ rescue Errno::ECONNREFUSED
51
+ return false
52
+ end
53
+
54
+ def destroy(env)
55
+ if sid = current_session_id(env)
56
+ @pool.del(sid)
57
+ end
58
+ rescue Errno::ECONNREFUSED
59
+ false
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ if ::Redis::Store.rails3?
67
+ class ActionDispatch::Session::RedisSessionStore < ActionDispatch::Session::AbstractStore
68
+ include RedisStore::Rack::Session::Rails
69
+ end
70
+ else
71
+ class ActionController::Session::RedisSessionStore < ActionController::Session::AbstractStore
72
+ include RedisStore::Rack::Session::Rails
73
+ end
74
+ end
@@ -0,0 +1,228 @@
1
+ require "redis-store"
2
+
3
+ module RedisStore
4
+ module Cache
5
+ module Rails2
6
+ # Instantiate the store.
7
+ #
8
+ # Example:
9
+ # RedisStore.new
10
+ # # => host: localhost, port: 6379, db: 0
11
+ #
12
+ # RedisStore.new "example.com"
13
+ # # => host: example.com, port: 6379, db: 0
14
+ #
15
+ # RedisStore.new "example.com:23682"
16
+ # # => host: example.com, port: 23682, db: 0
17
+ #
18
+ # RedisStore.new "example.com:23682/1"
19
+ # # => host: example.com, port: 23682, db: 1
20
+ #
21
+ # RedisStore.new "example.com:23682/1/theplaylist"
22
+ # # => host: example.com, port: 23682, db: 1, namespace: theplaylist
23
+ #
24
+ # RedisStore.new "localhost:6379/0", "localhost:6380/0"
25
+ # # => instantiate a cluster
26
+ def initialize(*addresses)
27
+ @data = ::Redis::Factory.create(addresses)
28
+ end
29
+
30
+ def write(key, value, options = nil)
31
+ super
32
+ method = options && options[:unless_exist] ? :setnx : :set
33
+ @data.send method, key, value, options
34
+ end
35
+
36
+ def read(key, options = nil)
37
+ super
38
+ @data.get key, options
39
+ end
40
+
41
+ def delete(key, options = nil)
42
+ super
43
+ @data.del key
44
+ end
45
+
46
+ def exist?(key, options = nil)
47
+ super
48
+ @data.exists key
49
+ end
50
+
51
+ # Delete objects for matched keys.
52
+ #
53
+ # Example:
54
+ # cache.del_matched "rab*"
55
+ def delete_matched(matcher, options = nil)
56
+ instrument(:delete_matched, matcher, options) do
57
+ @data.keys(matcher).each { |key| @data.del key }
58
+ end
59
+ end
60
+
61
+ private
62
+ def instrument(operation, key, options = nil)
63
+ log(operation.to_s, key, options)
64
+ yield
65
+ end
66
+ end
67
+
68
+ module Rails3
69
+ # Instantiate the store.
70
+ #
71
+ # Example:
72
+ # RedisStore.new
73
+ # # => host: localhost, port: 6379, db: 0
74
+ #
75
+ # RedisStore.new "example.com"
76
+ # # => host: example.com, port: 6379, db: 0
77
+ #
78
+ # RedisStore.new "example.com:23682"
79
+ # # => host: example.com, port: 23682, db: 0
80
+ #
81
+ # RedisStore.new "example.com:23682/1"
82
+ # # => host: example.com, port: 23682, db: 1
83
+ #
84
+ # RedisStore.new "example.com:23682/1/theplaylist"
85
+ # # => host: example.com, port: 23682, db: 1, namespace: theplaylist
86
+ #
87
+ # RedisStore.new "localhost:6379/0", "localhost:6380/0"
88
+ # # => instantiate a cluster
89
+ def initialize(*addresses)
90
+ @data = ::Redis::Factory.create(addresses)
91
+ super(addresses.extract_options!)
92
+ end
93
+
94
+ # Delete objects for matched keys.
95
+ #
96
+ # Example:
97
+ # cache.del_matched "rab*"
98
+ def delete_matched(matcher, options = nil)
99
+ options = merged_options(options)
100
+ instrument(:delete_matched, matcher.inspect) do
101
+ matcher = key_matcher(matcher, options)
102
+ @data.keys(matcher).each { |key| delete_entry(key, options) }
103
+ end
104
+ end
105
+
106
+ protected
107
+ def write_entry(key, entry, options)
108
+ method = options && options[:unless_exist] ? :setnx : :set
109
+ entry = entry.value.to_s if entry.is_a? ActiveSupport::Cache::Entry
110
+ @data.send method, key, entry, options
111
+ end
112
+
113
+ def read_entry(key, options)
114
+ raw_value = @data.get key, options
115
+ if raw_value
116
+ entry = Marshal.load(raw_value) rescue raw_value
117
+ entry.is_a?(ActiveSupport::Cache::Entry) ? entry : ActiveSupport::Cache::Entry.new(entry)
118
+ else
119
+ nil
120
+ end
121
+ end
122
+
123
+ def delete_entry(key, options)
124
+ key.include?("*") ? delete_matched(key, options) : @data.del(key)
125
+ end
126
+
127
+ # Add the namespace defined in the options to a pattern designed to match keys.
128
+ #
129
+ # This implementation is __different__ than ActiveSupport:
130
+ # __it doesn't accept Regular expressions__, because the Redis matcher is designed
131
+ # only for strings with wildcards.
132
+ def key_matcher(pattern, options)
133
+ prefix = options[:namespace].is_a?(Proc) ? options[:namespace].call : options[:namespace]
134
+ if prefix
135
+ raise "Regexps aren't supported, please use string with wildcards." if pattern.is_a?(Regexp)
136
+ "#{prefix}:#{pattern}"
137
+ else
138
+ pattern
139
+ end
140
+ end
141
+ end
142
+
143
+ Store = ::Redis::Store.rails3? ? Rails3 : Rails2
144
+ end
145
+ end
146
+
147
+ module ActiveSupport
148
+ module Cache
149
+ class RedisStore < Store
150
+ include ::RedisStore::Cache::Store
151
+
152
+ # Reads multiple keys from the cache using a single call to the
153
+ # servers for all keys. Options can be passed in the last argument.
154
+ #
155
+ # Example:
156
+ # cache.read_multi "rabbit", "white-rabbit"
157
+ # cache.read_multi "rabbit", "white-rabbit", :raw => true
158
+ def read_multi(*names)
159
+ @data.mget *names
160
+ end
161
+
162
+ # Increment a key in the store.
163
+ #
164
+ # If the key doesn't exist it will be initialized on 0.
165
+ # If the key exist but it isn't a Fixnum it will be initialized on 0.
166
+ #
167
+ # Example:
168
+ # We have two objects in cache:
169
+ # counter # => 23
170
+ # rabbit # => #<Rabbit:0x5eee6c>
171
+ #
172
+ # cache.increment "counter"
173
+ # cache.read "counter", :raw => true # => "24"
174
+ #
175
+ # cache.increment "counter", 6
176
+ # cache.read "counter", :raw => true # => "30"
177
+ #
178
+ # cache.increment "a counter"
179
+ # cache.read "a counter", :raw => true # => "1"
180
+ #
181
+ # cache.increment "rabbit"
182
+ # cache.read "rabbit", :raw => true # => "1"
183
+ def increment(key, amount = 1)
184
+ instrument(:increment, key, :amount => amount) do
185
+ @data.incrby key, amount
186
+ end
187
+ end
188
+
189
+ # Decrement a key in the store
190
+ #
191
+ # If the key doesn't exist it will be initialized on 0.
192
+ # If the key exist but it isn't a Fixnum it will be initialized on 0.
193
+ #
194
+ # Example:
195
+ # We have two objects in cache:
196
+ # counter # => 23
197
+ # rabbit # => #<Rabbit:0x5eee6c>
198
+ #
199
+ # cache.decrement "counter"
200
+ # cache.read "counter", :raw => true # => "22"
201
+ #
202
+ # cache.decrement "counter", 2
203
+ # cache.read "counter", :raw => true # => "20"
204
+ #
205
+ # cache.decrement "a counter"
206
+ # cache.read "a counter", :raw => true # => "-1"
207
+ #
208
+ # cache.decrement "rabbit"
209
+ # cache.read "rabbit", :raw => true # => "-1"
210
+ def decrement(key, amount = 1)
211
+ instrument(:decrement, key, :amount => amount) do
212
+ @data.decrby key, amount
213
+ end
214
+ end
215
+
216
+ # Clear all the data from the store.
217
+ def clear
218
+ instrument(:clear, nil, nil) do
219
+ @data.flushdb
220
+ end
221
+ end
222
+
223
+ def stats
224
+ @data.info
225
+ end
226
+ end
227
+ end
228
+ end