crnixon-mongomapper 0.2.0 → 0.3.4

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 (68) hide show
  1. data/.gitignore +1 -0
  2. data/History +48 -0
  3. data/README.rdoc +5 -3
  4. data/Rakefile +6 -4
  5. data/VERSION +1 -1
  6. data/bin/mmconsole +56 -0
  7. data/lib/mongomapper.rb +29 -18
  8. data/lib/mongomapper/associations.rb +53 -38
  9. data/lib/mongomapper/associations/base.rb +53 -20
  10. data/lib/mongomapper/associations/belongs_to_polymorphic_proxy.rb +34 -0
  11. data/lib/mongomapper/associations/belongs_to_proxy.rb +10 -14
  12. data/lib/mongomapper/associations/many_documents_as_proxy.rb +27 -0
  13. data/lib/mongomapper/associations/many_documents_proxy.rb +103 -0
  14. data/lib/mongomapper/associations/many_embedded_polymorphic_proxy.rb +33 -0
  15. data/lib/mongomapper/associations/{has_many_embedded_proxy.rb → many_embedded_proxy.rb} +6 -8
  16. data/lib/mongomapper/associations/many_polymorphic_proxy.rb +11 -0
  17. data/lib/mongomapper/associations/{array_proxy.rb → many_proxy.rb} +1 -1
  18. data/lib/mongomapper/associations/proxy.rb +24 -21
  19. data/lib/mongomapper/callbacks.rb +1 -1
  20. data/lib/mongomapper/document.rb +160 -74
  21. data/lib/mongomapper/dynamic_finder.rb +38 -0
  22. data/lib/mongomapper/embedded_document.rb +154 -105
  23. data/lib/mongomapper/finder_options.rb +11 -7
  24. data/lib/mongomapper/key.rb +15 -21
  25. data/lib/mongomapper/pagination.rb +52 -0
  26. data/lib/mongomapper/rails_compatibility/document.rb +15 -0
  27. data/lib/mongomapper/rails_compatibility/embedded_document.rb +25 -0
  28. data/lib/mongomapper/serialization.rb +1 -1
  29. data/lib/mongomapper/serializers/json_serializer.rb +15 -0
  30. data/lib/mongomapper/support.rb +30 -0
  31. data/mongomapper.gemspec +87 -46
  32. data/test/NOTE_ON_TESTING +1 -0
  33. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +53 -0
  34. data/test/functional/associations/test_belongs_to_proxy.rb +45 -0
  35. data/test/functional/associations/test_many_documents_as_proxy.rb +253 -0
  36. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +131 -0
  37. data/test/functional/associations/test_many_embedded_proxy.rb +106 -0
  38. data/test/functional/associations/test_many_polymorphic_proxy.rb +261 -0
  39. data/test/functional/associations/test_many_proxy.rb +295 -0
  40. data/test/functional/test_associations.rb +47 -0
  41. data/test/{test_callbacks.rb → functional/test_callbacks.rb} +2 -1
  42. data/test/functional/test_document.rb +952 -0
  43. data/test/functional/test_pagination.rb +81 -0
  44. data/test/functional/test_rails_compatibility.rb +30 -0
  45. data/test/functional/test_validations.rb +172 -0
  46. data/test/models.rb +169 -0
  47. data/test/test_helper.rb +7 -2
  48. data/test/unit/serializers/test_json_serializer.rb +189 -0
  49. data/test/unit/test_association_base.rb +144 -0
  50. data/test/unit/test_document.rb +123 -0
  51. data/test/unit/test_embedded_document.rb +526 -0
  52. data/test/{test_finder_options.rb → unit/test_finder_options.rb} +36 -1
  53. data/test/{test_key.rb → unit/test_key.rb} +59 -12
  54. data/test/{test_mongomapper.rb → unit/test_mongomapper.rb} +0 -0
  55. data/test/{test_observing.rb → unit/test_observing.rb} +0 -0
  56. data/test/unit/test_pagination.rb +113 -0
  57. data/test/unit/test_rails_compatibility.rb +34 -0
  58. data/test/{test_serializations.rb → unit/test_serializations.rb} +0 -2
  59. data/test/{test_validations.rb → unit/test_validations.rb} +0 -134
  60. metadata +81 -43
  61. data/lib/mongomapper/associations/has_many_proxy.rb +0 -28
  62. data/lib/mongomapper/associations/polymorphic_belongs_to_proxy.rb +0 -31
  63. data/lib/mongomapper/rails_compatibility.rb +0 -23
  64. data/test/serializers/test_json_serializer.rb +0 -104
  65. data/test/test_associations.rb +0 -174
  66. data/test/test_document.rb +0 -944
  67. data/test/test_embedded_document.rb +0 -253
  68. data/test/test_rails_compatibility.rb +0 -29
@@ -0,0 +1,526 @@
1
+ require 'test_helper'
2
+
3
+ class Grandparent
4
+ include MongoMapper::EmbeddedDocument
5
+ key :grandparent, String
6
+ end
7
+
8
+ class Parent < Grandparent
9
+ include MongoMapper::EmbeddedDocument
10
+ key :parent, String
11
+ end
12
+
13
+ class Child < Parent
14
+ include MongoMapper::EmbeddedDocument
15
+ key :child, String
16
+ end
17
+
18
+ module KeyOverride
19
+ def other_child
20
+ read_attribute(:other_child) || "special result"
21
+ end
22
+
23
+ def other_child=(value)
24
+ super(value + " modified")
25
+ end
26
+ end
27
+
28
+ class OtherChild < Parent
29
+ include MongoMapper::EmbeddedDocument
30
+ include KeyOverride
31
+
32
+ key :other_child, String
33
+ end
34
+
35
+ class EmbeddedDocumentTest < Test::Unit::TestCase
36
+ context "Including MongoMapper::EmbeddedDocument" do
37
+ setup do
38
+ @klass = Class.new do
39
+ include MongoMapper::EmbeddedDocument
40
+ end
41
+ end
42
+
43
+ should "add _id key" do
44
+ @klass.keys['_id'].should_not be_nil
45
+ end
46
+ end
47
+
48
+ context "parent_model" do
49
+ should "be nil if none of parents ancestors include EmbeddedDocument" do
50
+ parent = Class.new
51
+ document = Class.new(parent) do
52
+ include MongoMapper::EmbeddedDocument
53
+ end
54
+ document.parent_model.should be_nil
55
+ end
56
+
57
+ should "work when other modules have been included" do
58
+ grandparent = Class.new
59
+ parent = Class.new grandparent do
60
+ include MongoMapper::EmbeddedDocument
61
+ end
62
+
63
+ example_module = Module.new
64
+ document = Class.new(parent) do
65
+ include MongoMapper::EmbeddedDocument
66
+ include example_module
67
+ end
68
+
69
+ document.parent_model.should == parent
70
+ end
71
+
72
+ should "find parent" do
73
+ Parent.parent_model.should == Grandparent
74
+ Child.parent_model.should == Parent
75
+ end
76
+ end
77
+
78
+ context "defining a key" do
79
+ setup do
80
+ @document = Class.new do
81
+ include MongoMapper::EmbeddedDocument
82
+ end
83
+ end
84
+
85
+ should "work with name" do
86
+ key = @document.key(:name)
87
+ key.name.should == 'name'
88
+ end
89
+
90
+ should "work with name and type" do
91
+ key = @document.key(:name, String)
92
+ key.name.should == 'name'
93
+ key.type.should == String
94
+ end
95
+
96
+ should "work with name, type and options" do
97
+ key = @document.key(:name, String, :required => true)
98
+ key.name.should == 'name'
99
+ key.type.should == String
100
+ key.options[:required].should be_true
101
+ end
102
+
103
+ should "work with name and options" do
104
+ key = @document.key(:name, :required => true)
105
+ key.name.should == 'name'
106
+ key.options[:required].should be_true
107
+ end
108
+
109
+ should "be tracked per document" do
110
+ @document.key(:name, String)
111
+ @document.key(:age, Integer)
112
+ @document.keys['name'].name.should == 'name'
113
+ @document.keys['name'].type.should == String
114
+ @document.keys['age'].name.should == 'age'
115
+ @document.keys['age'].type.should == Integer
116
+ end
117
+
118
+ should "not be redefinable" do
119
+ @document.key(:foo, String)
120
+ @document.keys['foo'].type.should == String
121
+ @document.key(:foo, Integer)
122
+ @document.keys['foo'].type.should == String
123
+ end
124
+
125
+ should "create reader method" do
126
+ @document.new.should_not respond_to(:foo)
127
+ @document.key(:foo, String)
128
+ @document.new.should respond_to(:foo)
129
+ end
130
+
131
+ should "create reader before typecast method" do
132
+ @document.new.should_not respond_to(:foo_before_typecast)
133
+ @document.key(:foo, String)
134
+ @document.new.should respond_to(:foo_before_typecast)
135
+ end
136
+
137
+ should "create writer method" do
138
+ @document.new.should_not respond_to(:foo=)
139
+ @document.key(:foo, String)
140
+ @document.new.should respond_to(:foo=)
141
+ end
142
+
143
+ should "create boolean method" do
144
+ @document.new.should_not respond_to(:foo?)
145
+ @document.key(:foo, String)
146
+ @document.new.should respond_to(:foo?)
147
+ end
148
+ end
149
+
150
+ context "keys" do
151
+ should "be inherited" do
152
+ Grandparent.keys.keys.sort.should == ['_id', 'grandparent']
153
+ Parent.keys.keys.sort.should == ['_id', 'grandparent', 'parent']
154
+ Child.keys.keys.sort.should == ['_id', 'child', 'grandparent', 'parent']
155
+ end
156
+
157
+ should "propogate to subclasses if key added after class definition" do
158
+ Grandparent.key :_type, String
159
+
160
+ Grandparent.keys.keys.sort.should == ['_id', '_type', 'grandparent']
161
+ Parent.keys.keys.sort.should == ['_id', '_type', 'grandparent', 'parent']
162
+ Child.keys.keys.sort.should == ['_id', '_type', 'child', 'grandparent', 'parent']
163
+ end
164
+
165
+ should "not add anonymous objects to the ancestor tree" do
166
+ OtherChild.ancestors.any? { |a| a.name.blank? }.should be_false
167
+ end
168
+
169
+ should "not include descendant keys" do
170
+ lambda { Parent.new.other_child }.should raise_error
171
+ end
172
+ end
173
+
174
+ context "subclasses" do
175
+ should "default to nil" do
176
+ Child.subclasses.should be_nil
177
+ end
178
+
179
+ should "be recorded" do
180
+ Grandparent.subclasses.should == [Parent]
181
+ Parent.subclasses.should == [Child, OtherChild]
182
+ end
183
+ end
184
+
185
+ context "Applying default values for keys" do
186
+ setup do
187
+ @document = Class.new do
188
+ include MongoMapper::EmbeddedDocument
189
+
190
+ key :name, String, :default => 'foo'
191
+ key :age, Integer, :default => 20
192
+ key :net_worth, Float, :default => 100.00
193
+ key :active, Boolean, :default => true
194
+ key :smart, Boolean, :default => false
195
+ key :skills, Array, :default => [1]
196
+ key :options, Hash, :default => {'foo' => 'bar'}
197
+ end
198
+
199
+ @doc = @document.new
200
+ end
201
+
202
+ should "work for strings" do
203
+ @doc.name.should == 'foo'
204
+ end
205
+
206
+ should "work for integers" do
207
+ @doc.age.should == 20
208
+ end
209
+
210
+ should "work for floats" do
211
+ @doc.net_worth.should == 100.00
212
+ end
213
+
214
+ should "work for booleans" do
215
+ @doc.active.should == true
216
+ @doc.smart.should == false
217
+ end
218
+
219
+ should "work for arrays" do
220
+ @doc.skills.should == [1]
221
+ @doc.skills << 2
222
+ @doc.skills.should == [1, 2]
223
+ end
224
+
225
+ should "work for hashes" do
226
+ @doc.options['foo'].should == 'bar'
227
+ @doc.options['baz'] = 'wick'
228
+ @doc.options['baz'].should == 'wick'
229
+ end
230
+ end
231
+
232
+ context "An instance of an embedded document" do
233
+ setup do
234
+ @document = Class.new do
235
+ include MongoMapper::EmbeddedDocument
236
+
237
+ key :name, String
238
+ key :age, Integer
239
+ end
240
+ end
241
+
242
+ should "automatically have an _id key" do
243
+ @document.keys.keys.should include('_id')
244
+ end
245
+
246
+ should "have id method that sets _id" do
247
+ doc = @document.new
248
+ doc.id.should == doc._id.to_s
249
+ end
250
+
251
+ context "setting custom id" do
252
+ should "set _id" do
253
+ doc = @document.new(:id => '1234')
254
+ doc._id.should == '1234'
255
+ end
256
+
257
+ should "know that custom id is set" do
258
+ doc = @document.new
259
+ doc.using_custom_id?.should be_false
260
+ doc.id = '1234'
261
+ doc.using_custom_id?.should be_true
262
+ end
263
+ end
264
+
265
+ context "being initialized" do
266
+ should "accept a hash that sets keys and values" do
267
+ doc = @document.new(:name => 'John', :age => 23)
268
+ doc.attributes.keys.sort.should == ['_id', 'age', 'name']
269
+ doc.attributes['name'].should == 'John'
270
+ doc.attributes['age'].should == 23
271
+ end
272
+
273
+ should "be able to assign keys dynamically" do
274
+ doc = @document.new(:name => 'John', :skills => ['ruby', 'rails'])
275
+ doc.name.should == 'John'
276
+ doc.skills.should == ['ruby', 'rails']
277
+ end
278
+
279
+ should "not throw error if initialized with nil" do
280
+ lambda {
281
+ @document.new(nil)
282
+ }.should_not raise_error
283
+ end
284
+ end
285
+
286
+ context "mass assigning keys" do
287
+ should "update values for keys provided" do
288
+ doc = @document.new(:name => 'foobar', :age => 10)
289
+ doc.attributes = {:name => 'new value', :age => 5}
290
+ doc.attributes[:name].should == 'new value'
291
+ doc.attributes[:age].should == 5
292
+ end
293
+
294
+ should "not update values for keys that were not provided" do
295
+ doc = @document.new(:name => 'foobar', :age => 10)
296
+ doc.attributes = {:name => 'new value'}
297
+ doc.attributes[:name].should == 'new value'
298
+ doc.attributes[:age].should == 10
299
+ end
300
+
301
+ should "not ignore keys that have methods defined" do
302
+ @document.class_eval do
303
+ attr_writer :password
304
+
305
+ def passwd
306
+ @password
307
+ end
308
+ end
309
+
310
+ doc = @document.new(:name => 'foobar', :password => 'secret')
311
+ doc.passwd.should == 'secret'
312
+ end
313
+
314
+ should "typecast key values" do
315
+ doc = @document.new(:name => 1234, :age => '21')
316
+ doc.name.should == '1234'
317
+ doc.age.should == 21
318
+ end
319
+ end
320
+
321
+ context "attributes" do
322
+ should "default to hash with _id" do
323
+ doc = @document.new
324
+ doc.attributes.keys.should == ['_id']
325
+ end
326
+
327
+ should "return all keys that aren't nil" do
328
+ doc = @document.new(:name => 'string', :age => nil)
329
+ doc.attributes.keys.sort.should == ['_id', 'name']
330
+ doc.attributes.values.should include('string')
331
+ end
332
+ end
333
+
334
+ context "key shorcut access" do
335
+ should "be able to read key with []" do
336
+ doc = @document.new(:name => 'string')
337
+ doc[:name].should == 'string'
338
+ end
339
+
340
+ context "[]=" do
341
+ should "write key value for existing key" do
342
+ doc = @document.new
343
+ doc[:name] = 'string'
344
+ doc[:name].should == 'string'
345
+ end
346
+
347
+ should "create key and write value for missing key" do
348
+ doc = @document.new
349
+ doc[:foo] = 'string'
350
+ @document.keys.keys.include?('foo').should be_true
351
+ doc[:foo].should == 'string'
352
+ end
353
+ end
354
+ end
355
+
356
+ context "indifferent access" do
357
+ should "be enabled for keys" do
358
+ doc = @document.new(:name => 'string')
359
+ doc.attributes[:name].should == 'string'
360
+ doc.attributes['name'].should == 'string'
361
+ end
362
+ end
363
+
364
+ context "reading an attribute" do
365
+ should "work for defined keys" do
366
+ doc = @document.new(:name => 'string')
367
+ doc.name.should == 'string'
368
+ end
369
+
370
+ should "raise no method error for undefined keys" do
371
+ doc = @document.new
372
+ lambda { doc.fart }.should raise_error(NoMethodError)
373
+ end
374
+
375
+ should "be accessible for use in the model" do
376
+ @document.class_eval do
377
+ def name_and_age
378
+ "#{read_attribute(:name)} (#{read_attribute(:age)})"
379
+ end
380
+ end
381
+
382
+ doc = @document.new(:name => 'John', :age => 27)
383
+ doc.name_and_age.should == 'John (27)'
384
+ end
385
+
386
+ should "set instance variable" do
387
+ @document.key :foo, Array
388
+ doc = @document.new
389
+ doc.instance_variable_get("@foo").should be_nil
390
+ doc.foo
391
+ doc.instance_variable_get("@foo").should == []
392
+ end
393
+
394
+ should "not set instance variable if frozen" do
395
+ @document.key :foo, Array
396
+ doc = @document.new
397
+ doc.instance_variable_get("@foo").should be_nil
398
+ doc.freeze
399
+ doc.foo
400
+ doc.instance_variable_get("@foo").should be_nil
401
+ end
402
+
403
+ should "be overrideable by modules" do
404
+ @document = Class.new do
405
+ include MongoMapper::Document
406
+ key :other_child, String
407
+ end
408
+
409
+ child = @document.new
410
+ child.other_child.should be_nil
411
+
412
+ @document.send :include, KeyOverride
413
+
414
+ overriden_child = @document.new
415
+ overriden_child.other_child.should == 'special result'
416
+ end
417
+ end
418
+
419
+ context "reading an attribute before typcasting" do
420
+ should "work for defined keys" do
421
+ doc = @document.new(:name => 12)
422
+ doc.name_before_typecast.should == 12
423
+ end
424
+
425
+ should "raise no method error for undefined keys" do
426
+ doc = @document.new
427
+ lambda { doc.foo_before_typecast }.should raise_error(NoMethodError)
428
+ end
429
+
430
+ should "be accessible for use in a document" do
431
+ @document.class_eval do
432
+ def untypcasted_name
433
+ read_attribute_before_typecast(:name)
434
+ end
435
+ end
436
+
437
+ doc = @document.new(:name => 12)
438
+ doc.name.should == '12'
439
+ doc.untypcasted_name.should == 12
440
+ end
441
+ end
442
+
443
+ context "writing an attribute" do
444
+ should "work for defined keys" do
445
+ doc = @document.new
446
+ doc.name = 'John'
447
+ doc.name.should == 'John'
448
+ end
449
+
450
+ should "raise no method error for undefined keys" do
451
+ doc = @document.new
452
+ lambda { doc.fart = 'poof!' }.should raise_error(NoMethodError)
453
+ end
454
+
455
+ should "typecast value" do
456
+ doc = @document.new
457
+ doc.name = 1234
458
+ doc.name.should == '1234'
459
+ doc.age = '21'
460
+ doc.age.should == 21
461
+ end
462
+
463
+ should "be accessible for use in the model" do
464
+ @document.class_eval do
465
+ def name_and_age=(new_value)
466
+ new_value.match(/([^\(\s]+) \((.*)\)/)
467
+ write_attribute :name, $1
468
+ write_attribute :age, $2
469
+ end
470
+ end
471
+
472
+ doc = @document.new
473
+ doc.name_and_age = 'Frank (62)'
474
+ doc.name.should == 'Frank'
475
+ doc.age.should == 62
476
+ end
477
+
478
+ should "be overrideable by modules" do
479
+ @document = Class.new do
480
+ include MongoMapper::Document
481
+ key :other_child, String
482
+ end
483
+
484
+ child = @document.new(:other_child => 'foo')
485
+ child.other_child.should == 'foo'
486
+
487
+ @document.send :include, KeyOverride
488
+
489
+ overriden_child = @document.new(:other_child => 'foo')
490
+ overriden_child.other_child.should == 'foo modified'
491
+ end
492
+ end # writing an attribute
493
+
494
+ context "checking if an attributes value is present" do
495
+ should "work for defined keys" do
496
+ doc = @document.new
497
+ doc.name?.should be_false
498
+ doc.name = 'John'
499
+ doc.name?.should be_true
500
+ end
501
+
502
+ should "raise no method error for undefined keys" do
503
+ doc = @document.new
504
+ lambda { doc.fart? }.should raise_error(NoMethodError)
505
+ end
506
+ end
507
+
508
+ context "equality" do
509
+ should "be equal if id and class are the same" do
510
+ (@document.new('_id' => 1) == @document.new('_id' => 1)).should be(true)
511
+ end
512
+
513
+ should "not be equal if class same but id different" do
514
+ (@document.new('_id' => 1) == @document.new('_id' => 2)).should be(false)
515
+ end
516
+
517
+ should "not be equal if id same but class different" do
518
+ @another_document = Class.new do
519
+ include MongoMapper::Document
520
+ end
521
+
522
+ (@document.new('_id' => 1) == @another_document.new('_id' => 1)).should be(false)
523
+ end
524
+ end
525
+ end # instance of a embedded document
526
+ end