hashrocket-validatable 1.7.2

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 (45) hide show
  1. data/README.rdoc +118 -0
  2. data/Rakefile +54 -0
  3. data/VERSION.yml +4 -0
  4. data/lib/child_validation.rb +15 -0
  5. data/lib/errors.rb +88 -0
  6. data/lib/included_validation.rb +9 -0
  7. data/lib/macros.rb +306 -0
  8. data/lib/object_extension.rb +21 -0
  9. data/lib/requireable.rb +26 -0
  10. data/lib/understandable.rb +31 -0
  11. data/lib/validatable.rb +25 -0
  12. data/lib/validatable_class_methods.rb +85 -0
  13. data/lib/validatable_instance_methods.rb +116 -0
  14. data/lib/validations/validates_acceptance_of.rb +14 -0
  15. data/lib/validations/validates_confirmation_of.rb +23 -0
  16. data/lib/validations/validates_each.rb +14 -0
  17. data/lib/validations/validates_format_of.rb +16 -0
  18. data/lib/validations/validates_length_of.rb +30 -0
  19. data/lib/validations/validates_numericality_of.rb +27 -0
  20. data/lib/validations/validates_presence_of.rb +17 -0
  21. data/lib/validations/validates_true_for.rb +13 -0
  22. data/lib/validations/validation_base.rb +91 -0
  23. data/test/all_tests.rb +1 -0
  24. data/test/functional/test_validatable.rb +589 -0
  25. data/test/functional/test_validates_acceptance_of.rb +16 -0
  26. data/test/functional/test_validates_confirmation_of.rb +56 -0
  27. data/test/functional/test_validates_each.rb +14 -0
  28. data/test/functional/test_validates_format_of.rb +34 -0
  29. data/test/functional/test_validates_length_of.rb +64 -0
  30. data/test/functional/test_validates_numericality_of.rb +57 -0
  31. data/test/functional/test_validates_presence_of.rb +16 -0
  32. data/test/functional/test_validates_true_for.rb +27 -0
  33. data/test/test_helper.rb +33 -0
  34. data/test/unit/test_errors.rb +64 -0
  35. data/test/unit/test_understandable.rb +19 -0
  36. data/test/unit/test_validatable.rb +38 -0
  37. data/test/unit/test_validates_acceptance_of.rb +45 -0
  38. data/test/unit/test_validates_confirmation_of.rb +76 -0
  39. data/test/unit/test_validates_format_of.rb +44 -0
  40. data/test/unit/test_validates_length_of.rb +80 -0
  41. data/test/unit/test_validates_numericality_of.rb +76 -0
  42. data/test/unit/test_validates_presence_of.rb +35 -0
  43. data/test/unit/test_validates_true_for.rb +29 -0
  44. data/test/unit/test_validation_base.rb +52 -0
  45. metadata +118 -0
@@ -0,0 +1,17 @@
1
+ module Validatable
2
+ class ValidatesPresenceOf < ValidationBase #:nodoc:
3
+ def valid?(instance)
4
+ value = instance.send(self.attribute)
5
+ return true if allow_nil && value.nil?
6
+ return true if allow_blank && value.blank?
7
+
8
+ return false if instance.send(self.attribute).nil?
9
+ value.respond_to?(:strip) ? instance.send(self.attribute).strip.length != 0 : true
10
+ end
11
+
12
+ def message(instance)
13
+ super || "can't be empty"
14
+ end
15
+ end
16
+ end
17
+
@@ -0,0 +1,13 @@
1
+ module Validatable
2
+ class ValidatesTrueFor < ValidationBase #:nodoc:
3
+ required_option :logic
4
+
5
+ def valid?(instance)
6
+ instance.instance_eval(&logic) == true
7
+ end
8
+
9
+ def message(instance)
10
+ super || "is invalid"
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,91 @@
1
+ module Validatable
2
+ class ValidationBase #:nodoc:
3
+ def self.required_option(*args)
4
+ option(*args)
5
+ requires(*args)
6
+ end
7
+
8
+ def self.option(*args)
9
+ attr_accessor(*args)
10
+ understands(*args)
11
+ end
12
+
13
+ def self.default(hash)
14
+ defaults.merge! hash
15
+ end
16
+
17
+ def self.defaults
18
+ @defaults ||= {}
19
+ end
20
+
21
+ def self.all_defaults
22
+ return defaults.merge(self.superclass.all_defaults) if self.superclass.respond_to? :all_defaults
23
+ defaults
24
+ end
25
+
26
+ def self.after_validate(&block)
27
+ after_validations << block
28
+ end
29
+
30
+ def self.after_validations
31
+ @after_validations ||= []
32
+ end
33
+
34
+ def self.all_after_validations
35
+ return after_validations + self.superclass.all_after_validations if self.superclass.respond_to? :all_after_validations
36
+ after_validations
37
+ end
38
+
39
+ include Understandable
40
+ include Requireable
41
+
42
+ option :message, :if, :times, :level, :groups, :key, :after_validate, :allow_nil, :allow_blank
43
+ default :level => 1, :groups => []
44
+ attr_accessor :attribute
45
+
46
+ def initialize(klass, attribute, options={})
47
+ must_understand options
48
+ requires options
49
+ self.class.all_understandings.each do |understanding|
50
+ options[understanding] = self.class.all_defaults[understanding] unless options.has_key? understanding
51
+ self.instance_variable_set("@#{understanding}", options[understanding])
52
+ end
53
+ self.attribute = attribute
54
+ self.groups = [self.groups] unless self.groups.is_a?(Array)
55
+ self.key = "#{klass.name}/#{self.class.name}/#{self.key || self.attribute}"
56
+ raise_error_if_key_is_dup(klass)
57
+ end
58
+
59
+ def raise_error_if_key_is_dup(klass)
60
+ message = "key #{self.key} must be unique, provide the :key option to specify a unique key"
61
+ raise ArgumentError.new(message) if klass.validation_keys_include? self.key
62
+ end
63
+
64
+ def should_validate?(instance)
65
+ result = validate_this_time?(instance)
66
+ case self.if
67
+ when Proc
68
+ result &&= instance.instance_eval(&self.if)
69
+ when Symbol, String
70
+ result &&= instance.instance_eval(self.if.to_s)
71
+ end
72
+ result
73
+ end
74
+
75
+ def message(instance)
76
+ @message.respond_to?(:call) ? instance.instance_eval(&@message) : @message
77
+ end
78
+
79
+ def validate_this_time?(instance)
80
+ return true if @times.nil?
81
+ self.times > instance.times_validated(self.key)
82
+ end
83
+
84
+ def run_after_validate(result, instance, attribute)
85
+ self.class.all_after_validations.each do |block|
86
+ block.call result, instance, attribute
87
+ end
88
+ instance.instance_eval_with_params result, attribute, &self.after_validate unless self.after_validate.nil?
89
+ end
90
+ end
91
+ end
data/test/all_tests.rb ADDED
@@ -0,0 +1 @@
1
+ Dir['test/**/test_*.rb'].each { |test_case| require test_case }
@@ -0,0 +1,589 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ functional_tests do
4
+
5
+ expect "can't be empty" do
6
+ child_class = Module.new do
7
+ include Validatable
8
+ validates_presence_of :name
9
+ end
10
+ klass = Class.new do
11
+ include Validatable
12
+ include_validations_from :child
13
+ define_method :child do
14
+ child_class
15
+ end
16
+ attr_accessor :name
17
+ end
18
+ instance = klass.new
19
+ instance.valid?
20
+ instance.errors.on(:name)
21
+ end
22
+
23
+ expect "can't be empty" do
24
+ child_class = Module.new do
25
+ include Validatable
26
+ validates_presence_of :name
27
+ end
28
+ klass = Class.new do
29
+ include Validatable
30
+ validates_presence_of :address
31
+ include_validations_from :child
32
+ define_method :child do
33
+ child_class
34
+ end
35
+ attr_accessor :name, :address
36
+ end
37
+ instance = klass.new
38
+ instance.valid?
39
+ instance.errors.on(:address)
40
+ end
41
+
42
+ expect :is_set do
43
+ klass = Class.new do
44
+ include Validatable
45
+ attr_accessor :result
46
+ before_validation do
47
+ self.result = :is_set
48
+ end
49
+ end
50
+
51
+ instance = klass.new
52
+ instance.valid?
53
+ instance.result
54
+ end
55
+
56
+ expect :is_set do
57
+ klass = Class.new do
58
+ include Validatable
59
+ attr_accessor :name, :result
60
+ validates_presence_of :name, :after_validate => lambda { |result, attribute| self.result = :is_set }
61
+ end
62
+
63
+ instance = klass.new
64
+ instance.valid?
65
+ instance.result
66
+ end
67
+
68
+ expect false do
69
+ klass = Class.new do
70
+ include Validatable
71
+ attr_accessor :name
72
+ validates_presence_of :name
73
+ end
74
+ subclass = Class.new klass do
75
+ end
76
+ subsubclass = Class.new subclass do
77
+ end
78
+ subsubclass.new.valid?
79
+ end
80
+
81
+ expect false do
82
+ klass = Class.new do
83
+ include Validatable
84
+ attr_accessor :name
85
+ validates_presence_of :name
86
+ end
87
+ subclass = Class.new klass do
88
+ end
89
+ subclass.new.valid?
90
+ end
91
+
92
+ expect true do
93
+ klass = Class.new do
94
+ include Validatable
95
+ attr_accessor :name
96
+ validates_presence_of :name
97
+ end
98
+ subclass = Class.new klass do
99
+ end
100
+ instance = subclass.new
101
+ instance.name = 'a name'
102
+ instance.valid?
103
+ end
104
+
105
+ expect ArgumentError do
106
+ Class.new do
107
+ include Validatable
108
+ attr_accessor :name
109
+ validates_presence_of :name, :times => 1
110
+ validates_presence_of :name, :times => 1
111
+ end
112
+ end
113
+
114
+ expect ArgumentError do
115
+ Class.new do
116
+ include Validatable
117
+ attr_accessor :name
118
+ validates_presence_of :name, :times => 1, :key => 'anything'
119
+ validates_presence_of :name, :times => 1, :key => 'anything'
120
+ end
121
+ end
122
+
123
+ expect "is invalid" do
124
+ child_class = Class.new do
125
+ include Validatable
126
+ attr_accessor :name, :address
127
+ validates_presence_of :name
128
+ validates_format_of :address, :with => /.+/
129
+ end
130
+ klass = Class.new do
131
+ include Validatable
132
+ include_errors_from :child
133
+ define_method :child do
134
+ child_class.new
135
+ end
136
+ end
137
+ instance = klass.new
138
+ instance.valid?
139
+ instance.errors.on(:address)
140
+ end
141
+
142
+ expect "can't be empty" do
143
+ child_class = Class.new do
144
+ include Validatable
145
+ attr_accessor :name, :address
146
+ validates_presence_of :name
147
+ validates_format_of :address, :with => /.+/
148
+ end
149
+ klass = Class.new do
150
+ include Validatable
151
+ include_errors_from :child
152
+ define_method :child do
153
+ child_class.new
154
+ end
155
+ end
156
+ instance = klass.new
157
+ instance.valid?
158
+ instance.errors.on(:name)
159
+ end
160
+
161
+
162
+ test "when child validations have errors, level 2 and higher parent validations are not performed" do
163
+ child_class = Class.new do
164
+ include Validatable
165
+ attr_accessor :name
166
+ validates_presence_of :name
167
+ end
168
+
169
+ klass = Class.new do
170
+ include Validatable
171
+ extend Forwardable
172
+ def_delegator :child, :name
173
+ validates_true_for :name, :logic => lambda { false }, :level => 2, :message => "invalid message"
174
+ include_errors_from :child
175
+ define_method :child do
176
+ @child ||= child_class.new
177
+ end
178
+ end
179
+ instance = klass.new
180
+ instance.valid?
181
+ assert_equal "can't be empty", instance.errors.on(:name)
182
+ end
183
+
184
+ test "when child validations have errors, level 1 parent validations are still performed" do
185
+ child_class = Class.new do
186
+ include Validatable
187
+ attr_accessor :name
188
+ validates_presence_of :name
189
+ end
190
+
191
+ klass = Class.new do
192
+ include Validatable
193
+ validates_true_for :address, :logic => lambda { false }, :level => 1, :message => "invalid message"
194
+ include_errors_from :child
195
+ define_method :child do
196
+ @child ||= child_class.new
197
+ end
198
+
199
+ end
200
+ instance = klass.new
201
+ instance.valid?
202
+ assert_equal "can't be empty", instance.errors.on(:name)
203
+ assert_equal "invalid message", instance.errors.on(:address)
204
+ end
205
+
206
+ expect "can't be empty" do
207
+ child_class = Class.new do
208
+ include Validatable
209
+ attr_accessor :name, :address
210
+ validates_presence_of :name
211
+ end
212
+ klass = Class.new do
213
+ include Validatable
214
+ include_errors_from :child, :map => {:name => :namen}
215
+ define_method :child do
216
+ child_class.new
217
+ end
218
+ end
219
+ instance = klass.new
220
+ instance.valid?
221
+ instance.errors.on(:namen)
222
+ end
223
+
224
+ expect "can't be empty" do
225
+ child_class = Class.new do
226
+ include Validatable
227
+ attr_accessor :name, :address
228
+ validates_presence_of :name
229
+ end
230
+ klass = Class.new do
231
+ include Validatable
232
+ include_errors_from :child, :if => lambda { true }
233
+ define_method :child do
234
+ child_class.new
235
+ end
236
+ end
237
+ instance = klass.new
238
+ instance.valid?
239
+ instance.errors.on(:name)
240
+ end
241
+
242
+ expect true do
243
+ child_class = Class.new do
244
+ include Validatable
245
+ attr_accessor :name, :address
246
+ validates_presence_of :name
247
+ end
248
+ klass = Class.new do
249
+ include Validatable
250
+ include_errors_from :child, :if => lambda { false }
251
+ define_method :child do
252
+ child_class.new
253
+ end
254
+ end
255
+ instance = klass.new
256
+ instance.valid?
257
+ end
258
+
259
+ test ':if with symbol should work' do
260
+ klass = Class.new do
261
+ include Validatable
262
+ attr_accessor :name, :name_required
263
+ validates_presence_of :name, :if => :name_required?
264
+
265
+ def name_required?
266
+ name_required
267
+ end
268
+ end
269
+ instance = klass.new
270
+ instance.name_required = false
271
+ assert instance.valid?
272
+ instance.name_required = true
273
+ assert !instance.valid?
274
+ assert_equal "can't be empty", instance.errors.on(:name)
275
+ end
276
+
277
+ test ':if with string should work' do
278
+ klass = Class.new do
279
+ include Validatable
280
+ attr_accessor :name, :name_required
281
+ validates_presence_of :name, :if => 'name_required?'
282
+
283
+ def name_required?
284
+ name_required
285
+ end
286
+ end
287
+ instance = klass.new
288
+ instance.name_required = false
289
+ assert instance.valid?
290
+ instance.name_required = true
291
+ assert !instance.valid?
292
+ assert_equal "can't be empty", instance.errors.on(:name)
293
+ end
294
+
295
+ test "classes only have valid_for_* methods for groups that appear in their validations" do
296
+ class_with_group_one = Class.new do
297
+ include Validatable
298
+ validates_presence_of :name, :groups => :group_one
299
+ attr_accessor :name
300
+ end
301
+ class_with_group_two = Class.new do
302
+ include Validatable
303
+ validates_presence_of :name, :groups => :group_two
304
+ attr_accessor :name
305
+ end
306
+ assert_equal false, class_with_group_one.public_instance_methods.include?(:valid_for_group_two?)
307
+ assert_equal false, class_with_group_two.public_instance_methods.include?(:valid_for_group_one?)
308
+ end
309
+
310
+ test "nonmatching groups are not used as validations" do
311
+ klass = Class.new do
312
+ include Validatable
313
+ validates_presence_of :name, :groups => :group_one
314
+ validates_presence_of :address, :groups => :group_two
315
+ attr_accessor :name, :address
316
+ end
317
+ instance = klass.new
318
+ assert_equal nil, instance.errors.on(:name)
319
+ end
320
+
321
+ expect "can't be empty twice changed message" do
322
+ klass = Class.new do
323
+ include Validatable
324
+ validates_presence_of :name
325
+ attr_accessor :name
326
+ end
327
+
328
+ Validatable::ValidationBase.class_eval do
329
+ after_validate do |result, instance, attribute|
330
+ instance.errors.add(attribute, " changed message")
331
+ end
332
+ end
333
+ Validatable::ValidatesPresenceOf.class_eval do
334
+ after_validate do |result, instance, attribute|
335
+ instance.errors.add(attribute, " twice")
336
+ end
337
+ end
338
+ instance = klass.new
339
+ instance.valid?
340
+ Validatable::ValidatesPresenceOf.after_validations.clear
341
+ Validatable::ValidationBase.after_validations.clear
342
+ instance.errors.on(:name).join
343
+ end
344
+
345
+ expect false do
346
+ klass = Class.new do
347
+ include Validatable
348
+ validates_presence_of :name, :groups => :group_one
349
+ attr_accessor :name
350
+ end
351
+ instance = klass.new
352
+ instance.valid_for_group_one?
353
+ end
354
+
355
+ expect false do
356
+ klass = Class.new do
357
+ include Validatable
358
+ validates_presence_of :name, :groups => [:group_one, :group_two]
359
+ attr_accessor :name
360
+ end
361
+ instance = klass.new
362
+ instance.valid_for_group_one?
363
+ end
364
+
365
+ expect true do
366
+ klass = Class.new do
367
+ include Validatable
368
+ validates_presence_of :name, :groups => :group_one
369
+ validates_presence_of :address
370
+ attr_accessor :name, :address
371
+ end
372
+ instance = klass.new
373
+ instance.address = 'anything'
374
+ instance.valid?
375
+ end
376
+
377
+ expect true do
378
+ klass = Class.new do
379
+ include Validatable
380
+ validates_presence_of :name, :groups => :group_one
381
+ attr_accessor :name
382
+ end
383
+ instance = klass.new
384
+ instance.valid?
385
+ end
386
+
387
+ expect true do
388
+ klass = Class.new do
389
+ include Validatable
390
+ validates_presence_of :name, :times => 1
391
+ attr_accessor :name
392
+ end
393
+ instance = klass.new
394
+ instance.valid?
395
+ instance.valid?
396
+ end
397
+
398
+ expect false do
399
+ klass = Class.new do
400
+ include Validatable
401
+ validates_presence_of :name, :times => 1
402
+ attr_accessor :name
403
+ end
404
+ instance1 = klass.new
405
+ instance1.valid?
406
+ instance2 = klass.new
407
+ instance2.valid?
408
+ end
409
+
410
+ expect "name message" do
411
+ klass = Class.new do
412
+ include Validatable
413
+ validates_presence_of :name, :level => 1, :message => "name message"
414
+ validates_presence_of :address, :level => 2
415
+ attr_accessor :name, :address
416
+ end
417
+ instance = klass.new
418
+ instance.valid?
419
+ instance.errors.on(:name)
420
+ end
421
+
422
+ expect nil do
423
+ klass = Class.new do
424
+ include Validatable
425
+ validates_presence_of :name, :level => 1, :message => "name message"
426
+ validates_presence_of :address, :level => 2
427
+ attr_accessor :name, :address
428
+ end
429
+ instance = klass.new
430
+ instance.valid?
431
+ instance.errors.on(:address)
432
+ end
433
+
434
+ expect "Mod::Klass/Validatable::ValidatesPresenceOf/name" do
435
+ module Mod
436
+ class Klass
437
+ include Validatable
438
+ validates_presence_of :name
439
+ end
440
+ end
441
+ Mod::Klass.validations.first.key
442
+ end
443
+
444
+ expect "/Validatable::ValidatesPresenceOf/custom key" do
445
+ klass = Class.new do
446
+ include Validatable
447
+ validates_presence_of :name, :key => "custom key"
448
+ end
449
+ klass.validations.first.key
450
+ end
451
+
452
+ expect "can't be empty" do
453
+ klass = Class.new do
454
+ include Validatable
455
+ validates_presence_of :name, :address
456
+ attr_accessor :name, :address
457
+ end
458
+ instance = klass.new
459
+ instance.validate_only("presence_of/name")
460
+ instance.errors.on(:name)
461
+ end
462
+
463
+ expect nil do
464
+ klass = Class.new do
465
+ include Validatable
466
+ validates_presence_of :name, :address
467
+ attr_accessor :name, :address
468
+ end
469
+ instance = klass.new
470
+ instance.validate_only("presence_of/name")
471
+ instance.errors.on(:address)
472
+ end
473
+
474
+ test 'validate callback' do
475
+ klass = Class.new do
476
+ include Validatable
477
+ attr_accessor :action
478
+ validate :action_presence
479
+
480
+ private
481
+ def action_presence
482
+ errors.add(:action, 'is invalid') if action.blank?
483
+ end
484
+ end
485
+ instance = klass.new
486
+ instance.valid?
487
+ assert_equal 'is invalid', instance.errors.on(:action)
488
+
489
+ instance.action = 'walk'
490
+ instance.valid?
491
+ assert_nil instance.errors.on(:action)
492
+ end
493
+
494
+ test 'validate on create callback for new record' do
495
+ klass = Class.new do
496
+ include Validatable
497
+ attr_accessor :action
498
+ validate_on_create :action_presence
499
+
500
+ def new?
501
+ true
502
+ end
503
+
504
+ private
505
+ def action_presence
506
+ errors.add(:action, 'is invalid') if action.blank?
507
+ end
508
+ end
509
+ instance = klass.new
510
+ instance.valid?
511
+ assert_equal 'is invalid', instance.errors.on(:action)
512
+
513
+ instance.action = 'walk'
514
+ instance.valid?
515
+ assert_nil instance.errors.on(:action)
516
+ end
517
+
518
+ test 'validate on create callback for not new record' do
519
+ klass = Class.new do
520
+ include Validatable
521
+ attr_accessor :action
522
+ validate_on_create :action_presence
523
+
524
+ def new?
525
+ false
526
+ end
527
+
528
+ private
529
+ def action_presence
530
+ errors.add(:action, 'is invalid') if action.blank?
531
+ end
532
+ end
533
+ instance = klass.new
534
+ instance.valid?
535
+ assert_nil instance.errors.on(:action)
536
+
537
+ instance.action = 'walk'
538
+ instance.valid?
539
+ assert_nil instance.errors.on(:action)
540
+ end
541
+
542
+ test 'validate on update callback for new record' do
543
+ klass = Class.new do
544
+ include Validatable
545
+ attr_accessor :action
546
+ validate_on_update :action_presence
547
+
548
+ def new?
549
+ true
550
+ end
551
+
552
+ private
553
+ def action_presence
554
+ errors.add(:action, 'is invalid') if action.blank?
555
+ end
556
+ end
557
+ instance = klass.new
558
+ instance.valid?
559
+ assert_nil instance.errors.on(:action)
560
+
561
+ instance.action = 'walk'
562
+ instance.valid?
563
+ assert_nil instance.errors.on(:action)
564
+ end
565
+
566
+ test 'validate on update callback for not new record' do
567
+ klass = Class.new do
568
+ include Validatable
569
+ attr_accessor :action
570
+ validate_on_update :action_presence
571
+
572
+ def new?
573
+ false
574
+ end
575
+
576
+ private
577
+ def action_presence
578
+ errors.add(:action, 'is invalid') if action.blank?
579
+ end
580
+ end
581
+ instance = klass.new
582
+ instance.valid?
583
+ assert_equal 'is invalid', instance.errors.on(:action)
584
+
585
+ instance.action = 'walk'
586
+ instance.valid?
587
+ assert_nil instance.errors.on(:action)
588
+ end
589
+ end