jnunemaker-mongomapper 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. data/.gitignore +1 -0
  2. data/History +17 -0
  3. data/README.rdoc +6 -3
  4. data/Rakefile +3 -2
  5. data/VERSION +1 -1
  6. data/bin/mmconsole +56 -0
  7. data/lib/mongomapper.rb +48 -17
  8. data/lib/mongomapper/associations.rb +31 -39
  9. data/lib/mongomapper/associations/base.rb +40 -22
  10. data/lib/mongomapper/associations/belongs_to_polymorphic_proxy.rb +33 -0
  11. data/lib/mongomapper/associations/belongs_to_proxy.rb +10 -14
  12. data/lib/mongomapper/associations/many_embedded_polymorphic_proxy.rb +34 -0
  13. data/lib/mongomapper/associations/{has_many_embedded_proxy.rb → many_embedded_proxy.rb} +5 -5
  14. data/lib/mongomapper/associations/many_proxy.rb +55 -0
  15. data/lib/mongomapper/associations/proxy.rb +21 -14
  16. data/lib/mongomapper/callbacks.rb +1 -1
  17. data/lib/mongomapper/document.rb +82 -59
  18. data/lib/mongomapper/embedded_document.rb +121 -130
  19. data/lib/mongomapper/finder_options.rb +21 -6
  20. data/lib/mongomapper/key.rb +5 -7
  21. data/lib/mongomapper/observing.rb +1 -41
  22. data/lib/mongomapper/pagination.rb +52 -0
  23. data/lib/mongomapper/rails_compatibility/document.rb +15 -0
  24. data/lib/mongomapper/rails_compatibility/embedded_document.rb +25 -0
  25. data/lib/mongomapper/serialization.rb +1 -1
  26. data/mongomapper.gemspec +62 -36
  27. data/test/NOTE_ON_TESTING +1 -0
  28. data/test/functional/test_associations.rb +485 -0
  29. data/test/{test_callbacks.rb → functional/test_callbacks.rb} +2 -1
  30. data/test/functional/test_document.rb +636 -0
  31. data/test/functional/test_pagination.rb +82 -0
  32. data/test/functional/test_rails_compatibility.rb +31 -0
  33. data/test/functional/test_validations.rb +172 -0
  34. data/test/models.rb +92 -0
  35. data/test/test_helper.rb +5 -0
  36. data/test/{serializers → unit/serializers}/test_json_serializer.rb +0 -0
  37. data/test/unit/test_association_base.rb +131 -0
  38. data/test/unit/test_document.rb +115 -0
  39. data/test/{test_embedded_document.rb → unit/test_embedded_document.rb} +158 -66
  40. data/test/{test_finder_options.rb → unit/test_finder_options.rb} +66 -0
  41. data/test/{test_key.rb → unit/test_key.rb} +13 -1
  42. data/test/unit/test_mongo_id.rb +35 -0
  43. data/test/{test_mongomapper.rb → unit/test_mongomapper.rb} +0 -0
  44. data/test/{test_observing.rb → unit/test_observing.rb} +0 -0
  45. data/test/unit/test_pagination.rb +113 -0
  46. data/test/unit/test_rails_compatibility.rb +34 -0
  47. data/test/{test_serializations.rb → unit/test_serializations.rb} +0 -2
  48. data/test/{test_validations.rb → unit/test_validations.rb} +0 -134
  49. metadata +68 -36
  50. data/lib/mongomapper/associations/has_many_proxy.rb +0 -28
  51. data/lib/mongomapper/associations/polymorphic_belongs_to_proxy.rb +0 -31
  52. data/lib/mongomapper/rails_compatibility.rb +0 -23
  53. data/test/test_associations.rb +0 -149
  54. data/test/test_document.rb +0 -944
  55. data/test/test_rails_compatibility.rb +0 -29
@@ -47,11 +47,55 @@ class FinderOptionsTest < Test::Unit::TestCase
47
47
  }
48
48
  end
49
49
 
50
+ should "not use $in for arrays if already using array modifier" do
51
+ FinderOptions.to_mongo_criteria(:foo => {'$all' => [1,2,3]}).should == {
52
+ :foo => {'$all' => [1,2,3]}
53
+ }
54
+ end
55
+
50
56
  should "work arbitrarily deep" do
51
57
  FinderOptions.to_mongo_criteria(:foo => {:bar => [1,2,3]}).should == {
52
58
  :foo => {:bar => {'$in' => [1,2,3]}}
53
59
  }
54
60
  end
61
+
62
+ should "convert string _ids to objectid automatically" do
63
+ id = XGen::Mongo::Driver::ObjectID.new
64
+
65
+ FinderOptions.to_mongo_criteria(:_id => id.to_s).should == {
66
+ :_id => id
67
+ }
68
+ end
69
+
70
+ should "leave objectid _ids alone" do
71
+ id = XGen::Mongo::Driver::ObjectID.new
72
+
73
+ FinderOptions.to_mongo_criteria(:_id => id).should == {
74
+ :_id => id
75
+ }
76
+ end
77
+
78
+ should "convert array of string _ids to object ids" do
79
+ id1 = XGen::Mongo::Driver::ObjectID.new
80
+ id2 = XGen::Mongo::Driver::ObjectID.new
81
+
82
+ FinderOptions.to_mongo_criteria(:_id => [id1.to_s, id2.to_s]).should == {
83
+ :_id => {'$in' => [id1, id2]}
84
+ }
85
+ end
86
+
87
+ should "convert array of string _ids when using mongo array stuff" do
88
+ id1 = XGen::Mongo::Driver::ObjectID.new
89
+ id2 = XGen::Mongo::Driver::ObjectID.new
90
+
91
+ FinderOptions.to_mongo_criteria(:_id => {'$all' => [id1.to_s, id2.to_s]}).should == {
92
+ :_id => {'$all' => [id1, id2]}
93
+ }
94
+
95
+ FinderOptions.to_mongo_criteria(:_id => {'$any' => [id1.to_s, id2.to_s]}).should == {
96
+ :_id => {'$any' => [id1, id2]}
97
+ }
98
+ end
55
99
  end
56
100
 
57
101
  context "ordering" do
@@ -90,6 +134,28 @@ class FinderOptionsTest < Test::Unit::TestCase
90
134
  hash[:baz] = 1
91
135
  FinderOptions.to_mongo_options(:order => 'foo desc, bar, baz')[:sort].should == hash
92
136
  end
137
+
138
+ should "just use sort if sort and order are present" do
139
+ FinderOptions.to_mongo_options(:sort => {'$natural' => 1}, :order => 'foo asc')[:sort].should == {
140
+ '$natural' => 1
141
+ }
142
+ end
143
+
144
+ should "convert natural in order to proper" do
145
+ hash = OrderedHash.new
146
+ hash[:'$natural'] = 1
147
+ FinderOptions.to_mongo_options(:order => '$natural asc')[:sort].should == hash
148
+ hash[:'$natural'] = -1
149
+ FinderOptions.to_mongo_options(:order => '$natural desc')[:sort].should == hash
150
+ end
151
+
152
+ should "work for natural order ascending" do
153
+ FinderOptions.to_mongo_options(:sort => {'$natural' => 1})[:sort]['$natural'].should == 1
154
+ end
155
+
156
+ should "work for natural order descending" do
157
+ FinderOptions.to_mongo_options(:sort => {'$natural' => -1})[:sort]['$natural'].should == -1
158
+ end
93
159
  end
94
160
 
95
161
  context "offset" do
@@ -14,7 +14,7 @@ class KeyTest < Test::Unit::TestCase
14
14
 
15
15
  context "The Key Class" do
16
16
  should "have the native types defined" do
17
- Key::NativeTypes.should == [String, Float, Time, Integer, Boolean, Array, Hash, Ref]
17
+ Key::NativeTypes.should == [String, Float, Time, Integer, Boolean, Array, Hash, MongoID]
18
18
  end
19
19
  end
20
20
 
@@ -30,6 +30,10 @@ class KeyTest < Test::Unit::TestCase
30
30
  should "allow setting options" do
31
31
  Key.new(:foo, Integer, :required => true).options[:required].should be(true)
32
32
  end
33
+
34
+ should "default options to {}" do
35
+ Key.new(:foo, Integer, nil).options.should == {}
36
+ end
33
37
 
34
38
  should "symbolize option keys" do
35
39
  Key.new(:foo, Integer, 'required' => true).options[:required].should be(true)
@@ -71,6 +75,14 @@ class KeyTest < Test::Unit::TestCase
71
75
  end
72
76
 
73
77
  context "setting a value" do
78
+ should "correctly typecast MongoIDs" do
79
+ key = Key.new(:_id, MongoID)
80
+ id = MongoID.new
81
+ [id, id.to_s].each do |a|
82
+ key.set(a).should == id
83
+ end
84
+ end
85
+
74
86
  should "correctly typecast Strings" do
75
87
  key = Key.new(:foo, String)
76
88
  [21, '21'].each do |a|
@@ -0,0 +1,35 @@
1
+ require 'test_helper'
2
+
3
+ class MongoIDTest < Test::Unit::TestCase
4
+ PhonyError = Class.new(StandardError)
5
+
6
+ class Phony
7
+ def to_s
8
+ raise PhonyError
9
+ end
10
+ end
11
+
12
+ context "mm_typecast" do
13
+ should "return value if object id" do
14
+ id = XGen::Mongo::Driver::ObjectID.new
15
+ MongoID.mm_typecast(id).should == id
16
+ end
17
+
18
+ should "return object id if string" do
19
+ id = XGen::Mongo::Driver::ObjectID.new
20
+ MongoID.mm_typecast(id.to_s).should == id
21
+ end
22
+
23
+ should "raise DocumentNotFound if invalid id" do
24
+ lambda {
25
+ MongoID.mm_typecast(1234)
26
+ }.should raise_error(MongoMapper::DocumentNotFound)
27
+ end
28
+
29
+ should "raise exception if message does not match illegal object id" do
30
+ lambda {
31
+ MongoID.mm_typecast(Phony.new)
32
+ }.should raise_error(PhonyError)
33
+ end
34
+ end
35
+ end
File without changes
@@ -0,0 +1,113 @@
1
+ require 'test_helper'
2
+
3
+ class PaginationTest < Test::Unit::TestCase
4
+ context "Pagination proxy" do
5
+ include MongoMapper::Pagination
6
+
7
+ should "should have accessors for subject" do
8
+ subject = [1,2,3,4,5]
9
+ collection = PaginationProxy.new(25, 2)
10
+ collection.subject = subject
11
+ collection.subject.should == subject
12
+ end
13
+
14
+ should "delegate any methods not defined to the subject" do
15
+ subject = [1,2,3,4,5]
16
+ collection = PaginationProxy.new(25, 2, 10)
17
+ collection.subject = subject
18
+ collection.size.should == 5
19
+ collection.each_with_index do |value, i|
20
+ value.should == subject[i]
21
+ end
22
+ collection[0..2].should == [1,2,3]
23
+ collection.class.should == Array
24
+ end
25
+
26
+ should "return correct value for total_entries" do
27
+ PaginationProxy.new(25, 2, 10).total_entries.should == 25
28
+ PaginationProxy.new('25', 2, 10).total_entries.should == 25
29
+ end
30
+
31
+ should "return correct value for per_page" do
32
+ PaginationProxy.new(25, 2, 10).per_page.should == 10
33
+ PaginationProxy.new(25, 2, '10').per_page.should == 10
34
+ end
35
+
36
+ should "alias limit to per_page" do
37
+ PaginationProxy.new(100, 1, 300).limit.should == 300
38
+ end
39
+
40
+ should "set per_page to 25 if nil or blank" do
41
+ PaginationProxy.new(25, 2).per_page.should == 25
42
+ PaginationProxy.new(25, 2, '').per_page.should == 25
43
+ end
44
+
45
+ should "return correct value for current_page" do
46
+ PaginationProxy.new(25, 2, 10).current_page.should == 2
47
+ PaginationProxy.new(25, '2', 10).current_page.should == 2
48
+ end
49
+
50
+ should "not allow value less than 1 for current page" do
51
+ PaginationProxy.new(25, -1).current_page.should == 1
52
+ end
53
+
54
+ should "automatically calculate total_pages from total_entries and per page" do
55
+ PaginationProxy.new(25, 2, 10).total_pages.should == 3
56
+ end
57
+
58
+ should "know how many records to skip" do
59
+ PaginationProxy.new(25, 2, 10).skip.should == 10
60
+ end
61
+
62
+ should "alias offset to skip" do
63
+ PaginationProxy.new(25, 2, 10).offset.should == 10
64
+ end
65
+
66
+ context "previous_page" do
67
+ should "be nil if current page 1" do
68
+ PaginationProxy.new(25, 1, 10).previous_page.should be_nil
69
+ end
70
+
71
+ should "be one less than current page if current is > 1" do
72
+ PaginationProxy.new(25, 2, 10).previous_page.should == 1
73
+ end
74
+ end
75
+
76
+ context "next_page" do
77
+ should "be nil if current page is last page" do
78
+ PaginationProxy.new(25, 3, 10).next_page.should be_nil
79
+ end
80
+
81
+ should "work for any page that is not last" do
82
+ PaginationProxy.new(25, 1, 10).next_page.should == 2
83
+ PaginationProxy.new(25, 2, 10).next_page.should == 3
84
+ end
85
+ end
86
+
87
+ context "previous_page" do
88
+ should "be nil if current page is first page" do
89
+ PaginationProxy.new(25, 1, 10).previous_page.should be_nil
90
+ end
91
+
92
+ should "work for any page other than first" do
93
+ PaginationProxy.new(25, 2, 10).previous_page.should == 1
94
+ PaginationProxy.new(25, 3, 10).previous_page.should == 2
95
+ end
96
+ end
97
+
98
+ context "out_of_bounds?" do
99
+ should "be true if current_page is greater than total_pages" do
100
+ PaginationProxy.new(25, 10, 4).out_of_bounds?.should be_true
101
+ end
102
+
103
+ should "be false if current_page is less than total_pages" do
104
+ PaginationProxy.new(25, 10, 1).out_of_bounds?.should be_false
105
+ PaginationProxy.new(25, 2, 10).out_of_bounds?.should be_false
106
+ end
107
+
108
+ should "be false if current_page is equal to total_pages" do
109
+ PaginationProxy.new(25, 3, 10).out_of_bounds?.should be_false
110
+ end
111
+ end
112
+ end # end of pagination proxy
113
+ end # end of test case
@@ -0,0 +1,34 @@
1
+ require 'test_helper'
2
+
3
+ class TestRailsCompatibility < Test::Unit::TestCase
4
+ class Item
5
+ include MongoMapper::EmbeddedDocument
6
+ key :for_all, String
7
+ end
8
+
9
+ class FirstItem < Item
10
+ key :first_only, String
11
+ many :second_items
12
+ end
13
+
14
+ class SecondItem < Item
15
+ key :second_only, String
16
+ end
17
+
18
+ context "EmbeddedDocument" do
19
+ should "raise error for to_param as embedded do not have id's" do
20
+ lambda { Item.new.to_param }.should raise_error
21
+ end
22
+
23
+ should "alias many to has_many" do
24
+ FirstItem.should respond_to(:has_many)
25
+ FirstItem.method(:has_many).should == FirstItem.method(:many)
26
+ end
27
+
28
+ should "have column names" do
29
+ Item.column_names.sort.should == ['for_all']
30
+ FirstItem.column_names.sort.should == ['first_only', 'for_all']
31
+ SecondItem.column_names.sort.should == ['for_all', 'second_only']
32
+ end
33
+ end
34
+ end
@@ -20,7 +20,6 @@ class SerializationTest < Test::Unit::TestCase
20
20
  )
21
21
  end
22
22
 
23
- # [:xml, :json].each do |format|
24
23
  [:json].each do |format|
25
24
  context format do
26
25
  should "be reversable" do
@@ -48,7 +47,6 @@ class SerializationTest < Test::Unit::TestCase
48
47
  assert_nil unserialized.age
49
48
  assert_equal @instance.awesome, unserialized.awesome
50
49
  end
51
-
52
50
  end
53
51
  end
54
52
  end
@@ -154,46 +154,6 @@ class ValidationsTest < Test::Unit::TestCase
154
154
  doc.should have_error_on(:name)
155
155
  end
156
156
  end
157
-
158
- context "validating uniqueness of" do
159
- setup do
160
- @document.key :name, String
161
- @document.validates_uniqueness_of :name
162
- end
163
-
164
- should "not fail if object is new" do
165
- doc = @document.new
166
- doc.should_not have_error_on(:name)
167
- end
168
-
169
- should "allow to update an object" do
170
- doc = @document.new("name" => "joe")
171
- doc.save
172
- doc.name = "joe"
173
- doc.valid?.should be_true
174
- doc.should_not have_error_on(:name)
175
- end
176
-
177
- should "fail if object name is not unique" do
178
- doc = @document.new("name" => "joe")
179
- doc.save.should be_true
180
- sleep 0.2 # hack to avoid race condition
181
- doc2 = @document.new("name" => "joe")
182
- doc2.should have_error_on(:name)
183
- end
184
- end
185
-
186
- context "validates uniqueness of with :unique shortcut" do
187
- should "work" do
188
- @document.key :name, String, :unique => true
189
-
190
- doc = @document.create(:name => 'John')
191
- doc.should_not have_error_on(:name)
192
- sleep 0.2 # hack to avoid race condition
193
- second_john = @document.create(:name => 'John')
194
- second_john.should have_error_on(:name, 'has already been taken')
195
- end
196
- end
197
157
 
198
158
  context "validating exclusion of" do
199
159
  should "throw error if enumerator not provided" do
@@ -274,70 +234,6 @@ class ValidationsTest < Test::Unit::TestCase
274
234
  end
275
235
  end # Validations
276
236
 
277
- context "Saving a new document that is invalid" do
278
- setup do
279
- @document = Class.new do
280
- include MongoMapper::Document
281
- key :name, String, :required => true
282
- end
283
-
284
- @document.collection.clear
285
- end
286
-
287
- should "not insert document" do
288
- doc = @document.new
289
- doc.save
290
- @document.count.should == 0
291
- end
292
-
293
- should "populate document's errors" do
294
- doc = @document.new
295
- doc.errors.size.should == 0
296
- doc.save
297
- doc.errors.full_messages.should == ["Name can't be empty"]
298
- end
299
- end
300
-
301
- context "Saving a document that is invalid (destructive)" do
302
- setup do
303
- @document = Class.new do
304
- include MongoMapper::Document
305
- key :name, String, :required => true
306
- end
307
-
308
- @document.collection.clear
309
- end
310
-
311
- should "raise error" do
312
- doc = @document.new
313
- lambda { doc.save! }.should raise_error(MongoMapper::DocumentNotValid)
314
- end
315
- end
316
-
317
- context "Saving an existing document that is invalid" do
318
- setup do
319
- @document = Class.new do
320
- include MongoMapper::Document
321
- key :name, String, :required => true
322
- end
323
-
324
- @document.collection.clear
325
- @doc = @document.create(:name => 'John Nunemaker')
326
- end
327
-
328
- should "not update document" do
329
- @doc.name = nil
330
- @doc.save
331
- @document.find(@doc.id).name.should == 'John Nunemaker'
332
- end
333
-
334
- should "populate document's errors" do
335
- @doc.name = nil
336
- @doc.save
337
- @doc.errors.full_messages.should == ["Name can't be empty"]
338
- end
339
- end
340
-
341
237
  context "Adding validation errors" do
342
238
  setup do
343
239
  @document = Class.new do
@@ -359,35 +255,5 @@ class ValidationsTest < Test::Unit::TestCase
359
255
  doc.action = 'kick'
360
256
  doc.should_not have_error_on(:action)
361
257
  end
362
-
363
- should "work with validate_on_create callback" do
364
- @document.validate_on_create :action_present
365
-
366
- doc = @document.new
367
- doc.action = nil
368
- doc.should have_error_on(:action)
369
-
370
- doc.action = 'kick'
371
- doc.should_not have_error_on(:action)
372
- doc.save
373
-
374
- doc.action = nil
375
- doc.should_not have_error_on(:action)
376
- end
377
-
378
- should "work with validate_on_update callback" do
379
- @document.validate_on_update :action_present
380
-
381
- doc = @document.new
382
- doc.action = nil
383
- doc.should_not have_error_on(:action)
384
- doc.save
385
-
386
- doc.action = nil
387
- doc.should have_error_on(:action)
388
-
389
- doc.action = 'kick'
390
- doc.should_not have_error_on(:action)
391
- end
392
258
  end
393
259
  end