couchobject 0.5.0 → 0.6.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 (63) hide show
  1. data/History.txt +10 -0
  2. data/Manifest.txt +30 -6
  3. data/README.txt +580 -42
  4. data/TODO +2 -2
  5. data/config/hoe.rb +1 -1
  6. data/lib/couch_object.rb +7 -2
  7. data/lib/couch_object/database.rb +19 -34
  8. data/lib/couch_object/document.rb +13 -6
  9. data/lib/couch_object/error_classes.rb +110 -0
  10. data/lib/couch_object/persistable.rb +954 -36
  11. data/lib/couch_object/persistable/has_many_relations_array.rb +91 -0
  12. data/lib/couch_object/persistable/meta_classes.rb +568 -0
  13. data/lib/couch_object/persistable/overloaded_methods.rb +209 -0
  14. data/lib/couch_object/server.rb +1 -1
  15. data/lib/couch_object/utils.rb +44 -0
  16. data/lib/couch_object/version.rb +1 -1
  17. data/lib/couch_object/view.rb +129 -6
  18. data/script/console +0 -0
  19. data/script/destroy +0 -0
  20. data/script/generate +0 -0
  21. data/script/txt2html +0 -0
  22. data/spec/database_spec.rb +23 -31
  23. data/spec/database_spec.rb.orig +173 -0
  24. data/spec/document_spec.rb +21 -3
  25. data/spec/integration/database_integration_spec.rb +46 -15
  26. data/spec/integration/integration_helper.rb +3 -3
  27. data/spec/persistable/callback.rb +44 -0
  28. data/spec/persistable/callback_spec.rb +44 -0
  29. data/spec/persistable/cloning.rb +77 -0
  30. data/spec/persistable/cloning_spec.rb +77 -0
  31. data/spec/persistable/comparing_objects.rb +350 -0
  32. data/spec/persistable/comparing_objects_spec.rb +350 -0
  33. data/spec/persistable/deleting.rb +113 -0
  34. data/spec/persistable/deleting_spec.rb +113 -0
  35. data/spec/persistable/error_messages.rb +32 -0
  36. data/spec/persistable/error_messages_spec.rb +32 -0
  37. data/spec/persistable/loading.rb +339 -0
  38. data/spec/persistable/loading_spec.rb +339 -0
  39. data/spec/persistable/new_methods.rb +70 -0
  40. data/spec/persistable/new_methods_spec.rb +70 -0
  41. data/spec/persistable/persistable_helper.rb +194 -0
  42. data/spec/persistable/relations.rb +470 -0
  43. data/spec/persistable/relations_spec.rb +470 -0
  44. data/spec/persistable/saving.rb +137 -0
  45. data/spec/persistable/saving_spec.rb +137 -0
  46. data/spec/persistable/setting_storage_location.rb +65 -0
  47. data/spec/persistable/setting_storage_location_spec.rb +65 -0
  48. data/spec/persistable/timestamps.rb +76 -0
  49. data/spec/persistable/timestamps_spec.rb +76 -0
  50. data/spec/persistable/unsaved_changes.rb +211 -0
  51. data/spec/persistable/unsaved_changes_spec.rb +211 -0
  52. data/spec/server_spec.rb +5 -5
  53. data/spec/utils_spec.rb +60 -0
  54. data/spec/view_spec.rb +40 -7
  55. data/website/index.html +22 -7
  56. data/website/index.txt +13 -5
  57. metadata +93 -61
  58. data/bin/couch_ruby_view_requestor +0 -81
  59. data/lib/couch_object/model.rb +0 -5
  60. data/lib/couch_object/proc_condition.rb +0 -14
  61. data/spec/model_spec.rb +0 -5
  62. data/spec/persistable_spec.rb +0 -91
  63. data/spec/proc_condition_spec.rb +0 -26
@@ -0,0 +1,113 @@
1
+ require File.dirname(__FILE__) + '/persistable_helper.rb'
2
+
3
+ describe CouchObject::Persistable, "deleting objects" do
4
+ before(:each) do
5
+ @building = ApartmentBuilding.new
6
+ @apartment = Apartment.new
7
+
8
+ @db = mock("mock db")
9
+ CouchObject::Database.stub!(:open).and_return(@db)
10
+
11
+ @content_apartment = %{
12
+ {
13
+ "_id":"123BAC",
14
+ "_rev":"946B7D1C",
15
+ "class":"Apartment"
16
+ }
17
+ }
18
+ CouchObject::Database.stub!(:open).and_return(@db)
19
+
20
+ content = HTTPResponse.new(JSON.unparse({
21
+ "_id" => "123BAC",
22
+ "_rev" => "946B7D1C",
23
+ }))
24
+
25
+ @document_response = CouchObject::Response.new(content)
26
+ end
27
+
28
+ it "should be able to delete itself." do
29
+ response = HTTPResponse.new(@content_apartment)
30
+ @db.should_receive(:get).with("123BAC").and_return(response)
31
+ @db.should_receive(:delete).with("123BAC", "946B7D1C")
32
+
33
+ building = Apartment.get_by_id("123BAC", "foo")
34
+ building.delete.should == true
35
+ end
36
+
37
+ it "deleting an unsaved document should be possible" do
38
+ @building.delete.should == true
39
+ end
40
+
41
+ it "the deleted object should be reset" do
42
+ response = HTTPResponse.new(@content_apartment)
43
+ @db.should_receive(:get).with("123BAC").and_return(response)
44
+ @db.should_receive(:delete).with("123BAC", "946B7D1C")
45
+
46
+ building = Apartment.get_by_id("123BAC", "foo")
47
+ building.delete.should == true
48
+ building.id.should == nil
49
+ building.revision.should == nil
50
+ end
51
+
52
+ it "when deleting an object that has relations, the relations should also be deleted" do
53
+ # Receives open seven times. First to store the documents, then to delete.
54
+ CouchObject::Database.should_receive(:open).exactly(6).and_return(@db)
55
+
56
+ # Store the new documents
57
+ @db.should_receive(:post).exactly(3).and_return(@document_response)
58
+
59
+ # Delete all the objects
60
+ @db.should_receive(:delete).twice.with("123BAC", "946B7D1C")
61
+ @db.should_receive(:delete).with("new_ID_foo", "new_Revision_foo")
62
+
63
+ @view_content_no_rows = %{
64
+ {"total_rows":3,"rows":[]}
65
+ }
66
+ response = HTTPResponse.new(@view_content_no_rows)
67
+
68
+ another_apartment = @apartment.clone
69
+ @apartment.new?.should == true
70
+ another_apartment.new?.should == true
71
+
72
+ @building.apartments << @apartment << another_apartment
73
+ @building.save
74
+
75
+ @apartment.new?.should == false
76
+ another_apartment.new?.should == false
77
+
78
+ # They have been set up identically, so we have to cheat and change
79
+ # the ID of one of the apartments
80
+ another_apartment.instance_variable_set("@id", "new_ID_foo")
81
+ another_apartment.instance_variable_set("@revision", "new_Revision_foo")
82
+
83
+ @building.delete
84
+ @building.id.should == nil
85
+ @building.revision.should == nil
86
+
87
+ @apartment.id.should == nil
88
+ @apartment.revision.should == nil
89
+
90
+ another_apartment.id.should == nil
91
+ another_apartment.revision.should == nil
92
+ end
93
+
94
+ it "deleting an object that is in a relation should notify the owner" do
95
+ @building.apartments << @apartment
96
+ @apartment.apartment_building.should_not == nil
97
+ @building.apartments.size.should == 1
98
+ @apartment.delete
99
+ @building.apartments.size.should == 0
100
+ @apartment.apartment_building.should == nil
101
+ end
102
+
103
+ it "should be able to delete has_many relations" do
104
+ @building.apartments << @apartment
105
+ @apartment.apartment_building.should_not == nil
106
+ @building.apartments.size.should == 1
107
+ @building.apartments.delete(@apartment)
108
+ @building.apartments.size.should == 0
109
+ @apartment.apartment_building.should == nil
110
+
111
+ end
112
+ end
113
+
@@ -0,0 +1,113 @@
1
+ require File.dirname(__FILE__) + '/persistable_helper.rb'
2
+
3
+ describe CouchObject::Persistable, "deleting objects" do
4
+ before(:each) do
5
+ @building = ApartmentBuilding.new
6
+ @apartment = Apartment.new
7
+
8
+ @db = mock("mock db")
9
+ CouchObject::Database.stub!(:open).and_return(@db)
10
+
11
+ @content_apartment = %{
12
+ {
13
+ "_id":"123BAC",
14
+ "_rev":"946B7D1C",
15
+ "class":"Apartment"
16
+ }
17
+ }
18
+ CouchObject::Database.stub!(:open).and_return(@db)
19
+
20
+ content = HTTPResponse.new(JSON.unparse({
21
+ "_id" => "123BAC",
22
+ "_rev" => "946B7D1C",
23
+ }))
24
+
25
+ @document_response = CouchObject::Response.new(content)
26
+ end
27
+
28
+ it "should be able to delete itself." do
29
+ response = HTTPResponse.new(@content_apartment)
30
+ @db.should_receive(:get).with("123BAC").and_return(response)
31
+ @db.should_receive(:delete).with("123BAC", "946B7D1C")
32
+
33
+ building = Apartment.get_by_id("123BAC", "foo")
34
+ building.delete.should == true
35
+ end
36
+
37
+ it "deleting an unsaved document should be possible" do
38
+ @building.delete.should == true
39
+ end
40
+
41
+ it "the deleted object should be reset" do
42
+ response = HTTPResponse.new(@content_apartment)
43
+ @db.should_receive(:get).with("123BAC").and_return(response)
44
+ @db.should_receive(:delete).with("123BAC", "946B7D1C")
45
+
46
+ building = Apartment.get_by_id("123BAC", "foo")
47
+ building.delete.should == true
48
+ building.id.should == nil
49
+ building.revision.should == nil
50
+ end
51
+
52
+ it "when deleting an object that has relations, the relations should also be deleted" do
53
+ # Receives open seven times. First to store the documents, then to delete.
54
+ CouchObject::Database.should_receive(:open).exactly(6).and_return(@db)
55
+
56
+ # Store the new documents
57
+ @db.should_receive(:post).exactly(3).and_return(@document_response)
58
+
59
+ # Delete all the objects
60
+ @db.should_receive(:delete).twice.with("123BAC", "946B7D1C")
61
+ @db.should_receive(:delete).with("new_ID_foo", "new_Revision_foo")
62
+
63
+ @view_content_no_rows = %{
64
+ {"total_rows":3,"rows":[]}
65
+ }
66
+ response = HTTPResponse.new(@view_content_no_rows)
67
+
68
+ another_apartment = @apartment.clone
69
+ @apartment.new?.should == true
70
+ another_apartment.new?.should == true
71
+
72
+ @building.apartments << @apartment << another_apartment
73
+ @building.save
74
+
75
+ @apartment.new?.should == false
76
+ another_apartment.new?.should == false
77
+
78
+ # They have been set up identically, so we have to cheat and change
79
+ # the ID of one of the apartments
80
+ another_apartment.instance_variable_set("@id", "new_ID_foo")
81
+ another_apartment.instance_variable_set("@revision", "new_Revision_foo")
82
+
83
+ @building.delete
84
+ @building.id.should == nil
85
+ @building.revision.should == nil
86
+
87
+ @apartment.id.should == nil
88
+ @apartment.revision.should == nil
89
+
90
+ another_apartment.id.should == nil
91
+ another_apartment.revision.should == nil
92
+ end
93
+
94
+ it "deleting an object that is in a relation should notify the owner" do
95
+ @building.apartments << @apartment
96
+ @apartment.apartment_building.should_not == nil
97
+ @building.apartments.size.should == 1
98
+ @apartment.delete
99
+ @building.apartments.size.should == 0
100
+ @apartment.apartment_building.should == nil
101
+ end
102
+
103
+ it "should be able to delete has_many relations" do
104
+ @building.apartments << @apartment
105
+ @apartment.apartment_building.should_not == nil
106
+ @building.apartments.size.should == 1
107
+ @building.apartments.delete(@apartment)
108
+ @building.apartments.size.should == 0
109
+ @apartment.apartment_building.should == nil
110
+
111
+ end
112
+ end
113
+
@@ -0,0 +1,32 @@
1
+ require File.dirname(__FILE__) + '/persistable_helper.rb'
2
+
3
+ # This tests are bullshit really...
4
+ # Check if the errors exist and call their message parameters too...
5
+ # gives us good rcov coverage...
6
+
7
+ describe CouchObject::Errors, "errormessages: " do
8
+ it "CantCompareSize" do
9
+ error = CouchObject::Errors::CantCompareSize.new
10
+ error.message.should == "Can't compare the size of two objects unless at least one of them has been saved and both have timestamps."
11
+ end
12
+
13
+ it "CantCompareSize" do
14
+ error = CouchObject::Errors::NoDatabaseLocationSet.new
15
+ error.message.should == "Unless the document has previously been saved you need to supply the full URI to the CouchDB server where the document is to be saved."
16
+ end
17
+
18
+ it "HasManyAssociationError" do
19
+ error = CouchObject::Errors::HasManyAssociationError.new
20
+ error.message
21
+ end
22
+ it "BelongsToAssociationError" do
23
+ error = CouchObject::Errors::BelongsToAssociationError.new
24
+ error.message
25
+ end
26
+ it "MissingView" do
27
+ error = CouchObject::Errors::MissingView.new
28
+ error.message
29
+ end
30
+
31
+ end
32
+
@@ -0,0 +1,32 @@
1
+ require File.dirname(__FILE__) + '/persistable_helper.rb'
2
+
3
+ # This tests are bullshit really...
4
+ # Check if the errors exist and call their message parameters too...
5
+ # gives us good rcov coverage...
6
+
7
+ describe CouchObject::Errors, "errormessages: " do
8
+ it "CantCompareSize" do
9
+ error = CouchObject::Errors::CantCompareSize.new
10
+ error.message.should == "Can't compare the size of two objects unless at least one of them has been saved and both have timestamps."
11
+ end
12
+
13
+ it "CantCompareSize" do
14
+ error = CouchObject::Errors::NoDatabaseLocationSet.new
15
+ error.message.should == "Unless the document has previously been saved you need to supply the full URI to the CouchDB server where the document is to be saved."
16
+ end
17
+
18
+ it "HasManyAssociationError" do
19
+ error = CouchObject::Errors::HasManyAssociationError.new
20
+ error.message
21
+ end
22
+ it "BelongsToAssociationError" do
23
+ error = CouchObject::Errors::BelongsToAssociationError.new
24
+ error.message
25
+ end
26
+ it "MissingView" do
27
+ error = CouchObject::Errors::MissingView.new
28
+ error.message
29
+ end
30
+
31
+ end
32
+
@@ -0,0 +1,339 @@
1
+ require File.dirname(__FILE__) + '/persistable_helper.rb'
2
+
3
+ describe CouchObject::Persistable, "for loading objects:" do
4
+ before(:each) do
5
+ @bike = Bike.new
6
+ @with_location = WithStorageLocation.new
7
+
8
+ @db = mock("mock db")
9
+
10
+ @content_bike = %{
11
+ {
12
+ "_id":"123BAC",
13
+ "_rev":"946B7D1C",
14
+ "class":"Bike",
15
+ "attributes":
16
+ {
17
+ "wheels":2
18
+ }
19
+ }
20
+ }
21
+
22
+ @content_motor_bike = %{
23
+ {
24
+ "_id":"123BAC",
25
+ "_rev":"946B7D1C",
26
+ "class":"MotorBike",
27
+ "attributes":
28
+ {
29
+ "wheels":2
30
+ }
31
+ }
32
+ }
33
+
34
+ @content_with_location = %{
35
+ {
36
+ "_id":"123BAC",
37
+ "_rev":"946B7D1C",
38
+ "class":"WithStorageLocation",
39
+ "attributes":
40
+ {}
41
+ }
42
+ }
43
+
44
+ CouchObject::Database.stub!(:open).and_return(@db)
45
+ end
46
+
47
+ it "should get document by id" do
48
+ response = HTTPResponse.new(@content_bike)
49
+
50
+ @db.should_receive(:get).with("123BAC").and_return(response)
51
+
52
+ bike = Bike.get_by_id("123BAC", "foo")
53
+ bike.id.should == "123BAC"
54
+ end
55
+
56
+ it "should instantiate a new object WITH a :from_couch method when loaded from the document store" do
57
+ response = HTTPResponse.new(@content_bike)
58
+
59
+ @db.should_receive(:get).with("123BAC").and_return(response)
60
+
61
+ bike = Bike.get_by_id("123BAC", "foo")
62
+ bike.class.should == Bike
63
+ bike.wheels.should == 2
64
+ end
65
+
66
+ it "should instantiate a new object WITHOUT a :from_couch method when loaded from the document store" do
67
+ response = HTTPResponse.new(@content_motor_bike)
68
+ @db.should_receive(:get).with("123BAC").and_return(response)
69
+
70
+ motorbike = MotorBike.get_by_id("123BAC", "foo")
71
+ motorbike.class.should == MotorBike
72
+ motorbike.wheels.should == 2
73
+ end
74
+
75
+ it "classes with the storage location preset should be able to load documents by only supplying the ID" do
76
+ response = HTTPResponse.new(@content_with_location)
77
+ @db.should_receive(:get).with("123BAC").and_return(response)
78
+
79
+ with_content = WithStorageLocation.get_by_id("123BAC")
80
+ with_content.id.should == "123BAC"
81
+ end
82
+
83
+ it "should raise an error if a class that does not have the database uri set by default tries to laod a document only by ID" do
84
+ lambda{ bike = Bike.get_by_id("123BAC") }.
85
+ should raise_error(CouchObject::Errors::NoDatabaseLocationSet)
86
+ end
87
+
88
+ it "should raise an ID if the object doesn't exist in the database" do
89
+ response_missing = HTTPResponse.
90
+ new(%{{"error":"not_found","reason":"missing"}})
91
+ response_deleted = HTTPResponse.
92
+ new(%{{"error":"not_found","reason":"deleted"}})
93
+
94
+ @db.should_receive(:get).with("123BAC_1").and_return(response_missing)
95
+ @db.should_receive(:get).with("123BAC_2").and_return(response_deleted)
96
+
97
+ lambda{ bike = Bike.get_by_id("123BAC_1", "foo_db") }.
98
+ should raise_error(CouchObject::Errors::DocumentNotFound)
99
+
100
+ lambda{ bike = Bike.get_by_id("123BAC_2", "foo_db") }.
101
+ should raise_error(CouchObject::Errors::DocumentNotFound)
102
+
103
+ end
104
+
105
+ end
106
+
107
+ describe CouchObject::Persistable, "for loading object with subobject:" do
108
+ before(:each) do
109
+ @obj = WithSubobject.new
110
+
111
+ @db = mock("mock db")
112
+
113
+ @json_string = %{
114
+ {
115
+ "_id":"88F411C76D243294A8108DB25F2F8315",
116
+ "_rev":"4221814729",
117
+ "class":"WithSubobject",
118
+ "attributes":
119
+ {
120
+ "a_class":
121
+ {
122
+ "updated_at":
123
+ {
124
+ "json_class":"Time",
125
+ "s":1202756456,
126
+ "u":955933
127
+ },
128
+ "class":"Subobject",
129
+ "attributes":
130
+ {
131
+ "some_other_class":
132
+ {
133
+ "updated_at":
134
+ {
135
+ "json_class":"Time",
136
+ "s":1202756456,
137
+ "u":955986
138
+ },
139
+ "class":"SubSubobject",
140
+ "attributes":
141
+ {
142
+ "value":"New value"
143
+ },
144
+ "created_at":
145
+ {
146
+ "json_class":"Time",
147
+ "s":1202756456,
148
+ "u":955989
149
+ }
150
+ }
151
+ },
152
+ "created_at":
153
+ {
154
+ "json_class":"Time",
155
+ "s":1202756456,
156
+ "u":955936
157
+ }
158
+ }
159
+ }
160
+ }
161
+ }
162
+
163
+ CouchObject::Database.stub!(:open).and_return(@db)
164
+ end
165
+
166
+ it "should initialize all levels of subclasses" do
167
+ response = HTTPResponse.new(@json_string)
168
+ @db.should_receive(:get).with("123BAC").and_return(response)
169
+
170
+ with_subobject = WithSubobject.get_by_id("123BAC")
171
+ with_subobject.a_class.class.should == Subobject
172
+ with_subobject.a_class.some_other_class.class.should == SubSubobject
173
+ with_subobject.a_class.some_other_class.value.should == "New value"
174
+ with_subobject.a_class.some_other_class.created_at.class.should == Time
175
+ end
176
+ end
177
+
178
+ describe CouchObject::Persistable, "for loading objects from a view" do
179
+ before(:each) do
180
+ @bike = Bike.new
181
+ @with_location = WithStorageLocation.new
182
+
183
+ @db = mock("mock db")
184
+
185
+ @content_bike = %{
186
+ {
187
+ "_id":"123BAC",
188
+ "_rev":"946B7D1C",
189
+ "class":"Bike",
190
+ "attributes":
191
+ {
192
+ "wheels":2
193
+ }
194
+ }
195
+ }
196
+
197
+ @content_motor_bike = %{
198
+ {
199
+ "_id":"123BAC",
200
+ "_rev":"946B7D1C",
201
+ "class":"MotorBike",
202
+ "attributes":
203
+ {
204
+ "wheels":2
205
+ }
206
+ }
207
+ }
208
+
209
+ @content_with_location = %{
210
+ {
211
+ "_id":"123BAC",
212
+ "_rev":"946B7D1C",
213
+ "class":"WithStorageLocation",
214
+ "attributes":
215
+ {}
216
+ }
217
+ }
218
+
219
+ @view_content = %{
220
+ {"total_rows":4,"offset":0,"rows":[{"id":"1657A08F51BC66AEFD8630C27BD18E17","key":"1657A08F51BC66AEFD8630C27BD18E17","value":{"_id":"1657A08F51BC66AEFD8630C27BD18E17","_rev":"1626884106","class":"House","attributes":{}}},{"id":"1D7E2C45B946C1C4A7E76016FDAB104B","key":"1D7E2C45B946C1C4A7E76016FDAB104B","value":{"_id":"1D7E2C45B946C1C4A7E76016FDAB104B","_rev":"1069657208","class":"House","attributes":{}}},{"id":"D646E0796DF90D62AE402208E4043216","key":"D646E0796DF90D62AE402208E4043216","value":{"_id":"D646E0796DF90D62AE402208E4043216","_rev":"633260494","class":"House","attributes":{}}},{"id":"D770670E2219EC767BBB55FFB7763ADA","key":"D770670E2219EC767BBB55FFB7763ADA","value":{"_id":"D770670E2219EC767BBB55FFB7763ADA","_rev":"3666401817","class":"House","attributes":{}}}]}
221
+ }
222
+
223
+ @view_content_for_id = %{
224
+ {"total_rows":4,"offset":0,"rows":[{"id":"1657A08F51BC66AEFD8630C27BD18E17","key":"1657A08F51BC66AEFD8630C27BD18E17","value":{"_id":"1657A08F51BC66AEFD8630C27BD18E17","_rev":"1626884106","class":"House","attributes":{}}}]}
225
+ }
226
+
227
+ @view_content_mixed = %{
228
+ {"total_rows":2,"offset":0,"rows":[{"id":"5030848C1F2E31820ADF6B22832037DE","key":"5030848C1F2E31820ADF6B22832037DE","value":{"_id":"5030848C1F2E31820ADF6B22832037DE","_rev":"2247680957","class":"Bike","attributes":{}}},{"id":"D646E0796DF90D62AE402208E4043216","key":"D646E0796DF90D62AE402208E4043216","value":{"_id":"D646E0796DF90D62AE402208E4043216","_rev":"633260494","class":"House","attributes":{}}}]}
229
+ }
230
+
231
+ @view_content_subclasses = %{
232
+ {"total_rows":1,"offset":0,"rows":[{"id":"5A89FEF09C2E63E040B65909E277B604","key":"5A89FEF09C2E63E040B65909E277B604","value":{"_id":"5A89FEF09C2E63E040B65909E277B604","_rev":"4002434601","class":"WithSubobject","attributes":{"a_class":{"updated_at":{"json_class":"Time","s":1203104797,"u":954565},"class":"Subobject","attributes":{"some_other_class":{"updated_at":{"json_class":"Time","s":1203104797,"u":954624},"class":"SubSubobject","attributes":{"value":"Initialized"},"created_at":{"json_class":"Time","s":1203104797,"u":954627}}},"created_at":{"json_class":"Time","s":1203104797,"u":954569}}}}}]}
233
+ }
234
+
235
+ @view_content_no_rows = %{
236
+ {"total_rows":3,"rows":[]}
237
+ }
238
+
239
+ @view_content_missing_view = %{
240
+ {"error":"not_found","reason":"missing"}
241
+ }
242
+
243
+ CouchObject::Database.stub!(:open).and_return(@db)
244
+ end
245
+
246
+ it "should raise errors for missing arguments" do
247
+ lambda{bike = Bike.get_from_view()}.should \
248
+ raise_error(ArgumentError)
249
+
250
+ lambda{bike = Bike.get_from_view("foo_view")}.should \
251
+ raise_error(CouchObject::Errors::NoDatabaseLocationSet)
252
+ end
253
+
254
+ it "should return an array with four elements of class type House" do
255
+ response = HTTPResponse.new(@view_content)
256
+ @db.should_receive(:get).with("foo").and_return(response)
257
+
258
+ new_instances = Bike.get_from_view("foo", {:db_uri => "foo"})
259
+
260
+ new_instances.should be_a_kind_of(Array)
261
+ new_instances.size.should == 4
262
+ new_instances.each do |instance|
263
+ instance.class.should == House
264
+ instance.new?.should == false
265
+ end
266
+ end
267
+
268
+ it "should be able to get an element by key" do
269
+ response = HTTPResponse.new(@view_content_for_id)
270
+ @db.should_receive(:get).with("foo?key=%221657A08F51BC66AEFD8630C27BD18E17%22").and_return(response)
271
+
272
+ new_instances = Bike.get_from_view("foo", {:db_uri => "foo", :key => "1657A08F51BC66AEFD8630C27BD18E17"})
273
+
274
+ new_instances.should be_a_kind_of(Array)
275
+ new_instances.size.should == 1
276
+ new_instances.first.class.should == House
277
+ new_instances.first.new?.should == false
278
+
279
+ end
280
+
281
+ it "should return a blank array when sending in a non existent key" do
282
+ response = HTTPResponse.new(@view_content_no_rows)
283
+ @db.should_receive(:get).with("foo?key=%22D18E17%22").and_return(response)
284
+
285
+ new_instances = Bike.get_from_view("foo",
286
+ {:db_uri => "foo", :key => "D18E17"})
287
+
288
+ new_instances.should be_a_kind_of(Array)
289
+ new_instances.size.should == 0
290
+
291
+ end
292
+
293
+
294
+ it "should be able to return instances from a view with different objects" do
295
+ response = HTTPResponse.new(@view_content_mixed)
296
+ @db.should_receive(:get).with("foo").and_return(response)
297
+
298
+ new_instances = Bike.get_from_view("foo", {:db_uri => "foo"})
299
+
300
+ new_instances.should be_a_kind_of(Array)
301
+ new_instances.size.should == 2
302
+ new_instances[0].class.should == Bike
303
+ new_instances[0].new?.should == false
304
+
305
+ new_instances[1].class.should == House
306
+ new_instances[1].new?.should == false
307
+
308
+
309
+ end
310
+
311
+ it "should be able to load classes that have subclasses from a view" do
312
+ response = HTTPResponse.new(@view_content_subclasses)
313
+ @db.should_receive(:get).with("foo").and_return(response)
314
+
315
+ new_instances = WithSubobject.get_from_view("foo", {:db_uri => "foo"})
316
+
317
+ new_instances.should be_a_kind_of(Array)
318
+ new_instances.size.should == 1
319
+ new_instances.first.class.should == WithSubobject
320
+ new_instances.first.new?.should == false
321
+
322
+ new_instances.first.a_class.class.should == Subobject
323
+ new_instances.first.a_class.some_other_class.class.should == SubSubobject
324
+ new_instances.first.a_class.some_other_class.value.should == "Initialized"
325
+
326
+ end
327
+
328
+ it "should raise an error for non existing views" do
329
+ response = HTTPResponse.new(@view_content_missing_view)
330
+ @db.should_receive(:get).with("_view/doesnt_exist").and_return(response)
331
+
332
+ lambda{Bike.get_from_view("_view/doesnt_exist",
333
+ {:db_uri => "foo"})}.
334
+ should raise_error(CouchObject::Errors::MissingView)
335
+
336
+ end
337
+
338
+ end
339
+