dry-validation 0.13.3 → 1.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 -25
- data/LICENSE +1 -1
- data/README.md +9 -4
- data/lib/dry-validation.rb +2 -0
- data/lib/dry/validation.rb +8 -35
- data/lib/dry/validation/constants.rb +12 -0
- data/lib/dry/validation/contract.rb +132 -0
- data/lib/dry/validation/contract/class_interface.rb +114 -0
- data/lib/dry/validation/evaluator.rb +121 -0
- data/lib/dry/validation/extensions/monads.rb +23 -7
- data/lib/dry/validation/messages.rb +50 -6
- data/lib/dry/validation/result.rb +109 -45
- data/lib/dry/validation/rule.rb +37 -0
- data/lib/dry/validation/version.rb +3 -1
- metadata +36 -337
- data/.codeclimate.yml +0 -17
- data/.gitignore +0 -9
- data/.rspec +0 -3
- data/.travis.yml +0 -29
- data/CONTRIBUTING.md +0 -31
- data/Gemfile +0 -25
- data/Rakefile +0 -22
- data/benchmarks/benchmark_form_invalid.rb +0 -64
- data/benchmarks/benchmark_form_valid.rb +0 -64
- data/benchmarks/benchmark_schema_invalid_huge.rb +0 -52
- data/benchmarks/profile_schema_call_invalid.rb +0 -20
- data/benchmarks/profile_schema_call_valid.rb +0 -20
- data/benchmarks/profile_schema_definition.rb +0 -14
- data/benchmarks/profile_schema_huge_invalid.rb +0 -30
- data/benchmarks/profile_schema_messages_invalid.rb +0 -20
- data/benchmarks/suite.rb +0 -5
- data/config/errors.yml +0 -89
- data/dry-validation.gemspec +0 -28
- data/examples/basic.rb +0 -15
- data/examples/each.rb +0 -14
- data/examples/json.rb +0 -12
- data/examples/multiple.rb +0 -27
- data/examples/nested.rb +0 -22
- data/examples/params.rb +0 -11
- data/lib/dry/validation/compat/form.rb +0 -67
- data/lib/dry/validation/deprecations.rb +0 -24
- data/lib/dry/validation/executor.rb +0 -91
- data/lib/dry/validation/extensions.rb +0 -7
- data/lib/dry/validation/extensions/struct.rb +0 -32
- data/lib/dry/validation/input_processor_compiler.rb +0 -137
- data/lib/dry/validation/input_processor_compiler/json.rb +0 -45
- data/lib/dry/validation/input_processor_compiler/params.rb +0 -49
- data/lib/dry/validation/input_processor_compiler/sanitizer.rb +0 -47
- data/lib/dry/validation/message.rb +0 -98
- data/lib/dry/validation/message_compiler.rb +0 -188
- data/lib/dry/validation/message_compiler/visitor_opts.rb +0 -37
- data/lib/dry/validation/message_set.rb +0 -122
- data/lib/dry/validation/messages/abstract.rb +0 -119
- data/lib/dry/validation/messages/i18n.rb +0 -47
- data/lib/dry/validation/messages/namespaced.rb +0 -39
- data/lib/dry/validation/messages/yaml.rb +0 -61
- data/lib/dry/validation/predicate_registry.rb +0 -115
- data/lib/dry/validation/predicates.rb +0 -19
- data/lib/dry/validation/schema.rb +0 -126
- data/lib/dry/validation/schema/check.rb +0 -37
- data/lib/dry/validation/schema/class_interface.rb +0 -190
- data/lib/dry/validation/schema/deprecated.rb +0 -30
- data/lib/dry/validation/schema/dsl.rb +0 -118
- data/lib/dry/validation/schema/form.rb +0 -9
- data/lib/dry/validation/schema/json.rb +0 -21
- data/lib/dry/validation/schema/key.rb +0 -71
- data/lib/dry/validation/schema/params.rb +0 -22
- data/lib/dry/validation/schema/rule.rb +0 -202
- data/lib/dry/validation/schema/value.rb +0 -211
- data/lib/dry/validation/schema_compiler.rb +0 -81
- data/lib/dry/validation/template.rb +0 -66
- data/lib/dry/validation/type_specs.rb +0 -70
- data/spec/extensions/monads/result_spec.rb +0 -40
- data/spec/extensions/struct/schema_spec.rb +0 -32
- data/spec/fixtures/locales/en.yml +0 -8
- data/spec/fixtures/locales/pl.yml +0 -22
- data/spec/integration/custom_error_messages_spec.rb +0 -54
- data/spec/integration/custom_predicates_spec.rb +0 -228
- data/spec/integration/hints_spec.rb +0 -170
- data/spec/integration/injecting_rules_spec.rb +0 -30
- data/spec/integration/json/defining_base_schema_spec.rb +0 -41
- data/spec/integration/localized_error_messages_spec.rb +0 -72
- data/spec/integration/message_compiler_spec.rb +0 -405
- data/spec/integration/messages/i18n_spec.rb +0 -104
- data/spec/integration/optional_keys_spec.rb +0 -28
- data/spec/integration/params/predicates/array_spec.rb +0 -287
- data/spec/integration/params/predicates/empty_spec.rb +0 -263
- data/spec/integration/params/predicates/eql_spec.rb +0 -327
- data/spec/integration/params/predicates/even_spec.rb +0 -455
- data/spec/integration/params/predicates/excluded_from_spec.rb +0 -455
- data/spec/integration/params/predicates/excludes_spec.rb +0 -391
- data/spec/integration/params/predicates/false_spec.rb +0 -455
- data/spec/integration/params/predicates/filled_spec.rb +0 -467
- data/spec/integration/params/predicates/format_spec.rb +0 -454
- data/spec/integration/params/predicates/gt_spec.rb +0 -519
- data/spec/integration/params/predicates/gteq_spec.rb +0 -519
- data/spec/integration/params/predicates/included_in_spec.rb +0 -455
- data/spec/integration/params/predicates/includes_spec.rb +0 -391
- data/spec/integration/params/predicates/key_spec.rb +0 -67
- data/spec/integration/params/predicates/lt_spec.rb +0 -519
- data/spec/integration/params/predicates/lteq_spec.rb +0 -519
- data/spec/integration/params/predicates/max_size_spec.rb +0 -391
- data/spec/integration/params/predicates/min_size_spec.rb +0 -391
- data/spec/integration/params/predicates/none_spec.rb +0 -265
- data/spec/integration/params/predicates/not_eql_spec.rb +0 -327
- data/spec/integration/params/predicates/odd_spec.rb +0 -455
- data/spec/integration/params/predicates/size/fixed_spec.rb +0 -393
- data/spec/integration/params/predicates/size/range_spec.rb +0 -396
- data/spec/integration/params/predicates/true_spec.rb +0 -455
- data/spec/integration/params/predicates/type_spec.rb +0 -391
- data/spec/integration/result_spec.rb +0 -81
- data/spec/integration/schema/array_schema_spec.rb +0 -59
- data/spec/integration/schema/check_rules_spec.rb +0 -119
- data/spec/integration/schema/check_with_nested_el_spec.rb +0 -37
- data/spec/integration/schema/check_with_nth_el_spec.rb +0 -25
- data/spec/integration/schema/default_settings_spec.rb +0 -11
- data/spec/integration/schema/defining_base_schema_spec.rb +0 -41
- data/spec/integration/schema/dynamic_predicate_args_spec.rb +0 -43
- data/spec/integration/schema/each_with_set_spec.rb +0 -70
- data/spec/integration/schema/extending_dsl_spec.rb +0 -27
- data/spec/integration/schema/form_spec.rb +0 -236
- data/spec/integration/schema/hash_schema_spec.rb +0 -47
- data/spec/integration/schema/inheriting_schema_spec.rb +0 -31
- data/spec/integration/schema/input_processor_spec.rb +0 -46
- data/spec/integration/schema/json/explicit_types_spec.rb +0 -157
- data/spec/integration/schema/json_spec.rb +0 -163
- data/spec/integration/schema/macros/confirmation_spec.rb +0 -35
- data/spec/integration/schema/macros/each_spec.rb +0 -268
- data/spec/integration/schema/macros/filled_spec.rb +0 -87
- data/spec/integration/schema/macros/input_spec.rb +0 -139
- data/spec/integration/schema/macros/maybe_spec.rb +0 -99
- data/spec/integration/schema/macros/rule_spec.rb +0 -75
- data/spec/integration/schema/macros/value_spec.rb +0 -119
- data/spec/integration/schema/macros/when_spec.rb +0 -62
- data/spec/integration/schema/nested_schemas_spec.rb +0 -236
- data/spec/integration/schema/nested_values_spec.rb +0 -46
- data/spec/integration/schema/not_spec.rb +0 -34
- data/spec/integration/schema/numbers_spec.rb +0 -19
- data/spec/integration/schema/option_with_default_spec.rb +0 -64
- data/spec/integration/schema/or_spec.rb +0 -87
- data/spec/integration/schema/params/defining_base_schema_spec.rb +0 -41
- data/spec/integration/schema/params/explicit_types_spec.rb +0 -195
- data/spec/integration/schema/params_spec.rb +0 -234
- data/spec/integration/schema/predicate_verification_spec.rb +0 -9
- data/spec/integration/schema/predicates/array_spec.rb +0 -295
- data/spec/integration/schema/predicates/custom_spec.rb +0 -103
- data/spec/integration/schema/predicates/empty_spec.rb +0 -263
- data/spec/integration/schema/predicates/eql_spec.rb +0 -327
- data/spec/integration/schema/predicates/even_spec.rb +0 -455
- data/spec/integration/schema/predicates/excluded_from/array_spec.rb +0 -459
- data/spec/integration/schema/predicates/excluded_from/range_spec.rb +0 -459
- data/spec/integration/schema/predicates/excludes_spec.rb +0 -391
- data/spec/integration/schema/predicates/filled_spec.rb +0 -467
- data/spec/integration/schema/predicates/format_spec.rb +0 -455
- data/spec/integration/schema/predicates/gt_spec.rb +0 -519
- data/spec/integration/schema/predicates/gteq_spec.rb +0 -519
- data/spec/integration/schema/predicates/hash_spec.rb +0 -69
- data/spec/integration/schema/predicates/included_in/array_spec.rb +0 -459
- data/spec/integration/schema/predicates/included_in/range_spec.rb +0 -459
- data/spec/integration/schema/predicates/includes_spec.rb +0 -391
- data/spec/integration/schema/predicates/key_spec.rb +0 -88
- data/spec/integration/schema/predicates/lt_spec.rb +0 -520
- data/spec/integration/schema/predicates/lteq_spec.rb +0 -519
- data/spec/integration/schema/predicates/max_size_spec.rb +0 -391
- data/spec/integration/schema/predicates/min_size_spec.rb +0 -391
- data/spec/integration/schema/predicates/none_spec.rb +0 -265
- data/spec/integration/schema/predicates/not_eql_spec.rb +0 -391
- data/spec/integration/schema/predicates/odd_spec.rb +0 -455
- data/spec/integration/schema/predicates/size/fixed_spec.rb +0 -398
- data/spec/integration/schema/predicates/size/range_spec.rb +0 -395
- data/spec/integration/schema/predicates/type_spec.rb +0 -413
- data/spec/integration/schema/reusing_schema_spec.rb +0 -33
- data/spec/integration/schema/using_types_spec.rb +0 -135
- data/spec/integration/schema/validate_spec.rb +0 -120
- data/spec/integration/schema/xor_spec.rb +0 -35
- data/spec/integration/schema_builders_spec.rb +0 -17
- data/spec/integration/schema_spec.rb +0 -173
- data/spec/shared/message_compiler.rb +0 -11
- data/spec/shared/predicate_helper.rb +0 -15
- data/spec/shared/rule_compiler.rb +0 -8
- data/spec/spec_helper.rb +0 -62
- data/spec/support/define_struct.rb +0 -25
- data/spec/support/matchers.rb +0 -38
- data/spec/support/mutant.rb +0 -9
- data/spec/support/predicates_integration.rb +0 -7
- data/spec/unit/input_processor_compiler/json_spec.rb +0 -283
- data/spec/unit/input_processor_compiler/params_spec.rb +0 -328
- data/spec/unit/message_compiler/visit_failure_spec.rb +0 -38
- data/spec/unit/message_compiler/visit_spec.rb +0 -16
- data/spec/unit/message_compiler_spec.rb +0 -7
- data/spec/unit/predicate_registry_spec.rb +0 -34
- data/spec/unit/schema/key_spec.rb +0 -38
- data/spec/unit/schema/rule_spec.rb +0 -42
- data/spec/unit/schema/value_spec.rb +0 -131
- data/spec/unit/schema_spec.rb +0 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 60a3c6ffd96f85afae4533e24216f467888d9f3bfdb3d1f01e78b292a5cceb92
|
4
|
+
data.tar.gz: c6e27c162c638614e9fb876f11c8f5dde26ecc2b06edbaef62c14f6a23fd1b1d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 884c2d19f7de54aec614eaf90c6a11040c48f5c267482f09e6b86f8439871b7df6403762cb7885eb238d1dae0835f315abc7bbae6ff84208ea024ec33dcb93c4
|
7
|
+
data.tar.gz: bf22f2906b4d9892015b58c1c0f637b10d82f5f63770a703c1b92e8d105fb3f44b7a9d822c20ffc4bacc1a63e882f13303157c13fdd6c2a65a1c42e0931195fb
|
data/CHANGELOG.md
CHANGED
@@ -1,31 +1,13 @@
|
|
1
|
-
#
|
1
|
+
# v1.0.0.alpha1 2019-03-04
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
* Fixed regression with caching templates that rely on predicate arguments (solnic)
|
6
|
-
* Removed ugly workaround for template evaluation with extra tokens (solnic)
|
7
|
-
|
8
|
-
### Changed
|
9
|
-
|
10
|
-
* [internal] `Messages::Abstract#call` returns a `Template` object now, this was backported from dry-schema (solnic)
|
11
|
-
|
12
|
-
[Compare v0.13.2...v0.13.3](https://github.com/dry-rb/dry-validation/compare/v0.13.2...v0.13.3)
|
13
|
-
|
14
|
-
# v0.13.2 2019-05-12
|
15
|
-
|
16
|
-
### Fixed
|
17
|
-
|
18
|
-
* Caching message templates uses restricted set of known keys to calculate cache keys (solnic)
|
3
|
+
Complete rewrite on top of `dry-schema`.
|
19
4
|
|
20
|
-
|
21
|
-
|
22
|
-
# v0.13.1 2019-03-22
|
23
|
-
|
24
|
-
### Changed
|
5
|
+
### Added
|
25
6
|
|
26
|
-
|
7
|
+
* [BREAKING] `Dry::Validation::Contract` as a replacement for validation schemas (solnic)
|
8
|
+
* [BREAKING] New `rule` DSL with an improved API for setting error messages (solnic)
|
27
9
|
|
28
|
-
[Compare v0.13.0...
|
10
|
+
[Compare v0.13.0...v1.0.0.alpha1](https://github.com/dry-rb/dry-validation/compare/v0.13.0...v1.0.0.alpha1)
|
29
11
|
|
30
12
|
# v0.13.0 2019-01-29
|
31
13
|
|
@@ -38,7 +20,15 @@
|
|
38
20
|
|
39
21
|
- Warning about method redefined (amatsuda)
|
40
22
|
|
41
|
-
[Compare v0.12.
|
23
|
+
[Compare v0.12.3...v0.13.0](https://github.com/dry-rb/dry-validation/compare/v0.12.3...v0.13.0)
|
24
|
+
|
25
|
+
# v0.12.3 2019-01-29
|
26
|
+
|
27
|
+
### Changed
|
28
|
+
|
29
|
+
* [internal] dyr-logic was pinned to `~> 0.4.2` (flash-gordon)
|
30
|
+
|
31
|
+
[Compare v0.12.2...v0.12.3](https://github.com/dry-rb/dry-validation/compare/v0.12.2...v0.12.3)
|
42
32
|
|
43
33
|
# v0.12.2 2018-08-29
|
44
34
|
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -12,15 +12,20 @@
|
|
12
12
|
[][codeclimate]
|
13
13
|
[][inchpages]
|
14
14
|
|
15
|
-
## Status
|
16
|
-
|
17
|
-
We're working on a new foundation for dry-validation, called dry-schema. You can see progress in [this PR](https://github.com/dry-rb/dry-schema/pull/3). This will result in a partial rewrite for 1.0.0 version. Currently known bugs/issues will be addressed in 1.0.0, **not in 0.x** due to lack of time. More info about 1.0.0 plans can be found [in this thread](https://discourse.dry-rb.org/t/plans-for-dry-validation-dry-schema-a-new-gem/215/3).
|
18
|
-
|
19
15
|
## Links
|
20
16
|
|
21
17
|
* [Documentation](http://dry-rb.org/gems/dry-validation)
|
22
18
|
* [Guidelines for contributing](CONTRIBUTING.md)
|
23
19
|
|
20
|
+
## Supported Ruby versions
|
21
|
+
|
22
|
+
This library officially supports following Ruby versions:
|
23
|
+
|
24
|
+
* MRI >= `2.4`
|
25
|
+
* jruby >= `9.2`
|
26
|
+
|
27
|
+
It **should** work on MRI `2.3.x` too, but there's no official support for this version.
|
28
|
+
|
24
29
|
## License
|
25
30
|
|
26
31
|
See `LICENSE` file.
|
data/lib/dry-validation.rb
CHANGED
data/lib/dry/validation.rb
CHANGED
@@ -1,43 +1,16 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/validation/contract'
|
3
4
|
|
4
5
|
module Dry
|
6
|
+
# Main library namespace
|
7
|
+
#
|
8
|
+
# @api private
|
5
9
|
module Validation
|
6
10
|
extend Dry::Core::Extensions
|
7
|
-
include Dry::Core::Constants
|
8
|
-
|
9
|
-
MissingMessageError = Class.new(StandardError)
|
10
|
-
InvalidSchemaError = Class.new(StandardError)
|
11
|
-
|
12
|
-
def self.messages_paths
|
13
|
-
Messages::Abstract.config.paths
|
14
|
-
end
|
15
|
-
|
16
|
-
def self.Schema(base = Schema, **options, &block)
|
17
|
-
schema_class = Class.new(base.is_a?(Schema) ? base.class : base)
|
18
|
-
klass = schema_class.define(options.merge(schema_class: schema_class), &block)
|
19
11
|
|
20
|
-
|
21
|
-
|
22
|
-
else
|
23
|
-
klass.new
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def self.Params(base = nil, **options, &block)
|
28
|
-
klass = base ? Schema::Params.configure(Class.new(base)) : Schema::Params
|
29
|
-
Validation.Schema(klass, options, &block)
|
30
|
-
end
|
31
|
-
|
32
|
-
def self.JSON(base = Schema::JSON, **options, &block)
|
33
|
-
klass = base ? Schema::JSON.configure(Class.new(base)) : Schema::JSON
|
34
|
-
Validation.Schema(klass, options, &block)
|
12
|
+
register_extension(:monads) do
|
13
|
+
require 'dry/validation/extensions/monads'
|
35
14
|
end
|
36
15
|
end
|
37
16
|
end
|
38
|
-
|
39
|
-
require 'dry/validation/schema'
|
40
|
-
require 'dry/validation/schema/params'
|
41
|
-
require 'dry/validation/schema/json'
|
42
|
-
require 'dry/validation/extensions'
|
43
|
-
require 'dry/validation/version'
|
@@ -0,0 +1,132 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/equalizer'
|
4
|
+
require 'dry/configurable'
|
5
|
+
require 'dry/initializer'
|
6
|
+
|
7
|
+
require 'dry/validation/constants'
|
8
|
+
require 'dry/validation/rule'
|
9
|
+
require 'dry/validation/evaluator'
|
10
|
+
require 'dry/validation/result'
|
11
|
+
require 'dry/validation/contract/class_interface'
|
12
|
+
|
13
|
+
module Dry
|
14
|
+
module Validation
|
15
|
+
# Contract objects apply rules to input
|
16
|
+
#
|
17
|
+
# A contract consists of a schema and rules. The schema is applied to the
|
18
|
+
# input before rules are applied, this way you can be sure that your rules
|
19
|
+
# won't be applied to values that didn't pass schema checks.
|
20
|
+
#
|
21
|
+
# It's up to you how exactly you're going to separate schema checks from
|
22
|
+
# your rules.
|
23
|
+
#
|
24
|
+
# @example
|
25
|
+
# class NewUser < Dry::Validation::Contract
|
26
|
+
# params do
|
27
|
+
# required(:email).filled(:string)
|
28
|
+
# required(:age).filled(:integer)
|
29
|
+
# optional(:login).maybe(:string, :filled?)
|
30
|
+
# optional(:password).maybe(:string, min_size?: 10)
|
31
|
+
# optional(:password_confirmation).maybe(:string)
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# rule(:password) do
|
35
|
+
# failure('is required') if values[:login] && !values[:password]
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# rule(:age) do
|
39
|
+
# failure('must be greater or equal 18') if values[:age] < 18
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# new_user_contract = NewUserContract.new
|
44
|
+
# new_user_contract.call(email: 'jane@doe.org', age: 21)
|
45
|
+
#
|
46
|
+
# @api public
|
47
|
+
class Contract
|
48
|
+
include Dry::Equalizer(:schema, :rules, :messages)
|
49
|
+
|
50
|
+
extend Dry::Configurable
|
51
|
+
extend Dry::Initializer
|
52
|
+
extend ClassInterface
|
53
|
+
|
54
|
+
# @!group Configuration
|
55
|
+
|
56
|
+
# @overload config.messages=(identifier)
|
57
|
+
# Set message backend
|
58
|
+
#
|
59
|
+
# @param identifier [Symbol] the backend identifier, either `:yaml` or `:i18n`
|
60
|
+
#
|
61
|
+
# @api public
|
62
|
+
# @!scope class
|
63
|
+
setting :messages, :yaml
|
64
|
+
|
65
|
+
# @overload config.messages_file=(path)
|
66
|
+
# Set additional path to messages file
|
67
|
+
#
|
68
|
+
# @param path [String, Pathname] the path
|
69
|
+
#
|
70
|
+
# @api public
|
71
|
+
# @!scope class
|
72
|
+
setting :messages_file
|
73
|
+
|
74
|
+
# @overload config.namespace=(name)
|
75
|
+
# Set namespace that will be used to override default messages
|
76
|
+
#
|
77
|
+
# @param name [Symbol] the namespace
|
78
|
+
#
|
79
|
+
# @api public
|
80
|
+
# @!scope class
|
81
|
+
setting :namespace
|
82
|
+
|
83
|
+
# @!endgroup
|
84
|
+
|
85
|
+
# @!attribute [r] schema
|
86
|
+
# @return [Dry::Schema::Params, Dry::Schema::JSON, Dry::Schema::Processor]
|
87
|
+
# @api private
|
88
|
+
option :schema, default: -> { self.class.__schema__ }
|
89
|
+
|
90
|
+
# @!attribute [r] rules
|
91
|
+
# @return [Hash]
|
92
|
+
# @api private
|
93
|
+
option :rules, default: -> { self.class.rules }
|
94
|
+
|
95
|
+
# @!attribute [r] messages
|
96
|
+
# @return [Messages::I18n, Messages::YAML]
|
97
|
+
# @api private
|
98
|
+
option :messages, default: -> { self.class.messages }
|
99
|
+
|
100
|
+
# Apply contract to an input
|
101
|
+
#
|
102
|
+
# @return [Result]
|
103
|
+
#
|
104
|
+
# @api public
|
105
|
+
def call(input)
|
106
|
+
Result.new(schema.(input)) do |result|
|
107
|
+
rules.each do |rule|
|
108
|
+
next if rule.keys.any? { |key| result.error?(key) }
|
109
|
+
|
110
|
+
rule_result = rule.(self, result)
|
111
|
+
result.add_error(*rule_result.message) if rule_result.failure?
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Get message text for the given rule name and options
|
117
|
+
#
|
118
|
+
# @return [String]
|
119
|
+
#
|
120
|
+
# @api private
|
121
|
+
def message(key, tokens: EMPTY_HASH, **opts)
|
122
|
+
template = messages[key, opts.merge(tokens)]
|
123
|
+
|
124
|
+
if template
|
125
|
+
template.(template.data(tokens))
|
126
|
+
else
|
127
|
+
raise MissingMessageError, "Message template for #{key.inspect} was not found"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/schema'
|
4
|
+
require 'dry/validation/messages'
|
5
|
+
require 'dry/validation/constants'
|
6
|
+
|
7
|
+
module Dry
|
8
|
+
module Validation
|
9
|
+
class Contract
|
10
|
+
# Contract's class interface
|
11
|
+
#
|
12
|
+
# @api public
|
13
|
+
module ClassInterface
|
14
|
+
# Define a params schema for your contract
|
15
|
+
#
|
16
|
+
# This type of schema is suitable for HTTP parameters
|
17
|
+
#
|
18
|
+
# @return [Dry::Schema::Params]
|
19
|
+
#
|
20
|
+
# @api public
|
21
|
+
def params(&block)
|
22
|
+
define(:Params, &block)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Define a JSON schema for your contract
|
26
|
+
#
|
27
|
+
# This type of schema is suitable for JSON data
|
28
|
+
#
|
29
|
+
# @return [Dry::Schema::JSON]
|
30
|
+
#
|
31
|
+
# @api public
|
32
|
+
def json(&block)
|
33
|
+
define(:JSON, &block)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Define a plain schema for your contract
|
37
|
+
#
|
38
|
+
# This type of schema does not offer coercion out of the box
|
39
|
+
#
|
40
|
+
# @return [Dry::Schema::Processor]
|
41
|
+
#
|
42
|
+
# @api public
|
43
|
+
def schema(&block)
|
44
|
+
define(:schema, &block)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Define a rule for your contract
|
48
|
+
#
|
49
|
+
# @example using a symbol
|
50
|
+
# rule(:age) do
|
51
|
+
# failure('must be at least 18') if values[:age] < 18
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# @example using a path to a value and a custom predicate
|
55
|
+
# rule('address.street') do
|
56
|
+
# failure('please provide a valid street address') if valid_street?(values[:street])
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# @return [Array<Rule>]
|
60
|
+
#
|
61
|
+
# @api public
|
62
|
+
def rule(*keys, &block)
|
63
|
+
rules << Rule.new(keys: keys, block: block)
|
64
|
+
rules
|
65
|
+
end
|
66
|
+
|
67
|
+
# A shortcut that can be used to define contracts that won't be reused or inherited
|
68
|
+
#
|
69
|
+
# @api public
|
70
|
+
def build(option = nil, &block)
|
71
|
+
Class.new(self, &block).new(option)
|
72
|
+
end
|
73
|
+
|
74
|
+
# @api private
|
75
|
+
def __schema__
|
76
|
+
@__schema__ if defined?(@__schema__)
|
77
|
+
end
|
78
|
+
|
79
|
+
# @api private
|
80
|
+
def rules
|
81
|
+
@__rules__ ||= EMPTY_ARRAY
|
82
|
+
.dup
|
83
|
+
.concat(superclass.respond_to?(:rules) ? superclass.rules : EMPTY_ARRAY)
|
84
|
+
end
|
85
|
+
|
86
|
+
# @api private
|
87
|
+
def messages
|
88
|
+
@__messages__ ||= Messages.setup(config)
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
# @api private
|
94
|
+
def schema_opts
|
95
|
+
{ parent: superclass&.__schema__, config: config }
|
96
|
+
end
|
97
|
+
|
98
|
+
# @api private
|
99
|
+
def define(method_name, &block)
|
100
|
+
raise ::Dry::Validation::DuplicateSchemaError, 'Schema has already been defined' if defined?(@__schema__)
|
101
|
+
|
102
|
+
case method_name
|
103
|
+
when :schema
|
104
|
+
@__schema__ = Schema.define(schema_opts, &block)
|
105
|
+
when :Params
|
106
|
+
@__schema__ = Schema.Params(schema_opts, &block)
|
107
|
+
when :JSON
|
108
|
+
@__schema__ = Schema.JSON(schema_opts, &block)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/initializer'
|
4
|
+
require 'dry/validation/constants'
|
5
|
+
|
6
|
+
module Dry
|
7
|
+
module Validation
|
8
|
+
# Evaluator is the execution context for rules
|
9
|
+
#
|
10
|
+
# Evaluators expose an API for setting failure messages and forward
|
11
|
+
# method calls to the contracts, so that you can use your contract
|
12
|
+
# methods within rule blocks
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
class Evaluator
|
16
|
+
extend Dry::Initializer
|
17
|
+
|
18
|
+
# @!attribute [r] _context
|
19
|
+
# @return [Contract]
|
20
|
+
# @api private
|
21
|
+
param :_context
|
22
|
+
|
23
|
+
# @!attribute [r] keys
|
24
|
+
# @return [Array<String, Symbol, Hash>]
|
25
|
+
# @api private
|
26
|
+
option :keys
|
27
|
+
|
28
|
+
# @!attribute [r] default_id
|
29
|
+
# @return [String, Symbol, Hash]
|
30
|
+
# @api private
|
31
|
+
option :default_id, default: proc { keys.first }
|
32
|
+
|
33
|
+
# @!attribute [r] values
|
34
|
+
# @return [Object]
|
35
|
+
# @api private
|
36
|
+
option :values
|
37
|
+
|
38
|
+
# @!attribute [r] message
|
39
|
+
# @return [String]
|
40
|
+
# @api private
|
41
|
+
attr_reader :message
|
42
|
+
|
43
|
+
# Initialize a new evaluator
|
44
|
+
#
|
45
|
+
# @api private
|
46
|
+
def initialize(*args, &block)
|
47
|
+
super(*args)
|
48
|
+
@failure = false
|
49
|
+
instance_eval(&block)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Set failure message
|
53
|
+
#
|
54
|
+
# @overload failure(message)
|
55
|
+
# Set message text explicitly
|
56
|
+
# @param message [String] The message text
|
57
|
+
# @example
|
58
|
+
# failure('this failed')
|
59
|
+
#
|
60
|
+
# @overload failure(id)
|
61
|
+
# Use message identifier (needs localized messages setup)
|
62
|
+
# @param id [Symbol] The message id
|
63
|
+
# @example
|
64
|
+
# failure(:taken)
|
65
|
+
#
|
66
|
+
# @overload failure(key, message)
|
67
|
+
# Set message under specified key (overrides rule's default key)
|
68
|
+
# @param id [Symbol] The message key
|
69
|
+
# @example
|
70
|
+
# failure(:my_error, 'this failed')
|
71
|
+
#
|
72
|
+
# @return [Evaluator]
|
73
|
+
#
|
74
|
+
# @api public
|
75
|
+
def failure(*args, **tokens)
|
76
|
+
id, text =
|
77
|
+
if args.size.equal?(1)
|
78
|
+
case (msg = args[0])
|
79
|
+
when Symbol
|
80
|
+
[default_id, _context.message(msg, rule: default_id, tokens: tokens)]
|
81
|
+
when String
|
82
|
+
[default_id, msg]
|
83
|
+
end
|
84
|
+
else
|
85
|
+
args
|
86
|
+
end
|
87
|
+
@failure = true
|
88
|
+
@message = [id, text]
|
89
|
+
self
|
90
|
+
end
|
91
|
+
|
92
|
+
# Check if evaluation resulted in a failure message
|
93
|
+
#
|
94
|
+
# @return [Boolean]
|
95
|
+
#
|
96
|
+
# @api private
|
97
|
+
def failure?
|
98
|
+
@failure.equal?(true)
|
99
|
+
end
|
100
|
+
|
101
|
+
# @api private
|
102
|
+
def respond_to_missing?(meth, include_private = false)
|
103
|
+
super || _context.respond_to?(meth, true)
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
# Forward to the underlying context
|
109
|
+
#
|
110
|
+
# @api private
|
111
|
+
def method_missing(meth, *args, &block)
|
112
|
+
# yes, we do want to delegate to private methods too
|
113
|
+
if _context.respond_to?(meth, true)
|
114
|
+
_context.__send__(meth, *args, &block)
|
115
|
+
else
|
116
|
+
super
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|