ripple 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. data/.document +5 -0
  2. data/.gitignore +26 -0
  3. data/LICENSE +13 -0
  4. data/README.textile +126 -0
  5. data/RELEASE_NOTES.textile +24 -0
  6. data/Rakefile +61 -0
  7. data/VERSION +1 -0
  8. data/lib/riak.rb +45 -0
  9. data/lib/riak/bucket.rb +105 -0
  10. data/lib/riak/client.rb +138 -0
  11. data/lib/riak/client/curb_backend.rb +63 -0
  12. data/lib/riak/client/http_backend.rb +209 -0
  13. data/lib/riak/client/net_http_backend.rb +49 -0
  14. data/lib/riak/failed_request.rb +37 -0
  15. data/lib/riak/i18n.rb +15 -0
  16. data/lib/riak/invalid_response.rb +25 -0
  17. data/lib/riak/link.rb +54 -0
  18. data/lib/riak/locale/en.yml +37 -0
  19. data/lib/riak/map_reduce.rb +240 -0
  20. data/lib/riak/map_reduce_error.rb +20 -0
  21. data/lib/riak/robject.rb +234 -0
  22. data/lib/riak/util/headers.rb +44 -0
  23. data/lib/riak/util/multipart.rb +52 -0
  24. data/lib/riak/util/translation.rb +29 -0
  25. data/lib/riak/walk_spec.rb +113 -0
  26. data/lib/ripple.rb +48 -0
  27. data/lib/ripple/core_ext/casting.rb +96 -0
  28. data/lib/ripple/document.rb +60 -0
  29. data/lib/ripple/document/attribute_methods.rb +111 -0
  30. data/lib/ripple/document/attribute_methods/dirty.rb +52 -0
  31. data/lib/ripple/document/attribute_methods/query.rb +49 -0
  32. data/lib/ripple/document/attribute_methods/read.rb +38 -0
  33. data/lib/ripple/document/attribute_methods/write.rb +36 -0
  34. data/lib/ripple/document/bucket_access.rb +38 -0
  35. data/lib/ripple/document/finders.rb +84 -0
  36. data/lib/ripple/document/persistence.rb +93 -0
  37. data/lib/ripple/document/persistence/callbacks.rb +48 -0
  38. data/lib/ripple/document/properties.rb +85 -0
  39. data/lib/ripple/document/validations.rb +44 -0
  40. data/lib/ripple/embedded_document.rb +38 -0
  41. data/lib/ripple/embedded_document/persistence.rb +46 -0
  42. data/lib/ripple/i18n.rb +15 -0
  43. data/lib/ripple/locale/en.yml +16 -0
  44. data/lib/ripple/property_type_mismatch.rb +23 -0
  45. data/lib/ripple/translation.rb +24 -0
  46. data/ripple.gemspec +159 -0
  47. data/spec/fixtures/cat.jpg +0 -0
  48. data/spec/fixtures/multipart-blank.txt +7 -0
  49. data/spec/fixtures/multipart-with-body.txt +16 -0
  50. data/spec/riak/bucket_spec.rb +141 -0
  51. data/spec/riak/client_spec.rb +169 -0
  52. data/spec/riak/curb_backend_spec.rb +50 -0
  53. data/spec/riak/headers_spec.rb +34 -0
  54. data/spec/riak/http_backend_spec.rb +136 -0
  55. data/spec/riak/link_spec.rb +50 -0
  56. data/spec/riak/map_reduce_spec.rb +347 -0
  57. data/spec/riak/multipart_spec.rb +36 -0
  58. data/spec/riak/net_http_backend_spec.rb +28 -0
  59. data/spec/riak/object_spec.rb +444 -0
  60. data/spec/riak/walk_spec_spec.rb +208 -0
  61. data/spec/ripple/attribute_methods_spec.rb +149 -0
  62. data/spec/ripple/bucket_access_spec.rb +48 -0
  63. data/spec/ripple/callbacks_spec.rb +86 -0
  64. data/spec/ripple/document_spec.rb +35 -0
  65. data/spec/ripple/embedded_document_spec.rb +52 -0
  66. data/spec/ripple/finders_spec.rb +146 -0
  67. data/spec/ripple/persistence_spec.rb +89 -0
  68. data/spec/ripple/properties_spec.rb +195 -0
  69. data/spec/ripple/ripple_spec.rb +43 -0
  70. data/spec/ripple/validations_spec.rb +64 -0
  71. data/spec/spec.opts +1 -0
  72. data/spec/spec_helper.rb +32 -0
  73. data/spec/support/http_backend_implementation_examples.rb +215 -0
  74. data/spec/support/mock_server.rb +58 -0
  75. metadata +221 -0
@@ -0,0 +1,149 @@
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__)
15
+
16
+ describe Ripple::Document::AttributeMethods do
17
+ before :all do
18
+ class Widget
19
+ include Ripple::Document
20
+ property :size, Integer
21
+ property :name, String, :default => "widget"
22
+ end
23
+ end
24
+
25
+ before :each do
26
+ @widget = Widget.new
27
+ end
28
+
29
+ describe "object key" do
30
+ it "should provide access to the key" do
31
+ @widget.should respond_to(:key)
32
+ @widget.key.should be_nil
33
+ end
34
+
35
+ it "should provide a mutator for the key" do
36
+ @widget.should respond_to(:key=)
37
+ @widget.key = "cog"
38
+ @widget.key.should == "cog"
39
+ end
40
+
41
+ it "should accept the key in mass assignment" do
42
+ @widget.attributes = {:key => "cog"}
43
+ @widget.key.should == "cog"
44
+ end
45
+
46
+ end
47
+
48
+ describe "accessors" do
49
+ it "should be defined for defined properties" do
50
+ @widget.should respond_to(:size)
51
+ @widget.should respond_to(:name)
52
+ end
53
+
54
+ it "should return nil if no default is defined on the property" do
55
+ @widget.size.should be_nil
56
+ end
57
+
58
+ it "should return the property default if defined and not set" do
59
+ @widget.name.should == "widget"
60
+ end
61
+ end
62
+
63
+ describe "mutators" do
64
+ it "should have mutators for defined properties" do
65
+ @widget.should respond_to(:size=)
66
+ @widget.should respond_to(:name=)
67
+ end
68
+
69
+ it "should assign the value of the attribute" do
70
+ @widget.size = 10
71
+ @widget.size.should == 10
72
+ end
73
+
74
+ it "should type cast assigned values automatically" do
75
+ @widget.name = :so_what
76
+ @widget.name.should == "so_what"
77
+ end
78
+
79
+ it "should raise an error when assigning a bad value" do
80
+ lambda { @widget.size = true }.should raise_error(Ripple::PropertyTypeMismatch)
81
+ end
82
+ end
83
+
84
+ describe "query methods" do
85
+ it "should be defined for defined properties" do
86
+ @widget.should respond_to(:size?)
87
+ @widget.should respond_to(:name?)
88
+ end
89
+
90
+ it "should be false when the attribute is nil" do
91
+ @widget.size.should be_nil
92
+ @widget.size?.should be_false
93
+ end
94
+
95
+ it "should be true when the attribute has a value present" do
96
+ @widget.size = 10
97
+ @widget.size?.should be_true
98
+ end
99
+
100
+ it "should be false for 0 values" do
101
+ @widget.size = 0
102
+ @widget.size?.should be_false
103
+ end
104
+
105
+ it "should be false for empty values" do
106
+ @widget.name = ""
107
+ @widget.name?.should be_false
108
+ end
109
+ end
110
+
111
+ it "should track changes to attributes" do
112
+ @widget.name = "foobar"
113
+ @widget.changed?.should be_true
114
+ @widget.name_changed?.should be_true
115
+ @widget.name_change.should == ["widget", "foobar"]
116
+ @widget.changes.should == {"name" => ["widget", "foobar"]}
117
+ end
118
+
119
+
120
+ it "should refresh the attribute methods when adding a new property" do
121
+ Widget.should_receive(:undefine_attribute_methods)
122
+ Widget.property :start_date, Date
123
+ Widget.properties.delete(:start_date) # cleanup
124
+ end
125
+
126
+ it "should provide a hash representation of all of the attributes" do
127
+ @widget.attributes.should == {"name" => "widget", "size" => nil}
128
+ end
129
+
130
+ it "should load attributes from mass assignment" do
131
+ @widget.attributes = {"name" => "Riak", "size" => 100000 }
132
+ @widget.name.should == "Riak"
133
+ @widget.size.should == 100000
134
+ end
135
+
136
+ it "should assign attributes on initialization" do
137
+ @widget = Widget.new(:name => "Riak")
138
+ @widget.name.should == "Riak"
139
+ end
140
+
141
+ it "should have no changed attributes after initialization" do
142
+ @widget = Widget.new(:name => "Riak")
143
+ @widget.changes.should be_blank
144
+ end
145
+
146
+ after :all do
147
+ Object.send(:remove_const, :Widget)
148
+ end
149
+ end
@@ -0,0 +1,48 @@
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 Ripple::Document::BucketAccess do
17
+ before :all do
18
+ class Invoice; include Ripple::Document; end
19
+ class LateInvoice < Invoice; end
20
+ class PaidInvoice < Invoice; self.bucket_name = "paid"; end
21
+ end
22
+
23
+ it "should use the plural model name as the bucket name" do
24
+ Invoice.bucket_name.should == "invoices"
25
+ end
26
+
27
+ it "should use the parent bucket name by default (SBI)" do
28
+ LateInvoice.bucket_name.should == "invoices"
29
+ end
30
+
31
+ it "should allow a class to set the bucket name" do
32
+ PaidInvoice.bucket_name.should == "paid"
33
+ end
34
+
35
+ it "should allow access to the bucket" do
36
+ bucket = Riak::Bucket.new(Ripple.client, "invoices")
37
+ Ripple.client.should_receive(:[]).with("invoices", {:keys => false}).and_return(bucket)
38
+ Invoice.bucket.should == bucket
39
+ end
40
+
41
+ after :all do
42
+ Object.module_eval do
43
+ remove_const(:PaidInvoice)
44
+ remove_const(:LateInvoice)
45
+ remove_const(:Invoice)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,86 @@
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__)
15
+
16
+ describe Ripple::Document::Persistence::Callbacks do
17
+ before :all do
18
+ class Box; include Ripple::Document; property :shape, String end
19
+ end
20
+
21
+ it "should add create, update, save, and destroy callback declarations" do
22
+ [:save, :create, :update, :destroy].each do |event|
23
+ Box.private_instance_methods.should include("_run_#{event}_callbacks")
24
+ [:before, :after, :around].each do |time|
25
+ Box.should respond_to("#{time}_#{event}")
26
+ end
27
+ end
28
+ end
29
+
30
+ describe "invoking callbacks" do
31
+ before :each do
32
+ response = {:headers => {"content-type" => ["application/json"]}, :body => "{}"}
33
+ @client = Ripple.client
34
+ @http = mock("HTTP Backend", :get => response, :put => response, :post => response, :delete => response)
35
+ @client.stub!(:http).and_return(@http)
36
+ $pinger = mock("callback verifier")
37
+ end
38
+
39
+ it "should call save callbacks on save" do
40
+ Box.before_save { $pinger.ping }
41
+ Box.after_save { $pinger.ping }
42
+ Box.around_save(lambda { $pinger.ping })
43
+ $pinger.should_receive(:ping).exactly(3).times
44
+ @box = Box.new
45
+ @box.save
46
+ end
47
+
48
+ it "should call create callbacks on save when the document is new" do
49
+ Box.before_create { $pinger.ping }
50
+ Box.after_create { $pinger.ping }
51
+ Box.around_create(lambda { $pinger.ping })
52
+ $pinger.should_receive(:ping).exactly(3).times
53
+ @box = Box.new
54
+ @box.save
55
+ end
56
+
57
+ it "should call update callbacks on save when the document is not new" do
58
+ Box.before_update { $pinger.ping }
59
+ Box.after_update { $pinger.ping }
60
+ Box.around_update(lambda { $pinger.ping })
61
+ $pinger.should_receive(:ping).exactly(3).times
62
+ @box = Box.new
63
+ @box.stub!(:new?).and_return(false)
64
+ @box.save
65
+ end
66
+
67
+ it "should call destroy callbacks" do
68
+ Box.before_destroy { $pinger.ping }
69
+ Box.after_destroy { $pinger.ping }
70
+ Box.around_destroy(lambda { $pinger.ping })
71
+ $pinger.should_receive(:ping).exactly(3).times
72
+ @box = Box.new
73
+ @box.destroy
74
+ end
75
+
76
+ after :each do
77
+ [:save, :create, :update, :destroy].each do |type|
78
+ Box.reset_callbacks(type)
79
+ end
80
+ end
81
+ end
82
+
83
+ after :all do
84
+ Object.send(:remove_const, :Box)
85
+ end
86
+ end
@@ -0,0 +1,35 @@
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 Ripple::Document do
17
+ before :all do
18
+ class Page; include Ripple::Document; end
19
+ end
20
+
21
+ it "should add bucket access methods to classes when included" do
22
+ Page.metaclass.included_modules.should include(Ripple::Document::BucketAccess)
23
+ Page.should respond_to(:bucket_name)
24
+ Page.should respond_to(:bucket)
25
+ Page.should respond_to(:bucket_name=)
26
+ end
27
+
28
+ it "should not be embeddable" do
29
+ Page.should_not be_embeddable
30
+ end
31
+
32
+ after :all do
33
+ Object.module_eval { remove_const(:Page) }
34
+ end
35
+ end
@@ -0,0 +1,52 @@
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__)
15
+
16
+ describe Ripple::EmbeddedDocument do
17
+ before :all do
18
+ class Address; include Ripple::EmbeddedDocument; end
19
+ end
20
+
21
+ it "should have a model name when included" do
22
+ Address.should respond_to(:model_name)
23
+ Address.model_name.should be_kind_of(ActiveModel::Name)
24
+ end
25
+
26
+ it "should be embeddable" do
27
+ Address.should be_embeddable
28
+ end
29
+
30
+ describe "persistence" do
31
+ before :each do
32
+ @root = mock("root document")
33
+ @root.stub!(:new?).and_return(true)
34
+ @addr = Address.new
35
+ @addr._root_document = @root
36
+ end
37
+
38
+ it "should delegate new? to the root document" do
39
+ @root.should_receive(:new?).and_return(true)
40
+ @addr.should be_new
41
+ end
42
+
43
+ it "should delegate save to the root document" do
44
+ @root.should_receive(:save).and_return(true)
45
+ @addr.save.should be_true
46
+ end
47
+ end
48
+
49
+ after :all do
50
+ Object.send(:remove_const, :Address)
51
+ end
52
+ end
@@ -0,0 +1,146 @@
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__)
15
+
16
+ describe Ripple::Document::Finders do
17
+ before :all do
18
+ class Box
19
+ include Ripple::Document
20
+ property :shape, String
21
+ end
22
+ end
23
+
24
+ before :each do
25
+ @http = mock("HTTP Backend")
26
+ @client = Ripple.client
27
+ @client.stub!(:http).and_return(@http)
28
+ @bucket = Riak::Bucket.new(@client, "boxes")
29
+ @client.stub!(:[]).and_return(@bucket)
30
+ end
31
+
32
+ it "should return an empty array if no keys are passed to find" do
33
+ Box.find().should == []
34
+ end
35
+
36
+ describe "finding single documents" do
37
+ it "should find a single document by key and assign its attributes" do
38
+ @http.should_receive(:get).with(200, "/raw/", "boxes", "square", {}, {}).and_return({:code => 200, :headers => {"content-type" => ["application/json"]}, :body => '{"shape":"square"}'})
39
+ box = Box.find("square")
40
+ box.should be_kind_of(Box)
41
+ box.shape.should == "square"
42
+ box.key.should == "square"
43
+ box.instance_variable_get(:@robject).should_not be_nil
44
+ box.should_not be_new_record
45
+ end
46
+
47
+ it "should return nil when no object exists at that key" do
48
+ @http.should_receive(:get).with(200, "/raw/", "boxes", "square", {}, {}).and_raise(Riak::FailedRequest.new(:get, 200, 404, {}, "404 not found"))
49
+ box = Box.find("square")
50
+ box.should be_nil
51
+ end
52
+
53
+ it "should re-raise the failed request exception if not a 404" do
54
+ @http.should_receive(:get).with(200, "/raw/", "boxes", "square", {}, {}).and_raise(Riak::FailedRequest.new(:get, 200, 500, {}, "500 internal server error"))
55
+ lambda { Box.find("square") }.should raise_error(Riak::FailedRequest)
56
+ end
57
+ end
58
+
59
+ describe "finding multiple documents" do
60
+ it "should find multiple documents by the keys" do
61
+ @http.should_receive(:get).with(200, "/raw/", "boxes", "square", {}, {}).and_return({:code => 200, :headers => {"content-type" => ["application/json"]}, :body => '{"shape":"square"}'})
62
+ @http.should_receive(:get).with(200, "/raw/", "boxes", "rectangle", {}, {}).and_return({:code => 200, :headers => {"content-type" => ["application/json"]}, :body => '{"shape":"rectangle"}'})
63
+ boxes = Box.find("square", "rectangle")
64
+ boxes.should have(2).items
65
+ boxes.first.shape.should == "square"
66
+ boxes.last.shape.should == "rectangle"
67
+ end
68
+
69
+ it "should return nil for documents that no longer exist" do
70
+ @http.should_receive(:get).with(200, "/raw/", "boxes", "square", {}, {}).and_return({:code => 200, :headers => {"content-type" => ["application/json"]}, :body => '{"shape":"square"}'})
71
+ @http.should_receive(:get).with(200, "/raw/", "boxes", "rectangle", {}, {}).and_raise(Riak::FailedRequest.new(:get, 200, 404, {}, "404 not found"))
72
+ boxes = Box.find("square", "rectangle")
73
+ boxes.should have(2).items
74
+ boxes.first.shape.should == "square"
75
+ boxes.last.should be_nil
76
+ end
77
+ end
78
+
79
+ describe "finding all documents in the bucket" do
80
+ it "should load all objects in the bucket" do
81
+ @bucket.should_receive(:keys).and_return(["square", "rectangle"])
82
+ @http.should_receive(:get).with(200, "/raw/", "boxes", "square", {}, {}).and_return({:code => 200, :headers => {"content-type" => ["application/json"]}, :body => '{"shape":"square"}'})
83
+ @http.should_receive(:get).with(200, "/raw/", "boxes", "rectangle", {}, {}).and_return({:code => 200, :headers => {"content-type" => ["application/json"]}, :body => '{"shape":"rectangle"}'})
84
+ boxes = Box.all
85
+ boxes.should have(2).items
86
+ boxes.first.shape.should == "square"
87
+ boxes.last.shape.should == "rectangle"
88
+ end
89
+
90
+ it "should exclude objects that are not found" do
91
+ @bucket.should_receive(:keys).and_return(["square", "rectangle"])
92
+ @http.should_receive(:get).with(200, "/raw/", "boxes", "square", {}, {}).and_return({:code => 200, :headers => {"content-type" => ["application/json"]}, :body => '{"shape":"square"}'})
93
+ @http.should_receive(:get).with(200, "/raw/", "boxes", "rectangle", {}, {}).and_raise(Riak::FailedRequest.new(:get, 200, 404, {}, "404 not found"))
94
+ boxes = Box.all
95
+ boxes.should have(1).item
96
+ boxes.first.shape.should == "square"
97
+ end
98
+
99
+ it "should yield found objects to the passed block and return an empty array" do
100
+ @bucket.should_receive(:keys).and_yield("square").and_yield("rectangle")
101
+ @http.should_receive(:get).with(200, "/raw/", "boxes", "square", {}, {}).and_return({:code => 200, :headers => {"content-type" => ["application/json"]}, :body => '{"shape":"square"}'})
102
+ @http.should_receive(:get).with(200, "/raw/", "boxes", "rectangle", {}, {}).and_return({:code => 200, :headers => {"content-type" => ["application/json"]}, :body => '{"shape":"rectangle"}'})
103
+ @block = mock()
104
+ @block.should_receive(:ping).twice
105
+ Box.all do |box|
106
+ @block.ping
107
+ ["square", "rectangle"].should include(box.shape)
108
+ end.should == []
109
+ end
110
+
111
+ it "should yield found objects to the passed block, excluding missing objects, and return an empty array" do
112
+ @bucket.should_receive(:keys).and_yield("square").and_yield("rectangle")
113
+ @http.should_receive(:get).with(200, "/raw/", "boxes", "square", {}, {}).and_return({:code => 200, :headers => {"content-type" => ["application/json"]}, :body => '{"shape":"square"}'})
114
+ @http.should_receive(:get).with(200, "/raw/", "boxes", "rectangle", {}, {}).and_raise(Riak::FailedRequest.new(:get, 200, 404, {}, "404 not found"))
115
+ @block = mock()
116
+ @block.should_receive(:ping).once
117
+ Box.all do |box|
118
+ @block.ping
119
+ ["square", "rectangle"].should include(box.shape)
120
+ end.should == []
121
+ end
122
+ end
123
+
124
+ describe "single-bucket inheritance" do
125
+ before :all do
126
+ class CardboardBox < Box; end
127
+ end
128
+
129
+ it "should instantiate as the proper type if defined in the document" do
130
+ @http.should_receive(:get).with(200, "/raw/", "boxes", "square", {}, {}).and_return({:code => 200, :headers => {"content-type" => ["application/json"]}, :body => '{"shape":"square"}'})
131
+ @http.should_receive(:get).with(200, "/raw/", "boxes", "rectangle", {}, {}).and_return({:code => 200, :headers => {"content-type" => ["application/json"]}, :body => '{"shape":"rectangle", "_type":"CardboardBox"}'})
132
+ boxes = Box.find("square", "rectangle")
133
+ boxes.should have(2).items
134
+ boxes.first.should_not be_kind_of(CardboardBox)
135
+ boxes.last.should be_kind_of(CardboardBox)
136
+ end
137
+
138
+ after :all do
139
+ Object.send(:remove_const, :CardboardBox)
140
+ end
141
+ end
142
+
143
+ after :all do
144
+ Object.send(:remove_const, :Box)
145
+ end
146
+ end