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,405 @@
1
+ require 'spec_helper'
2
+
3
+ module ActiveSupport
4
+ module Cache
5
+ describe RedisStore do
6
+ before :each do
7
+ @store = ActiveSupport::Cache::RedisStore.new
8
+ @dstore = ActiveSupport::Cache::RedisStore.new "redis://127.0.0.1:6380/1", "redis://127.0.0.1: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 "counter"
14
+ store.delete "rub-a-dub"
15
+ end
16
+ end
17
+
18
+ it "should accept connection params" do
19
+ redis = instantiate_store
20
+ redis.to_s.should == "Redis Client connected to 127.0.0.1:6379 against DB 0"
21
+
22
+ redis = instantiate_store "redis://127.0.0.1"
23
+ redis.to_s.should == "Redis Client connected to 127.0.0.1:6379 against DB 0"
24
+
25
+ redis = instantiate_store "redis://127.0.0.1:6380"
26
+ redis.to_s.should == "Redis Client connected to 127.0.0.1:6380 against DB 0"
27
+
28
+ redis = instantiate_store "redis://127.0.0.1:6380/13"
29
+ redis.to_s.should == "Redis Client connected to 127.0.0.1:6380 against DB 13"
30
+
31
+ redis = instantiate_store "redis://127.0.0.1:6380/13/theplaylist"
32
+ redis.to_s.should == "Redis Client connected to 127.0.0.1:6380 against DB 13 with namespace theplaylist"
33
+ end
34
+
35
+ it "should instantiate a ring" do
36
+ store = instantiate_store
37
+ store.should be_kind_of(Redis::Store)
38
+ store = instantiate_store ["redis://127.0.0.1:6379/0", "redis://127.0.0.1:6379/1"]
39
+ store.should be_kind_of(Redis::DistributedStore)
40
+ end
41
+
42
+ it "should read the data" do
43
+ with_store_management do |store|
44
+ store.read("rabbit").should === @rabbit
45
+ end
46
+ end
47
+
48
+ it "should write the data" do
49
+ with_store_management do |store|
50
+ store.write "rabbit", @white_rabbit
51
+ store.read("rabbit").should === @white_rabbit
52
+ end
53
+ end
54
+
55
+ it "should write the data with expiration time" do
56
+ with_store_management do |store|
57
+ store.write "rabbit", @white_rabbit, :expires_in => 1.second
58
+ store.read("rabbit").should == @white_rabbit ; sleep 2
59
+ store.read("rabbit").should be_nil
60
+ end
61
+ end
62
+
63
+ it "should not write data if :unless_exist option is true" do
64
+ with_store_management do |store|
65
+ store.write "rabbit", @white_rabbit, :unless_exist => true
66
+ store.read("rabbit").should == @rabbit
67
+ end
68
+ end
69
+
70
+ if ::Redis::Store.rails3?
71
+ if RUBY_VERSION.match /1\.9/
72
+ it "should read raw data" do
73
+ with_store_management do |store|
74
+ result = store.read("rabbit", :raw => true)
75
+ result.should include("ActiveSupport::Cache::Entry")
76
+ result.should include("\x0FOpenStruct{\x06:\tnameI\"\nbunny\x06:\x06EF")
77
+ end
78
+ end
79
+ else
80
+ it "should read raw data" do
81
+ with_store_management do |store|
82
+ result = store.read("rabbit", :raw => true)
83
+ result.should include("ActiveSupport::Cache::Entry")
84
+ result.should include("\017OpenStruct{\006:\tname\"\nbunny")
85
+ end
86
+ end
87
+ end
88
+
89
+ it "should write raw data" do
90
+ with_store_management do |store|
91
+ store.write "rabbit", @white_rabbit, :raw => true
92
+ store.read("rabbit", :raw => true).should include("ActiveSupport::Cache::Entry")
93
+ end
94
+ end
95
+ else
96
+ it "should read raw data" do
97
+ with_store_management do |store|
98
+ store.read("rabbit", :raw => true).should == Marshal.dump(@rabbit)
99
+ end
100
+ end
101
+
102
+ it "should write raw data" do
103
+ with_store_management do |store|
104
+ store.write "rabbit", @white_rabbit, :raw => true
105
+ store.read("rabbit", :raw => true).should == %(#<OpenStruct color="white">)
106
+ end
107
+ end
108
+ end
109
+
110
+ it "should delete data" do
111
+ with_store_management do |store|
112
+ store.delete "rabbit"
113
+ store.read("rabbit").should be_nil
114
+ end
115
+ end
116
+
117
+ it "should delete matched data" do
118
+ with_store_management do |store|
119
+ store.delete_matched "rabb*"
120
+ store.read("rabbit").should be_nil
121
+ end
122
+ end
123
+
124
+ it "should delete (matched) data when a wildcard is given" do
125
+ with_store_management do |store|
126
+ store.delete "rabb*"
127
+ store.read("rabbit").should be_nil
128
+ end
129
+ end
130
+
131
+ it "should verify existence of an object in the store" do
132
+ with_store_management do |store|
133
+ store.exist?("rabbit").should be_true
134
+ store.exist?("rab-a-dub").should be_false
135
+ end
136
+ end
137
+
138
+ it "should increment a key" do
139
+ with_store_management do |store|
140
+ 3.times { store.increment "counter" }
141
+ store.read("counter", :raw => true).to_i.should == 3
142
+ end
143
+ end
144
+
145
+ it "should decrement a key" do
146
+ with_store_management do |store|
147
+ 3.times { store.increment "counter" }
148
+ 2.times { store.decrement "counter" }
149
+ store.read("counter", :raw => true).to_i.should == 1
150
+ end
151
+ end
152
+
153
+ it "should increment a key by given value" do
154
+ with_store_management do |store|
155
+ store.increment "counter", 3
156
+ store.read("counter", :raw => true).to_i.should == 3
157
+ end
158
+ end
159
+
160
+ it "should decrement a key by given value" do
161
+ with_store_management do |store|
162
+ 3.times { store.increment "counter" }
163
+ store.decrement "counter", 2
164
+ store.read("counter", :raw => true).to_i.should == 1
165
+ end
166
+ end
167
+
168
+ it "should clear the store" do
169
+ with_store_management do |store|
170
+ store.clear
171
+ store.instance_variable_get(:@data).keys("*").flatten.should be_empty
172
+ end
173
+ end
174
+
175
+ it "should return store stats" do
176
+ with_store_management do |store|
177
+ store.stats.should_not be_empty
178
+ end
179
+ end
180
+
181
+ it "should fetch data" do
182
+ with_store_management do |store|
183
+ store.fetch("rabbit").should == @rabbit
184
+ store.fetch("rub-a-dub").should be_nil
185
+ store.fetch("rub-a-dub") { "Flora de Cana" }
186
+ store.fetch("rub-a-dub").should === "Flora de Cana"
187
+ store.fetch("rabbit", :force => true) # force cache miss
188
+ store.fetch("rabbit", :force => true, :expires_in => 1.second) { @white_rabbit }
189
+ store.fetch("rabbit").should == @white_rabbit
190
+ sleep 2
191
+ store.fetch("rabbit").should be_nil
192
+ end
193
+ end
194
+
195
+ if ::Redis::Store.rails3?
196
+ it "should read multiple keys" do
197
+ @store.write "irish whisky", "Jameson"
198
+ rabbit, whisky = @store.read_multi "rabbit", "irish whisky"
199
+ rabbit.raw_value.should === @rabbit
200
+ whisky.raw_value.should == "Jameson"
201
+ end
202
+ else
203
+ it "should read multiple keys" do
204
+ @store.write "irish whisky", "Jameson"
205
+ rabbit, whisky = @store.read_multi "rabbit", "irish whisky"
206
+ rabbit.should === @rabbit
207
+ whisky.should == "Jameson"
208
+ end
209
+ end
210
+
211
+ describe "namespace" do
212
+ before :each do
213
+ @namespace = "theplaylist"
214
+ @store = ActiveSupport::Cache::RedisStore.new :namespace => @namespace
215
+ @data = @store.instance_variable_get(:@data)
216
+ @client = @data.instance_variable_get(:@client)
217
+ end
218
+
219
+ it "should read the data" do
220
+ @client.should_receive(:call).with(:get, "#{@namespace}:rabbit")
221
+ @store.read("rabbit")
222
+ end
223
+
224
+ if ::Redis::Store.rails3?
225
+ # it "should write the data"
226
+ # it "should write the data" do
227
+ # @data.should_receive(:set).with("#{@namespace}:rabbit"), Marshal.dump(ActiveSupport::Cache::Entry.new(@white_rabbit)))
228
+ # @store.write "rabbit", @white_rabbit
229
+ # end
230
+ else
231
+ it "should write the data" do
232
+ @client.should_receive(:call).with(:set, "#{@namespace}:rabbit", Marshal.dump(@white_rabbit))
233
+ @store.write "rabbit", @white_rabbit
234
+ end
235
+ end
236
+
237
+ it "should delete the data" do
238
+ @client.should_receive(:call).with(:del, "#{@namespace}:rabbit")
239
+ @store.delete "rabbit"
240
+ end
241
+
242
+ it "should delete matched data" do
243
+ @client.should_receive(:call).with(:del, "#{@namespace}:rabbit")
244
+ @client.should_receive(:call).with(:keys, "theplaylist:rabb*").and_return [ "#{@namespace}:rabbit" ]
245
+ @store.delete_matched "rabb*"
246
+ end
247
+
248
+ if ::Redis::Store.rails3?
249
+ it "should verify existence of an object in the store" do
250
+ @client.should_receive(:call).with(:get, "#{@namespace}:rabbit")
251
+ @store.exist?("rabbit")
252
+ end
253
+ else
254
+ it "should verify existence of an object in the store" do
255
+ @client.should_receive(:call).with(:exists, "#{@namespace}:rabbit")
256
+ @store.exist?("rabbit")
257
+ end
258
+ end
259
+
260
+ it "should increment a key" do
261
+ @client.should_receive(:call).with(:incrby, "#{@namespace}:counter", 1)
262
+ @store.increment "counter"
263
+ end
264
+
265
+ it "should decrement a key" do
266
+ @client.should_receive(:call).with(:decrby, "#{@namespace}:counter", 1)
267
+ @store.decrement "counter"
268
+ end
269
+
270
+ it "should fetch data" do
271
+ @client.should_receive(:call).with(:get, "#{@namespace}:rabbit")
272
+ @store.fetch "rabbit"
273
+ end
274
+
275
+ it "should read multiple keys" do
276
+ rabbits = [ Marshal.dump(@rabbit), Marshal.dump(@white_rabbit) ]
277
+ @client.should_receive(:call).with(:mget, "#{@namespace}:rabbit", "#{@namespace}:white_rabbit").and_return rabbits
278
+ @store.read_multi "rabbit", "white_rabbit"
279
+ end
280
+ end
281
+
282
+ if ::Redis::Store.rails3?
283
+ describe "notifications" do
284
+ it "should notify on #fetch" do
285
+ with_notifications do
286
+ @store.fetch("radiohead") { "House Of Cards" }
287
+ end
288
+
289
+ read, generate, write = @events
290
+ read.name.should == "cache_read.active_support"
291
+ read.payload.should == { :key => "radiohead", :super_operation => :fetch }
292
+ generate.name.should == "cache_generate.active_support"
293
+ generate.payload.should == { :key => "radiohead" }
294
+ write.name.should == "cache_write.active_support"
295
+ write.payload.should == { :key => "radiohead" }
296
+ end
297
+
298
+ it "should notify on #read" do
299
+ with_notifications do
300
+ @store.read "metallica"
301
+ end
302
+
303
+ read = @events.first
304
+ read.name.should == "cache_read.active_support"
305
+ read.payload.should == { :key => "metallica", :hit => false }
306
+ end
307
+
308
+ # it "should notify on #read_multi" # Not supported in Rails 3
309
+
310
+ it "should notify on #write" do
311
+ with_notifications do
312
+ @store.write "depeche mode", "Enjoy The Silence"
313
+ end
314
+
315
+ write = @events.first
316
+ write.name.should == "cache_write.active_support"
317
+ write.payload.should == { :key => "depeche mode" }
318
+ end
319
+
320
+ it "should notify on #delete" do
321
+ with_notifications do
322
+ @store.delete "the new cardigans"
323
+ end
324
+
325
+ delete = @events.first
326
+ delete.name.should == "cache_delete.active_support"
327
+ delete.payload.should == { :key => "the new cardigans" }
328
+ end
329
+
330
+ it "should notify on #exist?" do
331
+ with_notifications do
332
+ @store.exist? "the smiths"
333
+ end
334
+
335
+ exist = @events.first
336
+ exist.name.should == "cache_exist?.active_support"
337
+ exist.payload.should == { :key => "the smiths" }
338
+ end
339
+
340
+ it "should notify on #delete_matched" do
341
+ with_notifications do
342
+ @store.delete_matched "afterhours*"
343
+ end
344
+
345
+ delete_matched = @events.first
346
+ delete_matched.name.should == "cache_delete_matched.active_support"
347
+ delete_matched.payload.should == { :key => %("afterhours*") }
348
+ end
349
+
350
+ it "should notify on #increment" do
351
+ with_notifications do
352
+ @store.increment "pearl jam"
353
+ end
354
+
355
+ increment = @events.first
356
+ increment.name.should == "cache_increment.active_support"
357
+ increment.payload.should == { :key => "pearl jam", :amount => 1 }
358
+ end
359
+
360
+ it "should notify on #decrement" do
361
+ with_notifications do
362
+ @store.decrement "placebo"
363
+ end
364
+
365
+ decrement = @events.first
366
+ decrement.name.should == "cache_decrement.active_support"
367
+ decrement.payload.should == { :key => "placebo", :amount => 1 }
368
+ end
369
+
370
+ # it "should notify on cleanup" # TODO implement in ActiveSupport::Cache::RedisStore
371
+
372
+ it "should notify on clear" do
373
+ with_notifications do
374
+ @store.clear
375
+ end
376
+
377
+ clear = @events.first
378
+ clear.name.should == "cache_clear.active_support"
379
+ clear.payload.should == { :key => nil }
380
+ end
381
+ end
382
+ end
383
+
384
+ private
385
+ def instantiate_store(addresses = nil)
386
+ ActiveSupport::Cache::RedisStore.new(addresses).instance_variable_get(:@data)
387
+ end
388
+
389
+ def with_store_management
390
+ yield @store
391
+ yield @dstore
392
+ end
393
+
394
+ def with_notifications
395
+ @events = [ ]
396
+ ActiveSupport::Cache::RedisStore.instrument = true
397
+ ActiveSupport::Notifications.subscribe(/^cache_(.*)\.active_support$/) do |*args|
398
+ @events << ActiveSupport::Notifications::Event.new(*args)
399
+ end
400
+ yield
401
+ ActiveSupport::Cache::RedisStore.instrument = false
402
+ end
403
+ end
404
+ end
405
+ end
@@ -0,0 +1,143 @@
1
+ require '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 => ["redis://127.0.0.1:6380/1", "redis://127.0.0.1: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.to_s.should == "Redis Client connected to 127.0.0.1:6379 against DB 0"
20
+
21
+ redis = instantiate_store "redis://127.0.0.1"
22
+ redis.to_s.should == "Redis Client connected to 127.0.0.1:6379 against DB 0"
23
+
24
+ redis = instantiate_store "redis://127.0.0.1:6380"
25
+ redis.to_s.should == "Redis Client connected to 127.0.0.1:6380 against DB 0"
26
+
27
+ redis = instantiate_store "redis://127.0.0.1:6380/13"
28
+ redis.to_s.should == "Redis Client connected to 127.0.0.1:6380 against DB 13"
29
+
30
+ redis = instantiate_store "redis://127.0.0.1:6380/13/theplaylist"
31
+ redis.to_s.should == "Redis Client connected to 127.0.0.1:6380 against DB 13 with namespace theplaylist"
32
+ end
33
+
34
+ it "should instantiate a ring" do
35
+ store = instantiate_store
36
+ store.should be_kind_of(Redis::Store)
37
+ store = instantiate_store ["redis://127.0.0.1:6379/0", "redis://127.0.0.1:6379/1"]
38
+ store.should be_kind_of(Redis::DistributedStore)
39
+ end
40
+
41
+ it "should verify if writable" do
42
+ with_store_management do |store|
43
+ store.writable?("rabbit").should be_true
44
+ end
45
+ end
46
+
47
+ it "should read the data" do
48
+ with_store_management do |store|
49
+ store.read("rabbit").should === @rabbit
50
+ end
51
+ end
52
+
53
+ it "should read raw data" do
54
+ with_store_management do |store|
55
+ store.read("rabbit", {}, :raw => true).should == Marshal.dump(@rabbit)
56
+ end
57
+ end
58
+
59
+ it "should write the data" do
60
+ with_store_management do |store|
61
+ store.write "rabbit", @white_rabbit
62
+ store.read("rabbit").should === @white_rabbit
63
+ end
64
+ end
65
+
66
+ it "should write raw data" do
67
+ with_store_management do |store|
68
+ store.write "rabbit", @white_rabbit, {}, :raw => true
69
+ store.read("rabbit", {}, :raw => true).should == %(#<OpenStruct color="white">)
70
+ end
71
+ end
72
+
73
+ it "should write the data with expiration time" do
74
+ with_store_management do |store|
75
+ store.write "rabbit", @white_rabbit, {}, :expires_in => 1.second
76
+ store.read("rabbit").should === @white_rabbit ; sleep 2
77
+ store.read("rabbit").should be_nil
78
+ end
79
+ end
80
+
81
+ it "should not write data if :unless_exist option is true" do
82
+ with_store_management do |store|
83
+ store.write "rabbit", @white_rabbit, {}, :unless_exist => true
84
+ store.read("rabbit").should === @rabbit
85
+ end
86
+ end
87
+
88
+ it "should write all the data" do
89
+ with_store_management do |store|
90
+ store.write_all "rabbit", @white_rabbit
91
+ store.read("rabbit").should === @white_rabbit
92
+ end
93
+ end
94
+
95
+ it "should fetch data" do
96
+ with_store_management do |store|
97
+ store.fetch("rabbit").should == @rabbit
98
+ store.fetch("rub-a-dub").should be_nil
99
+ store.fetch("rub-a-dub") { "Flora de Cana" }
100
+ store.fetch("rub-a-dub").should === "Flora de Cana"
101
+ end
102
+ end
103
+
104
+ it "should verify existence" do
105
+ with_store_management do |store|
106
+ store.exists?("rabbit").should be_true
107
+ store.exists?("rab-a-dub").should be_false
108
+ end
109
+ end
110
+
111
+ it "should delete data" do
112
+ with_store_management do |store|
113
+ store.delete "rabbit"
114
+ store.read("rabbit").should be_nil
115
+ end
116
+ end
117
+
118
+ it "should delete all the data" do
119
+ with_store_management do |store|
120
+ store.delete_all
121
+ store.instance_variable_get(:@data).keys("*").flatten.should be_empty
122
+ end
123
+ end
124
+
125
+ it "should delete all the data with bang method" do
126
+ with_store_management do |store|
127
+ store.delete_all!
128
+ store.instance_variable_get(:@data).keys("*").flatten.should be_empty
129
+ end
130
+ end
131
+
132
+ private
133
+ def instantiate_store(addresses = nil)
134
+ Merb::Cache::RedisStore.new(:servers => [addresses].flatten).instance_variable_get(:@data)
135
+ end
136
+
137
+ def with_store_management
138
+ yield @store
139
+ yield @dstore
140
+ end
141
+ end
142
+ end
143
+ end