mongo_mapper 0.13.0 → 0.15.1

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 (137) hide show
  1. checksums.yaml +5 -5
  2. data/LICENSE +1 -1
  3. data/README.md +61 -0
  4. data/examples/keys.rb +1 -1
  5. data/examples/modifiers/set.rb +1 -1
  6. data/examples/querying.rb +1 -1
  7. data/examples/safe.rb +2 -2
  8. data/examples/scopes.rb +1 -1
  9. data/lib/mongo_mapper.rb +7 -0
  10. data/lib/mongo_mapper/connection.rb +16 -37
  11. data/lib/mongo_mapper/document.rb +4 -0
  12. data/lib/mongo_mapper/extensions/array.rb +14 -6
  13. data/lib/mongo_mapper/extensions/hash.rb +15 -3
  14. data/lib/mongo_mapper/extensions/object.rb +4 -0
  15. data/lib/mongo_mapper/extensions/object_id.rb +5 -1
  16. data/lib/mongo_mapper/extensions/string.rb +13 -5
  17. data/lib/mongo_mapper/extensions/symbol.rb +18 -0
  18. data/lib/mongo_mapper/plugins/accessible.rb +15 -5
  19. data/lib/mongo_mapper/plugins/associations.rb +7 -6
  20. data/lib/mongo_mapper/plugins/associations/base.rb +27 -14
  21. data/lib/mongo_mapper/plugins/associations/belongs_to_association.rb +10 -1
  22. data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +9 -8
  23. data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +12 -11
  24. data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +4 -4
  25. data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +60 -29
  26. data/lib/mongo_mapper/plugins/associations/in_foreign_array_proxy.rb +136 -0
  27. data/lib/mongo_mapper/plugins/associations/many_association.rb +4 -2
  28. data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +18 -16
  29. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +55 -48
  30. data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +14 -13
  31. data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +7 -6
  32. data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +7 -5
  33. data/lib/mongo_mapper/plugins/associations/one_as_proxy.rb +14 -11
  34. data/lib/mongo_mapper/plugins/associations/one_embedded_polymorphic_proxy.rb +14 -13
  35. data/lib/mongo_mapper/plugins/associations/one_embedded_proxy.rb +9 -9
  36. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +27 -26
  37. data/lib/mongo_mapper/plugins/associations/proxy.rb +36 -29
  38. data/lib/mongo_mapper/plugins/associations/single_association.rb +5 -4
  39. data/lib/mongo_mapper/plugins/callbacks.rb +13 -0
  40. data/lib/mongo_mapper/plugins/counter_cache.rb +97 -0
  41. data/lib/mongo_mapper/plugins/dirty.rb +29 -37
  42. data/lib/mongo_mapper/plugins/document.rb +1 -1
  43. data/lib/mongo_mapper/plugins/dynamic_querying.rb +10 -9
  44. data/lib/mongo_mapper/plugins/dynamic_querying/dynamic_finder.rb +18 -17
  45. data/lib/mongo_mapper/plugins/embedded_callbacks.rb +2 -1
  46. data/lib/mongo_mapper/plugins/embedded_document.rb +1 -1
  47. data/lib/mongo_mapper/plugins/identity_map.rb +4 -2
  48. data/lib/mongo_mapper/plugins/indexes.rb +14 -7
  49. data/lib/mongo_mapper/plugins/keys.rb +170 -151
  50. data/lib/mongo_mapper/plugins/keys/key.rb +27 -16
  51. data/lib/mongo_mapper/plugins/keys/static.rb +45 -0
  52. data/lib/mongo_mapper/plugins/modifiers.rb +64 -38
  53. data/lib/mongo_mapper/plugins/partial_updates.rb +86 -0
  54. data/lib/mongo_mapper/plugins/persistence.rb +13 -8
  55. data/lib/mongo_mapper/plugins/protected.rb +6 -5
  56. data/lib/mongo_mapper/plugins/querying.rb +85 -42
  57. data/lib/mongo_mapper/plugins/querying/decorated_plucky_query.rb +20 -15
  58. data/lib/mongo_mapper/plugins/rails.rb +1 -0
  59. data/lib/mongo_mapper/plugins/safe.rb +10 -4
  60. data/lib/mongo_mapper/plugins/sci.rb +0 -0
  61. data/lib/mongo_mapper/plugins/scopes.rb +78 -7
  62. data/lib/mongo_mapper/plugins/stats.rb +17 -0
  63. data/lib/mongo_mapper/plugins/strong_parameters.rb +26 -0
  64. data/lib/mongo_mapper/plugins/timestamps.rb +1 -0
  65. data/lib/mongo_mapper/plugins/validations.rb +1 -1
  66. data/lib/mongo_mapper/railtie.rb +4 -3
  67. data/lib/mongo_mapper/utils.rb +2 -2
  68. data/lib/mongo_mapper/version.rb +1 -1
  69. data/lib/rails/generators/mongo_mapper/config/config_generator.rb +12 -13
  70. data/lib/rails/generators/mongo_mapper/model/model_generator.rb +9 -9
  71. data/spec/examples.txt +1717 -0
  72. data/spec/functional/accessible_spec.rb +19 -13
  73. data/spec/functional/associations/belongs_to_polymorphic_proxy_spec.rb +13 -13
  74. data/spec/functional/associations/belongs_to_proxy_spec.rb +36 -20
  75. data/spec/functional/associations/in_array_proxy_spec.rb +145 -10
  76. data/spec/functional/associations/in_foreign_array_proxy_spec.rb +321 -0
  77. data/spec/functional/associations/many_documents_as_proxy_spec.rb +6 -6
  78. data/spec/functional/associations/many_documents_proxy_spec.rb +85 -14
  79. data/spec/functional/associations/many_embedded_polymorphic_proxy_spec.rb +13 -13
  80. data/spec/functional/associations/many_embedded_proxy_spec.rb +1 -1
  81. data/spec/functional/associations/many_polymorphic_proxy_spec.rb +4 -4
  82. data/spec/functional/associations/one_as_proxy_spec.rb +10 -10
  83. data/spec/functional/associations/one_embedded_polymorphic_proxy_spec.rb +9 -9
  84. data/spec/functional/associations/one_embedded_proxy_spec.rb +3 -3
  85. data/spec/functional/associations/one_proxy_spec.rb +10 -10
  86. data/spec/functional/associations_spec.rb +3 -3
  87. data/spec/functional/binary_spec.rb +2 -2
  88. data/spec/functional/caching_spec.rb +8 -15
  89. data/spec/functional/callbacks_spec.rb +89 -2
  90. data/spec/functional/counter_cache_spec.rb +235 -0
  91. data/spec/functional/dirty_spec.rb +63 -46
  92. data/spec/functional/document_spec.rb +30 -5
  93. data/spec/functional/dumpable_spec.rb +1 -1
  94. data/spec/functional/embedded_document_spec.rb +17 -17
  95. data/spec/functional/identity_map_spec.rb +29 -16
  96. data/spec/functional/indexes_spec.rb +19 -18
  97. data/spec/functional/keys_spec.rb +86 -28
  98. data/spec/functional/logger_spec.rb +3 -3
  99. data/spec/functional/modifiers_spec.rb +81 -19
  100. data/spec/functional/partial_updates_spec.rb +577 -0
  101. data/spec/functional/protected_spec.rb +14 -14
  102. data/spec/functional/querying_spec.rb +77 -28
  103. data/spec/functional/safe_spec.rb +23 -27
  104. data/spec/functional/sci_spec.rb +9 -9
  105. data/spec/functional/scopes_spec.rb +235 -2
  106. data/spec/functional/static_keys_spec.rb +153 -0
  107. data/spec/functional/stats_spec.rb +86 -0
  108. data/spec/functional/strong_parameters_spec.rb +49 -0
  109. data/spec/functional/touch_spec.rb +1 -1
  110. data/spec/functional/validations_spec.rb +51 -57
  111. data/spec/quality_spec.rb +51 -0
  112. data/spec/spec_helper.rb +37 -9
  113. data/spec/support/matchers.rb +5 -14
  114. data/spec/unit/associations/base_spec.rb +12 -12
  115. data/spec/unit/associations/belongs_to_association_spec.rb +2 -2
  116. data/spec/unit/associations/many_association_spec.rb +2 -2
  117. data/spec/unit/associations/one_association_spec.rb +2 -2
  118. data/spec/unit/associations/proxy_spec.rb +19 -20
  119. data/spec/unit/clone_spec.rb +1 -1
  120. data/spec/unit/document_spec.rb +8 -8
  121. data/spec/unit/dynamic_finder_spec.rb +8 -8
  122. data/spec/unit/embedded_document_spec.rb +18 -19
  123. data/spec/unit/extensions_spec.rb +41 -17
  124. data/spec/unit/identity_map_middleware_spec.rb +65 -96
  125. data/spec/unit/key_spec.rb +28 -26
  126. data/spec/unit/keys_spec.rb +20 -11
  127. data/spec/unit/model_generator_spec.rb +0 -0
  128. data/spec/unit/mongo_mapper_spec.rb +38 -85
  129. data/spec/unit/rails_spec.rb +5 -0
  130. data/spec/unit/serialization_spec.rb +1 -1
  131. data/spec/unit/time_zones_spec.rb +2 -2
  132. data/spec/unit/validations_spec.rb +46 -33
  133. metadata +66 -37
  134. data/README.rdoc +0 -59
  135. data/lib/mongo_mapper/connections/10gen.rb +0 -0
  136. data/lib/mongo_mapper/connections/moped.rb +0 -0
  137. data/lib/mongo_mapper/extensions/ordered_hash.rb +0 -23
@@ -39,12 +39,12 @@ describe "Accessible" do
39
39
 
40
40
  it "should assign inaccessible attribute through accessor" do
41
41
  @doc.admin = true
42
- @doc.admin.should be_true
42
+ @doc.admin.should be_truthy
43
43
  end
44
44
 
45
45
  it "should ignore inaccessible attribute on #initialize" do
46
46
  doc = @doc_class.new(:name => 'John', :admin => true)
47
- doc.admin.should be_false
47
+ doc.admin.should be_falsey
48
48
  doc.name.should == 'John'
49
49
  end
50
50
 
@@ -54,7 +54,7 @@ describe "Accessible" do
54
54
  doc.save!
55
55
 
56
56
  doc = @doc_class.first(:name => 'John')
57
- doc.admin.should be_true
57
+ doc.admin.should be_truthy
58
58
  doc.name.should == 'John'
59
59
  end
60
60
 
@@ -64,37 +64,43 @@ describe "Accessible" do
64
64
  doc.save!
65
65
 
66
66
  doc.reload
67
- doc.admin.should be_true
67
+ doc.admin.should be_truthy
68
68
  doc.name.should == 'John'
69
69
  end
70
70
 
71
71
  it "should not ignore inaccessible attribute on #update_attribute" do
72
72
  @doc.update_attribute('admin', true)
73
- @doc.admin.should be_true
73
+ @doc.admin.should be_truthy
74
74
  end
75
75
 
76
76
  it "should ignore inaccessible attribute on #update_attributes" do
77
77
  @doc.update_attributes(:name => 'Ren Hoek', :admin => true)
78
78
  @doc.name.should == 'Ren Hoek'
79
- @doc.admin.should be_false
79
+ @doc.admin.should be_falsey
80
80
  end
81
81
 
82
82
  it "should ignore inaccessible attribute on #update_attributes!" do
83
83
  @doc.update_attributes!(:name => 'Stimpson J. Cat', :admin => true)
84
84
  @doc.name.should == 'Stimpson J. Cat'
85
- @doc.admin.should be_false
85
+ @doc.admin.should be_falsey
86
86
  end
87
87
 
88
88
  it "should ignore inaccessible attribute on #attributes=" do
89
89
  @doc.attributes = {:name => 'Ren Hoek', :admin => true}
90
90
  @doc.name.should == 'Ren Hoek'
91
- @doc.admin.should be_false
91
+ @doc.admin.should be_falsey
92
+ end
93
+
94
+ it "should ignore inaccessible attribute on #assign_attributes" do
95
+ @doc.assign_attributes({:name => 'Ren Hoek', :admin => true})
96
+ @doc.name.should == 'Ren Hoek'
97
+ @doc.admin.should be_falsey
92
98
  end
93
99
 
94
100
  it "should be indifferent to whether the accessible keys are strings or symbols" do
95
101
  @doc.update_attributes!("name" => 'Stimpson J. Cat', "admin" => true)
96
102
  @doc.name.should == 'Stimpson J. Cat'
97
- @doc.admin.should be_false
103
+ @doc.admin.should be_falsey
98
104
  end
99
105
 
100
106
  it "should accept nil as constructor's argument without raising exception" do
@@ -119,7 +125,7 @@ describe "Accessible" do
119
125
  key :name, String
120
126
  key :site_id, ObjectId
121
127
  end
122
- GrandParent.collection.remove
128
+ GrandParent.collection.drop
123
129
 
124
130
  class ::Child < ::GrandParent
125
131
  attr_accessible :position
@@ -180,19 +186,19 @@ describe "Accessible" do
180
186
 
181
187
  it "should assign inaccessible attribute through accessor" do
182
188
  @edoc.admin = true
183
- @edoc.admin.should be_true
189
+ @edoc.admin.should be_truthy
184
190
  end
185
191
 
186
192
  it "should ignore inaccessible attribute on #update_attributes" do
187
193
  @edoc.update_attributes(:name => 'Ren Hoek', :admin => true)
188
194
  @edoc.name.should == 'Ren Hoek'
189
- @edoc.admin.should be_false
195
+ @edoc.admin.should be_falsey
190
196
  end
191
197
 
192
198
  it "should ignore inaccessible attribute on #update_attributes!" do
193
199
  @edoc.update_attributes!(:name => 'Stimpson J. Cat', :admin => true)
194
200
  @edoc.name.should == 'Stimpson J. Cat'
195
- @edoc.admin.should be_false
201
+ @edoc.admin.should be_falsey
196
202
  end
197
203
  end
198
204
  end
@@ -3,32 +3,32 @@ require 'spec_helper'
3
3
 
4
4
  describe "BelongsToPolymorphicProxy" do
5
5
  before do
6
- Status.collection.remove
7
- Project.collection.remove
6
+ Status.collection.drop
7
+ Project.collection.drop
8
8
  end
9
9
 
10
10
  it "should default to nil" do
11
11
  status = Status.new
12
- status.target.nil?.should be_true
12
+ status.target.nil?.should be_truthy
13
13
  status.target.inspect.should == "nil"
14
14
  end
15
15
 
16
16
  it "should have boolean presence method" do
17
17
  status = Status.new
18
- status.target?.should be_false
18
+ status.target?.should be_falsey
19
19
 
20
20
  status.target = Project.new(:name => 'mongomapper')
21
- status.target?.should be_true
21
+ status.target?.should be_truthy
22
22
  end
23
23
 
24
24
  it "should be able to replace the association" do
25
25
  status = Status.new(:name => 'Foo!')
26
26
  project = Project.new(:name => "mongomapper")
27
27
  status.target = project
28
- status.save.should be_true
28
+ status.save.should be_truthy
29
29
 
30
30
  status = status.reload
31
- status.target.nil?.should be_false
31
+ status.target.nil?.should be_falsey
32
32
  status.target_id.should == project._id
33
33
  status.target_type.should == "Project"
34
34
  status.target.name.should == "mongomapper"
@@ -38,13 +38,13 @@ describe "BelongsToPolymorphicProxy" do
38
38
  status = Status.new(:name => 'Foo!')
39
39
  project = Project.new(:name => "mongomapper")
40
40
  status.target = project
41
- status.save.should be_true
41
+ status.save.should be_truthy
42
42
 
43
43
  status = status.reload
44
44
  status.target = nil
45
- status.target_type.nil?.should be_true
46
- status.target_id.nil?.should be_true
47
- status.target.nil?.should be_true
45
+ status.target_type.nil?.should be_truthy
46
+ status.target_id.nil?.should be_truthy
47
+ status.target.nil?.should be_truthy
48
48
  end
49
49
 
50
50
  context "association id set but document not found" do
@@ -52,13 +52,13 @@ describe "BelongsToPolymorphicProxy" do
52
52
  @status = Status.new(:name => 'Foo!')
53
53
  project = Project.new(:name => "mongomapper")
54
54
  @status.target = project
55
- @status.save.should be_true
55
+ @status.save.should be_truthy
56
56
  project.destroy
57
57
  @status.reload
58
58
  end
59
59
 
60
60
  it "should return nil instead of raising error" do
61
- @status.target.nil?.should be_true
61
+ @status.target.nil?.should be_truthy
62
62
  end
63
63
  end
64
64
  end
@@ -1,6 +1,5 @@
1
1
  require 'spec_helper'
2
2
 
3
-
4
3
  describe "BelongsToProxy" do
5
4
  before do
6
5
  @post_class = Doc()
@@ -11,7 +10,7 @@ describe "BelongsToProxy" do
11
10
  end
12
11
 
13
12
  it "should default to nil" do
14
- @comment_class.new.post.nil?.should be_true
13
+ @comment_class.new.post.nil?.should be_truthy
15
14
  end
16
15
 
17
16
  it "should return nil instead of a proxy" do
@@ -20,10 +19,10 @@ describe "BelongsToProxy" do
20
19
 
21
20
  it "should have boolean presence method" do
22
21
  comment = @comment_class.new(:name => 'Foo!')
23
- comment.post?.should be_false
22
+ comment.post?.should be_falsey
24
23
 
25
24
  comment.post = @post_class.new(:name => 'mongomapper')
26
- comment.post?.should be_true
25
+ comment.post?.should be_truthy
27
26
  end
28
27
 
29
28
  it "should allow overriding association methods" do
@@ -34,25 +33,25 @@ describe "BelongsToProxy" do
34
33
  end
35
34
 
36
35
  instance = @comment_class.new
37
- instance.post?.should be_false
36
+ instance.post?.should be_falsey
38
37
  instance.post = @post_class.new
39
- instance.post?.should be_true
38
+ instance.post?.should be_truthy
40
39
  end
41
40
 
42
41
  it "should be able to replace the association" do
43
42
  post = @post_class.new(:name => 'mongomapper')
44
43
  comment = @comment_class.new(:name => 'Foo!', :post => post)
45
- comment.save.should be_true
44
+ comment.save.should be_truthy
46
45
 
47
46
  comment = comment.reload
48
47
  comment.post.should == post
49
- comment.post.nil?.should be_false
48
+ comment.post.nil?.should be_falsey
50
49
  end
51
50
 
52
51
  it "should not reload the association when replacing" do
53
52
  post = @post_class.new(:name => 'mongomapper')
54
53
  comment = @comment_class.new(:name => 'Foo!', :post => post)
55
- comment.post.proxy_target.object_id.should == post.object_id
54
+ comment.post.object_id.should == post.object_id
56
55
  end
57
56
 
58
57
  it "should properly assign the associated object when assigning the association with create" do
@@ -71,12 +70,12 @@ describe "BelongsToProxy" do
71
70
  post2 = @post_class.create(:name => 'post2')
72
71
 
73
72
  comment = @comment_class.new(:name => 'Foo!', :post => post1)
74
- comment.save.should be_true
73
+ comment.save.should be_truthy
75
74
 
76
75
 
77
76
  comment = comment.reload
78
77
  comment.post.should == post1
79
- comment.post.nil?.should be_false
78
+ comment.post.nil?.should be_falsey
80
79
 
81
80
  original_post = comment.post
82
81
  original_post.name.should == 'post1'
@@ -89,23 +88,23 @@ describe "BelongsToProxy" do
89
88
  it "should unset the association" do
90
89
  post = @post_class.new(:name => 'mongomapper')
91
90
  comment = @comment_class.new(:name => 'Foo!', :post => post)
92
- comment.save.should be_true
91
+ comment.save.should be_truthy
93
92
 
94
93
  comment = comment.reload
95
94
  comment.post = nil
96
- comment.post.nil?.should be_true
95
+ comment.post.nil?.should be_truthy
97
96
  end
98
97
 
99
98
  it "should return nil if id set but document not found" do
100
99
  id = BSON::ObjectId.new
101
- @comment_class.new(:name => 'Foo', :post_id => id).post.nil?.should be_true
100
+ @comment_class.new(:name => 'Foo', :post_id => id).post.nil?.should be_truthy
102
101
  end
103
102
 
104
103
  it "should define foreign key if it doesn't exist" do
105
104
  @category_class = Doc()
106
105
  @post_class.belongs_to :category, :class => @category_class
107
106
 
108
- @post_class.key?(:category_id).should be_true
107
+ @post_class.key?(:category_id).should be_truthy
109
108
  end
110
109
 
111
110
  it "should not define foreign key if it already exists" do
@@ -122,13 +121,13 @@ describe "BelongsToProxy" do
122
121
  class ::Property
123
122
  include MongoMapper::Document
124
123
  end
125
- Property.collection.remove
124
+ Property.collection.drop
126
125
 
127
126
  class ::Thing
128
127
  include MongoMapper::Document
129
128
  key :name, String
130
129
  end
131
- Thing.collection.remove
130
+ Thing.collection.drop
132
131
  end
133
132
 
134
133
  after do
@@ -212,7 +211,7 @@ describe "BelongsToProxy" do
212
211
  end
213
212
 
214
213
  it "should raise exception if invalid" do
215
- expect { comment.create_post! }.to raise_error(MongoMapper::DocumentNotValid)
214
+ lambda { comment.create_post! }.should raise_error(MongoMapper::DocumentNotValid)
216
215
  end
217
216
 
218
217
  it "should work if valid" do
@@ -228,7 +227,7 @@ describe "BelongsToProxy" do
228
227
 
229
228
  context 'autosave' do
230
229
  it 'should not be true by default' do
231
- @comment_class.associations[:post].options[:autosave].should_not be_true
230
+ @comment_class.associations[:post].options[:autosave].should_not be_truthy
232
231
  end
233
232
 
234
233
  it 'should save parent changes when true' do
@@ -253,4 +252,21 @@ describe "BelongsToProxy" do
253
252
  post.reload.title.should == 'Hello, world!'
254
253
  end
255
254
  end
256
- end
255
+
256
+ context "regression with reassignment" do
257
+ it "should not infinite regress (shouldn't raise SystemStackError) when assigned to twice (when the proxy isn't around)" do
258
+ post = @post_class.create!
259
+ comment = @comment_class.create!(post: post)
260
+
261
+ comment = @comment_class.find(comment.id)
262
+
263
+ post = comment.post
264
+
265
+ comment.post = post
266
+
267
+ lambda do
268
+ comment.post = post
269
+ end.should_not raise_error
270
+ end
271
+ end
272
+ end
@@ -14,8 +14,8 @@ describe "InArrayProxy" do
14
14
  key :list_ids, Array
15
15
  many :lists, :in => :list_ids
16
16
  end
17
- User.collection.remove
18
- List.collection.remove
17
+ User.collection.drop
18
+ List.collection.drop
19
19
  end
20
20
 
21
21
  after do
@@ -50,7 +50,7 @@ describe "InArrayProxy" do
50
50
  user = User.new(:name => 'John')
51
51
  list = List.new(:name => 'Foo')
52
52
  user.lists = [list]
53
- user.save.should be_true
53
+ user.save.should be_truthy
54
54
 
55
55
  user.reload
56
56
  user.list_ids.should == [list.id]
@@ -112,9 +112,9 @@ describe "InArrayProxy" do
112
112
  end
113
113
 
114
114
  it "should raise exception if invalid" do
115
- expect {
115
+ lambda {
116
116
  @user.lists.create!
117
- }.to raise_error(MongoMapper::DocumentNotValid)
117
+ }.should raise_error(MongoMapper::DocumentNotValid)
118
118
  end
119
119
 
120
120
  it "should reset cache" do
@@ -195,9 +195,9 @@ describe "InArrayProxy" do
195
195
  end
196
196
 
197
197
  it "should raise error when using ! and not found" do
198
- expect {
198
+ lambda {
199
199
  @user.lists.find!(@list3.id)
200
- }.to raise_error(MongoMapper::DocumentNotFound)
200
+ }.should raise_error(MongoMapper::DocumentNotFound)
201
201
  end
202
202
  end
203
203
 
@@ -249,9 +249,9 @@ describe "InArrayProxy" do
249
249
  end
250
250
 
251
251
  it "should raise error when using ! and not found" do
252
- expect {
252
+ lambda {
253
253
  @user.lists.find_by_name!('Foo 3')
254
- }.to raise_error(MongoMapper::DocumentNotFound)
254
+ }.should raise_error(MongoMapper::DocumentNotFound)
255
255
  end
256
256
 
257
257
  context "find_or_create_by" do
@@ -346,4 +346,139 @@ describe "InArrayProxy" do
346
346
  end
347
347
  end
348
348
  end
349
- end
349
+
350
+ describe "ordering" do
351
+ before do
352
+ @user_class = Doc("User") do
353
+ key :list_ids, Array
354
+ end
355
+
356
+ @list_class = Doc("List") do
357
+ key :name, String, :required => true
358
+ end
359
+ end
360
+
361
+ def create_list_items
362
+ @user = @user_class.create!
363
+ @list1 = @list_class.create!(:name => "one")
364
+ @list2 = @list_class.create!(:name => "two")
365
+ @list3 = @list_class.create!(:name => "three")
366
+
367
+ @user.lists << [@list3, @list2, @list1]
368
+ @user.save!
369
+
370
+ @user.reload
371
+ end
372
+
373
+ shared_examples_for "ordering is on" do
374
+ it "should have the items ordered according to order of the ids" do
375
+ @user.lists.should == [@list3, @list2, @list1]
376
+ end
377
+
378
+ it "should have the right item for #first" do
379
+ @user.lists.first.should == @list3
380
+ end
381
+
382
+ it "should have the right item for #last" do
383
+ @user.lists.last.should == @list1
384
+ end
385
+
386
+ it "does not work for pagination (but probably should)" do
387
+ @user.lists.paginate(:per_page => 100).should == [@list1, @list2, @list3]
388
+ end
389
+
390
+ it "should work with find" do
391
+ @user.lists.find(@list2.id, @list1.id).should == [@list2, @list1]
392
+ @user.lists.find(@list1.id, @list2.id).should == [@list2, @list1]
393
+ @user.lists.find(@list1.id).should == @list1
394
+ end
395
+
396
+ it "should work with find!" do
397
+ @user.lists.find!(@list2.id, @list1.id).should == [@list2, @list1]
398
+ @user.lists.find!(@list1.id, @list2.id).should == [@list2, @list1]
399
+ end
400
+
401
+ it "should only load one object with first (and no params given)" do
402
+ @list_class.should_receive(:allocate).once.and_return @list3
403
+ @user.lists.first
404
+ end
405
+
406
+ it "should only load one object with first (and params given)" do
407
+ @list_class.should_receive(:allocate).once.and_return @list3
408
+ @user.lists.first(:name => /^t/)
409
+ end
410
+
411
+ it "should only load one object with last (and no params given)" do
412
+ @list_class.should_receive(:allocate).once.and_return @list1
413
+ @user.lists.last
414
+ end
415
+
416
+ it "should only load one object with last (and params given)" do
417
+ @list_class.should_receive(:allocate).once.and_return @list3
418
+ @user.lists.last(:name => /^t/)
419
+ end
420
+
421
+ it "should return nil if first with a query matches no objects" do
422
+ @user.lists.first(:name => "non-existent").should be_nil
423
+ end
424
+
425
+ it "should return nil if last with a query matches no objects" do
426
+ @user.lists.last(:name => "non-existent").should be_nil
427
+ end
428
+
429
+ it "should work with extra query params to first and last" do
430
+ @user.lists.first(:name => /^t/).should == @list3
431
+ @user.lists.first(:name => /^o/).should == @list1
432
+
433
+ @user.lists.last(:name => /^t/).should == @list2
434
+ @user.lists.last(:name => /^o/).should == @list1
435
+ end
436
+ end
437
+
438
+ shared_examples_for "ordering is off" do
439
+ it "should include all items" do
440
+ @user.lists.length == 3
441
+ @user.lists.should include(@list1)
442
+ @user.lists.should include(@list2)
443
+ @user.lists.should include(@list3)
444
+ end
445
+
446
+ it "should return the items in the natural order" do
447
+ @user.lists.should == [@list1, @list2, @list3]
448
+ end
449
+
450
+ it "should work with find" do
451
+ @user.lists.find(@list2.id, @list1.id).should == [@list1, @list2]
452
+ @user.lists.find(@list1.id, @list2.id).should == [@list1, @list2]
453
+ @user.lists.find(@list1.id).should == @list1
454
+ end
455
+ end
456
+
457
+ describe "without a param" do
458
+ before do
459
+ @user_class.many :lists, :in => :list_ids, :class => @list_class
460
+ create_list_items
461
+ end
462
+
463
+ it_behaves_like "ordering is off"
464
+ end
465
+
466
+ describe "with ordering off" do
467
+ before do
468
+ @user_class.many :lists, :in => :list_ids, :class => @list_class, :ordered => false
469
+ create_list_items
470
+ end
471
+
472
+ it_behaves_like "ordering is off"
473
+ end
474
+
475
+ describe "with ordering on" do
476
+ before do
477
+ @user_class.many :lists, :in => :list_ids, :class => @list_class, :ordered => true
478
+ create_list_items
479
+ end
480
+
481
+ it_behaves_like "ordering is on"
482
+ end
483
+ end
484
+ end