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.
@@ -1,28 +1,28 @@
1
- lib = File.expand_path('../lib', __FILE__)
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 'hanami/validations/version'
5
+ require "hanami/validations/version"
4
6
 
5
7
  Gem::Specification.new do |spec|
6
- spec.name = 'hanami-validations'
8
+ spec.name = "hanami-validations"
7
9
  spec.version = Hanami::Validations::VERSION
8
- spec.authors = ['Luca Guidi']
9
- spec.email = ['me@lucaguidi.com']
10
- spec.summary = 'Validations mixin for Ruby objects'
11
- spec.description = 'Validations mixin for Ruby objects and support for Hanami'
12
- spec.homepage = 'http://hanamirb.org'
13
- spec.license = 'MIT'
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 = ['lib']
19
- spec.required_ruby_version = '>= 2.3.0'
20
+ spec.require_paths = ["lib"]
21
+ spec.required_ruby_version = ">= 2.4.0"
20
22
 
21
- spec.add_dependency 'hanami-utils', '~> 1.3'
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 'bundler', '>= 1.6', '< 3'
26
- spec.add_development_dependency 'rake', '~> 13'
27
- spec.add_development_dependency 'rspec', '~> 3.7'
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
@@ -1,63 +1,34 @@
1
- require 'dry-validation'
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
- Dry::Validation::Messages::Namespaced.configure do |config|
9
- # rubocop:disable Lint/NestedPercentLiteral
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
- # @since 0.6.0
37
- # @api private
38
- DEFAULT_MESSAGES_ENGINE = :yaml
9
+ class Error < StandardError; end
39
10
 
40
- # Override Ruby's hook for modules.
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
- include Utils::ClassAttribute
53
- class_attribute :schema
54
- class_attribute :_messages
55
- class_attribute :_messages_path
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
- class_attribute :_predicates
60
- self._predicates = Set.new
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 'hanami/validations'
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) # rubocop:disable Metrics/AbcSize
99
- schema_predicates = _predicates_module || __predicates
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
- # Assign a set of shared predicates wrapped in a module
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 _build(options = {}, &blk)
264
- options = { build: false }.merge(options)
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.to_h
85
+ def initialize(input)
86
+ @input = input
363
87
  end
364
88
 
365
89
  # Validates the object.
366
90
  #
367
- # @return [Dry::Validations::Result]
91
+ # @return [Hanami::Validations::Result]
368
92
  #
369
93
  # @since 0.2.4
370
94
  def validate
371
- self.class.schema.call(@input)
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.output
107
+ validate.to_h
382
108
  end
383
109
  end
384
110
  end
@@ -1,4 +1,4 @@
1
- require 'hanami/validations'
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 'hanami/validations/form'
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(:str?)
20
- # optional(:location).filled(:str?)
19
+ # required(:name).filled(:string)
20
+ # optional(:location).filled(:string)
21
21
  # end
22
22
  # end
23
23
  #
24
- # result = Signup.new('location' => 'Rome').validate
24
+ # result = Signup.new("location" => "Rome").validate
25
25
  # result.success? # => false
26
26
  #
27
- # result = Signup.new('name' => 'Luca').validate
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: 'Rome').validate
31
+ # result = Signup.new(location: "Rome").validate
32
32
  # result.success? # => false
33
33
  #
34
- # result = Signup.new(name: 'Luca').validate
34
+ # result = Signup.new(name: "Luca").validate
35
35
  # result.success? # => true
36
36
  #
37
- # result = Signup.new(name: 'Luca', location: 'Rome').validate
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 base [Class] the target action
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(base)
49
- base.class_eval do
50
- include Validations
51
- extend ClassMethods
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
- private
59
-
60
- # @since 0.6.0
68
+ # @since 2.0.0
61
69
  # @api private
62
- def _schema_type
63
- :Form
70
+ def validations(&blk)
71
+ @_validator = Class.new(BaseValidator) { params(&blk) }.new
64
72
  end
65
73
  end
66
74
  end