redis-store 1.0.0.1 → 1.1.0.rc

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of redis-store might be problematic. Click here for more details.

Files changed (53) hide show
  1. data/Gemfile +2 -34
  2. data/README.md +17 -220
  3. data/Rakefile +7 -54
  4. data/lib/redis-store.rb +11 -44
  5. data/lib/redis/distributed_store.rb +8 -1
  6. data/lib/redis/factory.rb +17 -21
  7. data/lib/redis/store.rb +3 -8
  8. data/lib/redis/store/interface.rb +4 -0
  9. data/lib/redis/store/marshalling.rb +4 -0
  10. data/lib/redis/store/version.rb +1 -8
  11. data/lib/tasks/redis.tasks.rb +167 -0
  12. data/redis-store.gemspec +22 -97
  13. data/{spec → test}/config/node-one.conf +2 -2
  14. data/{spec → test}/config/node-two.conf +2 -2
  15. data/{spec → test}/config/redis.conf +3 -2
  16. data/{spec/redis/distributed_store_spec.rb → test/redis/distributed_store_test.rb} +13 -15
  17. data/test/redis/factory_test.rb +98 -0
  18. data/test/redis/store/interface_test.rb +27 -0
  19. data/test/redis/store/marshalling_test.rb +127 -0
  20. data/test/redis/store/namespace_test.rb +86 -0
  21. data/test/redis/store/version_test.rb +7 -0
  22. data/test/redis/store_test.rb +17 -0
  23. data/test/test_helper.rb +22 -0
  24. metadata +85 -97
  25. data/.travis.yml +0 -7
  26. data/CHANGELOG +0 -311
  27. data/VERSION +0 -1
  28. data/lib/action_controller/session/redis_session_store.rb +0 -81
  29. data/lib/active_support/cache/redis_store.rb +0 -254
  30. data/lib/cache/merb/redis_store.rb +0 -79
  31. data/lib/cache/sinatra/redis_store.rb +0 -131
  32. data/lib/i18n/backend/redis.rb +0 -67
  33. data/lib/rack/cache/redis_entitystore.rb +0 -48
  34. data/lib/rack/cache/redis_metastore.rb +0 -40
  35. data/lib/rack/session/merb.rb +0 -32
  36. data/lib/rack/session/redis.rb +0 -88
  37. data/spec/action_controller/session/redis_session_store_spec.rb +0 -126
  38. data/spec/active_support/cache/redis_store_spec.rb +0 -426
  39. data/spec/cache/merb/redis_store_spec.rb +0 -143
  40. data/spec/cache/sinatra/redis_store_spec.rb +0 -192
  41. data/spec/i18n/backend/redis_spec.rb +0 -72
  42. data/spec/rack/cache/entitystore/pony.jpg +0 -0
  43. data/spec/rack/cache/entitystore/redis_spec.rb +0 -124
  44. data/spec/rack/cache/metastore/redis_spec.rb +0 -259
  45. data/spec/rack/session/redis_spec.rb +0 -234
  46. data/spec/redis/factory_spec.rb +0 -110
  47. data/spec/redis/store/interface_spec.rb +0 -23
  48. data/spec/redis/store/marshalling_spec.rb +0 -119
  49. data/spec/redis/store/namespace_spec.rb +0 -76
  50. data/spec/redis/store/version_spec.rb +0 -7
  51. data/spec/redis/store_spec.rb +0 -13
  52. data/spec/spec_helper.rb +0 -43
  53. data/tasks/redis.tasks.rb +0 -235
@@ -1,259 +0,0 @@
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
@@ -1,234 +0,0 @@
1
- require 'spec_helper'
2
-
3
- module Rack
4
- module Session
5
- describe "Rack::Session::Redis" do
6
- before(:each) do
7
- @session_key = Rack::Session::Redis::DEFAULT_OPTIONS[:key]
8
- @session_match = /#{@session_key}=[0-9a-fA-F]+;/
9
- @incrementor = lambda do |env|
10
- env["rack.session"]["counter"] ||= 0
11
- env["rack.session"]["counter"] += 1
12
- Rack::Response.new(env["rack.session"].inspect).to_a
13
- end
14
- @drop_session = proc do |env|
15
- env['rack.session.options'][:drop] = true
16
- @incrementor.call(env)
17
- end
18
- @renew_session = proc do |env|
19
- env['rack.session.options'][:renew] = true
20
- @incrementor.call(env)
21
- end
22
- @defer_session = proc do |env|
23
- env['rack.session.options'][:defer] = true
24
- @incrementor.call(env)
25
- end
26
- end
27
-
28
- it "should specify connection params" do
29
- pool = Rack::Session::Redis.new(@incrementor, :redis_server => "redis://127.0.0.1:6380/1/theplaylist").pool
30
- pool.should be_kind_of(::Redis::Store)
31
- pool.to_s.should == "Redis Client connected to 127.0.0.1:6380 against DB 1 with namespace theplaylist"
32
-
33
- pool = Rack::Session::Redis.new(@incrementor, :redis_server => ["redis://127.0.0.1:6379", "redis://127.0.0.1:6380"]).pool
34
- pool.should be_kind_of(::Redis::DistributedStore)
35
- end
36
-
37
- it "creates a new cookie" do
38
- pool = Rack::Session::Redis.new(@incrementor)
39
- res = Rack::MockRequest.new(pool).get("/")
40
- res["Set-Cookie"].should match(/#{@session_key}=/)
41
- res.body.should == '{"counter"=>1}'
42
- end
43
-
44
- it "determines session from a cookie" do
45
- pool = Rack::Session::Redis.new(@incrementor)
46
- req = Rack::MockRequest.new(pool)
47
- res = req.get("/")
48
- cookie = res["Set-Cookie"]
49
- req.get("/", "HTTP_COOKIE" => cookie).body.should == '{"counter"=>2}'
50
- req.get("/", "HTTP_COOKIE" => cookie).body.should == '{"counter"=>3}'
51
- end
52
-
53
- it "survives nonexistant cookies" do
54
- bad_cookie = "rack.session=blarghfasel"
55
- pool = Rack::Session::Redis.new(@incrementor)
56
- res = Rack::MockRequest.new(pool).
57
- get("/", "HTTP_COOKIE" => bad_cookie)
58
- res.body.should == '{"counter"=>1}'
59
- cookie = res["Set-Cookie"][@session_match]
60
- cookie.should_not match(/#{bad_cookie}/)
61
- end
62
-
63
- it "should maintain freshness" do
64
- pool = Rack::Session::Redis.new(@incrementor, :expire_after => 3)
65
- res = Rack::MockRequest.new(pool).get('/')
66
- res.body.should include('"counter"=>1')
67
- cookie = res["Set-Cookie"]
68
- res = Rack::MockRequest.new(pool).get('/', "HTTP_COOKIE" => cookie)
69
- res["Set-Cookie"].should == cookie
70
- res.body.should include('"counter"=>2')
71
- puts 'Sleeping to expire session' if $DEBUG
72
- sleep 4
73
- res = Rack::MockRequest.new(pool).get('/', "HTTP_COOKIE" => cookie)
74
- res["Set-Cookie"].should_not == cookie
75
- res.body.should include('"counter"=>1')
76
- end
77
-
78
- it "deletes cookies with :drop option" do
79
- pool = Rack::Session::Redis.new(@incrementor)
80
- req = Rack::MockRequest.new(pool)
81
- drop = Rack::Utils::Context.new(pool, @drop_session)
82
- dreq = Rack::MockRequest.new(drop)
83
-
84
- res0 = req.get("/")
85
- session = (cookie = res0["Set-Cookie"])[@session_match]
86
- res0.body.should == '{"counter"=>1}'
87
-
88
- res1 = req.get("/", "HTTP_COOKIE" => cookie)
89
- res1["Set-Cookie"][@session_match].should == session
90
- res1.body.should == '{"counter"=>2}'
91
-
92
- res2 = dreq.get("/", "HTTP_COOKIE" => cookie)
93
- res2["Set-Cookie"].should be_nil
94
- res2.body.should == '{"counter"=>3}'
95
-
96
- res3 = req.get("/", "HTTP_COOKIE" => cookie)
97
- res3["Set-Cookie"][@session_match].should_not == session
98
- res3.body.should == '{"counter"=>1}'
99
- end
100
-
101
- it "provides new session id with :renew option" do
102
- pool = Rack::Session::Redis.new(@incrementor)
103
- req = Rack::MockRequest.new(pool)
104
- renew = Rack::Utils::Context.new(pool, @renew_session)
105
- rreq = Rack::MockRequest.new(renew)
106
-
107
- res0 = req.get("/")
108
- session = (cookie = res0["Set-Cookie"])[@session_match]
109
- res0.body.should == '{"counter"=>1}'
110
-
111
- res1 = req.get("/", "HTTP_COOKIE" => cookie)
112
- res1["Set-Cookie"][@session_match].should == session
113
- res1.body.should == '{"counter"=>2}'
114
-
115
- res2 = rreq.get("/", "HTTP_COOKIE" => cookie)
116
- new_cookie = res2["Set-Cookie"]
117
- new_session = new_cookie[@session_match]
118
- new_session.should_not == session
119
- res2.body.should == '{"counter"=>3}'
120
-
121
- res3 = req.get("/", "HTTP_COOKIE" => new_cookie)
122
- res3["Set-Cookie"][@session_match].should == new_session
123
- res3.body.should == '{"counter"=>4}'
124
- end
125
-
126
- specify "omits cookie with :defer option" do
127
- pool = Rack::Session::Redis.new(@incrementor)
128
- req = Rack::MockRequest.new(pool)
129
- defer = Rack::Utils::Context.new(pool, @defer_session)
130
- dreq = Rack::MockRequest.new(defer)
131
-
132
- res0 = req.get("/")
133
- session = (cookie = res0["Set-Cookie"])[@session_match]
134
- res0.body.should == '{"counter"=>1}'
135
-
136
- res1 = req.get("/", "HTTP_COOKIE" => cookie)
137
- res1["Set-Cookie"][@session_match].should == session
138
- res1.body.should == '{"counter"=>2}'
139
-
140
- res2 = dreq.get("/", "HTTP_COOKIE" => cookie)
141
- res2["Set-Cookie"].should be_nil
142
- res2.body.should == '{"counter"=>3}'
143
-
144
- res3 = req.get("/", "HTTP_COOKIE" => cookie)
145
- res3["Set-Cookie"][@session_match].should == session
146
- res3.body.should == '{"counter"=>4}'
147
- end
148
-
149
- # anyone know how to do this better?
150
- specify "multithread: should cleanly merge sessions" do
151
- next unless $DEBUG
152
- warn 'Running multithread test for Session::Redis'
153
- pool = Rack::Session::Redis.new(@incrementor)
154
- req = Rack::MockRequest.new(pool)
155
-
156
- res = req.get('/')
157
- res.body.should == '{"counter"=>1}'
158
- cookie = res["Set-Cookie"]
159
- sess_id = cookie[/#{pool.key}=([^,;]+)/,1]
160
-
161
- delta_incrementor = lambda do |env|
162
- # emulate disconjoinment of threading
163
- env['rack.session'] = env['rack.session'].dup
164
- Thread.stop
165
- env['rack.session'][(Time.now.usec*rand).to_i] = true
166
- @incrementor.call(env)
167
- end
168
- tses = Rack::Utils::Context.new pool, delta_incrementor
169
- treq = Rack::MockRequest.new(tses)
170
- tnum = rand(7).to_i+5
171
- r = Array.new(tnum) do
172
- Thread.new(treq) do |run|
173
- run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true)
174
- end
175
- end.reverse.map{|t| t.run.join.value }
176
- r.each do |res|
177
- res['Set-Cookie'].should == cookie
178
- res.body.should include('"counter"=>2')
179
- end
180
-
181
- session = pool.pool.get(sess_id)
182
- session.size.should == tnum+1 # counter
183
- session['counter'].should == 2 # meeeh
184
-
185
- tnum = rand(7).to_i+5
186
- r = Array.new(tnum) do |i|
187
- delta_time = proc do |env|
188
- env['rack.session'][i] = Time.now
189
- Thread.stop
190
- env['rack.session'] = env['rack.session'].dup
191
- env['rack.session'][i] -= Time.now
192
- @incrementor.call(env)
193
- end
194
- app = Rack::Utils::Context.new pool, time_delta
195
- req = Rack::MockRequest.new app
196
- Thread.new(req) do |run|
197
- run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true)
198
- end
199
- end.reverse.map{|t| t.run.join.value }
200
- r.each do |res|
201
- res['Set-Cookie'].should == cookie
202
- res.body.should include('"counter"=>3')
203
- end
204
-
205
- session = pool.pool.get(sess_id)
206
- session.size.should == tnum+1
207
- session['counter'].should == 3
208
-
209
- drop_counter = proc do |env|
210
- env['rack.session'].del 'counter'
211
- env['rack.session']['foo'] = 'bar'
212
- [200, {'Content-Type'=>'text/plain'}, env['rack.session'].inspect]
213
- end
214
- tses = Rack::Utils::Context.new pool, drop_counter
215
- treq = Rack::MockRequest.new(tses)
216
- tnum = rand(7).to_i+5
217
- r = Array.new(tnum) do
218
- Thread.new(treq) do |run|
219
- run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true)
220
- end
221
- end.reverse.map{|t| t.run.join.value }
222
- r.each do |res|
223
- res['Set-Cookie'].should == cookie
224
- res.body.should include('"foo"=>"bar"')
225
- end
226
-
227
- session = pool.pool.get(sess_id)
228
- session.size.should == r.size+1
229
- session['counter'].should be_nil
230
- session['foo'].should == 'bar'
231
- end
232
- end
233
- end
234
- end