instructure-redis-store 1.0.0.1.instructure1

Sign up to get free protection for your applications and to get access to all the features.
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