dry-validation 0.7.4 → 0.8.0

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 (143) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +9 -6
  4. data/CHANGELOG.md +58 -0
  5. data/Gemfile +3 -3
  6. data/benchmarks/benchmark_form_invalid.rb +64 -0
  7. data/benchmarks/benchmark_form_valid.rb +64 -0
  8. data/benchmarks/profile_schema_call_invalid.rb +20 -0
  9. data/benchmarks/profile_schema_call_valid.rb +20 -0
  10. data/benchmarks/profile_schema_definition.rb +14 -0
  11. data/benchmarks/profile_schema_messages_invalid.rb +20 -0
  12. data/benchmarks/suite.rb +5 -0
  13. data/config/errors.yml +12 -5
  14. data/dry-validation.gemspec +2 -2
  15. data/examples/basic.rb +2 -2
  16. data/examples/form.rb +2 -2
  17. data/examples/json.rb +2 -2
  18. data/examples/nested.rb +6 -6
  19. data/lib/dry/validation.rb +22 -3
  20. data/lib/dry/validation/deprecations.rb +23 -0
  21. data/lib/dry/validation/error_compiler.rb +31 -11
  22. data/lib/dry/validation/error_compiler/input.rb +44 -57
  23. data/lib/dry/validation/hint_compiler.rb +15 -7
  24. data/lib/dry/validation/input_processor_compiler.rb +13 -6
  25. data/lib/dry/validation/input_processor_compiler/form.rb +2 -0
  26. data/lib/dry/validation/input_processor_compiler/sanitizer.rb +1 -1
  27. data/lib/dry/validation/messages/abstract.rb +9 -1
  28. data/lib/dry/validation/predicate_registry.rb +101 -0
  29. data/lib/dry/validation/result.rb +8 -1
  30. data/lib/dry/validation/schema.rb +110 -44
  31. data/lib/dry/validation/schema/check.rb +4 -2
  32. data/lib/dry/validation/schema/deprecated.rb +31 -0
  33. data/lib/dry/validation/schema/dsl.rb +18 -11
  34. data/lib/dry/validation/schema/form.rb +1 -0
  35. data/lib/dry/validation/schema/json.rb +1 -0
  36. data/lib/dry/validation/schema/key.rb +23 -10
  37. data/lib/dry/validation/schema/rule.rb +81 -20
  38. data/lib/dry/validation/schema/value.rb +110 -25
  39. data/lib/dry/validation/version.rb +1 -1
  40. data/spec/fixtures/locales/en.yml +1 -0
  41. data/spec/fixtures/locales/pl.yml +1 -1
  42. data/spec/integration/custom_error_messages_spec.rb +2 -2
  43. data/spec/integration/custom_predicates_spec.rb +98 -15
  44. data/spec/integration/error_compiler_spec.rb +111 -96
  45. data/spec/integration/form/predicates/array_spec.rb +243 -0
  46. data/spec/integration/form/predicates/empty_spec.rb +263 -0
  47. data/spec/integration/form/predicates/eql_spec.rb +327 -0
  48. data/spec/integration/form/predicates/even_spec.rb +455 -0
  49. data/spec/integration/form/predicates/excluded_from_spec.rb +455 -0
  50. data/spec/integration/form/predicates/excludes_spec.rb +391 -0
  51. data/spec/integration/form/predicates/false_spec.rb +455 -0
  52. data/spec/integration/form/predicates/filled_spec.rb +467 -0
  53. data/spec/integration/form/predicates/format_spec.rb +454 -0
  54. data/spec/integration/form/predicates/gt_spec.rb +519 -0
  55. data/spec/integration/form/predicates/gteq_spec.rb +519 -0
  56. data/spec/integration/form/predicates/included_in_spec.rb +455 -0
  57. data/spec/integration/form/predicates/includes_spec.rb +391 -0
  58. data/spec/integration/form/predicates/key_spec.rb +75 -0
  59. data/spec/integration/form/predicates/lt_spec.rb +519 -0
  60. data/spec/integration/form/predicates/lteq_spec.rb +519 -0
  61. data/spec/integration/form/predicates/max_size_spec.rb +391 -0
  62. data/spec/integration/form/predicates/min_size_spec.rb +391 -0
  63. data/spec/integration/form/predicates/none_spec.rb +265 -0
  64. data/spec/integration/form/predicates/not_eql_spec.rb +327 -0
  65. data/spec/integration/form/predicates/odd_spec.rb +455 -0
  66. data/spec/integration/form/predicates/size/fixed_spec.rb +399 -0
  67. data/spec/integration/form/predicates/size/range_spec.rb +398 -0
  68. data/spec/integration/form/predicates/true_spec.rb +455 -0
  69. data/spec/integration/form/predicates/type_spec.rb +391 -0
  70. data/spec/integration/hints_spec.rb +90 -4
  71. data/spec/integration/injecting_rules_spec.rb +2 -2
  72. data/spec/integration/localized_error_messages_spec.rb +2 -2
  73. data/spec/integration/messages/i18n_spec.rb +3 -3
  74. data/spec/integration/optional_keys_spec.rb +3 -3
  75. data/spec/integration/schema/array_schema_spec.rb +49 -13
  76. data/spec/integration/schema/check_rules_spec.rb +6 -6
  77. data/spec/integration/schema/check_with_nested_el_spec.rb +3 -3
  78. data/spec/integration/schema/check_with_nth_el_spec.rb +1 -1
  79. data/spec/integration/schema/each_with_set_spec.rb +3 -3
  80. data/spec/integration/schema/extending_dsl_spec.rb +27 -0
  81. data/spec/integration/schema/form/explicit_types_spec.rb +182 -0
  82. data/spec/integration/schema/form_spec.rb +90 -18
  83. data/spec/integration/schema/hash_schema_spec.rb +47 -0
  84. data/spec/integration/schema/inheriting_schema_spec.rb +4 -4
  85. data/spec/integration/schema/input_processor_spec.rb +8 -8
  86. data/spec/integration/schema/json/explicit_types_spec.rb +157 -0
  87. data/spec/integration/schema/json_spec.rb +18 -18
  88. data/spec/integration/schema/macros/confirmation_spec.rb +1 -1
  89. data/spec/integration/schema/macros/each_spec.rb +177 -43
  90. data/spec/integration/schema/macros/{required_spec.rb → filled_spec.rb} +34 -6
  91. data/spec/integration/schema/macros/input_spec.rb +21 -0
  92. data/spec/integration/schema/macros/maybe_spec.rb +39 -2
  93. data/spec/integration/schema/macros/value_spec.rb +105 -0
  94. data/spec/integration/schema/macros/when_spec.rb +28 -8
  95. data/spec/integration/schema/nested_values_spec.rb +11 -8
  96. data/spec/integration/schema/not_spec.rb +2 -2
  97. data/spec/integration/schema/numbers_spec.rb +1 -1
  98. data/spec/integration/schema/option_with_default_spec.rb +1 -1
  99. data/spec/integration/schema/predicate_verification_spec.rb +9 -0
  100. data/spec/integration/schema/predicates/array_spec.rb +295 -0
  101. data/spec/integration/schema/predicates/custom_spec.rb +103 -0
  102. data/spec/integration/schema/predicates/empty_spec.rb +263 -0
  103. data/spec/integration/schema/predicates/eql_spec.rb +327 -0
  104. data/spec/integration/schema/predicates/even_spec.rb +455 -0
  105. data/spec/integration/schema/predicates/excluded_from_spec.rb +455 -0
  106. data/spec/integration/schema/predicates/excludes_spec.rb +391 -0
  107. data/spec/integration/schema/predicates/filled_spec.rb +467 -0
  108. data/spec/integration/schema/predicates/format_spec.rb +455 -0
  109. data/spec/integration/schema/predicates/gt_spec.rb +519 -0
  110. data/spec/integration/schema/predicates/gteq_spec.rb +519 -0
  111. data/spec/integration/schema/predicates/hash_spec.rb +69 -0
  112. data/spec/integration/schema/predicates/included_in_spec.rb +455 -0
  113. data/spec/integration/schema/predicates/includes_spec.rb +391 -0
  114. data/spec/integration/schema/predicates/key_spec.rb +88 -0
  115. data/spec/integration/schema/predicates/lt_spec.rb +520 -0
  116. data/spec/integration/schema/predicates/lteq_spec.rb +519 -0
  117. data/spec/integration/schema/predicates/max_size_spec.rb +391 -0
  118. data/spec/integration/schema/predicates/min_size_spec.rb +391 -0
  119. data/spec/integration/schema/predicates/none_spec.rb +265 -0
  120. data/spec/integration/schema/predicates/not_eql_spec.rb +391 -0
  121. data/spec/integration/schema/predicates/odd_spec.rb +455 -0
  122. data/spec/integration/schema/predicates/size/fixed_spec.rb +401 -0
  123. data/spec/integration/schema/predicates/size/range_spec.rb +399 -0
  124. data/spec/integration/schema/predicates/type_spec.rb +391 -0
  125. data/spec/integration/schema/reusing_schema_spec.rb +4 -4
  126. data/spec/integration/schema/using_types_spec.rb +24 -6
  127. data/spec/integration/schema/xor_spec.rb +2 -2
  128. data/spec/integration/schema_builders_spec.rb +15 -0
  129. data/spec/integration/schema_spec.rb +37 -12
  130. data/spec/shared/predicate_helper.rb +13 -0
  131. data/spec/spec_helper.rb +10 -0
  132. data/spec/support/matchers.rb +38 -0
  133. data/spec/support/predicates_integration.rb +7 -0
  134. data/spec/unit/hint_compiler_spec.rb +10 -8
  135. data/spec/unit/input_processor_compiler/form_spec.rb +45 -43
  136. data/spec/unit/input_processor_compiler/json_spec.rb +37 -35
  137. data/spec/unit/predicate_registry_spec.rb +34 -0
  138. data/spec/unit/schema/key_spec.rb +12 -14
  139. data/spec/unit/schema/rule_spec.rb +4 -2
  140. data/spec/unit/schema/value_spec.rb +38 -121
  141. metadata +150 -16
  142. data/lib/dry/validation/schema/attr.rb +0 -15
  143. data/spec/integration/attr_spec.rb +0 -122
@@ -17,6 +17,9 @@ module Dry
17
17
  setting :lookup_paths, %w(
18
18
  %{root}.rules.%{rule}.%{predicate}.arg.%{arg_type}
19
19
  %{root}.rules.%{rule}.%{predicate}
20
+ %{root}.%{predicate}.%{message_type}
21
+ %{root}.%{predicate}.value.%{rule}.arg.%{arg_type}
22
+ %{root}.%{predicate}.value.%{rule}
20
23
  %{root}.%{predicate}.value.%{val_type}.arg.%{arg_type}
21
24
  %{root}.%{predicate}.value.%{val_type}
22
25
  %{root}.%{predicate}.arg.%{arg_type}
@@ -45,6 +48,10 @@ module Dry
45
48
  @config = self.class.config
46
49
  end
47
50
 
51
+ def hash
52
+ @hash ||= config.hash
53
+ end
54
+
48
55
  def rule(name, options = {})
49
56
  path = "%{locale}.rules.#{name}"
50
57
  get(path, options) if key?(path, options)
@@ -63,7 +70,8 @@ module Dry
63
70
  root: root,
64
71
  predicate: predicate,
65
72
  arg_type: config.arg_types[options[:arg_type]],
66
- val_type: config.val_types[options[:val_type]]
73
+ val_type: config.val_types[options[:val_type]],
74
+ message_type: options[:message_type] || :failure
67
75
  )
68
76
 
69
77
  tokens[:rule] = predicate unless tokens.key?(:rule)
@@ -0,0 +1,101 @@
1
+ module Dry
2
+ module Validation
3
+ class PredicateRegistry
4
+ attr_reader :predicates
5
+ attr_reader :external
6
+
7
+ class Bound < PredicateRegistry
8
+ def initialize(*args)
9
+ super
10
+ freeze
11
+ end
12
+ end
13
+
14
+ class Unbound < PredicateRegistry
15
+ def bind(schema)
16
+ bound_predicates = predicates.each_with_object({}) do |(n, p), res|
17
+ res[n] = p.bind(schema)
18
+ end
19
+ Bound.new(external, bound_predicates)
20
+ end
21
+
22
+ def update(other)
23
+ unbound_predicates = other.each_with_object({}) { |(n, p), res|
24
+ res[n] = Logic::Predicate.new(n, fn: p)
25
+ }
26
+ predicates.update(unbound_predicates)
27
+ self
28
+ end
29
+ end
30
+
31
+ def self.[](klass, predicates)
32
+ Unbound.new(predicates).tap do |registry|
33
+ klass.class_eval do
34
+ def self.method_added(name)
35
+ super
36
+ if name.to_s.end_with?('?')
37
+ registry.update(name => instance_method(name))
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ def initialize(external, predicates = {})
45
+ @external = external
46
+ @predicates = predicates
47
+ end
48
+
49
+ def new(klass)
50
+ new_predicates = predicates
51
+ .keys
52
+ .each_with_object({}) { |key, res| res[key] = klass.instance_method(key) }
53
+
54
+ self.class.new(external).update(new_predicates)
55
+ end
56
+
57
+ def [](name)
58
+ predicates.fetch(name) do
59
+ if external.key?(name)
60
+ external[name]
61
+ else
62
+ raise_unknown_predicate_error(name)
63
+ end
64
+ end
65
+ end
66
+
67
+ def key?(name)
68
+ predicates.key?(name) || external.key?(name)
69
+ end
70
+
71
+ def ensure_valid_predicate(name, args_or_arity)
72
+ if name == :key?
73
+ raise InvalidSchemaError, "#{name} is a reserved predicate name"
74
+ end
75
+
76
+ if key?(name)
77
+ arity = self[name].arity
78
+
79
+ case args_or_arity
80
+ when Array
81
+ raise_invalid_arity_error(name) if ![0, args_or_arity.size + 1].include?(arity)
82
+ when Fixnum
83
+ raise_invalid_arity_error(name) if args_or_arity != arity
84
+ end
85
+ else
86
+ raise_unknown_predicate_error(name)
87
+ end
88
+ end
89
+
90
+ private
91
+
92
+ def raise_unknown_predicate_error(name)
93
+ raise ArgumentError, "+#{name}+ is not a valid predicate name"
94
+ end
95
+
96
+ def raise_invalid_arity_error(name)
97
+ raise ArgumentError, "#{name} predicate arity is invalid"
98
+ end
99
+ end
100
+ end
101
+ end
@@ -45,7 +45,14 @@ module Dry
45
45
  hints = hint_compiler.with(options).call
46
46
  comp = error_compiler.with(options.merge(hints: hints))
47
47
 
48
- comp.(error_ast)
48
+ messages = comp.(error_ast)
49
+ msg_hash = comp.dump_messages(messages)
50
+
51
+ if msg_hash.key?(nil)
52
+ msg_hash.values.flatten
53
+ else
54
+ msg_hash
55
+ end
49
56
  end
50
57
  end
51
58
 
@@ -1,8 +1,9 @@
1
+ require 'dry/logic/predicates'
1
2
  require 'dry/types/constraints'
2
3
 
4
+ require 'dry/validation/predicate_registry'
3
5
  require 'dry/validation/schema_compiler'
4
6
  require 'dry/validation/schema/key'
5
- require 'dry/validation/schema/attr'
6
7
  require 'dry/validation/schema/value'
7
8
  require 'dry/validation/schema/check'
8
9
 
@@ -12,7 +13,7 @@ require 'dry/validation/messages'
12
13
  require 'dry/validation/error_compiler'
13
14
  require 'dry/validation/hint_compiler'
14
15
 
15
- require 'dry/validation/input_processor_compiler'
16
+ require 'dry/validation/schema/deprecated'
16
17
 
17
18
  module Dry
18
19
  module Validation
@@ -22,24 +23,109 @@ module Dry
22
23
  NOOP_INPUT_PROCESSOR = -> input { input }
23
24
 
24
25
  setting :path
25
- setting :predicates, Types::Predicates
26
+ setting :predicates, Logic::Predicates
27
+ setting :registry
26
28
  setting :messages, :yaml
27
29
  setting :messages_file
28
30
  setting :namespace
29
31
  setting :rules, []
30
32
  setting :checks, []
33
+ setting :options, {}
34
+ setting :type_map, {}
35
+ setting :hash_type, :weak
36
+ setting :input, nil
37
+ setting :dsl_extensions, nil
31
38
 
32
39
  setting :input_processor, :noop
33
-
34
40
  setting :input_processor_map, {
35
41
  sanitizer: InputProcessorCompiler::Sanitizer.new,
36
42
  json: InputProcessorCompiler::JSON.new,
37
43
  form: InputProcessorCompiler::Form.new,
38
44
  }.freeze
39
45
 
46
+ setting :type_specs, false
47
+
40
48
  def self.inherited(klass)
41
49
  super
42
- klass.setting :options, {}
50
+ klass.config.options = klass.config.options.dup
51
+
52
+ if registry && self != Schema
53
+ klass.config.registry = registry.new(self)
54
+ else
55
+ klass.set_registry!
56
+ end
57
+ end
58
+
59
+ def self.clone
60
+ klass = Class.new(self)
61
+ klass.config.rules = []
62
+ klass.config.registry = registry
63
+ klass
64
+ end
65
+
66
+ def self.set_registry!
67
+ config.registry = PredicateRegistry[self, config.predicates]
68
+ end
69
+
70
+ def self.registry
71
+ config.registry
72
+ end
73
+
74
+ def self.build_array_type(spec, category)
75
+ member_schema = build_type_map(spec, category)
76
+ member_type = lookup_type("hash", category)
77
+ .public_send(config.hash_type, member_schema)
78
+
79
+ lookup_type("array", category).member(member_type)
80
+ end
81
+
82
+ def self.build_type_map(type_specs, category = config.input_processor)
83
+ if type_specs.is_a?(Array)
84
+ build_array_type(type_specs[0], category)
85
+ else
86
+ type_specs.each_with_object({}) do |(name, spec), result|
87
+ result[name] =
88
+ case spec
89
+ when Hash
90
+ lookup_type("hash", category).public_send(config.hash_type, spec)
91
+ when Array
92
+ if spec.size == 1
93
+ if spec[0].is_a?(Hash)
94
+ build_array_type(spec[0], category)
95
+ else
96
+ lookup_type("array", category).member(lookup_type(spec[0], category))
97
+ end
98
+ else
99
+ spec
100
+ .map { |id| id.is_a?(Symbol) ? lookup_type(id, category) : id }
101
+ .reduce(:|)
102
+ end
103
+ when Symbol
104
+ lookup_type(spec, category)
105
+ else
106
+ spec
107
+ end
108
+ end
109
+ end
110
+ end
111
+
112
+ def self.lookup_type(name, category)
113
+ id = "#{category}.#{name}"
114
+ Types.type_keys.include?(id) ? Types[id] : Types[name.to_s]
115
+ end
116
+
117
+
118
+ def self.type_map
119
+ config.type_map
120
+ end
121
+
122
+ def self.predicates(predicate_set = nil)
123
+ if predicate_set
124
+ config.predicates = predicate_set
125
+ set_registry!
126
+ else
127
+ config.predicates
128
+ end
43
129
  end
44
130
 
45
131
  def self.new(rules = config.rules, **options)
@@ -52,8 +138,10 @@ module Dry
52
138
  Class.new(other.class)
53
139
  elsif other.is_a?(Class) && other < Types::Struct
54
140
  Validation.Schema(parent: target, build: false) do
55
- other.schema.each { |attr, type| key(attr).required(type) }
141
+ other.schema.each { |attr, type| required(attr).filled(type) }
56
142
  end
143
+ elsif other.respond_to?(:schema) && other.schema.is_a?(self)
144
+ Class.new(other.schema.class)
57
145
  else
58
146
  Validation.Schema(target.schema_class, parent: target, build: false, &block)
59
147
  end
@@ -77,10 +165,6 @@ module Dry
77
165
  config.rules
78
166
  end
79
167
 
80
- def self.predicates
81
- config.predicates
82
- end
83
-
84
168
  def self.options
85
169
  config.options
86
170
  end
@@ -116,31 +200,12 @@ module Dry
116
200
  @hint_compiler ||= HintCompiler.new(messages, rules: rule_ast)
117
201
  end
118
202
 
119
- def self.input_processor
120
- @input_processor ||=
121
- begin
122
- if input_processor_compiler
123
- input_processor_compiler.(rule_ast)
124
- else
125
- NOOP_INPUT_PROCESSOR
126
- end
127
- end
128
- end
129
-
130
- def self.input_processor_ast(type)
131
- config.input_processor_map.fetch(type).schema_ast(rule_ast)
132
- end
133
-
134
- def self.input_processor_compiler
135
- @input_processor_comp ||= config.input_processor_map[config.input_processor]
136
- end
137
-
138
203
  def self.rule_ast
139
- @rule_ast ||= config.rules.flat_map(&:rules).map(&:to_ast)
204
+ @rule_ast ||= config.rules.map(&:to_ast)
140
205
  end
141
206
 
142
207
  def self.default_options
143
- { predicates: predicates,
208
+ { predicate_registry: registry,
144
209
  error_compiler: error_compiler,
145
210
  hint_compiler: hint_compiler,
146
211
  input_processor: input_processor,
@@ -163,11 +228,14 @@ module Dry
163
228
 
164
229
  attr_reader :options
165
230
 
231
+ attr_reader :type_map
232
+
166
233
  def initialize(rules, options)
167
- @rule_compiler = SchemaCompiler.new(self)
234
+ @type_map = self.class.type_map
235
+ @predicates = options.fetch(:predicate_registry).bind(self)
236
+ @rule_compiler = SchemaCompiler.new(predicates)
168
237
  @error_compiler = options.fetch(:error_compiler)
169
238
  @hint_compiler = options.fetch(:hint_compiler)
170
- @predicates = options.fetch(:predicates)
171
239
  @input_processor = options.fetch(:input_processor, NOOP_INPUT_PROCESSOR)
172
240
 
173
241
  initialize_options(options)
@@ -186,22 +254,20 @@ module Dry
186
254
  Result.new(processed_input, apply(processed_input), error_compiler, hint_compiler)
187
255
  end
188
256
 
189
- def [](name)
190
- if predicates.key?(name)
191
- predicates[name]
192
- elsif respond_to?(name)
193
- Logic::Predicate.new(name, &method(name))
194
- else
195
- raise ArgumentError, "+#{name}+ is not a valid predicate name"
196
- end
197
- end
198
-
199
257
  def curry(*args)
200
258
  to_proc.curry.(*args)
201
259
  end
202
260
 
203
261
  def to_proc
204
- -> input { self.call(input) }
262
+ -> input { call(input) }
263
+ end
264
+
265
+ def arity
266
+ 1
267
+ end
268
+
269
+ def to_ast
270
+ self.class.to_ast
205
271
  end
206
272
 
207
273
  private
@@ -22,9 +22,11 @@ module Dry
22
22
  vals, args = meth_args.partition { |arg| arg.class < DSL }
23
23
 
24
24
  keys = [name, *vals.map(&:name)]
25
- predicate = [:predicate, [meth, args]]
26
25
 
27
- rule = create_rule([:check, [name, predicate, keys]])
26
+ registry.ensure_valid_predicate(meth, args.size + keys.size)
27
+ predicate = registry[meth].curry(*args)
28
+
29
+ rule = create_rule([:check, [name, predicate.to_ast, keys]])
28
30
  add_rule(rule)
29
31
  rule
30
32
  end
@@ -0,0 +1,31 @@
1
+ require 'dry/validation/input_processor_compiler'
2
+
3
+ module Dry
4
+ module Validation
5
+ class Schema
6
+ def self.input_processor
7
+ @input_processor ||=
8
+ begin
9
+ if type_map.is_a?(Dry::Types::Safe) && config.input_processor != :noop
10
+ type_map
11
+ elsif type_map.size > 0 && config.input_processor != :noop
12
+ lookup_type("hash", config.input_processor)
13
+ .public_send(config.hash_type, type_map)
14
+ elsif input_processor_compiler
15
+ input_processor_compiler.(rule_ast)
16
+ else
17
+ NOOP_INPUT_PROCESSOR
18
+ end
19
+ end
20
+ end
21
+
22
+ def self.input_processor_ast(type)
23
+ config.input_processor_map.fetch(type).schema_ast(rule_ast)
24
+ end
25
+
26
+ def self.input_processor_compiler
27
+ @input_processor_comp ||= config.input_processor_map[config.input_processor]
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,10 +1,13 @@
1
1
  require 'dry/validation/schema/rule'
2
+ require 'dry/validation/deprecations'
2
3
 
3
4
  module Dry
4
5
  module Validation
5
6
  class Schema
6
7
  class DSL < BasicObject
7
- attr_reader :name, :rules, :checks, :parent, :options
8
+ include ::Dry::Validation::Deprecations
9
+
10
+ attr_reader :name, :registry, :rules, :checks, :parent, :options
8
11
 
9
12
  def self.[](name, options = {})
10
13
  new(options.merge(name: name))
@@ -13,6 +16,7 @@ module Dry
13
16
  def initialize(options = {})
14
17
  @name = options[:name]
15
18
  @parent = options[:parent]
19
+ @registry = options.fetch(:registry)
16
20
  @rules = options.fetch(:rules, [])
17
21
  @checks = options.fetch(:checks, [])
18
22
  @options = options
@@ -23,16 +27,14 @@ module Dry
23
27
  end
24
28
  alias_method :to_s, :inspect
25
29
 
26
- def key(name, &block)
27
- define(name, Key, &block)
28
- end
30
+ def optional(name, type_spec = nil, &block)
31
+ rule = define(name, Key, :then, &block)
29
32
 
30
- def optional(name, &block)
31
- define(name, Key, :then, &block)
32
- end
33
+ if type_spec
34
+ type_map[name] = type_spec
35
+ end
33
36
 
34
- def attr(name, &block)
35
- define(name, Attr, &block)
37
+ rule
36
38
  end
37
39
 
38
40
  def not
@@ -73,17 +75,22 @@ module Dry
73
75
  self.class.new(options.merge(new_options))
74
76
  end
75
77
 
78
+ def predicate?(meth)
79
+ registry.key?(meth)
80
+ end
81
+
76
82
  private
77
83
 
78
84
  def define(name, key_class, op = :and, &block)
79
85
  type = key_class.type
80
86
 
81
87
  val = Value[
82
- name, type: type, parent: self, rules: rules, checks: checks
88
+ name, registry: registry, type: type, parent: self, rules: rules,
89
+ checks: checks, schema_class: schema_class.clone
83
90
  ].__send__(:"#{type}?", name)
84
91
 
85
92
  if block
86
- key = key_class[name]
93
+ key = key_class[name, registry: registry]
87
94
  res = key.instance_eval(&block)
88
95
 
89
96
  if res.class == Value