jnunemaker-mongomapper 0.1.2 → 0.2.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/.gitignore +1 -0
- data/History +13 -0
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/lib/mongomapper.rb +20 -6
- data/lib/mongomapper/associations.rb +69 -0
- data/lib/mongomapper/associations/array_proxy.rb +6 -0
- data/lib/mongomapper/associations/base.rb +50 -0
- data/lib/mongomapper/associations/belongs_to_proxy.rb +26 -0
- data/lib/mongomapper/associations/has_many_embedded_proxy.rb +19 -0
- data/lib/mongomapper/associations/has_many_proxy.rb +28 -0
- data/lib/mongomapper/associations/polymorphic_belongs_to_proxy.rb +31 -0
- data/lib/mongomapper/associations/proxy.rb +60 -0
- data/lib/mongomapper/callbacks.rb +106 -0
- data/lib/mongomapper/document.rb +83 -54
- data/lib/mongomapper/embedded_document.rb +61 -91
- data/lib/mongomapper/finder_options.rb +37 -35
- data/lib/mongomapper/key.rb +11 -10
- data/lib/mongomapper/observing.rb +90 -0
- data/lib/mongomapper/rails_compatibility.rb +5 -2
- data/lib/mongomapper/save_with_validation.rb +6 -36
- data/lib/mongomapper/validations.rb +47 -0
- data/mongomapper.gemspec +18 -5
- data/test/test_associations.rb +121 -24
- data/test/test_callbacks.rb +3 -6
- data/test/test_document.rb +20 -14
- data/test/test_embedded_document.rb +2 -3
- data/test/test_finder_options.rb +37 -22
- data/test/test_key.rb +30 -30
- data/test/test_observing.rb +101 -0
- data/test/test_rails_compatibility.rb +8 -3
- data/test/test_validations.rb +193 -22
- metadata +16 -3
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class Comment
|
4
|
+
include MongoMapper::Document
|
5
|
+
|
6
|
+
key :name, String
|
7
|
+
key :body, String
|
8
|
+
|
9
|
+
attr_accessor :callers
|
10
|
+
before_validation :record_callers
|
11
|
+
|
12
|
+
def after_validation
|
13
|
+
record_callers
|
14
|
+
end
|
15
|
+
|
16
|
+
def record_callers
|
17
|
+
callers << self.class if callers
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Article
|
22
|
+
include MongoMapper::Document
|
23
|
+
|
24
|
+
key :title, String
|
25
|
+
key :body, String
|
26
|
+
end
|
27
|
+
|
28
|
+
class CommentObserver < MongoMapper::Observer
|
29
|
+
attr_accessor :callers
|
30
|
+
|
31
|
+
def after_validation(model)
|
32
|
+
callers << self.class if callers
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class AuditObserver < MongoMapper::Observer
|
37
|
+
observe Article, Comment
|
38
|
+
attr_reader :document
|
39
|
+
|
40
|
+
def after_validation(document)
|
41
|
+
@document = document
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class GlobalObserver < MongoMapper::Observer
|
46
|
+
observe Article, Comment
|
47
|
+
attr_reader :document
|
48
|
+
|
49
|
+
def before_save(document)
|
50
|
+
@document = document
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class NonAutomaticObserver < MongoMapper::Observer
|
55
|
+
observe Comment
|
56
|
+
attr_reader :comment
|
57
|
+
|
58
|
+
def after_validation(comment)
|
59
|
+
@comment = comment
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class ObserverTest < Test::Unit::TestCase
|
64
|
+
should "fire model callbacks before observer" do
|
65
|
+
callers = []
|
66
|
+
comment = Comment.new
|
67
|
+
comment.callers = callers
|
68
|
+
|
69
|
+
CommentObserver.instance.callers = callers
|
70
|
+
|
71
|
+
comment.valid?
|
72
|
+
callers.should == [Comment, Comment, CommentObserver]
|
73
|
+
end
|
74
|
+
|
75
|
+
should "automatically observe model based on name when possible" do
|
76
|
+
CommentObserver.observed_class.should == Comment
|
77
|
+
end
|
78
|
+
|
79
|
+
should "be able to observe other models using observe" do
|
80
|
+
obs = NonAutomaticObserver.instance
|
81
|
+
comment = Comment.new(:name => 'John Nunemaker', :body => 'is awesome')
|
82
|
+
comment.valid?
|
83
|
+
obs.comment.name.should == 'John Nunemaker'
|
84
|
+
obs.comment.body.should == 'is awesome'
|
85
|
+
end
|
86
|
+
|
87
|
+
should "be able to observe multiple models" do
|
88
|
+
obs = AuditObserver.instance
|
89
|
+
comment = Comment.new(:name => 'Steve Smith', :body => 'is awesome')
|
90
|
+
comment.valid?
|
91
|
+
|
92
|
+
obs.document.name.should == 'Steve Smith'
|
93
|
+
obs.document.body.should == 'is awesome'
|
94
|
+
|
95
|
+
article = Article.new(:title => 'Ordered List Is Awesome', :body => 'Learn to accept it!')
|
96
|
+
article.valid?
|
97
|
+
|
98
|
+
obs.document.title.should == 'Ordered List Is Awesome'
|
99
|
+
obs.document.body.should == 'Learn to accept it!'
|
100
|
+
end
|
101
|
+
end
|
@@ -6,17 +6,22 @@ class TestRailsCompatibility < Test::Unit::TestCase
|
|
6
6
|
include MongoMapper::Document
|
7
7
|
end
|
8
8
|
end
|
9
|
-
|
9
|
+
|
10
10
|
should "have to_param that returns id" do
|
11
11
|
instance = @document.create('_id' => '1234')
|
12
12
|
instance.to_param.should == '1234'
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
should "alias new to new_record?" do
|
16
16
|
instance = @document.new
|
17
17
|
instance.new_record?.should == instance.new?
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
|
+
should "alias many to has_many" do
|
21
|
+
@document.should respond_to(:has_many)
|
22
|
+
@document.method(:has_many).should == @document.method(:many)
|
23
|
+
end
|
24
|
+
|
20
25
|
should "have column names" do
|
21
26
|
@document.key :fname, String
|
22
27
|
@document.column_names.sort.should == ['_id', 'created_at', 'fname', 'updated_at']
|
data/test/test_validations.rb
CHANGED
@@ -7,7 +7,7 @@ class ValidationsTest < Test::Unit::TestCase
|
|
7
7
|
include MongoMapper::Document
|
8
8
|
end
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
context "Validating acceptance of" do
|
12
12
|
should "work with validates_acceptance_of macro" do
|
13
13
|
@document.key :terms, String
|
@@ -40,7 +40,7 @@ class ValidationsTest < Test::Unit::TestCase
|
|
40
40
|
doc.name = 'John'
|
41
41
|
doc.should_not have_error_on(:name)
|
42
42
|
end
|
43
|
-
|
43
|
+
|
44
44
|
should "work with :format shorcut key" do
|
45
45
|
@document.key :name, String, :format => /.+/
|
46
46
|
doc = @document.new
|
@@ -49,7 +49,7 @@ class ValidationsTest < Test::Unit::TestCase
|
|
49
49
|
doc.should_not have_error_on(:name)
|
50
50
|
end
|
51
51
|
end
|
52
|
-
|
52
|
+
|
53
53
|
context "validating length of" do
|
54
54
|
should "work with validates_length_of macro" do
|
55
55
|
@document.key :name, String
|
@@ -57,7 +57,7 @@ class ValidationsTest < Test::Unit::TestCase
|
|
57
57
|
doc = @document.new
|
58
58
|
doc.should have_error_on(:name)
|
59
59
|
end
|
60
|
-
|
60
|
+
|
61
61
|
context "with :length => integer shortcut" do
|
62
62
|
should "set maximum of integer provided" do
|
63
63
|
@document.key :name, String, :length => 5
|
@@ -68,19 +68,19 @@ class ValidationsTest < Test::Unit::TestCase
|
|
68
68
|
doc.should_not have_error_on(:name)
|
69
69
|
end
|
70
70
|
end
|
71
|
-
|
71
|
+
|
72
72
|
context "with :length => range shortcut" do
|
73
73
|
setup do
|
74
74
|
@document.key :name, String, :length => 5..7
|
75
75
|
end
|
76
|
-
|
76
|
+
|
77
77
|
should "set minimum of range min" do
|
78
78
|
doc = @document.new
|
79
79
|
doc.should have_error_on(:name)
|
80
80
|
doc.name = '123456'
|
81
81
|
doc.should_not have_error_on(:name)
|
82
82
|
end
|
83
|
-
|
83
|
+
|
84
84
|
should "set maximum of range max" do
|
85
85
|
doc = @document.new
|
86
86
|
doc.should have_error_on(:name)
|
@@ -90,7 +90,7 @@ class ValidationsTest < Test::Unit::TestCase
|
|
90
90
|
doc.should_not have_error_on(:name)
|
91
91
|
end
|
92
92
|
end
|
93
|
-
|
93
|
+
|
94
94
|
context "with :length => hash shortcut" do
|
95
95
|
should "pass options through" do
|
96
96
|
@document.key :name, String, :length => {:minimum => 2}
|
@@ -101,7 +101,7 @@ class ValidationsTest < Test::Unit::TestCase
|
|
101
101
|
end
|
102
102
|
end
|
103
103
|
end # validates_length_of
|
104
|
-
|
104
|
+
|
105
105
|
context "Validating numericality of" do
|
106
106
|
should "work with validates_numericality_of macro" do
|
107
107
|
@document.key :age, Integer
|
@@ -112,7 +112,7 @@ class ValidationsTest < Test::Unit::TestCase
|
|
112
112
|
doc.age = 23
|
113
113
|
doc.should_not have_error_on(:age)
|
114
114
|
end
|
115
|
-
|
115
|
+
|
116
116
|
context "with :numeric shortcut" do
|
117
117
|
should "work with integer or float" do
|
118
118
|
@document.key :weight, Float, :numeric => true
|
@@ -125,7 +125,7 @@ class ValidationsTest < Test::Unit::TestCase
|
|
125
125
|
doc.should_not have_error_on(:weight)
|
126
126
|
end
|
127
127
|
end
|
128
|
-
|
128
|
+
|
129
129
|
context "with :numeric shortcut on Integer key" do
|
130
130
|
should "only work with integers" do
|
131
131
|
@document.key :age, Integer, :numeric => true
|
@@ -139,7 +139,7 @@ class ValidationsTest < Test::Unit::TestCase
|
|
139
139
|
end
|
140
140
|
end
|
141
141
|
end # numericality of
|
142
|
-
|
142
|
+
|
143
143
|
context "validating presence of" do
|
144
144
|
should "work with validates_presence_of macro" do
|
145
145
|
@document.key :name, String
|
@@ -147,22 +147,140 @@ class ValidationsTest < Test::Unit::TestCase
|
|
147
147
|
doc = @document.new
|
148
148
|
doc.should have_error_on(:name)
|
149
149
|
end
|
150
|
-
|
150
|
+
|
151
151
|
should "work with :required shortcut on key definition" do
|
152
152
|
@document.key :name, String, :required => true
|
153
153
|
doc = @document.new
|
154
154
|
doc.should have_error_on(:name)
|
155
155
|
end
|
156
|
-
end
|
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
|
+
|
198
|
+
context "validating exclusion of" do
|
199
|
+
should "throw error if enumerator not provided" do
|
200
|
+
@document.key :action, String
|
201
|
+
lambda {
|
202
|
+
@document.validates_exclusion_of :action
|
203
|
+
}.should raise_error(ArgumentError)
|
204
|
+
end
|
205
|
+
|
206
|
+
should "work with validates_exclusion_of macro" do
|
207
|
+
@document.key :action, String
|
208
|
+
@document.validates_exclusion_of :action, :within => %w(kick run)
|
209
|
+
|
210
|
+
doc = @document.new
|
211
|
+
doc.should_not have_error_on(:action)
|
212
|
+
|
213
|
+
doc.action = 'fart'
|
214
|
+
doc.should_not have_error_on(:action)
|
215
|
+
|
216
|
+
doc.action = 'kick'
|
217
|
+
doc.should have_error_on(:action, 'is reserved')
|
218
|
+
end
|
219
|
+
|
220
|
+
should "not have error if allow nil is true and value is nil" do
|
221
|
+
@document.key :action, String
|
222
|
+
@document.validates_exclusion_of :action, :within => %w(kick run), :allow_nil => true
|
223
|
+
|
224
|
+
doc = @document.new
|
225
|
+
doc.should_not have_error_on(:action)
|
226
|
+
end
|
227
|
+
|
228
|
+
should "not have error if allow blank is true and value is blank" do
|
229
|
+
@document.key :action, String
|
230
|
+
@document.validates_exclusion_of :action, :within => %w(kick run), :allow_nil => true
|
231
|
+
|
232
|
+
doc = @document.new(:action => '')
|
233
|
+
doc.should_not have_error_on(:action)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
context "validating inclusion of" do
|
238
|
+
should "throw error if enumerator not provided" do
|
239
|
+
@document.key :action, String
|
240
|
+
lambda {
|
241
|
+
@document.validates_inclusion_of :action
|
242
|
+
}.should raise_error(ArgumentError)
|
243
|
+
end
|
244
|
+
|
245
|
+
should "work with validates_inclusion_of macro" do
|
246
|
+
@document.key :action, String
|
247
|
+
@document.validates_inclusion_of :action, :within => %w(kick run)
|
248
|
+
|
249
|
+
doc = @document.new
|
250
|
+
doc.should have_error_on(:action, 'is not in the list')
|
251
|
+
|
252
|
+
doc.action = 'fart'
|
253
|
+
doc.should have_error_on(:action, 'is not in the list')
|
254
|
+
|
255
|
+
doc.action = 'kick'
|
256
|
+
doc.should_not have_error_on(:action)
|
257
|
+
end
|
258
|
+
|
259
|
+
should "not have error if allow nil is true and value is nil" do
|
260
|
+
@document.key :action, String
|
261
|
+
@document.validates_inclusion_of :action, :within => %w(kick run), :allow_nil => true
|
262
|
+
|
263
|
+
doc = @document.new
|
264
|
+
doc.should_not have_error_on(:action)
|
265
|
+
end
|
266
|
+
|
267
|
+
should "not have error if allow blank is true and value is blank" do
|
268
|
+
@document.key :action, String
|
269
|
+
@document.validates_inclusion_of :action, :within => %w(kick run), :allow_blank => true
|
270
|
+
|
271
|
+
doc = @document.new(:action => '')
|
272
|
+
doc.should_not have_error_on(:action)
|
273
|
+
end
|
274
|
+
end
|
157
275
|
end # Validations
|
158
|
-
|
276
|
+
|
159
277
|
context "Saving a new document that is invalid" do
|
160
278
|
setup do
|
161
279
|
@document = Class.new do
|
162
280
|
include MongoMapper::Document
|
163
281
|
key :name, String, :required => true
|
164
282
|
end
|
165
|
-
|
283
|
+
|
166
284
|
@document.collection.clear
|
167
285
|
end
|
168
286
|
|
@@ -171,7 +289,7 @@ class ValidationsTest < Test::Unit::TestCase
|
|
171
289
|
doc.save
|
172
290
|
@document.count.should == 0
|
173
291
|
end
|
174
|
-
|
292
|
+
|
175
293
|
should "populate document's errors" do
|
176
294
|
doc = @document.new
|
177
295
|
doc.errors.size.should == 0
|
@@ -179,14 +297,14 @@ class ValidationsTest < Test::Unit::TestCase
|
|
179
297
|
doc.errors.full_messages.should == ["Name can't be empty"]
|
180
298
|
end
|
181
299
|
end
|
182
|
-
|
300
|
+
|
183
301
|
context "Saving a document that is invalid (destructive)" do
|
184
302
|
setup do
|
185
303
|
@document = Class.new do
|
186
304
|
include MongoMapper::Document
|
187
305
|
key :name, String, :required => true
|
188
306
|
end
|
189
|
-
|
307
|
+
|
190
308
|
@document.collection.clear
|
191
309
|
end
|
192
310
|
|
@@ -195,14 +313,14 @@ class ValidationsTest < Test::Unit::TestCase
|
|
195
313
|
lambda { doc.save! }.should raise_error(MongoMapper::DocumentNotValid)
|
196
314
|
end
|
197
315
|
end
|
198
|
-
|
316
|
+
|
199
317
|
context "Saving an existing document that is invalid" do
|
200
318
|
setup do
|
201
319
|
@document = Class.new do
|
202
320
|
include MongoMapper::Document
|
203
321
|
key :name, String, :required => true
|
204
322
|
end
|
205
|
-
|
323
|
+
|
206
324
|
@document.collection.clear
|
207
325
|
@doc = @document.create(:name => 'John Nunemaker')
|
208
326
|
end
|
@@ -212,11 +330,64 @@ class ValidationsTest < Test::Unit::TestCase
|
|
212
330
|
@doc.save
|
213
331
|
@document.find(@doc.id).name.should == 'John Nunemaker'
|
214
332
|
end
|
215
|
-
|
333
|
+
|
216
334
|
should "populate document's errors" do
|
217
335
|
@doc.name = nil
|
218
336
|
@doc.save
|
219
337
|
@doc.errors.full_messages.should == ["Name can't be empty"]
|
220
338
|
end
|
221
339
|
end
|
340
|
+
|
341
|
+
context "Adding validation errors" do
|
342
|
+
setup do
|
343
|
+
@document = Class.new do
|
344
|
+
include MongoMapper::Document
|
345
|
+
key :action, String
|
346
|
+
def action_present
|
347
|
+
errors.add(:action, 'is invalid') if action.blank?
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
should "work with validate callback" do
|
353
|
+
@document.validate :action_present
|
354
|
+
|
355
|
+
doc = @document.new
|
356
|
+
doc.action = nil
|
357
|
+
doc.should have_error_on(:action)
|
358
|
+
|
359
|
+
doc.action = 'kick'
|
360
|
+
doc.should_not have_error_on(:action)
|
361
|
+
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
|
+
end
|
222
393
|
end
|