hanami-validations 0.0.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +89 -0
- data/LICENSE.md +22 -0
- data/README.md +559 -7
- data/hanami-validations.gemspec +16 -12
- data/lib/hanami-validations.rb +1 -0
- data/lib/hanami/validations.rb +300 -2
- data/lib/hanami/validations/attribute.rb +252 -0
- data/lib/hanami/validations/attribute_definer.rb +526 -0
- data/lib/hanami/validations/blank_value_checker.rb +55 -0
- data/lib/hanami/validations/coercions.rb +31 -0
- data/lib/hanami/validations/error.rb +95 -0
- data/lib/hanami/validations/errors.rb +155 -0
- data/lib/hanami/validations/nested_attributes.rb +22 -0
- data/lib/hanami/validations/validation_set.rb +81 -0
- data/lib/hanami/validations/validator.rb +29 -0
- data/lib/hanami/validations/version.rb +2 -1
- metadata +57 -16
- data/.gitignore +0 -9
- data/Gemfile +0 -4
- data/Rakefile +0 -2
- data/bin/console +0 -14
- data/bin/setup +0 -8
data/hanami-validations.gemspec
CHANGED
@@ -4,20 +4,24 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'hanami/validations/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
7
|
+
spec.name = 'hanami-validations'
|
8
8
|
spec.version = Hanami::Validations::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
9
|
+
spec.authors = ['Luca Guidi', 'Trung Lê', 'Alfonso Uceda']
|
10
|
+
spec.email = ['me@lucaguidi.com', 'trung.le@ruby-journal.com', 'uceda73@gmail.com']
|
11
|
+
spec.summary = %q{Validations mixin for Ruby objects}
|
12
|
+
spec.description = %q{Validations mixin for Ruby objects and support for Hanami}
|
13
|
+
spec.homepage = 'http://hanamirb.org'
|
14
|
+
spec.license = 'MIT'
|
11
15
|
|
12
|
-
spec.
|
13
|
-
spec.
|
14
|
-
spec.
|
16
|
+
spec.files = `git ls-files -- lib/* LICENSE.md README.md CHANGELOG.md hanami-validations.gemspec`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
spec.required_ruby_version = '>= 2.0.0'
|
15
21
|
|
16
|
-
spec.
|
17
|
-
spec.bindir = "exe"
|
18
|
-
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
|
-
spec.require_paths = ["lib"]
|
22
|
+
spec.add_dependency 'hanami-utils', '~> 0.7'
|
20
23
|
|
21
|
-
spec.add_development_dependency
|
22
|
-
spec.add_development_dependency
|
24
|
+
spec.add_development_dependency 'bundler', '~> 1.6'
|
25
|
+
spec.add_development_dependency 'minitest', '~> 5'
|
26
|
+
spec.add_development_dependency 'rake', '~> 10'
|
23
27
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'hanami/validations'
|
data/lib/hanami/validations.rb
CHANGED
@@ -1,7 +1,305 @@
|
|
1
|
-
require
|
1
|
+
require 'hanami/utils/hash'
|
2
|
+
require 'hanami/validations/version'
|
3
|
+
require 'hanami/validations/blank_value_checker'
|
4
|
+
require 'hanami/validations/attribute_definer'
|
5
|
+
require 'hanami/validations/validation_set'
|
6
|
+
require 'hanami/validations/validator'
|
7
|
+
require 'hanami/validations/attribute'
|
8
|
+
require 'hanami/validations/errors'
|
2
9
|
|
3
10
|
module Hanami
|
11
|
+
# Hanami::Validations is a set of lightweight validations for Ruby objects.
|
12
|
+
#
|
13
|
+
# @since 0.1.0
|
4
14
|
module Validations
|
5
|
-
#
|
15
|
+
# Override Ruby's hook for modules.
|
16
|
+
#
|
17
|
+
# @param base [Class] the target action
|
18
|
+
#
|
19
|
+
# @since 0.1.0
|
20
|
+
# @api private
|
21
|
+
#
|
22
|
+
# @see http://www.ruby-doc.org/core/Module.html#method-i-included
|
23
|
+
def self.included(base)
|
24
|
+
base.class_eval do
|
25
|
+
extend ClassMethods
|
26
|
+
include AttributeDefiner
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Validations DSL
|
31
|
+
#
|
32
|
+
# @since 0.1.0
|
33
|
+
module ClassMethods
|
34
|
+
# Override Ruby's hook for class inheritance. When a class includes
|
35
|
+
# Hanami::Validations and it is subclassed, this passes
|
36
|
+
# the attributes from the superclass to the subclass.
|
37
|
+
#
|
38
|
+
# @param base [Class] the target action
|
39
|
+
#
|
40
|
+
# @since 0.2.2
|
41
|
+
# @api private
|
42
|
+
#
|
43
|
+
# @see http://www.ruby-doc.org/core/Class.html#method-i-inherited
|
44
|
+
def inherited(base)
|
45
|
+
transfer_validations_to_base(base)
|
46
|
+
super
|
47
|
+
end
|
48
|
+
|
49
|
+
# Override Ruby's hook for modules. When a module includes
|
50
|
+
# Hanami::Validations and it is included in a class or module, this passes
|
51
|
+
# the validations from the module to the base.
|
52
|
+
#
|
53
|
+
# @param base [Class] the target action
|
54
|
+
#
|
55
|
+
# @since 0.1.0
|
56
|
+
# @api private
|
57
|
+
#
|
58
|
+
# @see http://www.ruby-doc.org/core/Module.html#method-i-included
|
59
|
+
#
|
60
|
+
# @example
|
61
|
+
# require 'hanami/validations'
|
62
|
+
#
|
63
|
+
# module NameValidations
|
64
|
+
# include Hanami::Validations
|
65
|
+
#
|
66
|
+
# attribute :name, presence: true
|
67
|
+
# end
|
68
|
+
#
|
69
|
+
# class Signup
|
70
|
+
# include NameValidations
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
# signup = Signup.new(name: '')
|
74
|
+
# signup.valid? # => false
|
75
|
+
#
|
76
|
+
# signup = Signup.new(name: 'Luca')
|
77
|
+
# signup.valid? # => true
|
78
|
+
def included(base)
|
79
|
+
base.class_eval do
|
80
|
+
include Hanami::Validations
|
81
|
+
end
|
82
|
+
|
83
|
+
super
|
84
|
+
|
85
|
+
transfer_validations_to_base(base)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Define a validation for an existing attribute
|
89
|
+
#
|
90
|
+
# @param name [#to_sym] the name of the attribute
|
91
|
+
# @param options [Hash] set of validations
|
92
|
+
#
|
93
|
+
# @see Hanami::Validations::ClassMethods#validations
|
94
|
+
#
|
95
|
+
# @example Presence
|
96
|
+
# require 'hanami/validations'
|
97
|
+
#
|
98
|
+
# class Signup
|
99
|
+
# include Hanami::Validations
|
100
|
+
#
|
101
|
+
# def initialize(attributes = {})
|
102
|
+
# @name = attributes.fetch(:name)
|
103
|
+
# end
|
104
|
+
#
|
105
|
+
# attr_accessor :name
|
106
|
+
#
|
107
|
+
# validates :name, presence: true
|
108
|
+
# end
|
109
|
+
#
|
110
|
+
# signup = Signup.new(name: 'Luca')
|
111
|
+
# signup.valid? # => true
|
112
|
+
#
|
113
|
+
# signup = Signup.new(name: nil)
|
114
|
+
# signup.valid? # => false
|
115
|
+
def validates(name, options)
|
116
|
+
validations.add(name, options)
|
117
|
+
end
|
118
|
+
|
119
|
+
# Set of user defined validations
|
120
|
+
#
|
121
|
+
# @return [Hash]
|
122
|
+
#
|
123
|
+
# @since 0.2.2
|
124
|
+
# @api private
|
125
|
+
def validations
|
126
|
+
@validations ||= ValidationSet.new
|
127
|
+
end
|
128
|
+
|
129
|
+
# Set of user defined attributes
|
130
|
+
#
|
131
|
+
# @return [Array<String>]
|
132
|
+
#
|
133
|
+
# @since 0.2.3
|
134
|
+
# @api private
|
135
|
+
def defined_attributes
|
136
|
+
validations.names.map(&:to_s)
|
137
|
+
end
|
138
|
+
|
139
|
+
private
|
140
|
+
|
141
|
+
# Transfers attributes to a base class
|
142
|
+
#
|
143
|
+
# @param base [Module] the base class to transfer attributes to
|
144
|
+
#
|
145
|
+
# @since 0.2.2
|
146
|
+
# @api private
|
147
|
+
def transfer_validations_to_base(base)
|
148
|
+
validations.each do |attribute, options|
|
149
|
+
base.validates attribute, options
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# Validation errors
|
155
|
+
#
|
156
|
+
# @return [Hanami::Validations::Errors] the set of validation errors
|
157
|
+
#
|
158
|
+
# @since 0.1.0
|
159
|
+
#
|
160
|
+
# @see Hanami::Validations::Errors
|
161
|
+
#
|
162
|
+
# @example Valid attributes
|
163
|
+
# require 'hanami/validations'
|
164
|
+
#
|
165
|
+
# class Signup
|
166
|
+
# include Hanami::Validations
|
167
|
+
#
|
168
|
+
# attribute :email, presence: true, format: /\A(.*)@(.*)\.(.*)\z/
|
169
|
+
# end
|
170
|
+
#
|
171
|
+
# signup = Signup.new(email: 'user@example.org')
|
172
|
+
# signup.valid? # => true
|
173
|
+
#
|
174
|
+
# signup.errors
|
175
|
+
# # => #<Hanami::Validations::Errors:0x007fd594ba9228 @errors={}>
|
176
|
+
#
|
177
|
+
# @example Invalid attributes
|
178
|
+
# require 'hanami/validations'
|
179
|
+
#
|
180
|
+
# class Signup
|
181
|
+
# include Hanami::Validations
|
182
|
+
#
|
183
|
+
# attribute :email, presence: true, format: /\A(.*)@(.*)\.(.*)\z/
|
184
|
+
# attribute :age, size: 18..99
|
185
|
+
# end
|
186
|
+
#
|
187
|
+
# signup = Signup.new(email: '', age: 17)
|
188
|
+
# signup.valid? # => false
|
189
|
+
#
|
190
|
+
# signup.errors
|
191
|
+
# # => #<Hanami::Validations::Errors:0x007fe00ced9b78
|
192
|
+
# # @errors={
|
193
|
+
# # :email=>[
|
194
|
+
# # #<Hanami::Validations::Error:0x007fe00cee3290 @attribute=:email, @validation=:presence, @expected=true, @actual="">,
|
195
|
+
# # #<Hanami::Validations::Error:0x007fe00cee31f0 @attribute=:email, @validation=:format, @expected=/\A(.*)@(.*)\.(.*)\z/, @actual="">
|
196
|
+
# # ],
|
197
|
+
# # :age=>[
|
198
|
+
# # #<Hanami::Validations::Error:0x007fe00cee30d8 @attribute=:age, @validation=:size, @expected=18..99, @actual=17>
|
199
|
+
# # ]
|
200
|
+
# # }>
|
201
|
+
#
|
202
|
+
# @example Invalid attributes
|
203
|
+
# require 'hanami/validations'
|
204
|
+
#
|
205
|
+
# class Post
|
206
|
+
# include Hanami::Validations
|
207
|
+
#
|
208
|
+
# attribute :title, presence: true
|
209
|
+
# end
|
210
|
+
#
|
211
|
+
# post = Post.new
|
212
|
+
# post.invalid? # => true
|
213
|
+
#
|
214
|
+
# post.errors
|
215
|
+
# # => #<Hanami::Validations::Errors:0x2931522b
|
216
|
+
# # @errors={
|
217
|
+
# # :title=>[
|
218
|
+
# # #<Hanami::Validations::Error:0x662706a7 @actual=nil, @attribute_name="title", @validation=:presence, @expected=true, @namespace=nil, @attribute="title">
|
219
|
+
# # ]
|
220
|
+
# # }>
|
221
|
+
def errors
|
222
|
+
@errors ||= Errors.new
|
223
|
+
end
|
224
|
+
|
225
|
+
# Checks if the current data satisfies the defined validations
|
226
|
+
#
|
227
|
+
# @return [TrueClass,FalseClass] the result of the validations
|
228
|
+
#
|
229
|
+
# @since 0.1.0
|
230
|
+
def valid?
|
231
|
+
validate
|
232
|
+
|
233
|
+
errors.empty?
|
234
|
+
end
|
235
|
+
|
236
|
+
# Checks if the current data doesn't satisfies the defined validations
|
237
|
+
#
|
238
|
+
# @return [TrueClass,FalseClass] the result of the validations
|
239
|
+
#
|
240
|
+
# @since 0.3.2
|
241
|
+
def invalid?
|
242
|
+
!valid?
|
243
|
+
end
|
244
|
+
|
245
|
+
# Validates the object.
|
246
|
+
#
|
247
|
+
# @return [Errors]
|
248
|
+
#
|
249
|
+
# @since 0.2.4
|
250
|
+
# @api private
|
251
|
+
#
|
252
|
+
# @see Hanami::Attribute#nested
|
253
|
+
def validate
|
254
|
+
validator = Validator.new(defined_validations, read_attributes, errors)
|
255
|
+
validator.validate
|
256
|
+
end
|
257
|
+
|
258
|
+
# Iterates thru the defined attributes and their values
|
259
|
+
#
|
260
|
+
# @param blk [Proc] a block
|
261
|
+
# @yieldparam attribute [Symbol] the name of the attribute
|
262
|
+
# @yieldparam value [Object,nil] the value of the attribute
|
263
|
+
#
|
264
|
+
# @since 0.2.0
|
265
|
+
def each(&blk)
|
266
|
+
to_h.each(&blk)
|
267
|
+
end
|
268
|
+
|
269
|
+
# Returns a Hash with the defined attributes as symbolized keys, and their
|
270
|
+
# relative values.
|
271
|
+
#
|
272
|
+
# @return [Hash]
|
273
|
+
#
|
274
|
+
# @since 0.1.0
|
275
|
+
def to_h
|
276
|
+
# TODO remove this symbolization when we'll support Ruby 2.2+ only
|
277
|
+
Utils::Hash.new(
|
278
|
+
@attributes
|
279
|
+
).deep_dup.symbolize!.to_h
|
280
|
+
end
|
281
|
+
|
282
|
+
private
|
283
|
+
# The set of user defined validations.
|
284
|
+
#
|
285
|
+
# @since 0.2.2
|
286
|
+
# @api private
|
287
|
+
#
|
288
|
+
# @see Hanami::Validations::ClassMethods#validations
|
289
|
+
def defined_validations
|
290
|
+
self.class.__send__(:validations)
|
291
|
+
end
|
292
|
+
|
293
|
+
# Builds a Hash of current attribute values.
|
294
|
+
#
|
295
|
+
# @since 0.2.2
|
296
|
+
# @api private
|
297
|
+
def read_attributes
|
298
|
+
{}.tap do |attributes|
|
299
|
+
defined_validations.each_key do |attribute|
|
300
|
+
attributes[attribute] = public_send(attribute)
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
6
304
|
end
|
7
305
|
end
|
@@ -0,0 +1,252 @@
|
|
1
|
+
require 'hanami/validations/coercions'
|
2
|
+
|
3
|
+
# Quick fix for non MRI VMs that don't implement Range#size
|
4
|
+
#
|
5
|
+
# @since 0.1.0
|
6
|
+
class Range
|
7
|
+
def size
|
8
|
+
to_a.size
|
9
|
+
end unless instance_methods.include?(:size)
|
10
|
+
end
|
11
|
+
|
12
|
+
module Hanami
|
13
|
+
module Validations
|
14
|
+
# A validable attribute
|
15
|
+
#
|
16
|
+
# @since 0.1.0
|
17
|
+
# @api private
|
18
|
+
class Attribute
|
19
|
+
# Attribute naming convention for "confirmation" validation
|
20
|
+
#
|
21
|
+
# @see Hanami::Validations::Attribute#confirmation
|
22
|
+
#
|
23
|
+
# @since 0.2.0
|
24
|
+
# @api private
|
25
|
+
CONFIRMATION_TEMPLATE = '%{name}_confirmation'.freeze
|
26
|
+
|
27
|
+
# Instantiate an attribute
|
28
|
+
#
|
29
|
+
# @param attributes [Hash] a set of attributes and values coming from the
|
30
|
+
# input
|
31
|
+
# @param name [Symbol] the name of the attribute
|
32
|
+
# @param value [Object,nil] the value coming from the input
|
33
|
+
# @param validations [Hash] a set of validation rules
|
34
|
+
#
|
35
|
+
# @since 0.2.0
|
36
|
+
# @api private
|
37
|
+
def initialize(attributes, name, value, validations, errors)
|
38
|
+
@attributes = attributes
|
39
|
+
@name = name
|
40
|
+
@value = value
|
41
|
+
@validations = validations
|
42
|
+
@errors = errors
|
43
|
+
end
|
44
|
+
|
45
|
+
# @api private
|
46
|
+
# @since 0.2.0
|
47
|
+
def validate
|
48
|
+
presence
|
49
|
+
acceptance
|
50
|
+
|
51
|
+
return if skip?
|
52
|
+
|
53
|
+
format
|
54
|
+
inclusion
|
55
|
+
exclusion
|
56
|
+
size
|
57
|
+
confirmation
|
58
|
+
nested
|
59
|
+
|
60
|
+
@errors
|
61
|
+
end
|
62
|
+
|
63
|
+
# @api private
|
64
|
+
# @since 0.2.0
|
65
|
+
attr_reader :value
|
66
|
+
|
67
|
+
private
|
68
|
+
# Validates presence of the value.
|
69
|
+
# This fails with `nil` and "blank" values.
|
70
|
+
#
|
71
|
+
# An object is blank if it isn't `nil`, but doesn't hold a value.
|
72
|
+
# Empty strings and enumerables are an example.
|
73
|
+
#
|
74
|
+
# @see Hanami::Validations::ClassMethods#attribute
|
75
|
+
# @see Hanami::Validations::Attribute#nil_value?
|
76
|
+
#
|
77
|
+
# @since 0.2.0
|
78
|
+
# @api private
|
79
|
+
def presence
|
80
|
+
_validate(__method__) { !blank_value? }
|
81
|
+
end
|
82
|
+
|
83
|
+
# Validates acceptance of the value.
|
84
|
+
#
|
85
|
+
# This passes if the value is "truthy", it fails if not.
|
86
|
+
#
|
87
|
+
# Truthy examples: `Object.new`, `1`, `"1"`, `true`.
|
88
|
+
# Falsy examples: `nil`, `0`, `"0"`, `false`, `""`.
|
89
|
+
#
|
90
|
+
# @see Hanami::Validations::ClassMethods#attribute
|
91
|
+
# @see http://www.rubydoc.info/gems/hanami-utils/Hanami/Utils/Kernel#Boolean-class_method
|
92
|
+
#
|
93
|
+
# @since 0.2.0
|
94
|
+
# @api private
|
95
|
+
def acceptance
|
96
|
+
_validate(__method__) do
|
97
|
+
!blank_value? && Hanami::Utils::Kernel.Boolean(@value)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Validates format of the value.
|
102
|
+
#
|
103
|
+
# Coerces the value to a string and then check if it satisfies the defined
|
104
|
+
# matcher.
|
105
|
+
#
|
106
|
+
# @see Hanami::Validations::ClassMethods#attribute
|
107
|
+
#
|
108
|
+
# @since 0.2.0
|
109
|
+
# @api private
|
110
|
+
def format
|
111
|
+
_validate(__method__) {|matcher| @value.to_s.match(matcher) } unless blank_value?
|
112
|
+
end
|
113
|
+
|
114
|
+
# Validates inclusion of the value in the defined collection.
|
115
|
+
#
|
116
|
+
# The collection is an objects which implements `#include?`.
|
117
|
+
#
|
118
|
+
# @see Hanami::Validations::ClassMethods#attribute
|
119
|
+
#
|
120
|
+
# @since 0.2.0
|
121
|
+
# @api private
|
122
|
+
def inclusion
|
123
|
+
_validate(__method__) {|collection| collection.include?(value) }
|
124
|
+
end
|
125
|
+
|
126
|
+
# Validates exclusion of the value in the defined collection.
|
127
|
+
#
|
128
|
+
# The collection is an objects which implements `#include?`.
|
129
|
+
#
|
130
|
+
# @see Hanami::Validations::ClassMethods#attribute
|
131
|
+
#
|
132
|
+
# @since 0.2.0
|
133
|
+
# @api private
|
134
|
+
def exclusion
|
135
|
+
_validate(__method__) {|collection| !collection.include?(value) }
|
136
|
+
end
|
137
|
+
|
138
|
+
# Validates confirmation of the value with another corresponding value.
|
139
|
+
#
|
140
|
+
# Given a `:password` attribute, it passes if the corresponding attribute
|
141
|
+
# `:password_confirmation` has the same value.
|
142
|
+
#
|
143
|
+
# @see Hanami::Validations::ClassMethods#attribute
|
144
|
+
# @see Hanami::Validations::Attribute::CONFIRMATION_TEMPLATE
|
145
|
+
#
|
146
|
+
# @since 0.2.0
|
147
|
+
# @api private
|
148
|
+
def confirmation
|
149
|
+
_validate(__method__) do
|
150
|
+
_attribute == _attribute(CONFIRMATION_TEMPLATE % { name: @name })
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# Validates if value's size matches the defined quantity.
|
155
|
+
#
|
156
|
+
# The quantity can be a Ruby Numeric:
|
157
|
+
#
|
158
|
+
# * `Integer`
|
159
|
+
# * `Fixnum`
|
160
|
+
# * `Float`
|
161
|
+
# * `BigNum`
|
162
|
+
# * `BigDecimal`
|
163
|
+
# * `Complex`
|
164
|
+
# * `Rational`
|
165
|
+
# * Octal literals
|
166
|
+
# * Hex literals
|
167
|
+
# * `#to_int`
|
168
|
+
#
|
169
|
+
# The quantity can be also any object which implements `#include?`.
|
170
|
+
#
|
171
|
+
# If the quantity is a Numeric, the size of the value MUST be exactly the
|
172
|
+
# same.
|
173
|
+
#
|
174
|
+
# If the quantity is a Range, the size of the value MUST be included.
|
175
|
+
#
|
176
|
+
# If the attribute's value is blank, the size will not be considered
|
177
|
+
#
|
178
|
+
# The value is an object which implements `#size`.
|
179
|
+
#
|
180
|
+
# @raise [ArgumentError] if the defined quantity isn't a Numeric or a
|
181
|
+
# collection
|
182
|
+
#
|
183
|
+
# @see Hanami::Validations::ClassMethods#attribute
|
184
|
+
#
|
185
|
+
# @since 0.2.0
|
186
|
+
# @api private
|
187
|
+
def size
|
188
|
+
return if blank_value?
|
189
|
+
|
190
|
+
_validate(__method__) do |validator|
|
191
|
+
case validator
|
192
|
+
when Numeric, ->(v) { v.respond_to?(:to_int) }
|
193
|
+
value.size == validator.to_int
|
194
|
+
when Range
|
195
|
+
validator.include?(value.size)
|
196
|
+
else
|
197
|
+
raise ArgumentError.new("Size validator must be a number or a range, it was: #{ validator }")
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
# Validates nested Hanami Validations objects
|
203
|
+
#
|
204
|
+
# @since 0.2.4
|
205
|
+
# @api private
|
206
|
+
def nested
|
207
|
+
_validate(__method__) do |validator|
|
208
|
+
errors = value.validate
|
209
|
+
errors.each do |error|
|
210
|
+
new_error = Error.new(error.attribute, error.validation, error.expected, error.actual, @name)
|
211
|
+
@errors.add new_error.attribute, new_error
|
212
|
+
end
|
213
|
+
true
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
# @since 0.1.0
|
218
|
+
# @api private
|
219
|
+
def skip?
|
220
|
+
@value.nil?
|
221
|
+
end
|
222
|
+
|
223
|
+
# Checks if the value is "blank".
|
224
|
+
#
|
225
|
+
# @see Hanami::Validations::BlankValueChecker
|
226
|
+
#
|
227
|
+
# @since 0.2.0
|
228
|
+
# @api private
|
229
|
+
def blank_value?
|
230
|
+
BlankValueChecker.new(@value).blank_value?
|
231
|
+
end
|
232
|
+
|
233
|
+
# Reads an attribute from the validator.
|
234
|
+
#
|
235
|
+
# @since 0.2.0
|
236
|
+
# @api private
|
237
|
+
def _attribute(name = @name)
|
238
|
+
@attributes[name.to_sym]
|
239
|
+
end
|
240
|
+
|
241
|
+
# Run a single validation and collects the results.
|
242
|
+
#
|
243
|
+
# @since 0.2.0
|
244
|
+
# @api private
|
245
|
+
def _validate(validation)
|
246
|
+
if (validator = @validations[validation]) && !(yield validator)
|
247
|
+
@errors.add(@name, Error.new(@name, validation, @validations.fetch(validation), @value))
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|