validatable 1.4.0 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/errors.rb +6 -0
- data/lib/macros.rb +200 -0
- data/lib/requireable.rb +1 -1
- data/lib/understandable.rb +1 -1
- data/lib/validatable.rb +1 -0
- data/lib/validatable_assertions.rb +61 -38
- data/lib/validatable_class_methods.rb +7 -201
- data/lib/validatable_instance_methods.rb +11 -10
- data/lib/validation_assertion.rb +1 -1
- data/lib/validation_assertion_collector.rb +2 -2
- data/rakefile.rb +1 -1
- data/test/functional/validatable_assertions_test.rb +11 -11
- data/test/functional/validatable_test.rb +14 -2
- data/test/unit/validatable_test.rb +1 -1
- metadata +3 -2
data/lib/errors.rb
CHANGED
@@ -25,10 +25,16 @@ module Validatable
|
|
25
25
|
self
|
26
26
|
end
|
27
27
|
|
28
|
+
# call-seq: replace(attribute)
|
29
|
+
#
|
30
|
+
# * Replaces the errors value for the given +attribute+
|
28
31
|
def replace(attribute, value)
|
29
32
|
errors[attribute.to_sym] = value
|
30
33
|
end
|
31
34
|
|
35
|
+
# call-seq: raw(attribute)
|
36
|
+
#
|
37
|
+
# * Returns an array of error messages associated with the specified +attribute+.
|
32
38
|
def raw(attribute)
|
33
39
|
errors[attribute.to_sym]
|
34
40
|
end
|
data/lib/macros.rb
ADDED
@@ -0,0 +1,200 @@
|
|
1
|
+
module Validatable
|
2
|
+
module Macros
|
3
|
+
# call-seq: validates_format_of(*args)
|
4
|
+
#
|
5
|
+
# Validates whether the value of the specified attribute is of the
|
6
|
+
# correct form by matching it against the regular expression provided.
|
7
|
+
#
|
8
|
+
# class Person
|
9
|
+
# include Validatable
|
10
|
+
# validates_format_of :first_name, :with => /[ A-Za-z]/
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# A regular expression must be provided or else an exception will be raised.
|
14
|
+
#
|
15
|
+
# Configuration options:
|
16
|
+
#
|
17
|
+
# * message - The message to add to the errors collection when the validation fails
|
18
|
+
# * times - The number of times the validation applies
|
19
|
+
# * level - The level at which the validation should occur
|
20
|
+
# * if - A block that when executed must return true of the validation will not occur
|
21
|
+
# * with - The regular expression used to validate the format
|
22
|
+
# * group - The group that this validation belongs to. A validation can belong to multiple groups
|
23
|
+
def validates_format_of(*args)
|
24
|
+
add_validations(args, ValidatesFormatOf)
|
25
|
+
end
|
26
|
+
|
27
|
+
# call-seq: validates_length_of(*args)
|
28
|
+
#
|
29
|
+
# Validates that the specified attribute matches the length restrictions supplied.
|
30
|
+
#
|
31
|
+
# class Person
|
32
|
+
# include Validatable
|
33
|
+
# validates_length_of :first_name, :maximum=>30
|
34
|
+
# validates_length_of :last_name, :minimum=>30
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# Configuration options:
|
38
|
+
#
|
39
|
+
# * message - The message to add to the errors collection when the validation fails
|
40
|
+
# * times - The number of times the validation applies
|
41
|
+
# * level - The level at which the validation should occur
|
42
|
+
# * if - A block that when executed must return true of the validation will not occur
|
43
|
+
# * minimum - The minimum size of the attribute
|
44
|
+
# * maximum - The maximum size of the attribute
|
45
|
+
# * is - The size the attribute must be
|
46
|
+
# * within - A range that the size of the attribute must fall within
|
47
|
+
# * group - The group that this validation belongs to. A validation can belong to multiple groups
|
48
|
+
def validates_length_of(*args)
|
49
|
+
add_validations(args, ValidatesLengthOf)
|
50
|
+
end
|
51
|
+
|
52
|
+
# call-seq: validates_numericality_of(*args)
|
53
|
+
#
|
54
|
+
# Validates that the specified attribute is numeric.
|
55
|
+
#
|
56
|
+
# class Person
|
57
|
+
# include Validatable
|
58
|
+
# validates_numericality_of :age
|
59
|
+
# end
|
60
|
+
#
|
61
|
+
# Configuration options:
|
62
|
+
#
|
63
|
+
# * message - The message to add to the errors collection when the validation fails
|
64
|
+
# * times - The number of times the validation applies
|
65
|
+
# * level - The level at which the validation should occur
|
66
|
+
# * if - A block that when executed must return true of the validation will not occur
|
67
|
+
# * group - The group that this validation belongs to. A validation can belong to multiple groups
|
68
|
+
# * only_integer - Whether the attribute must be an integer (default is false)
|
69
|
+
def validates_numericality_of(*args)
|
70
|
+
add_validations(args, ValidatesNumericalityOf)
|
71
|
+
end
|
72
|
+
|
73
|
+
# call-seq: validates_acceptance_of(*args)
|
74
|
+
#
|
75
|
+
# Encapsulates the pattern of wanting to validate the acceptance of a terms of service check box (or similar agreement). Example:
|
76
|
+
#
|
77
|
+
# class Person
|
78
|
+
# include Validatable
|
79
|
+
# validates_acceptance_of :terms_of_service
|
80
|
+
# validates_acceptance_of :eula, :message => "must be abided"
|
81
|
+
# end
|
82
|
+
#
|
83
|
+
# Configuration options:
|
84
|
+
#
|
85
|
+
# * message - The message to add to the errors collection when the validation fails
|
86
|
+
# * times - The number of times the validation applies
|
87
|
+
# * level - The level at which the validation should occur
|
88
|
+
# * if - A block that when executed must return true of the validation will not occur
|
89
|
+
# * group - The group that this validation belongs to. A validation can belong to multiple groups
|
90
|
+
def validates_acceptance_of(*args)
|
91
|
+
add_validations(args, ValidatesAcceptanceOf)
|
92
|
+
end
|
93
|
+
|
94
|
+
# call-seq: validates_confirmation_of(*args)
|
95
|
+
#
|
96
|
+
# Encapsulates the pattern of wanting to validate a password or email address field with a confirmation. Example:
|
97
|
+
#
|
98
|
+
# Class:
|
99
|
+
# class PersonPresenter
|
100
|
+
# include Validatable
|
101
|
+
# validates_confirmation_of :user_name, :password
|
102
|
+
# validates_confirmation_of :email_address, :message => "should match confirmation"
|
103
|
+
# end
|
104
|
+
#
|
105
|
+
# View:
|
106
|
+
# <%= password_field "person", "password" %>
|
107
|
+
# <%= password_field "person", "password_confirmation" %>
|
108
|
+
#
|
109
|
+
# Configuration options:
|
110
|
+
#
|
111
|
+
# * case_sensitive - Whether or not to apply case-sensitivity on the comparison. Defaults to true.
|
112
|
+
# * message - The message to add to the errors collection when the validation fails
|
113
|
+
# * times - The number of times the validation applies
|
114
|
+
# * level - The level at which the validation should occur
|
115
|
+
# * if - A block that when executed must return true of the validation will not occur
|
116
|
+
# * group - The group that this validation belongs to. A validation can belong to multiple groups
|
117
|
+
def validates_confirmation_of(*args)
|
118
|
+
add_validations(args, ValidatesConfirmationOf)
|
119
|
+
end
|
120
|
+
|
121
|
+
# call-seq: validates_presence_of(*args)
|
122
|
+
#
|
123
|
+
# Validates that the specified attributes are not nil or an empty string
|
124
|
+
#
|
125
|
+
# class Person
|
126
|
+
# include Validatable
|
127
|
+
# validates_presence_of :first_name
|
128
|
+
# end
|
129
|
+
#
|
130
|
+
# The first_name attribute must be in the object and it cannot be nil or empty.
|
131
|
+
#
|
132
|
+
# Configuration options:
|
133
|
+
#
|
134
|
+
# * message - The message to add to the errors collection when the validation fails
|
135
|
+
# * times - The number of times the validation applies
|
136
|
+
# * level - The level at which the validation should occur
|
137
|
+
# * if - A block that when executed must return true of the validation will not occur
|
138
|
+
# * group - The group that this validation belongs to. A validation can belong to multiple groups
|
139
|
+
def validates_presence_of(*args)
|
140
|
+
add_validations(args, ValidatesPresenceOf)
|
141
|
+
end
|
142
|
+
|
143
|
+
# call-seq: validates_true_for(*args)
|
144
|
+
#
|
145
|
+
# Validates that the logic evaluates to true
|
146
|
+
#
|
147
|
+
# class Person
|
148
|
+
# include Validatable
|
149
|
+
# validates_true_for :first_name, :logic => lambda { first_name == 'Jamie' }
|
150
|
+
# end
|
151
|
+
#
|
152
|
+
# The logic option is required.
|
153
|
+
#
|
154
|
+
# Configuration options:
|
155
|
+
#
|
156
|
+
# * message - The message to add to the errors collection when the validation fails
|
157
|
+
# * times - The number of times the validation applies
|
158
|
+
# * level - The level at which the validation should occur
|
159
|
+
# * if - A block that when executed must return true of the validation will not occur
|
160
|
+
# * group - The group that this validation belongs to. A validation can belong to multiple groups
|
161
|
+
# * logic - A block that executes to perform the validation
|
162
|
+
def validates_true_for(*args)
|
163
|
+
add_validations(args, ValidatesTrueFor)
|
164
|
+
end
|
165
|
+
|
166
|
+
# call-seq: include_validations_for(attribute_to_validate, options = {})
|
167
|
+
#
|
168
|
+
# Validates the specified attributes.
|
169
|
+
# class Person
|
170
|
+
# include Validatable
|
171
|
+
# validates_presence_of :name
|
172
|
+
# attr_accessor :name
|
173
|
+
# end
|
174
|
+
#
|
175
|
+
# class PersonPresenter
|
176
|
+
# include Validatable
|
177
|
+
# include_validations_for :person, :map => { :name => :namen }, :if => lambda { not person.nil? }
|
178
|
+
# attr_accessor :person
|
179
|
+
#
|
180
|
+
# def initialize(person)
|
181
|
+
# @person = person
|
182
|
+
# end
|
183
|
+
# end
|
184
|
+
#
|
185
|
+
# presenter = PersonPresenter.new(Person.new)
|
186
|
+
# presenter.valid? #=> false
|
187
|
+
# presenter.errors.on(:namen) #=> "can't be blank"
|
188
|
+
#
|
189
|
+
# The person attribute will be validated.
|
190
|
+
# If person is invalid the errors will be added to the PersonPresenter errors collection.
|
191
|
+
#
|
192
|
+
# Configuration options:
|
193
|
+
#
|
194
|
+
# * map - A hash that maps attributes of the child to attributes of the parent.
|
195
|
+
# * if - A block that when executed must return true of the validation will not occur.
|
196
|
+
def include_validations_for(attribute_to_validate, options = {})
|
197
|
+
children_to_validate << ChildValidation.new(attribute_to_validate, options[:map] || {}, options[:if] || lambda { true })
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
data/lib/requireable.rb
CHANGED
data/lib/understandable.rb
CHANGED
data/lib/validatable.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'forwardable'
|
2
2
|
require File.expand_path(File.dirname(__FILE__) + '/errors')
|
3
3
|
require File.expand_path(File.dirname(__FILE__) + '/validatable_class_methods')
|
4
|
+
require File.expand_path(File.dirname(__FILE__) + '/macros')
|
4
5
|
require File.expand_path(File.dirname(__FILE__) + '/validatable_instance_methods')
|
5
6
|
require File.expand_path(File.dirname(__FILE__) + '/child_validation')
|
6
7
|
require File.expand_path(File.dirname(__FILE__) + '/understandable')
|
@@ -1,51 +1,74 @@
|
|
1
|
-
module
|
1
|
+
module Validatable
|
2
|
+
module Assertions
|
2
3
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
def create_message_for_assertion(assertion) #:nodoc:
|
5
|
+
message = "#{assertion.klass} does not contain a #{assertion.validation_type} for #{assertion.attribute}"
|
6
|
+
message += " with options #{assertion.options.inspect}" unless assertion.options == {}
|
7
|
+
message
|
8
|
+
end
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
10
|
+
def create_backtrace #:nodoc:
|
11
|
+
backtrace = caller
|
12
|
+
backtrace.shift
|
13
|
+
backtrace.shift
|
14
|
+
backtrace
|
15
|
+
end
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
17
|
+
def validation_matching_proc(assertion) #:nodoc:
|
18
|
+
lambda do |validation|
|
19
|
+
result = assertion.validation_type === validation
|
20
|
+
result &&= assertion.attribute == validation.attribute
|
21
|
+
assertion.options.each_pair do |key, value|
|
22
|
+
validation_value = validation.send key if validation.respond_to? key
|
23
|
+
result = false if validation_value.nil?
|
24
|
+
result &&= validation_value == value
|
25
|
+
end
|
26
|
+
result
|
24
27
|
end
|
25
|
-
result
|
26
28
|
end
|
27
|
-
end
|
28
29
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
30
|
+
def self.included(klass) #:nodoc:
|
31
|
+
Test::Unit::TestCase.class_eval do
|
32
|
+
def self.create_test_name(assertion) #:nodoc:
|
33
|
+
"test#{assertion.validation_type.to_s.gsub(/Validatable::/,'').gsub(/([A-Z])/, '_\1').downcase}_#{assertion.attribute}"
|
34
|
+
end
|
34
35
|
|
35
|
-
|
36
|
-
|
37
|
-
|
36
|
+
def self.define_test_method name, &block #:nodoc:
|
37
|
+
class_eval do
|
38
|
+
define_method name, &block
|
39
|
+
end
|
38
40
|
end
|
39
41
|
end
|
40
|
-
end
|
41
42
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
43
|
+
Class.class_eval do
|
44
|
+
# call-seq: must_validate
|
45
|
+
#
|
46
|
+
# class FooTest < Test::Unit::TestCase
|
47
|
+
# include Validatable::Assertions
|
48
|
+
#
|
49
|
+
# Foo.must_validate do
|
50
|
+
# presence_of :name
|
51
|
+
# format_of(:name).with(/^[A-Z]/)
|
52
|
+
# numericality_of(:age).only_integer(true)
|
53
|
+
# end
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# The above code creates a test for each line in the block given to must_validate.
|
57
|
+
# If the Foo class does not contain a presence of validation for name,
|
58
|
+
# an error with the text "Foo does not contain a Validatable::ValidatesPresenceOf for name" will be raised.
|
59
|
+
#
|
60
|
+
# Clearly this solution has limitations. Any validates_true_for validation cannot be tested using
|
61
|
+
# this DSL style of testing. Furthermore, any validation that uses an :if argument cannot use this DSL,
|
62
|
+
# since those validations require an instance to eval the :if argument against. However, for validations
|
63
|
+
# that are not validates_true_for and do not rely on an :if argument, the ValidatableAssertions can
|
64
|
+
# replace various existing success and failure validation tests.
|
65
|
+
def must_validate(&block)
|
66
|
+
test_class = eval "self", block.binding
|
67
|
+
ValidationAssertionCollector.gather(self, &block).each do |assertion|
|
68
|
+
test_class.define_test_method test_class.create_test_name(assertion) do
|
69
|
+
validation = assertion.klass.validations.find &validation_matching_proc(assertion)
|
70
|
+
add_failure create_message_for_assertion(assertion), create_backtrace if validation.nil?
|
71
|
+
end
|
49
72
|
end
|
50
73
|
end
|
51
74
|
end
|
@@ -1,201 +1,7 @@
|
|
1
1
|
module Validatable
|
2
|
-
module ClassMethods
|
3
|
-
# call-seq: validates_format_of(*args)
|
4
|
-
#
|
5
|
-
# Validates whether the value of the specified attribute is of the correct form by matching it against the regular expression provided.
|
6
|
-
#
|
7
|
-
# class Person
|
8
|
-
# include Validatable
|
9
|
-
# validates_format_of :first_name, :with => /[ A-Za-z]/
|
10
|
-
# end
|
11
|
-
#
|
12
|
-
# A regular expression must be provided or else an exception will be raised.
|
13
|
-
#
|
14
|
-
# Configuration options:
|
15
|
-
#
|
16
|
-
# * message - The message to add to the errors collection when the validation fails
|
17
|
-
# * times - The number of times the validation applies
|
18
|
-
# * level - The level at which the validation should occur
|
19
|
-
# * if - A block that when executed must return true of the validation will not occur
|
20
|
-
# * with - The regular expression used to validate the format
|
21
|
-
# * group - The group that this validation belongs to. A validation can belong to multiple groups
|
22
|
-
def validates_format_of(*args)
|
23
|
-
add_validations(args, ValidatesFormatOf)
|
24
|
-
end
|
25
|
-
|
26
|
-
# call-seq: validates_length_of(*args)
|
27
|
-
#
|
28
|
-
# Validates that the specified attribute matches the length restrictions supplied.
|
29
|
-
#
|
30
|
-
# class Person
|
31
|
-
# include Validatable
|
32
|
-
# validates_length_of :first_name, :maximum=>30
|
33
|
-
# validates_length_of :last_name, :minimum=>30
|
34
|
-
# end
|
35
|
-
#
|
36
|
-
# Configuration options:
|
37
|
-
#
|
38
|
-
# * message - The message to add to the errors collection when the validation fails
|
39
|
-
# * times - The number of times the validation applies
|
40
|
-
# * level - The level at which the validation should occur
|
41
|
-
# * if - A block that when executed must return true of the validation will not occur
|
42
|
-
# * minimum - The minimum size of the attribute
|
43
|
-
# * maximum - The maximum size of the attribute
|
44
|
-
# * is - The size the attribute must be
|
45
|
-
# * within - A range that the size of the attribute must fall within
|
46
|
-
# * group - The group that this validation belongs to. A validation can belong to multiple groups
|
47
|
-
def validates_length_of(*args)
|
48
|
-
add_validations(args, ValidatesLengthOf)
|
49
|
-
end
|
50
|
-
|
51
|
-
# call-seq: validates_numericality_of(*args)
|
52
|
-
#
|
53
|
-
# Validates that the specified attribute is numeric.
|
54
|
-
#
|
55
|
-
# class Person
|
56
|
-
# include Validatable
|
57
|
-
# validates_numericality_of :age
|
58
|
-
# end
|
59
|
-
#
|
60
|
-
# Configuration options:
|
61
|
-
#
|
62
|
-
# * message - The message to add to the errors collection when the validation fails
|
63
|
-
# * times - The number of times the validation applies
|
64
|
-
# * level - The level at which the validation should occur
|
65
|
-
# * if - A block that when executed must return true of the validation will not occur
|
66
|
-
# * group - The group that this validation belongs to. A validation can belong to multiple groups
|
67
|
-
# * only_integer - Whether the attribute must be an integer (default is false)
|
68
|
-
def validates_numericality_of(*args)
|
69
|
-
add_validations(args, ValidatesNumericalityOf)
|
70
|
-
end
|
71
|
-
|
72
|
-
# call-seq: validates_acceptance_of(*args)
|
73
|
-
#
|
74
|
-
# Encapsulates the pattern of wanting to validate the acceptance of a terms of service check box (or similar agreement). Example:
|
75
|
-
#
|
76
|
-
# class Person
|
77
|
-
# include Validatable
|
78
|
-
# validates_acceptance_of :terms_of_service
|
79
|
-
# validates_acceptance_of :eula, :message => "must be abided"
|
80
|
-
# end
|
81
|
-
#
|
82
|
-
# Configuration options:
|
83
|
-
#
|
84
|
-
# * message - The message to add to the errors collection when the validation fails
|
85
|
-
# * times - The number of times the validation applies
|
86
|
-
# * level - The level at which the validation should occur
|
87
|
-
# * if - A block that when executed must return true of the validation will not occur
|
88
|
-
# * group - The group that this validation belongs to. A validation can belong to multiple groups
|
89
|
-
def validates_acceptance_of(*args)
|
90
|
-
add_validations(args, ValidatesAcceptanceOf)
|
91
|
-
end
|
92
|
-
|
93
|
-
# call-seq: validates_confirmation_of(*args)
|
94
|
-
#
|
95
|
-
# Encapsulates the pattern of wanting to validate a password or email address field with a confirmation. Example:
|
96
|
-
#
|
97
|
-
# Class:
|
98
|
-
# class PersonPresenter
|
99
|
-
# include Validatable
|
100
|
-
# validates_confirmation_of :user_name, :password
|
101
|
-
# validates_confirmation_of :email_address, :message => "should match confirmation"
|
102
|
-
# end
|
103
|
-
#
|
104
|
-
# View:
|
105
|
-
# <%= password_field "person", "password" %>
|
106
|
-
# <%= password_field "person", "password_confirmation" %>
|
107
|
-
#
|
108
|
-
# Configuration options:
|
109
|
-
#
|
110
|
-
# * case_sensitive - Whether or not to apply case-sensitivity on the comparison. Defaults to true.
|
111
|
-
# * message - The message to add to the errors collection when the validation fails
|
112
|
-
# * times - The number of times the validation applies
|
113
|
-
# * level - The level at which the validation should occur
|
114
|
-
# * if - A block that when executed must return true of the validation will not occur
|
115
|
-
# * group - The group that this validation belongs to. A validation can belong to multiple groups
|
116
|
-
def validates_confirmation_of(*args)
|
117
|
-
add_validations(args, ValidatesConfirmationOf)
|
118
|
-
end
|
119
|
-
|
120
|
-
# call-seq: validates_presence_of(*args)
|
121
|
-
#
|
122
|
-
# Validates that the specified attributes are not nil or an empty string
|
123
|
-
#
|
124
|
-
# class Person
|
125
|
-
# include Validatable
|
126
|
-
# validates_presence_of :first_name
|
127
|
-
# end
|
128
|
-
#
|
129
|
-
# The first_name attribute must be in the object and it cannot be nil or empty.
|
130
|
-
#
|
131
|
-
# Configuration options:
|
132
|
-
#
|
133
|
-
# * message - The message to add to the errors collection when the validation fails
|
134
|
-
# * times - The number of times the validation applies
|
135
|
-
# * level - The level at which the validation should occur
|
136
|
-
# * if - A block that when executed must return true of the validation will not occur
|
137
|
-
# * group - The group that this validation belongs to. A validation can belong to multiple groups
|
138
|
-
def validates_presence_of(*args)
|
139
|
-
add_validations(args, ValidatesPresenceOf)
|
140
|
-
end
|
141
|
-
|
142
|
-
# call-seq: validates_true_for(*args)
|
143
|
-
#
|
144
|
-
# Validates that the logic evaluates to true
|
145
|
-
#
|
146
|
-
# class Person
|
147
|
-
# include Validatable
|
148
|
-
# validates_true_for :first_name, :logic => lambda { first_name == 'Jamie' }
|
149
|
-
# end
|
150
|
-
#
|
151
|
-
# The logic option is required.
|
152
|
-
#
|
153
|
-
# Configuration options:
|
154
|
-
#
|
155
|
-
# * message - The message to add to the errors collection when the validation fails
|
156
|
-
# * times - The number of times the validation applies
|
157
|
-
# * level - The level at which the validation should occur
|
158
|
-
# * if - A block that when executed must return true of the validation will not occur
|
159
|
-
# * group - The group that this validation belongs to. A validation can belong to multiple groups
|
160
|
-
# * logic - A block that executes to perform the validation
|
161
|
-
def validates_true_for(*args)
|
162
|
-
add_validations(args, ValidatesTrueFor)
|
163
|
-
end
|
164
|
-
|
165
|
-
# call-seq: include_validations_for(attribute_to_validate, options = {})
|
166
|
-
#
|
167
|
-
# Validates the specified attributes.
|
168
|
-
# class Person
|
169
|
-
# include Validatable
|
170
|
-
# validates_presence_of :name
|
171
|
-
# attr_accessor :name
|
172
|
-
# end
|
173
|
-
#
|
174
|
-
# class PersonPresenter
|
175
|
-
# include Validatable
|
176
|
-
# include_validations_for :person, :map => { :name => :namen }, :if => lambda { not person.nil? }
|
177
|
-
# attr_accessor :person
|
178
|
-
#
|
179
|
-
# def initialize(person)
|
180
|
-
# @person = person
|
181
|
-
# end
|
182
|
-
# end
|
183
|
-
#
|
184
|
-
# presenter = PersonPresenter.new(Person.new)
|
185
|
-
# presenter.valid? #=> false
|
186
|
-
# presenter.errors.on(:namen) #=> "can't be blank"
|
187
|
-
#
|
188
|
-
# The person attribute will be validated. If person is invalid the errors will be added to the PersonPresenter errors collection.
|
189
|
-
#
|
190
|
-
# Configuration options:
|
191
|
-
#
|
192
|
-
# * map - A hash that maps attributes of the child to attributes of the parent.
|
193
|
-
# * if - A block that when executed must return true of the validation will not occur.
|
194
|
-
def include_validations_for(attribute_to_validate, options = {})
|
195
|
-
children_to_validate << ChildValidation.new(attribute_to_validate, options[:map] || {}, options[:if] || lambda { true })
|
196
|
-
end
|
2
|
+
module ClassMethods #:nodoc:
|
197
3
|
|
198
|
-
def validate_children(instance, group)
|
4
|
+
def validate_children(instance, group)
|
199
5
|
self.children_to_validate.each do |child_validation|
|
200
6
|
next unless child_validation.should_validate?(instance)
|
201
7
|
child = instance.send child_validation.attribute
|
@@ -216,11 +22,11 @@ module Validatable
|
|
216
22
|
end
|
217
23
|
end
|
218
24
|
|
219
|
-
def validations
|
25
|
+
def validations
|
220
26
|
@validations ||= []
|
221
27
|
end
|
222
28
|
|
223
|
-
def add_error(instance, attribute, msg)
|
29
|
+
def add_error(instance, attribute, msg)
|
224
30
|
instance.errors.add(attribute, msg)
|
225
31
|
end
|
226
32
|
|
@@ -230,7 +36,7 @@ module Validatable
|
|
230
36
|
|
231
37
|
protected
|
232
38
|
|
233
|
-
def add_validations(args, klass)
|
39
|
+
def add_validations(args, klass)
|
234
40
|
options = args.last.is_a?(Hash) ? args.pop : {}
|
235
41
|
args.each do |attribute|
|
236
42
|
new_validation = klass.new self, attribute, options
|
@@ -239,7 +45,7 @@ module Validatable
|
|
239
45
|
end
|
240
46
|
end
|
241
47
|
|
242
|
-
def create_valid_method_for_groups(groups)
|
48
|
+
def create_valid_method_for_groups(groups)
|
243
49
|
groups.each do |group|
|
244
50
|
self.class_eval do
|
245
51
|
define_method "valid_for_#{group}?".to_sym do
|
@@ -249,7 +55,7 @@ module Validatable
|
|
249
55
|
end
|
250
56
|
end
|
251
57
|
|
252
|
-
def children_to_validate
|
58
|
+
def children_to_validate
|
253
59
|
@children_to_validate ||= []
|
254
60
|
end
|
255
61
|
|
@@ -1,13 +1,14 @@
|
|
1
1
|
module Validatable
|
2
2
|
def self.included(klass) #:nodoc:
|
3
3
|
klass.extend Validatable::ClassMethods
|
4
|
+
klass.extend Validatable::Macros
|
4
5
|
end
|
5
6
|
|
6
7
|
# call-seq: valid?
|
7
8
|
#
|
8
|
-
# Returns true if no errors were added otherwise false.
|
9
|
+
# Returns true if no errors were added otherwise false. Only executes validations that have no :groups option specified
|
9
10
|
def valid?
|
10
|
-
valid_for_group?
|
11
|
+
valid_for_group?(nil)
|
11
12
|
end
|
12
13
|
|
13
14
|
# call-seq: errors
|
@@ -17,22 +18,18 @@ module Validatable
|
|
17
18
|
@errors ||= Validatable::Errors.new
|
18
19
|
end
|
19
20
|
|
20
|
-
def valid_for_group?(group
|
21
|
+
def valid_for_group?(group) #:nodoc:
|
21
22
|
errors.clear
|
22
23
|
self.class.validate_children(self, group)
|
23
24
|
self.validate(group)
|
24
25
|
errors.empty?
|
25
26
|
end
|
26
27
|
|
27
|
-
def times_validated(key)
|
28
|
+
def times_validated(key) #:nodoc:
|
28
29
|
times_validated_hash[key] || 0
|
29
30
|
end
|
30
31
|
|
31
|
-
def
|
32
|
-
@times_validated_hash ||= {}
|
33
|
-
end
|
34
|
-
|
35
|
-
def increment_times_validated_for(validation)
|
32
|
+
def increment_times_validated_for(validation) #:nodoc:
|
36
33
|
if validation.key != nil
|
37
34
|
if times_validated_hash[validation.key].nil?
|
38
35
|
times_validated_hash[validation.key] = 1
|
@@ -43,6 +40,10 @@ module Validatable
|
|
43
40
|
end
|
44
41
|
|
45
42
|
protected
|
43
|
+
def times_validated_hash #:nodoc:
|
44
|
+
@times_validated_hash ||= {}
|
45
|
+
end
|
46
|
+
|
46
47
|
def validate(group) #:nodoc:
|
47
48
|
validation_levels.each do |level|
|
48
49
|
validations_for_level_and_group(level, group).each do |validation|
|
@@ -65,7 +66,7 @@ module Validatable
|
|
65
66
|
|
66
67
|
def validations_for_level_and_group(level, group) #:nodoc:
|
67
68
|
validations_for_level = self.class.validations.select { |validation| validation.level == level }
|
68
|
-
return validations_for_level if group.nil?
|
69
|
+
return validations_for_level.select { |validation| validation.groups.empty? } if group.nil?
|
69
70
|
validations_for_level.select { |validation| validation.groups.include?(group) }
|
70
71
|
end
|
71
72
|
|
data/lib/validation_assertion.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
class ValidationAssertionCollector
|
1
|
+
class ValidationAssertionCollector #:nodoc:
|
2
2
|
def self.gather(klass, &block)
|
3
3
|
collector = self.new(klass)
|
4
4
|
collector.instance_eval(&block)
|
@@ -12,7 +12,7 @@ class ValidationAssertionCollector
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
-
Validatable::
|
15
|
+
Validatable::Macros.public_instance_methods.sort.grep(/^validates_/).each do |validation_method|
|
16
16
|
next if validation_method == 'validates_true_for'
|
17
17
|
validatable_class = Validatable.const_get(validation_method.split(/_/).collect { |word| word.capitalize}.join)
|
18
18
|
define_method_for_validation_method(validation_method, validatable_class)
|
data/rakefile.rb
CHANGED
@@ -29,7 +29,7 @@ Gem::manage_gems
|
|
29
29
|
specification = Gem::Specification.new do |s|
|
30
30
|
s.name = "validatable"
|
31
31
|
s.summary = "Validatable is a library for adding validations."
|
32
|
-
s.version = "1.
|
32
|
+
s.version = "1.5.0"
|
33
33
|
s.author = 'Jay Fields'
|
34
34
|
s.description = "Validatable is a library for adding validations."
|
35
35
|
s.email = 'validatable-developer@rubyforge.org'
|
@@ -1,23 +1,23 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
2
2
|
|
3
|
-
class
|
3
|
+
class Validatable::AssertionsTest < Test::Unit::TestCase
|
4
4
|
expect "Klass does not contain a ValidationType for Attribute" do
|
5
5
|
klass = Class.new do
|
6
|
-
include
|
6
|
+
include Validatable::Assertions
|
7
7
|
end
|
8
8
|
klass.new.create_message_for_assertion(stub(:klass=>"Klass", :validation_type=>"ValidationType", :attribute=>"Attribute", :options=>{}))
|
9
9
|
end
|
10
10
|
|
11
11
|
expect "Klass does not contain a ValidationType for Attribute" do
|
12
12
|
klass = Class.new do
|
13
|
-
include
|
13
|
+
include Validatable::Assertions
|
14
14
|
end
|
15
15
|
klass.new.create_message_for_assertion(stub(:klass=>"Klass", :validation_type=>"ValidationType", :attribute=>"Attribute", :options=>{}))
|
16
16
|
end
|
17
17
|
|
18
18
|
expect false do
|
19
19
|
klass = Class.new do
|
20
|
-
include
|
20
|
+
include Validatable::Assertions
|
21
21
|
end
|
22
22
|
assertion = stub(:validation_type=>stub(:=== => true), :attribute => "attribute", :options=>{:level => 1, :times => 2})
|
23
23
|
validation = stub(:validation_type=>"validation_type", :attribute => "attribute", :level => 2, :times => 2)
|
@@ -26,7 +26,7 @@ class ValidatableAssertionsTest < Test::Unit::TestCase
|
|
26
26
|
|
27
27
|
expect false do
|
28
28
|
klass = Class.new do
|
29
|
-
include
|
29
|
+
include Validatable::Assertions
|
30
30
|
end
|
31
31
|
assertion = stub(:validation_type=>stub(:=== => true), :attribute => "non matching attribute", :options=>{})
|
32
32
|
validation = stub(:validation_type=>"validation_type", :attribute => nil)
|
@@ -35,7 +35,7 @@ class ValidatableAssertionsTest < Test::Unit::TestCase
|
|
35
35
|
|
36
36
|
expect false do
|
37
37
|
klass = Class.new do
|
38
|
-
include
|
38
|
+
include Validatable::Assertions
|
39
39
|
end
|
40
40
|
assertion = stub(:validation_type=>"non matching validation_type", :options=>{})
|
41
41
|
validation = stub(:validation_type=>"validation_type")
|
@@ -44,7 +44,7 @@ class ValidatableAssertionsTest < Test::Unit::TestCase
|
|
44
44
|
|
45
45
|
expect true do
|
46
46
|
klass = Class.new do
|
47
|
-
include
|
47
|
+
include Validatable::Assertions
|
48
48
|
end
|
49
49
|
assertion = stub(:validation_type=>stub(:=== => true), :attribute => "attribute", :options=>{:level => 1, :times => 2})
|
50
50
|
validation = stub(:validation_type=>"validation_type", :attribute => "attribute", :level => 1, :times => 2)
|
@@ -53,7 +53,7 @@ class ValidatableAssertionsTest < Test::Unit::TestCase
|
|
53
53
|
|
54
54
|
expect true do
|
55
55
|
Class.new do
|
56
|
-
include
|
56
|
+
include Validatable::Assertions
|
57
57
|
end
|
58
58
|
|
59
59
|
Class.respond_to? :must_validate
|
@@ -61,14 +61,14 @@ class ValidatableAssertionsTest < Test::Unit::TestCase
|
|
61
61
|
|
62
62
|
expect "test_validates_presence_of_name" do
|
63
63
|
test_class = Class.new(Test::Unit::TestCase) do
|
64
|
-
include
|
64
|
+
include Validatable::Assertions
|
65
65
|
end
|
66
66
|
test_class.create_test_name(stub(:validation_type=>Validatable::ValidatesPresenceOf, :attribute=>:name))
|
67
67
|
end
|
68
68
|
|
69
69
|
expect true do
|
70
70
|
test_class = Class.new(Test::Unit::TestCase) do
|
71
|
-
include
|
71
|
+
include Validatable::Assertions
|
72
72
|
end
|
73
73
|
|
74
74
|
test_class.define_test_method :some_test do
|
@@ -83,7 +83,7 @@ class ValidatableAssertionsTest < Test::Unit::TestCase
|
|
83
83
|
include Validatable
|
84
84
|
end
|
85
85
|
test_class = Class.new Test::Unit::TestCase do
|
86
|
-
include
|
86
|
+
include Validatable::Assertions
|
87
87
|
|
88
88
|
klass.must_validate do
|
89
89
|
presence_of :anything
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
2
2
|
|
3
3
|
module Functional
|
4
|
-
class ValidatableTest < Test::Unit::TestCase
|
4
|
+
class ValidatableTest < Test::Unit::TestCase
|
5
5
|
|
6
6
|
expect ArgumentError do
|
7
7
|
Class.new do
|
@@ -227,7 +227,19 @@ module Functional
|
|
227
227
|
instance.valid_for_group_one?
|
228
228
|
end
|
229
229
|
|
230
|
-
expect
|
230
|
+
expect true do
|
231
|
+
klass = Class.new do
|
232
|
+
include Validatable
|
233
|
+
validates_presence_of :name, :groups => :group_one
|
234
|
+
validates_presence_of :address
|
235
|
+
attr_accessor :name, :address
|
236
|
+
end
|
237
|
+
instance = klass.new
|
238
|
+
instance.address = 'anything'
|
239
|
+
instance.valid?
|
240
|
+
end
|
241
|
+
|
242
|
+
expect true do
|
231
243
|
klass = Class.new do
|
232
244
|
include Validatable
|
233
245
|
validates_presence_of :name, :groups => :group_one
|
@@ -3,7 +3,7 @@ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
|
3
3
|
module Unit
|
4
4
|
class ValidatableTest < Test::Unit::TestCase
|
5
5
|
expect false do
|
6
|
-
validation = stub_everything(:
|
6
|
+
validation = stub_everything(:should_validate? => true, :attribute => "attribute", :level => 1, :groups => [])
|
7
7
|
klass = Class.new do
|
8
8
|
include Validatable
|
9
9
|
validations << validation
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.2
|
|
3
3
|
specification_version: 1
|
4
4
|
name: validatable
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 1.
|
7
|
-
date: 2007-06-
|
6
|
+
version: 1.5.0
|
7
|
+
date: 2007-06-19 00:00:00 -04:00
|
8
8
|
summary: Validatable is a library for adding validations.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -31,6 +31,7 @@ authors:
|
|
31
31
|
files:
|
32
32
|
- lib/child_validation.rb
|
33
33
|
- lib/errors.rb
|
34
|
+
- lib/macros.rb
|
34
35
|
- lib/requireable.rb
|
35
36
|
- lib/understandable.rb
|
36
37
|
- lib/validatable.rb
|