hanami-validations 0.5.0 → 0.6.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 +22 -0
- data/README.md +537 -326
- data/hanami-validations.gemspec +6 -5
- data/lib/hanami-validations.rb +1 -1
- data/lib/hanami/validations.rb +259 -208
- data/lib/hanami/validations/form.rb +47 -0
- data/lib/hanami/validations/inline_predicate.rb +46 -0
- data/lib/hanami/validations/namespace.rb +65 -0
- data/lib/hanami/validations/predicates.rb +45 -0
- data/lib/hanami/validations/version.rb +1 -1
- metadata +24 -16
- data/lib/hanami/validations/attribute.rb +0 -252
- data/lib/hanami/validations/attribute_definer.rb +0 -526
- data/lib/hanami/validations/blank_value_checker.rb +0 -55
- data/lib/hanami/validations/coercions.rb +0 -31
- data/lib/hanami/validations/error.rb +0 -95
- data/lib/hanami/validations/errors.rb +0 -155
- data/lib/hanami/validations/nested_attributes.rb +0 -22
- data/lib/hanami/validations/validation_set.rb +0 -81
- data/lib/hanami/validations/validator.rb +0 -29
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'hanami/validations'
|
2
|
+
|
3
|
+
module Hanami
|
4
|
+
module Validations
|
5
|
+
# Validations mixin for forms/HTTP params.
|
6
|
+
#
|
7
|
+
# This must be used when the input comes from a browser or an HTTP endpoint.
|
8
|
+
# It knows how to deal with common data types, and common edge cases like blank strings.
|
9
|
+
#
|
10
|
+
# @since 0.6.0
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# require 'hanami/validations/form'
|
14
|
+
#
|
15
|
+
# class Signup
|
16
|
+
# include Hanami::Validations::Form
|
17
|
+
# end
|
18
|
+
module Form
|
19
|
+
# Override Ruby's hook for modules.
|
20
|
+
#
|
21
|
+
# @param base [Class] the target action
|
22
|
+
#
|
23
|
+
# @since 0.6.0
|
24
|
+
# @api private
|
25
|
+
#
|
26
|
+
# @see http://www.ruby-doc.org/core/Module.html#method-i-included
|
27
|
+
def self.included(base)
|
28
|
+
base.class_eval do
|
29
|
+
include Validations
|
30
|
+
extend ClassMethods
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# @since 0.6.0
|
35
|
+
# @api private
|
36
|
+
module ClassMethods
|
37
|
+
private
|
38
|
+
|
39
|
+
# @since 0.6.0
|
40
|
+
# @api private
|
41
|
+
def _schema_type
|
42
|
+
:Form
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Hanami
|
2
|
+
module Validations
|
3
|
+
# Inline predicate
|
4
|
+
#
|
5
|
+
# @since 0.6.0
|
6
|
+
# @api private
|
7
|
+
class InlinePredicate
|
8
|
+
# @since 0.6.0
|
9
|
+
# @api private
|
10
|
+
attr_reader :name
|
11
|
+
|
12
|
+
# @since 0.6.0
|
13
|
+
# @api private
|
14
|
+
attr_reader :message
|
15
|
+
|
16
|
+
# Return a new instance.
|
17
|
+
#
|
18
|
+
# @param name [Symbol] inline predicate name
|
19
|
+
# @param message [String] optional failure message
|
20
|
+
# @param blk [Proc] predicate implementation
|
21
|
+
#
|
22
|
+
# @return [Hanami::Validations::InlinePredicate] a new instance
|
23
|
+
#
|
24
|
+
# @since 0.6.0
|
25
|
+
# @api private
|
26
|
+
def initialize(name, message, &blk)
|
27
|
+
@name = name
|
28
|
+
@message = message
|
29
|
+
@blk = blk
|
30
|
+
end
|
31
|
+
|
32
|
+
# @since 0.6.0
|
33
|
+
# @api private
|
34
|
+
def to_proc
|
35
|
+
@blk
|
36
|
+
end
|
37
|
+
|
38
|
+
# @since 0.6.0
|
39
|
+
# @api private
|
40
|
+
def ==(other)
|
41
|
+
self.class == other.class &&
|
42
|
+
name == other.name
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'hanami/utils/string'
|
2
|
+
|
3
|
+
module Hanami
|
4
|
+
module Validations
|
5
|
+
# Validations message namespace.
|
6
|
+
#
|
7
|
+
# For a given `FooValidator` class, it will look for I18n messages within
|
8
|
+
# the `foo` group.
|
9
|
+
#
|
10
|
+
# @since 0.6.0
|
11
|
+
# @api private
|
12
|
+
class Namespace
|
13
|
+
# @since 0.6.0
|
14
|
+
# @api private
|
15
|
+
SUFFIX = 'Validator'.freeze
|
16
|
+
|
17
|
+
# @since 0.6.0
|
18
|
+
# @api private
|
19
|
+
SUFFIX_REPLACEMENT = ''.freeze
|
20
|
+
|
21
|
+
# @since 0.6.0
|
22
|
+
# @api private
|
23
|
+
RUBY_NAMESPACE_SEPARATOR = '/'.freeze
|
24
|
+
|
25
|
+
# @since 0.6.0
|
26
|
+
# @api private
|
27
|
+
RUBY_NAMESPACE_REPLACEMENT = '.'.freeze
|
28
|
+
|
29
|
+
# @since 0.6.0
|
30
|
+
# @api private
|
31
|
+
def self.new(name, klass)
|
32
|
+
result = name || klass.name
|
33
|
+
return nil if result.nil?
|
34
|
+
|
35
|
+
super(result)
|
36
|
+
end
|
37
|
+
|
38
|
+
# @since 0.6.0
|
39
|
+
# @api private
|
40
|
+
def initialize(name)
|
41
|
+
@name = name
|
42
|
+
end
|
43
|
+
|
44
|
+
# @since 0.6.0
|
45
|
+
# @api private
|
46
|
+
def to_s
|
47
|
+
underscored_name.gsub(RUBY_NAMESPACE_SEPARATOR, RUBY_NAMESPACE_REPLACEMENT)
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
# @since 0.6.0
|
53
|
+
# @api private
|
54
|
+
def underscored_name
|
55
|
+
Utils::String.new(name_without_suffix).underscore
|
56
|
+
end
|
57
|
+
|
58
|
+
# @since 0.6.0
|
59
|
+
# @api private
|
60
|
+
def name_without_suffix
|
61
|
+
@name.sub(SUFFIX, SUFFIX_REPLACEMENT)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'dry/logic/predicates'
|
2
|
+
require 'hanami/utils/class_attribute'
|
3
|
+
|
4
|
+
module Hanami
|
5
|
+
module Validations
|
6
|
+
# Mixin to include when defining shared predicates
|
7
|
+
#
|
8
|
+
# @since 0.6.0
|
9
|
+
#
|
10
|
+
# @see Hanami::Validations::ClassMethods#predicates
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# require 'hanami/validations'
|
14
|
+
#
|
15
|
+
# module MySharedPredicates
|
16
|
+
# include Hanami::Validations::Predicates
|
17
|
+
#
|
18
|
+
# predicate :foo? fo |actual|
|
19
|
+
# actual == 'foo'
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# class MyValidator
|
24
|
+
# include Hanami::Validations
|
25
|
+
# predicates MySharedPredicates
|
26
|
+
#
|
27
|
+
# validations do
|
28
|
+
# required(:name).filled(:foo?)
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
module Predicates
|
32
|
+
# @since 0.6.0
|
33
|
+
# @api private
|
34
|
+
def self.included(base)
|
35
|
+
base.class_eval do
|
36
|
+
include Dry::Logic::Predicates
|
37
|
+
include Utils::ClassAttribute
|
38
|
+
|
39
|
+
class_attribute :messages
|
40
|
+
class_attribute :messages_path
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hanami-validations
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Luca Guidi
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2016-
|
13
|
+
date: 2016-07-22 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: hanami-utils
|
@@ -18,14 +18,28 @@ dependencies:
|
|
18
18
|
requirements:
|
19
19
|
- - "~>"
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version: '0.
|
21
|
+
version: '0.8'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
25
|
requirements:
|
26
26
|
- - "~>"
|
27
27
|
- !ruby/object:Gem::Version
|
28
|
-
version: '0.
|
28
|
+
version: '0.8'
|
29
|
+
- !ruby/object:Gem::Dependency
|
30
|
+
name: dry-validation
|
31
|
+
requirement: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - "~>"
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '0.9'
|
36
|
+
type: :runtime
|
37
|
+
prerelease: false
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - "~>"
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0.9'
|
29
43
|
- !ruby/object:Gem::Dependency
|
30
44
|
name: bundler
|
31
45
|
requirement: !ruby/object:Gem::Requirement
|
@@ -83,15 +97,10 @@ files:
|
|
83
97
|
- hanami-validations.gemspec
|
84
98
|
- lib/hanami-validations.rb
|
85
99
|
- lib/hanami/validations.rb
|
86
|
-
- lib/hanami/validations/
|
87
|
-
- lib/hanami/validations/
|
88
|
-
- lib/hanami/validations/
|
89
|
-
- lib/hanami/validations/
|
90
|
-
- lib/hanami/validations/error.rb
|
91
|
-
- lib/hanami/validations/errors.rb
|
92
|
-
- lib/hanami/validations/nested_attributes.rb
|
93
|
-
- lib/hanami/validations/validation_set.rb
|
94
|
-
- lib/hanami/validations/validator.rb
|
100
|
+
- lib/hanami/validations/form.rb
|
101
|
+
- lib/hanami/validations/inline_predicate.rb
|
102
|
+
- lib/hanami/validations/namespace.rb
|
103
|
+
- lib/hanami/validations/predicates.rb
|
95
104
|
- lib/hanami/validations/version.rb
|
96
105
|
homepage: http://hanamirb.org
|
97
106
|
licenses:
|
@@ -105,7 +114,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
105
114
|
requirements:
|
106
115
|
- - ">="
|
107
116
|
- !ruby/object:Gem::Version
|
108
|
-
version: 2.
|
117
|
+
version: 2.2.0
|
109
118
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
110
119
|
requirements:
|
111
120
|
- - ">="
|
@@ -113,9 +122,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
113
122
|
version: '0'
|
114
123
|
requirements: []
|
115
124
|
rubyforge_project:
|
116
|
-
rubygems_version: 2.
|
125
|
+
rubygems_version: 2.6.4
|
117
126
|
signing_key:
|
118
127
|
specification_version: 4
|
119
128
|
summary: Validations mixin for Ruby objects
|
120
129
|
test_files: []
|
121
|
-
has_rdoc:
|
@@ -1,252 +0,0 @@
|
|
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
|