hanami-validations 1.3.6 → 2.0.0.alpha1
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 +15 -5
- data/README.md +259 -568
- data/hanami-validations.gemspec +17 -17
- data/lib/hanami/validations.rb +37 -311
- data/lib/hanami/validations/form.rb +27 -19
- data/lib/hanami/validations/version.rb +3 -2
- data/lib/hanami/validator.rb +9 -0
- metadata +11 -54
- data/lib/hanami-validations.rb +0 -1
- data/lib/hanami/validations/inline_predicate.rb +0 -46
- data/lib/hanami/validations/namespace.rb +0 -65
- data/lib/hanami/validations/predicates.rb +0 -45
data/hanami-validations.gemspec
CHANGED
@@ -1,28 +1,28 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path("lib", __dir__)
|
2
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
-
require
|
5
|
+
require "hanami/validations/version"
|
4
6
|
|
5
7
|
Gem::Specification.new do |spec|
|
6
|
-
spec.name =
|
8
|
+
spec.name = "hanami-validations"
|
7
9
|
spec.version = Hanami::Validations::VERSION
|
8
|
-
spec.authors = [
|
9
|
-
spec.email = [
|
10
|
-
spec.summary =
|
11
|
-
spec.description =
|
12
|
-
spec.homepage =
|
13
|
-
spec.license =
|
10
|
+
spec.authors = ["Luca Guidi"]
|
11
|
+
spec.email = ["me@lucaguidi.com"]
|
12
|
+
spec.summary = "Validations mixin for Ruby objects"
|
13
|
+
spec.description = "Validations mixin for Ruby objects and support for Hanami"
|
14
|
+
spec.homepage = "http://hanamirb.org"
|
15
|
+
spec.license = "MIT"
|
14
16
|
|
15
17
|
spec.files = `git ls-files -- lib/* LICENSE.md README.md CHANGELOG.md hanami-validations.gemspec`.split($INPUT_RECORD_SEPARATOR)
|
16
18
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
19
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
-
spec.require_paths = [
|
19
|
-
spec.required_ruby_version =
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
spec.required_ruby_version = ">= 2.4.0"
|
20
22
|
|
21
|
-
spec.add_dependency
|
22
|
-
spec.add_dependency 'dry-validation', '~> 0.11', '< 0.12'
|
23
|
-
spec.add_dependency 'dry-logic', '~> 0.4.2', '< 0.5'
|
23
|
+
spec.add_dependency "dry-validation", "~> 1.0"
|
24
24
|
|
25
|
-
spec.add_development_dependency
|
26
|
-
spec.add_development_dependency
|
27
|
-
spec.add_development_dependency
|
25
|
+
spec.add_development_dependency "bundler", ">= 1.6", "< 3"
|
26
|
+
spec.add_development_dependency "rake", "~> 12"
|
27
|
+
spec.add_development_dependency "rspec", "~> 3.7"
|
28
28
|
end
|
data/lib/hanami/validations.rb
CHANGED
@@ -1,63 +1,34 @@
|
|
1
|
-
|
2
|
-
require 'hanami/utils/class_attribute'
|
3
|
-
require 'hanami/validations/namespace'
|
4
|
-
require 'hanami/validations/predicates'
|
5
|
-
require 'hanami/validations/inline_predicate'
|
6
|
-
require 'set'
|
1
|
+
# frozen_string_literal: true
|
7
2
|
|
8
|
-
|
9
|
-
|
10
|
-
#
|
11
|
-
# This is probably a false positive.
|
12
|
-
# See: https://github.com/bbatsov/rubocop/issues/5314
|
13
|
-
config.lookup_paths = config.lookup_paths + %w[
|
14
|
-
%<root>s.%<rule>s.%<predicate>s
|
15
|
-
].freeze
|
16
|
-
# rubocop:enable Lint/NestedPercentLiteral
|
17
|
-
end
|
3
|
+
require "dry/validation"
|
4
|
+
require "delegate"
|
18
5
|
|
19
|
-
# @since 0.1.0
|
20
6
|
module Hanami
|
21
|
-
# Hanami::Validations is a set of lightweight validations for Ruby objects.
|
22
|
-
#
|
23
7
|
# @since 0.1.0
|
24
|
-
#
|
25
|
-
# @example
|
26
|
-
# require 'hanami/validations'
|
27
|
-
#
|
28
|
-
# class Signup
|
29
|
-
# include Hanami::Validations
|
30
|
-
#
|
31
|
-
# validations do
|
32
|
-
# # ...
|
33
|
-
# end
|
34
|
-
# end
|
35
8
|
module Validations
|
36
|
-
|
37
|
-
# @api private
|
38
|
-
DEFAULT_MESSAGES_ENGINE = :yaml
|
9
|
+
class Error < StandardError; end
|
39
10
|
|
40
|
-
|
41
|
-
|
42
|
-
# @param base [Class] the target action
|
43
|
-
#
|
44
|
-
# @since 0.1.0
|
45
|
-
# @api private
|
46
|
-
#
|
47
|
-
# @see http://www.ruby-doc.org/core/Module.html#method-i-included
|
48
|
-
def self.included(base) # rubocop:disable Metrics/MethodLength
|
49
|
-
base.class_eval do
|
50
|
-
extend ClassMethods
|
11
|
+
require "hanami/validations/version"
|
12
|
+
require "hanami/validator"
|
51
13
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
class_attribute :_namespace
|
57
|
-
class_attribute :_predicates_module
|
14
|
+
def self.included(klass)
|
15
|
+
super
|
16
|
+
klass.extend(ClassMethods)
|
17
|
+
end
|
58
18
|
|
59
|
-
|
60
|
-
|
19
|
+
# @since 2.0.0
|
20
|
+
# @api private
|
21
|
+
class Result < SimpleDelegator
|
22
|
+
# @since 2.0.0
|
23
|
+
# @api private
|
24
|
+
def output
|
25
|
+
__getobj__.to_h
|
26
|
+
end
|
27
|
+
|
28
|
+
# @since 2.0.0
|
29
|
+
# @api private
|
30
|
+
def messages
|
31
|
+
__getobj__.errors.to_h
|
61
32
|
end
|
62
33
|
end
|
63
34
|
|
@@ -74,13 +45,13 @@ module Hanami
|
|
74
45
|
# @see https://guides.hanamirb.org/validations/overview
|
75
46
|
#
|
76
47
|
# @example Basic Example
|
77
|
-
# require
|
48
|
+
# require "hanami/validations"
|
78
49
|
#
|
79
50
|
# class Signup
|
80
51
|
# include Hanami::Validations
|
81
52
|
#
|
82
53
|
# validations do
|
83
|
-
# required(:name).filled
|
54
|
+
# required(:name).filled(:string)
|
84
55
|
# end
|
85
56
|
# end
|
86
57
|
#
|
@@ -95,261 +66,14 @@ module Hanami
|
|
95
66
|
# result.success? # => false
|
96
67
|
# result.messages # => {:name=>["must be filled"]}
|
97
68
|
# result.output # => {:name=>""}
|
98
|
-
def validations(&blk)
|
99
|
-
|
100
|
-
|
101
|
-
base = _build(predicates: schema_predicates, &_base_rules)
|
102
|
-
schema = _build(predicates: schema_predicates, rules: base.rules, &blk)
|
103
|
-
schema.configure(&_schema_config)
|
104
|
-
schema.configure(&_schema_predicates)
|
105
|
-
schema.extend(__messages) unless _predicates.empty?
|
106
|
-
|
107
|
-
self.schema = schema.new
|
108
|
-
end
|
109
|
-
|
110
|
-
# Define an inline predicate
|
111
|
-
#
|
112
|
-
# @param name [Symbol] inline predicate name
|
113
|
-
# @param message [String] optional error message
|
114
|
-
# @param blk [Proc] predicate implementation
|
115
|
-
#
|
116
|
-
# @return nil
|
117
|
-
#
|
118
|
-
# @since 0.6.0
|
119
|
-
#
|
120
|
-
# @example Without Custom Message
|
121
|
-
# require 'hanami/validations'
|
122
|
-
#
|
123
|
-
# class Signup
|
124
|
-
# include Hanami::Validations
|
125
|
-
#
|
126
|
-
# predicate :foo? do |actual|
|
127
|
-
# actual == 'foo'
|
128
|
-
# end
|
129
|
-
#
|
130
|
-
# validations do
|
131
|
-
# required(:name).filled(:foo?)
|
132
|
-
# end
|
133
|
-
# end
|
134
|
-
#
|
135
|
-
# result = Signup.new(name: nil).call
|
136
|
-
# result.messages # => { :name => ['is invalid'] }
|
137
|
-
#
|
138
|
-
# @example With Custom Message
|
139
|
-
# require 'hanami/validations'
|
140
|
-
#
|
141
|
-
# class Signup
|
142
|
-
# include Hanami::Validations
|
143
|
-
#
|
144
|
-
# predicate :foo?, message: 'must be foo' do |actual|
|
145
|
-
# actual == 'foo'
|
146
|
-
# end
|
147
|
-
#
|
148
|
-
# validations do
|
149
|
-
# required(:name).filled(:foo?)
|
150
|
-
# end
|
151
|
-
# end
|
152
|
-
#
|
153
|
-
# result = Signup.new(name: nil).call
|
154
|
-
# result.messages # => { :name => ['must be foo'] }
|
155
|
-
def predicate(name, message: 'is invalid', &blk)
|
156
|
-
_predicates << InlinePredicate.new(name, message, &blk)
|
69
|
+
def validations(&blk)
|
70
|
+
@_validator = Dry::Validation::Contract.build { schema(&blk) }
|
157
71
|
end
|
158
72
|
|
159
|
-
#
|
160
|
-
#
|
161
|
-
# @param mod [Module] a module with shared predicates
|
162
|
-
#
|
163
|
-
# @since 0.6.0
|
164
|
-
#
|
165
|
-
# @see Hanami::Validations::Predicates
|
166
|
-
#
|
167
|
-
# @example
|
168
|
-
# require 'hanami/validations'
|
169
|
-
#
|
170
|
-
# module MySharedPredicates
|
171
|
-
# include Hanami::Validations::Predicates
|
172
|
-
#
|
173
|
-
# predicate :foo? fo |actual|
|
174
|
-
# actual == 'foo'
|
175
|
-
# end
|
176
|
-
# end
|
177
|
-
#
|
178
|
-
# class MyValidator
|
179
|
-
# include Hanami::Validations
|
180
|
-
# predicates MySharedPredicates
|
181
|
-
#
|
182
|
-
# validations do
|
183
|
-
# required(:name).filled(:foo?)
|
184
|
-
# end
|
185
|
-
# end
|
186
|
-
def predicates(mod)
|
187
|
-
self._predicates_module = mod
|
188
|
-
end
|
189
|
-
|
190
|
-
# Define the type of engine for error messages.
|
191
|
-
#
|
192
|
-
# Accepted values are `:yaml` (default), `:i18n`.
|
193
|
-
#
|
194
|
-
# @param type [Symbol] the preferred engine
|
195
|
-
#
|
196
|
-
# @since 0.6.0
|
197
|
-
#
|
198
|
-
# @example
|
199
|
-
# require 'hanami/validations'
|
200
|
-
#
|
201
|
-
# class Signup
|
202
|
-
# include Hanami::Validations
|
203
|
-
#
|
204
|
-
# messages :i18n
|
205
|
-
# end
|
206
|
-
def messages(type)
|
207
|
-
self._messages = type
|
208
|
-
end
|
209
|
-
|
210
|
-
# Define the path where to find translation file
|
211
|
-
#
|
212
|
-
# @param path [String] path to translation file
|
213
|
-
#
|
214
|
-
# @since 0.6.0
|
215
|
-
#
|
216
|
-
# @example
|
217
|
-
# require 'hanami/validations'
|
218
|
-
#
|
219
|
-
# class Signup
|
220
|
-
# include Hanami::Validations
|
221
|
-
#
|
222
|
-
# messages_path 'config/messages.yml'
|
223
|
-
# end
|
224
|
-
def messages_path(path)
|
225
|
-
self._messages_path = path
|
226
|
-
end
|
227
|
-
|
228
|
-
# Namespace for error messages.
|
229
|
-
#
|
230
|
-
# @param name [String] namespace
|
231
|
-
#
|
232
|
-
# @since 0.6.0
|
233
|
-
#
|
234
|
-
# @example
|
235
|
-
# require 'hanami/validations'
|
236
|
-
#
|
237
|
-
# module MyApp
|
238
|
-
# module Validators
|
239
|
-
# class Signup
|
240
|
-
# include Hanami::Validations
|
241
|
-
#
|
242
|
-
# namespace 'signup'
|
243
|
-
# end
|
244
|
-
# end
|
245
|
-
# end
|
246
|
-
#
|
247
|
-
# # Instead of looking for error messages under the `my_app.validator.signup`
|
248
|
-
# # namespace, it will look just for `signup`.
|
249
|
-
# #
|
250
|
-
# # This helps to simplify YAML files where are stored error messages
|
251
|
-
def namespace(name = nil)
|
252
|
-
if name.nil?
|
253
|
-
Namespace.new(_namespace, self)
|
254
|
-
else
|
255
|
-
self._namespace = name.to_s
|
256
|
-
end
|
257
|
-
end
|
258
|
-
|
259
|
-
private
|
260
|
-
|
261
|
-
# @since 0.6.0
|
73
|
+
# @since 2.0.0
|
262
74
|
# @api private
|
263
|
-
def
|
264
|
-
|
265
|
-
Dry::Validation.__send__(_schema_type, options, &blk)
|
266
|
-
end
|
267
|
-
|
268
|
-
# @since 0.6.0
|
269
|
-
# @api private
|
270
|
-
def _schema_type
|
271
|
-
:Schema
|
272
|
-
end
|
273
|
-
|
274
|
-
# @since 0.6.0
|
275
|
-
# @api private
|
276
|
-
def _base_rules
|
277
|
-
lambda do
|
278
|
-
end
|
279
|
-
end
|
280
|
-
|
281
|
-
# @since 0.6.0
|
282
|
-
# @api private
|
283
|
-
def _schema_config
|
284
|
-
lambda do |config|
|
285
|
-
config.messages = _messages unless _messages.nil?
|
286
|
-
config.messages_file = _messages_path unless _messages_path.nil?
|
287
|
-
config.namespace = namespace
|
288
|
-
|
289
|
-
require "dry/validation/messages/i18n" if config.messages == :i18n
|
290
|
-
end
|
291
|
-
end
|
292
|
-
|
293
|
-
# @since 0.6.0
|
294
|
-
# @api private
|
295
|
-
def _schema_predicates # rubocop:disable Metrics/CyclomaticComplexity
|
296
|
-
return if _predicates_module.nil? && _predicates.empty?
|
297
|
-
|
298
|
-
lambda do |config|
|
299
|
-
config.messages = _predicates_module&.messages || @_messages || DEFAULT_MESSAGES_ENGINE
|
300
|
-
config.messages_file = _predicates_module.messages_path unless _predicates_module.nil?
|
301
|
-
|
302
|
-
require "dry/validation/messages/i18n" if config.messages == :i18n
|
303
|
-
end
|
304
|
-
end
|
305
|
-
|
306
|
-
# @since 0.6.0
|
307
|
-
# @api private
|
308
|
-
def __predicates
|
309
|
-
mod = Module.new { include Hanami::Validations::Predicates }
|
310
|
-
|
311
|
-
_predicates.each do |p|
|
312
|
-
mod.module_eval do
|
313
|
-
predicate(p.name, &p.to_proc)
|
314
|
-
end
|
315
|
-
end
|
316
|
-
|
317
|
-
mod
|
318
|
-
end
|
319
|
-
|
320
|
-
# @since 0.6.0
|
321
|
-
# @api private
|
322
|
-
def __messages # rubocop:disable Metrics/MethodLength
|
323
|
-
result = _predicates.each_with_object({}) do |p, ret|
|
324
|
-
ret[p.name] = p.message
|
325
|
-
end
|
326
|
-
|
327
|
-
# @api private
|
328
|
-
Module.new do
|
329
|
-
@@__messages = result # rubocop:disable Style/ClassVars
|
330
|
-
|
331
|
-
# @api private
|
332
|
-
def self.extended(base)
|
333
|
-
base.instance_eval do
|
334
|
-
def __messages
|
335
|
-
Hash[en: { errors: @@__messages }]
|
336
|
-
end
|
337
|
-
end
|
338
|
-
end
|
339
|
-
|
340
|
-
# @api private
|
341
|
-
def messages
|
342
|
-
engine = super
|
343
|
-
|
344
|
-
messages = if engine.respond_to?(:merge)
|
345
|
-
engine
|
346
|
-
else
|
347
|
-
engine.messages
|
348
|
-
end
|
349
|
-
|
350
|
-
config.messages == :i18n ? messages : messages.merge(__messages)
|
351
|
-
end
|
352
|
-
end
|
75
|
+
def _validator
|
76
|
+
@_validator
|
353
77
|
end
|
354
78
|
end
|
355
79
|
|
@@ -358,17 +82,19 @@ module Hanami
|
|
358
82
|
# @param input [#to_h] a set of input data
|
359
83
|
#
|
360
84
|
# @since 0.6.0
|
361
|
-
def initialize(input
|
362
|
-
@input = input
|
85
|
+
def initialize(input)
|
86
|
+
@input = input
|
363
87
|
end
|
364
88
|
|
365
89
|
# Validates the object.
|
366
90
|
#
|
367
|
-
# @return [
|
91
|
+
# @return [Hanami::Validations::Result]
|
368
92
|
#
|
369
93
|
# @since 0.2.4
|
370
94
|
def validate
|
371
|
-
|
95
|
+
Result.new(
|
96
|
+
self.class._validator.call(@input)
|
97
|
+
)
|
372
98
|
end
|
373
99
|
|
374
100
|
# Returns a Hash with the defined attributes as symbolized keys, and their
|
@@ -378,7 +104,7 @@ module Hanami
|
|
378
104
|
#
|
379
105
|
# @since 0.1.0
|
380
106
|
def to_h
|
381
|
-
validate.
|
107
|
+
validate.to_h
|
382
108
|
end
|
383
109
|
end
|
384
110
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Hanami
|
4
4
|
module Validations
|
@@ -10,57 +10,65 @@ module Hanami
|
|
10
10
|
# @since 0.6.0
|
11
11
|
#
|
12
12
|
# @example
|
13
|
-
# require
|
13
|
+
# require "hanami/validations/form"
|
14
14
|
#
|
15
15
|
# class Signup
|
16
16
|
# include Hanami::Validations::Form
|
17
17
|
#
|
18
18
|
# validations do
|
19
|
-
# required(:name).filled(:
|
20
|
-
# optional(:location).filled(:
|
19
|
+
# required(:name).filled(:string)
|
20
|
+
# optional(:location).filled(:string)
|
21
21
|
# end
|
22
22
|
# end
|
23
23
|
#
|
24
|
-
# result = Signup.new(
|
24
|
+
# result = Signup.new("location" => "Rome").validate
|
25
25
|
# result.success? # => false
|
26
26
|
#
|
27
|
-
# result = Signup.new(
|
27
|
+
# result = Signup.new("name" => "Luca").validate
|
28
28
|
# result.success? # => true
|
29
29
|
#
|
30
30
|
# # it works with symbol keys too
|
31
|
-
# result = Signup.new(location:
|
31
|
+
# result = Signup.new(location: "Rome").validate
|
32
32
|
# result.success? # => false
|
33
33
|
#
|
34
|
-
# result = Signup.new(name:
|
34
|
+
# result = Signup.new(name: "Luca").validate
|
35
35
|
# result.success? # => true
|
36
36
|
#
|
37
|
-
# result = Signup.new(name:
|
37
|
+
# result = Signup.new(name: "Luca", location: "Rome").validate
|
38
38
|
# result.success? # => true
|
39
39
|
module Form
|
40
|
+
# @since 2.0.0
|
41
|
+
# @api private
|
42
|
+
class BaseValidator < Dry::Validation::Contract
|
43
|
+
params do
|
44
|
+
optional(:_csrf_token).filled(:string)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
40
48
|
# Override Ruby's hook for modules.
|
41
49
|
#
|
42
|
-
# @param
|
50
|
+
# @param klass [Class] the target action
|
43
51
|
#
|
44
52
|
# @since 0.6.0
|
45
53
|
# @api private
|
46
54
|
#
|
47
55
|
# @see http://www.ruby-doc.org/core/Module.html#method-i-included
|
48
|
-
def self.included(
|
49
|
-
|
50
|
-
|
51
|
-
|
56
|
+
def self.included(klass)
|
57
|
+
super
|
58
|
+
|
59
|
+
klass.class_eval do
|
60
|
+
include ::Hanami::Validations
|
61
|
+
extend ClassMethods
|
52
62
|
end
|
53
63
|
end
|
54
64
|
|
55
65
|
# @since 0.6.0
|
56
66
|
# @api private
|
57
67
|
module ClassMethods
|
58
|
-
|
59
|
-
|
60
|
-
# @since 0.6.0
|
68
|
+
# @since 2.0.0
|
61
69
|
# @api private
|
62
|
-
def
|
63
|
-
|
70
|
+
def validations(&blk)
|
71
|
+
@_validator = Class.new(BaseValidator) { params(&blk) }.new
|
64
72
|
end
|
65
73
|
end
|
66
74
|
end
|