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.
- data/.document +5 -0
- data/.gitignore +7 -0
- data/LICENSE +20 -0
- data/README.textile +174 -0
- data/Rakefile +135 -0
- data/TODO +31 -0
- data/VERSION +1 -0
- data/data/.gitignore +2 -0
- data/examples/simple_document.rb +35 -0
- data/examples/simple_object.rb +30 -0
- data/features/finders.feature +76 -0
- data/features/mongodb.yml +7 -0
- data/features/mongodoc_base.feature +128 -0
- data/features/new_record.feature +36 -0
- data/features/partial_updates.feature +105 -0
- data/features/removing_documents.feature +68 -0
- data/features/saving_an_object.feature +15 -0
- data/features/scopes.feature +66 -0
- data/features/step_definitions/collection_steps.rb +14 -0
- data/features/step_definitions/document_steps.rb +149 -0
- data/features/step_definitions/documents.rb +30 -0
- data/features/step_definitions/finder_steps.rb +15 -0
- data/features/step_definitions/json_steps.rb +9 -0
- data/features/step_definitions/object_steps.rb +50 -0
- data/features/step_definitions/objects.rb +24 -0
- data/features/step_definitions/partial_update_steps.rb +32 -0
- data/features/step_definitions/query_steps.rb +54 -0
- data/features/step_definitions/removing_documents_steps.rb +14 -0
- data/features/step_definitions/scope_steps.rb +18 -0
- data/features/step_definitions/util_steps.rb +7 -0
- data/features/support/support.rb +10 -0
- data/features/using_criteria.feature +128 -0
- data/lib/mongo_doc/associations/collection_proxy.rb +105 -0
- data/lib/mongo_doc/associations/document_proxy.rb +56 -0
- data/lib/mongo_doc/associations/hash_proxy.rb +98 -0
- data/lib/mongo_doc/associations/proxy_base.rb +53 -0
- data/lib/mongo_doc/attributes.rb +140 -0
- data/lib/mongo_doc/bson.rb +45 -0
- data/lib/mongo_doc/collection.rb +55 -0
- data/lib/mongo_doc/connection.rb +88 -0
- data/lib/mongo_doc/contexts/enumerable.rb +128 -0
- data/lib/mongo_doc/contexts/ids.rb +41 -0
- data/lib/mongo_doc/contexts/mongo.rb +232 -0
- data/lib/mongo_doc/contexts.rb +25 -0
- data/lib/mongo_doc/criteria.rb +38 -0
- data/lib/mongo_doc/cursor.rb +32 -0
- data/lib/mongo_doc/document.rb +216 -0
- data/lib/mongo_doc/ext/array.rb +5 -0
- data/lib/mongo_doc/ext/binary.rb +7 -0
- data/lib/mongo_doc/ext/boolean_class.rb +11 -0
- data/lib/mongo_doc/ext/date.rb +16 -0
- data/lib/mongo_doc/ext/date_time.rb +13 -0
- data/lib/mongo_doc/ext/dbref.rb +7 -0
- data/lib/mongo_doc/ext/hash.rb +7 -0
- data/lib/mongo_doc/ext/nil_class.rb +5 -0
- data/lib/mongo_doc/ext/numeric.rb +17 -0
- data/lib/mongo_doc/ext/object.rb +17 -0
- data/lib/mongo_doc/ext/object_id.rb +7 -0
- data/lib/mongo_doc/ext/regexp.rb +5 -0
- data/lib/mongo_doc/ext/string.rb +5 -0
- data/lib/mongo_doc/ext/symbol.rb +5 -0
- data/lib/mongo_doc/ext/time.rb +5 -0
- data/lib/mongo_doc/finders.rb +49 -0
- data/lib/mongo_doc/matchers.rb +35 -0
- data/lib/mongo_doc/query.rb +7 -0
- data/lib/mongo_doc/scope.rb +64 -0
- data/lib/mongo_doc/validations/macros.rb +11 -0
- data/lib/mongo_doc/validations/validates_embedded.rb +13 -0
- data/lib/mongo_doc.rb +19 -0
- data/lib/mongoid/contexts/paging.rb +42 -0
- data/lib/mongoid/criteria.rb +247 -0
- data/lib/mongoid/criterion/complex.rb +21 -0
- data/lib/mongoid/criterion/exclusion.rb +65 -0
- data/lib/mongoid/criterion/inclusion.rb +92 -0
- data/lib/mongoid/criterion/optional.rb +136 -0
- data/lib/mongoid/extensions/hash/criteria_helpers.rb +20 -0
- data/lib/mongoid/extensions/symbol/inflections.rb +36 -0
- data/lib/mongoid/matchers/all.rb +11 -0
- data/lib/mongoid/matchers/default.rb +26 -0
- data/lib/mongoid/matchers/exists.rb +13 -0
- data/lib/mongoid/matchers/gt.rb +11 -0
- data/lib/mongoid/matchers/gte.rb +11 -0
- data/lib/mongoid/matchers/in.rb +11 -0
- data/lib/mongoid/matchers/lt.rb +11 -0
- data/lib/mongoid/matchers/lte.rb +11 -0
- data/lib/mongoid/matchers/ne.rb +11 -0
- data/lib/mongoid/matchers/nin.rb +11 -0
- data/lib/mongoid/matchers/size.rb +11 -0
- data/mongo_doc.gemspec +205 -0
- data/mongod.example.yml +2 -0
- data/mongodb.example.yml +14 -0
- data/perf/mongo_doc_runner.rb +90 -0
- data/perf/ruby_driver_runner.rb +64 -0
- data/script/console +8 -0
- data/spec/associations/collection_proxy_spec.rb +200 -0
- data/spec/associations/document_proxy_spec.rb +42 -0
- data/spec/associations/hash_proxy_spec.rb +163 -0
- data/spec/attributes_spec.rb +273 -0
- data/spec/bson_matchers.rb +54 -0
- data/spec/bson_spec.rb +196 -0
- data/spec/collection_spec.rb +161 -0
- data/spec/connection_spec.rb +147 -0
- data/spec/contexts/enumerable_spec.rb +274 -0
- data/spec/contexts/ids_spec.rb +49 -0
- data/spec/contexts/mongo_spec.rb +198 -0
- data/spec/contexts_spec.rb +28 -0
- data/spec/criteria_spec.rb +33 -0
- data/spec/cursor_spec.rb +91 -0
- data/spec/document_ext.rb +9 -0
- data/spec/document_spec.rb +664 -0
- data/spec/embedded_save_spec.rb +109 -0
- data/spec/finders_spec.rb +73 -0
- data/spec/hash_matchers.rb +27 -0
- data/spec/matchers_spec.rb +342 -0
- data/spec/mongodb.yml +6 -0
- data/spec/mongodb_pairs.yml +8 -0
- data/spec/new_record_spec.rb +128 -0
- data/spec/query_spec.rb +12 -0
- data/spec/scope_spec.rb +79 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +13 -0
- 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
|