riak-client 0.8.3 → 0.9.0.beta

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.
@@ -32,11 +32,19 @@ describe Riak::Link do
32
32
  result[1].key.should == nil
33
33
  result[1].rel.should == "up"
34
34
  end
35
+
36
+ it "should keep the url intact if it does not point to a bucket or bucket/key" do
37
+ result = Riak::Link.parse('</mapred>; rel="riak_kv_wm_mapred"')
38
+ result[0].url.should == "/mapred"
39
+ result[0].bucket.should be_nil
40
+ result[0].key.should be_nil
41
+ end
35
42
  end
36
43
 
37
44
  it "should convert to a string appropriate for use in the Link header" do
38
45
  Riak::Link.new("/riak/foo", "up").to_s.should == '</riak/foo>; riaktag="up"'
39
46
  Riak::Link.new("/riak/foo/bar", "next").to_s.should == '</riak/foo/bar>; riaktag="next"'
47
+ Riak::Link.new("/riak", "riak_kv_wm_raw").to_s.should == '</riak>; riaktag="riak_kv_wm_raw"'
40
48
  end
41
49
 
42
50
  it "should convert to a walk spec when pointing to an object" do
@@ -16,8 +16,8 @@ require File.expand_path("../spec_helper", File.dirname(__FILE__))
16
16
  describe Riak::MapReduce do
17
17
  before :each do
18
18
  @client = Riak::Client.new
19
- @http = mock("HTTPBackend")
20
- @client.stub!(:http).and_return(@http)
19
+ @backend = mock("Backend")
20
+ @client.stub!(:backend).and_return(@backend)
21
21
  @mr = Riak::MapReduce.new(@client)
22
22
  end
23
23
 
@@ -101,6 +101,20 @@ describe Riak::MapReduce do
101
101
  @mr.add("docs")
102
102
  @mr.inputs.should == "docs"
103
103
  end
104
+
105
+ it "should accept a list of key-filters along with a bucket" do
106
+ @mr.add("foo", [[:tokenize, "-", 3], [:string_to_int], [:between, 2009, 2010]])
107
+ @mr.inputs.should == {:bucket => "foo", :filters => [[:tokenize, "-", 3], [:string_to_int], [:between, 2009, 2010]]}
108
+ end
109
+
110
+ it "should add a bucket and filter list via a builder block" do
111
+ @mr.filter("foo") do
112
+ tokenize "-", 3
113
+ string_to_int
114
+ between 2009, 2010
115
+ end
116
+ @mr.inputs.should == {:bucket => "foo", :filters => [[:tokenize, "-", 3], [:string_to_int], [:between, 2009, 2010]]}
117
+ end
104
118
  end
105
119
 
106
120
  [:map, :reduce].each do |type|
@@ -223,24 +237,20 @@ describe Riak::MapReduce do
223
237
  lambda { @mr.run }.should raise_error(Riak::MapReduceError)
224
238
  end
225
239
 
226
- it "should issue POST request to the mapred endpoint" do
227
- @http.should_receive(:post).with(200, "/mapred", @mr.to_json, hash_including("Content-Type" => "application/json")).and_return({:headers => {'content-type' => ["application/json"]}, :body => "[]"})
228
- @mr.run
229
- end
230
-
231
- it "should vivify JSON responses" do
232
- @http.stub!(:post).and_return(:headers => {'content-type' => ["application/json"]}, :body => '[{"key":"value"}]')
233
- @mr.run.should == [{"key" => "value"}]
240
+ it "should submit the query to the backend" do
241
+ @backend.should_receive(:mapred).with(@mr).and_return([])
242
+ @mr.run.should == []
234
243
  end
235
244
 
236
- it "should return the full response hash for non-JSON responses" do
237
- response = {:code => 200, :headers => {'content-type' => ["text/plain"]}, :body => 'This is some text.'}
238
- @http.stub!(:post).and_return(response)
239
- @mr.run.should == response
245
+ it "should pass the given block to the backend for streaming" do
246
+ arr = []
247
+ @backend.should_receive(:mapred).with(@mr).and_yield("foo").and_yield("bar")
248
+ @mr.run {|v| arr << v }
249
+ arr.should == ["foo", "bar"]
240
250
  end
241
251
 
242
252
  it "should interpret failed requests with JSON content-types as map reduce errors" do
243
- @http.stub!(:post).and_raise(Riak::FailedRequest.new(:post, 200, 500, {"content-type" => ["application/json"]}, '{"error":"syntax error"}'))
253
+ @backend.stub!(:mapred).and_raise(Riak::FailedRequest.new(:post, 200, 500, {"content-type" => ["application/json"]}, '{"error":"syntax error"}'))
244
254
  lambda { @mr.run }.should raise_error(Riak::MapReduceError)
245
255
  begin
246
256
  @mr.run
@@ -252,143 +262,8 @@ describe Riak::MapReduce do
252
262
  end
253
263
 
254
264
  it "should re-raise non-JSON error responses" do
255
- @http.stub!(:post).and_raise(Riak::FailedRequest.new(:post, 200, 500, {"content-type" => ["text/plain"]}, 'Oops, you bwoke it.'))
265
+ @backend.stub!(:mapred).and_raise(Riak::FailedRequest.new(:post, 200, 500, {"content-type" => ["text/plain"]}, 'Oops, you bwoke it.'))
256
266
  lambda { @mr.run }.should raise_error(Riak::FailedRequest)
257
267
  end
258
268
  end
259
269
  end
260
-
261
- describe Riak::MapReduce::Phase do
262
- before :each do
263
- @fun = "function(v,_,_){ return v['values'][0]['data']; }"
264
- end
265
-
266
- it "should initialize with a type and a function" do
267
- phase = Riak::MapReduce::Phase.new(:type => :map, :function => @fun, :language => "javascript")
268
- phase.type.should == :map
269
- phase.function.should == @fun
270
- phase.language.should == "javascript"
271
- end
272
-
273
- it "should initialize with a type and an MF" do
274
- phase = Riak::MapReduce::Phase.new(:type => :map, :function => ["module", "function"], :language => "erlang")
275
- phase.type.should == :map
276
- phase.function.should == ["module", "function"]
277
- phase.language.should == "erlang"
278
- end
279
-
280
- it "should initialize with a type and a bucket/key" do
281
- phase = Riak::MapReduce::Phase.new(:type => :map, :function => {:bucket => "funs", :key => "awesome_map"}, :language => "javascript")
282
- phase.type.should == :map
283
- phase.function.should == {:bucket => "funs", :key => "awesome_map"}
284
- phase.language.should == "javascript"
285
- end
286
-
287
- it "should assume the language is erlang when the function is an array" do
288
- phase = Riak::MapReduce::Phase.new(:type => :map, :function => ["module", "function"])
289
- phase.language.should == "erlang"
290
- end
291
-
292
- it "should assume the language is javascript when the function is a string" do
293
- phase = Riak::MapReduce::Phase.new(:type => :map, :function => @fun)
294
- phase.language.should == "javascript"
295
- end
296
-
297
- it "should assume the language is javascript when the function is a hash" do
298
- phase = Riak::MapReduce::Phase.new(:type => :map, :function => {:bucket => "jobs", :key => "awesome_map"})
299
- phase.language.should == "javascript"
300
- end
301
-
302
- it "should accept a WalkSpec for the function when a link phase" do
303
- phase = Riak::MapReduce::Phase.new(:type => :link, :function => Riak::WalkSpec.new({}))
304
- phase.function.should be_kind_of(Riak::WalkSpec)
305
- end
306
-
307
- it "should raise an error if a WalkSpec is given for a phase type other than :link" do
308
- lambda { Riak::MapReduce::Phase.new(:type => :map, :function => Riak::WalkSpec.new({})) }.should raise_error(ArgumentError)
309
- end
310
-
311
- describe "converting to JSON for the job" do
312
- before :each do
313
- @phase = Riak::MapReduce::Phase.new(:type => :map, :function => "")
314
- end
315
-
316
- [:map, :reduce].each do |type|
317
- describe "when a #{type} phase" do
318
- before :each do
319
- @phase.type = type
320
- end
321
-
322
- it "should be an object with a single key of '#{type}'" do
323
- @phase.to_json.should =~ /^\{"#{type}":/
324
- end
325
-
326
- it "should include the language" do
327
- @phase.to_json.should =~ /"language":/
328
- end
329
-
330
- it "should include the keep value" do
331
- @phase.to_json.should =~ /"keep":false/
332
- @phase.keep = true
333
- @phase.to_json.should =~ /"keep":true/
334
- end
335
-
336
- it "should include the function source when the function is a source string" do
337
- @phase.function = "function(v,_,_){ return v; }"
338
- @phase.to_json.should include(@phase.function)
339
- @phase.to_json.should =~ /"source":/
340
- end
341
-
342
- it "should include the function name when the function is not a lambda" do
343
- @phase.function = "Riak.mapValues"
344
- @phase.to_json.should include('"name":"Riak.mapValues"')
345
- @phase.to_json.should_not include('"source"')
346
- end
347
-
348
- it "should include the bucket and key when referring to a stored function" do
349
- @phase.function = {:bucket => "design", :key => "wordcount_map"}
350
- @phase.to_json.should include('"bucket":"design"')
351
- @phase.to_json.should include('"key":"wordcount_map"')
352
- end
353
-
354
- it "should include the module and function when invoking an Erlang function" do
355
- @phase.function = ["riak_mapreduce", "mapreduce_fun"]
356
- @phase.to_json.should include('"module":"riak_mapreduce"')
357
- @phase.to_json.should include('"function":"mapreduce_fun"')
358
- end
359
- end
360
- end
361
-
362
- describe "when a link phase" do
363
- before :each do
364
- @phase.type = :link
365
- @phase.function = {}
366
- end
367
-
368
- it "should be an object of a single key 'link'" do
369
- @phase.to_json.should =~ /^\{"link":/
370
- end
371
-
372
- it "should include the bucket" do
373
- @phase.to_json.should =~ /"bucket":"_"/
374
- @phase.function[:bucket] = "foo"
375
- @phase.to_json.should =~ /"bucket":"foo"/
376
- end
377
-
378
- it "should include the tag" do
379
- @phase.to_json.should =~ /"tag":"_"/
380
- @phase.function[:tag] = "parent"
381
- @phase.to_json.should =~ /"tag":"parent"/
382
- end
383
-
384
- it "should include the keep value" do
385
- @phase.to_json.should =~ /"keep":false/
386
- @phase.keep = true
387
- @phase.to_json.should =~ /"keep":true/
388
- @phase.keep = false
389
- @phase.function[:keep] = true
390
- @phase.to_json.should =~ /"keep":true/
391
- end
392
- end
393
- end
394
- end
@@ -103,7 +103,6 @@ describe Riak::RObject do
103
103
  @payload = Marshal.dump({"foo" => "bar"})
104
104
  end
105
105
 
106
-
107
106
  it "should dump via Marshal" do
108
107
  @object.serialize({"foo" => "bar"}).should == @payload
109
108
  end
@@ -172,88 +171,6 @@ describe Riak::RObject do
172
171
  end
173
172
  end
174
173
 
175
- describe "loading data from the response" do
176
- before :each do
177
- @object = Riak::RObject.new(@bucket, "bar")
178
- end
179
-
180
- it "should load the content type" do
181
- @object.load({:headers => {"content-type" => ["application/json"]}})
182
- @object.content_type.should == "application/json"
183
- end
184
-
185
- it "should load the body data" do
186
- @object.load({:headers => {"content-type" => ["application/json"]}, :body => '{"foo":"bar"}'})
187
- @object.raw_data.should be_present
188
- @object.data.should be_present
189
- end
190
-
191
- it "should handle raw data properly" do
192
- @object.should_not_receive(:deserialize) # optimize for the raw_data case, don't penalize people for using raw_data
193
- @object.load({:headers => {"content-type" => ["application/json"]}, :body => body = '{"foo":"bar"}'})
194
- @object.raw_data.should == body
195
- end
196
-
197
- it "should deserialize the body data" do
198
- @object.should_receive(:deserialize).with("{}").and_return({})
199
- @object.load({:headers => {"content-type" => ["application/json"]}, :body => "{}"})
200
- @object.data.should == {}
201
- end
202
-
203
- it "should leave the object data unchanged if the response body is blank" do
204
- @object.data = "Original data"
205
- @object.load({:headers => {"content-type" => ["application/json"]}, :body => ""})
206
- @object.data.should == "Original data"
207
- end
208
-
209
- it "should load the vclock from the headers" do
210
- @object.load({:headers => {"content-type" => ["application/json"], 'x-riak-vclock' => ["somereallylongbase64string=="]}, :body => "{}"})
211
- @object.vclock.should == "somereallylongbase64string=="
212
- end
213
-
214
- it "should load links from the headers" do
215
- @object.load({:headers => {"content-type" => ["application/json"], "link" => ['</riak/bar>; rel="up"']}, :body => "{}"})
216
- @object.links.should have(1).item
217
- @object.links.first.url.should == "/riak/bar"
218
- @object.links.first.rel.should == "up"
219
- end
220
-
221
- it "should load the ETag from the headers" do
222
- @object.load({:headers => {"content-type" => ["application/json"], "etag" => ["32748nvas83572934"]}, :body => "{}"})
223
- @object.etag.should == "32748nvas83572934"
224
- end
225
-
226
- it "should load the modified date from the headers" do
227
- time = Time.now
228
- @object.load({:headers => {"content-type" => ["application/json"], "last-modified" => [time.httpdate]}, :body => "{}"})
229
- @object.last_modified.to_s.should == time.to_s # bah, times are not equivalent unless equal
230
- end
231
-
232
- it "should load meta information from the headers" do
233
- @object.load({:headers => {"content-type" => ["application/json"], "x-riak-meta-some-kind-of-robot" => ["for AWESOME"]}, :body => "{}"})
234
- @object.meta["some-kind-of-robot"].should == ["for AWESOME"]
235
- end
236
-
237
- it "should parse the location header into the key when present" do
238
- @object.load({:headers => {"content-type" => ["application/json"], "location" => ["/riak/foo/baz"]}})
239
- @object.key.should == "baz"
240
- end
241
-
242
- it "should parse and escape the location header into the key when present" do
243
- @object.load({:headers => {"content-type" => ["application/json"], "location" => ["/riak/foo/%5Bbaz%5D"]}})
244
- @object.key.should == "[baz]"
245
- end
246
-
247
- it "should be in conflict when the response code is 300 and the content-type is multipart/mixed" do
248
- @object.load({:headers => {"content-type" => ["multipart/mixed; boundary=foo"]}, :code => 300 })
249
- @object.should be_conflict
250
- end
251
-
252
- it "should unescape the key given in the location header" do
253
- @object.load({:headers => {"content-type" => ["application/json"], "location" => ["/riak/foo/baz%20"]}})
254
- @object.key.should == "baz "
255
- end
256
- end
257
174
 
258
175
  describe "instantiating new object from a map reduce operation" do
259
176
  before :each do
@@ -355,144 +272,21 @@ describe Riak::RObject do
355
272
  end
356
273
  end
357
274
 
358
- describe "extracting siblings" do
359
- before :each do
360
- @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"})
361
- end
362
-
363
- it "should extract the siblings" do
364
- @object.should have(2).siblings
365
- siblings = @object.siblings
366
- siblings[0].data.should == "bar"
367
- siblings[1].data.should == "baz"
368
- end
369
-
370
- it "should set the key on both siblings" do
371
- @object.siblings.should be_all {|s| s.key == "bar" }
372
- end
373
-
374
- it "should set the vclock on both siblings to the merged vclock" do
375
- @object.siblings.should be_all {|s| s.vclock == "merged" }
376
- end
377
- end
378
-
379
- describe "headers used for storing the object" do
380
- before :each do
381
- @object = Riak::RObject.new(@bucket, "bar")
382
- end
383
-
384
- it "should include the content type" do
385
- @object.content_type = "application/json"
386
- @object.store_headers["Content-Type"].should == "application/json"
387
- end
388
-
389
- it "should include the vclock when present" do
390
- @object.vclock = "123445678990"
391
- @object.store_headers["X-Riak-Vclock"].should == "123445678990"
392
- end
393
-
394
- it "should exclude the vclock when nil" do
395
- @object.vclock = nil
396
- @object.store_headers.should_not have_key("X-Riak-Vclock")
397
- end
398
-
399
- describe "when conditional PUTs are requested" do
400
- before :each do
401
- @object.prevent_stale_writes = true
402
- end
403
-
404
- it "should include an If-None-Match: * header" do
405
- @object.store_headers.should have_key("If-None-Match")
406
- @object.store_headers["If-None-Match"].should == "*"
407
- end
408
-
409
- it "should include an If-Match header with the etag when an etag is present" do
410
- @object.etag = "foobar"
411
- @object.store_headers.should have_key("If-Match")
412
- @object.store_headers["If-Match"].should == @object.etag
413
- end
414
- end
415
-
416
- describe "when links are defined" do
417
- before :each do
418
- @object.links << Riak::Link.new("/riak/foo/baz", "next")
419
- end
420
-
421
- it "should include a Link header with references to other objects" do
422
- @object.store_headers.should have_key("Link")
423
- @object.store_headers["Link"].should include('</riak/foo/baz>; riaktag="next"')
424
- end
425
-
426
- it "should exclude the 'up' link to the bucket from the header" do
427
- @object.links << Riak::Link.new("/riak/foo", "up")
428
- @object.store_headers.should have_key("Link")
429
- @object.store_headers["Link"].should_not include('riaktag="up"')
430
- end
431
-
432
- it "should not allow duplicate links" do
433
- @object.links << Riak::Link.new("/riak/foo/baz", "next")
434
- @object.links.length.should == 1
435
- end
436
- end
437
-
438
- it "should exclude the Link header when no links are present" do
439
- @object.links = Set.new
440
- @object.store_headers.should_not have_key("Link")
441
- end
442
-
443
- describe "when meta fields are present" do
444
- before :each do
445
- @object.meta = {"some-kind-of-robot" => true, "powers" => "for awesome", "cold-ones" => 10}
446
- end
447
-
448
- it "should include X-Riak-Meta-* headers for each meta key" do
449
- @object.store_headers.should have_key("X-Riak-Meta-some-kind-of-robot")
450
- @object.store_headers.should have_key("X-Riak-Meta-cold-ones")
451
- @object.store_headers.should have_key("X-Riak-Meta-powers")
452
- end
453
-
454
- it "should turn non-string meta values into strings" do
455
- @object.store_headers["X-Riak-Meta-some-kind-of-robot"].should == "true"
456
- @object.store_headers["X-Riak-Meta-cold-ones"].should == "10"
457
- end
458
-
459
- it "should leave string meta values unchanged in the header" do
460
- @object.store_headers["X-Riak-Meta-powers"].should == "for awesome"
461
- end
462
- end
463
- end
464
-
465
- describe "headers used for reloading the object" do
466
- before :each do
467
- @object = Riak::RObject.new(@bucket, "bar")
468
- end
469
-
470
- it "should be blank when the etag and last_modified properties are blank" do
471
- @object.etag.should be_blank
472
- @object.last_modified.should be_blank
473
- @object.reload_headers.should be_blank
474
- end
475
-
476
- it "should include the If-None-Match key when the etag is present" do
477
- @object.etag = "etag!"
478
- @object.reload_headers['If-None-Match'].should == "etag!"
479
- end
480
-
481
- it "should include the If-Modified-Since header when the last_modified time is present" do
482
- time = Time.now
483
- @object.last_modified = time
484
- @object.reload_headers['If-Modified-Since'].should == time.httpdate
485
- end
275
+ it "should not allow duplicate links" do
276
+ @object = Riak::RObject.new(@bucket, "foo")
277
+ @object.links << Riak::Link.new("/riak/foo/baz", "next")
278
+ @object.links << Riak::Link.new("/riak/foo/baz", "next")
279
+ @object.links.length.should == 1
486
280
  end
487
281
 
488
282
  describe "when storing the object normally" do
489
283
  before :each do
490
- @http = mock("HTTPBackend")
491
- @client.stub!(:http).and_return(@http)
284
+ @backend = mock("Backend")
285
+ @client.stub!(:backend).and_return(@backend)
492
286
  @object = Riak::RObject.new(@bucket)
493
287
  @object.content_type = "text/plain"
494
288
  @object.data = "This is some text."
495
- @headers = @object.store_headers
289
+ # @headers = @object.store_headers
496
290
  end
497
291
 
498
292
  it "should raise an error when the content_type is blank" do
@@ -500,124 +294,49 @@ describe Riak::RObject do
500
294
  lambda { @object.content_type = " "; @object.store }.should raise_error(ArgumentError)
501
295
  end
502
296
 
503
- describe "when the object has no key" do
504
- it "should issue a POST request to the bucket, and update the object properties (returning the body by default)" do
505
- @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})
506
- @object.store
507
- @object.key.should == "somereallylongstring"
508
- @object.vclock.should == "areallylonghashvalue"
509
- end
510
-
511
- it "should include persistence-tuning parameters in the query string" do
512
- @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})
513
- @object.store(:dw => 2)
514
- end
515
-
516
- it "should escape the bucket name" do
517
- @bucket.should_receive(:name).and_return("foo ")
518
- @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})
519
- @object.store
520
- end
521
- end
522
-
523
- describe "when the object has a key" do
524
- before :each do
525
- @object.key = "bar"
526
- end
527
-
528
- describe "when the content is of a known serializable type" do
529
- before :each do
530
- @object.content_type = "application/json"
531
- @headers = @object.store_headers
532
- end
533
-
534
- it "should not serialize content if #raw_data is used" do
535
- storing = @object.raw_data = "{this is probably invalid json}}"
536
- @http.should_receive(:put).with([200,204,300], "/riak/", "foo/bar", {:returnbody => true}, storing, @headers).and_return({:headers => {"x-riak-vclock" => ["areallylonghashvalue"]}, :code => 204})
537
- @object.should_not_receive(:serialize)
538
- @object.should_not_receive(:deserialize)
539
- @object.store
540
- @object.raw_data.should == storing
541
- end
542
- end
543
-
544
- it "should issue a PUT request to the bucket, and update the object properties (returning the body by default)" do
545
- @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})
546
- @object.store
547
- @object.key.should == "somereallylongstring"
548
- @object.vclock.should == "areallylonghashvalue"
549
- end
550
-
551
- it "should include persistence-tuning parameters in the query string" do
552
- @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})
553
- @object.store(:dw => 2)
554
- end
555
-
556
- it "should escape the bucket and key names" do
557
- @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})
558
- @bucket.instance_variable_set(:@name, "foo ")
559
- @object.key = "bar/baz"
560
- @object.store
561
- end
297
+ it "should pass along quorum parameters and returnbody to the backend" do
298
+ @backend.should_receive(:store_object).with(@object, false, 3, 2).and_return(true)
299
+ @object.store(:returnbody => false, :w => 3, :dw => 2)
562
300
  end
563
301
  end
564
302
 
565
303
  describe "when reloading the object" do
566
304
  before :each do
567
- @http = mock("HTTPBackend")
568
- @client.stub!(:http).and_return(@http)
305
+ @backend = mock("Backend")
306
+ @client.stub!(:backend).and_return(@backend)
569
307
  @object = Riak::RObject.new(@bucket, "bar")
570
308
  @object.vclock = "somereallylongstring"
571
- @object.stub!(:reload_headers).and_return({})
572
309
  end
573
310
 
574
311
  it "should return without requesting if the key is blank" do
575
312
  @object.key = nil
576
- @http.should_not_receive(:get)
313
+ @backend.should_not_receive(:reload_object)
577
314
  @object.reload
578
315
  end
579
316
 
580
317
  it "should return without requesting if the vclock is blank" do
581
318
  @object.vclock = nil
582
- @http.should_not_receive(:get)
583
- @object.reload
584
- end
585
-
586
- it "should make the request if the key is present and the :force option is given" do
587
- @http.should_receive(:get).and_return({:headers => {}, :code => 304})
588
- @object.reload :force => true
589
- end
590
-
591
- it "should pass along the reload_headers" do
592
- @headers = {"If-None-Match" => "etag"}
593
- @object.should_receive(:reload_headers).and_return(@headers)
594
- @http.should_receive(:get).with([200,304], "/riak/", "foo", "bar", {}, @headers).and_return({:code => 304})
319
+ @backend.should_not_receive(:reload_object)
595
320
  @object.reload
596
321
  end
597
322
 
598
- it "should return without modifying the object if the response is 304 Not Modified" do
599
- @http.should_receive(:get).and_return({:code => 304})
600
- @object.should_not_receive(:load)
323
+ it "should reload the object if the key is present" do
324
+ @backend.should_receive(:reload_object).with(@object, nil).and_return(@object)
601
325
  @object.reload
602
326
  end
603
327
 
604
- it "should raise an exception when the response code is not 200 or 304" do
605
- @http.should_receive(:get).and_raise(Riak::FailedRequest.new(:get, 200, 500, {}, ''))
606
- @object.should_not_receive(:load)
607
- lambda { @object.reload }.should raise_error(Riak::FailedRequest)
328
+ it "should pass along the requested R quorum" do
329
+ @backend.should_receive(:reload_object).with(@object, 2).and_return(@object)
330
+ @object.reload :r => 2
608
331
  end
609
-
610
- it "should include 300 in valid responses if the bucket has allow_mult set" do
611
- @object.bucket.should_receive(:allow_mult).and_return(true)
612
- @http.should_receive(:get).with([200,300,304], "/riak/", "foo", "bar", {}, {}).and_return({:code => 304})
613
- @object.reload
614
- end
615
-
616
- it "should escape the bucket and key names" do
617
- @bucket.should_receive(:name).and_return("some/deep/path")
618
- @object.key = "another/deep/path"
619
- @http.should_receive(:get).with([200,304], "/riak/", "some%2Fdeep%2Fpath", "another%2Fdeep%2Fpath", {}, {}).and_return({:code => 304})
620
- @object.reload
332
+
333
+ it "should disable matching conditions if the key is present and the :force option is given" do
334
+ @backend.should_receive(:reload_object) do |obj, _|
335
+ obj.etag.should be_nil
336
+ obj.last_modified.should be_nil
337
+ obj
338
+ end
339
+ @object.reload :force => true
621
340
  end
622
341
  end
623
342
 
@@ -627,70 +346,37 @@ describe Riak::RObject do
627
346
  @client.stub!(:http).and_return(@http)
628
347
  @client.stub!(:bucket).and_return(@bucket)
629
348
  @object = Riak::RObject.new(@bucket, "bar")
630
- @body = File.read(File.expand_path("#{File.dirname(__FILE__)}/../fixtures/multipart-with-body.txt"))
631
- end
632
-
633
- it "should issue a GET request to the given walk spec" do
634
- @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")
635
- @object.walk(nil,"next",true)
636
349
  end
637
350
 
638
- it "should parse the results into arrays of objects" do
639
- @http.stub!(:get).and_return(:headers => {"content-type" => ["multipart/mixed; boundary=5EiMOjuGavQ2IbXAqsJPLLfJNlA"]}, :body => @body)
640
- results = @object.walk(nil,"next",true)
641
- results.should be_kind_of(Array)
642
- results.first.should be_kind_of(Array)
643
- obj = results.first.first
644
- obj.should be_kind_of(Riak::RObject)
645
- obj.content_type.should == "text/plain"
646
- obj.key.should == "baz"
647
- obj.bucket.should == @bucket
648
- end
649
-
650
- it "should assign the bucket for newly parsed objects" do
651
- @http.stub!(:get).and_return(:headers => {"content-type" => ["multipart/mixed; boundary=5EiMOjuGavQ2IbXAqsJPLLfJNlA"]}, :body => @body)
652
- @client.should_receive(:bucket).with("foo", :keys => false).and_return(@bucket)
653
- @object.walk(nil,"next",true)
654
- end
655
-
656
- it "should escape the bucket, key and link specs" do
657
- @object.key = "bar/baz"
658
- @bucket.should_receive(:name).and_return("quin/quux")
659
- @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")
660
- @object.walk(:tag => "next/2", :keep => true)
351
+ it "should normalize the walk specs and submit the link-walking request to the HTTP backend" do
352
+ @http.should_receive(:link_walk).with(@object, [instance_of(Riak::WalkSpec)]).and_return([])
353
+ @object.walk(nil,"next",true).should == []
661
354
  end
662
355
  end
663
356
 
664
357
  describe "when deleting" do
665
358
  before :each do
666
- @http = mock("HTTPBackend")
667
- @client.stub!(:http).and_return(@http)
359
+ @backend = mock("Backend")
360
+ @client.stub!(:backend).and_return(@backend)
668
361
  @object = Riak::RObject.new(@bucket, "bar")
669
362
  end
670
363
 
671
364
  it "should make a DELETE request to the Riak server and freeze the object" do
672
- @http.should_receive(:delete).with([204,404], "/riak/", "foo", "bar", {},{}).and_return({:code => 204, :headers => {}})
365
+ @backend.should_receive(:delete_object).with(@bucket, "bar", nil)
673
366
  @object.delete
674
367
  @object.should be_frozen
675
368
  end
676
369
 
677
370
  it "should do nothing when the key is blank" do
678
- @http.should_not_receive(:delete)
371
+ @backend.should_not_receive(:delete_object)
679
372
  @object.key = nil
680
373
  @object.delete
681
374
  end
682
375
 
683
376
  it "should pass through a failed request exception" do
684
- @http.should_receive(:delete).and_raise(Riak::FailedRequest.new(:delete, [204,404], 500, {}, ""))
377
+ @backend.should_receive(:delete_object).and_raise(Riak::FailedRequest.new(:delete, [204,404], 500, {}, ""))
685
378
  lambda { @object.delete }.should raise_error(Riak::FailedRequest)
686
379
  end
687
-
688
- it "should escape the bucket and key names" do
689
- @object.key = "deep/path"
690
- @bucket.should_receive(:name).and_return("bucket spaces")
691
- @http.should_receive(:delete).with([204,404], "/riak/", "bucket%20spaces", "deep%2Fpath",{},{}).and_return({:code => 204, :headers => {}})
692
- @object.delete
693
- end
694
380
  end
695
381
 
696
382
  it "should not convert to link without a tag" do