hanami-validations 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -1,6 +1,6 @@
1
1
  module Hanami
2
2
  module Validations
3
3
  # @since 0.1.0
4
- VERSION = '0.5.0'.freeze
4
+ VERSION = '0.6.0'.freeze
5
5
  end
6
6
  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.5.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-01-22 00:00:00.000000000 Z
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.7'
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.7'
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/attribute.rb
87
- - lib/hanami/validations/attribute_definer.rb
88
- - lib/hanami/validations/blank_value_checker.rb
89
- - lib/hanami/validations/coercions.rb
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.0.0
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.5.1
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