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
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+
3
+ describe "I18n::Backend::Redis" do
4
+ before :each do
5
+ @backend = I18n::Backend::Redis.new
6
+ @store = @backend.store
7
+ I18n.backend = @backend
8
+ end
9
+
10
+ it "should instantiate a store" do
11
+ @store.should be_kind_of(Redis::Store)
12
+ end
13
+
14
+ it "should instantiate a distributed store" do
15
+ store = I18n::Backend::Redis.new([ "redis://127.0.0.1:6379", "redis://127.0.0.1:6380" ]).store
16
+ store.should be_kind_of(Redis::DistributedStore)
17
+ end
18
+
19
+ it "should accept string uri" do
20
+ store = I18n::Backend::Redis.new("redis://127.0.0.1:6380/13/theplaylist").store
21
+ store.to_s.should == "Redis Client connected to 127.0.0.1:6380 against DB 13 with namespace theplaylist"
22
+ end
23
+
24
+ it "should accept hash params" do
25
+ store = I18n::Backend::Redis.new(:host => "127.0.0.1", :port => "6380", :db =>"13", :namespace => "theplaylist").store
26
+ store.to_s.should == "Redis Client connected to 127.0.0.1:6380 against DB 13 with namespace theplaylist"
27
+ end
28
+
29
+ it "should store translations" do
30
+ I18n.backend.store_translations :en, :foo => { :bar => :baz }
31
+ I18n.t(:"foo.bar").should == :baz
32
+
33
+ I18n.backend.store_translations :en, "foo" => { "bar" => "baz" }
34
+ I18n.t(:"foo.bar").should == "baz"
35
+ end
36
+
37
+ it "should get translations" do
38
+ I18n.backend.store_translations :en, :foo => { :bar => { :baz => :bang } }
39
+ I18n.t(:"foo.bar.baz").should == :bang
40
+ I18n.t(:"baz", :scope => :"foo.bar").should == :bang
41
+ end
42
+
43
+ it "should not store proc translations" do
44
+ lambda { I18n.backend.store_translations :en, :foo => lambda {|| } }.should raise_error("Key-value stores cannot handle procs")
45
+ end
46
+
47
+ describe "available locales" do
48
+ before :each do
49
+ @locales = [ :en, :it, :es, :fr, :de ]
50
+ end
51
+
52
+ it "should list" do
53
+ @locales.each { |locale| I18n.backend.store_translations locale, :foo => "bar" }
54
+ available_locales = I18n.backend.available_locales
55
+
56
+ @locales.each do |locale|
57
+ available_locales.should include(locale)
58
+ end
59
+ end
60
+
61
+ it "should list when namespaced" do
62
+ I18n.backend = I18n::Backend::Redis.new :namespace => 'foo'
63
+
64
+ @locales.each { |locale| I18n.backend.store_translations locale, :foo => "bar" }
65
+ available_locales = I18n.backend.available_locales
66
+
67
+ @locales.each do |locale|
68
+ available_locales.should include(locale)
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,124 @@
1
+ require 'spec_helper'
2
+
3
+ class Object
4
+ def sha_like?
5
+ length == 40 && self =~ /^[0-9a-z]+$/
6
+ end
7
+ end
8
+
9
+ module Rack
10
+ module Cache
11
+ class EntityStore
12
+ # courtesy of http://github.com/rtomayko/rack-cache team
13
+ describe "Rack::Cache::EntityStore::Redis" do
14
+ before(:each) do
15
+ @store = Rack::Cache::EntityStore::Redis.new :host => "localhost"
16
+ end
17
+
18
+ # Redis store specific examples ===========================================
19
+
20
+ it "should have the class referenced by homonym constant" do
21
+ Rack::Cache::EntityStore::REDIS.should be(Rack::Cache::EntityStore::Redis)
22
+ end
23
+
24
+ it "should resolve the connection uri" do
25
+ cache = Rack::Cache::EntityStore::Redis.resolve(uri("redis://127.0.0.1")).cache
26
+ cache.should be_kind_of(::Redis)
27
+ cache.id.should == "redis://127.0.0.1:6379/0"
28
+
29
+ cache = Rack::Cache::EntityStore::Redis.resolve(uri("redis://127.0.0.1:6380")).cache
30
+ cache.id.should == "redis://127.0.0.1:6380/0"
31
+
32
+ cache = Rack::Cache::EntityStore::Redis.resolve(uri("redis://127.0.0.1/13")).cache
33
+ cache.id.should == "redis://127.0.0.1:6379/13"
34
+
35
+ cache = Rack::Cache::EntityStore::Redis.resolve(uri("redis://:secret@127.0.0.1")).cache
36
+ cache.id.should == "redis://127.0.0.1:6379/0"
37
+ cache.client.password.should == 'secret'
38
+ end
39
+
40
+ # Entity store shared examples ===========================================
41
+
42
+ it 'responds to all required messages' do
43
+ %w[read open write exist?].each do |message|
44
+ @store.should respond_to(message)
45
+ end
46
+ end
47
+
48
+ it 'stores bodies with #write' do
49
+ key, size = @store.write(['My wild love went riding,'])
50
+ key.should_not be_nil
51
+ key.should be_sha_like
52
+
53
+ data = @store.read(key)
54
+ data.should == 'My wild love went riding,'
55
+ end
56
+
57
+ it 'correctly determines whether cached body exists for key with #exist?' do
58
+ key, size = @store.write(['She rode to the devil,'])
59
+ @store.should be_exist(key)
60
+ @store.should_not be_exist('938jasddj83jasdh4438021ksdfjsdfjsdsf')
61
+ end
62
+
63
+ it 'can read data written with #write' do
64
+ key, size = @store.write(['And asked him to pay.'])
65
+ data = @store.read(key)
66
+ data.should == 'And asked him to pay.'
67
+ end
68
+
69
+ it 'gives a 40 character SHA1 hex digest from #write' do
70
+ key, size = @store.write(['she rode to the sea;'])
71
+ key.should_not be_nil
72
+ key.length.should == 40
73
+ key.should =~ /^[0-9a-z]+$/
74
+ key.should == '90a4c84d51a277f3dafc34693ca264531b9f51b6'
75
+ end
76
+
77
+ it 'returns the entire body as a String from #read' do
78
+ key, size = @store.write(['She gathered together'])
79
+ @store.read(key).should == 'She gathered together'
80
+ end
81
+
82
+ it 'returns nil from #read when key does not exist' do
83
+ @store.read('87fe0a1ae82a518592f6b12b0183e950b4541c62').should be_nil
84
+ end
85
+
86
+ it 'returns a Rack compatible body from #open' do
87
+ key, size = @store.write(['Some shells for her hair.'])
88
+ body = @store.open(key)
89
+ body.should respond_to(:each)
90
+ buf = ''
91
+ body.each { |part| buf << part }
92
+ buf.should == 'Some shells for her hair.'
93
+ end
94
+
95
+ it 'returns nil from #open when key does not exist' do
96
+ @store.open('87fe0a1ae82a518592f6b12b0183e950b4541c62').should be_nil
97
+ end
98
+
99
+ if RUBY_VERSION < '1.9'
100
+ it 'can store largish bodies with binary data' do
101
+ pony = ::File.open(::File.dirname(__FILE__) + '/pony.jpg', 'rb') { |f| f.read }
102
+ key, size = @store.write([pony])
103
+ key.should == 'd0f30d8659b4d268c5c64385d9790024c2d78deb'
104
+ data = @store.read(key)
105
+ data.length.should == pony.length
106
+ data.hash.should == pony.hash
107
+ end
108
+ end
109
+
110
+ it 'deletes stored entries with #purge' do
111
+ key, size = @store.write(['My wild love went riding,'])
112
+ @store.purge(key).should be_nil
113
+ @store.read(key).should be_nil
114
+ end
115
+
116
+ # Helper Methods =============================================================
117
+
118
+ define_method :uri do |uri|
119
+ URI.parse uri
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,259 @@
1
+ require 'spec_helper'
2
+
3
+ module Rack
4
+ module Cache
5
+ class MetaStore
6
+ # courtesy of http://github.com/rtomayko/rack-cache team
7
+ describe "Rack::Cache::MetaStore::Redis" do
8
+ before :each do
9
+ @store = Rack::Cache::MetaStore::Redis.resolve uri("redis://127.0.0.1")
10
+ @entity_store = Rack::Cache::EntityStore::Redis.resolve uri("redis://127.0.0.1:6380")
11
+ @request = mock_request('/', {})
12
+ @response = mock_response(200, {}, ['hello world'])
13
+ end
14
+
15
+ after :each do
16
+ @store.cache.flushall
17
+ @entity_store.cache.flushall
18
+ end
19
+
20
+ it "should have the class referenced by homonym constant" do
21
+ Rack::Cache::MetaStore::REDIS.should be(Rack::Cache::MetaStore::Redis)
22
+ end
23
+
24
+ it "should resolve the connection uri" do
25
+ cache = Rack::Cache::MetaStore::Redis.resolve(uri("redis://127.0.0.1")).cache
26
+ cache.should be_kind_of(::Redis::Store)
27
+ cache.to_s.should == "Redis Client connected to 127.0.0.1:6379 against DB 0"
28
+
29
+ cache = Rack::Cache::MetaStore::Redis.resolve(uri("redis://127.0.0.1:6380")).cache
30
+ cache.to_s.should == "Redis Client connected to 127.0.0.1:6380 against DB 0"
31
+
32
+ cache = Rack::Cache::MetaStore::Redis.resolve(uri("redis://127.0.0.1/13")).cache
33
+ cache.to_s.should == "Redis Client connected to 127.0.0.1:6379 against DB 13"
34
+
35
+ cache = Rack::Cache::MetaStore::Redis.resolve(uri("redis://:secret@127.0.0.1")).cache
36
+ cache.id.should == "redis://127.0.0.1:6379/0"
37
+ cache.client.password.should == 'secret'
38
+ end
39
+
40
+ # Low-level implementation methods ===========================================
41
+
42
+ it 'writes a list of negotation tuples with #write' do
43
+ lambda { @store.write('/test', [[{}, {}]]) }.should_not raise_error
44
+ end
45
+
46
+ it 'reads a list of negotation tuples with #read' do
47
+ @store.write('/test', [[{},{}],[{},{}]])
48
+ tuples = @store.read('/test')
49
+ tuples.should == [ [{},{}], [{},{}] ]
50
+ end
51
+
52
+ it 'reads an empty list with #read when nothing cached at key' do
53
+ @store.read('/nothing').should be_empty
54
+ end
55
+
56
+ it 'removes entries for key with #purge' do
57
+ @store.write('/test', [[{},{}]])
58
+ @store.read('/test').should_not be_empty
59
+
60
+ @store.purge('/test')
61
+ @store.read('/test').should be_empty
62
+ end
63
+
64
+ it 'succeeds when purging non-existing entries' do
65
+ @store.read('/test').should be_empty
66
+ @store.purge('/test')
67
+ end
68
+
69
+ it 'returns nil from #purge' do
70
+ @store.write('/test', [[{},{}]])
71
+ @store.purge('/test').should be_nil
72
+ @store.read('/test').should == []
73
+ end
74
+
75
+ %w[/test http://example.com:8080/ /test?x=y /test?x=y&p=q].each do |key|
76
+ it "can read and write key: '#{key}'" do
77
+ lambda { @store.write(key, [[{},{}]]) }.should_not raise_error
78
+ @store.read(key).should == [[{},{}]]
79
+ end
80
+ end
81
+
82
+ it "can read and write fairly large keys" do
83
+ key = "b" * 4096
84
+ lambda { @store.write(key, [[{},{}]]) }.should_not raise_error
85
+ @store.read(key).should == [[{},{}]]
86
+ end
87
+
88
+ it "allows custom cache keys from block" do
89
+ request = mock_request('/test', {})
90
+ request.env['rack-cache.cache_key'] =
91
+ lambda { |request| request.path_info.reverse }
92
+ @store.cache_key(request).should == 'tset/'
93
+ end
94
+
95
+ it "allows custom cache keys from class" do
96
+ request = mock_request('/test', {})
97
+ request.env['rack-cache.cache_key'] = Class.new do
98
+ def self.call(request); request.path_info.reverse end
99
+ end
100
+ @store.cache_key(request).should == 'tset/'
101
+ end
102
+
103
+ # Abstract methods ===========================================================
104
+
105
+ # Stores an entry for the given request args, returns a url encoded cache key
106
+ # for the request.
107
+ define_method :store_simple_entry do |*request_args|
108
+ path, headers = request_args
109
+ @request = mock_request(path || '/test', headers || {})
110
+ @response = mock_response(200, {'Cache-Control' => 'max-age=420'}, ['test'])
111
+ body = @response.body
112
+ cache_key = @store.store(@request, @response, @entity_store)
113
+ @response.body.should_not equal(body)
114
+ cache_key
115
+ end
116
+
117
+ it 'stores a cache entry' do
118
+ cache_key = store_simple_entry
119
+ @store.read(cache_key).should_not be_empty
120
+ end
121
+
122
+ it 'sets the X-Content-Digest response header before storing' do
123
+ cache_key = store_simple_entry
124
+ req, res = @store.read(cache_key).first
125
+ res['X-Content-Digest'].should == 'a94a8fe5ccb19ba61c4c0873d391e987982fbbd3'
126
+ end
127
+
128
+ it 'finds a stored entry with #lookup' do
129
+ store_simple_entry
130
+ response = @store.lookup(@request, @entity_store)
131
+ response.should_not be_nil
132
+ response.should be_kind_of(Rack::Cache::Response)
133
+ end
134
+
135
+ it 'does not find an entry with #lookup when none exists' do
136
+ req = mock_request('/test', {'HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar'})
137
+ @store.lookup(req, @entity_store).should be_nil
138
+ end
139
+
140
+ it "canonizes urls for cache keys" do
141
+ store_simple_entry(path='/test?x=y&p=q')
142
+
143
+ hits_req = mock_request(path, {})
144
+ miss_req = mock_request('/test?p=x', {})
145
+
146
+ @store.lookup(hits_req, @entity_store).should_not be_nil
147
+ @store.lookup(miss_req, @entity_store).should be_nil
148
+ end
149
+
150
+ it 'does not find an entry with #lookup when the body does not exist' do
151
+ store_simple_entry
152
+ @response.headers['X-Content-Digest'].should_not be_nil
153
+ @entity_store.purge(@response.headers['X-Content-Digest'])
154
+ @store.lookup(@request, @entity_store).should be_nil
155
+ end
156
+
157
+ it 'restores response headers properly with #lookup' do
158
+ store_simple_entry
159
+ response = @store.lookup(@request, @entity_store)
160
+ response.headers.should == @response.headers.merge('Content-Length' => '4')
161
+ end
162
+
163
+ it 'restores response body from entity store with #lookup' do
164
+ store_simple_entry
165
+ response = @store.lookup(@request, @entity_store)
166
+ body = '' ; response.body.each {|p| body << p}
167
+ body.should == 'test'
168
+ end
169
+
170
+ it 'invalidates meta and entity store entries with #invalidate' do
171
+ store_simple_entry
172
+ @store.invalidate(@request, @entity_store)
173
+ response = @store.lookup(@request, @entity_store)
174
+ response.should be_kind_of(Rack::Cache::Response)
175
+ response.should_not be_fresh
176
+ end
177
+
178
+ it 'succeeds quietly when #invalidate called with no matching entries' do
179
+ req = mock_request('/test', {})
180
+ @store.invalidate(req, @entity_store)
181
+ @store.lookup(@request, @entity_store).should be_nil
182
+ end
183
+
184
+ # Vary =======================================================================
185
+
186
+ it 'does not return entries that Vary with #lookup' do
187
+ req1 = mock_request('/test', {'HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar'})
188
+ req2 = mock_request('/test', {'HTTP_FOO' => 'Bling', 'HTTP_BAR' => 'Bam'})
189
+ res = mock_response(200, {'Vary' => 'Foo Bar'}, ['test'])
190
+ @store.store(req1, res, @entity_store)
191
+
192
+ @store.lookup(req2, @entity_store).should be_nil
193
+ end
194
+
195
+ it 'stores multiple responses for each Vary combination' do
196
+ req1 = mock_request('/test', {'HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar'})
197
+ res1 = mock_response(200, {'Vary' => 'Foo Bar'}, ['test 1'])
198
+ key = @store.store(req1, res1, @entity_store)
199
+
200
+ req2 = mock_request('/test', {'HTTP_FOO' => 'Bling', 'HTTP_BAR' => 'Bam'})
201
+ res2 = mock_response(200, {'Vary' => 'Foo Bar'}, ['test 2'])
202
+ @store.store(req2, res2, @entity_store)
203
+
204
+ req3 = mock_request('/test', {'HTTP_FOO' => 'Baz', 'HTTP_BAR' => 'Boom'})
205
+ res3 = mock_response(200, {'Vary' => 'Foo Bar'}, ['test 3'])
206
+ @store.store(req3, res3, @entity_store)
207
+
208
+ slurp(@store.lookup(req3, @entity_store).body).should == 'test 3'
209
+ slurp(@store.lookup(req1, @entity_store).body).should == 'test 1'
210
+ slurp(@store.lookup(req2, @entity_store).body).should == 'test 2'
211
+
212
+ @store.read(key).length.should == 3
213
+ end
214
+
215
+ it 'overwrites non-varying responses with #store' do
216
+ req1 = mock_request('/test', {'HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar'})
217
+ res1 = mock_response(200, {'Vary' => 'Foo Bar'}, ['test 1'])
218
+ key = @store.store(req1, res1, @entity_store)
219
+ slurp(@store.lookup(req1, @entity_store).body).should == 'test 1'
220
+
221
+ req2 = mock_request('/test', {'HTTP_FOO' => 'Bling', 'HTTP_BAR' => 'Bam'})
222
+ res2 = mock_response(200, {'Vary' => 'Foo Bar'}, ['test 2'])
223
+ @store.store(req2, res2, @entity_store)
224
+ slurp(@store.lookup(req2, @entity_store).body).should == 'test 2'
225
+
226
+ req3 = mock_request('/test', {'HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar'})
227
+ res3 = mock_response(200, {'Vary' => 'Foo Bar'}, ['test 3'])
228
+ @store.store(req3, res3, @entity_store)
229
+ slurp(@store.lookup(req1, @entity_store).body).should == 'test 3'
230
+
231
+ @store.read(key).length.should == 2
232
+ end
233
+
234
+ # Helper Methods =============================================================
235
+
236
+ define_method :mock_request do |uri,opts|
237
+ env = Rack::MockRequest.env_for(uri, opts || {})
238
+ Rack::Cache::Request.new(env)
239
+ end
240
+
241
+ define_method :mock_response do |status,headers,body|
242
+ headers ||= {}
243
+ body = Array(body).compact
244
+ Rack::Cache::Response.new(status, headers, body)
245
+ end
246
+
247
+ define_method :slurp do |body|
248
+ buf = ''
249
+ body.each {|part| buf << part }
250
+ buf
251
+ end
252
+
253
+ define_method :uri do |uri|
254
+ URI.parse uri
255
+ end
256
+ end
257
+ end
258
+ end
259
+ end