dry-schema 1.2.0 → 1.4.1
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 +214 -100
- data/README.md +4 -2
- data/config/errors.yml +9 -0
- data/lib/dry/schema/constants.rb +3 -2
- data/lib/dry/schema/dsl.rb +64 -17
- data/lib/dry/schema/extensions/hints/message_set_methods.rb +48 -1
- data/lib/dry/schema/macros/dsl.rb +22 -11
- data/lib/dry/schema/macros/filled.rb +0 -7
- data/lib/dry/schema/macros/key.rb +0 -1
- data/lib/dry/schema/macros/schema.rb +18 -7
- data/lib/dry/schema/macros/value.rb +21 -2
- data/lib/dry/schema/message.rb +1 -1
- data/lib/dry/schema/message_compiler.rb +2 -1
- data/lib/dry/schema/messages/abstract.rb +42 -12
- data/lib/dry/schema/messages/i18n.rb +9 -0
- data/lib/dry/schema/messages/namespaced.rb +6 -1
- data/lib/dry/schema/messages/yaml.rb +12 -0
- data/lib/dry/schema/namespaced_rule.rb +2 -1
- data/lib/dry/schema/path.rb +10 -6
- data/lib/dry/schema/predicate_inferrer.rb +69 -16
- data/lib/dry/schema/predicate_registry.rb +2 -23
- data/lib/dry/schema/processor.rb +9 -26
- data/lib/dry/schema/processor_steps.rb +116 -0
- data/lib/dry/schema/trace.rb +15 -20
- data/lib/dry/schema/type_registry.rb +2 -2
- data/lib/dry/schema/version.rb +1 -1
- metadata +5 -5
- data/lib/dry/schema/primitive_inferrer.rb +0 -98
@@ -0,0 +1,116 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/initializer'
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module Schema
|
7
|
+
# Steps for the Dry::Schema::Processor
|
8
|
+
#
|
9
|
+
# There are 4 main steps:
|
10
|
+
#
|
11
|
+
# 1. `key_coercer` - Prepare input hash using a key map
|
12
|
+
# 2. `filter_schema` - Apply pre-coercion filtering rules
|
13
|
+
# (optional step, used only when `filter` was used)
|
14
|
+
# 3. `value_coercer` - Apply value coercions based on type specifications
|
15
|
+
# 4. `rule_applier` - Apply rules
|
16
|
+
#
|
17
|
+
# @see Processor
|
18
|
+
#
|
19
|
+
# @api public
|
20
|
+
class ProcessorSteps
|
21
|
+
extend Dry::Initializer
|
22
|
+
|
23
|
+
STEPS_IN_ORDER = %i[key_coercer filter_schema value_coercer rule_applier].freeze
|
24
|
+
|
25
|
+
option :steps, default: -> { EMPTY_HASH.dup }
|
26
|
+
option :before_steps, default: -> { EMPTY_HASH.dup }
|
27
|
+
option :after_steps, default: -> { EMPTY_HASH.dup }
|
28
|
+
|
29
|
+
# Executes steps and callbacks in order
|
30
|
+
#
|
31
|
+
# @param [Result] result
|
32
|
+
#
|
33
|
+
# @return [Result]
|
34
|
+
#
|
35
|
+
# @api public
|
36
|
+
def call(result)
|
37
|
+
STEPS_IN_ORDER.each do |name|
|
38
|
+
before_steps[name]&.each { |step| process_step(step, result) }
|
39
|
+
process_step(steps[name], result)
|
40
|
+
after_steps[name]&.each { |step| process_step(step, result) }
|
41
|
+
end
|
42
|
+
result
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns step by name
|
46
|
+
#
|
47
|
+
# @param [Symbol] name The step name
|
48
|
+
#
|
49
|
+
# @api public
|
50
|
+
def [](name)
|
51
|
+
steps[name]
|
52
|
+
end
|
53
|
+
|
54
|
+
# Sets step by name
|
55
|
+
#
|
56
|
+
# @param [Symbol] name The step name
|
57
|
+
#
|
58
|
+
# @api public
|
59
|
+
def []=(name, value)
|
60
|
+
validate_step_name(name)
|
61
|
+
steps[name] = value
|
62
|
+
end
|
63
|
+
|
64
|
+
# Add passed block before mentioned step
|
65
|
+
#
|
66
|
+
# @param [Symbol] name The step name
|
67
|
+
#
|
68
|
+
# @return [ProcessorSteps]
|
69
|
+
#
|
70
|
+
# @api public
|
71
|
+
def after(name, &block)
|
72
|
+
validate_step_name(name)
|
73
|
+
after_steps[name] ||= EMPTY_ARRAY.dup
|
74
|
+
after_steps[name] << block.to_proc
|
75
|
+
self
|
76
|
+
end
|
77
|
+
|
78
|
+
# Add passed block before mentioned step
|
79
|
+
#
|
80
|
+
# @param [Symbol] name The step name
|
81
|
+
#
|
82
|
+
# @return [ProcessorSteps]
|
83
|
+
#
|
84
|
+
# @api public
|
85
|
+
def before(name, &block)
|
86
|
+
validate_step_name(name)
|
87
|
+
before_steps[name] ||= EMPTY_ARRAY.dup
|
88
|
+
before_steps[name] << block.to_proc
|
89
|
+
self
|
90
|
+
end
|
91
|
+
|
92
|
+
# @api private
|
93
|
+
def process_step(step, result)
|
94
|
+
return unless step
|
95
|
+
|
96
|
+
output = step.(result)
|
97
|
+
result.replace(output) if output.is_a?(::Hash)
|
98
|
+
end
|
99
|
+
|
100
|
+
# @api private
|
101
|
+
def validate_step_name(name)
|
102
|
+
return if STEPS_IN_ORDER.include?(name)
|
103
|
+
|
104
|
+
raise ArgumentError, "Undefined step name #{name}. Available names: #{STEPS_IN_ORDER}"
|
105
|
+
end
|
106
|
+
|
107
|
+
# @api private
|
108
|
+
def initialize_copy(source)
|
109
|
+
super
|
110
|
+
@steps = source.steps.dup
|
111
|
+
@before_steps = source.before_steps.dup
|
112
|
+
@after_steps = source.after_steps.dup
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
data/lib/dry/schema/trace.rb
CHANGED
@@ -29,33 +29,28 @@ module Dry
|
|
29
29
|
end
|
30
30
|
|
31
31
|
# @api private
|
32
|
-
def evaluate(*
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
predicates.each do |predicate|
|
37
|
-
if predicate.respond_to?(:call)
|
38
|
-
append(predicate)
|
39
|
-
elsif predicate.is_a?(::Hash)
|
40
|
-
evaluate_hash_predicates(predicate)
|
41
|
-
elsif predicate.is_a?(::Array)
|
42
|
-
append(predicate.map { |pred| __send__(pred) }.reduce(:|))
|
43
|
-
else
|
44
|
-
append(__send__(predicate))
|
45
|
-
end
|
32
|
+
def evaluate(*args, type_spec: ::Dry::Schema::Undefined, **opts)
|
33
|
+
predicates = opts.empty? ? args : args.push(opts)
|
34
|
+
evaluate_predicates(predicates).each do |rule|
|
35
|
+
append(rule)
|
46
36
|
end
|
47
37
|
|
48
|
-
evaluate_hash_predicates(pred_opts)
|
49
|
-
|
50
38
|
self
|
51
39
|
end
|
52
40
|
|
53
41
|
# @api private
|
54
|
-
def
|
55
|
-
predicates.
|
56
|
-
|
42
|
+
def evaluate_predicates(predicates)
|
43
|
+
predicates.flat_map do |predicate|
|
44
|
+
if predicate.respond_to?(:call)
|
45
|
+
predicate
|
46
|
+
elsif predicate.is_a?(::Array)
|
47
|
+
predicate.map { |pred| evaluate_predicates(pred).reduce(:&) }.reduce(:|)
|
48
|
+
elsif predicate.is_a?(::Hash)
|
49
|
+
predicate.map { |pred, *args| __send__(pred, *args) }
|
50
|
+
else
|
51
|
+
__send__(predicate)
|
52
|
+
end
|
57
53
|
end
|
58
|
-
self
|
59
54
|
end
|
60
55
|
|
61
56
|
# @api private
|
@@ -18,12 +18,12 @@ module Dry
|
|
18
18
|
attr_reader :namespace
|
19
19
|
|
20
20
|
# @api private
|
21
|
-
def self.new(types = Dry::Types, namespace = :
|
21
|
+
def self.new(types = Dry::Types, namespace = :strict)
|
22
22
|
super
|
23
23
|
end
|
24
24
|
|
25
25
|
# @api private
|
26
|
-
def initialize(types, namespace = :
|
26
|
+
def initialize(types, namespace = :strict)
|
27
27
|
@types = types
|
28
28
|
@namespace = namespace
|
29
29
|
end
|
data/lib/dry/schema/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dry-schema
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Piotr Solnica
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-10-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -106,14 +106,14 @@ dependencies:
|
|
106
106
|
requirements:
|
107
107
|
- - "~>"
|
108
108
|
- !ruby/object:Gem::Version
|
109
|
-
version: '1.
|
109
|
+
version: '1.2'
|
110
110
|
type: :runtime
|
111
111
|
prerelease: false
|
112
112
|
version_requirements: !ruby/object:Gem::Requirement
|
113
113
|
requirements:
|
114
114
|
- - "~>"
|
115
115
|
- !ruby/object:Gem::Version
|
116
|
-
version: '1.
|
116
|
+
version: '1.2'
|
117
117
|
- !ruby/object:Gem::Dependency
|
118
118
|
name: bundler
|
119
119
|
requirement: !ruby/object:Gem::Requirement
|
@@ -217,8 +217,8 @@ files:
|
|
217
217
|
- lib/dry/schema/predicate.rb
|
218
218
|
- lib/dry/schema/predicate_inferrer.rb
|
219
219
|
- lib/dry/schema/predicate_registry.rb
|
220
|
-
- lib/dry/schema/primitive_inferrer.rb
|
221
220
|
- lib/dry/schema/processor.rb
|
221
|
+
- lib/dry/schema/processor_steps.rb
|
222
222
|
- lib/dry/schema/result.rb
|
223
223
|
- lib/dry/schema/rule_applier.rb
|
224
224
|
- lib/dry/schema/trace.rb
|
@@ -1,98 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'dry/core/cache'
|
4
|
-
|
5
|
-
module Dry
|
6
|
-
module Schema
|
7
|
-
# PrimitiveInferrer is used internally by `Macros::Filled`
|
8
|
-
# for inferring a list of possible primitives that a given
|
9
|
-
# type can handle.
|
10
|
-
#
|
11
|
-
# @api private
|
12
|
-
class PrimitiveInferrer
|
13
|
-
extend Dry::Core::Cache
|
14
|
-
|
15
|
-
# Compiler reduces type AST into a list of primitives
|
16
|
-
#
|
17
|
-
# @api private
|
18
|
-
class Compiler
|
19
|
-
# @api private
|
20
|
-
def visit(node)
|
21
|
-
meth, rest = node
|
22
|
-
public_send(:"visit_#{meth}", rest)
|
23
|
-
end
|
24
|
-
|
25
|
-
# @api private
|
26
|
-
def visit_nominal(node)
|
27
|
-
type, _ = node
|
28
|
-
type
|
29
|
-
end
|
30
|
-
|
31
|
-
# @api private
|
32
|
-
def visit_hash(_)
|
33
|
-
Hash
|
34
|
-
end
|
35
|
-
|
36
|
-
# @api private
|
37
|
-
def visit_array(_)
|
38
|
-
Array
|
39
|
-
end
|
40
|
-
|
41
|
-
# @api private
|
42
|
-
def visit_lax(node)
|
43
|
-
visit(node)
|
44
|
-
end
|
45
|
-
|
46
|
-
# @api private
|
47
|
-
def visit_constructor(node)
|
48
|
-
other, * = node
|
49
|
-
visit(other)
|
50
|
-
end
|
51
|
-
|
52
|
-
# @api private
|
53
|
-
def visit_enum(node)
|
54
|
-
other, * = node
|
55
|
-
visit(other)
|
56
|
-
end
|
57
|
-
|
58
|
-
# @api private
|
59
|
-
def visit_sum(node)
|
60
|
-
left, right = node
|
61
|
-
|
62
|
-
[visit(left), visit(right)].flatten(1)
|
63
|
-
end
|
64
|
-
|
65
|
-
# @api private
|
66
|
-
def visit_constrained(node)
|
67
|
-
other, * = node
|
68
|
-
visit(other)
|
69
|
-
end
|
70
|
-
|
71
|
-
# @api private
|
72
|
-
def visit_any(_)
|
73
|
-
Object
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
# @return [Compiler]
|
78
|
-
# @api private
|
79
|
-
attr_reader :compiler
|
80
|
-
|
81
|
-
# @api private
|
82
|
-
def initialize
|
83
|
-
@compiler = Compiler.new
|
84
|
-
end
|
85
|
-
|
86
|
-
# Infer predicate identifier from the provided type
|
87
|
-
#
|
88
|
-
# @return [Symbol]
|
89
|
-
#
|
90
|
-
# @api private
|
91
|
-
def [](type)
|
92
|
-
self.class.fetch_or_store(type.hash) do
|
93
|
-
Array(compiler.visit(type.to_ast)).freeze
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|