ant-mapper 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/CHANGE +6 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README +25 -0
  4. data/ant.gemspec +48 -0
  5. data/data.tch +0 -0
  6. data/examples/account.rb +71 -0
  7. data/examples/data.tch +0 -0
  8. data/examples/light_cloud.yml +18 -0
  9. data/examples/user.rb +84 -0
  10. data/init.rb +4 -0
  11. data/lib/ant.rb +7 -0
  12. data/lib/ant_mapper.rb +10 -0
  13. data/lib/ant_mapper/adapters/light_cloud.rb +59 -0
  14. data/lib/ant_mapper/adapters/tokyo_cabinet.rb +42 -0
  15. data/lib/ant_mapper/adapters/tokyo_tyrant.rb +14 -0
  16. data/lib/ant_mapper/base.rb +367 -0
  17. data/lib/ant_mapper/callbacks.rb +180 -0
  18. data/lib/ant_mapper/observer.rb +180 -0
  19. data/lib/ant_mapper/validations.rb +687 -0
  20. data/lib/ant_support.rb +4 -0
  21. data/lib/ant_support/callbacks.rb +303 -0
  22. data/lib/ant_support/core_ext.rb +4 -0
  23. data/lib/ant_support/core_ext/array.rb +5 -0
  24. data/lib/ant_support/core_ext/array/extract_options.rb +20 -0
  25. data/lib/ant_support/core_ext/blank.rb +58 -0
  26. data/lib/ant_support/core_ext/class.rb +3 -0
  27. data/lib/ant_support/core_ext/class/attribute_accessors.rb +54 -0
  28. data/lib/ant_support/core_ext/class/inheritable_attributes.rb +140 -0
  29. data/lib/ant_support/core_ext/class/removal.rb +50 -0
  30. data/lib/ant_support/core_ext/duplicable.rb +43 -0
  31. data/lib/ant_support/core_ext/enumerable.rb +72 -0
  32. data/lib/ant_support/core_ext/hash.rb +6 -0
  33. data/lib/ant_support/core_ext/hash/keys.rb +52 -0
  34. data/lib/ant_support/core_ext/module.rb +16 -0
  35. data/lib/ant_support/core_ext/module/aliasing.rb +74 -0
  36. data/lib/ant_support/core_ext/module/attr_accessor_with_default.rb +31 -0
  37. data/lib/ant_support/core_ext/module/attribute_accessors.rb +58 -0
  38. data/lib/ant_support/core_ext/object.rb +1 -0
  39. data/lib/ant_support/core_ext/object/extending.rb +80 -0
  40. data/lib/ant_support/core_ext/string.rb +7 -0
  41. data/lib/ant_support/core_ext/string/inflections.rb +51 -0
  42. data/spec/case/callbacks_observers_test.rb +38 -0
  43. data/spec/case/callbacks_test.rb +417 -0
  44. data/spec/case/create_object_test.rb +56 -0
  45. data/spec/case/set_class_name_test.rb +17 -0
  46. data/spec/case/validations_test.rb +1482 -0
  47. data/spec/helper.rb +15 -0
  48. data/spec/light_cloud.yml +18 -0
  49. data/spec/model/account.rb +3 -0
  50. data/spec/model/topic.rb +28 -0
  51. data/spec/model/user.rb +4 -0
  52. metadata +125 -0
@@ -0,0 +1,56 @@
1
+ require '../helper'
2
+ require '../model/user'
3
+
4
+ class CreateObjectTest < Test::Unit::TestCase
5
+ def setup
6
+ @attributes = {:name=>'aaron',:email=>'aaron@nonobo.com',:password=>'123456'}
7
+ end
8
+
9
+ def test_new_object
10
+ @u = User.new
11
+ assert_equal true,@u.new_record?
12
+ User.attributes.each do |attribute|
13
+ assert_equal true,@u.respond_to?(attribute)
14
+ end
15
+ end
16
+
17
+ def test_new_object_with_attributes
18
+ @u = User.new(@attributes)
19
+ User.attributes.each do |attribute|
20
+ assert_equal @attributes[attribute],@u.send(attribute)
21
+ end
22
+ end
23
+
24
+ def test_create_object
25
+ @u = User.create(@attributes)
26
+ assert_equal false,@u.new_record?
27
+ @u.destroy
28
+ end
29
+
30
+ def test_find_object
31
+ User.create(@attributes)
32
+ @u = User.find(@attributes[:email])
33
+ User.attributes.each do |attribute|
34
+ assert_equal @attributes[attribute],@u.send(attribute)
35
+ end
36
+ assert_equal false,@u.new_record?
37
+ end
38
+
39
+ def test_delete_object
40
+ User.create(@attributes)
41
+ User.delete(@attributes[:email])
42
+ assert_raise AntMapper::ObjectNotFound do
43
+ User.find(@attributes[:email])
44
+ end
45
+ end
46
+
47
+ def test_create_multiple_object
48
+ @objects = [{:name=>'aaron',:email=>'aaron@nonobo.com'},{:name=>'yalong',:email=>'yalong@nonobo.com'}]
49
+ User.create(@objects)
50
+ @objects.each do |object|
51
+ @u = User.find(object[:email])
52
+ assert_equal object[:email],@u.email
53
+ @u.destroy
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,17 @@
1
+ require '../helper'
2
+ require '../model/user'
3
+ require '../model/account'
4
+
5
+ class SetClassNameTest < Test::Unit::TestCase
6
+ def setup
7
+ @attributes = {:name=>'aaron',:email=>'aaron@nonobo.com',:password=>'123456'}
8
+ end
9
+
10
+ def test_create_object
11
+ User.create(@attributes)
12
+ assert_nothing_raised do
13
+ @u = User.find(@attributes[:email])
14
+ end
15
+ @u.destroy
16
+ end
17
+ end
@@ -0,0 +1,1482 @@
1
+ # encoding: utf-8
2
+ require "../helper"
3
+ require '../model/topic'
4
+
5
+ class ValidationsTest < Test::Unit::TestCase
6
+
7
+ def setup
8
+
9
+ end
10
+
11
+ def test_single_field_validation
12
+ r = Topic.new
13
+ r.title = "There's no content!"
14
+ assert !r.valid?, "A reply without content shouldn't be saveable"
15
+
16
+ r.content = "Messa content!"
17
+ assert r.valid?, "A reply with content should be saveable"
18
+ end
19
+
20
+
21
+ def test_single_attr_validation_and_error_msg
22
+ r = Topic.new
23
+ r.title = "There's no content!"
24
+ assert !r.valid?
25
+ assert r.errors.invalid?("content"), "A reply without content should mark that attribute as invalid"
26
+ assert_equal AntMapper::Errors.default_error_messages[:blank], r.errors.on("content"), "A reply without content should contain an error"
27
+ assert_equal 1, r.errors.count
28
+ end
29
+
30
+ def test_double_attr_validation_and_error_msg
31
+ r = Topic.new
32
+ assert !r.valid?
33
+
34
+ assert r.errors.invalid?("title"), "A reply without title should mark that attribute as invalid"
35
+ assert_equal AntMapper::Errors.default_error_messages[:blank], r.errors.on("title"), "A reply without title should contain an error"
36
+
37
+ assert r.errors.invalid?("content"), "A reply without content should mark that attribute as invalid"
38
+ assert_equal AntMapper::Errors.default_error_messages[:blank], r.errors.on("content"), "A reply without content should contain an error"
39
+
40
+ assert_equal 2, r.errors.count
41
+ end
42
+
43
+ def test_error_on_create
44
+ r = Topic.new
45
+ r.title = "Wrong Create"
46
+ assert !r.valid?
47
+ assert r.errors.invalid?("title"), "A reply with a bad title should mark that attribute as invalid"
48
+ assert_equal "is Wrong Create", r.errors.on("title"), "A reply with a bad content should contain an error"
49
+ end
50
+
51
+
52
+ def test_error_on_update
53
+ r = Topic.new
54
+ r.title = "Bad"
55
+ r.content = "Good"
56
+ assert r.save, "First save should be successful"
57
+
58
+ r.title = "Wrong Update"
59
+ assert !r.save, "Second save should fail"
60
+
61
+ assert r.errors.invalid?("title"), "A reply with a bad title should mark that attribute as invalid"
62
+ assert_equal "is Wrong Update", r.errors.on("title"), "A reply with a bad content should contain an error"
63
+ end
64
+
65
+
66
+
67
+ def test_invalid_record_exception
68
+ assert_raises(AntMapper::ObjectInvalid) { Topic.create! }
69
+ assert_raises(AntMapper::ObjectInvalid) { Topic.new.save! }
70
+
71
+ begin
72
+ r = Topic.new
73
+ r.save!
74
+ flunk
75
+ rescue AntMapper::ObjectInvalid => invalid
76
+ assert_equal r, invalid.object
77
+ end
78
+ end
79
+
80
+
81
+ def test_exception_on_create_bang_many
82
+ assert_raises(AntMapper::ObjectInvalid) do
83
+ Topic.create!([ { "title" => "OK" }, { "title" => "Wrong Create" }])
84
+ end
85
+ end
86
+
87
+ def test_exception_on_create_bang_with_block
88
+ assert_raises(AntMapper::ObjectInvalid) do
89
+ Topic.create!({ "title" => "OK" }) do |r|
90
+ r.content = nil
91
+ end
92
+ end
93
+ end
94
+
95
+
96
+
97
+ def test_exception_on_create_bang_many_with_block
98
+ assert_raises(AntMapper::ObjectInvalid) do
99
+ Topic.create!([{ "title" => "OK" }, { "title" => "Wrong Create" }]) do |r|
100
+ r.content = nil
101
+ end
102
+ end
103
+ end
104
+
105
+
106
+ def test_single_error_per_attr_iteration
107
+ r = Topic.new
108
+ r.save
109
+
110
+ errors = []
111
+ r.errors.each { |attr, msg| errors << [attr, msg] }
112
+
113
+ assert errors.include?(["title", AntMapper::Errors.default_error_messages[:blank]])
114
+ assert errors.include?(["content",AntMapper::Errors.default_error_messages[:blank]])
115
+ end
116
+
117
+ def test_multiple_errors_per_attr_iteration_with_full_error_composition
118
+ r = Topic.new
119
+ r.title = "Wrong Create"
120
+ r.content = "Mismatch"
121
+ r.save
122
+
123
+ errors = []
124
+ r.errors.each_full { |error| errors << error }
125
+
126
+ assert_equal "title is Wrong Create", errors[0]
127
+ assert_equal "title is Content Mismatch", errors[1]
128
+ assert_equal 2, r.errors.count
129
+ end
130
+
131
+
132
+
133
+ def test_errors_on_base
134
+ r = Topic.new
135
+ r.content = "Mismatch"
136
+ r.save
137
+ r.errors.add_to_base "Topic is not dignifying"
138
+
139
+ errors = []
140
+ r.errors.each_full { |error| errors << error }
141
+
142
+ assert_equal "Topic is not dignifying", r.errors.on_base
143
+
144
+ assert errors.include?("title #{AntMapper::Errors.default_error_messages[:blank]}")
145
+ assert errors.include?("Topic is not dignifying")
146
+ assert_equal 3, r.errors.count
147
+ end
148
+
149
+
150
+ def test_create_without_validation
151
+ reply = Topic.new
152
+ assert !reply.save
153
+ assert reply.save(false)
154
+ end
155
+
156
+
157
+ def test_validates_each
158
+ perform = true
159
+ hits = 0
160
+ Topic.validates_each(:title, :content, [:title, :content]) do |record, attr|
161
+ if perform
162
+ record.errors.add attr, 'gotcha'
163
+ hits += 1
164
+ end
165
+ end
166
+ t = Topic.new("title" => "valid", "content" => "whatever")
167
+ assert !t.save
168
+ assert_equal 4, hits
169
+ assert_equal %w(gotcha gotcha), t.errors.on(:title)
170
+ assert_equal %w(gotcha gotcha), t.errors.on(:content)
171
+ ensure
172
+ perform = false
173
+ end
174
+
175
+ =begin
176
+ def test_no_title_confirmation
177
+ Topic.validates_confirmation_of(:title)
178
+
179
+ t = Topic.new(:author_name => "Plutarch")
180
+ assert t.valid?
181
+
182
+ t.title_confirmation = "Parallel Lives"
183
+ assert !t.valid?
184
+
185
+ t.title_confirmation = nil
186
+ t.title = "Parallel Lives"
187
+ assert t.valid?
188
+
189
+ t.title_confirmation = "Parallel Lives"
190
+ assert t.valid?
191
+ end
192
+
193
+ def test_title_confirmation
194
+ Topic.validates_confirmation_of(:title)
195
+
196
+ t = Topic.create("title" => "We should be confirmed","title_confirmation" => "")
197
+ assert !t.save
198
+
199
+ t.title_confirmation = "We should be confirmed"
200
+ assert t.save
201
+ end
202
+
203
+
204
+ def test_terms_of_service_agreement_no_acceptance
205
+ Topic.validates_acceptance_of(:terms_of_service, :on => :create)
206
+
207
+ t = Topic.create("title" => "We should not be confirmed")
208
+ assert t.save
209
+ end
210
+
211
+ def test_terms_of_service_agreement
212
+ Topic.validates_acceptance_of(:terms_of_service, :on => :create)
213
+
214
+ t = Topic.create("title" => "We should be confirmed","terms_of_service" => "")
215
+ assert !t.save
216
+ assert_equal "must be accepted", t.errors.on(:terms_of_service)
217
+
218
+ t.terms_of_service = "1"
219
+ assert t.save
220
+ end
221
+
222
+
223
+ def test_eula
224
+ Topic.validates_acceptance_of(:eula, :message => "must be abided", :on => :create)
225
+
226
+ t = Topic.create("title" => "We should be confirmed","eula" => "")
227
+ assert !t.save
228
+ assert_equal "must be abided", t.errors.on(:eula)
229
+
230
+ t.eula = "1"
231
+ assert t.save
232
+ end
233
+
234
+ def test_terms_of_service_agreement_with_accept_value
235
+ Topic.validates_acceptance_of(:terms_of_service, :on => :create, :accept => "I agree.")
236
+
237
+ t = Topic.create("title" => "We should be confirmed", "terms_of_service" => "")
238
+ assert !t.save
239
+ assert_equal "must be accepted", t.errors.on(:terms_of_service)
240
+
241
+ t.terms_of_service = "I agree."
242
+ assert t.save
243
+ end
244
+
245
+ def test_validates_acceptance_of_as_database_column
246
+ reply = PlagiarizedReply.create("author_name" => "Dan Brown")
247
+ assert_equal "Dan Brown", reply["author_name"]
248
+ end
249
+
250
+ def test_validates_acceptance_of_with_non_existant_table
251
+ Object.const_set :IncorporealModel, Class.new(AntMapper::Base)
252
+
253
+ assert_nothing_raised AntMapper::StatementInvalid do
254
+ IncorporealModel.validates_acceptance_of(:incorporeal_column)
255
+ end
256
+ end
257
+
258
+ def test_validate_presences
259
+ Topic.validates_presence_of(:title, :content)
260
+
261
+ t = Topic.create
262
+ assert !t.save
263
+ assert_equal "can't be blank", t.errors.on(:title)
264
+ assert_equal "can't be blank", t.errors.on(:content)
265
+
266
+ t.title = "something"
267
+ t.content = " "
268
+
269
+ assert !t.save
270
+ assert_equal "can't be blank", t.errors.on(:content)
271
+
272
+ t.content = "like stuff"
273
+
274
+ assert t.save
275
+ end
276
+
277
+ def test_validate_uniqueness
278
+ Topic.validates_uniqueness_of(:title)
279
+
280
+ t = Topic.new("title" => "I'm unique!")
281
+ assert t.save, "Should save t as unique"
282
+
283
+ t.content = "Remaining unique"
284
+ assert t.save, "Should still save t as unique"
285
+
286
+ t2 = Topic.new("title" => "I'm unique!")
287
+ assert !t2.valid?, "Shouldn't be valid"
288
+ assert !t2.save, "Shouldn't save t2 as unique"
289
+ assert_equal "has already been taken", t2.errors.on(:title)
290
+
291
+ t2.title = "Now Im really also unique"
292
+ assert t2.save, "Should now save t2 as unique"
293
+ end
294
+
295
+ def test_validates_uniquness_with_newline_chars
296
+ Topic.validates_uniqueness_of(:title, :case_sensitive => false)
297
+
298
+ t = Topic.new("title" => "new\nline")
299
+ assert t.save, "Should save t as unique"
300
+ end
301
+
302
+ def test_validate_uniqueness_with_scope
303
+ Reply.validates_uniqueness_of(:content, :scope => "parent_id")
304
+
305
+ t = Topic.create("title" => "I'm unique!")
306
+
307
+ r1 = t.replies.create "title" => "r1", "content" => "hello world"
308
+ assert r1.valid?, "Saving r1"
309
+
310
+ r2 = t.replies.create "title" => "r2", "content" => "hello world"
311
+ assert !r2.valid?, "Saving r2 first time"
312
+
313
+ r2.content = "something else"
314
+ assert r2.save, "Saving r2 second time"
315
+
316
+ t2 = Topic.create("title" => "I'm unique too!")
317
+ r3 = t2.replies.create "title" => "r3", "content" => "hello world"
318
+ assert r3.valid?, "Saving r3"
319
+ end
320
+
321
+ def test_validate_uniqueness_scoped_to_defining_class
322
+ t = Topic.create("title" => "What, me worry?")
323
+
324
+ r1 = t.unique_replies.create "title" => "r1", "content" => "a barrel of fun"
325
+ assert r1.valid?, "Saving r1"
326
+
327
+ r2 = t.silly_unique_replies.create "title" => "r2", "content" => "a barrel of fun"
328
+ assert !r2.valid?, "Saving r2"
329
+
330
+ # Should succeed as validates_uniqueness_of only applies to
331
+ # UniqueReply and its subclasses
332
+ r3 = t.replies.create "title" => "r2", "content" => "a barrel of fun"
333
+ assert r3.valid?, "Saving r3"
334
+ end
335
+
336
+ def test_validate_uniqueness_with_scope_array
337
+ Reply.validates_uniqueness_of(:author_name, :scope => [:author_email_address, :parent_id])
338
+
339
+ t = Topic.create("title" => "The earth is actually flat!")
340
+
341
+ r1 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy@rubyonrails.com", "title" => "You're crazy!", "content" => "Crazy reply"
342
+ assert r1.valid?, "Saving r1"
343
+
344
+ r2 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy@rubyonrails.com", "title" => "You're crazy!", "content" => "Crazy reply again..."
345
+ assert !r2.valid?, "Saving r2. Double reply by same author."
346
+
347
+ r2.author_email_address = "jeremy_alt_email@rubyonrails.com"
348
+ assert r2.save, "Saving r2 the second time."
349
+
350
+ r3 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy_alt_email@rubyonrails.com", "title" => "You're wrong", "content" => "It's cubic"
351
+ assert !r3.valid?, "Saving r3"
352
+
353
+ r3.author_name = "jj"
354
+ assert r3.save, "Saving r3 the second time."
355
+
356
+ r3.author_name = "jeremy"
357
+ assert !r3.save, "Saving r3 the third time."
358
+ end
359
+
360
+ def test_validate_case_insensitive_uniqueness
361
+ Topic.validates_uniqueness_of(:title, :parent_id, :case_sensitive => false, :allow_nil => true)
362
+
363
+ t = Topic.new("title" => "I'm unique!", :parent_id => 2)
364
+ assert t.save, "Should save t as unique"
365
+
366
+ t.content = "Remaining unique"
367
+ assert t.save, "Should still save t as unique"
368
+
369
+ t2 = Topic.new("title" => "I'm UNIQUE!", :parent_id => 1)
370
+ assert !t2.valid?, "Shouldn't be valid"
371
+ assert !t2.save, "Shouldn't save t2 as unique"
372
+ assert t2.errors.on(:title)
373
+ assert t2.errors.on(:parent_id)
374
+ assert_equal "has already been taken", t2.errors.on(:title)
375
+
376
+ t2.title = "I'm truly UNIQUE!"
377
+ assert !t2.valid?, "Shouldn't be valid"
378
+ assert !t2.save, "Shouldn't save t2 as unique"
379
+ assert_nil t2.errors.on(:title)
380
+ assert t2.errors.on(:parent_id)
381
+
382
+ t2.parent_id = 4
383
+ assert t2.save, "Should now save t2 as unique"
384
+
385
+ t2.parent_id = nil
386
+ t2.title = nil
387
+ assert t2.valid?, "should validate with nil"
388
+ assert t2.save, "should save with nil"
389
+
390
+ with_kcode('UTF8') do
391
+ t_utf8 = Topic.new("title" => "Я тоже уникальный!")
392
+ assert t_utf8.save, "Should save t_utf8 as unique"
393
+
394
+ # If database hasn't UTF-8 character set, this test fails
395
+ if Topic.find(t_utf8, :select => 'LOWER(title) AS title').title == "я тоже уникальный!"
396
+ t2_utf8 = Topic.new("title" => "я тоже УНИКАЛЬНЫЙ!")
397
+ assert !t2_utf8.valid?, "Shouldn't be valid"
398
+ assert !t2_utf8.save, "Shouldn't save t2_utf8 as unique"
399
+ end
400
+ end
401
+ end
402
+
403
+ def test_validate_case_sensitive_uniqueness
404
+ Topic.validates_uniqueness_of(:title, :case_sensitive => true, :allow_nil => true)
405
+
406
+ t = Topic.new("title" => "I'm unique!")
407
+ assert t.save, "Should save t as unique"
408
+
409
+ t.content = "Remaining unique"
410
+ assert t.save, "Should still save t as unique"
411
+
412
+ t2 = Topic.new("title" => "I'M UNIQUE!")
413
+ assert t2.valid?, "Should be valid"
414
+ assert t2.save, "Should save t2 as unique"
415
+ assert !t2.errors.on(:title)
416
+ assert !t2.errors.on(:parent_id)
417
+ assert_not_equal "has already been taken", t2.errors.on(:title)
418
+
419
+ t3 = Topic.new("title" => "I'M uNiQUe!")
420
+ assert t3.valid?, "Should be valid"
421
+ assert t3.save, "Should save t2 as unique"
422
+ assert !t3.errors.on(:title)
423
+ assert !t3.errors.on(:parent_id)
424
+ assert_not_equal "has already been taken", t3.errors.on(:title)
425
+ end
426
+
427
+ def test_validate_case_sensitive_uniqueness_with_attribute_passed_as_integer
428
+ Topic.validates_uniqueness_of(:title, :case_sensitve => true)
429
+ t = Topic.create!('title' => 101)
430
+
431
+ t2 = Topic.new('title' => 101)
432
+ assert !t2.valid?
433
+ assert t2.errors.on(:title)
434
+ end
435
+
436
+ def test_validate_uniqueness_with_non_standard_table_names
437
+ i1 = WarehouseThing.create(:value => 1000)
438
+ assert !i1.valid?, "i1 should not be valid"
439
+ assert i1.errors.on(:value), "Should not be empty"
440
+ end
441
+
442
+ def test_validates_uniqueness_inside_with_scope
443
+ Topic.validates_uniqueness_of(:title)
444
+
445
+ Topic.with_scope(:find => { :conditions => { :author_name => "David" } }) do
446
+ t1 = Topic.new("title" => "I'm unique!", "author_name" => "Mary")
447
+ assert t1.save
448
+ t2 = Topic.new("title" => "I'm unique!", "author_name" => "David")
449
+ assert !t2.valid?
450
+ end
451
+ end
452
+
453
+ def test_validate_uniqueness_with_columns_which_are_sql_keywords
454
+ Guid.validates_uniqueness_of :key
455
+ g = Guid.new
456
+ g.key = "foo"
457
+ assert_nothing_raised { !g.valid? }
458
+ end
459
+
460
+ def test_validate_straight_inheritance_uniqueness
461
+ w1 = IneptWizard.create(:name => "Rincewind", :city => "Ankh-Morpork")
462
+ assert w1.valid?, "Saving w1"
463
+
464
+ # Should use validation from base class (which is abstract)
465
+ w2 = IneptWizard.new(:name => "Rincewind", :city => "Quirm")
466
+ assert !w2.valid?, "w2 shouldn't be valid"
467
+ assert w2.errors.on(:name), "Should have errors for name"
468
+ assert_equal "has already been taken", w2.errors.on(:name), "Should have uniqueness message for name"
469
+
470
+ w3 = Conjurer.new(:name => "Rincewind", :city => "Quirm")
471
+ assert !w3.valid?, "w3 shouldn't be valid"
472
+ assert w3.errors.on(:name), "Should have errors for name"
473
+ assert_equal "has already been taken", w3.errors.on(:name), "Should have uniqueness message for name"
474
+
475
+ w4 = Conjurer.create(:name => "The Amazing Bonko", :city => "Quirm")
476
+ assert w4.valid?, "Saving w4"
477
+
478
+ w5 = Thaumaturgist.new(:name => "The Amazing Bonko", :city => "Lancre")
479
+ assert !w5.valid?, "w5 shouldn't be valid"
480
+ assert w5.errors.on(:name), "Should have errors for name"
481
+ assert_equal "has already been taken", w5.errors.on(:name), "Should have uniqueness message for name"
482
+
483
+ w6 = Thaumaturgist.new(:name => "Mustrum Ridcully", :city => "Quirm")
484
+ assert !w6.valid?, "w6 shouldn't be valid"
485
+ assert w6.errors.on(:city), "Should have errors for city"
486
+ assert_equal "has already been taken", w6.errors.on(:city), "Should have uniqueness message for city"
487
+ end
488
+
489
+ def test_validate_format
490
+ Topic.validates_format_of(:title, :content, :with => /^Validation\smacros \w+!$/, :message => "is bad data")
491
+
492
+ t = Topic.create("title" => "i'm incorrect", "content" => "Validation macros rule!")
493
+ assert !t.valid?, "Shouldn't be valid"
494
+ assert !t.save, "Shouldn't save because it's invalid"
495
+ assert_equal "is bad data", t.errors.on(:title)
496
+ assert_nil t.errors.on(:content)
497
+
498
+ t.title = "Validation macros rule!"
499
+
500
+ assert t.save
501
+ assert_nil t.errors.on(:title)
502
+
503
+ assert_raise(ArgumentError) { Topic.validates_format_of(:title, :content) }
504
+ end
505
+
506
+ def test_validate_format_with_allow_blank
507
+ Topic.validates_format_of(:title, :with => /^Validation\smacros \w+!$/, :allow_blank=>true)
508
+ assert !Topic.create("title" => "Shouldn't be valid").valid?
509
+ assert Topic.create("title" => "").valid?
510
+ assert Topic.create("title" => nil).valid?
511
+ assert Topic.create("title" => "Validation macros rule!").valid?
512
+ end
513
+
514
+ # testing ticket #3142
515
+ def test_validate_format_numeric
516
+ Topic.validates_format_of(:title, :content, :with => /^[1-9][0-9]*$/, :message => "is bad data")
517
+
518
+ t = Topic.create("title" => "72x", "content" => "6789")
519
+ assert !t.valid?, "Shouldn't be valid"
520
+ assert !t.save, "Shouldn't save because it's invalid"
521
+ assert_equal "is bad data", t.errors.on(:title)
522
+ assert_nil t.errors.on(:content)
523
+
524
+ t.title = "-11"
525
+ assert !t.valid?, "Shouldn't be valid"
526
+
527
+ t.title = "03"
528
+ assert !t.valid?, "Shouldn't be valid"
529
+
530
+ t.title = "z44"
531
+ assert !t.valid?, "Shouldn't be valid"
532
+
533
+ t.title = "5v7"
534
+ assert !t.valid?, "Shouldn't be valid"
535
+
536
+ t.title = "1"
537
+
538
+ assert t.save
539
+ assert_nil t.errors.on(:title)
540
+ end
541
+
542
+ def test_validate_format_with_formatted_message
543
+ Topic.validates_format_of(:title, :with => /^Valid Title$/, :message => "can't be {{value}}")
544
+ t = Topic.create(:title => 'Invalid title')
545
+ assert_equal "can't be Invalid title", t.errors.on(:title)
546
+ end
547
+
548
+ def test_validates_inclusion_of
549
+ Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ) )
550
+
551
+ assert !Topic.create("title" => "a!", "content" => "abc").valid?
552
+ assert !Topic.create("title" => "a b", "content" => "abc").valid?
553
+ assert !Topic.create("title" => nil, "content" => "def").valid?
554
+
555
+ t = Topic.create("title" => "a", "content" => "I know you are but what am I?")
556
+ assert t.valid?
557
+ t.title = "uhoh"
558
+ assert !t.valid?
559
+ assert t.errors.on(:title)
560
+ assert_equal "is not included in the list", t.errors["title"]
561
+
562
+ assert_raise(ArgumentError) { Topic.validates_inclusion_of( :title, :in => nil ) }
563
+ assert_raise(ArgumentError) { Topic.validates_inclusion_of( :title, :in => 0) }
564
+
565
+ assert_nothing_raised(ArgumentError) { Topic.validates_inclusion_of( :title, :in => "hi!" ) }
566
+ assert_nothing_raised(ArgumentError) { Topic.validates_inclusion_of( :title, :in => {} ) }
567
+ assert_nothing_raised(ArgumentError) { Topic.validates_inclusion_of( :title, :in => [] ) }
568
+ end
569
+
570
+ def test_validates_inclusion_of_with_allow_nil
571
+ Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ), :allow_nil=>true )
572
+
573
+ assert !Topic.create("title" => "a!", "content" => "abc").valid?
574
+ assert !Topic.create("title" => "", "content" => "abc").valid?
575
+ assert Topic.create("title" => nil, "content" => "abc").valid?
576
+ end
577
+
578
+ def test_numericality_with_getter_method
579
+ Developer.validates_numericality_of( :salary )
580
+ developer = Developer.new("name" => "michael", "salary" => nil)
581
+ developer.instance_eval("def salary; read_attribute('salary') ? read_attribute('salary') : 100000; end")
582
+ assert developer.valid?
583
+ end
584
+
585
+ def test_validates_length_of_with_allow_nil
586
+ Topic.validates_length_of( :title, :is => 5, :allow_nil=>true )
587
+
588
+ assert !Topic.create("title" => "ab").valid?
589
+ assert !Topic.create("title" => "").valid?
590
+ assert Topic.create("title" => nil).valid?
591
+ assert Topic.create("title" => "abcde").valid?
592
+ end
593
+
594
+ def test_validates_length_of_with_allow_blank
595
+ Topic.validates_length_of( :title, :is => 5, :allow_blank=>true )
596
+
597
+ assert !Topic.create("title" => "ab").valid?
598
+ assert Topic.create("title" => "").valid?
599
+ assert Topic.create("title" => nil).valid?
600
+ assert Topic.create("title" => "abcde").valid?
601
+ end
602
+
603
+ def test_validates_inclusion_of_with_formatted_message
604
+ Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ), :message => "option {{value}} is not in the list" )
605
+
606
+ assert Topic.create("title" => "a", "content" => "abc").valid?
607
+
608
+ t = Topic.create("title" => "uhoh", "content" => "abc")
609
+ assert !t.valid?
610
+ assert t.errors.on(:title)
611
+ assert_equal "option uhoh is not in the list", t.errors["title"]
612
+ end
613
+
614
+ def test_numericality_with_allow_nil_and_getter_method
615
+ Developer.validates_numericality_of( :salary, :allow_nil => true)
616
+ developer = Developer.new("name" => "michael", "salary" => nil)
617
+ developer.instance_eval("def salary; read_attribute('salary') ? read_attribute('salary') : 100000; end")
618
+ assert developer.valid?
619
+ end
620
+
621
+ def test_validates_exclusion_of
622
+ Topic.validates_exclusion_of( :title, :in => %w( abe monkey ) )
623
+
624
+ assert Topic.create("title" => "something", "content" => "abc").valid?
625
+ assert !Topic.create("title" => "monkey", "content" => "abc").valid?
626
+ end
627
+
628
+ def test_validates_exclusion_of_with_formatted_message
629
+ Topic.validates_exclusion_of( :title, :in => %w( abe monkey ), :message => "option {{value}} is restricted" )
630
+
631
+ assert Topic.create("title" => "something", "content" => "abc")
632
+
633
+ t = Topic.create("title" => "monkey")
634
+ assert !t.valid?
635
+ assert t.errors.on(:title)
636
+ assert_equal "option monkey is restricted", t.errors["title"]
637
+ end
638
+
639
+ def test_validates_length_of_using_minimum
640
+ Topic.validates_length_of :title, :minimum => 5
641
+
642
+ t = Topic.create("title" => "valid", "content" => "whatever")
643
+ assert t.valid?
644
+
645
+ t.title = "not"
646
+ assert !t.valid?
647
+ assert t.errors.on(:title)
648
+ assert_equal "is too short (minimum is 5 characters)", t.errors["title"]
649
+
650
+ t.title = ""
651
+ assert !t.valid?
652
+ assert t.errors.on(:title)
653
+ assert_equal "is too short (minimum is 5 characters)", t.errors["title"]
654
+
655
+ t.title = nil
656
+ assert !t.valid?
657
+ assert t.errors.on(:title)
658
+ assert_equal "is too short (minimum is 5 characters)", t.errors["title"]
659
+ end
660
+
661
+ def test_optionally_validates_length_of_using_minimum
662
+ Topic.validates_length_of :title, :minimum => 5, :allow_nil => true
663
+
664
+ t = Topic.create("title" => "valid", "content" => "whatever")
665
+ assert t.valid?
666
+
667
+ t.title = nil
668
+ assert t.valid?
669
+ end
670
+
671
+ def test_validates_length_of_using_maximum
672
+ Topic.validates_length_of :title, :maximum => 5
673
+
674
+ t = Topic.create("title" => "valid", "content" => "whatever")
675
+ assert t.valid?
676
+
677
+ t.title = "notvalid"
678
+ assert !t.valid?
679
+ assert t.errors.on(:title)
680
+ assert_equal "is too long (maximum is 5 characters)", t.errors["title"]
681
+
682
+ t.title = ""
683
+ assert t.valid?
684
+
685
+ t.title = nil
686
+ assert !t.valid?
687
+ end
688
+
689
+ def test_optionally_validates_length_of_using_maximum
690
+ Topic.validates_length_of :title, :maximum => 5, :allow_nil => true
691
+
692
+ t = Topic.create("title" => "valid", "content" => "whatever")
693
+ assert t.valid?
694
+
695
+ t.title = nil
696
+ assert t.valid?
697
+ end
698
+
699
+ def test_validates_length_of_using_within
700
+ Topic.validates_length_of(:title, :content, :within => 3..5)
701
+
702
+ t = Topic.new("title" => "a!", "content" => "I'm ooooooooh so very long")
703
+ assert !t.valid?
704
+ assert_equal "is too short (minimum is 3 characters)", t.errors.on(:title)
705
+ assert_equal "is too long (maximum is 5 characters)", t.errors.on(:content)
706
+
707
+ t.title = nil
708
+ t.content = nil
709
+ assert !t.valid?
710
+ assert_equal "is too short (minimum is 3 characters)", t.errors.on(:title)
711
+ assert_equal "is too short (minimum is 3 characters)", t.errors.on(:content)
712
+
713
+ t.title = "abe"
714
+ t.content = "mad"
715
+ assert t.valid?
716
+ end
717
+
718
+ def test_optionally_validates_length_of_using_within
719
+ Topic.validates_length_of :title, :content, :within => 3..5, :allow_nil => true
720
+
721
+ t = Topic.create('title' => 'abc', 'content' => 'abcd')
722
+ assert t.valid?
723
+
724
+ t.title = nil
725
+ assert t.valid?
726
+ end
727
+
728
+ def test_optionally_validates_length_of_using_within_on_create
729
+ Topic.validates_length_of :title, :content, :within => 5..10, :on => :create, :too_long => "my string is too long: {{count}}"
730
+
731
+ t = Topic.create("title" => "thisisnotvalid", "content" => "whatever")
732
+ assert !t.save
733
+ assert t.errors.on(:title)
734
+ assert_equal "my string is too long: 10", t.errors[:title]
735
+
736
+ t.title = "butthisis"
737
+ assert t.save
738
+
739
+ t.title = "few"
740
+ assert t.save
741
+
742
+ t.content = "andthisislong"
743
+ assert t.save
744
+
745
+ t.content = t.title = "iamfine"
746
+ assert t.save
747
+ end
748
+
749
+ def test_optionally_validates_length_of_using_within_on_update
750
+ Topic.validates_length_of :title, :content, :within => 5..10, :on => :update, :too_short => "my string is too short: {{count}}"
751
+
752
+ t = Topic.create("title" => "vali", "content" => "whatever")
753
+ assert !t.save
754
+ assert t.errors.on(:title)
755
+
756
+ t.title = "not"
757
+ assert !t.save
758
+ assert t.errors.on(:title)
759
+ assert_equal "my string is too short: 5", t.errors[:title]
760
+
761
+ t.title = "valid"
762
+ t.content = "andthisistoolong"
763
+ assert !t.save
764
+ assert t.errors.on(:content)
765
+
766
+ t.content = "iamfine"
767
+ assert t.save
768
+ end
769
+
770
+ def test_validates_length_of_using_is
771
+ Topic.validates_length_of :title, :is => 5
772
+
773
+ t = Topic.create("title" => "valid", "content" => "whatever")
774
+ assert t.valid?
775
+
776
+ t.title = "notvalid"
777
+ assert !t.valid?
778
+ assert t.errors.on(:title)
779
+ assert_equal "is the wrong length (should be 5 characters)", t.errors["title"]
780
+
781
+ t.title = ""
782
+ assert !t.valid?
783
+
784
+ t.title = nil
785
+ assert !t.valid?
786
+ end
787
+
788
+ def test_optionally_validates_length_of_using_is
789
+ Topic.validates_length_of :title, :is => 5, :allow_nil => true
790
+
791
+ t = Topic.create("title" => "valid", "content" => "whatever")
792
+ assert t.valid?
793
+
794
+ t.title = nil
795
+ assert t.valid?
796
+ end
797
+
798
+ def test_validates_length_of_using_bignum
799
+ bigmin = 2 ** 30
800
+ bigmax = 2 ** 32
801
+ bigrange = bigmin...bigmax
802
+ assert_nothing_raised do
803
+ Topic.validates_length_of :title, :is => bigmin + 5
804
+ Topic.validates_length_of :title, :within => bigrange
805
+ Topic.validates_length_of :title, :in => bigrange
806
+ Topic.validates_length_of :title, :minimum => bigmin
807
+ Topic.validates_length_of :title, :maximum => bigmax
808
+ end
809
+ end
810
+
811
+ def test_validates_length_with_globally_modified_error_message
812
+ AntSupport::Deprecation.silence do
813
+ AntMapper::Errors.default_error_messages[:too_short] = 'tu est trops petit hombre {{count}}'
814
+ end
815
+ Topic.validates_length_of :title, :minimum => 10
816
+ t = Topic.create(:title => 'too short')
817
+ assert !t.valid?
818
+
819
+ assert_equal 'tu est trops petit hombre 10', t.errors['title']
820
+ end
821
+
822
+ def test_validates_size_of_association
823
+ assert_nothing_raised { Topic.validates_size_of :replies, :minimum => 1 }
824
+ t = Topic.new('title' => 'noreplies', 'content' => 'whatever')
825
+ assert !t.save
826
+ assert t.errors.on(:replies)
827
+ reply = t.replies.build('title' => 'areply', 'content' => 'whateveragain')
828
+ assert t.valid?
829
+ end
830
+
831
+ def test_validates_size_of_association_using_within
832
+ assert_nothing_raised { Topic.validates_size_of :replies, :within => 1..2 }
833
+ t = Topic.new('title' => 'noreplies', 'content' => 'whatever')
834
+ assert !t.save
835
+ assert t.errors.on(:replies)
836
+
837
+ reply = t.replies.build('title' => 'areply', 'content' => 'whateveragain')
838
+ assert t.valid?
839
+
840
+ 2.times { t.replies.build('title' => 'areply', 'content' => 'whateveragain') }
841
+ assert !t.save
842
+ assert t.errors.on(:replies)
843
+ end
844
+
845
+ def test_validates_length_of_nasty_params
846
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :minimum=>6, :maximum=>9) }
847
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within=>6, :maximum=>9) }
848
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within=>6, :minimum=>9) }
849
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within=>6, :is=>9) }
850
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :minimum=>"a") }
851
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :maximum=>"a") }
852
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within=>"a") }
853
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :is=>"a") }
854
+ end
855
+
856
+ def test_validates_length_of_custom_errors_for_minimum_with_message
857
+ Topic.validates_length_of( :title, :minimum=>5, :message=>"boo {{count}}" )
858
+ t = Topic.create("title" => "uhoh", "content" => "whatever")
859
+ assert !t.valid?
860
+ assert t.errors.on(:title)
861
+ assert_equal "boo 5", t.errors["title"]
862
+ end
863
+
864
+ def test_validates_length_of_custom_errors_for_minimum_with_too_short
865
+ Topic.validates_length_of( :title, :minimum=>5, :too_short=>"hoo {{count}}" )
866
+ t = Topic.create("title" => "uhoh", "content" => "whatever")
867
+ assert !t.valid?
868
+ assert t.errors.on(:title)
869
+ assert_equal "hoo 5", t.errors["title"]
870
+ end
871
+
872
+ def test_validates_length_of_custom_errors_for_maximum_with_message
873
+ Topic.validates_length_of( :title, :maximum=>5, :message=>"boo {{count}}" )
874
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
875
+ assert !t.valid?
876
+ assert t.errors.on(:title)
877
+ assert_equal "boo 5", t.errors["title"]
878
+ end
879
+
880
+ def test_validates_length_of_custom_errors_for_maximum_with_too_long
881
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}" )
882
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
883
+ assert !t.valid?
884
+ assert t.errors.on(:title)
885
+ assert_equal "hoo 5", t.errors["title"]
886
+ end
887
+
888
+ def test_validates_length_of_custom_errors_for_is_with_message
889
+ Topic.validates_length_of( :title, :is=>5, :message=>"boo {{count}}" )
890
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
891
+ assert !t.valid?
892
+ assert t.errors.on(:title)
893
+ assert_equal "boo 5", t.errors["title"]
894
+ end
895
+
896
+ def test_validates_length_of_custom_errors_for_is_with_wrong_length
897
+ Topic.validates_length_of( :title, :is=>5, :wrong_length=>"hoo {{count}}" )
898
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
899
+ assert !t.valid?
900
+ assert t.errors.on(:title)
901
+ assert_equal "hoo 5", t.errors["title"]
902
+ end
903
+
904
+ def test_validates_length_of_using_minimum_utf8
905
+ with_kcode('UTF8') do
906
+ Topic.validates_length_of :title, :minimum => 5
907
+
908
+ t = Topic.create("title" => "一二三四五", "content" => "whatever")
909
+ assert t.valid?
910
+
911
+ t.title = "一二三四"
912
+ assert !t.valid?
913
+ assert t.errors.on(:title)
914
+ assert_equal "is too short (minimum is 5 characters)", t.errors["title"]
915
+ end
916
+ end
917
+
918
+ def test_validates_length_of_using_maximum_utf8
919
+ with_kcode('UTF8') do
920
+ Topic.validates_length_of :title, :maximum => 5
921
+
922
+ t = Topic.create("title" => "一二三四五", "content" => "whatever")
923
+ assert t.valid?
924
+
925
+ t.title = "一二34五六"
926
+ assert !t.valid?
927
+ assert t.errors.on(:title)
928
+ assert_equal "is too long (maximum is 5 characters)", t.errors["title"]
929
+ end
930
+ end
931
+
932
+ def test_validates_length_of_using_within_utf8
933
+ with_kcode('UTF8') do
934
+ Topic.validates_length_of(:title, :content, :within => 3..5)
935
+
936
+ t = Topic.new("title" => "一二", "content" => "12三四五六七")
937
+ assert !t.valid?
938
+ assert_equal "is too short (minimum is 3 characters)", t.errors.on(:title)
939
+ assert_equal "is too long (maximum is 5 characters)", t.errors.on(:content)
940
+ t.title = "一二三"
941
+ t.content = "12三"
942
+ assert t.valid?
943
+ end
944
+ end
945
+
946
+ def test_optionally_validates_length_of_using_within_utf8
947
+ with_kcode('UTF8') do
948
+ Topic.validates_length_of :title, :within => 3..5, :allow_nil => true
949
+
950
+ t = Topic.create(:title => "一二三四五")
951
+ assert t.valid?, t.errors.inspect
952
+
953
+ t = Topic.create(:title => "一二三")
954
+ assert t.valid?, t.errors.inspect
955
+
956
+ t.title = nil
957
+ assert t.valid?, t.errors.inspect
958
+ end
959
+ end
960
+
961
+ def test_optionally_validates_length_of_using_within_on_create_utf8
962
+ with_kcode('UTF8') do
963
+ Topic.validates_length_of :title, :within => 5..10, :on => :create, :too_long => "長すぎます: {{count}}"
964
+
965
+ t = Topic.create("title" => "一二三四五六七八九十A", "content" => "whatever")
966
+ assert !t.save
967
+ assert t.errors.on(:title)
968
+ assert_equal "長すぎます: 10", t.errors[:title]
969
+
970
+ t.title = "一二三四五六七八九"
971
+ assert t.save
972
+
973
+ t.title = "一二3"
974
+ assert t.save
975
+
976
+ t.content = "一二三四五六七八九十"
977
+ assert t.save
978
+
979
+ t.content = t.title = "一二三四五六"
980
+ assert t.save
981
+ end
982
+ end
983
+
984
+ def test_optionally_validates_length_of_using_within_on_update_utf8
985
+ with_kcode('UTF8') do
986
+ Topic.validates_length_of :title, :within => 5..10, :on => :update, :too_short => "短すぎます: {{count}}"
987
+
988
+ t = Topic.create("title" => "一二三4", "content" => "whatever")
989
+ assert !t.save
990
+ assert t.errors.on(:title)
991
+
992
+ t.title = "1二三4"
993
+ assert !t.save
994
+ assert t.errors.on(:title)
995
+ assert_equal "短すぎます: 5", t.errors[:title]
996
+
997
+ t.title = "一二三四五六七八九十A"
998
+ assert !t.save
999
+ assert t.errors.on(:title)
1000
+
1001
+ t.title = "一二345"
1002
+ assert t.save
1003
+ end
1004
+ end
1005
+
1006
+ def test_validates_length_of_using_is_utf8
1007
+ with_kcode('UTF8') do
1008
+ Topic.validates_length_of :title, :is => 5
1009
+
1010
+ t = Topic.create("title" => "一二345", "content" => "whatever")
1011
+ assert t.valid?
1012
+
1013
+ t.title = "一二345六"
1014
+ assert !t.valid?
1015
+ assert t.errors.on(:title)
1016
+ assert_equal "is the wrong length (should be 5 characters)", t.errors["title"]
1017
+ end
1018
+ end
1019
+
1020
+ def test_validates_length_of_with_block
1021
+ Topic.validates_length_of :content, :minimum => 5, :too_short=>"Your essay must be at least {{count}} words.",
1022
+ :tokenizer => lambda {|str| str.scan(/\w+/) }
1023
+ t = Topic.create!(:content => "this content should be long enough")
1024
+ assert t.valid?
1025
+
1026
+ t.content = "not long enough"
1027
+ assert !t.valid?
1028
+ assert t.errors.on(:content)
1029
+ assert_equal "Your essay must be at least 5 words.", t.errors[:content]
1030
+ end
1031
+
1032
+ def test_validates_size_of_association_utf8
1033
+ with_kcode('UTF8') do
1034
+ assert_nothing_raised { Topic.validates_size_of :replies, :minimum => 1 }
1035
+ t = Topic.new('title' => 'あいうえお', 'content' => 'かきくけこ')
1036
+ assert !t.save
1037
+ assert t.errors.on(:replies)
1038
+ t.replies.build('title' => 'あいうえお', 'content' => 'かきくけこ')
1039
+ assert t.valid?
1040
+ end
1041
+ end
1042
+
1043
+ def test_validates_associated_many
1044
+ Topic.validates_associated( :replies )
1045
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1046
+ t.replies << [r = Reply.new("title" => "A reply"), r2 = Reply.new("title" => "Another reply", "content" => "non-empty"), r3 = Reply.new("title" => "Yet another reply"), r4 = Reply.new("title" => "The last reply", "content" => "non-empty")]
1047
+ assert !t.valid?
1048
+ assert t.errors.on(:replies)
1049
+ assert_equal 1, r.errors.count # make sure all associated objects have been validated
1050
+ assert_equal 0, r2.errors.count
1051
+ assert_equal 1, r3.errors.count
1052
+ assert_equal 0, r4.errors.count
1053
+ r.content = r3.content = "non-empty"
1054
+ assert t.valid?
1055
+ end
1056
+
1057
+ def test_validates_associated_one
1058
+ Reply.validates_associated( :topic )
1059
+ Topic.validates_presence_of( :content )
1060
+ r = Reply.new("title" => "A reply", "content" => "with content!")
1061
+ r.topic = Topic.create("title" => "uhohuhoh")
1062
+ assert !r.valid?
1063
+ assert r.errors.on(:topic)
1064
+ r.topic.content = "non-empty"
1065
+ assert r.valid?
1066
+ end
1067
+
1068
+ def test_validate_block
1069
+ Topic.validate { |topic| topic.errors.add("title", "will never be valid") }
1070
+ t = Topic.create("title" => "Title", "content" => "whatever")
1071
+ assert !t.valid?
1072
+ assert t.errors.on(:title)
1073
+ assert_equal "will never be valid", t.errors["title"]
1074
+ end
1075
+
1076
+ def test_invalid_validator
1077
+ Topic.validate 3
1078
+ assert_raise(ArgumentError) { t = Topic.create }
1079
+ end
1080
+
1081
+ def test_throw_away_typing
1082
+ d = Developer.new("name" => "David", "salary" => "100,000")
1083
+ assert !d.valid?
1084
+ assert_equal 100, d.salary
1085
+ assert_equal "100,000", d.salary_before_type_cast
1086
+ end
1087
+
1088
+ def test_validates_acceptance_of_with_custom_error_using_quotes
1089
+ Developer.validates_acceptance_of :salary, :message=> "This string contains 'single' and \"double\" quotes"
1090
+ d = Developer.new
1091
+ d.salary = "0"
1092
+ assert !d.valid?
1093
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:salary).last
1094
+ end
1095
+
1096
+ def test_validates_confirmation_of_with_custom_error_using_quotes
1097
+ Developer.validates_confirmation_of :name, :message=> "confirm 'single' and \"double\" quotes"
1098
+ d = Developer.new
1099
+ d.name = "John"
1100
+ d.name_confirmation = "Johnny"
1101
+ assert !d.valid?
1102
+ assert_equal "confirm 'single' and \"double\" quotes", d.errors.on(:name)
1103
+ end
1104
+
1105
+ def test_validates_format_of_with_custom_error_using_quotes
1106
+ Developer.validates_format_of :name, :with => /^(A-Z*)$/, :message=> "format 'single' and \"double\" quotes"
1107
+ d = Developer.new
1108
+ d.name = d.name_confirmation = "John 32"
1109
+ assert !d.valid?
1110
+ assert_equal "format 'single' and \"double\" quotes", d.errors.on(:name)
1111
+ end
1112
+
1113
+ def test_validates_inclusion_of_with_custom_error_using_quotes
1114
+ Developer.validates_inclusion_of :salary, :in => 1000..80000, :message=> "This string contains 'single' and \"double\" quotes"
1115
+ d = Developer.new
1116
+ d.salary = "90,000"
1117
+ assert !d.valid?
1118
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:salary).last
1119
+ end
1120
+
1121
+ def test_validates_length_of_with_custom_too_long_using_quotes
1122
+ Developer.validates_length_of :name, :maximum => 4, :too_long=> "This string contains 'single' and \"double\" quotes"
1123
+ d = Developer.new
1124
+ d.name = "Jeffrey"
1125
+ assert !d.valid?
1126
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).last
1127
+ end
1128
+
1129
+ def test_validates_length_of_with_custom_too_short_using_quotes
1130
+ Developer.validates_length_of :name, :minimum => 4, :too_short=> "This string contains 'single' and \"double\" quotes"
1131
+ d = Developer.new
1132
+ d.name = "Joe"
1133
+ assert !d.valid?
1134
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).last
1135
+ end
1136
+
1137
+ def test_validates_length_of_with_custom_message_using_quotes
1138
+ Developer.validates_length_of :name, :minimum => 4, :message=> "This string contains 'single' and \"double\" quotes"
1139
+ d = Developer.new
1140
+ d.name = "Joe"
1141
+ assert !d.valid?
1142
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).last
1143
+ end
1144
+
1145
+ def test_validates_presence_of_with_custom_message_using_quotes
1146
+ Developer.validates_presence_of :non_existent, :message=> "This string contains 'single' and \"double\" quotes"
1147
+ d = Developer.new
1148
+ d.name = "Joe"
1149
+ assert !d.valid?
1150
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:non_existent)
1151
+ end
1152
+
1153
+ def test_validates_uniqueness_of_with_custom_message_using_quotes
1154
+ Developer.validates_uniqueness_of :name, :message=> "This string contains 'single' and \"double\" quotes"
1155
+ d = Developer.new
1156
+ d.name = "David"
1157
+ assert !d.valid?
1158
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).last
1159
+ end
1160
+
1161
+ def test_validates_associated_with_custom_message_using_quotes
1162
+ Reply.validates_associated :topic, :message=> "This string contains 'single' and \"double\" quotes"
1163
+ Topic.validates_presence_of :content
1164
+ r = Reply.create("title" => "A reply", "content" => "with content!")
1165
+ r.topic = Topic.create("title" => "uhohuhoh")
1166
+ assert !r.valid?
1167
+ assert_equal "This string contains 'single' and \"double\" quotes", r.errors.on(:topic).last
1168
+ end
1169
+
1170
+ def test_if_validation_using_method_true
1171
+ # When the method returns true
1172
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :if => :condition_is_true )
1173
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1174
+ assert !t.valid?
1175
+ assert t.errors.on(:title)
1176
+ assert_equal "hoo 5", t.errors["title"]
1177
+ end
1178
+
1179
+ def test_unless_validation_using_method_true
1180
+ # When the method returns true
1181
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :unless => :condition_is_true )
1182
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1183
+ assert t.valid?
1184
+ assert !t.errors.on(:title)
1185
+ end
1186
+
1187
+ def test_if_validation_using_method_false
1188
+ # When the method returns false
1189
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :if => :condition_is_true_but_its_not )
1190
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1191
+ assert t.valid?
1192
+ assert !t.errors.on(:title)
1193
+ end
1194
+
1195
+ def test_unless_validation_using_method_false
1196
+ # When the method returns false
1197
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :unless => :condition_is_true_but_its_not )
1198
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1199
+ assert !t.valid?
1200
+ assert t.errors.on(:title)
1201
+ assert_equal "hoo 5", t.errors["title"]
1202
+ end
1203
+
1204
+ def test_if_validation_using_string_true
1205
+ # When the evaluated string returns true
1206
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :if => "a = 1; a == 1" )
1207
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1208
+ assert !t.valid?
1209
+ assert t.errors.on(:title)
1210
+ assert_equal "hoo 5", t.errors["title"]
1211
+ end
1212
+
1213
+ def test_unless_validation_using_string_true
1214
+ # When the evaluated string returns true
1215
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :unless => "a = 1; a == 1" )
1216
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1217
+ assert t.valid?
1218
+ assert !t.errors.on(:title)
1219
+ end
1220
+
1221
+ def test_if_validation_using_string_false
1222
+ # When the evaluated string returns false
1223
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :if => "false")
1224
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1225
+ assert t.valid?
1226
+ assert !t.errors.on(:title)
1227
+ end
1228
+
1229
+ def test_unless_validation_using_string_false
1230
+ # When the evaluated string returns false
1231
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :unless => "false")
1232
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1233
+ assert !t.valid?
1234
+ assert t.errors.on(:title)
1235
+ assert_equal "hoo 5", t.errors["title"]
1236
+ end
1237
+
1238
+ def test_if_validation_using_block_true
1239
+ # When the block returns true
1240
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}",
1241
+ :if => Proc.new { |r| r.content.size > 4 } )
1242
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1243
+ assert !t.valid?
1244
+ assert t.errors.on(:title)
1245
+ assert_equal "hoo 5", t.errors["title"]
1246
+ end
1247
+
1248
+ def test_unless_validation_using_block_true
1249
+ # When the block returns true
1250
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}",
1251
+ :unless => Proc.new { |r| r.content.size > 4 } )
1252
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1253
+ assert t.valid?
1254
+ assert !t.errors.on(:title)
1255
+ end
1256
+
1257
+ def test_if_validation_using_block_false
1258
+ # When the block returns false
1259
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}",
1260
+ :if => Proc.new { |r| r.title != "uhohuhoh"} )
1261
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1262
+ assert t.valid?
1263
+ assert !t.errors.on(:title)
1264
+ end
1265
+
1266
+ def test_unless_validation_using_block_false
1267
+ # When the block returns false
1268
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}",
1269
+ :unless => Proc.new { |r| r.title != "uhohuhoh"} )
1270
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1271
+ assert !t.valid?
1272
+ assert t.errors.on(:title)
1273
+ assert_equal "hoo 5", t.errors["title"]
1274
+ end
1275
+
1276
+ def test_validates_associated_missing
1277
+ Reply.validates_presence_of(:topic)
1278
+ r = Reply.create("title" => "A reply", "content" => "with content!")
1279
+ assert !r.valid?
1280
+ assert r.errors.on(:topic)
1281
+
1282
+ r.topic = Topic.find :first
1283
+ assert r.valid?
1284
+ end
1285
+
1286
+ def test_errors_to_xml
1287
+ r = Reply.new :title => "Wrong Create"
1288
+ assert !r.valid?
1289
+ xml = r.errors.to_xml(:skip_instruct => true)
1290
+ assert_equal "<errors>", xml.first(8)
1291
+ assert xml.include?("<error>Title is Wrong Create</error>")
1292
+ assert xml.include?("<error>Content Empty</error>")
1293
+ end
1294
+
1295
+ def test_validation_order
1296
+ Topic.validates_presence_of :title
1297
+ Topic.validates_length_of :title, :minimum => 2
1298
+
1299
+ t = Topic.new("title" => "")
1300
+ assert !t.valid?
1301
+ assert_equal "can't be blank", t.errors.on("title").first
1302
+ end
1303
+
1304
+ # previous implementation of validates_presence_of eval'd the
1305
+ # string with the wrong binding, this regression test is to
1306
+ # ensure that it works correctly
1307
+ def test_validation_with_if_as_string
1308
+ Topic.validates_presence_of(:title)
1309
+ Topic.validates_presence_of(:author_name, :if => "title.to_s.match('important')")
1310
+
1311
+ t = Topic.new
1312
+ assert !t.valid?, "A topic without a title should not be valid"
1313
+ assert !t.errors.invalid?("author_name"), "A topic without an 'important' title should not require an author"
1314
+
1315
+ t.title = "Just a title"
1316
+ assert t.valid?, "A topic with a basic title should be valid"
1317
+
1318
+ t.title = "A very important title"
1319
+ assert !t.valid?, "A topic with an important title, but without an author, should not be valid"
1320
+ assert t.errors.invalid?("author_name"), "A topic with an 'important' title should require an author"
1321
+
1322
+ t.author_name = "Hubert J. Farnsworth"
1323
+ assert t.valid?, "A topic with an important title and author should be valid"
1324
+ end
1325
+
1326
+ private
1327
+ def with_kcode(kcode)
1328
+ if RUBY_VERSION < '1.9'
1329
+ orig_kcode, $KCODE = $KCODE, kcode
1330
+ begin
1331
+ yield
1332
+ ensure
1333
+ $KCODE = orig_kcode
1334
+ end
1335
+ else
1336
+ yield
1337
+ end
1338
+ end
1339
+ =end
1340
+ end
1341
+
1342
+ =begin
1343
+ class ValidatesNumericalityTest < Test::Unit::TestCase
1344
+ NIL = [nil]
1345
+ BLANK = ["", " ", " \t \r \n"]
1346
+ BIGDECIMAL_STRINGS = %w(12345678901234567890.1234567890) # 30 significent digits
1347
+ FLOAT_STRINGS = %w(0.0 +0.0 -0.0 10.0 10.5 -10.5 -0.0001 -090.1 90.1e1 -90.1e5 -90.1e-5 90e-5)
1348
+ INTEGER_STRINGS = %w(0 +0 -0 10 +10 -10 0090 -090)
1349
+ FLOATS = [0.0, 10.0, 10.5, -10.5, -0.0001] + FLOAT_STRINGS
1350
+ INTEGERS = [0, 10, -10] + INTEGER_STRINGS
1351
+ BIGDECIMAL = BIGDECIMAL_STRINGS.collect! { |bd| BigDecimal.new(bd) }
1352
+ JUNK = ["not a number", "42 not a number", "0xdeadbeef", "00-1", "--3", "+-3", "+3-1", "-+019.0", "12.12.13.12", "123\nnot a number"]
1353
+ INFINITY = [1.0/0.0]
1354
+
1355
+ def setup
1356
+ Topic.instance_variable_set("@validate_callbacks", AntSupport::Callbacks::CallbackChain.new)
1357
+ Topic.instance_variable_set("@validate_on_create_callbacks", AntSupport::Callbacks::CallbackChain.new)
1358
+ Topic.instance_variable_set("@validate_on_update_callbacks", AntSupport::Callbacks::CallbackChain.new)
1359
+ end
1360
+
1361
+ def test_default_validates_numericality_of
1362
+ Topic.validates_numericality_of :approved
1363
+
1364
+ invalid!(NIL + BLANK + JUNK)
1365
+ valid!(FLOATS + INTEGERS + BIGDECIMAL + INFINITY)
1366
+ end
1367
+
1368
+ def test_validates_numericality_of_with_nil_allowed
1369
+ Topic.validates_numericality_of :approved, :allow_nil => true
1370
+
1371
+ invalid!(JUNK)
1372
+ valid!(NIL + BLANK + FLOATS + INTEGERS + BIGDECIMAL + INFINITY)
1373
+ end
1374
+
1375
+ def test_validates_numericality_of_with_integer_only
1376
+ Topic.validates_numericality_of :approved, :only_integer => true
1377
+
1378
+ invalid!(NIL + BLANK + JUNK + FLOATS + BIGDECIMAL + INFINITY)
1379
+ valid!(INTEGERS)
1380
+ end
1381
+
1382
+ def test_validates_numericality_of_with_integer_only_and_nil_allowed
1383
+ Topic.validates_numericality_of :approved, :only_integer => true, :allow_nil => true
1384
+
1385
+ invalid!(JUNK + FLOATS + BIGDECIMAL + INFINITY)
1386
+ valid!(NIL + BLANK + INTEGERS)
1387
+ end
1388
+
1389
+ def test_validates_numericality_with_greater_than
1390
+ Topic.validates_numericality_of :approved, :greater_than => 10
1391
+
1392
+ invalid!([-10, 10], 'must be greater than 10')
1393
+ valid!([11])
1394
+ end
1395
+
1396
+ def test_validates_numericality_with_greater_than_or_equal
1397
+ Topic.validates_numericality_of :approved, :greater_than_or_equal_to => 10
1398
+
1399
+ invalid!([-9, 9], 'must be greater than or equal to 10')
1400
+ valid!([10])
1401
+ end
1402
+
1403
+ def test_validates_numericality_with_equal_to
1404
+ Topic.validates_numericality_of :approved, :equal_to => 10
1405
+
1406
+ invalid!([-10, 11] + INFINITY, 'must be equal to 10')
1407
+ valid!([10])
1408
+ end
1409
+
1410
+ def test_validates_numericality_with_less_than
1411
+ Topic.validates_numericality_of :approved, :less_than => 10
1412
+
1413
+ invalid!([10], 'must be less than 10')
1414
+ valid!([-9, 9])
1415
+ end
1416
+
1417
+ def test_validates_numericality_with_less_than_or_equal_to
1418
+ Topic.validates_numericality_of :approved, :less_than_or_equal_to => 10
1419
+
1420
+ invalid!([11], 'must be less than or equal to 10')
1421
+ valid!([-10, 10])
1422
+ end
1423
+
1424
+ def test_validates_numericality_with_odd
1425
+ Topic.validates_numericality_of :approved, :odd => true
1426
+
1427
+ invalid!([-2, 2], 'must be odd')
1428
+ valid!([-1, 1])
1429
+ end
1430
+
1431
+ def test_validates_numericality_with_even
1432
+ Topic.validates_numericality_of :approved, :even => true
1433
+
1434
+ invalid!([-1, 1], 'must be even')
1435
+ valid!([-2, 2])
1436
+ end
1437
+
1438
+ def test_validates_numericality_with_greater_than_less_than_and_even
1439
+ Topic.validates_numericality_of :approved, :greater_than => 1, :less_than => 4, :even => true
1440
+
1441
+ invalid!([1, 3, 4])
1442
+ valid!([2])
1443
+ end
1444
+
1445
+ def test_validates_numericality_with_numeric_message
1446
+ Topic.validates_numericality_of :approved, :less_than => 4, :message => "smaller than {{count}}"
1447
+ topic = Topic.new("title" => "numeric test", "approved" => 10)
1448
+
1449
+ assert !topic.valid?
1450
+ assert_equal "smaller than 4", topic.errors.on(:approved)
1451
+
1452
+ Topic.validates_numericality_of :approved, :greater_than => 4, :message => "greater than {{count}}"
1453
+ topic = Topic.new("title" => "numeric test", "approved" => 1)
1454
+
1455
+ assert !topic.valid?
1456
+ assert_equal "greater than 4", topic.errors.on(:approved)
1457
+ end
1458
+
1459
+ private
1460
+ def invalid!(values, error=nil)
1461
+ with_each_topic_approved_value(values) do |topic, value|
1462
+ assert !topic.valid?, "#{value.inspect} not rejected as a number"
1463
+ assert topic.errors.on(:approved)
1464
+ assert_equal error, topic.errors.on(:approved) if error
1465
+ end
1466
+ end
1467
+
1468
+ def valid!(values)
1469
+ with_each_topic_approved_value(values) do |topic, value|
1470
+ assert topic.valid?, "#{value.inspect} not accepted as a number"
1471
+ end
1472
+ end
1473
+
1474
+ def with_each_topic_approved_value(values)
1475
+ topic = Topic.new("title" => "numeric test", "content" => "whatever")
1476
+ values.each do |value|
1477
+ topic.approved = value
1478
+ yield topic, value
1479
+ end
1480
+ end
1481
+ end
1482
+ =end