instructure-redis-store 1.0.0.1.instructure1

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 (47) hide show
  1. data/.travis.yml +7 -0
  2. data/CHANGELOG +311 -0
  3. data/Gemfile +34 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +239 -0
  6. data/Rakefile +60 -0
  7. data/VERSION +1 -0
  8. data/lib/action_controller/session/redis_session_store.rb +81 -0
  9. data/lib/active_support/cache/redis_store.rb +254 -0
  10. data/lib/cache/merb/redis_store.rb +79 -0
  11. data/lib/cache/sinatra/redis_store.rb +131 -0
  12. data/lib/i18n/backend/redis.rb +67 -0
  13. data/lib/rack/cache/redis_entitystore.rb +48 -0
  14. data/lib/rack/cache/redis_metastore.rb +40 -0
  15. data/lib/rack/session/merb.rb +32 -0
  16. data/lib/rack/session/redis.rb +88 -0
  17. data/lib/redis-store.rb +45 -0
  18. data/lib/redis/distributed_store.rb +39 -0
  19. data/lib/redis/factory.rb +46 -0
  20. data/lib/redis/store.rb +39 -0
  21. data/lib/redis/store/interface.rb +17 -0
  22. data/lib/redis/store/marshalling.rb +51 -0
  23. data/lib/redis/store/namespace.rb +62 -0
  24. data/lib/redis/store/ttl.rb +37 -0
  25. data/lib/redis/store/version.rb +12 -0
  26. data/spec/action_controller/session/redis_session_store_spec.rb +126 -0
  27. data/spec/active_support/cache/redis_store_spec.rb +426 -0
  28. data/spec/cache/merb/redis_store_spec.rb +143 -0
  29. data/spec/cache/sinatra/redis_store_spec.rb +192 -0
  30. data/spec/config/node-one.conf +417 -0
  31. data/spec/config/node-two.conf +417 -0
  32. data/spec/config/redis.conf +417 -0
  33. data/spec/i18n/backend/redis_spec.rb +72 -0
  34. data/spec/rack/cache/entitystore/pony.jpg +0 -0
  35. data/spec/rack/cache/entitystore/redis_spec.rb +124 -0
  36. data/spec/rack/cache/metastore/redis_spec.rb +259 -0
  37. data/spec/rack/session/redis_spec.rb +234 -0
  38. data/spec/redis/distributed_store_spec.rb +55 -0
  39. data/spec/redis/factory_spec.rb +110 -0
  40. data/spec/redis/store/interface_spec.rb +23 -0
  41. data/spec/redis/store/marshalling_spec.rb +119 -0
  42. data/spec/redis/store/namespace_spec.rb +76 -0
  43. data/spec/redis/store/version_spec.rb +7 -0
  44. data/spec/redis/store_spec.rb +13 -0
  45. data/spec/spec_helper.rb +43 -0
  46. data/tasks/redis.tasks.rb +235 -0
  47. metadata +249 -0
data/Rakefile ADDED
@@ -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 = "instructure-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 = "brianp@instructure.com"
17
+ gemspec.homepage = "http://github.com/instructure/redis-store"
18
+ gemspec.authors = [ "Luca Guidi", "Brian Palmer" ]
19
+ gemspec.executables = [ ]
20
+ gemspec.add_dependency "redis", "3.0.1"
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_replication "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_replication "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.1.instructure1
@@ -0,0 +1,81 @@
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
+ options = options.dup
25
+ servers = [ options.delete(:servers) ].flatten.compact
26
+ servers = [ "redis://localhost:6379/0" ] if servers.empty?
27
+ servers.map! do |server|
28
+ server = Redis::Factory.convert_to_redis_client_options(server)
29
+ server.merge(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, opts=nil)
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
+ sid
58
+ end
59
+ rescue Errno::ECONNREFUSED
60
+ false
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ if ::Redis::Store.rails31?
67
+ require 'action_dispatch/middleware/session/abstract_store'
68
+ class ActionDispatch::Session::RedisSessionStore < Rack::Session::Redis
69
+ include ActionDispatch::Session::Compatibility
70
+ include ActionDispatch::Session::StaleSessionCheck
71
+ end
72
+ elsif ::Redis::Store.rails3?
73
+ class ActionDispatch::Session::RedisSessionStore < ActionDispatch::Session::AbstractStore
74
+ include RedisStore::Rack::Session::Rails
75
+ end
76
+ else
77
+ class ActionController::Session::RedisSessionStore < ActionController::Session::AbstractStore
78
+ include RedisStore::Rack::Session::Rails
79
+ end
80
+ end
81
+
@@ -0,0 +1,254 @@
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
+ !(keys = @data.keys(matcher)).empty? && @data.del(*keys)
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
+ begin
103
+ !(keys = @data.keys(matcher)).empty? && @data.del(*keys)
104
+ rescue Errno::ECONNREFUSED => e
105
+ false
106
+ end
107
+ end
108
+ end
109
+
110
+ protected
111
+ def write_entry(key, entry, options)
112
+ method = options && options[:unless_exist] ? :setnx : :set
113
+ @data.send method, key, entry, options
114
+ rescue Errno::ECONNREFUSED => e
115
+ false
116
+ end
117
+
118
+ def read_entry(key, options)
119
+ entry = @data.get key, options
120
+ if entry
121
+ entry.is_a?(ActiveSupport::Cache::Entry) ? entry : ActiveSupport::Cache::Entry.new(entry)
122
+ end
123
+ rescue Errno::ECONNREFUSED => e
124
+ nil
125
+ end
126
+
127
+ ##
128
+ # Implement the ActiveSupport::Cache#delete_entry
129
+ #
130
+ # It's really needed and use
131
+ #
132
+ def delete_entry(key, options)
133
+ @data.del key
134
+ rescue Errno::ECONNREFUSED => e
135
+ false
136
+ end
137
+
138
+
139
+ # Add the namespace defined in the options to a pattern designed to match keys.
140
+ #
141
+ # This implementation is __different__ than ActiveSupport:
142
+ # __it doesn't accept Regular expressions__, because the Redis matcher is designed
143
+ # only for strings with wildcards.
144
+ def key_matcher(pattern, options)
145
+ prefix = options[:namespace].is_a?(Proc) ? options[:namespace].call : options[:namespace]
146
+ if prefix
147
+ raise "Regexps aren't supported, please use string with wildcards." if pattern.is_a?(Regexp)
148
+ "#{prefix}:#{pattern}"
149
+ else
150
+ pattern
151
+ end
152
+ end
153
+ end
154
+
155
+ module Store
156
+ include ::Redis::Store.rails3? ? Rails3 : Rails2
157
+ end
158
+ end
159
+ end
160
+
161
+ module ActiveSupport
162
+ module Cache
163
+ class RedisStore < Store
164
+ include ::RedisStore::Cache::Store
165
+
166
+ # Reads multiple keys from the cache using a single call to the
167
+ # servers for all keys. Options can be passed in the last argument.
168
+ #
169
+ # Example:
170
+ # cache.read_multi "rabbit", "white-rabbit"
171
+ # cache.read_multi "rabbit", "white-rabbit", :raw => true
172
+ def read_multi(*names)
173
+ values = @data.mget(*names)
174
+
175
+ # Remove the options hash before mapping keys to values
176
+ names.extract_options!
177
+
178
+ result = Hash[names.zip(values)]
179
+ result.reject!{ |k,v| v.nil? }
180
+ result
181
+ end
182
+
183
+ # Increment a key in the store.
184
+ #
185
+ # If the key doesn't exist it will be initialized on 0.
186
+ # If the key exist but it isn't a Fixnum it will be initialized on 0.
187
+ #
188
+ # Example:
189
+ # We have two objects in cache:
190
+ # counter # => 23
191
+ # rabbit # => #<Rabbit:0x5eee6c>
192
+ #
193
+ # cache.increment "counter"
194
+ # cache.read "counter", :raw => true # => "24"
195
+ #
196
+ # cache.increment "counter", 6
197
+ # cache.read "counter", :raw => true # => "30"
198
+ #
199
+ # cache.increment "a counter"
200
+ # cache.read "a counter", :raw => true # => "1"
201
+ #
202
+ # cache.increment "rabbit"
203
+ # cache.read "rabbit", :raw => true # => "1"
204
+ def increment(key, amount = 1)
205
+ instrument(:increment, key, :amount => amount) do
206
+ @data.incrby key, amount
207
+ end
208
+ end
209
+
210
+ # Decrement a key in the store
211
+ #
212
+ # If the key doesn't exist it will be initialized on 0.
213
+ # If the key exist but it isn't a Fixnum it will be initialized on 0.
214
+ #
215
+ # Example:
216
+ # We have two objects in cache:
217
+ # counter # => 23
218
+ # rabbit # => #<Rabbit:0x5eee6c>
219
+ #
220
+ # cache.decrement "counter"
221
+ # cache.read "counter", :raw => true # => "22"
222
+ #
223
+ # cache.decrement "counter", 2
224
+ # cache.read "counter", :raw => true # => "20"
225
+ #
226
+ # cache.decrement "a counter"
227
+ # cache.read "a counter", :raw => true # => "-1"
228
+ #
229
+ # cache.decrement "rabbit"
230
+ # cache.read "rabbit", :raw => true # => "-1"
231
+ def decrement(key, amount = 1)
232
+ instrument(:decrement, key, :amount => amount) do
233
+ @data.decrby key, amount
234
+ end
235
+ end
236
+
237
+ # Clear all the data from the store.
238
+ def clear
239
+ instrument(:clear, nil, nil) do
240
+ @data.flushdb
241
+ end
242
+ end
243
+
244
+ def stats
245
+ @data.info
246
+ end
247
+
248
+ # Force client reconnection, useful Unicorn deployed apps.
249
+ def reconnect
250
+ @data.reconnect
251
+ end
252
+ end
253
+ end
254
+ end
@@ -0,0 +1,79 @@
1
+ module Merb
2
+ module Cache
3
+ class RedisStore < AbstractStore
4
+ # Instantiate the store.
5
+ #
6
+ # Example:
7
+ # RedisStore.new
8
+ # # => host: localhost, port: 6379, db: 0
9
+ #
10
+ # RedisStore.new :servers => ["example.com"]
11
+ # # => host: example.com, port: 6379, db: 0
12
+ #
13
+ # RedisStore.new :servers => ["example.com:23682"]
14
+ # # => host: example.com, port: 23682, db: 0
15
+ #
16
+ # RedisStore.new :servers => ["example.com:23682/1"]
17
+ # # => host: example.com, port: 23682, db: 1
18
+ #
19
+ # RedisStore.new :servers => ["example.com:23682/1/theplaylist"]
20
+ # # => host: example.com, port: 23682, db: 1, namespace: theplaylist
21
+ #
22
+ # RedisStore.new :servers => ["localhost:6379/0", "localhost:6380/0"]
23
+ # # => instantiate a cluster
24
+ def initialize(config = { })
25
+ @data = Redis::Factory.create config[:servers]
26
+ end
27
+
28
+ def writable?(key, parameters = {}, conditions = {})
29
+ true
30
+ end
31
+
32
+ def read(key, parameters = {}, conditions = {})
33
+ @data.get normalize(key, parameters), conditions
34
+ end
35
+
36
+ def write(key, data = nil, parameters = {}, conditions = {})
37
+ if writable?(key, parameters, conditions)
38
+ method = conditions && conditions[:unless_exist] ? :setnx : :set
39
+ @data.send method, normalize(key, parameters), data, conditions
40
+ end
41
+ end
42
+
43
+ def write_all(key, data = nil, parameters = {}, conditions = {})
44
+ write key, data, parameters, conditions
45
+ end
46
+
47
+ def fetch(key, parameters = {}, conditions = {}, &blk)
48
+ (data = read(key, parameters)) || block_given? && begin
49
+ data = yield
50
+ write(key, data, parameters, conditions)
51
+ end
52
+ data || nil
53
+ end
54
+
55
+ def exists?(key, parameters = {})
56
+ @data.exists normalize(key, parameters)
57
+ end
58
+
59
+ def delete(key, parameters = {})
60
+ @data.del normalize(key, parameters)
61
+ end
62
+
63
+ def delete_all
64
+ @data.flushdb
65
+ end
66
+
67
+ def delete_all!
68
+ delete_all
69
+ end
70
+
71
+ private
72
+ # Returns cache key calculated from base key
73
+ # and SHA2 hex from parameters.
74
+ def normalize(key, parameters = {})
75
+ parameters.empty? ? "#{key}" : "#{key}--#{parameters.to_sha2}"
76
+ end
77
+ end
78
+ end
79
+ end