mongo_doc 0.3.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 (122) hide show
  1. data/.document +5 -0
  2. data/.gitignore +7 -0
  3. data/LICENSE +20 -0
  4. data/README.textile +174 -0
  5. data/Rakefile +135 -0
  6. data/TODO +31 -0
  7. data/VERSION +1 -0
  8. data/data/.gitignore +2 -0
  9. data/examples/simple_document.rb +35 -0
  10. data/examples/simple_object.rb +30 -0
  11. data/features/finders.feature +76 -0
  12. data/features/mongodb.yml +7 -0
  13. data/features/mongodoc_base.feature +128 -0
  14. data/features/new_record.feature +36 -0
  15. data/features/partial_updates.feature +105 -0
  16. data/features/removing_documents.feature +68 -0
  17. data/features/saving_an_object.feature +15 -0
  18. data/features/scopes.feature +66 -0
  19. data/features/step_definitions/collection_steps.rb +14 -0
  20. data/features/step_definitions/document_steps.rb +149 -0
  21. data/features/step_definitions/documents.rb +30 -0
  22. data/features/step_definitions/finder_steps.rb +15 -0
  23. data/features/step_definitions/json_steps.rb +9 -0
  24. data/features/step_definitions/object_steps.rb +50 -0
  25. data/features/step_definitions/objects.rb +24 -0
  26. data/features/step_definitions/partial_update_steps.rb +32 -0
  27. data/features/step_definitions/query_steps.rb +54 -0
  28. data/features/step_definitions/removing_documents_steps.rb +14 -0
  29. data/features/step_definitions/scope_steps.rb +18 -0
  30. data/features/step_definitions/util_steps.rb +7 -0
  31. data/features/support/support.rb +10 -0
  32. data/features/using_criteria.feature +128 -0
  33. data/lib/mongo_doc/associations/collection_proxy.rb +105 -0
  34. data/lib/mongo_doc/associations/document_proxy.rb +56 -0
  35. data/lib/mongo_doc/associations/hash_proxy.rb +98 -0
  36. data/lib/mongo_doc/associations/proxy_base.rb +53 -0
  37. data/lib/mongo_doc/attributes.rb +140 -0
  38. data/lib/mongo_doc/bson.rb +45 -0
  39. data/lib/mongo_doc/collection.rb +55 -0
  40. data/lib/mongo_doc/connection.rb +88 -0
  41. data/lib/mongo_doc/contexts/enumerable.rb +128 -0
  42. data/lib/mongo_doc/contexts/ids.rb +41 -0
  43. data/lib/mongo_doc/contexts/mongo.rb +232 -0
  44. data/lib/mongo_doc/contexts.rb +25 -0
  45. data/lib/mongo_doc/criteria.rb +38 -0
  46. data/lib/mongo_doc/cursor.rb +32 -0
  47. data/lib/mongo_doc/document.rb +216 -0
  48. data/lib/mongo_doc/ext/array.rb +5 -0
  49. data/lib/mongo_doc/ext/binary.rb +7 -0
  50. data/lib/mongo_doc/ext/boolean_class.rb +11 -0
  51. data/lib/mongo_doc/ext/date.rb +16 -0
  52. data/lib/mongo_doc/ext/date_time.rb +13 -0
  53. data/lib/mongo_doc/ext/dbref.rb +7 -0
  54. data/lib/mongo_doc/ext/hash.rb +7 -0
  55. data/lib/mongo_doc/ext/nil_class.rb +5 -0
  56. data/lib/mongo_doc/ext/numeric.rb +17 -0
  57. data/lib/mongo_doc/ext/object.rb +17 -0
  58. data/lib/mongo_doc/ext/object_id.rb +7 -0
  59. data/lib/mongo_doc/ext/regexp.rb +5 -0
  60. data/lib/mongo_doc/ext/string.rb +5 -0
  61. data/lib/mongo_doc/ext/symbol.rb +5 -0
  62. data/lib/mongo_doc/ext/time.rb +5 -0
  63. data/lib/mongo_doc/finders.rb +49 -0
  64. data/lib/mongo_doc/matchers.rb +35 -0
  65. data/lib/mongo_doc/query.rb +7 -0
  66. data/lib/mongo_doc/scope.rb +64 -0
  67. data/lib/mongo_doc/validations/macros.rb +11 -0
  68. data/lib/mongo_doc/validations/validates_embedded.rb +13 -0
  69. data/lib/mongo_doc.rb +19 -0
  70. data/lib/mongoid/contexts/paging.rb +42 -0
  71. data/lib/mongoid/criteria.rb +247 -0
  72. data/lib/mongoid/criterion/complex.rb +21 -0
  73. data/lib/mongoid/criterion/exclusion.rb +65 -0
  74. data/lib/mongoid/criterion/inclusion.rb +92 -0
  75. data/lib/mongoid/criterion/optional.rb +136 -0
  76. data/lib/mongoid/extensions/hash/criteria_helpers.rb +20 -0
  77. data/lib/mongoid/extensions/symbol/inflections.rb +36 -0
  78. data/lib/mongoid/matchers/all.rb +11 -0
  79. data/lib/mongoid/matchers/default.rb +26 -0
  80. data/lib/mongoid/matchers/exists.rb +13 -0
  81. data/lib/mongoid/matchers/gt.rb +11 -0
  82. data/lib/mongoid/matchers/gte.rb +11 -0
  83. data/lib/mongoid/matchers/in.rb +11 -0
  84. data/lib/mongoid/matchers/lt.rb +11 -0
  85. data/lib/mongoid/matchers/lte.rb +11 -0
  86. data/lib/mongoid/matchers/ne.rb +11 -0
  87. data/lib/mongoid/matchers/nin.rb +11 -0
  88. data/lib/mongoid/matchers/size.rb +11 -0
  89. data/mongo_doc.gemspec +205 -0
  90. data/mongod.example.yml +2 -0
  91. data/mongodb.example.yml +14 -0
  92. data/perf/mongo_doc_runner.rb +90 -0
  93. data/perf/ruby_driver_runner.rb +64 -0
  94. data/script/console +8 -0
  95. data/spec/associations/collection_proxy_spec.rb +200 -0
  96. data/spec/associations/document_proxy_spec.rb +42 -0
  97. data/spec/associations/hash_proxy_spec.rb +163 -0
  98. data/spec/attributes_spec.rb +273 -0
  99. data/spec/bson_matchers.rb +54 -0
  100. data/spec/bson_spec.rb +196 -0
  101. data/spec/collection_spec.rb +161 -0
  102. data/spec/connection_spec.rb +147 -0
  103. data/spec/contexts/enumerable_spec.rb +274 -0
  104. data/spec/contexts/ids_spec.rb +49 -0
  105. data/spec/contexts/mongo_spec.rb +198 -0
  106. data/spec/contexts_spec.rb +28 -0
  107. data/spec/criteria_spec.rb +33 -0
  108. data/spec/cursor_spec.rb +91 -0
  109. data/spec/document_ext.rb +9 -0
  110. data/spec/document_spec.rb +664 -0
  111. data/spec/embedded_save_spec.rb +109 -0
  112. data/spec/finders_spec.rb +73 -0
  113. data/spec/hash_matchers.rb +27 -0
  114. data/spec/matchers_spec.rb +342 -0
  115. data/spec/mongodb.yml +6 -0
  116. data/spec/mongodb_pairs.yml +8 -0
  117. data/spec/new_record_spec.rb +128 -0
  118. data/spec/query_spec.rb +12 -0
  119. data/spec/scope_spec.rb +79 -0
  120. data/spec/spec.opts +2 -0
  121. data/spec/spec_helper.rb +13 -0
  122. metadata +290 -0
@@ -0,0 +1,664 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "MongoDoc::Document" do
4
+
5
+ context "satisfies form_for requirements" do
6
+ class FormForTest
7
+ include MongoDoc::Document
8
+
9
+ key :data
10
+ end
11
+
12
+ before do
13
+ @doc = FormForTest.new
14
+ @doc._id = '1'
15
+ end
16
+
17
+ it "#id returns the _id" do
18
+ @doc.id.should == @doc._id
19
+ end
20
+
21
+ it "#to_param returns the string of the _id" do
22
+ @doc.to_param.should == @doc._id.to_s
23
+ end
24
+
25
+ context "#new_record?" do
26
+ it "is true when the object does not have an _id" do
27
+ @doc._id = nil
28
+ @doc.should be_new_record
29
+ end
30
+
31
+ it "is false when the object has an id" do
32
+ @doc.should_not be_new_record
33
+ end
34
+ end
35
+
36
+ context "attributes" do
37
+ it "has an initialize method that takes a hash" do
38
+ data = 'data'
39
+ FormForTest.new(:data => data).data.should == data
40
+ end
41
+
42
+ it "can set attributes from a hash" do
43
+ test = FormForTest.new
44
+ data = 'data'
45
+ test.attributes = {:data => data}
46
+ test.data.should == data
47
+ end
48
+
49
+ it "returns all its attributes" do
50
+ data = 'data'
51
+ test = FormForTest.new(:data => data)
52
+ test.attributes.should == {:data => data}
53
+ end
54
+ end
55
+ end
56
+
57
+ context "validations" do
58
+ class SimpleValidationTest
59
+ include MongoDoc::Document
60
+
61
+ key :data
62
+ validates_presence_of :data
63
+ end
64
+
65
+ it "are included by MongoDoc::Document" do
66
+ Validatable.should === SimpleValidationTest.new
67
+ end
68
+
69
+ it "valid? fails when a document is invalid" do
70
+ doc = SimpleValidationTest.new
71
+ doc.should_not be_valid
72
+ doc.should have(1).error_on(:data)
73
+ end
74
+ end
75
+
76
+ context "saving" do
77
+ class SaveRoot
78
+ include MongoDoc::Document
79
+
80
+ has_many :save_children
81
+ end
82
+
83
+ class SaveChild
84
+ include MongoDoc::Document
85
+
86
+ key :data
87
+ end
88
+
89
+ before do
90
+ @root = SaveRoot.new
91
+ @root.stub(:_save)
92
+ @child = SaveChild.new
93
+ @root.save_children << @child
94
+ end
95
+
96
+ context "#save" do
97
+ it "delegates to the root" do
98
+ validate = true
99
+ @root.should_receive(:save).with(validate)
100
+ @child.save(validate)
101
+ end
102
+
103
+ context "when validating" do
104
+ it "validates" do
105
+ @root.should_receive(:valid?)
106
+ @root.save(true)
107
+ end
108
+
109
+ context "and valid" do
110
+ it "delegates to _save" do
111
+ @root.should_receive(:_save).with(false)
112
+ @root.save(true)
113
+ end
114
+
115
+ it "returns the result of _save if valid" do
116
+ id = 'id'
117
+ @root.stub(:valid?).and_return(true)
118
+ @root.should_receive(:_save).and_return(id)
119
+ @root.save(true).should == id
120
+ end
121
+ end
122
+
123
+ context "and invalid" do
124
+ it "does not call _save" do
125
+ @root.stub(:valid?).and_return(false)
126
+ @root.should_not_receive(:_save)
127
+ @root.save(true)
128
+ end
129
+
130
+ it "returns false" do
131
+ @root.stub(:valid?).and_return(false)
132
+ @root.save(true).should be_false
133
+ end
134
+ end
135
+ end
136
+
137
+ context "when not validating" do
138
+ it "does not validate" do
139
+ @root.should_not_receive(:valid?)
140
+ @root.save(false)
141
+ end
142
+
143
+ it "delegates to _save" do
144
+ @root.should_receive(:_save).with(false)
145
+ @root.save(false)
146
+ end
147
+
148
+ it "returns the result of _save" do
149
+ id = 'id'
150
+ @root.stub(:_save).and_return(id)
151
+ @root.save(false).should == id
152
+ end
153
+ end
154
+ end
155
+
156
+ context "#save!" do
157
+ it "delegates to the root" do
158
+ @root.should_receive(:save!)
159
+ @child.save!
160
+ end
161
+
162
+ it "validates" do
163
+ @root.should_receive(:valid?).and_return(true)
164
+ @root.save!
165
+ end
166
+
167
+ it "returns the result of _save if valid" do
168
+ id = 'id'
169
+ @root.stub(:valid?).and_return(true)
170
+ @root.should_receive(:_save).with(true).and_return(id)
171
+ @root.save!.should == id
172
+ end
173
+
174
+ it "raises if invalid" do
175
+ @root.stub(:valid?).and_return(false)
176
+ expect do
177
+ @root.save!
178
+ end.should raise_error(MongoDoc::DocumentInvalidError)
179
+ end
180
+ end
181
+ end
182
+
183
+ context "#_save" do
184
+ class SaveTest
185
+ include MongoDoc::Document
186
+ end
187
+
188
+ before do
189
+ @collection = stub('collection')
190
+ @doc = SaveTest.new
191
+ @doc.stub(:_collection).and_return(@collection)
192
+ end
193
+
194
+ it "delegates to the collection save" do
195
+ safe = true
196
+ @collection.should_receive(:save)
197
+ @doc.send(:_save, safe)
198
+ end
199
+
200
+ it "sets the _id of the document" do
201
+ id = 'id'
202
+ @collection.stub(:save).and_return(id)
203
+ @doc.send(:_save, true)
204
+ @doc._id.should == id
205
+ end
206
+
207
+ it "returns the _id" do
208
+ id = 'id'
209
+ @collection.stub(:save).and_return(id)
210
+ @doc.send(:_save, true).should == id
211
+ end
212
+ end
213
+
214
+ context "creating" do
215
+ class CreateTest
216
+ include MongoDoc::Document
217
+
218
+ key :data
219
+ validates_presence_of :data
220
+ end
221
+
222
+ let(:data) { 'data' }
223
+ let(:instance) { CreateTest.new(:data => data) }
224
+
225
+ before do
226
+ instance.stub(:save)
227
+ instance.stub(:save!)
228
+ end
229
+
230
+ context ".create" do
231
+ it "creates a new document with the attributes" do
232
+ CreateTest.should_receive(:new).with(:data => data).and_return(instance)
233
+ CreateTest.create(:data => data)
234
+ end
235
+
236
+ context "with the new document" do
237
+ before do
238
+ CreateTest.stub(:new).and_return(instance)
239
+ end
240
+
241
+ it "calls save on the instance with safe => false" do
242
+ instance.should_receive(:save).with(false)
243
+ CreateTest.create(:data => data)
244
+ end
245
+
246
+ it "returns the new object" do
247
+ CreateTest.create(:data => data).should == instance
248
+ end
249
+ end
250
+ end
251
+
252
+ context ".create!" do
253
+ it "creates a new document with the attributes" do
254
+ CreateTest.should_receive(:new).with(:data => data).and_return(instance)
255
+ CreateTest.create!(:data => data)
256
+ end
257
+
258
+ context "with the new document" do
259
+ before do
260
+ CreateTest.stub(:new).and_return(instance)
261
+ end
262
+
263
+ it "calls save! on the instance" do
264
+ instance.should_receive(:save!)
265
+ CreateTest.create!(:data => data)
266
+ end
267
+
268
+ it "returns the new object" do
269
+ CreateTest.create!(:data => data).should == instance
270
+ end
271
+ end
272
+ end
273
+ end
274
+
275
+ context "updating attributes" do
276
+ class UpdateAttributesRoot
277
+ include MongoDoc::Document
278
+
279
+ has_one :update_attributes_child
280
+ end
281
+
282
+ class UpdateAttributesChild
283
+ include MongoDoc::Document
284
+
285
+ key :data
286
+ end
287
+
288
+ let(:data) {'data'}
289
+
290
+ let(:attrs) {{:data => data}}
291
+
292
+ let(:path_attrs) {{'update_attributes_child.data' => data}}
293
+
294
+ let(:doc) do
295
+ doc = UpdateAttributesChild.new
296
+ doc._id = 'id'
297
+ doc.stub(:_naive_update_attributes)
298
+ doc
299
+ end
300
+
301
+ before do
302
+ root = UpdateAttributesRoot.new
303
+ root.update_attributes_child = doc
304
+ root._id = 'id'
305
+ end
306
+
307
+ context "#update_attributes" do
308
+ it "delegates to save if the object is a new record" do
309
+ check = 'check'
310
+ doc.stub(:new_record?).and_return(true)
311
+ doc.should_receive(:save).and_return(check)
312
+ doc.update_attributes(attrs).should == check
313
+ end
314
+
315
+ it "sets the attributes" do
316
+ doc.update_attributes(attrs)
317
+ doc.data.should == data
318
+ end
319
+
320
+ it "normalizes the attributes to the parent" do
321
+ doc.should_receive(:_path_to_root)
322
+ doc.update_attributes(attrs)
323
+ end
324
+
325
+ it "validates" do
326
+ doc.should_receive(:valid?)
327
+ doc.update_attributes(attrs)
328
+ end
329
+
330
+ it "returns false if the object is not valid" do
331
+ doc.stub(:valid?).and_return(false)
332
+ doc.update_attributes(attrs).should be_false
333
+ end
334
+
335
+ context "if valid" do
336
+ context "and strict" do
337
+ it "delegates to _strict_update_attributes" do
338
+ strict_attrs = attrs.merge(:__strict__ => true)
339
+ doc.should_receive(:_strict_update_attributes).with(path_attrs, false)
340
+ doc.update_attributes(strict_attrs)
341
+ end
342
+ end
343
+
344
+ context "and naive" do
345
+ it "delegates to _naive_update_attributes" do
346
+ doc.should_receive(:_naive_update_attributes).with(path_attrs, false)
347
+ doc.update_attributes(attrs)
348
+ end
349
+ end
350
+
351
+ it "returns the result of _naive_update_attributes" do
352
+ result = 'check'
353
+ doc.stub(:_naive_update_attributes).and_return(result)
354
+ doc.update_attributes(attrs).should == result
355
+ end
356
+ end
357
+ end
358
+
359
+ context "#update_attributes!" do
360
+ it "delegates to save! if the object is a new record" do
361
+ check = 'check'
362
+ doc.stub(:new_record?).and_return(true)
363
+ doc.should_receive(:save!).and_return(check)
364
+ doc.update_attributes!(attrs).should == check
365
+ end
366
+
367
+ it "sets the attributes" do
368
+ doc.update_attributes!(attrs)
369
+ doc.data.should == data
370
+ end
371
+
372
+ it "normalizes the attributes to the parent" do
373
+ doc.should_receive(:_path_to_root)
374
+ doc.update_attributes!(attrs)
375
+ end
376
+
377
+ it "validates" do
378
+ doc.should_receive(:valid?).and_return(true)
379
+ doc.update_attributes!(attrs)
380
+ end
381
+
382
+ it "raises if not valid" do
383
+ doc.stub(:valid?).and_return(false)
384
+ expect do
385
+ doc.update_attributes!(attrs)
386
+ end.should raise_error(MongoDoc::DocumentInvalidError)
387
+ end
388
+
389
+ context "if valid" do
390
+ context "and strict" do
391
+ it "delegates to _strict_update_attributes with safe == true" do
392
+ strict_attrs = attrs.merge(:__strict__ => true)
393
+ doc.should_receive(:_strict_update_attributes).with(path_attrs, true)
394
+ doc.update_attributes!(strict_attrs)
395
+ end
396
+ end
397
+
398
+ context "and naive" do
399
+ it "delegates to _naive_update_attributes with safe == true" do
400
+ doc.should_receive(:_naive_update_attributes).with(path_attrs, true)
401
+ doc.update_attributes!(attrs)
402
+ end
403
+ end
404
+
405
+ it "returns the result of _naive_update_attributes" do
406
+ result = 'check'
407
+ doc.stub(:_naive_update_attributes).and_return(result)
408
+ doc.update_attributes!(attrs).should == result
409
+ end
410
+ end
411
+ end
412
+ end
413
+
414
+ context "#_naive_update_attributes" do
415
+ class NaiveUpdateAttributes
416
+ include MongoDoc::Document
417
+ end
418
+
419
+
420
+ let(:id) { 'id' }
421
+
422
+ let(:attrs) { {:data => 'data'} }
423
+
424
+ let(:safe) { false }
425
+
426
+ let(:doc) do
427
+ doc = NaiveUpdateAttributes.new
428
+ doc.stub(:_id).and_return(id)
429
+ doc
430
+ end
431
+
432
+ it "without a root delegates to _update" do
433
+ doc.should_receive(:_update).with({}, attrs, safe)
434
+ doc.send(:_naive_update_attributes, attrs, safe)
435
+ end
436
+
437
+ it "with a root, calls _naive_update_attributes on the root" do
438
+ root = NaiveUpdateAttributes.new
439
+ doc.stub(:_root).and_return(root)
440
+ root.should_receive(:_naive_update_attributes).with(attrs, safe)
441
+ doc.send(:_naive_update_attributes, attrs, safe)
442
+ end
443
+ end
444
+
445
+ context "#_strict_update_attributes" do
446
+ class StrictUpdateAttributes
447
+ include MongoDoc::Document
448
+ end
449
+
450
+ let(:id) { 'id' }
451
+
452
+ let(:attrs) { {:data => 'data'} }
453
+
454
+ let(:selector) { {:selector => 'selector'} }
455
+
456
+ let(:safe) { false }
457
+
458
+ let(:doc) do
459
+ doc = StrictUpdateAttributes.new
460
+ doc.stub(:_id).and_return(id)
461
+ doc
462
+ end
463
+
464
+ context "without a root" do
465
+ it "without a root delegates to _update" do
466
+ doc.should_receive(:_update).with(selector, attrs, safe)
467
+ doc.send(:_strict_update_attributes, attrs, safe, selector)
468
+ end
469
+ end
470
+
471
+ context "with a root" do
472
+ let(:root) { StrictUpdateAttributes.new }
473
+
474
+ before do
475
+ doc.stub(:_root).and_return(root)
476
+ end
477
+
478
+ it "calls _path_to_root on our id" do
479
+ root.stub(:_strict_update_attributes)
480
+ doc.should_receive(:_path_to_root).with(doc, '_id' => id)
481
+ doc.send(:_strict_update_attributes, attrs, safe)
482
+ end
483
+
484
+ it "calls _strict_update_attributes on the root with our selector" do
485
+ selector = {'path._id' => id}
486
+ doc.stub(:_path_to_root).with(doc, '_id' => id).and_return(selector)
487
+ root.should_receive(:_strict_update_attributes).with(attrs, safe, selector)
488
+ doc.send(:_strict_update_attributes, attrs, safe)
489
+ end
490
+ end
491
+ end
492
+
493
+ describe "bson" do
494
+ class BSONTest
495
+ include MongoDoc::Document
496
+
497
+ key :other
498
+ end
499
+
500
+ class BSONDerived < BSONTest
501
+ include MongoDoc::Document
502
+
503
+ key :derived
504
+ end
505
+
506
+ class OtherObject
507
+ attr_accessor :value
508
+ end
509
+
510
+ before do
511
+ @value = 'value'
512
+ @other = OtherObject.new
513
+ @other.value = @value
514
+ @doc = BSONTest.new(:other => @other)
515
+ end
516
+
517
+ it "encodes the class for the object" do
518
+ @doc.to_bson[MongoDoc::BSON::CLASS_KEY].should == BSONTest.name
519
+ end
520
+
521
+ it "renders a json representation of the object" do
522
+ @doc.to_bson.should be_bson_eql({MongoDoc::BSON::CLASS_KEY => BSONTest.name, "other" => {MongoDoc::BSON::CLASS_KEY => OtherObject.name, "value" => @value}})
523
+ end
524
+
525
+ it "includes the _id of the object" do
526
+ @doc._id = Mongo::ObjectID.new
527
+ @doc.to_bson.should be_bson_eql({MongoDoc::BSON::CLASS_KEY => BSONTest.name, "_id" => @doc._id.to_bson, "other" => {MongoDoc::BSON::CLASS_KEY => OtherObject.name, "value" => @value}})
528
+ end
529
+
530
+ it "roundtrips the object" do
531
+ MongoDoc::BSON.decode(@doc.to_bson).should be_kind_of(BSONTest)
532
+ end
533
+
534
+ it "ignores the class hash when the :raw_json option is used" do
535
+ MongoDoc::BSON.decode(@doc.to_bson.except(MongoDoc::BSON::CLASS_KEY), :raw_json => true)['other'].should == @other.to_bson
536
+ end
537
+
538
+ it "allows for derived classes" do
539
+ derived = BSONDerived.new(:other => @other, :derived => 'derived')
540
+ MongoDoc::BSON.decode(derived.to_bson).other.should be_kind_of(OtherObject)
541
+ end
542
+
543
+ it "roundtrips embedded ruby objects" do
544
+ MongoDoc::BSON.decode(@doc.to_bson).other.should be_kind_of(OtherObject)
545
+ end
546
+
547
+ context "associations" do
548
+ context "has_one" do
549
+ class TestHasOneBsonDoc
550
+ include MongoDoc::Document
551
+
552
+ has_one :subdoc
553
+ end
554
+
555
+ class SubHasOneBsonDoc
556
+ include MongoDoc::Document
557
+
558
+ key :attr
559
+ end
560
+
561
+ it "#to_bson renders a bson representation of the document" do
562
+ doc = TestHasOneBsonDoc.new
563
+ subdoc = SubHasOneBsonDoc.new(:attr => "value")
564
+ bson = doc.to_bson
565
+ bson["subdoc"] = subdoc.to_bson
566
+ doc.subdoc = subdoc
567
+ doc.to_bson.should == bson
568
+ end
569
+
570
+ it "roundtrips" do
571
+ doc = TestHasOneBsonDoc.new
572
+ subdoc = SubHasOneBsonDoc.new(:attr => "value")
573
+ doc.subdoc = subdoc
574
+ MongoDoc::BSON.decode(doc.to_bson).should == doc
575
+ end
576
+ end
577
+
578
+ context "has_many" do
579
+
580
+ class SubHasManyBsonDoc
581
+ include MongoDoc::Document
582
+
583
+ key :attr
584
+ end
585
+
586
+ class TestHasManyBsonDoc
587
+ include MongoDoc::Document
588
+ has_many :subdoc, :class_name => 'SubHasManyBsonDoc'
589
+ end
590
+
591
+ it "#to_bson renders a bson representation of the document" do
592
+ doc = TestHasManyBsonDoc.new
593
+ subdoc = SubHasManyBsonDoc.new(:attr => "value")
594
+ bson = doc.to_bson
595
+ bson["subdoc"] = [subdoc].to_bson
596
+ doc.subdoc = subdoc
597
+ doc.to_bson.should == bson
598
+ end
599
+
600
+ it "roundtrips" do
601
+ doc = TestHasManyBsonDoc.new
602
+ subdoc = SubHasManyBsonDoc.new(:attr => "value")
603
+ doc.subdoc = subdoc
604
+ MongoDoc::BSON.decode(doc.to_bson).should == doc
605
+ end
606
+
607
+ it "roundtrips the proxy" do
608
+ doc = TestHasManyBsonDoc.new(:subdoc => SubHasManyBsonDoc.new(:attr => "value"))
609
+ MongoDoc::Associations::CollectionProxy.should === MongoDoc::BSON.decode(doc.to_bson).subdoc
610
+ end
611
+ end
612
+ end
613
+ end
614
+
615
+ context "removing documents" do
616
+ class RemoveDocument
617
+ include MongoDoc::Document
618
+ end
619
+
620
+ let(:doc) { RemoveDocument.new }
621
+
622
+ context "#remove" do
623
+ it "when called on a embedded document with a _root raises UnsupportedOperation" do
624
+ doc._root = RemoveDocument.new
625
+ expect { doc.remove }.to raise_error(MongoDoc::UnsupportedOperation)
626
+ end
627
+
628
+ it "delegates to remove document" do
629
+ doc.should_receive(:remove_document)
630
+ doc.remove
631
+ end
632
+ end
633
+
634
+ context "#remove_document" do
635
+ it "when the document is the root, removes the document" do
636
+ doc.should_receive(:_remove)
637
+ doc.remove_document
638
+ end
639
+
640
+ it "when the document is not the root, calls remove_document on the root" do
641
+ doc._root = root = RemoveDocument.new
642
+ root.should_receive(:remove_document)
643
+ doc.remove_document
644
+ end
645
+ end
646
+ end
647
+
648
+ context "misc class methods" do
649
+ class ClassMethods
650
+ include MongoDoc::Document
651
+ end
652
+
653
+ it ".collection_name returns the name of the collection for this class" do
654
+ ClassMethods.collection_name.should == ClassMethods.to_s.tableize.gsub('/', '.')
655
+ end
656
+
657
+ it ".collection returns a wrapped MongoDoc::Collection" do
658
+ db = stub('db')
659
+ db.should_receive(:collection).with(ClassMethods.to_s.tableize.gsub('/', '.'))
660
+ MongoDoc::Connection.should_receive(:database).and_return(db)
661
+ MongoDoc::Collection.should === ClassMethods.collection
662
+ end
663
+ end
664
+ end