ward 0.1.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/.document +5 -0
- data/.gitignore +28 -0
- data/LICENSE +19 -0
- data/README.markdown +99 -0
- data/Rakefile +47 -0
- data/VERSION +1 -0
- data/features/acceptance_matcher.feature +78 -0
- data/features/attribute_keyword.feature +13 -0
- data/features/close_to_matcher.feature +130 -0
- data/features/context_arguments.feature +47 -0
- data/features/equal_to_matcher.feature +25 -0
- data/features/error_messages.feature +69 -0
- data/features/external_validation.feature +15 -0
- data/features/has_matcher.feature +72 -0
- data/features/has_matcher_initialized_with_expectation.feature +94 -0
- data/features/has_matcher_relativities.feature +171 -0
- data/features/include_matcher.feature +28 -0
- data/features/is_keyword.feature +42 -0
- data/features/is_not_keyword.feature +62 -0
- data/features/match_matcher.feature +49 -0
- data/features/multiple_validators.feature +29 -0
- data/features/nil_matcher.feature +25 -0
- data/features/predicate_matcher.feature +23 -0
- data/features/present_matcher.feature +59 -0
- data/features/satisfy_matcher.feature +80 -0
- data/features/scenario_validation.feature +81 -0
- data/features/step_definitions/external_validation_steps.rb +69 -0
- data/features/step_definitions/generic_validation_steps.rb +33 -0
- data/features/step_definitions/object_definition_steps.rb +43 -0
- data/features/support/env.rb +12 -0
- data/features/support/object_builder.rb +33 -0
- data/features/support/struct.rb +38 -0
- data/lang/en.yml +56 -0
- data/lib/ward.rb +26 -0
- data/lib/ward/context.rb +70 -0
- data/lib/ward/context_chain.rb +87 -0
- data/lib/ward/dsl.rb +7 -0
- data/lib/ward/dsl/validation_block.rb +73 -0
- data/lib/ward/dsl/validation_builder.rb +190 -0
- data/lib/ward/errors.rb +213 -0
- data/lib/ward/matchers.rb +97 -0
- data/lib/ward/matchers/acceptance.rb +43 -0
- data/lib/ward/matchers/close_to.rb +60 -0
- data/lib/ward/matchers/equal_to.rb +33 -0
- data/lib/ward/matchers/has.rb +283 -0
- data/lib/ward/matchers/include.rb +54 -0
- data/lib/ward/matchers/match.rb +29 -0
- data/lib/ward/matchers/matcher.rb +68 -0
- data/lib/ward/matchers/nil.rb +30 -0
- data/lib/ward/matchers/predicate.rb +31 -0
- data/lib/ward/matchers/present.rb +56 -0
- data/lib/ward/matchers/satisfy.rb +65 -0
- data/lib/ward/spec.rb +17 -0
- data/lib/ward/spec/matcher_matcher.rb +114 -0
- data/lib/ward/support.rb +7 -0
- data/lib/ward/support/basic_object.rb +55 -0
- data/lib/ward/support/result.rb +49 -0
- data/lib/ward/validator.rb +147 -0
- data/lib/ward/validator_set.rb +115 -0
- data/lib/ward/version.rb +3 -0
- data/spec/lib/has_matcher_relativity_examples.rb +15 -0
- data/spec/lib/have_public_method_defined.rb +22 -0
- data/spec/rcov.opts +8 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +19 -0
- data/spec/ward/context_chain_spec.rb +178 -0
- data/spec/ward/context_spec.rb +57 -0
- data/spec/ward/dsl/validation_block_spec.rb +27 -0
- data/spec/ward/dsl/validation_builder_spec.rb +212 -0
- data/spec/ward/errors_spec.rb +149 -0
- data/spec/ward/matchers/acceptance_spec.rb +16 -0
- data/spec/ward/matchers/close_to_spec.rb +57 -0
- data/spec/ward/matchers/equal_to_spec.rb +16 -0
- data/spec/ward/matchers/has_spec.rb +175 -0
- data/spec/ward/matchers/include_spec.rb +41 -0
- data/spec/ward/matchers/match_spec.rb +21 -0
- data/spec/ward/matchers/matcher_spec.rb +54 -0
- data/spec/ward/matchers/nil_spec.rb +16 -0
- data/spec/ward/matchers/predicate_spec.rb +19 -0
- data/spec/ward/matchers/present_spec.rb +16 -0
- data/spec/ward/matchers/satisfy_spec.rb +68 -0
- data/spec/ward/matchers_spec.rb +51 -0
- data/spec/ward/spec/have_public_method_defined_spec.rb +31 -0
- data/spec/ward/spec/matcher_matcher_spec.rb +217 -0
- data/spec/ward/validator_set_spec.rb +178 -0
- data/spec/ward/validator_spec.rb +264 -0
- data/tasks/features.rake +15 -0
- data/tasks/rcov.rake +24 -0
- data/tasks/spec.rake +18 -0
- data/tasks/yard.rake +9 -0
- data/ward.gemspec +176 -0
- metadata +239 -0
data/lib/ward/dsl.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
module Ward
|
2
|
+
module DSL
|
3
|
+
# Creates one or more validators using a block.
|
4
|
+
#
|
5
|
+
# @see Ward::ValidatorSet.build
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
#
|
9
|
+
# # Builds a validation set with two validators
|
10
|
+
# #
|
11
|
+
# # * One for "author.name" and the EqualTo matcher.
|
12
|
+
# # * One for "title" with the Match matcher.
|
13
|
+
#
|
14
|
+
# ValidationBlock.new do |object|
|
15
|
+
# object.author.name.is.equal_to('Michael Scarn')
|
16
|
+
# object.title.match(/something/)
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
class ValidationBlock < Support::BasicObject
|
20
|
+
|
21
|
+
# Creates a new ValidationBlock instance.
|
22
|
+
#
|
23
|
+
# NOTE: Providing an existing ValidatorSet will result in a copy of that
|
24
|
+
# set being mutated; the original will not be changed.
|
25
|
+
#
|
26
|
+
# @param [Ward::ValidatorSet] set
|
27
|
+
# A ValidatorSet to which the built validators should be added.
|
28
|
+
#
|
29
|
+
def initialize(set = nil, &block)
|
30
|
+
@set = if set.nil? then Ward::ValidatorSet.new else set.dup end
|
31
|
+
run(&block) if block
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns the ValidatorSet created by the DSL.
|
35
|
+
#
|
36
|
+
# @return [Ward::ValidatorSet]
|
37
|
+
#
|
38
|
+
def to_validator_set
|
39
|
+
@set
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
# Runs a block, creating the appropriate validators.
|
45
|
+
#
|
46
|
+
# @todo Ensure that each matcher was correctly set up.
|
47
|
+
#
|
48
|
+
def run
|
49
|
+
@builders = []
|
50
|
+
yield self
|
51
|
+
@set.merge!(@builders.map { |builder| builder.to_validator })
|
52
|
+
@builders = nil
|
53
|
+
end
|
54
|
+
|
55
|
+
# Provides the DSL.
|
56
|
+
#
|
57
|
+
# Will take the given message and creates a new ValidationBuilder.
|
58
|
+
#
|
59
|
+
# @return [Ward::DSL::ValidationBuilder]
|
60
|
+
# Returns the created builder.
|
61
|
+
#
|
62
|
+
def method_missing(method, *extra_args, &block)
|
63
|
+
raise 'ValidationBlock can only be used when provided ' \
|
64
|
+
'with a block' if @builders.nil?
|
65
|
+
|
66
|
+
builder = ValidationBuilder.new.__send__(method, *extra_args, &block)
|
67
|
+
@builders.push(builder)
|
68
|
+
builder
|
69
|
+
end
|
70
|
+
|
71
|
+
end # ValidationBlock
|
72
|
+
end # DSL
|
73
|
+
end # Ward
|
@@ -0,0 +1,190 @@
|
|
1
|
+
module Ward
|
2
|
+
module DSL
|
3
|
+
# Creates a single {Validator}. Any message received which doesn't
|
4
|
+
# correspond with a matcher will be assumed to be part of the context.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
#
|
8
|
+
# # Builds a validation whose context is "author.name" and uses the
|
9
|
+
# # EqualTo matcher to ensure that the "author.name" is "Michel Scarn".
|
10
|
+
#
|
11
|
+
# ValidationBuilder.new.author.name.is.equal_to('Michael Scarn')
|
12
|
+
#
|
13
|
+
class ValidationBuilder < Support::BasicObject
|
14
|
+
|
15
|
+
# Creates a new ValidationBuilder instance.
|
16
|
+
#
|
17
|
+
def initialize
|
18
|
+
@context = Ward::ContextChain.new
|
19
|
+
@matcher, @message, @scenarios, @negative = nil, nil, nil, false
|
20
|
+
end
|
21
|
+
|
22
|
+
# Sets the error message to be used if the validation fails.
|
23
|
+
#
|
24
|
+
# This can be one of three possibilities:
|
25
|
+
#
|
26
|
+
# * A Hash. If the matcher used returns a different error state in
|
27
|
+
# order to provide more details about what failed (such as the Has
|
28
|
+
# matcher), you may provide a hash with custom error messages for
|
29
|
+
# each error state.
|
30
|
+
#
|
31
|
+
# * A String which will be used whenever the validation fails,
|
32
|
+
# regardless of what went wrong.
|
33
|
+
#
|
34
|
+
# * nil (default). The validation will use the default error message
|
35
|
+
# for the matcher.
|
36
|
+
#
|
37
|
+
# @param [Hash{Symbol => String}, String, nil] message
|
38
|
+
#
|
39
|
+
# @return [Ward::DSL::ValidatorBuilder]
|
40
|
+
# Returns self.
|
41
|
+
#
|
42
|
+
# @example Setting an explicit error message.
|
43
|
+
#
|
44
|
+
# validate do |person|
|
45
|
+
# person.name.is.present.message('You must enter a name!')
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# @example Setting an explicit error message with a Hash.
|
49
|
+
#
|
50
|
+
# validate do |person|
|
51
|
+
# person.name.length.is(1..50).message(
|
52
|
+
# :too_short => "Your name must be at least 1 character long",
|
53
|
+
# :too_long => "That's an interesting name!"
|
54
|
+
# )
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
def message(message)
|
58
|
+
@message = message
|
59
|
+
self
|
60
|
+
end
|
61
|
+
|
62
|
+
# Sets the name to be used for the context in error messages.
|
63
|
+
#
|
64
|
+
# When Ward generates error messages for you, it determines the
|
65
|
+
# 'context name' by joining the method names; for example 'name.length'
|
66
|
+
# becomes 'name length'.
|
67
|
+
#
|
68
|
+
# This isn't much use if you want to support languages other than
|
69
|
+
# English in your application, so the 'context' method allows you to set
|
70
|
+
# a custom string to be used. You may provide a String, in which case it
|
71
|
+
# will be used literally, a Hash of +language => String+, or a Symbol
|
72
|
+
# identifying a string to be used from a language file.
|
73
|
+
#
|
74
|
+
# See the localisation documentation for more examples.
|
75
|
+
#
|
76
|
+
def context(name)
|
77
|
+
@context_name = name
|
78
|
+
self
|
79
|
+
end
|
80
|
+
|
81
|
+
# Sets the scenarios under which the built validator should run.
|
82
|
+
#
|
83
|
+
# @param [Symbol, ...] scenarios
|
84
|
+
# The scenarios as Symbols.
|
85
|
+
#
|
86
|
+
# @return [Ward::DSL::ValidatorBuilder]
|
87
|
+
# Returns self.
|
88
|
+
#
|
89
|
+
def scenarios(*scenarios)
|
90
|
+
@scenarios = scenarios.flatten
|
91
|
+
self
|
92
|
+
end
|
93
|
+
|
94
|
+
alias_method :scenario, :scenarios
|
95
|
+
|
96
|
+
# Adds an attribute to the context chain.
|
97
|
+
#
|
98
|
+
# Useful your classes have attribute which you want to validate, and
|
99
|
+
# their name conflicts with a method on the validator DSL (e.g.
|
100
|
+
# "message").
|
101
|
+
#
|
102
|
+
# @param [Symbol] attribute
|
103
|
+
# The attribute to be validated.
|
104
|
+
#
|
105
|
+
# @return [Ward::DSL::ValidatorBuilder]
|
106
|
+
# Returns self.
|
107
|
+
#
|
108
|
+
def attribute(attribute, *args, &block)
|
109
|
+
@context << Ward::Context.new(attribute, *args, &block)
|
110
|
+
self
|
111
|
+
end
|
112
|
+
|
113
|
+
# Set this as a positive expectation. Can be omitted.
|
114
|
+
#
|
115
|
+
# @example
|
116
|
+
# object.name.is.blank
|
117
|
+
#
|
118
|
+
# @return [Ward::DSL::ValidatorBuilder]
|
119
|
+
# Returns self.
|
120
|
+
#
|
121
|
+
def is(*args)
|
122
|
+
equal_to(*args) unless args.empty?
|
123
|
+
self
|
124
|
+
end
|
125
|
+
|
126
|
+
# Set this as a negative expectation.
|
127
|
+
#
|
128
|
+
# @example
|
129
|
+
# object.name.is_not.blank
|
130
|
+
#
|
131
|
+
# @return [Ward::DSL::ValidatorBuilder]
|
132
|
+
# Returns self.
|
133
|
+
#
|
134
|
+
def is_not(*args)
|
135
|
+
@negative = true
|
136
|
+
is(*args)
|
137
|
+
end
|
138
|
+
|
139
|
+
alias_method :does_not, :is_not
|
140
|
+
|
141
|
+
# Provides the DSL.
|
142
|
+
#
|
143
|
+
# Will take the given message and use it to customise the matcher (if
|
144
|
+
# one is set), set a matcher, or extend the context.
|
145
|
+
#
|
146
|
+
# @return [Ward::DSL::ValidatorBuilder]
|
147
|
+
# Returns self.
|
148
|
+
#
|
149
|
+
def method_missing(method, *args, &block)
|
150
|
+
# I'd normally shy away from using method_missing, but there's no
|
151
|
+
# good alternative here since a user may register their own matchers
|
152
|
+
# later in the load process.
|
153
|
+
|
154
|
+
if @matcher
|
155
|
+
@matcher.__send__(method, *args, &block)
|
156
|
+
elsif Ward::Matchers.matchers.has_key?(method)
|
157
|
+
@matcher = Ward::Matchers.matchers[method].new(*args, &block)
|
158
|
+
elsif method.to_s =~ /\?$/
|
159
|
+
@matcher = Ward::Matchers::Predicate.new(method, *args, &block)
|
160
|
+
else
|
161
|
+
attribute(method, *args, &block)
|
162
|
+
end
|
163
|
+
|
164
|
+
self
|
165
|
+
end
|
166
|
+
|
167
|
+
# Converts the builder to a Validator instance.
|
168
|
+
#
|
169
|
+
# @return [Ward::Validator]
|
170
|
+
#
|
171
|
+
# @raise [IncompleteValidation]
|
172
|
+
# An IncompleteValidationError will be raised if builder does not have
|
173
|
+
# all of the needed information in order to create the necessary
|
174
|
+
# validation (for example, if no matcher has been set).
|
175
|
+
#
|
176
|
+
# @todo
|
177
|
+
# More descriptive error messages.
|
178
|
+
#
|
179
|
+
def to_validator
|
180
|
+
raise Ward::IncompleteValidator,
|
181
|
+
'Validator was missing a matcher' if @matcher.nil?
|
182
|
+
|
183
|
+
Ward::Validator.new(@context, @matcher, :message => @message,
|
184
|
+
:scenarios => @scenarios, :negative => @negative,
|
185
|
+
:context_name => @context_name)
|
186
|
+
end
|
187
|
+
|
188
|
+
end # Validate
|
189
|
+
end # DSL
|
190
|
+
end # Ward
|
data/lib/ward/errors.rb
ADDED
@@ -0,0 +1,213 @@
|
|
1
|
+
module Ward
|
2
|
+
# Holds errors associated with a valid? call.
|
3
|
+
#
|
4
|
+
class Errors
|
5
|
+
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
# Returns a localisation message.
|
11
|
+
#
|
12
|
+
# @param [keys] String
|
13
|
+
# The key of the message to be returned from the current language
|
14
|
+
# file.
|
15
|
+
#
|
16
|
+
# @return [String, nil]
|
17
|
+
# Returns the message or nil if it couldn't be found.
|
18
|
+
#
|
19
|
+
# @example
|
20
|
+
#
|
21
|
+
# Ward::Errors.message('has.eql.positive')
|
22
|
+
# # => '%{context} should have %{expected} %{collection}'
|
23
|
+
#
|
24
|
+
# @example Passing multiple keys as fallbacks.
|
25
|
+
#
|
26
|
+
# Ward::Errors.message(
|
27
|
+
# 'does.not.exist', 'has.eql.negative', 'has.eql.positive')
|
28
|
+
#
|
29
|
+
# # => '%{context} should not have %{expected} %{collection}'
|
30
|
+
#
|
31
|
+
def message(*keys)
|
32
|
+
messages[ keys.detect { |key| messages.has_key?(key) } ]
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns the unformatted error message for a matcher.
|
36
|
+
#
|
37
|
+
# @param [Ward::Matchers::Matcher] matcher
|
38
|
+
# The matcher.
|
39
|
+
# @param [Boolean] negative
|
40
|
+
# Whether to return a negative message, rather than a positive.
|
41
|
+
# @param [nil, Symbol, String] key
|
42
|
+
# If a string is supplied, +error_for+ will assume that the string
|
43
|
+
# should be used as the error. A symbol will be assumed to be a 'key'
|
44
|
+
# from the language file, while nil will result in the validator using
|
45
|
+
# the default error message for the matcher.
|
46
|
+
#
|
47
|
+
# @return [String]
|
48
|
+
#
|
49
|
+
def error_for(matcher, negative, key = nil)
|
50
|
+
return key if key.is_a?(String)
|
51
|
+
|
52
|
+
language_key = if key.nil?
|
53
|
+
"#{matcher.class.error_id}."
|
54
|
+
else
|
55
|
+
"#{matcher.class.error_id}.#{key}."
|
56
|
+
end
|
57
|
+
|
58
|
+
language_key << (negative ? 'negative' : 'positive')
|
59
|
+
|
60
|
+
message(language_key) || '%{context} is invalid'
|
61
|
+
end
|
62
|
+
|
63
|
+
# Receives an array and formats it nicely, assuming that only one value
|
64
|
+
# is expected.
|
65
|
+
#
|
66
|
+
# @example One member
|
67
|
+
# format_exclusive_list([1]) # => '1'
|
68
|
+
#
|
69
|
+
# @example Two members
|
70
|
+
# format_exclusive_list([1, 2]) # => '1 or 2'
|
71
|
+
#
|
72
|
+
# @example Many members
|
73
|
+
# format_exclusive_list([1, 2, 3]) # => '1, 2, or 3'
|
74
|
+
#
|
75
|
+
# @param [Enumerable] list
|
76
|
+
# The list to be formatted.
|
77
|
+
#
|
78
|
+
# @return [String]
|
79
|
+
#
|
80
|
+
def format_exclusive_list(list)
|
81
|
+
format_list(list, message('generic.exclusive_conjunction'))
|
82
|
+
end
|
83
|
+
|
84
|
+
# Receives an array and formats it nicely, assuming that all values are
|
85
|
+
# expected.
|
86
|
+
#
|
87
|
+
# @example One member
|
88
|
+
# format_inclusive_list([1]) # => '1'
|
89
|
+
#
|
90
|
+
# @example Two members
|
91
|
+
# format_inclusive_list([1, 2]) # => '1 and 2'
|
92
|
+
#
|
93
|
+
# @example Many members
|
94
|
+
# format_inclusive_list([1, 2, 3]) # => '1, 2, and 3'
|
95
|
+
#
|
96
|
+
# @param [Enumerable] list
|
97
|
+
# The list to be formatted.
|
98
|
+
#
|
99
|
+
# @return [String]
|
100
|
+
#
|
101
|
+
def format_inclusive_list(list)
|
102
|
+
format_list(list, message('generic.inclusive_conjunction'))
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
# Formats a list.
|
108
|
+
#
|
109
|
+
# @see Ward::Errors.format_exclusive_list
|
110
|
+
# @see Ward::Errors.format_inclusive_list
|
111
|
+
#
|
112
|
+
def format_list(list, conjunction)
|
113
|
+
case list.size
|
114
|
+
when 0 then ''
|
115
|
+
when 1 then list.first.to_s
|
116
|
+
when 2 then "#{list.first.to_s} #{conjunction} #{list.last.to_s}"
|
117
|
+
else
|
118
|
+
as_strings = list.map { |value| value.to_s }
|
119
|
+
as_strings[-1] = "#{conjunction} #{as_strings[-1]}"
|
120
|
+
as_strings.join("#{message('generic.list_seperator')} ")
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Returns the en-US error message hash. TEMPORARY.
|
125
|
+
#
|
126
|
+
# @return [Hash]
|
127
|
+
#
|
128
|
+
def messages
|
129
|
+
@error_messages ||= normalise_messages(YAML.load(
|
130
|
+
File.read(File.expand_path('../../../lang/en.yml', __FILE__)) ))
|
131
|
+
end
|
132
|
+
|
133
|
+
# Transforms a hash of messages to a single hash using dot notation.
|
134
|
+
#
|
135
|
+
def normalise_messages(messages, transformed = {}, key_prefix = '')
|
136
|
+
messages.each do |key, value|
|
137
|
+
item_key = key_prefix.empty? ? key : "#{key_prefix}.#{key}"
|
138
|
+
|
139
|
+
if value.is_a?(Hash)
|
140
|
+
normalise_messages(value, transformed, item_key)
|
141
|
+
else
|
142
|
+
transformed[item_key] = value
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
transformed
|
147
|
+
end
|
148
|
+
|
149
|
+
end # class << self
|
150
|
+
|
151
|
+
# Creates a new Errors instance.
|
152
|
+
#
|
153
|
+
def initialize
|
154
|
+
@errors = {}
|
155
|
+
end
|
156
|
+
|
157
|
+
# Adds an error message to the instance.
|
158
|
+
#
|
159
|
+
# @param [Symbol, Ward::Context, Ward::ContextChain] attribute
|
160
|
+
# The attribute or context for the error.
|
161
|
+
# @param [String] message
|
162
|
+
# The error message to add.
|
163
|
+
#
|
164
|
+
# @return [String]
|
165
|
+
# Returns the error message which was set.
|
166
|
+
#
|
167
|
+
# @todo
|
168
|
+
# Support symbols for i18n.
|
169
|
+
#
|
170
|
+
def add(attribute, message)
|
171
|
+
if attribute.kind_of?(Context) or attribute.kind_of?(ContextChain)
|
172
|
+
attribute = attribute.attribute
|
173
|
+
end
|
174
|
+
|
175
|
+
@errors[attribute] ||= []
|
176
|
+
@errors[attribute] << message
|
177
|
+
message
|
178
|
+
end
|
179
|
+
|
180
|
+
# Returns an array of the errors present on an an attribute.
|
181
|
+
#
|
182
|
+
# @param [Symbol] attribute
|
183
|
+
# The attribute whose errors you wish to retrieve.
|
184
|
+
#
|
185
|
+
# @return [Array]
|
186
|
+
# Returns the error messages for an attribute, or nil if there are none.
|
187
|
+
#
|
188
|
+
def on(attribute)
|
189
|
+
@errors[attribute]
|
190
|
+
end
|
191
|
+
|
192
|
+
# Iterates through each attribute and the errors.
|
193
|
+
#
|
194
|
+
# @yieldparam [Symbol] attribute
|
195
|
+
# The attribute name.
|
196
|
+
# @yieldparam [Array, nil] messages
|
197
|
+
# An array with each error message for the attribute, or nil if the
|
198
|
+
# attribute has no errors.
|
199
|
+
#
|
200
|
+
def each(&block)
|
201
|
+
@errors.each(&block)
|
202
|
+
end
|
203
|
+
|
204
|
+
# Returns if there are no errors contained.
|
205
|
+
#
|
206
|
+
# @return [Boolean]
|
207
|
+
#
|
208
|
+
def empty?
|
209
|
+
@errors.empty?
|
210
|
+
end
|
211
|
+
|
212
|
+
end # Errors
|
213
|
+
end # Ward
|