ripple 0.5.0

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.
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