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,194 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ HTTPResponse = Struct.new(:body)
4
+
5
+ class Bike
6
+ include CouchObject::Persistable
7
+
8
+ def initialize
9
+ @wheels = 2
10
+ end
11
+ attr_accessor :wheels
12
+
13
+ def to_couch
14
+ {:wheels => @wheels}
15
+ end
16
+
17
+ def self.from_couch(attributes)
18
+ bike = new
19
+ bike.wheels = attributes["wheels"]
20
+ bike
21
+ end
22
+ end
23
+
24
+ class MotorBike
25
+ include CouchObject::Persistable
26
+ def initialize
27
+ @wheels = 2
28
+ end
29
+ attr_accessor :wheels
30
+ end
31
+
32
+ class WithTimeStamp
33
+ include CouchObject::Persistable
34
+ add_timestamp_for :on_update, :on_create
35
+
36
+ def intialize
37
+ @some_variable = "some value"
38
+ @other_variable = "other value"
39
+ end
40
+ end
41
+
42
+ class WithTimeStampCreatedOn
43
+ include CouchObject::Persistable
44
+ add_timestamp_for :on_create
45
+
46
+ def intialize
47
+ @some_variable = "some value"
48
+ @other_variable = "other value"
49
+ end
50
+ end
51
+
52
+ class WithStorageLocation
53
+ include CouchObject::Persistable
54
+ database 'http://localhost:5984/mydb'
55
+ end
56
+
57
+ class SubSubobject
58
+ include CouchObject::Persistable
59
+ add_timestamp_for :on_create, :on_update
60
+ def initialize
61
+ @value = "Initialized"
62
+ end
63
+ attr_accessor :value
64
+ end
65
+
66
+ class Subobject
67
+ include CouchObject::Persistable
68
+ add_timestamp_for :on_create, :on_update
69
+ def initialize
70
+ @some_other_class = SubSubobject.new
71
+ end
72
+ attr_accessor :some_other_class
73
+ end
74
+
75
+ class WithSubobject
76
+ include CouchObject::Persistable
77
+ database 'http://localhost:5984/mydb'
78
+ def initialize
79
+ @a_class = Subobject.new
80
+ end
81
+ attr_accessor :a_class
82
+ end
83
+
84
+ class ApartmentBuilding
85
+ include CouchObject::Persistable
86
+ database 'foo'
87
+ has_many :inhabitants
88
+ has_many :apartments
89
+ end
90
+
91
+ class Inhabitant
92
+ include CouchObject::Persistable
93
+ database 'foo2'
94
+ belongs_to :apartment_building, :as => :inhabitants
95
+ end
96
+
97
+ class Apartment
98
+ include CouchObject::Persistable
99
+ belongs_to :apartment_building, :as => :apartments
100
+ end
101
+
102
+ class House
103
+ include CouchObject::Persistable
104
+ database 'http://localhost:5984/mydb'
105
+ has_many :has
106
+ end
107
+
108
+ class WithCallbacks
109
+ include CouchObject::Persistable
110
+ database 'foo'
111
+
112
+ def initialize
113
+ @calls = []
114
+ end
115
+ attr_accessor :calls
116
+
117
+ def before_save
118
+ @calls << "before_save"
119
+ end
120
+ def after_save
121
+ @calls << "after_save"
122
+ end
123
+ def before_create
124
+ @calls << "before_create"
125
+ end
126
+ def after_create
127
+ @calls << "after_create"
128
+ end
129
+ def before_update
130
+ @calls << "before_update"
131
+ end
132
+ def after_update
133
+ @calls << "after_update"
134
+ end
135
+ def before_delete
136
+ @calls << "before_delete"
137
+ end
138
+ def after_delete
139
+ @calls << "after_delete"
140
+ end
141
+ end
142
+
143
+ class Person
144
+ include CouchObject::Persistable
145
+ database 'foo'
146
+ has_one :life
147
+ end
148
+
149
+ class Life
150
+ include CouchObject::Persistable
151
+ database 'foo'
152
+ belongs_to :person, :as => :life
153
+ end
154
+
155
+ class Person
156
+ include CouchObject::Persistable
157
+ database 'http://localhost:5984/mydb'
158
+ has_one :life
159
+ end
160
+
161
+ class Life
162
+ include CouchObject::Persistable
163
+ database 'http://localhost:5984/mydb'
164
+ belongs_to :person, :as => :life
165
+ end
166
+
167
+
168
+ #
169
+ # Classes used with the smart_save feature specs
170
+ #
171
+ class OtherMotorBike
172
+ include CouchObject::Persistable
173
+ smart_save
174
+ def initialize
175
+ @wheels = 2
176
+ end
177
+ attr_accessor :wheels
178
+ end
179
+ class OtherApartmentBuilding
180
+ include CouchObject::Persistable
181
+ smart_save
182
+ database 'foo'
183
+ has_many :inhabitants
184
+ has_many :apartments
185
+ end
186
+ class OtherInhabitant
187
+ include CouchObject::Persistable
188
+ database 'foo2'
189
+ smart_save
190
+ belongs_to :apartment_building, :as => :inhabitants
191
+ def change
192
+ @changable_value = false
193
+ end
194
+ end
@@ -0,0 +1,470 @@
1
+ require File.dirname(__FILE__) + '/persistable_helper.rb'
2
+
3
+ describe CouchObject::Persistable, "methods added and their values: " do
4
+ before(:each) do
5
+ @building = ApartmentBuilding.new
6
+ @inhabitant = Inhabitant.new
7
+ @apartment = Apartment.new
8
+
9
+ @building.stub!(:couch_load_has_many_relations).
10
+ and_return(CouchObject::Persistable::HasManyRelation.new(@building))
11
+ @inhabitant.stub!(:couch_load_has_many_relations).
12
+ and_return(CouchObject::Persistable::HasManyRelation.new(@inhabitant))
13
+ @apartment.stub!(:couch_load_has_many_relations).
14
+ and_return(CouchObject::Persistable::HasManyRelation.new(@apartment))
15
+ end
16
+
17
+ it "should give ApartmentBuilding inhabitant getters and setters" do
18
+ @building.respond_to?(:inhabitants).should == true
19
+ @building.respond_to?(:apartments).should == true
20
+ end
21
+
22
+ it "should give Inhabitant apartment_building getters and setters" do
23
+ @inhabitant.respond_to?(:apartment_building).should == true
24
+ @inhabitant.respond_to?(:apartment_building=).should == true
25
+ end
26
+
27
+ it "should give Apartment apartment_building getters and setters" do
28
+ @apartment.respond_to?(:apartment_building).should == true
29
+ @apartment.respond_to?(:apartment_building=).should == true
30
+ end
31
+
32
+ it "should be able to list the classes it has relations to" do
33
+ @building.respond_to?(:has_many).should == true
34
+ @building.respond_to?(:belongs_to).should == true
35
+ @apartment.respond_to?(:has_many).should == true
36
+ @apartment.respond_to?(:belongs_to).should == true
37
+ @inhabitant.respond_to?(:has_many).should == true
38
+ @inhabitant.respond_to?(:belongs_to).should == true
39
+ end
40
+
41
+ it "should be able to list as what it has belongs_to relations" do
42
+ @apartment.belongs_to.each do |what_it_belongs_to|
43
+ @apartment.respond_to?("belongs_to_#{what_it_belongs_to}_as".to_sym).
44
+ should == true
45
+ @apartment.send("belongs_to_#{what_it_belongs_to}_as".to_sym).
46
+ should == :apartments
47
+ end
48
+ @inhabitant.belongs_to.each do |what_it_belongs_to|
49
+ @inhabitant.respond_to?("belongs_to_#{what_it_belongs_to}_as".to_sym).
50
+ should == true
51
+ @inhabitant.send("belongs_to_#{what_it_belongs_to}_as".to_sym).
52
+ should == :inhabitants
53
+ end
54
+ end
55
+
56
+ it "should list the has_many relations as an array" do
57
+ @building.has_many.should be_a_kind_of(Array)
58
+ @apartment.has_many.should be_a_kind_of(Array)
59
+ @inhabitant.has_many.should be_a_kind_of(Array)
60
+ end
61
+
62
+ it "should list the belongs_to relation as a symbol or nil" do
63
+ @inhabitant.belongs_to.should be_a_kind_of(Array)
64
+ @apartment.belongs_to.should be_a_kind_of(Array)
65
+ @building.belongs_to.should == []
66
+ end
67
+
68
+ it "should be able to return the names of the accessor methods used to access its relations" do
69
+
70
+ @building.has_many.should == [:inhabitants, :apartments]
71
+ @inhabitant.belongs_to.should == [:apartment_building]
72
+ @apartment.belongs_to.should == [:apartment_building]
73
+ end
74
+
75
+ end
76
+
77
+ describe CouchObject::Persistable, "getting and setting relations: " do
78
+ before(:each) do
79
+ @building_1 = ApartmentBuilding.new
80
+ @building_2 = ApartmentBuilding.new
81
+ @inhabitant_1 = Inhabitant.new
82
+ @inhabitant_2 = Inhabitant.new
83
+ @apartment = Apartment.new
84
+
85
+ @db = mock("mock db")
86
+ # Should call the DB at all...
87
+ CouchObject::Database.should_receive(:open).exactly(0).and_return(@db)
88
+
89
+ @building_1.stub!(:couch_load_has_many_relations).and_return(CouchObject::Persistable::HasManyRelation.new(@building_1))
90
+ @building_2.stub!(:couch_load_has_many_relations).and_return(CouchObject::Persistable::HasManyRelation.new(@building_2))
91
+ @inhabitant_1.stub!(:couch_load_has_many_relations).and_return(CouchObject::Persistable::HasManyRelation.new(@inhabitant_1))
92
+ @inhabitant_2.stub!(:couch_load_has_many_relations).and_return(CouchObject::Persistable::HasManyRelation.new(@inhabitant_2))
93
+ @apartment.stub!(:couch_load_has_many_relations).and_return(CouchObject::Persistable::HasManyRelation.new(@apartment))
94
+ end
95
+
96
+ it "should return an empty array for empty has_many relatoins" do
97
+ @building_1.inhabitants.should be_a_kind_of(Array)
98
+ @building_1.inhabitants.should == []
99
+ end
100
+
101
+ it "should return nil for empty belongs_to relatoins" do
102
+ @inhabitant_1.apartment_building.should == nil
103
+ end
104
+
105
+ it "should be able to add any number of object to a has_many relation and get them again" do
106
+ @building_1.inhabitants.should == []
107
+ @building_1.inhabitants << @inhabitant_1 << @inhabitant_2
108
+ # @building_1.inhabitants.size.should == 2
109
+ # @building_1.inhabitants.should == [@inhabitant_1, @inhabitant_2]
110
+ end
111
+
112
+ it "should be able to assign a class that it belongs to and retrieve it" do
113
+ @inhabitant_1.apartment_building = @building_1
114
+ @inhabitant_1.apartment_building.should == @building_1
115
+ @inhabitant_1.apartment_building = @building_2
116
+ @inhabitant_1.apartment_building.should_not == @building_1
117
+ end
118
+
119
+ it "when setting a has_many relationsship, the related class should be updated so it has a belongs_to relationsship with the other class" do
120
+ @building_1.inhabitants << @inhabitant_1
121
+ @inhabitant_1.apartment_building.should == @building_1
122
+ end
123
+
124
+ it "should be able to remove an object from a has_many relationsship" do
125
+ @building_1.inhabitants << @inhabitant_1
126
+ @building_1.inhabitants.size.should == 1
127
+ @building_1.inhabitants.remove(@inhabitant_1)
128
+ @building_1.inhabitants.size.should == 0
129
+ @inhabitant_1.apartment_building.should == nil
130
+ end
131
+
132
+ it "when setting a belongs_to relationsship, the related class should be updated so it has a has_many relationsship to the other class" do
133
+ @inhabitant_1.apartment_building = @building_1
134
+ @building_1.inhabitants.size.should == 1
135
+ @building_1.inhabitants.first.should == @inhabitant_1
136
+ end
137
+
138
+ it "should be able to set a belongs_to relationship to a master that already has children" do
139
+ @building_1.inhabitants << @inhabitant_1
140
+ @building_1.inhabitants.size.should == 1
141
+ @inhabitant_2.apartment_building = @building_1
142
+ @building_1.inhabitants.size.should == 2
143
+ end
144
+
145
+ end
146
+
147
+ describe CouchObject::Persistable, "errors when creating classes with bad relations :)" do
148
+ it "should raise an error for badly formatted belongs_to relations" do
149
+ lambda {
150
+ class Garage
151
+ include CouchObject::Persistable
152
+ belongs_to :apartment_building # missing , :as => :something
153
+ end
154
+ }.should raise_error(CouchObject::Errors::BelongsToAssociationError)
155
+
156
+ lambda {
157
+ class Lamp
158
+ include CouchObject::Persistable
159
+ belongs_to # missing :apartment_building, :as => :something
160
+ end
161
+ }.should raise_error(CouchObject::Errors::BelongsToAssociationError)
162
+
163
+ lambda {
164
+ class Mouse
165
+ include CouchObject::Persistable
166
+ belongs_to :apartment_building, :as => :animal # is correct
167
+ end
168
+ }.should_not \
169
+ raise_error(CouchObject::Errors::BelongsToAssociationError)
170
+
171
+
172
+ end
173
+
174
+ it "should raise an error for badly formatted has_many relations" do
175
+ lambda {
176
+ class Oven
177
+ include CouchObject::Persistable
178
+ has_many # missing what it is related to :users
179
+ end
180
+ }.should raise_error(CouchObject::Errors::HasManyAssociationError)
181
+
182
+ lambda {
183
+ class FavoriteOven
184
+ include CouchObject::Persistable
185
+ has_many :users
186
+ end
187
+ }.should_not \
188
+ raise_error(CouchObject::Errors::HasManyAssociationError)
189
+
190
+ end
191
+
192
+ end
193
+
194
+ describe CouchObject::Persistable, "saving related objects: " do
195
+ before(:each) do
196
+ @building = ApartmentBuilding.new
197
+ @apartment = Apartment.new
198
+
199
+ @db = mock("mock db")
200
+
201
+ content = HTTPResponse.new(JSON.unparse({
202
+ "_id" => "123BAC",
203
+ "_rev" => "946B7D1C",
204
+ }))
205
+
206
+ @document_response = CouchObject::Response.new(content)
207
+ end
208
+
209
+ it "a class should save it's has_many relations" do
210
+ CouchObject::Database.should_receive(:open).exactly(3).and_return(@db)
211
+ @db.should_receive(:post).exactly(3).and_return(@document_response)
212
+
213
+ another_apartment = @apartment.clone
214
+
215
+ @apartment.new?.should == true
216
+ another_apartment.new?.should == true
217
+ @building.new?.should == true
218
+
219
+ # Should not load the relations because it is a new object
220
+ @building.apartments.class.should == \
221
+ CouchObject::Persistable::HasManyRelation
222
+
223
+ @building.apartments << @apartment << another_apartment
224
+ @building.apartments.size.should == 2
225
+
226
+ @building.inhabitants.class.should == \
227
+ CouchObject::Persistable::HasManyRelation
228
+
229
+ @building.inhabitants.size.should == 0
230
+
231
+ @apartment.new?.should == true
232
+ another_apartment.new?.should == true
233
+ @building.new?.should == true
234
+
235
+ # Should call save on itself which results in a post, and
236
+ # then on its children which also results in a post.
237
+ # And also load it's inhabitants relations
238
+ @building.save
239
+
240
+ @apartment.new?.should == false
241
+ another_apartment.new?.should == false
242
+
243
+ end
244
+
245
+ it "should save its belongs_to relation if it is unsaved" do
246
+ CouchObject::Database.should_receive(:open).exactly(2).and_return(@db)
247
+ @db.should_receive(:post).exactly(2).and_return(@document_response)
248
+
249
+ @building.new?.should == true
250
+ @apartment.new?.should == true
251
+ @building.apartments << @apartment
252
+
253
+ @apartment.save("foo")
254
+
255
+ @apartment.new?.should == false
256
+ @building.new?.should == false
257
+
258
+ @building.id.should_not == nil
259
+ @apartment.id.should_not == nil
260
+
261
+ end
262
+
263
+ it "a class that normally is in a belongs_to relation but doesn't have a relation set should be able to save" do
264
+ CouchObject::Database.should_receive(:open).exactly(1).and_return(@db)
265
+ @db.should_receive(:post).exactly(1).and_return(@document_response)
266
+
267
+ @apartment.new?.should == true
268
+ @apartment.save("foo")
269
+ @apartment.new?.should == false
270
+ end
271
+
272
+ it "should not load the belongs_to relations if they are not already loaded before saving" do
273
+ @inhabitant_content = %{ {"_id":"50F1A07A283C1879057AFE4C4B727D35","_rev":"1957693345","belongs_to":{"inhabitants":"12A64B5156002BFD66F23F559C5AD5FA"},"class":"Inhabitant","attributes":{}}
274
+ }
275
+
276
+ response = HTTPResponse.new(@inhabitant_content)
277
+
278
+ CouchObject::Database.should_receive(:open).exactly(1).and_return(@db)
279
+
280
+ @db.should_receive(:get).once.with("foo").and_return(response)
281
+
282
+ inhabitant = Inhabitant.get_with_smart_save("foo")
283
+ inhabitant.location.should_not == nil
284
+
285
+ # Telling the object not to load its relatives
286
+ inhabitant.save
287
+
288
+ end
289
+ end
290
+
291
+ describe CouchObject::Persistable, "loading object relations: " do
292
+ before(:each) do
293
+ @db = mock("mock db")
294
+
295
+ @view_content_no_rows = %{
296
+ {"total_rows":3,"rows":[]}
297
+ }
298
+
299
+ @apartment_building_content = %{ {"_id":"A26EDC59D99D50C2CB2615404D357E49","_rev":"1756574922","class":"ApartmentBuilding","attributes":{}}
300
+ }
301
+
302
+ @inhabitant_content = %{ {"_id":"50F1A07A283C1879057AFE4C4B727D35","_rev":"1957693345","belongs_to":{"inhabitants":"12A64B5156002BFD66F23F559C5AD5FA"},"class":"Inhabitant","attributes":{}}
303
+ }
304
+
305
+ @inhabitant_content_from_view = %{
306
+ {"total_rows":2,"offset":0,"rows":[{"id":"50F1A07A283C1879057AFE4C4B727D35","key":["12A64B5156002BFD66F23F559C5AD5FA","inhabitants"],"value":{"_id":"50F1A07A283C1879057AFE4C4B727D35","_rev":"1957693345","class":"Inhabitant","attributes":{},"belongs_to":{"inhabitants":"12A64B5156002BFD66F23F559C5AD5FA"}}}]}
307
+ }
308
+
309
+ @apartment_building_inhabitants = %{
310
+ {"total_rows":2,"offset":0,"rows":[{"id":"241757E4D7D2272C78048B4007399837","key":["12A64B5156002BFD66F23F559C5AD5FA","inhabitants"],"value":{"_id":"241757E4D7D2272C78048B4007399837","_rev":"2508041460","class":"Inhabitant","attributes":{},"belongs_to":{"inhabitants":"CCC19E6CAE557D058CC8E0961507AC4C"}}},{"id":"E94C93DEDB94BFC30B9AB396D4874D52","key":["12A64B5156002BFD66F23F559C5AD5FA","inhabitants"],"value":{"_id":"E94C93DEDB94BFC30B9AB396D4874D52","_rev":"960098874","class":"Inhabitant","attributes":{},"belongs_to":{"inhabitants":"CCC19E6CAE557D058CC8E0961507AC4C"}}}]}
311
+ }
312
+
313
+ end
314
+
315
+ it "should load all has_many relations when needed" do
316
+ response = HTTPResponse.new(@apartment_building_content)
317
+ response_relation = HTTPResponse.new(@apartment_building_inhabitants)
318
+
319
+ CouchObject::Database.should_receive(:open).exactly(2).and_return(@db)
320
+
321
+ @db.should_receive(:get).with("foo").and_return(response)
322
+
323
+ # has_many relations are loaded
324
+ @db.should_receive(:get).
325
+ with("_view/couch_object_has_many_relations/related_documents?" + \
326
+ "key=[%22A26EDC59D99D50C2CB2615404D357E49%22,%22inhabitants%22]").
327
+ and_return(response_relation)
328
+
329
+ apartment_building = ApartmentBuilding.get_by_id("foo")
330
+ apartment_building.location.should_not == nil
331
+
332
+ # The content should be loaded on first request!
333
+ apartment_building.inhabitants.size.should == 2
334
+
335
+ end
336
+
337
+ it "should load belongs_to relations when needed" do
338
+ response_inhabitant = HTTPResponse.new(@inhabitant_content)
339
+ response_inhabitant_from_view = HTTPResponse.
340
+ new(@inhabitant_content_from_view)
341
+ response_apartment_buil = HTTPResponse.new(@apartment_building_content)
342
+
343
+ # 1. class gets loaded
344
+ # 2. belongs_to relation is loaded
345
+ # 3. then self is added to the belongs_to relations has_many relation
346
+ # which is first loaded from the DB
347
+ # 4. ? I don't know where it gets called...
348
+ CouchObject::Database.should_receive(:open).exactly(4).and_return(@db)
349
+
350
+ @db.should_receive(:get).with("foo").once.and_return(response_inhabitant)
351
+ @db.should_receive(:get).once.
352
+ with("12A64B5156002BFD66F23F559C5AD5FA").
353
+ and_return(response_apartment_buil)
354
+ @db.should_receive(:get).once.with("_view/couch_object_has_many_relat" + \
355
+ "ions/related_documents?key=[%22A26EDC59D99D50C2CB2615404D357E49" + \
356
+ "%22,%22inhabitants%22]").and_return(response_inhabitant_from_view)
357
+
358
+ # 1)
359
+ inhabitant = Inhabitant.get_by_id("foo","foo_db")
360
+ inhabitant.new?.should == false
361
+
362
+ # 2)
363
+ # 3)
364
+ inhabitant.apartment_building.class.should == ApartmentBuilding
365
+ inhabitant.apartment_building.new?.should == false
366
+ inhabitant.apartment_building.inhabitants.first.should == inhabitant
367
+
368
+ end
369
+
370
+ it "should be possible to tell the object not to load its has many relations" do
371
+ response = HTTPResponse.new(@apartment_building_content)
372
+
373
+ CouchObject::Database.should_receive(:open).exactly(1).and_return(@db)
374
+
375
+ @db.should_receive(:get).with("foo").and_return(response)
376
+
377
+ apartment_building = ApartmentBuilding.get_by_id("foo")
378
+ apartment_building.location.should_not == nil
379
+
380
+ # Telling the object not to load its relatives
381
+ apartment_building.do_not_load_has_many_relations
382
+
383
+ # The content should be loaded on first request!
384
+ apartment_building.inhabitants.size.should == 0
385
+
386
+ end
387
+
388
+ it "should be possible to tell the object not to load its has many relations" do
389
+ response = HTTPResponse.new(@inhabitant_content)
390
+
391
+ CouchObject::Database.should_receive(:open).exactly(1).and_return(@db)
392
+
393
+ @db.should_receive(:get).once.with("foo").and_return(response)
394
+
395
+ inhabitant = Inhabitant.get("foo")
396
+ inhabitant.location.should_not == nil
397
+
398
+ # Telling the object not to load its relatives
399
+ inhabitant.do_not_load_belongs_to_relations
400
+
401
+ # The content should be loaded on first request!
402
+ inhabitant.apartment_building.should == nil
403
+
404
+ end
405
+
406
+ end
407
+
408
+ describe CouchObject::Persistable, "has_one relations" do
409
+ before(:each) do
410
+ @person = Person.new
411
+ @life = Life.new
412
+
413
+ @db = mock("mock db")
414
+
415
+ content = HTTPResponse.new(JSON.unparse({
416
+ "_id" => "123BAC",
417
+ "_rev" => "946B7D1C",
418
+ }))
419
+
420
+ @document_response = CouchObject::Response.new(content)
421
+ end
422
+
423
+ it "should be able to set a has_one relation" do
424
+ @person.life = @life
425
+ @life.person.should == @person
426
+
427
+ @person.life = nil
428
+ @life.person.should == nil
429
+
430
+ @life.person = @person
431
+ @person.life.should == @life
432
+ end
433
+
434
+ it "should be able to save its has_one relation" do
435
+ CouchObject::Database.should_receive(:open).exactly(2).and_return(@db)
436
+ @db.should_receive(:post).exactly(2).and_return(@document_response)
437
+ @person.life = @life
438
+ @person.save
439
+ @person.new?.should == false
440
+ @life.new?.should == false
441
+ end
442
+
443
+ it "the child should save it's has_one parent when it is a new object" do
444
+ CouchObject::Database.should_receive(:open).exactly(2).and_return(@db)
445
+ @db.should_receive(:post).exactly(2).and_return(@document_response)
446
+ @person.life = @life
447
+ @life.save
448
+ @person.new?.should == false
449
+ @life.new?.should == false
450
+ end
451
+
452
+ it "should only save the child when the parent isn't new" do
453
+ CouchObject::Database.should_receive(:open).exactly(1).and_return(@db)
454
+ @db.should_receive(:post).exactly(1).and_return(@document_response)
455
+ @person.life = @life
456
+ # fake the parent into being an old object
457
+ @person.new?.should == true
458
+ @person.instance_variable_set("@id", 123)
459
+ @person.instance_variable_set("@revision", 123)
460
+ @person.new?.should == false
461
+
462
+ @life.save
463
+
464
+ @life.new?.should == false
465
+ end
466
+
467
+
468
+
469
+
470
+ end