hanami-validations 1.3.9 → 2.0.0.alpha1

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