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.
Files changed (196) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +15 -25
  3. data/LICENSE +1 -1
  4. data/README.md +9 -4
  5. data/lib/dry-validation.rb +2 -0
  6. data/lib/dry/validation.rb +8 -35
  7. data/lib/dry/validation/constants.rb +12 -0
  8. data/lib/dry/validation/contract.rb +132 -0
  9. data/lib/dry/validation/contract/class_interface.rb +114 -0
  10. data/lib/dry/validation/evaluator.rb +121 -0
  11. data/lib/dry/validation/extensions/monads.rb +23 -7
  12. data/lib/dry/validation/messages.rb +50 -6
  13. data/lib/dry/validation/result.rb +109 -45
  14. data/lib/dry/validation/rule.rb +37 -0
  15. data/lib/dry/validation/version.rb +3 -1
  16. metadata +36 -337
  17. data/.codeclimate.yml +0 -17
  18. data/.gitignore +0 -9
  19. data/.rspec +0 -3
  20. data/.travis.yml +0 -29
  21. data/CONTRIBUTING.md +0 -31
  22. data/Gemfile +0 -25
  23. data/Rakefile +0 -22
  24. data/benchmarks/benchmark_form_invalid.rb +0 -64
  25. data/benchmarks/benchmark_form_valid.rb +0 -64
  26. data/benchmarks/benchmark_schema_invalid_huge.rb +0 -52
  27. data/benchmarks/profile_schema_call_invalid.rb +0 -20
  28. data/benchmarks/profile_schema_call_valid.rb +0 -20
  29. data/benchmarks/profile_schema_definition.rb +0 -14
  30. data/benchmarks/profile_schema_huge_invalid.rb +0 -30
  31. data/benchmarks/profile_schema_messages_invalid.rb +0 -20
  32. data/benchmarks/suite.rb +0 -5
  33. data/config/errors.yml +0 -89
  34. data/dry-validation.gemspec +0 -28
  35. data/examples/basic.rb +0 -15
  36. data/examples/each.rb +0 -14
  37. data/examples/json.rb +0 -12
  38. data/examples/multiple.rb +0 -27
  39. data/examples/nested.rb +0 -22
  40. data/examples/params.rb +0 -11
  41. data/lib/dry/validation/compat/form.rb +0 -67
  42. data/lib/dry/validation/deprecations.rb +0 -24
  43. data/lib/dry/validation/executor.rb +0 -91
  44. data/lib/dry/validation/extensions.rb +0 -7
  45. data/lib/dry/validation/extensions/struct.rb +0 -32
  46. data/lib/dry/validation/input_processor_compiler.rb +0 -137
  47. data/lib/dry/validation/input_processor_compiler/json.rb +0 -45
  48. data/lib/dry/validation/input_processor_compiler/params.rb +0 -49
  49. data/lib/dry/validation/input_processor_compiler/sanitizer.rb +0 -47
  50. data/lib/dry/validation/message.rb +0 -98
  51. data/lib/dry/validation/message_compiler.rb +0 -188
  52. data/lib/dry/validation/message_compiler/visitor_opts.rb +0 -37
  53. data/lib/dry/validation/message_set.rb +0 -122
  54. data/lib/dry/validation/messages/abstract.rb +0 -119
  55. data/lib/dry/validation/messages/i18n.rb +0 -47
  56. data/lib/dry/validation/messages/namespaced.rb +0 -39
  57. data/lib/dry/validation/messages/yaml.rb +0 -61
  58. data/lib/dry/validation/predicate_registry.rb +0 -115
  59. data/lib/dry/validation/predicates.rb +0 -19
  60. data/lib/dry/validation/schema.rb +0 -126
  61. data/lib/dry/validation/schema/check.rb +0 -37
  62. data/lib/dry/validation/schema/class_interface.rb +0 -190
  63. data/lib/dry/validation/schema/deprecated.rb +0 -30
  64. data/lib/dry/validation/schema/dsl.rb +0 -118
  65. data/lib/dry/validation/schema/form.rb +0 -9
  66. data/lib/dry/validation/schema/json.rb +0 -21
  67. data/lib/dry/validation/schema/key.rb +0 -71
  68. data/lib/dry/validation/schema/params.rb +0 -22
  69. data/lib/dry/validation/schema/rule.rb +0 -202
  70. data/lib/dry/validation/schema/value.rb +0 -211
  71. data/lib/dry/validation/schema_compiler.rb +0 -81
  72. data/lib/dry/validation/template.rb +0 -66
  73. data/lib/dry/validation/type_specs.rb +0 -70
  74. data/spec/extensions/monads/result_spec.rb +0 -40
  75. data/spec/extensions/struct/schema_spec.rb +0 -32
  76. data/spec/fixtures/locales/en.yml +0 -8
  77. data/spec/fixtures/locales/pl.yml +0 -22
  78. data/spec/integration/custom_error_messages_spec.rb +0 -54
  79. data/spec/integration/custom_predicates_spec.rb +0 -228
  80. data/spec/integration/hints_spec.rb +0 -170
  81. data/spec/integration/injecting_rules_spec.rb +0 -30
  82. data/spec/integration/json/defining_base_schema_spec.rb +0 -41
  83. data/spec/integration/localized_error_messages_spec.rb +0 -72
  84. data/spec/integration/message_compiler_spec.rb +0 -405
  85. data/spec/integration/messages/i18n_spec.rb +0 -104
  86. data/spec/integration/optional_keys_spec.rb +0 -28
  87. data/spec/integration/params/predicates/array_spec.rb +0 -287
  88. data/spec/integration/params/predicates/empty_spec.rb +0 -263
  89. data/spec/integration/params/predicates/eql_spec.rb +0 -327
  90. data/spec/integration/params/predicates/even_spec.rb +0 -455
  91. data/spec/integration/params/predicates/excluded_from_spec.rb +0 -455
  92. data/spec/integration/params/predicates/excludes_spec.rb +0 -391
  93. data/spec/integration/params/predicates/false_spec.rb +0 -455
  94. data/spec/integration/params/predicates/filled_spec.rb +0 -467
  95. data/spec/integration/params/predicates/format_spec.rb +0 -454
  96. data/spec/integration/params/predicates/gt_spec.rb +0 -519
  97. data/spec/integration/params/predicates/gteq_spec.rb +0 -519
  98. data/spec/integration/params/predicates/included_in_spec.rb +0 -455
  99. data/spec/integration/params/predicates/includes_spec.rb +0 -391
  100. data/spec/integration/params/predicates/key_spec.rb +0 -67
  101. data/spec/integration/params/predicates/lt_spec.rb +0 -519
  102. data/spec/integration/params/predicates/lteq_spec.rb +0 -519
  103. data/spec/integration/params/predicates/max_size_spec.rb +0 -391
  104. data/spec/integration/params/predicates/min_size_spec.rb +0 -391
  105. data/spec/integration/params/predicates/none_spec.rb +0 -265
  106. data/spec/integration/params/predicates/not_eql_spec.rb +0 -327
  107. data/spec/integration/params/predicates/odd_spec.rb +0 -455
  108. data/spec/integration/params/predicates/size/fixed_spec.rb +0 -393
  109. data/spec/integration/params/predicates/size/range_spec.rb +0 -396
  110. data/spec/integration/params/predicates/true_spec.rb +0 -455
  111. data/spec/integration/params/predicates/type_spec.rb +0 -391
  112. data/spec/integration/result_spec.rb +0 -81
  113. data/spec/integration/schema/array_schema_spec.rb +0 -59
  114. data/spec/integration/schema/check_rules_spec.rb +0 -119
  115. data/spec/integration/schema/check_with_nested_el_spec.rb +0 -37
  116. data/spec/integration/schema/check_with_nth_el_spec.rb +0 -25
  117. data/spec/integration/schema/default_settings_spec.rb +0 -11
  118. data/spec/integration/schema/defining_base_schema_spec.rb +0 -41
  119. data/spec/integration/schema/dynamic_predicate_args_spec.rb +0 -43
  120. data/spec/integration/schema/each_with_set_spec.rb +0 -70
  121. data/spec/integration/schema/extending_dsl_spec.rb +0 -27
  122. data/spec/integration/schema/form_spec.rb +0 -236
  123. data/spec/integration/schema/hash_schema_spec.rb +0 -47
  124. data/spec/integration/schema/inheriting_schema_spec.rb +0 -31
  125. data/spec/integration/schema/input_processor_spec.rb +0 -46
  126. data/spec/integration/schema/json/explicit_types_spec.rb +0 -157
  127. data/spec/integration/schema/json_spec.rb +0 -163
  128. data/spec/integration/schema/macros/confirmation_spec.rb +0 -35
  129. data/spec/integration/schema/macros/each_spec.rb +0 -268
  130. data/spec/integration/schema/macros/filled_spec.rb +0 -87
  131. data/spec/integration/schema/macros/input_spec.rb +0 -139
  132. data/spec/integration/schema/macros/maybe_spec.rb +0 -99
  133. data/spec/integration/schema/macros/rule_spec.rb +0 -75
  134. data/spec/integration/schema/macros/value_spec.rb +0 -119
  135. data/spec/integration/schema/macros/when_spec.rb +0 -62
  136. data/spec/integration/schema/nested_schemas_spec.rb +0 -236
  137. data/spec/integration/schema/nested_values_spec.rb +0 -46
  138. data/spec/integration/schema/not_spec.rb +0 -34
  139. data/spec/integration/schema/numbers_spec.rb +0 -19
  140. data/spec/integration/schema/option_with_default_spec.rb +0 -64
  141. data/spec/integration/schema/or_spec.rb +0 -87
  142. data/spec/integration/schema/params/defining_base_schema_spec.rb +0 -41
  143. data/spec/integration/schema/params/explicit_types_spec.rb +0 -195
  144. data/spec/integration/schema/params_spec.rb +0 -234
  145. data/spec/integration/schema/predicate_verification_spec.rb +0 -9
  146. data/spec/integration/schema/predicates/array_spec.rb +0 -295
  147. data/spec/integration/schema/predicates/custom_spec.rb +0 -103
  148. data/spec/integration/schema/predicates/empty_spec.rb +0 -263
  149. data/spec/integration/schema/predicates/eql_spec.rb +0 -327
  150. data/spec/integration/schema/predicates/even_spec.rb +0 -455
  151. data/spec/integration/schema/predicates/excluded_from/array_spec.rb +0 -459
  152. data/spec/integration/schema/predicates/excluded_from/range_spec.rb +0 -459
  153. data/spec/integration/schema/predicates/excludes_spec.rb +0 -391
  154. data/spec/integration/schema/predicates/filled_spec.rb +0 -467
  155. data/spec/integration/schema/predicates/format_spec.rb +0 -455
  156. data/spec/integration/schema/predicates/gt_spec.rb +0 -519
  157. data/spec/integration/schema/predicates/gteq_spec.rb +0 -519
  158. data/spec/integration/schema/predicates/hash_spec.rb +0 -69
  159. data/spec/integration/schema/predicates/included_in/array_spec.rb +0 -459
  160. data/spec/integration/schema/predicates/included_in/range_spec.rb +0 -459
  161. data/spec/integration/schema/predicates/includes_spec.rb +0 -391
  162. data/spec/integration/schema/predicates/key_spec.rb +0 -88
  163. data/spec/integration/schema/predicates/lt_spec.rb +0 -520
  164. data/spec/integration/schema/predicates/lteq_spec.rb +0 -519
  165. data/spec/integration/schema/predicates/max_size_spec.rb +0 -391
  166. data/spec/integration/schema/predicates/min_size_spec.rb +0 -391
  167. data/spec/integration/schema/predicates/none_spec.rb +0 -265
  168. data/spec/integration/schema/predicates/not_eql_spec.rb +0 -391
  169. data/spec/integration/schema/predicates/odd_spec.rb +0 -455
  170. data/spec/integration/schema/predicates/size/fixed_spec.rb +0 -398
  171. data/spec/integration/schema/predicates/size/range_spec.rb +0 -395
  172. data/spec/integration/schema/predicates/type_spec.rb +0 -413
  173. data/spec/integration/schema/reusing_schema_spec.rb +0 -33
  174. data/spec/integration/schema/using_types_spec.rb +0 -135
  175. data/spec/integration/schema/validate_spec.rb +0 -120
  176. data/spec/integration/schema/xor_spec.rb +0 -35
  177. data/spec/integration/schema_builders_spec.rb +0 -17
  178. data/spec/integration/schema_spec.rb +0 -173
  179. data/spec/shared/message_compiler.rb +0 -11
  180. data/spec/shared/predicate_helper.rb +0 -15
  181. data/spec/shared/rule_compiler.rb +0 -8
  182. data/spec/spec_helper.rb +0 -62
  183. data/spec/support/define_struct.rb +0 -25
  184. data/spec/support/matchers.rb +0 -38
  185. data/spec/support/mutant.rb +0 -9
  186. data/spec/support/predicates_integration.rb +0 -7
  187. data/spec/unit/input_processor_compiler/json_spec.rb +0 -283
  188. data/spec/unit/input_processor_compiler/params_spec.rb +0 -328
  189. data/spec/unit/message_compiler/visit_failure_spec.rb +0 -38
  190. data/spec/unit/message_compiler/visit_spec.rb +0 -16
  191. data/spec/unit/message_compiler_spec.rb +0 -7
  192. data/spec/unit/predicate_registry_spec.rb +0 -34
  193. data/spec/unit/schema/key_spec.rb +0 -38
  194. data/spec/unit/schema/rule_spec.rb +0 -42
  195. data/spec/unit/schema/value_spec.rb +0 -131
  196. data/spec/unit/schema_spec.rb +0 -35
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d569600e476b1c0830184bea7e9393f333693e11c1a9d8b074e2b2329deeba7a
4
- data.tar.gz: 4811db9bc4e1cc3f398df8717333683355779116a72314532b10bc2d49a87333
3
+ metadata.gz: 60a3c6ffd96f85afae4533e24216f467888d9f3bfdb3d1f01e78b292a5cceb92
4
+ data.tar.gz: c6e27c162c638614e9fb876f11c8f5dde26ecc2b06edbaef62c14f6a23fd1b1d
5
5
  SHA512:
6
- metadata.gz: c497db70c2bf78e3355c685176361b06357ee9d9f17a2c867127d4d575865f0e6cb1f5bafae4b595e1db3af8748fc6122e6522177a152e17e8e6119006591b3c
7
- data.tar.gz: 2b93c44b453e47f6a68ec05bedaec9d2f48d001a6dc9d0f62f2f2d7b70de5f8c1a7130bfc33c558fff1129fceb69d37f33f341d38c96549570a684d7279d24b0
6
+ metadata.gz: 884c2d19f7de54aec614eaf90c6a11040c48f5c267482f09e6b86f8439871b7df6403762cb7885eb238d1dae0835f315abc7bbae6ff84208ea024ec33dcb93c4
7
+ data.tar.gz: bf22f2906b4d9892015b58c1c0f637b10d82f5f63770a703c1b92e8d105fb3f44b7a9d822c20ffc4bacc1a63e882f13303157c13fdd6c2a65a1c42e0931195fb
@@ -1,31 +1,13 @@
1
- # v0.13.3 2019-05-21
1
+ # v1.0.0.alpha1 2019-03-04
2
2
 
3
- ### Fixed
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
- [Compare v0.13.1...v0.13.2](https://github.com/dry-rb/dry-validation/compare/v0.13.1...v0.13.2)
21
-
22
- # v0.13.1 2019-03-22
23
-
24
- ### Changed
5
+ ### Added
25
6
 
26
- - `dry-types` was locked to `~> 0.14.0` (flash-gordon)
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...v0.13.1](https://github.com/dry-rb/dry-validation/compare/v0.13.0...v0.13.1)
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.2...v0.13.0](https://github.com/dry-rb/dry-validation/compare/v0.12.2...v0.13.0)
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
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2015 Dryrb Team
3
+ Copyright (c) 2015-2019 dry-rb team
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy of
6
6
  this software and associated documentation files (the "Software"), to deal in
data/README.md CHANGED
@@ -12,15 +12,20 @@
12
12
  [![Test Coverage](https://codeclimate.com/github/dry-rb/dry-validation/badges/coverage.svg)][codeclimate]
13
13
  [![Inline docs](http://inch-ci.org/github/dry-rb/dry-validation.svg?branch=master)][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.
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'dry/validation'
@@ -1,43 +1,16 @@
1
- require 'dry/core/extensions'
2
- require 'dry/core/constants'
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
- if options[:build] == false
21
- klass
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,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry/core/constants'
4
+
5
+ module Dry
6
+ module Validation
7
+ include Dry::Core::Constants
8
+
9
+ MissingMessageError = Class.new(StandardError)
10
+ DuplicateSchemaError = Class.new(StandardError)
11
+ end
12
+ end
@@ -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