hanami-validations 0.0.0 → 0.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.
- 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
|