mongo_mapper_ign 0.7.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. data/.gitignore +10 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +35 -0
  4. data/Rakefile +37 -0
  5. data/bin/mmconsole +60 -0
  6. data/lib/mongo_mapper.rb +116 -0
  7. data/lib/mongo_mapper/document.rb +313 -0
  8. data/lib/mongo_mapper/embedded_document.rb +70 -0
  9. data/lib/mongo_mapper/plugins.rb +35 -0
  10. data/lib/mongo_mapper/plugins/associations.rb +114 -0
  11. data/lib/mongo_mapper/plugins/associations/base.rb +123 -0
  12. data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +30 -0
  13. data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +25 -0
  14. data/lib/mongo_mapper/plugins/associations/collection.rb +21 -0
  15. data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +39 -0
  16. data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +144 -0
  17. data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +28 -0
  18. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +129 -0
  19. data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +31 -0
  20. data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +23 -0
  21. data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +13 -0
  22. data/lib/mongo_mapper/plugins/associations/one_embedded_proxy.rb +41 -0
  23. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +69 -0
  24. data/lib/mongo_mapper/plugins/associations/proxy.rb +124 -0
  25. data/lib/mongo_mapper/plugins/callbacks.rb +240 -0
  26. data/lib/mongo_mapper/plugins/clone.rb +13 -0
  27. data/lib/mongo_mapper/plugins/descendants.rb +16 -0
  28. data/lib/mongo_mapper/plugins/dirty.rb +119 -0
  29. data/lib/mongo_mapper/plugins/equality.rb +23 -0
  30. data/lib/mongo_mapper/plugins/identity_map.rb +122 -0
  31. data/lib/mongo_mapper/plugins/inspect.rb +14 -0
  32. data/lib/mongo_mapper/plugins/keys.rb +345 -0
  33. data/lib/mongo_mapper/plugins/logger.rb +17 -0
  34. data/lib/mongo_mapper/plugins/modifiers.rb +107 -0
  35. data/lib/mongo_mapper/plugins/pagination.rb +24 -0
  36. data/lib/mongo_mapper/plugins/pagination/proxy.rb +72 -0
  37. data/lib/mongo_mapper/plugins/persistence.rb +68 -0
  38. data/lib/mongo_mapper/plugins/protected.rb +45 -0
  39. data/lib/mongo_mapper/plugins/rails.rb +57 -0
  40. data/lib/mongo_mapper/plugins/serialization.rb +91 -0
  41. data/lib/mongo_mapper/plugins/serialization/array.rb +56 -0
  42. data/lib/mongo_mapper/plugins/serialization/xml_serializer.rb +240 -0
  43. data/lib/mongo_mapper/plugins/timestamps.rb +21 -0
  44. data/lib/mongo_mapper/plugins/userstamps.rb +14 -0
  45. data/lib/mongo_mapper/plugins/validations.rb +46 -0
  46. data/lib/mongo_mapper/query.rb +143 -0
  47. data/lib/mongo_mapper/support.rb +218 -0
  48. data/lib/mongo_mapper/support/descendant_appends.rb +46 -0
  49. data/lib/mongo_mapper/support/find.rb +77 -0
  50. data/lib/mongo_mapper/version.rb +3 -0
  51. data/mongo_mapper.gemspec +214 -0
  52. data/mongo_mapper_ign.gemspec +217 -0
  53. data/performance/read_write.rb +52 -0
  54. data/specs.watchr +51 -0
  55. data/test/NOTE_ON_TESTING +1 -0
  56. data/test/active_model_lint_test.rb +13 -0
  57. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +63 -0
  58. data/test/functional/associations/test_belongs_to_proxy.rb +101 -0
  59. data/test/functional/associations/test_in_array_proxy.rb +325 -0
  60. data/test/functional/associations/test_many_documents_as_proxy.rb +229 -0
  61. data/test/functional/associations/test_many_documents_proxy.rb +536 -0
  62. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +176 -0
  63. data/test/functional/associations/test_many_embedded_proxy.rb +256 -0
  64. data/test/functional/associations/test_many_polymorphic_proxy.rb +302 -0
  65. data/test/functional/associations/test_one_embedded_proxy.rb +68 -0
  66. data/test/functional/associations/test_one_proxy.rb +196 -0
  67. data/test/functional/test_associations.rb +44 -0
  68. data/test/functional/test_binary.rb +27 -0
  69. data/test/functional/test_callbacks.rb +151 -0
  70. data/test/functional/test_dirty.rb +163 -0
  71. data/test/functional/test_document.rb +1219 -0
  72. data/test/functional/test_embedded_document.rb +210 -0
  73. data/test/functional/test_identity_map.rb +507 -0
  74. data/test/functional/test_indexing.rb +44 -0
  75. data/test/functional/test_logger.rb +20 -0
  76. data/test/functional/test_modifiers.rb +394 -0
  77. data/test/functional/test_pagination.rb +93 -0
  78. data/test/functional/test_protected.rb +163 -0
  79. data/test/functional/test_string_id_compatibility.rb +67 -0
  80. data/test/functional/test_timestamps.rb +64 -0
  81. data/test/functional/test_userstamps.rb +28 -0
  82. data/test/functional/test_validations.rb +342 -0
  83. data/test/models.rb +227 -0
  84. data/test/support/custom_matchers.rb +37 -0
  85. data/test/support/timing.rb +16 -0
  86. data/test/test_helper.rb +64 -0
  87. data/test/unit/associations/test_base.rb +212 -0
  88. data/test/unit/associations/test_proxy.rb +105 -0
  89. data/test/unit/serializers/test_json_serializer.rb +202 -0
  90. data/test/unit/test_descendant_appends.rb +71 -0
  91. data/test/unit/test_document.rb +225 -0
  92. data/test/unit/test_dynamic_finder.rb +123 -0
  93. data/test/unit/test_embedded_document.rb +657 -0
  94. data/test/unit/test_keys.rb +185 -0
  95. data/test/unit/test_mongo_mapper.rb +118 -0
  96. data/test/unit/test_pagination.rb +160 -0
  97. data/test/unit/test_plugins.rb +50 -0
  98. data/test/unit/test_query.rb +374 -0
  99. data/test/unit/test_rails.rb +181 -0
  100. data/test/unit/test_rails_compatibility.rb +52 -0
  101. data/test/unit/test_serialization.rb +51 -0
  102. data/test/unit/test_support.rb +382 -0
  103. data/test/unit/test_time_zones.rb +39 -0
  104. data/test/unit/test_validations.rb +544 -0
  105. metadata +327 -0
@@ -0,0 +1,163 @@
1
+ require 'test_helper'
2
+
3
+ class DirtyTest < Test::Unit::TestCase
4
+ def setup
5
+ @document = Doc { key :phrase, String }
6
+ end
7
+
8
+ context "marking changes" do
9
+ should "not happen if there are none" do
10
+ doc = @document.new
11
+ doc.phrase_changed?.should be_false
12
+ doc.phrase_change.should be_nil
13
+ end
14
+
15
+ should "happen when change happens" do
16
+ doc = @document.new
17
+ doc.phrase = 'Golly Gee Willikers Batman'
18
+ doc.phrase_changed?.should be_true
19
+ doc.phrase_was.should be_nil
20
+ doc.phrase_change.should == [nil, 'Golly Gee Willikers Batman']
21
+ end
22
+
23
+ should "happen when initializing" do
24
+ doc = @document.new(:phrase => 'Foo')
25
+ doc.changed?.should be_true
26
+ end
27
+
28
+ should "clear changes on save" do
29
+ doc = @document.new
30
+ doc.phrase = 'Golly Gee Willikers Batman'
31
+ doc.phrase_changed?.should be_true
32
+ doc.save
33
+ doc.phrase_changed?.should_not be_true
34
+ doc.phrase_change.should be_nil
35
+ end
36
+
37
+ should "clear changes on save!" do
38
+ doc = @document.new
39
+ doc.phrase = 'Golly Gee Willikers Batman'
40
+ doc.phrase_changed?.should be_true
41
+ doc.save!
42
+ doc.phrase_changed?.should_not be_true
43
+ doc.phrase_change.should be_nil
44
+ end
45
+
46
+ should "not happen when loading from database" do
47
+ doc = @document.create(:phrase => 'Foo')
48
+ doc = @document.find(doc.id)
49
+
50
+ doc.changed?.should be_false
51
+ doc.phrase = 'Fart'
52
+ doc.changed?.should be_true
53
+ doc.reload
54
+ doc.changed?.should be_false
55
+ end
56
+
57
+ should "happen if changed after loading from database" do
58
+ doc = @document.create(:phrase => 'Foo')
59
+ doc.reload
60
+ doc.changed?.should be_false
61
+ doc.phrase = 'Bar'
62
+ doc.changed?.should be_true
63
+ end
64
+ end
65
+
66
+ context "blank new value and type integer" do
67
+ should "not mark changes" do
68
+ @document.key :age, Integer
69
+
70
+ [nil, ''].each do |value|
71
+ doc = @document.new
72
+ doc.age = value
73
+ doc.age_changed?.should be_false
74
+ doc.age_change.should be_nil
75
+ end
76
+ end
77
+ end
78
+
79
+ context "blank new value and type float" do
80
+ should "not mark changes" do
81
+ @document.key :amount, Float
82
+
83
+ [nil, ''].each do |value|
84
+ doc = @document.new
85
+ doc.amount = value
86
+ doc.amount_changed?.should be_false
87
+ doc.amount_change.should be_nil
88
+ end
89
+ end
90
+ end
91
+
92
+ context "changed?" do
93
+ should "be true if key changed" do
94
+ doc = @document.new
95
+ doc.phrase = 'A penny saved is a penny earned.'
96
+ doc.changed?.should be_true
97
+ end
98
+
99
+ should "be false if no keys changed" do
100
+ @document.new.changed?.should be_false
101
+ end
102
+ end
103
+
104
+ context "changes" do
105
+ should "be empty hash if no changes" do
106
+ @document.new.changes.should == {}
107
+ end
108
+
109
+ should "be hash of keys with values of changes if there are changes" do
110
+ doc = @document.new
111
+ doc.phrase = 'A penny saved is a penny earned.'
112
+ doc.changes['phrase'].should == [nil, 'A penny saved is a penny earned.']
113
+ end
114
+ end
115
+
116
+ context "changed" do
117
+ should "be empty array if no changes" do
118
+ @document.new.changed.should == []
119
+ end
120
+
121
+ should "be array of keys that have changed if there are changes" do
122
+ doc = @document.new
123
+ doc.phrase = 'A penny saved is a penny earned.'
124
+ doc.changed.should == ['phrase']
125
+ end
126
+ end
127
+
128
+ context "will_change!" do
129
+ should "mark changes" do
130
+ doc = @document.create(:phrase => 'Foo')
131
+
132
+ doc.phrase << 'bar'
133
+ doc.phrase_changed?.should be_false
134
+
135
+ doc.phrase_will_change!
136
+ doc.phrase_changed?.should be_true
137
+ doc.phrase_change.should == ['Foobar', 'Foobar']
138
+
139
+ doc.phrase << '!'
140
+ doc.phrase_changed?.should be_true
141
+ doc.phrase_change.should == ['Foobar', 'Foobar!']
142
+ end
143
+ end
144
+
145
+ context "changing a foreign key through association" do
146
+ should "mark changes" do
147
+ project_class = Doc do
148
+ key :name, String
149
+ end
150
+
151
+ milestone_class = Doc do
152
+ key :project_id, ObjectId
153
+ key :name, String
154
+ end
155
+ milestone_class.belongs_to :project, :class => project_class
156
+
157
+ milestone = milestone_class.create(:name => 'Launch')
158
+ milestone.project = project_class.create(:name => 'Harmony')
159
+ milestone.changed?.should be_true
160
+ milestone.changed.should == %w(project_id)
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,1219 @@
1
+ require 'test_helper'
2
+ require 'models'
3
+
4
+ class DocumentTest < Test::Unit::TestCase
5
+ def setup
6
+ @document = Doc do
7
+ set_collection_name 'users'
8
+
9
+ key :first_name, String
10
+ key :last_name, String
11
+ key :age, Integer
12
+ key :date, Date
13
+ end
14
+ end
15
+
16
+ context "array key" do
17
+ setup do
18
+ @document.key :tags, Array
19
+ end
20
+
21
+ should "give correct default" do
22
+ doc = @document.new
23
+ doc.tags.should == []
24
+ end
25
+
26
+ should "work with assignment" do
27
+ doc = @document.new
28
+ doc.tags = %w(foo bar)
29
+ doc.tags.should == %w(foo bar)
30
+ end
31
+
32
+ should "work with assignment after saving" do
33
+ doc = @document.new
34
+ doc.tags = %w(foo bar)
35
+ doc.save
36
+ doc.tags.should == %w(foo bar)
37
+ doc.reload.tags.should == %w(foo bar)
38
+ end
39
+
40
+ should "work with assignment then <<" do
41
+ doc = @document.new
42
+ doc.tags = []
43
+ doc.tags << "foo"
44
+ doc.tags.should == ["foo"]
45
+ end
46
+
47
+ should "work with <<" do
48
+ doc = @document.new
49
+ doc.tags << "foo"
50
+ doc.tags.should == ["foo"]
51
+ end
52
+
53
+ should "work with << then save" do
54
+ doc = @document.new
55
+ doc.tags << "foo"
56
+ doc.tags << "bar"
57
+ doc.save
58
+ doc.tags.should == %w(foo bar)
59
+ doc.reload.tags.should == %w(foo bar)
60
+ end
61
+ end
62
+
63
+ context "hash key" do
64
+ setup do
65
+ @document.key :foo, Hash
66
+ end
67
+
68
+ should "give correct default" do
69
+ doc = @document.new
70
+ doc.foo.should == {}
71
+ end
72
+
73
+ should "work with []=" do
74
+ doc = @document.new
75
+ doc.foo["quux"] = "bar"
76
+ doc.foo["quux"].should == "bar"
77
+ doc.foo.should == { "quux" => "bar" }
78
+ end
79
+
80
+ should "work with indifferent access" do
81
+ doc = @document.new
82
+ doc.foo = {:baz => 'bar'}
83
+ doc.foo[:baz].should == 'bar'
84
+ doc.foo['baz'].should == 'bar'
85
+ end
86
+
87
+ should "work with indifferent access after save" do
88
+ doc = @document.new
89
+ doc.foo = {:baz => 'bar'}
90
+ doc.save
91
+
92
+ doc = doc.reload
93
+ doc.foo[:baz].should == 'bar'
94
+ doc.foo['baz'].should == 'bar'
95
+ end
96
+ end
97
+
98
+ context "custom type key with default" do
99
+ setup do
100
+ @document.key :window, WindowSize, :default => WindowSize.new(600, 480)
101
+ end
102
+
103
+ should "default to default" do
104
+ doc = @document.new
105
+ doc.window.should == WindowSize.new(600, 480)
106
+
107
+ end
108
+
109
+ should "save and load from mongo" do
110
+ doc = @document.new
111
+ doc.save
112
+
113
+ doc = doc.reload
114
+ doc.window.should == WindowSize.new(600, 480)
115
+ end
116
+ end
117
+
118
+ context "key with proc default value" do
119
+ setup do
120
+ @document.key :proc_default, String, :default => lambda { return 'string' }
121
+ end
122
+
123
+ should "detect and run proc default" do
124
+ doc = @document.new
125
+ doc.proc_default.should == 'string'
126
+ end
127
+
128
+ should "save and load from mongo" do
129
+ doc = @document.create
130
+ doc = doc.reload
131
+ doc.proc_default.should == 'string'
132
+ end
133
+ end
134
+
135
+ context "ClassMethods#create (single document)" do
136
+ setup do
137
+ @doc_instance = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
138
+ end
139
+
140
+ should "create a document in correct collection" do
141
+ @document.count.should == 1
142
+ end
143
+
144
+ should "automatically set id" do
145
+ @doc_instance.id.should be_instance_of(BSON::ObjectID)
146
+ @doc_instance._id.should be_instance_of(BSON::ObjectID)
147
+ end
148
+
149
+ should "no longer be new?" do
150
+ @doc_instance.new?.should be_false
151
+ end
152
+
153
+ should "return instance of document" do
154
+ @doc_instance.should be_instance_of(@document)
155
+ @doc_instance.first_name.should == 'John'
156
+ @doc_instance.last_name.should == 'Nunemaker'
157
+ @doc_instance.age.should == 27
158
+ end
159
+
160
+ should "not fail if no attributes provided" do
161
+ document = Doc()
162
+ lambda { document.create }.should change { document.count }.by(1)
163
+ end
164
+ end
165
+
166
+ context "ClassMethods#create (multiple documents)" do
167
+ setup do
168
+ @doc_instances = @document.create([
169
+ {:first_name => 'John', :last_name => 'Nunemaker', :age => '27'},
170
+ {:first_name => 'Steve', :last_name => 'Smith', :age => '28'},
171
+ ])
172
+ end
173
+
174
+ should "create multiple documents" do
175
+ @document.count.should == 2
176
+ end
177
+
178
+ should "return an array of doc instances" do
179
+ @doc_instances.map do |doc_instance|
180
+ doc_instance.should be_instance_of(@document)
181
+ end
182
+ end
183
+ end
184
+
185
+ context "ClassMethods#update (single document)" do
186
+ setup do
187
+ doc = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
188
+ @doc_instance = @document.update(doc._id, {:age => 40})
189
+ end
190
+
191
+ should "update attributes provided" do
192
+ @doc_instance.age.should == 40
193
+ end
194
+
195
+ should "not update existing attributes that were not set to update" do
196
+ @doc_instance.first_name.should == 'John'
197
+ @doc_instance.last_name.should == 'Nunemaker'
198
+ end
199
+
200
+ should "not create new document" do
201
+ @document.count.should == 1
202
+ end
203
+
204
+ should "raise error if not provided id" do
205
+ doc = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
206
+ lambda { @document.update }.should raise_error(ArgumentError)
207
+ end
208
+
209
+ should "raise error if not provided attributes" do
210
+ doc = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
211
+ lambda { @document.update(doc._id) }.should raise_error(ArgumentError)
212
+ lambda { @document.update(doc._id, [1]) }.should raise_error(ArgumentError)
213
+ end
214
+ end
215
+
216
+ context "ClassMethods#update (multiple documents)" do
217
+ setup do
218
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
219
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
220
+
221
+ @doc_instances = @document.update({
222
+ @doc1._id => {:age => 30},
223
+ @doc2._id => {:age => 30},
224
+ })
225
+ end
226
+
227
+ should "not create any new documents" do
228
+ @document.count.should == 2
229
+ end
230
+
231
+ should "should return an array of doc instances" do
232
+ @doc_instances.map do |doc_instance|
233
+ doc_instance.should be_instance_of(@document)
234
+ end
235
+ end
236
+
237
+ should "update the documents" do
238
+ @document.find(@doc1._id).age.should == 30
239
+ @document.find(@doc2._id).age.should == 30
240
+ end
241
+
242
+ should "raise error if not a hash" do
243
+ lambda { @document.update([1, 2]) }.should raise_error(ArgumentError)
244
+ end
245
+ end
246
+
247
+ context "ClassMethods#find" do
248
+ setup do
249
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
250
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
251
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
252
+ end
253
+
254
+ should "return nil if nothing provided for find" do
255
+ @document.find.should be_nil
256
+ end
257
+
258
+ should "raise document not found if nothing provided for find!" do
259
+ assert_raises(MongoMapper::DocumentNotFound) do
260
+ @document.find!
261
+ end
262
+ end
263
+
264
+ should "raise error if trying to find with :all, :first, or :last" do
265
+ [:all, :first, :last].each do |m|
266
+ assert_raises(ArgumentError) { @document.find(m) }
267
+ end
268
+
269
+ [:all, :first, :last].each do |m|
270
+ assert_raises(ArgumentError) { @document.find!(m) }
271
+ end
272
+ end
273
+
274
+ context "(with a single id)" do
275
+ should "work" do
276
+ @document.find(@doc1._id).should == @doc1
277
+ end
278
+
279
+ should "return nil if document not found with find" do
280
+ @document.find(123).should be_nil
281
+ end
282
+
283
+ should "raise error if document not found with find!" do
284
+ assert_raises(MongoMapper::DocumentNotFound) { @document.find!(123) }
285
+ end
286
+ end
287
+
288
+ context "(with multiple id's)" do
289
+ should "work as arguments" do
290
+ @document.find(@doc1._id, @doc2._id).should == [@doc1, @doc2]
291
+ end
292
+
293
+ should "work as arguments with string ids" do
294
+ @document.find(@doc1._id.to_s, @doc2._id.to_s).should == [@doc1, @doc2]
295
+ end
296
+
297
+ should "work as array" do
298
+ @document.find([@doc1._id, @doc2._id]).should == [@doc1, @doc2]
299
+ end
300
+
301
+ should "work as array with string ids" do
302
+ @document.find([@doc1._id.to_s, @doc2._id.to_s]).should == [@doc1, @doc2]
303
+ end
304
+
305
+ should "compact not found when using find" do
306
+ @document.find(@doc1._id, BSON::ObjectID.new.to_s).should == [@doc1]
307
+ end
308
+
309
+ should "raise error if not all found when using find!" do
310
+ assert_raises(MongoMapper::DocumentNotFound) do
311
+ @document.find!(@doc1._id, BSON::ObjectID.new.to_s)
312
+ end
313
+ end
314
+
315
+ should "return array if array with one element" do
316
+ @document.find([@doc1._id]).should == [@doc1]
317
+ end
318
+ end
319
+
320
+ should "be able to find using condition auto-detection" do
321
+ @document.first(:first_name => 'John').should == @doc1
322
+ @document.all(:last_name => 'Nunemaker', :order => 'age desc').should == [@doc1, @doc3]
323
+ end
324
+
325
+ context "#all" do
326
+ should "find all documents based on criteria" do
327
+ @document.all(:order => 'first_name').should == [@doc1, @doc3, @doc2]
328
+ @document.all(:last_name => 'Nunemaker', :order => 'age desc').should == [@doc1, @doc3]
329
+ end
330
+ end
331
+
332
+ context "#first" do
333
+ should "find first document based on criteria" do
334
+ @document.first(:order => 'first_name').should == @doc1
335
+ @document.first(:age => 28).should == @doc2
336
+ end
337
+ end
338
+
339
+ context "#last" do
340
+ should "find last document based on criteria" do
341
+ @document.last(:order => 'age').should == @doc2
342
+ @document.last(:order => 'age', :age => 28).should == @doc2
343
+ end
344
+
345
+ should "raise error if no order provided" do
346
+ lambda { @document.last() }.should raise_error
347
+ end
348
+ end
349
+
350
+ context "#find_by..." do
351
+ should "find document based on argument" do
352
+ @document.find_by_first_name('John').should == @doc1
353
+ @document.find_by_last_name('Nunemaker', :order => 'age desc').should == @doc1
354
+ @document.find_by_age(27).should == @doc1
355
+ end
356
+
357
+ should "not raise error" do
358
+ @document.find_by_first_name('Mongo').should be_nil
359
+ end
360
+
361
+ should "define a method for each key" do
362
+ @document.methods(false).select { |e| e =~ /^find_by_/ }.size == @document.keys.size
363
+ end
364
+ end
365
+
366
+ context "#find_each" do
367
+ should "yield all documents found, based on criteria" do
368
+ yield_documents = []
369
+ @document.find_each(:order => "first_name") {|doc| yield_documents << doc }
370
+ yield_documents.should == [@doc1, @doc3, @doc2]
371
+
372
+ yield_documents = []
373
+ @document.find_each(:last_name => 'Nunemaker', :order => 'age desc') {|doc| yield_documents << doc }
374
+ yield_documents.should == [@doc1, @doc3]
375
+ end
376
+ end
377
+
378
+ context "dynamic finders" do
379
+ should "find document based on all arguments" do
380
+ @document.find_by_first_name_and_last_name_and_age('John', 'Nunemaker', 27).should == @doc1
381
+ end
382
+
383
+ should "not find the document if an argument is wrong" do
384
+ @document.find_by_first_name_and_last_name_and_age('John', 'Nunemaker', 28).should be_nil
385
+ end
386
+
387
+ should "find all documents based on arguments" do
388
+ docs = @document.find_all_by_last_name('Nunemaker')
389
+ docs.should be_kind_of(Array)
390
+ docs.should include(@doc1)
391
+ docs.should include(@doc3)
392
+ end
393
+
394
+ should "initialize document with given arguments" do
395
+ doc = @document.find_or_initialize_by_first_name_and_last_name('David', 'Cuadrado')
396
+ doc.should be_new
397
+ doc.first_name.should == 'David'
398
+ end
399
+
400
+ should "not initialize document if document is found" do
401
+ doc = @document.find_or_initialize_by_first_name('John')
402
+ doc.should_not be_new
403
+ end
404
+
405
+ should "create document with given arguments" do
406
+ doc = @document.find_or_create_by_first_name_and_last_name('David', 'Cuadrado')
407
+ doc.should_not be_new
408
+ doc.first_name.should == 'David'
409
+ end
410
+
411
+ should "raise error if document is not found when using !" do
412
+ lambda {
413
+ @document.find_by_first_name_and_last_name!(1,2)
414
+ }.should raise_error(MongoMapper::DocumentNotFound)
415
+ end
416
+ end
417
+ end # finding documents
418
+
419
+ context "ClassMethods#find_by_id" do
420
+ setup do
421
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
422
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
423
+ end
424
+
425
+ should "be able to find by id" do
426
+ @document.find_by_id(@doc1._id).should == @doc1
427
+ @document.find_by_id(@doc2._id).should == @doc2
428
+ end
429
+
430
+ should "return nil if document not found" do
431
+ @document.find_by_id(1234).should be_nil
432
+ end
433
+ end
434
+
435
+ context "first_or_create" do
436
+ should "find if exists" do
437
+ created = @document.create(:first_name => 'John', :last_name => 'Nunemaker')
438
+ lambda {
439
+ found = @document.first_or_create(:first_name => 'John', :last_name => 'Nunemaker')
440
+ found.should == created
441
+ }.should_not change { @document.count }
442
+ end
443
+
444
+ should "create if not found" do
445
+ lambda {
446
+ created = @document.first_or_create(:first_name => 'John', :last_name => 'Nunemaker')
447
+ created.first_name.should == 'John'
448
+ created.last_name.should == 'Nunemaker'
449
+ }.should change { @document.count }.by(1)
450
+ end
451
+
452
+ should "disregard non-keys when creating, but use them in the query" do
453
+ assert_nothing_raised do
454
+ @document.create(:first_name => 'John', :age => 9)
455
+ lambda {
456
+ @document.first_or_create(:first_name => 'John', :age.gt => 10).first_name.should == 'John'
457
+ }.should change { @document.count }.by(1)
458
+ end
459
+ end
460
+ end
461
+
462
+ context "first_or_new" do
463
+ should "find if exists" do
464
+ created = @document.create(:first_name => 'John', :last_name => 'Nunemaker')
465
+ lambda {
466
+ found = @document.first_or_new(:first_name => 'John', :last_name => 'Nunemaker')
467
+ found.should == created
468
+ }.should_not change { @document.count }
469
+ end
470
+
471
+ should "initialize if not found" do
472
+ lambda {
473
+ created = @document.first_or_new(:first_name => 'John', :last_name => 'Nunemaker')
474
+ created.first_name.should == 'John'
475
+ created.last_name.should == 'Nunemaker'
476
+ created.should be_new
477
+ }.should_not change { @document.count }
478
+ end
479
+
480
+ should "disregard non-keys when initializing, but use them in the query" do
481
+ assert_nothing_raised do
482
+ @document.create(:first_name => 'John', :age => 9)
483
+ @document.first_or_new(:first_name => 'John', :age.gt => 10).first_name.should == 'John'
484
+ end
485
+ end
486
+ end
487
+
488
+ context "ClassMethods#delete (single document)" do
489
+ setup do
490
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
491
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
492
+ @document.delete(@doc1._id)
493
+ end
494
+
495
+ should "remove document from collection" do
496
+ @document.count.should == 1
497
+ end
498
+
499
+ should "not remove other documents" do
500
+ @document.find(@doc2._id).should_not be(nil)
501
+ end
502
+ end
503
+
504
+ context "ClassMethods#delete (multiple documents)" do
505
+ should "work with multiple arguments" do
506
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
507
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
508
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
509
+ @document.delete(@doc1._id, @doc2._id)
510
+
511
+ @document.count.should == 1
512
+ end
513
+
514
+ should "work with array as argument" do
515
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
516
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
517
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
518
+ @document.delete([@doc1._id, @doc2._id])
519
+
520
+ @document.count.should == 1
521
+ end
522
+ end
523
+
524
+ context "ClassMethods#delete_all" do
525
+ setup do
526
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
527
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
528
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
529
+ end
530
+
531
+ should "remove all documents when given no conditions" do
532
+ @document.delete_all
533
+ @document.count.should == 0
534
+ end
535
+
536
+ should "only remove matching documents when given conditions" do
537
+ @document.delete_all({:first_name => 'John'})
538
+ @document.count.should == 2
539
+ end
540
+
541
+ should "convert the conditions to mongo criteria" do
542
+ @document.delete_all(:age => [26, 27])
543
+ @document.count.should == 1
544
+ end
545
+ end
546
+
547
+ context "ClassMethods#destroy (single document)" do
548
+ setup do
549
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
550
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
551
+ @document.destroy(@doc1._id)
552
+ end
553
+
554
+ should "remove document from collection" do
555
+ @document.count.should == 1
556
+ end
557
+
558
+ should "not remove other documents" do
559
+ @document.find(@doc2._id).should_not be(nil)
560
+ end
561
+ end
562
+
563
+ context "ClassMethods#destroy (multiple documents)" do
564
+ should "work with multiple arguments" do
565
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
566
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
567
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
568
+ @document.destroy(@doc1._id, @doc2._id)
569
+
570
+ @document.count.should == 1
571
+ end
572
+
573
+ should "work with array as argument" do
574
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
575
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
576
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
577
+ @document.destroy([@doc1._id, @doc2._id])
578
+
579
+ @document.count.should == 1
580
+ end
581
+ end
582
+
583
+ context "ClassMethods#destroy_all" do
584
+ setup do
585
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
586
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
587
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
588
+ end
589
+
590
+ should "remove all documents when given no conditions" do
591
+ @document.destroy_all
592
+ @document.count.should == 0
593
+ end
594
+
595
+ should "only remove matching documents when given conditions" do
596
+ @document.destroy_all(:first_name => 'John')
597
+ @document.count.should == 2
598
+ @document.destroy_all(:age => 26)
599
+ @document.count.should == 1
600
+ end
601
+
602
+ should "convert the conditions to mongo criteria" do
603
+ @document.destroy_all(:age => [26, 27])
604
+ @document.count.should == 1
605
+ end
606
+ end
607
+
608
+ context "ClassMethods#count" do
609
+ setup do
610
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
611
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
612
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
613
+ end
614
+
615
+ should "count all with no arguments" do
616
+ @document.count.should == 3
617
+ end
618
+
619
+ should "return 0 if there are no documents in the collection" do
620
+ @document.delete_all
621
+ @document.count.should == 0
622
+ end
623
+
624
+ should "return 0 if the collection does not exist" do
625
+ klass = Doc do
626
+ set_collection_name 'foobarbazwickdoesnotexist'
627
+ end
628
+
629
+ klass.count.should == 0
630
+ end
631
+
632
+ should "return count for matching documents if conditions provided" do
633
+ @document.count(:age => 27).should == 1
634
+ end
635
+
636
+ should "convert the conditions to mongo criteria" do
637
+ @document.count(:age => [26, 27]).should == 2
638
+ end
639
+ end
640
+
641
+ should "have instance method for collection" do
642
+ @document.new.collection.name.should == @document.collection.name
643
+ end
644
+
645
+ should "have instance method for database" do
646
+ @document.new.database.should == @document.database
647
+ end
648
+
649
+ context "#update_attributes (new document)" do
650
+ setup do
651
+ @doc = @document.new(:first_name => 'John', :age => '27')
652
+ @doc.update_attributes(:first_name => 'Johnny', :age => 30)
653
+ end
654
+
655
+ should "insert document into the collection" do
656
+ @document.count.should == 1
657
+ end
658
+
659
+ should "assign an id for the document" do
660
+ @doc.id.should be_instance_of(BSON::ObjectID)
661
+ end
662
+
663
+ should "save attributes" do
664
+ @doc.first_name.should == 'Johnny'
665
+ @doc.age.should == 30
666
+ end
667
+
668
+ should "update attributes in the database" do
669
+ doc = @doc.reload
670
+ doc.should == @doc
671
+ doc.first_name.should == 'Johnny'
672
+ doc.age.should == 30
673
+ end
674
+
675
+ should "allow updating custom attributes" do
676
+ @doc.update_attributes(:gender => 'mALe')
677
+ @doc.reload.gender.should == 'mALe'
678
+ end
679
+ end
680
+
681
+ context "#update_attributes (existing document)" do
682
+ setup do
683
+ @doc = @document.create(:first_name => 'John', :age => '27')
684
+ @doc.update_attributes(:first_name => 'Johnny', :age => 30)
685
+ end
686
+
687
+ should "not insert document into collection" do
688
+ @document.count.should == 1
689
+ end
690
+
691
+ should "update attributes" do
692
+ @doc.first_name.should == 'Johnny'
693
+ @doc.age.should == 30
694
+ end
695
+
696
+ should "update attributes in the database" do
697
+ doc = @doc.reload
698
+ doc.first_name.should == 'Johnny'
699
+ doc.age.should == 30
700
+ end
701
+ end
702
+
703
+ context "#update_attributes (return value)" do
704
+ setup do
705
+ @document.key :foo, String, :required => true
706
+ end
707
+
708
+ should "be true if document valid" do
709
+ @document.new.update_attributes(:foo => 'bar').should be_true
710
+ end
711
+
712
+ should "be false if document not valid" do
713
+ @document.new.update_attributes({}).should be_false
714
+ end
715
+ end
716
+
717
+ context "#save (new document)" do
718
+ setup do
719
+ @doc = @document.new(:first_name => 'John', :age => '27')
720
+ @doc.save
721
+ end
722
+
723
+ should "insert document into the collection" do
724
+ @document.count.should == 1
725
+ end
726
+
727
+ should "assign an id for the document" do
728
+ @doc.id.should be_instance_of(BSON::ObjectID)
729
+ end
730
+
731
+ should "save attributes" do
732
+ @doc.first_name.should == 'John'
733
+ @doc.age.should == 27
734
+ end
735
+
736
+ should "update attributes in the database" do
737
+ doc = @doc.reload
738
+ doc.should == @doc
739
+ doc.first_name.should == 'John'
740
+ doc.age.should == 27
741
+ end
742
+
743
+ should "allow to add custom attributes to the document" do
744
+ @doc = @document.new(:first_name => 'David', :age => '26', :gender => 'male', :tags => [1, "2"])
745
+ @doc.save
746
+ doc = @doc.reload
747
+ doc.gender.should == 'male'
748
+ doc.tags.should == [1, "2"]
749
+ end
750
+
751
+ should "allow to use custom methods to assign properties" do
752
+ klass = Doc do
753
+ key :name, String
754
+
755
+ def realname=(value)
756
+ self.name = value
757
+ end
758
+ end
759
+
760
+ person = klass.new(:realname => 'David')
761
+ person.save
762
+ person.reload.name.should == 'David'
763
+ end
764
+
765
+ context "with key of type date" do
766
+ should "save the date value as a Time object" do
767
+ doc = @document.new(:first_name => 'John', :age => '27', :date => "2009-12-01")
768
+ doc.save
769
+ doc.date.should == Date.new(2009, 12, 1)
770
+ end
771
+ end
772
+ end
773
+
774
+ context "#save (existing document)" do
775
+ setup do
776
+ @doc = @document.create(:first_name => 'John', :age => '27')
777
+ @doc.first_name = 'Johnny'
778
+ @doc.age = 30
779
+ @doc.save
780
+ end
781
+
782
+ should "not insert document into collection" do
783
+ @document.count.should == 1
784
+ end
785
+
786
+ should "update attributes" do
787
+ @doc.first_name.should == 'Johnny'
788
+ @doc.age.should == 30
789
+ end
790
+
791
+ should "update attributes in the database" do
792
+ doc = @doc.reload
793
+ doc.first_name.should == 'Johnny'
794
+ doc.age.should == 30
795
+ end
796
+
797
+ should "allow updating custom attributes" do
798
+ @doc = @document.new(:first_name => 'David', :age => '26', :gender => 'male')
799
+ @doc.gender = 'Male'
800
+ @doc.save
801
+ @doc.reload.gender.should == 'Male'
802
+ end
803
+ end
804
+
805
+ context "#save (with validations off)" do
806
+ setup do
807
+ @document = Doc do
808
+ key :name, String, :required => true
809
+ end
810
+ end
811
+
812
+ should "insert invalid document" do
813
+ doc = @document.new
814
+ doc.expects(:valid?).never
815
+ doc.save(:validate => false)
816
+ @document.count.should == 1
817
+ end
818
+ end
819
+
820
+ context "#save (with options)" do
821
+ setup do
822
+ @document = Doc do
823
+ key :name, String
824
+ set_collection_name 'test_indexes'
825
+ end
826
+ drop_indexes(@document)
827
+ @document.ensure_index :name, :unique => true
828
+ end
829
+
830
+ should "allow passing safe" do
831
+ @document.create(:name => 'John')
832
+ assert_raises(Mongo::OperationFailure) do
833
+ @document.new(:name => 'John').save(:safe => true)
834
+ end
835
+ end
836
+
837
+ should "raise argument error if options has unsupported key" do
838
+ assert_raises(ArgumentError) do
839
+ @document.new.save(:foo => true)
840
+ end
841
+ end
842
+ end
843
+
844
+ context "#save! (with options)" do
845
+ setup do
846
+ @document = Doc do
847
+ key :name, String
848
+ set_collection_name 'test_indexes'
849
+ end
850
+ drop_indexes(@document)
851
+ @document.ensure_index :name, :unique => true
852
+ end
853
+
854
+ should "allow passing safe" do
855
+ @document.create(:name => 'John')
856
+ assert_raises(Mongo::OperationFailure) do
857
+ @document.new(:name => 'John').save!(:safe => true)
858
+ end
859
+ end
860
+
861
+ should "raise argument error if options has unsupported key" do
862
+ assert_raises(ArgumentError) do
863
+ @document.new.save!(:foo => true)
864
+ end
865
+ end
866
+
867
+ should "raise argument error if using validate as that would be pointless with save!" do
868
+ assert_raises(ArgumentError) do
869
+ @document.new.save!(:validate => false)
870
+ end
871
+ end
872
+ end
873
+
874
+ context "#destroy" do
875
+ setup do
876
+ @doc = @document.create(:first_name => 'John', :age => '27')
877
+ @doc.destroy
878
+ end
879
+
880
+ should "remove the document from the collection" do
881
+ @document.count.should == 0
882
+ end
883
+ end
884
+
885
+ context "#delete" do
886
+ setup do
887
+ @doc1 = @document.create(:first_name => 'John', :last_name => 'Nunemaker', :age => '27')
888
+ @doc2 = @document.create(:first_name => 'Steve', :last_name => 'Smith', :age => '28')
889
+
890
+ @document.class_eval do
891
+ before_destroy :before_destroy_callback
892
+ after_destroy :after_destroy_callback
893
+
894
+ def history; @history ||= [] end
895
+ def before_destroy_callback; history << :after_destroy end
896
+ def after_destroy_callback; history << :after_destroy end
897
+ end
898
+
899
+ @doc1.delete
900
+ end
901
+
902
+ should "remove document from collection" do
903
+ @document.count.should == 1
904
+ end
905
+
906
+ should "not remove other documents" do
907
+ @document.find(@doc2.id).should_not be(nil)
908
+ end
909
+
910
+ should "not call before/after destroy callbacks" do
911
+ @doc1.history.should == []
912
+ end
913
+ end
914
+
915
+ context "#destroyed?" do
916
+ setup do
917
+ @doc1 = @document.create(:first_name => 'John', :last_name => 'Nunemaker', :age => '27')
918
+ end
919
+
920
+ should "be true if deleted" do
921
+ @doc1.delete
922
+ assert @doc1.destroyed?
923
+ end
924
+
925
+ should "be true if destroyed" do
926
+ @doc1.destroy
927
+ assert @doc1.destroyed?
928
+ end
929
+
930
+ should "be false if not deleted or destroyed" do
931
+ assert ! @doc1.destroyed?
932
+ end
933
+ end
934
+
935
+ context "#persisted?" do
936
+ setup do
937
+ @doc = @document.new(:first_name => 'John', :last_name => 'Nunemaker', :age => '27')
938
+ end
939
+
940
+ should "be false if new" do
941
+ @doc.should_not be_persisted
942
+ end
943
+
944
+ should "be false if destroyed" do
945
+ @doc.save
946
+ @doc.destroy
947
+ @doc.should be_destroyed
948
+ @doc.should_not be_persisted
949
+ end
950
+
951
+ should "be true if not new or destroyed" do
952
+ @doc.save
953
+ @doc.should be_persisted
954
+ end
955
+ end
956
+
957
+ context "Single collection inheritance" do
958
+ setup do
959
+ class ::DocParent
960
+ include MongoMapper::Document
961
+ key :name, String
962
+ end
963
+ DocParent.collection.remove
964
+
965
+ class ::DocDaughter < ::DocParent; end
966
+ class ::DocSon < ::DocParent; end
967
+ class ::DocGrandSon < ::DocSon; end
968
+
969
+ DocSon.many :children, :class_name => 'DocGrandSon'
970
+
971
+ @parent = DocParent.new({:name => "Daddy Warbucks"})
972
+ @daughter = DocDaughter.new({:name => "Little Orphan Annie"})
973
+ end
974
+
975
+ teardown do
976
+ Object.send :remove_const, 'DocParent' if defined?(::DocParent)
977
+ Object.send :remove_const, 'DocDaughter' if defined?(::DocDaughter)
978
+ Object.send :remove_const, 'DocSon' if defined?(::DocSon)
979
+ Object.send :remove_const, 'DocGrandSon' if defined?(::DocGrandSon)
980
+ end
981
+
982
+ should "automatically add _type key to store class" do
983
+ DocParent.keys.should include(:_type)
984
+ end
985
+
986
+ should "use the same collection in the subclass" do
987
+ DocDaughter.collection.name.should == DocParent.collection.name
988
+ end
989
+
990
+ should "assign the class name into the _type property" do
991
+ @parent._type.should == 'DocParent'
992
+ @daughter._type.should == 'DocDaughter'
993
+ end
994
+
995
+ should "load the document with the assigned type" do
996
+ @parent.save
997
+ @daughter.save
998
+
999
+ collection = DocParent.all
1000
+ collection.size.should == 2
1001
+ collection.first.should be_kind_of(DocParent)
1002
+ collection.first.name.should == "Daddy Warbucks"
1003
+ collection.last.should be_kind_of(DocDaughter)
1004
+ collection.last.name.should == "Little Orphan Annie"
1005
+ end
1006
+
1007
+ should "gracefully handle when the type can't be constantized" do
1008
+ doc = DocParent.new(:name => 'Nunes')
1009
+ doc._type = 'FoobarBaz'
1010
+ doc.save
1011
+
1012
+ collection = DocParent.all
1013
+ collection.last.should == doc
1014
+ collection.last.should be_kind_of(DocParent)
1015
+ end
1016
+
1017
+ should "find scoped to class" do
1018
+ john = DocSon.create(:name => 'John')
1019
+ steve = DocSon.create(:name => 'Steve')
1020
+ steph = DocDaughter.create(:name => 'Steph')
1021
+ carrie = DocDaughter.create(:name => 'Carrie')
1022
+
1023
+ DocGrandSon.all(:order => 'name').should == []
1024
+ DocSon.all(:order => 'name').should == [john, steve]
1025
+ DocDaughter.all(:order => 'name').should == [carrie, steph]
1026
+ DocParent.all(:order => 'name').should == [carrie, john, steph, steve]
1027
+ end
1028
+
1029
+ should "work with nested hash conditions" do
1030
+ john = DocSon.create(:name => 'John')
1031
+ steve = DocSon.create(:name => 'Steve')
1032
+ DocSon.all(:name => {'$ne' => 'Steve'}).should == [john]
1033
+ end
1034
+
1035
+ should "raise error if not found scoped to class" do
1036
+ john = DocSon.create(:name => 'John')
1037
+ steph = DocDaughter.create(:name => 'Steph')
1038
+
1039
+ lambda {
1040
+ DocSon.find!(steph._id)
1041
+ }.should raise_error(MongoMapper::DocumentNotFound)
1042
+ end
1043
+
1044
+ should "not raise error for find with parent" do
1045
+ john = DocSon.create(:name => 'John')
1046
+
1047
+ DocParent.find!(john._id).should == john
1048
+ end
1049
+
1050
+ should "count scoped to class" do
1051
+ john = DocSon.create(:name => 'John')
1052
+ steve = DocSon.create(:name => 'Steve')
1053
+ steph = DocDaughter.create(:name => 'Steph')
1054
+ carrie = DocDaughter.create(:name => 'Carrie')
1055
+
1056
+ DocGrandSon.count.should == 0
1057
+ DocSon.count.should == 2
1058
+ DocDaughter.count.should == 2
1059
+ DocParent.count.should == 4
1060
+ end
1061
+
1062
+ should "know if it is single_collection_inherited?" do
1063
+ DocParent.single_collection_inherited?.should be_false
1064
+
1065
+ DocDaughter.single_collection_inherited?.should be_true
1066
+ DocSon.single_collection_inherited?.should be_true
1067
+ end
1068
+
1069
+ should "know if single_collection_inherited_superclass?" do
1070
+ DocParent.single_collection_inherited_superclass?.should be_false
1071
+
1072
+ DocDaughter.single_collection_inherited_superclass?.should be_true
1073
+ DocSon.single_collection_inherited_superclass?.should be_true
1074
+ DocGrandSon.single_collection_inherited_superclass?.should be_true
1075
+ end
1076
+
1077
+ should "not be able to destroy each other" do
1078
+ john = DocSon.create(:name => 'John')
1079
+ steph = DocDaughter.create(:name => 'Steph')
1080
+
1081
+ lambda {
1082
+ DocSon.destroy(steph._id)
1083
+ }.should raise_error(MongoMapper::DocumentNotFound)
1084
+ end
1085
+
1086
+ should "not be able to delete each other" do
1087
+ john = DocSon.create(:name => 'John')
1088
+ steph = DocDaughter.create(:name => 'Steph')
1089
+
1090
+ lambda {
1091
+ DocSon.delete(steph._id)
1092
+ }.should_not change { DocParent.count }
1093
+ end
1094
+
1095
+ should "be able to destroy using parent" do
1096
+ john = DocSon.create(:name => 'John')
1097
+ steph = DocDaughter.create(:name => 'Steph')
1098
+
1099
+ lambda {
1100
+ DocParent.destroy_all
1101
+ }.should change { DocParent.count }.by(-2)
1102
+ end
1103
+
1104
+ should "be able to delete using parent" do
1105
+ john = DocSon.create(:name => 'John')
1106
+ steph = DocDaughter.create(:name => 'Steph')
1107
+
1108
+ lambda {
1109
+ DocParent.delete_all
1110
+ }.should change { DocParent.count }.by(-2)
1111
+ end
1112
+
1113
+ should "set type from class and ignore _type in attributes" do
1114
+ doc = DocSon.create(:_type => 'DocDaughter', :name => 'John')
1115
+ DocParent.first.should be_instance_of(DocSon)
1116
+ end
1117
+
1118
+ should "be able to reload parent inherited class" do
1119
+ brian = DocParent.create(:name => 'Brian')
1120
+ brian.name = 'B-Dawg'
1121
+ brian.reload
1122
+ brian.name.should == 'Brian'
1123
+ end
1124
+ end
1125
+
1126
+ context "#exists?" do
1127
+ setup do
1128
+ @doc = @document.create(:first_name => "James", :age => 27)
1129
+ end
1130
+
1131
+ should "be true when at least one document exists" do
1132
+ @document.exists?.should == true
1133
+ end
1134
+
1135
+ should "be false when no documents exist" do
1136
+ @doc.destroy
1137
+ @document.exists?.should == false
1138
+ end
1139
+
1140
+ should "be true when at least one document exists that matches the conditions" do
1141
+ @document.exists?(:first_name => "James").should == true
1142
+ end
1143
+
1144
+ should "be false when no documents exist with the provided conditions" do
1145
+ @document.exists?(:first_name => "Jean").should == false
1146
+ end
1147
+ end
1148
+
1149
+ context "#reload" do
1150
+ setup do
1151
+ @foo_class = Doc do
1152
+ key :name
1153
+ end
1154
+
1155
+ @bar_class = EDoc do
1156
+ key :name
1157
+ end
1158
+
1159
+ @document.many :foos, :class => @foo_class
1160
+ @document.many :bars, :class => @bar_class
1161
+
1162
+ @instance = @document.create({
1163
+ :age => 39,
1164
+ :foos => [@foo_class.new(:name => '1')],
1165
+ :bars => [@bar_class.new(:name => '1')],
1166
+ })
1167
+ end
1168
+
1169
+ should "reload keys from the database" do
1170
+ @instance.age = 37
1171
+ @instance.age.should == 37
1172
+ @instance.reload
1173
+ @instance.age.should == 39
1174
+ end
1175
+
1176
+ should "reset all associations" do
1177
+ @instance.foos.expects(:reset).at_least_once
1178
+ @instance.bars.expects(:reset).at_least_once
1179
+ @instance.reload
1180
+ end
1181
+
1182
+ should "reinstantiate embedded associations" do
1183
+ @instance.reload
1184
+ @instance.bars.first.name.should == '1'
1185
+ end
1186
+
1187
+ should "return self" do
1188
+ @instance.reload.object_id.should == @instance.object_id
1189
+ end
1190
+
1191
+ should "raise DocumentNotFound if not found" do
1192
+ @instance.destroy
1193
+ assert_raises(MongoMapper::DocumentNotFound) { @instance.reload }
1194
+ end
1195
+ end
1196
+
1197
+ context "database has keys not defined in model" do
1198
+ setup do
1199
+ @id = BSON::ObjectID.new
1200
+ @document.collection.insert({
1201
+ :_id => @id,
1202
+ :first_name => 'John',
1203
+ :last_name => 'Nunemaker',
1204
+ :age => 27,
1205
+ :favorite_color => 'red',
1206
+ :skills => ['ruby', 'rails', 'javascript', 'xhtml', 'css']
1207
+ })
1208
+ end
1209
+
1210
+ should "assign all keys from database" do
1211
+ doc = @document.find(@id)
1212
+ doc.first_name.should == 'John'
1213
+ doc.last_name.should == 'Nunemaker'
1214
+ doc.age.should == 27
1215
+ doc.favorite_color.should == 'red'
1216
+ doc.skills.should == ['ruby', 'rails', 'javascript', 'xhtml', 'css']
1217
+ end
1218
+ end
1219
+ end