hanami-validations 1.3.6 → 2.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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