validatable 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/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
|