ripple 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. data/Rakefile +61 -48
  2. data/lib/ripple.rb +5 -1
  3. data/lib/ripple/core_ext/casting.rb +3 -0
  4. data/lib/ripple/document.rb +6 -2
  5. data/lib/ripple/document/associations.rb +154 -0
  6. data/lib/ripple/document/{persistence/callbacks.rb → associations/embedded.rb} +20 -24
  7. data/lib/ripple/document/associations/instantiators.rb +41 -0
  8. data/lib/{riak/util/translation.rb → ripple/document/associations/linked.rb} +14 -11
  9. data/lib/ripple/document/associations/many.rb +52 -0
  10. data/lib/ripple/document/associations/many_embedded_proxy.rb +49 -0
  11. data/lib/{riak/i18n.rb → ripple/document/associations/one.rb} +18 -2
  12. data/lib/ripple/document/associations/one_embedded_proxy.rb +41 -0
  13. data/lib/ripple/document/associations/proxy.rb +125 -0
  14. data/lib/ripple/document/attribute_methods.rb +8 -1
  15. data/lib/ripple/document/attribute_methods/read.rb +4 -0
  16. data/lib/ripple/document/attribute_methods/write.rb +4 -0
  17. data/lib/ripple/document/bucket_access.rb +1 -1
  18. data/lib/ripple/document/callbacks.rb +75 -0
  19. data/lib/ripple/document/finders.rb +50 -3
  20. data/lib/ripple/document/persistence.rb +14 -6
  21. data/lib/ripple/document/validations.rb +35 -7
  22. data/lib/ripple/document/validations/associated_validator.rb +37 -0
  23. data/lib/ripple/embedded_document.rb +8 -2
  24. data/lib/{riak/map_reduce_error.rb → ripple/embedded_document/conversion.rb} +19 -5
  25. data/lib/{riak/invalid_response.rb → ripple/embedded_document/finders.rb} +17 -8
  26. data/lib/ripple/embedded_document/persistence.rb +75 -13
  27. data/lib/ripple/locale/en.yml +7 -1
  28. data/{spec/riak/net_http_backend_spec.rb → lib/ripple/railtie.rb} +17 -13
  29. data/spec/fixtures/config.yml +3 -0
  30. data/spec/integration/ripple/associations_spec.rb +81 -0
  31. data/spec/integration/ripple/persistence_spec.rb +54 -0
  32. data/spec/ripple/associations/many_embedded_proxy_spec.rb +124 -0
  33. data/spec/ripple/associations/one_embedded_proxy_spec.rb +130 -0
  34. data/spec/ripple/associations/proxy_spec.rb +78 -0
  35. data/spec/ripple/associations_spec.rb +111 -0
  36. data/spec/ripple/attribute_methods_spec.rb +37 -16
  37. data/spec/ripple/bucket_access_spec.rb +3 -14
  38. data/spec/ripple/callbacks_spec.rb +53 -9
  39. data/spec/ripple/document_spec.rb +22 -6
  40. data/spec/ripple/embedded_document/conversion_spec.rb +35 -0
  41. data/spec/{riak/headers_spec.rb → ripple/embedded_document/finders_spec.rb} +17 -14
  42. data/spec/ripple/embedded_document/persistence_spec.rb +86 -0
  43. data/spec/ripple/embedded_document_spec.rb +1 -26
  44. data/spec/ripple/finders_spec.rb +66 -30
  45. data/spec/ripple/persistence_spec.rb +33 -21
  46. data/spec/ripple/properties_spec.rb +1 -7
  47. data/spec/ripple/ripple_spec.rb +10 -0
  48. data/spec/ripple/timestamps_spec.rb +12 -19
  49. data/spec/ripple/validations_spec.rb +48 -6
  50. data/spec/spec_helper.rb +4 -10
  51. data/spec/support/associations/proxies.rb +16 -0
  52. data/spec/support/integration.rb +4 -0
  53. data/spec/support/mocks.rb +3 -0
  54. data/spec/support/models/address.rb +8 -0
  55. data/spec/support/models/box.rb +6 -0
  56. data/spec/support/models/cardboard_box.rb +3 -0
  57. data/spec/support/models/clock.rb +6 -0
  58. data/spec/support/models/customer.rb +4 -0
  59. data/spec/support/models/email.rb +4 -0
  60. data/spec/support/models/family.rb +14 -0
  61. data/spec/support/models/favorite.rb +4 -0
  62. data/spec/support/models/invoice.rb +6 -0
  63. data/spec/support/models/late_invoice.rb +3 -0
  64. data/spec/support/models/note.rb +4 -0
  65. data/spec/support/models/page.rb +4 -0
  66. data/spec/support/models/paid_invoice.rb +4 -0
  67. data/spec/support/models/tree.rb +3 -0
  68. data/spec/support/models/user.rb +6 -0
  69. data/spec/support/models/widget.rb +6 -0
  70. metadata +111 -138
  71. data/.document +0 -5
  72. data/.gitignore +0 -26
  73. data/CONTRIBUTORS.textile +0 -5
  74. data/LICENSE +0 -13
  75. data/README.textile +0 -128
  76. data/RELEASE_NOTES.textile +0 -68
  77. data/VERSION +0 -1
  78. data/lib/riak.rb +0 -46
  79. data/lib/riak/bucket.rb +0 -157
  80. data/lib/riak/client.rb +0 -139
  81. data/lib/riak/client/curb_backend.rb +0 -82
  82. data/lib/riak/client/http_backend.rb +0 -209
  83. data/lib/riak/client/net_http_backend.rb +0 -49
  84. data/lib/riak/failed_request.rb +0 -37
  85. data/lib/riak/link.rb +0 -73
  86. data/lib/riak/locale/en.yml +0 -37
  87. data/lib/riak/map_reduce.rb +0 -248
  88. data/lib/riak/robject.rb +0 -258
  89. data/lib/riak/util/escape.rb +0 -12
  90. data/lib/riak/util/fiber1.8.rb +0 -48
  91. data/lib/riak/util/headers.rb +0 -44
  92. data/lib/riak/util/multipart.rb +0 -52
  93. data/lib/riak/walk_spec.rb +0 -117
  94. data/ripple.gemspec +0 -169
  95. data/spec/fixtures/cat.jpg +0 -0
  96. data/spec/fixtures/multipart-blank.txt +0 -7
  97. data/spec/fixtures/multipart-with-body.txt +0 -16
  98. data/spec/riak/bucket_spec.rb +0 -230
  99. data/spec/riak/client_spec.rb +0 -174
  100. data/spec/riak/curb_backend_spec.rb +0 -50
  101. data/spec/riak/escape_spec.rb +0 -17
  102. data/spec/riak/http_backend_spec.rb +0 -131
  103. data/spec/riak/link_spec.rb +0 -82
  104. data/spec/riak/map_reduce_spec.rb +0 -352
  105. data/spec/riak/multipart_spec.rb +0 -36
  106. data/spec/riak/object_spec.rb +0 -532
  107. data/spec/riak/walk_spec_spec.rb +0 -208
  108. data/spec/spec.opts +0 -1
  109. data/spec/support/http_backend_implementation_examples.rb +0 -215
  110. data/spec/support/mock_server.rb +0 -58
@@ -1,36 +0,0 @@
1
- # Copyright 2010 Sean Cribbs, Sonian Inc., and Basho Technologies, Inc.
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # http://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
- require File.expand_path("../spec_helper", File.dirname(__FILE__))
15
-
16
- describe Riak::Util::Multipart do
17
- it "should extract the boundary string from a header value" do
18
- Riak::Util::Multipart.extract_boundary("multipart/mixed; boundary=123446677890").should == "123446677890"
19
- end
20
-
21
- it "should parse an empty multipart body into empty arrays" do
22
- data = File.read(File.expand_path("#{File.dirname(__FILE__)}/../fixtures/multipart-blank.txt"))
23
- Riak::Util::Multipart.parse(data, "73NmmA8dJxSB5nL2dVerpFIi8ze").should == [[]]
24
- end
25
-
26
- it "should parse multipart body into nested arrays with response-like results" do
27
- data = File.read(File.expand_path("#{File.dirname(__FILE__)}/../fixtures/multipart-with-body.txt"))
28
- results = Riak::Util::Multipart.parse(data, "5EiMOjuGavQ2IbXAqsJPLLfJNlA")
29
- results.should be_kind_of(Array)
30
- results.first.should be_kind_of(Array)
31
- obj = results.first.first
32
- obj.should be_kind_of(Hash)
33
- obj.should have_key(:headers)
34
- obj.should have_key(:body)
35
- end
36
- end
@@ -1,532 +0,0 @@
1
- # Copyright 2010 Sean Cribbs, Sonian Inc., and Basho Technologies, Inc.
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # http://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
- require File.expand_path("../spec_helper", File.dirname(__FILE__))
15
-
16
- describe Riak::RObject do
17
- before :each do
18
- @client = Riak::Client.new
19
- @bucket = Riak::Bucket.new(@client, "foo")
20
- end
21
-
22
- describe "initialization" do
23
- it "should set the bucket" do
24
- @object = Riak::RObject.new(@bucket)
25
- @object.bucket.should == @bucket
26
- end
27
-
28
- it "should set the key" do
29
- @object = Riak::RObject.new(@bucket, "bar")
30
- @object.key.should == "bar"
31
- end
32
-
33
- it "should initialize the links to an empty set" do
34
- @object = Riak::RObject.new(@bucket, "bar")
35
- @object.links.should == Set.new
36
- end
37
-
38
- it "should initialize the meta to an empty hash" do
39
- @object = Riak::RObject.new(@bucket, "bar")
40
- @object.meta.should == {}
41
- end
42
-
43
- it "should yield itself to a given block" do
44
- Riak::RObject.new(@bucket, "bar") do |r|
45
- r.key.should == "bar"
46
- end
47
- end
48
- end
49
-
50
- describe "serialization" do
51
- before :each do
52
- @object = Riak::RObject.new(@bucket, "bar")
53
- end
54
-
55
- it "should change the data into a string by default when serializing" do
56
- @object.serialize("foo").should == "foo"
57
- @object.serialize(2).should == "2"
58
- end
59
-
60
- it "should not change the data when it is an IO" do
61
- file = File.open("#{File.dirname(__FILE__)}/../fixtures/cat.jpg", "r")
62
- file.should_not_receive(:to_s)
63
- @object.serialize(file).should == file
64
- end
65
-
66
- it "should not modify the data by default when deserializing" do
67
- @object.deserialize("foo").should == "foo"
68
- end
69
-
70
- describe "when the content type is YAML" do
71
- before :each do
72
- @object.content_type = "text/x-yaml"
73
- end
74
-
75
- it "should serialize into a YAML stream" do
76
- @object.serialize({"foo" => "bar"}).should == "--- \nfoo: bar\n"
77
- end
78
-
79
- it "should deserialize a YAML stream" do
80
- @object.deserialize("--- \nfoo: bar\n").should == {"foo" => "bar"}
81
- end
82
- end
83
-
84
- describe "when the content type is JSON" do
85
- before :each do
86
- @object.content_type = "application/json"
87
- end
88
-
89
- it "should serialize into a JSON blob" do
90
- @object.serialize({"foo" => "bar"}).should == '{"foo":"bar"}'
91
- @object.serialize(2).should == "2"
92
- @object.serialize("Some text").should == '"Some text"'
93
- @object.serialize([1,2,3]).should == "[1,2,3]"
94
- end
95
-
96
- it "should deserialize a JSON blob" do
97
- @object.deserialize('{"foo":"bar"}').should == {"foo" => "bar"}
98
- @object.deserialize("2").should == 2
99
- @object.deserialize('"Some text"').should == "Some text"
100
- @object.deserialize('[1,2,3]').should == [1,2,3]
101
- end
102
- end
103
-
104
- describe "when the content type is an octet-stream" do
105
- before :each do
106
- @object.content_type = "application/octet-stream"
107
- end
108
-
109
- describe "if the ruby-serialization meta field is set to Marshal" do
110
- before :each do
111
- @object.meta['ruby-serialization'] = "Marshal"
112
- @payload = Marshal.dump({"foo" => "bar"})
113
- end
114
-
115
- it "should dump via Marshal" do
116
- @object.serialize({"foo" => "bar"}).should == @payload
117
- end
118
-
119
- it "should load from Marshal" do
120
- @object.deserialize(@payload).should == {"foo" => "bar"}
121
- end
122
- end
123
-
124
- describe "if the ruby-serialization meta field is not set to Marshal" do
125
- before :each do
126
- @object.meta.delete("ruby-serialization")
127
- end
128
-
129
- it "should dump to a string" do
130
- @object.serialize(2).should == "2"
131
- @object.serialize("foo").should == "foo"
132
- end
133
-
134
- it "should load the body unmodified" do
135
- @object.deserialize("foo").should == "foo"
136
- end
137
- end
138
- end
139
- end
140
-
141
- describe "loading data from the response" do
142
- before :each do
143
- @object = Riak::RObject.new(@bucket, "bar")
144
- end
145
-
146
- it "should load the content type" do
147
- @object.load({:headers => {"content-type" => ["application/json"]}})
148
- @object.content_type.should == "application/json"
149
- end
150
-
151
- it "should load the body data" do
152
- @object.load({:headers => {"content-type" => ["application/json"]}, :body => '{"foo":"bar"}'})
153
- @object.data.should be_present
154
- end
155
-
156
- it "should deserialize the body data" do
157
- @object.should_receive(:deserialize).with("{}").and_return({})
158
- @object.load({:headers => {"content-type" => ["application/json"]}, :body => "{}"})
159
- @object.data.should == {}
160
- end
161
-
162
- it "should leave the object data unchanged if the response body is blank" do
163
- @object.data = "Original data"
164
- @object.load({:headers => {"content-type" => ["application/json"]}, :body => ""})
165
- @object.data.should == "Original data"
166
- end
167
-
168
- it "should load the vclock from the headers" do
169
- @object.load({:headers => {"content-type" => ["application/json"], 'x-riak-vclock' => ["somereallylongbase64string=="]}, :body => "{}"})
170
- @object.vclock.should == "somereallylongbase64string=="
171
- end
172
-
173
- it "should load links from the headers" do
174
- @object.load({:headers => {"content-type" => ["application/json"], "link" => ['</riak/bar>; rel="up"']}, :body => "{}"})
175
- @object.links.should have(1).item
176
- @object.links.first.url.should == "/riak/bar"
177
- @object.links.first.rel.should == "up"
178
- end
179
-
180
- it "should load the ETag from the headers" do
181
- @object.load({:headers => {"content-type" => ["application/json"], "etag" => ["32748nvas83572934"]}, :body => "{}"})
182
- @object.etag.should == "32748nvas83572934"
183
- end
184
-
185
- it "should load the modified date from the headers" do
186
- time = Time.now
187
- @object.load({:headers => {"content-type" => ["application/json"], "last-modified" => [time.httpdate]}, :body => "{}"})
188
- @object.last_modified.to_s.should == time.to_s # bah, times are not equivalent unless equal
189
- end
190
-
191
- it "should load meta information from the headers" do
192
- @object.load({:headers => {"content-type" => ["application/json"], "x-riak-meta-some-kind-of-robot" => ["for AWESOME"]}, :body => "{}"})
193
- @object.meta["some-kind-of-robot"].should == ["for AWESOME"]
194
- end
195
-
196
- it "should parse the location header into the key when present" do
197
- @object.load({:headers => {"content-type" => ["application/json"], "location" => ["/riak/foo/baz"]}})
198
- @object.key.should == "baz"
199
- end
200
-
201
- it "should be in conflict when the response code is 300 and the content-type is multipart/mixed" do
202
- @object.load({:headers => {"content-type" => ["multipart/mixed; boundary=foo"]}, :code => 300 })
203
- @object.should be_conflict
204
- end
205
-
206
- it "should unescape the key given in the location header" do
207
- @object.load({:headers => {"content-type" => ["application/json"], "location" => ["/riak/foo/baz%20"]}})
208
- @object.key.should == "baz "
209
- end
210
- end
211
-
212
- describe "extracting siblings" do
213
- before :each do
214
- @object = Riak::RObject.new(@bucket, "bar").load({:headers => {"x-riak-vclock" => ["merged"], "content-type" => ["multipart/mixed; boundary=foo"]}, :code => 300, :body => "\n--foo\nContent-Type: text/plain\n\nbar\n--foo\nContent-Type: text/plain\n\nbaz\n--foo--\n"})
215
- end
216
-
217
- it "should extract the siblings" do
218
- @object.should have(2).siblings
219
- siblings = @object.siblings
220
- siblings[0].data.should == "bar"
221
- siblings[1].data.should == "baz"
222
- end
223
-
224
- it "should set the key on both siblings" do
225
- @object.siblings.should be_all {|s| s.key == "bar" }
226
- end
227
-
228
- it "should set the vclock on both siblings to the merged vclock" do
229
- @object.siblings.should be_all {|s| s.vclock == "merged" }
230
- end
231
- end
232
-
233
- describe "headers used for storing the object" do
234
- before :each do
235
- @object = Riak::RObject.new(@bucket, "bar")
236
- end
237
-
238
- it "should include the content type" do
239
- @object.content_type = "application/json"
240
- @object.store_headers["Content-Type"].should == "application/json"
241
- end
242
-
243
- it "should include the vclock when present" do
244
- @object.vclock = "123445678990"
245
- @object.store_headers["X-Riak-Vclock"].should == "123445678990"
246
- end
247
-
248
- it "should exclude the vclock when nil" do
249
- @object.vclock = nil
250
- @object.store_headers.should_not have_key("X-Riak-Vclock")
251
- end
252
-
253
- describe "when links are defined" do
254
- before :each do
255
- @object.links << Riak::Link.new("/riak/foo/baz", "next")
256
- end
257
-
258
- it "should include a Link header with references to other objects" do
259
- @object.store_headers.should have_key("Link")
260
- @object.store_headers["Link"].should include('</riak/foo/baz>; riaktag="next"')
261
- end
262
-
263
- it "should exclude the 'up' link to the bucket from the header" do
264
- @object.links << Riak::Link.new("/riak/foo", "up")
265
- @object.store_headers.should have_key("Link")
266
- @object.store_headers["Link"].should_not include('riaktag="up"')
267
- end
268
-
269
- it "should not allow duplicate links" do
270
- @object.links << Riak::Link.new("/riak/foo/baz", "next")
271
- @object.links.length.should == 1
272
- end
273
- end
274
-
275
- it "should exclude the Link header when no links are present" do
276
- @object.links = Set.new
277
- @object.store_headers.should_not have_key("Link")
278
- end
279
-
280
- describe "when meta fields are present" do
281
- before :each do
282
- @object.meta = {"some-kind-of-robot" => true, "powers" => "for awesome", "cold-ones" => 10}
283
- end
284
-
285
- it "should include X-Riak-Meta-* headers for each meta key" do
286
- @object.store_headers.should have_key("X-Riak-Meta-some-kind-of-robot")
287
- @object.store_headers.should have_key("X-Riak-Meta-cold-ones")
288
- @object.store_headers.should have_key("X-Riak-Meta-powers")
289
- end
290
-
291
- it "should turn non-string meta values into strings" do
292
- @object.store_headers["X-Riak-Meta-some-kind-of-robot"].should == "true"
293
- @object.store_headers["X-Riak-Meta-cold-ones"].should == "10"
294
- end
295
-
296
- it "should leave string meta values unchanged in the header" do
297
- @object.store_headers["X-Riak-Meta-powers"].should == "for awesome"
298
- end
299
- end
300
- end
301
-
302
- describe "headers used for reloading the object" do
303
- before :each do
304
- @object = Riak::RObject.new(@bucket, "bar")
305
- end
306
-
307
- it "should be blank when the etag and last_modified properties are blank" do
308
- @object.etag.should be_blank
309
- @object.last_modified.should be_blank
310
- @object.reload_headers.should be_blank
311
- end
312
-
313
- it "should include the If-None-Match key when the etag is present" do
314
- @object.etag = "etag!"
315
- @object.reload_headers['If-None-Match'].should == "etag!"
316
- end
317
-
318
- it "should include the If-Modified-Since header when the last_modified time is present" do
319
- time = Time.now
320
- @object.last_modified = time
321
- @object.reload_headers['If-Modified-Since'].should == time.httpdate
322
- end
323
- end
324
-
325
- describe "when storing the object normally" do
326
- before :each do
327
- @http = mock("HTTPBackend")
328
- @client.stub!(:http).and_return(@http)
329
- @object = Riak::RObject.new(@bucket)
330
- @object.content_type = "text/plain"
331
- @object.data = "This is some text."
332
- @headers = @object.store_headers
333
- end
334
-
335
- it "should raise an error when the content_type is blank" do
336
- lambda { @object.content_type = nil; @object.store }.should raise_error(ArgumentError)
337
- lambda { @object.content_type = " "; @object.store }.should raise_error(ArgumentError)
338
- end
339
-
340
- describe "when the object has no key" do
341
- it "should issue a POST request to the bucket, and update the object properties (returning the body by default)" do
342
- @http.should_receive(:post).with(201, "/riak/", "foo", {:returnbody => true}, "This is some text.", @headers).and_return({:headers => {'location' => ["/riak/foo/somereallylongstring"], "x-riak-vclock" => ["areallylonghashvalue"]}, :code => 201})
343
- @object.store
344
- @object.key.should == "somereallylongstring"
345
- @object.vclock.should == "areallylonghashvalue"
346
- end
347
-
348
- it "should include persistence-tuning parameters in the query string" do
349
- @http.should_receive(:post).with(201, "/riak/", "foo", {:dw => 2, :returnbody => true}, "This is some text.", @headers).and_return({:headers => {'location' => ["/riak/foo/somereallylongstring"], "x-riak-vclock" => ["areallylonghashvalue"]}, :code => 201})
350
- @object.store(:dw => 2)
351
- end
352
-
353
- it "should escape the bucket name" do
354
- @bucket.should_receive(:name).and_return("foo ")
355
- @http.should_receive(:post).with(201, "/riak/", "foo%20", {:returnbody => true}, "This is some text.", @headers).and_return({:headers => {'location' => ["/riak/foo/somereallylongstring"], "x-riak-vclock" => ["areallylonghashvalue"]}, :code => 201})
356
- @object.store
357
- end
358
- end
359
-
360
- describe "when the object has a key" do
361
- before :each do
362
- @object.key = "bar"
363
- end
364
-
365
- it "should issue a PUT request to the bucket, and update the object properties (returning the body by default)" do
366
- @http.should_receive(:put).with([200,204,300], "/riak/", "foo/bar", {:returnbody => true}, "This is some text.", @headers).and_return({:headers => {'location' => ["/riak/foo/somereallylongstring"], "x-riak-vclock" => ["areallylonghashvalue"]}, :code => 204})
367
- @object.store
368
- @object.key.should == "somereallylongstring"
369
- @object.vclock.should == "areallylonghashvalue"
370
- end
371
-
372
- it "should include persistence-tuning parameters in the query string" do
373
- @http.should_receive(:put).with([200,204,300], "/riak/", "foo/bar", {:dw => 2, :returnbody => true}, "This is some text.", @headers).and_return({:headers => {'location' => ["/riak/foo/somereallylongstring"], "x-riak-vclock" => ["areallylonghashvalue"]}, :code => 204})
374
- @object.store(:dw => 2)
375
- end
376
-
377
- it "should escape the bucket and key names" do
378
- @http.should_receive(:put).with([200,204,300], "/riak/", "foo%20/bar%2Fbaz", {:returnbody => true}, "This is some text.", @headers).and_return({:headers => {'location' => ["/riak/foo/somereallylongstring"], "x-riak-vclock" => ["areallylonghashvalue"]}, :code => 204})
379
- @bucket.instance_variable_set(:@name, "foo ")
380
- @object.key = "bar/baz"
381
- @object.store
382
- end
383
- end
384
- end
385
-
386
- describe "when reloading the object" do
387
- before :each do
388
- @http = mock("HTTPBackend")
389
- @client.stub!(:http).and_return(@http)
390
- @object = Riak::RObject.new(@bucket, "bar")
391
- @object.vclock = "somereallylongstring"
392
- @object.stub!(:reload_headers).and_return({})
393
- end
394
-
395
- it "should return without requesting if the key is blank" do
396
- @object.key = nil
397
- @http.should_not_receive(:get)
398
- @object.reload
399
- end
400
-
401
- it "should return without requesting if the vclock is blank" do
402
- @object.vclock = nil
403
- @http.should_not_receive(:get)
404
- @object.reload
405
- end
406
-
407
- it "should make the request if the key is present and the :force option is given" do
408
- @http.should_receive(:get).and_return({:headers => {}, :code => 304})
409
- @object.reload :force => true
410
- end
411
-
412
- it "should pass along the reload_headers" do
413
- @headers = {"If-None-Match" => "etag"}
414
- @object.should_receive(:reload_headers).and_return(@headers)
415
- @http.should_receive(:get).with([200,304], "/riak/", "foo", "bar", {}, @headers).and_return({:code => 304})
416
- @object.reload
417
- end
418
-
419
- it "should return without modifying the object if the response is 304 Not Modified" do
420
- @http.should_receive(:get).and_return({:code => 304})
421
- @object.should_not_receive(:load)
422
- @object.reload
423
- end
424
-
425
- it "should raise an exception when the response code is not 200 or 304" do
426
- @http.should_receive(:get).and_raise(Riak::FailedRequest.new(:get, 200, 500, {}, ''))
427
- @object.should_not_receive(:load)
428
- lambda { @object.reload }.should raise_error(Riak::FailedRequest)
429
- end
430
-
431
- it "should include 300 in valid responses if the bucket has allow_mult set" do
432
- @object.bucket.should_receive(:allow_mult).and_return(true)
433
- @http.should_receive(:get).with([200,300,304], "/riak/", "foo", "bar", {}, {}).and_return({:code => 304})
434
- @object.reload
435
- end
436
-
437
- it "should escape the bucket and key names" do
438
- @bucket.should_receive(:name).and_return("some/deep/path")
439
- @object.key = "another/deep/path"
440
- @http.should_receive(:get).with([200,304], "/riak/", "some%2Fdeep%2Fpath", "another%2Fdeep%2Fpath", {}, {}).and_return({:code => 304})
441
- @object.reload
442
- end
443
- end
444
-
445
- describe "walking from the object to linked objects" do
446
- before :each do
447
- @http = mock("HTTPBackend")
448
- @client.stub!(:http).and_return(@http)
449
- @client.stub!(:bucket).and_return(@bucket)
450
- @object = Riak::RObject.new(@bucket, "bar")
451
- @body = File.read(File.expand_path("#{File.dirname(__FILE__)}/../fixtures/multipart-with-body.txt"))
452
- end
453
-
454
- it "should issue a GET request to the given walk spec" do
455
- @http.should_receive(:get).with(200, "/riak/", "foo", "bar", "_,next,1").and_return(:headers => {"content-type" => ["multipart/mixed; boundary=12345"]}, :body => "\n--12345\nContent-Type: multipart/mixed; boundary=09876\n\n--09876--\n\n--12345--\n")
456
- @object.walk(nil,"next",true)
457
- end
458
-
459
- it "should parse the results into arrays of objects" do
460
- @http.stub!(:get).and_return(:headers => {"content-type" => ["multipart/mixed; boundary=5EiMOjuGavQ2IbXAqsJPLLfJNlA"]}, :body => @body)
461
- results = @object.walk(nil,"next",true)
462
- results.should be_kind_of(Array)
463
- results.first.should be_kind_of(Array)
464
- obj = results.first.first
465
- obj.should be_kind_of(Riak::RObject)
466
- obj.content_type.should == "text/plain"
467
- obj.key.should == "baz"
468
- obj.bucket.should == @bucket
469
- end
470
-
471
- it "should assign the bucket for newly parsed objects" do
472
- @http.stub!(:get).and_return(:headers => {"content-type" => ["multipart/mixed; boundary=5EiMOjuGavQ2IbXAqsJPLLfJNlA"]}, :body => @body)
473
- @client.should_receive(:bucket).with("foo", :keys => false).and_return(@bucket)
474
- @object.walk(nil,"next",true)
475
- end
476
-
477
- it "should escape the bucket, key and link specs" do
478
- @object.key = "bar/baz"
479
- @bucket.should_receive(:name).and_return("quin/quux")
480
- @http.should_receive(:get).with(200, "/riak/", "quin%2Fquux", "bar%2Fbaz", "_,next%2F2,1").and_return(:headers => {"content-type" => ["multipart/mixed; boundary=12345"]}, :body => "\n--12345\nContent-Type: multipart/mixed; boundary=09876\n\n--09876--\n\n--12345--\n")
481
- @object.walk(:tag => "next/2", :keep => true)
482
- end
483
- end
484
-
485
- describe "when deleting" do
486
- before :each do
487
- @http = mock("HTTPBackend")
488
- @client.stub!(:http).and_return(@http)
489
- @object = Riak::RObject.new(@bucket, "bar")
490
- end
491
-
492
- it "should make a DELETE request to the Riak server and freeze the object" do
493
- @http.should_receive(:delete).with([204,404], "/riak/", "foo", "bar").and_return({:code => 204, :headers => {}})
494
- @object.delete
495
- @object.should be_frozen
496
- end
497
-
498
- it "should do nothing when the key is blank" do
499
- @http.should_not_receive(:delete)
500
- @object.key = nil
501
- @object.delete
502
- end
503
-
504
- it "should pass through a failed request exception" do
505
- @http.should_receive(:delete).and_raise(Riak::FailedRequest.new(:delete, [204,404], 500, {}, ""))
506
- lambda { @object.delete }.should raise_error(Riak::FailedRequest)
507
- end
508
-
509
- it "should escape the bucket and key names" do
510
- @object.key = "deep/path"
511
- @bucket.should_receive(:name).and_return("bucket spaces")
512
- @http.should_receive(:delete).with([204,404], "/riak/", "bucket%20spaces", "deep%2Fpath").and_return({:code => 204, :headers => {}})
513
- @object.delete
514
- end
515
- end
516
-
517
- it "should convert to a link having the same url and an empty tag" do
518
- @object = Riak::RObject.new(@bucket, "bar")
519
- @object.to_link.should == Riak::Link.new("/riak/foo/bar", nil)
520
- end
521
-
522
- it "should convert to a link having the same url and a supplied tag" do
523
- @object = Riak::RObject.new(@bucket, "bar")
524
- @object.to_link("next").should == Riak::Link.new("/riak/foo/bar", "next")
525
- end
526
-
527
- it "should escape the bucket and key when converting to a link" do
528
- @object = Riak::RObject.new(@bucket, "deep/path")
529
- @bucket.should_receive(:name).and_return("bucket spaces")
530
- @object.to_link("bar").url.should == "/riak/bucket%20spaces/deep%2Fpath"
531
- end
532
- end