hanami-validations 2.0.0.rc1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +15 -422
- data/hanami-validations.gemspec +1 -0
- data/lib/hanami/validations/form.rb +1 -37
- data/lib/hanami/validations/version.rb +4 -1
- data/lib/hanami/validations.rb +27 -28
- data/lib/hanami/validator.rb +1 -0
- data/lib/hanami-validations.rb +3 -0
- metadata +20 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4a0d6367d5ae641a10c53578dba401c026a6e7b125210312a18a8f9f1430093e
|
4
|
+
data.tar.gz: 22891e60e1235640d73b896acda9961764255bf219f76cc3107f884ea4fc27b7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f1aa7f299bed7884008b0325870fc40143e838e6af4b498bd6979e676198cd8166e04d987cb31ac619d39a14851ab04b825e192865bed4c571cd01243197e9f7
|
7
|
+
data.tar.gz: 3854da5422fe1c48aa839fa3cefa58b7f93c7ea5115d8ad15601e82561245cb10de27fc8810fe416b56e43d9c0c11ae2c26506b56bf625d40c74e82fa7b52226
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Hanami::Validations
|
2
2
|
|
3
|
-
|
3
|
+
Internal support gem for `Hanami::Action` params validation.
|
4
4
|
|
5
5
|
## Status
|
6
6
|
|
@@ -51,437 +51,30 @@ $ gem install hanami-validations
|
|
51
51
|
|
52
52
|
## Usage
|
53
53
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
### Overview
|
58
|
-
|
59
|
-
The main object provided by this gem is `Hanami::Validator`.
|
60
|
-
It providers a powerful DSL to define a validation contract, which is made of a schema and optional rules.
|
61
|
-
|
62
|
-
A validation **schema** is a set of steps that filters, coerces, and checks the validity of incoming data.
|
63
|
-
Validation **rules** are a set of directives, to check if business rules are respected.
|
64
|
-
|
65
|
-
Only when the input is formally valid (according to the **schema**), validation **rules** are checked.
|
66
|
-
|
67
|
-
```ruby
|
68
|
-
# frozen_string_literal: true
|
69
|
-
|
70
|
-
require "hanami/validations"
|
71
|
-
|
72
|
-
class SignupValidator < Hanami::Validator
|
73
|
-
schema do
|
74
|
-
required(:email).value(:string)
|
75
|
-
required(:age).value(:integer)
|
76
|
-
end
|
77
|
-
|
78
|
-
rule(:age) do
|
79
|
-
key.failure("must be greater than 18") if value < 18
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
validator = SignupValidator.new
|
84
|
-
|
85
|
-
result = validator.call(email: "user@hanamirb.test", age: 37)
|
86
|
-
result.success? # => true
|
87
|
-
|
88
|
-
result = validator.call(email: "user@hanamirb.test", age: "foo")
|
89
|
-
result.success? # => false
|
90
|
-
result.errors.to_h # => {:age=>["must be an integer"]}
|
91
|
-
|
92
|
-
result = validator.call(email: "user@hanamirb.test", age: 17)
|
93
|
-
puts result.success? # => false
|
94
|
-
puts result.errors.to_h # => {:age=>["must be greater than 18"]}
|
95
|
-
```
|
96
|
-
|
97
|
-
### Schemas
|
98
|
-
|
99
|
-
A basic schema doesn't apply data coercion, input must already have the right Ruby types.
|
100
|
-
|
101
|
-
```ruby
|
102
|
-
# frozen_string_literal: true
|
103
|
-
|
104
|
-
require "hanami/validations"
|
105
|
-
|
106
|
-
class SignupValidator < Hanami::Validator
|
107
|
-
schema do
|
108
|
-
required(:email).value(:string)
|
109
|
-
required(:age).value(:integer)
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
validator = SignupValidator.new
|
114
|
-
|
115
|
-
result = validator.call(email: "user@hanamirb.test", age: 37)
|
116
|
-
puts result.success? # => true
|
117
|
-
|
118
|
-
result = validator.call(email: "user@hanamirb.test", age: "37")
|
119
|
-
puts result.success? # => false
|
120
|
-
puts result.errors.to_h # => {:age=>["must be an integer"]}
|
121
|
-
```
|
122
|
-
|
123
|
-
### Params
|
124
|
-
|
125
|
-
When used in _params mode_, a schema applies data coercion, before to run validation checks.
|
126
|
-
|
127
|
-
This is designed for Web form/HTTP params.
|
128
|
-
|
129
|
-
```ruby
|
130
|
-
# frozen_string_literal: true
|
131
|
-
|
132
|
-
require "bundler/setup"
|
133
|
-
require "hanami/validations"
|
134
|
-
|
135
|
-
class SignupValidator < Hanami::Validator
|
136
|
-
params do
|
137
|
-
required(:email).value(:string)
|
138
|
-
required(:age).value(:integer)
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
validator = SignupValidator.new
|
143
|
-
|
144
|
-
result = validator.call(email: "user@hanamirb.test", age: "37")
|
145
|
-
puts result.success? # => true
|
146
|
-
puts result.to_h # => {:email=>"user@hanamirb.test", :age=>37}
|
147
|
-
```
|
148
|
-
|
149
|
-
### JSON
|
150
|
-
|
151
|
-
When used in _JSON mode_, data coercions are still applied, but they follow different policies.
|
152
|
-
For instance, because JSON supports integers, strings won't be coerced into integers.
|
153
|
-
|
154
|
-
```ruby
|
155
|
-
# frozen_string_literal: true
|
156
|
-
|
157
|
-
require "hanami/validations"
|
158
|
-
|
159
|
-
class SignupValidator < Hanami::Validator
|
160
|
-
json do
|
161
|
-
required(:email).value(:string)
|
162
|
-
required(:age).value(:integer)
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
validator = SignupValidator.new
|
167
|
-
|
168
|
-
result = validator.call(email: "user@hanamirb.test", age: 37)
|
169
|
-
puts result.success? # => true
|
170
|
-
puts result.to_h # => {:email=>"user@hanamirb.test", :age=>37}
|
171
|
-
|
172
|
-
result = validator.call(email: "user@hanamirb.test", age: "37")
|
173
|
-
puts result.success? # => false
|
174
|
-
```
|
175
|
-
|
176
|
-
### Whitelisting
|
177
|
-
|
178
|
-
Unknown keys from incoming data are filtered out:
|
179
|
-
|
180
|
-
```ruby
|
181
|
-
# frozen_string_literal: true
|
182
|
-
|
183
|
-
require "hanami/validations"
|
184
|
-
|
185
|
-
class SignupValidator < Hanami::Validator
|
186
|
-
schema do
|
187
|
-
required(:email).value(:string)
|
188
|
-
end
|
189
|
-
end
|
190
|
-
|
191
|
-
validator = SignupValidator.new
|
192
|
-
|
193
|
-
result = validator.call(email: "user@hanamirb.test", foo: "bar")
|
194
|
-
puts result.success? # => true
|
195
|
-
puts result.to_h # => {:email=>"user@hanamirb.test"}
|
196
|
-
```
|
197
|
-
|
198
|
-
### Custom Types
|
199
|
-
|
200
|
-
```ruby
|
201
|
-
# frozen_string_literal: true
|
202
|
-
|
203
|
-
require "hanami/validations"
|
204
|
-
|
205
|
-
module Types
|
206
|
-
include Dry::Types()
|
207
|
-
|
208
|
-
StrippedString = Types::String.constructor(&:strip)
|
209
|
-
end
|
210
|
-
|
211
|
-
class SignupValidator < Hanami::Validator
|
212
|
-
params do
|
213
|
-
required(:email).value(Types::StrippedString)
|
214
|
-
required(:age).value(:integer)
|
215
|
-
end
|
216
|
-
end
|
217
|
-
|
218
|
-
validator = SignupValidator.new
|
219
|
-
|
220
|
-
result = validator.call(email: " user@hanamirb.test ", age: "37")
|
221
|
-
puts result.success? # => true
|
222
|
-
puts result.to_h # => {:email=>"user@hanamirb.test", :age=>37}
|
223
|
-
```
|
224
|
-
|
225
|
-
### Rules
|
226
|
-
|
227
|
-
Rules are performing a set of domain-specific validation checks.
|
228
|
-
Rules are executed only after the validations from the schema are satisfied.
|
229
|
-
|
230
|
-
```ruby
|
231
|
-
# frozen_string_literal: true
|
232
|
-
|
233
|
-
require "hanami/validations"
|
234
|
-
|
235
|
-
class EventValidator < Hanami::Validator
|
236
|
-
params do
|
237
|
-
required(:start_date).value(:date)
|
238
|
-
end
|
239
|
-
|
240
|
-
rule(:start_date) do
|
241
|
-
key.failure("must be in the future") if value <= Date.today
|
242
|
-
end
|
243
|
-
end
|
244
|
-
|
245
|
-
validator = EventValidator.new
|
246
|
-
|
247
|
-
result = validator.call(start_date: "foo")
|
248
|
-
puts result.success? # => false
|
249
|
-
puts result.errors.to_h # => {:start_date=>["must be a date"]}
|
250
|
-
|
251
|
-
result = validator.call(start_date: Date.today)
|
252
|
-
puts result.success? # => false
|
253
|
-
puts result.errors.to_h # => {:start_date=>["must be in the future"]}
|
254
|
-
|
255
|
-
result = validator.call(start_date: Date.today + 1)
|
256
|
-
puts result.success? # => true
|
257
|
-
puts result.to_h # => {:start_date=>#<Date: 2019-07-03 ((2458668j,0s,0n),+0s,2299161j)>}
|
258
|
-
```
|
259
|
-
|
260
|
-
Learn more about rules: https://dry-rb.org/gems/dry-validation/master/rules/
|
261
|
-
|
262
|
-
### Inheritance
|
263
|
-
|
264
|
-
Schema and rules validations can be inherited and used by subclasses
|
54
|
+
Installing hanami-validations enables support for `params` validation in
|
55
|
+
[hanami-controller][controller]’s `Hanami::Action` classes.
|
265
56
|
|
266
57
|
```ruby
|
267
|
-
|
268
|
-
|
269
|
-
require "hanami/validations"
|
270
|
-
|
271
|
-
class ApplicationValidator < Hanami::Validator
|
58
|
+
class Signup < Hanami::Action
|
272
59
|
params do
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
class SignupValidator < ApplicationValidator
|
278
|
-
params do
|
279
|
-
required(:user).hash do
|
280
|
-
required(:email).filled(:string)
|
281
|
-
end
|
282
|
-
end
|
283
|
-
end
|
284
|
-
|
285
|
-
validator = SignupValidator.new
|
286
|
-
|
287
|
-
result = validator.call(user: { email: "user@hanamirb.test" }, _csrf_token: "abc123")
|
288
|
-
puts result.success? # => true
|
289
|
-
puts result.to_h # => {:user=>{:email=>"user@hanamirb.test"}, :_csrf_token=>"abc123"}
|
290
|
-
```
|
291
|
-
|
292
|
-
### Messages
|
293
|
-
|
294
|
-
Failure messages can be hardcoded or refer to a message template system.
|
295
|
-
`hanami-validations` supports natively a default YAML based message template system, or alternatively, `i18n` gem.
|
296
|
-
|
297
|
-
We have already seen rule failures set with hardcoded messages, here's an example of how to use keys to refer to interpolated messages.
|
298
|
-
|
299
|
-
```ruby
|
300
|
-
# frozen_string_literal: true
|
301
|
-
|
302
|
-
require "hanami/validations"
|
303
|
-
|
304
|
-
class ApplicationValidator < Hanami::Validator
|
305
|
-
config.messages.top_namespace = "bookshelf"
|
306
|
-
config.messages.load_paths << "config/errors.yml"
|
307
|
-
end
|
308
|
-
```
|
309
|
-
|
310
|
-
In the `ApplicationValidator` there is defined the application namespace (`"bookshelf"`), which is the root of the messages file.
|
311
|
-
Below that top name, there is the key `errors`. Everything that is nested here is accessible by the validations.
|
312
|
-
|
313
|
-
There are two ways to organize messages:
|
314
|
-
|
315
|
-
1. Right below `errors`. This is for **general purposes** error messages (e.g. `bookshelf` => `errors` => `taken`)
|
316
|
-
2. Below `errors` => `rules` => name of the attribute => custom key (e.g. `bookshelf` => `errors` => `age` => `invalid`). This is for **specific** messages that affect only a specific attribute.
|
317
|
-
|
318
|
-
Our **suggestion** is to start with **specific** messages and see if there is a need to generalize them.
|
319
|
-
|
320
|
-
```yaml
|
321
|
-
# config/errors.yml
|
322
|
-
en:
|
323
|
-
bookshelf:
|
324
|
-
errors:
|
325
|
-
taken: "oh noes, it's already taken"
|
326
|
-
network: "there is a network error (%{code})"
|
327
|
-
rules:
|
328
|
-
age:
|
329
|
-
invalid: "must be greater than 18"
|
330
|
-
email:
|
331
|
-
invalid: "not a valid email"
|
332
|
-
```
|
333
|
-
|
334
|
-
#### General purpose messages
|
335
|
-
|
336
|
-
```ruby
|
337
|
-
class SignupValidator < ApplicationValidator
|
338
|
-
schema do
|
339
|
-
required(:username).filled(:string)
|
340
|
-
end
|
341
|
-
|
342
|
-
rule(:username) do
|
343
|
-
key.failure(:taken) if values[:username] == "jodosha"
|
344
|
-
end
|
345
|
-
end
|
346
|
-
|
347
|
-
validator = SignupValidator.new
|
348
|
-
|
349
|
-
result = validator.call(username: "foo")
|
350
|
-
puts result.success? # => true
|
351
|
-
|
352
|
-
result = validator.call(username: "jodosha")
|
353
|
-
puts result.success? # => false
|
354
|
-
puts result.errors.to_h # => {:username=>["oh noes, it's already taken"]}
|
355
|
-
```
|
356
|
-
|
357
|
-
#### Specific messages
|
358
|
-
|
359
|
-
Please note that the failure key used it's the same for both the attributes (`:invalid`), but thanks to the nesting, the library is able to lookup the right message.
|
360
|
-
|
361
|
-
```ruby
|
362
|
-
class SignupValidator < ApplicationValidator
|
363
|
-
schema do
|
364
|
-
required(:email).filled(:string)
|
365
|
-
required(:age).filled(:integer)
|
366
|
-
end
|
367
|
-
|
368
|
-
rule(:email) do
|
369
|
-
key.failure(:invalid) unless values[:email] =~ /@/
|
60
|
+
required(:first_name)
|
61
|
+
required(:last_name)
|
62
|
+
required(:email)
|
370
63
|
end
|
371
64
|
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
end
|
376
|
-
|
377
|
-
validator = SignupValidator.new
|
378
|
-
|
379
|
-
result = validator.call(email: "foo", age: 17)
|
380
|
-
puts result.success? # => false
|
381
|
-
puts result.errors.to_h # => {:email=>["not a valid email"], :age=>["must be greater than 18"]}
|
382
|
-
```
|
383
|
-
|
384
|
-
#### Extra information
|
65
|
+
def handle(req, *)
|
66
|
+
puts req.params.class # => Signup::Params
|
67
|
+
puts req.params.class.superclass # => Hanami::Action::Params
|
385
68
|
|
386
|
-
|
387
|
-
|
388
|
-
```ruby
|
389
|
-
class RefundValidator < ApplicationValidator
|
390
|
-
schema do
|
391
|
-
required(:refunded_code).filled(:string)
|
392
|
-
end
|
393
|
-
|
394
|
-
rule(:refunded_code) do
|
395
|
-
key.failure(:network, code: "123") if values[:refunded_code] == "error"
|
69
|
+
puts req.params[:first_name] # => "Luca"
|
70
|
+
puts req.params[:admin] # => nil
|
396
71
|
end
|
397
72
|
end
|
398
|
-
|
399
|
-
validator = RefundValidator.new
|
400
|
-
|
401
|
-
result = validator.call(refunded_code: "error")
|
402
|
-
puts result.success? # => false
|
403
|
-
puts result.errors.to_h # => {:refunded_code=>["there is a network error (123)"]}
|
404
73
|
```
|
405
74
|
|
406
|
-
|
407
|
-
|
408
|
-
### External dependencies
|
409
|
-
|
410
|
-
If the validator needs to plug one or more objects to run the validations, there is a DSL to do so: `:option`.
|
411
|
-
When the validator is instantiated, the declared dependencies must be passed.
|
412
|
-
|
413
|
-
```ruby
|
414
|
-
# frozen_string_literal: true
|
415
|
-
|
416
|
-
require "hanami/validations"
|
417
|
-
|
418
|
-
class AddressValidator
|
419
|
-
def valid?(value)
|
420
|
-
value.match(/Rome/)
|
421
|
-
end
|
422
|
-
end
|
423
|
-
|
424
|
-
class DeliveryValidator < Hanami::Validator
|
425
|
-
option :address_validator
|
426
|
-
|
427
|
-
schema do
|
428
|
-
required(:address).filled(:string)
|
429
|
-
end
|
430
|
-
|
431
|
-
rule(:address) do
|
432
|
-
key.failure("not a valid address") unless address_validator.valid?(values[:address])
|
433
|
-
end
|
434
|
-
end
|
435
|
-
|
436
|
-
validator = DeliveryValidator.new(address_validator: AddressValidator.new)
|
437
|
-
|
438
|
-
result = validator.call(address: "foo")
|
439
|
-
puts result.success? # => false
|
440
|
-
puts result.errors.to_h # => {:address=>["not a valid address"]}
|
441
|
-
```
|
442
|
-
|
443
|
-
Read more about external dependencies: https://dry-rb.org/gems/dry-validation/master/external-dependencies/
|
444
|
-
|
445
|
-
### Mixin
|
446
|
-
|
447
|
-
`hanami-validations` 1.x used to ship a mixin `Hanami::Validations` to be included in classes to provide validation rules.
|
448
|
-
The 2.x series, still ships this mixin, but it will be probably removed in 3.x.
|
449
|
-
|
450
|
-
```ruby
|
451
|
-
# frozen_string_literal: true
|
452
|
-
|
453
|
-
require "hanami/validations"
|
454
|
-
|
455
|
-
class UserValidator
|
456
|
-
include Hanami::Validations
|
457
|
-
|
458
|
-
validations do
|
459
|
-
required(:number).filled(:integer, eql?: 23)
|
460
|
-
end
|
461
|
-
end
|
462
|
-
|
463
|
-
result = UserValidator.new(number: 23).validate
|
464
|
-
|
465
|
-
puts result.success? # => true
|
466
|
-
puts result.to_h # => {:number=>23}
|
467
|
-
puts result.errors.to_h # => {}
|
468
|
-
|
469
|
-
result = UserValidator.new(number: 11).validate
|
470
|
-
|
471
|
-
puts result.success? # => true
|
472
|
-
puts result.to_h # => {:number=>21}
|
473
|
-
puts result.errors.to_h # => {:number=>["must be equal to 23"]}
|
474
|
-
```
|
475
|
-
|
476
|
-
## FAQs
|
477
|
-
### Uniqueness Validation
|
478
|
-
|
479
|
-
Uniqueness validation isn't implemented by `Hanami::Validations` because the context of execution is completely decoupled from persistence.
|
480
|
-
Please remember that **uniqueness validation is a huge race condition between application and the database, and it doesn't guarantee records uniqueness for real.** To effectively enforce this policy you can use SQL database constraints.
|
481
|
-
|
482
|
-
Please read more at: [The Perils of Uniqueness Validations](http://robots.thoughtbot.com/the-perils-of-uniqueness-validations).
|
75
|
+
See [hanami-controller][controller] for more detail on params validation.
|
483
76
|
|
484
|
-
|
77
|
+
[controller]: http://github.com/hanami/controller
|
485
78
|
|
486
79
|
## Contributing
|
487
80
|
|
@@ -493,6 +86,6 @@ If you need to implement it, please use the External dependencies feature (see a
|
|
493
86
|
|
494
87
|
## Copyright
|
495
88
|
|
496
|
-
Copyright © 2014-
|
89
|
+
Copyright © 2014-2022 Hanami Team – Released under MIT License
|
497
90
|
|
498
91
|
This project was formerly known as Lotus (`lotus-validations`).
|
data/hanami-validations.gemspec
CHANGED
@@ -21,6 +21,7 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.required_ruby_version = ">= 3.0"
|
22
22
|
|
23
23
|
spec.add_dependency "dry-validation", ">= 1.10", "< 2"
|
24
|
+
spec.add_dependency "zeitwerk", "~> 2.6.0"
|
24
25
|
|
25
26
|
spec.add_development_dependency "bundler", ">= 1.6", "< 3"
|
26
27
|
spec.add_development_dependency "rake", "~> 13"
|
@@ -4,38 +4,8 @@ module Hanami
|
|
4
4
|
module Validations
|
5
5
|
# Validations mixin for forms/HTTP params.
|
6
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
7
|
# @since 0.6.0
|
11
|
-
#
|
12
|
-
# @example
|
13
|
-
# require "hanami/validations/form"
|
14
|
-
#
|
15
|
-
# class Signup
|
16
|
-
# include Hanami::Validations::Form
|
17
|
-
#
|
18
|
-
# validations do
|
19
|
-
# required(:name).filled(:string)
|
20
|
-
# optional(:location).filled(:string)
|
21
|
-
# end
|
22
|
-
# end
|
23
|
-
#
|
24
|
-
# result = Signup.new("location" => "Rome").validate
|
25
|
-
# result.success? # => false
|
26
|
-
#
|
27
|
-
# result = Signup.new("name" => "Luca").validate
|
28
|
-
# result.success? # => true
|
29
|
-
#
|
30
|
-
# # it works with symbol keys too
|
31
|
-
# result = Signup.new(location: "Rome").validate
|
32
|
-
# result.success? # => false
|
33
|
-
#
|
34
|
-
# result = Signup.new(name: "Luca").validate
|
35
|
-
# result.success? # => true
|
36
|
-
#
|
37
|
-
# result = Signup.new(name: "Luca", location: "Rome").validate
|
38
|
-
# result.success? # => true
|
8
|
+
# @api private
|
39
9
|
module Form
|
40
10
|
# @since 2.0.0
|
41
11
|
# @api private
|
@@ -45,14 +15,8 @@ module Hanami
|
|
45
15
|
end
|
46
16
|
end
|
47
17
|
|
48
|
-
# Override Ruby's hook for modules.
|
49
|
-
#
|
50
|
-
# @param klass [Class] the target action
|
51
|
-
#
|
52
18
|
# @since 0.6.0
|
53
19
|
# @api private
|
54
|
-
#
|
55
|
-
# @see http://www.ruby-doc.org/core/Module.html#method-i-included
|
56
20
|
def self.included(klass)
|
57
21
|
super
|
58
22
|
|
data/lib/hanami/validations.rb
CHANGED
@@ -2,15 +2,34 @@
|
|
2
2
|
|
3
3
|
require "dry/validation"
|
4
4
|
require "delegate"
|
5
|
+
require "zeitwerk"
|
5
6
|
|
7
|
+
# @see Hanami::Validations
|
8
|
+
# @since 0.1.0
|
6
9
|
module Hanami
|
7
10
|
# @since 0.1.0
|
11
|
+
# @api private
|
8
12
|
module Validations
|
9
|
-
|
13
|
+
# @since 2.0.0
|
14
|
+
# @api private
|
15
|
+
def self.gem_loader
|
16
|
+
@gem_loader ||= Zeitwerk::Loader.new.tap do |loader|
|
17
|
+
root = File.expand_path("..", __dir__)
|
18
|
+
loader.tag = "hanami-validations"
|
19
|
+
loader.inflector = Zeitwerk::GemInflector.new("#{root}/hanami-validations.rb")
|
20
|
+
loader.push_dir(root)
|
21
|
+
loader.ignore(
|
22
|
+
"#{root}/hanami-validations.rb",
|
23
|
+
"#{root}/hanami/validations/version.rb"
|
24
|
+
)
|
25
|
+
end
|
26
|
+
end
|
10
27
|
|
11
|
-
|
12
|
-
|
28
|
+
gem_loader.setup
|
29
|
+
require_relative "validations/version"
|
13
30
|
|
31
|
+
# @since 0.1.0
|
32
|
+
# @api private
|
14
33
|
def self.included(klass)
|
15
34
|
super
|
16
35
|
klass.extend(ClassMethods)
|
@@ -35,37 +54,14 @@ module Hanami
|
|
35
54
|
# Validations DSL
|
36
55
|
#
|
37
56
|
# @since 0.1.0
|
57
|
+
# @api private
|
38
58
|
module ClassMethods
|
39
59
|
# Define validation rules from the given block.
|
40
60
|
#
|
41
61
|
# @param blk [Proc] validation rules
|
42
62
|
#
|
43
63
|
# @since 0.6.0
|
44
|
-
#
|
45
|
-
# @see https://guides.hanamirb.org/validations/overview
|
46
|
-
#
|
47
|
-
# @example Basic Example
|
48
|
-
# require "hanami/validations"
|
49
|
-
#
|
50
|
-
# class Signup
|
51
|
-
# include Hanami::Validations
|
52
|
-
#
|
53
|
-
# validations do
|
54
|
-
# required(:name).filled(:string)
|
55
|
-
# end
|
56
|
-
# end
|
57
|
-
#
|
58
|
-
# result = Signup.new(name: "Luca").validate
|
59
|
-
#
|
60
|
-
# result.success? # => true
|
61
|
-
# result.messages # => []
|
62
|
-
# result.output # => {:name=>""}
|
63
|
-
#
|
64
|
-
# result = Signup.new(name: "").validate
|
65
|
-
#
|
66
|
-
# result.success? # => false
|
67
|
-
# result.messages # => {:name=>["must be filled"]}
|
68
|
-
# result.output # => {:name=>""}
|
64
|
+
# @api private
|
69
65
|
def validations(&blk)
|
70
66
|
@_validator = Dry::Validation::Contract.build { schema(&blk) }
|
71
67
|
end
|
@@ -82,6 +78,7 @@ module Hanami
|
|
82
78
|
# @param input [#to_h] a set of input data
|
83
79
|
#
|
84
80
|
# @since 0.6.0
|
81
|
+
# @api private
|
85
82
|
def initialize(input)
|
86
83
|
@input = input
|
87
84
|
end
|
@@ -91,6 +88,7 @@ module Hanami
|
|
91
88
|
# @return [Hanami::Validations::Result]
|
92
89
|
#
|
93
90
|
# @since 0.2.4
|
91
|
+
# @api private
|
94
92
|
def validate
|
95
93
|
Result.new(
|
96
94
|
self.class._validator.call(@input)
|
@@ -103,6 +101,7 @@ module Hanami
|
|
103
101
|
# @return [Hash]
|
104
102
|
#
|
105
103
|
# @since 0.1.0
|
104
|
+
# @api private
|
106
105
|
def to_h
|
107
106
|
validate.to_h
|
108
107
|
end
|
data/lib/hanami/validator.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hanami-validations
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.0
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Luca Guidi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-11-
|
11
|
+
date: 2022-11-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dry-validation
|
@@ -30,6 +30,20 @@ dependencies:
|
|
30
30
|
- - "<"
|
31
31
|
- !ruby/object:Gem::Version
|
32
32
|
version: '2'
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: zeitwerk
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: 2.6.0
|
40
|
+
type: :runtime
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 2.6.0
|
33
47
|
- !ruby/object:Gem::Dependency
|
34
48
|
name: bundler
|
35
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -103,6 +117,7 @@ files:
|
|
103
117
|
- LICENSE.md
|
104
118
|
- README.md
|
105
119
|
- hanami-validations.gemspec
|
120
|
+
- lib/hanami-validations.rb
|
106
121
|
- lib/hanami/validations.rb
|
107
122
|
- lib/hanami/validations/form.rb
|
108
123
|
- lib/hanami/validations/version.rb
|
@@ -123,11 +138,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
123
138
|
version: '3.0'
|
124
139
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
125
140
|
requirements:
|
126
|
-
- - "
|
141
|
+
- - ">="
|
127
142
|
- !ruby/object:Gem::Version
|
128
|
-
version:
|
143
|
+
version: '0'
|
129
144
|
requirements: []
|
130
|
-
rubygems_version: 3.3.
|
145
|
+
rubygems_version: 3.3.7
|
131
146
|
signing_key:
|
132
147
|
specification_version: 4
|
133
148
|
summary: Validations mixin for Ruby objects
|