dry-schema 0.3.0 → 0.4.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +31 -0
- data/config/errors.yml +1 -1
- data/lib/dry/schema.rb +0 -9
- data/lib/dry/schema/config.rb +21 -27
- data/lib/dry/schema/constants.rb +17 -3
- data/lib/dry/schema/dsl.rb +13 -22
- data/lib/dry/schema/extensions/hints/message_set_methods.rb +2 -7
- data/lib/dry/schema/extensions/hints/result_methods.rb +3 -3
- data/lib/dry/schema/extensions/monads.rb +1 -1
- data/lib/dry/schema/macros/hash.rb +0 -1
- data/lib/dry/schema/macros/key.rb +13 -8
- data/lib/dry/schema/macros/optional.rb +5 -0
- data/lib/dry/schema/macros/schema.rb +1 -1
- data/lib/dry/schema/message.rb +3 -8
- data/lib/dry/schema/message_compiler.rb +47 -42
- data/lib/dry/schema/message_compiler/visitor_opts.rb +0 -1
- data/lib/dry/schema/message_set.rb +13 -1
- data/lib/dry/schema/messages.rb +16 -25
- data/lib/dry/schema/messages/abstract.rb +53 -36
- data/lib/dry/schema/messages/i18n.rb +40 -23
- data/lib/dry/schema/messages/namespaced.rb +3 -4
- data/lib/dry/schema/messages/yaml.rb +54 -33
- data/lib/dry/schema/predicate_inferrer.rb +56 -14
- data/lib/dry/schema/processor.rb +59 -40
- data/lib/dry/schema/result.rb +2 -2
- data/lib/dry/schema/rule_applier.rb +5 -4
- data/lib/dry/schema/trace.rb +11 -3
- data/lib/dry/schema/type_registry.rb +2 -2
- data/lib/dry/schema/types.rb +1 -1
- data/lib/dry/schema/value_coercer.rb +5 -3
- data/lib/dry/schema/version.rb +1 -1
- metadata +15 -15
@@ -12,6 +12,7 @@ module Dry
|
|
12
12
|
extend Dry::Core::Cache
|
13
13
|
|
14
14
|
TYPE_TO_PREDICATE = {
|
15
|
+
DateTime => :date_time?,
|
15
16
|
FalseClass => :false?,
|
16
17
|
Integer => :int?,
|
17
18
|
NilClass => :nil?,
|
@@ -27,38 +28,68 @@ module Dry
|
|
27
28
|
#
|
28
29
|
# @api private
|
29
30
|
class Compiler
|
31
|
+
# @!attribute [r] registry
|
32
|
+
# @return [PredicateRegistry]
|
33
|
+
# @api private
|
34
|
+
attr_reader :registry
|
35
|
+
|
36
|
+
# @api private
|
37
|
+
def initialize(registry)
|
38
|
+
@registry = registry
|
39
|
+
end
|
40
|
+
|
41
|
+
# @api private
|
42
|
+
def infer_predicate(type)
|
43
|
+
TYPE_TO_PREDICATE.fetch(type) { :"#{type.name.split('::').last.downcase}?" }
|
44
|
+
end
|
45
|
+
|
46
|
+
# @api private
|
30
47
|
def visit(node)
|
31
48
|
meth, rest = node
|
32
49
|
public_send(:"visit_#{meth}", rest)
|
33
50
|
end
|
34
51
|
|
35
|
-
|
52
|
+
# @api private
|
53
|
+
def visit_nominal(node)
|
36
54
|
type = node[0]
|
55
|
+
predicate = infer_predicate(type)
|
37
56
|
|
38
|
-
|
39
|
-
|
40
|
-
|
57
|
+
if registry.key?(predicate)
|
58
|
+
predicate
|
59
|
+
else
|
60
|
+
{ type?: type }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# @api private
|
65
|
+
def visit_hash(_)
|
66
|
+
:hash?
|
41
67
|
end
|
42
68
|
|
43
|
-
|
69
|
+
# @api private
|
70
|
+
def visit_array(_)
|
44
71
|
:array?
|
45
72
|
end
|
46
73
|
|
74
|
+
# @api private
|
47
75
|
def visit_safe(node)
|
48
76
|
other, * = node
|
49
77
|
visit(other)
|
50
78
|
end
|
51
79
|
|
80
|
+
# @api private
|
52
81
|
def visit_constructor(node)
|
53
82
|
other, * = node
|
54
83
|
visit(other)
|
55
84
|
end
|
56
85
|
|
86
|
+
# @api private
|
57
87
|
def visit_enum(node)
|
58
88
|
other, * = node
|
59
89
|
visit(other)
|
60
90
|
end
|
61
91
|
|
92
|
+
# @api private
|
62
93
|
def visit_sum(node)
|
63
94
|
left, right = node
|
64
95
|
|
@@ -71,27 +102,38 @@ module Dry
|
|
71
102
|
end
|
72
103
|
end
|
73
104
|
|
105
|
+
# @api private
|
74
106
|
def visit_constrained(node)
|
75
107
|
other, * = node
|
76
108
|
visit(other)
|
77
109
|
end
|
78
110
|
end
|
79
111
|
|
112
|
+
# @!attribute [r] compiler
|
113
|
+
# @return [Compiler]
|
114
|
+
# @api private
|
115
|
+
attr_reader :compiler
|
116
|
+
|
117
|
+
# @api private
|
118
|
+
def initialize(registry)
|
119
|
+
@compiler = Compiler.new(registry)
|
120
|
+
end
|
121
|
+
|
80
122
|
# Infer predicate identifier from the provided type
|
81
123
|
#
|
82
124
|
# @return [Symbol]
|
83
125
|
#
|
84
126
|
# @api private
|
85
|
-
def
|
86
|
-
fetch_or_store(type.hash)
|
87
|
-
predicates =
|
88
|
-
Array(REDUCED_TYPES[predicates] || predicates).flatten
|
89
|
-
}
|
90
|
-
end
|
127
|
+
def [](type)
|
128
|
+
self.class.fetch_or_store(type.hash) do
|
129
|
+
predicates = compiler.visit(type.to_ast)
|
91
130
|
|
92
|
-
|
93
|
-
|
94
|
-
|
131
|
+
if predicates.is_a?(Hash)
|
132
|
+
predicates
|
133
|
+
else
|
134
|
+
Array(REDUCED_TYPES[predicates] || predicates).flatten
|
135
|
+
end
|
136
|
+
end
|
95
137
|
end
|
96
138
|
end
|
97
139
|
end
|
data/lib/dry/schema/processor.rb
CHANGED
@@ -32,42 +32,41 @@ module Dry
|
|
32
32
|
|
33
33
|
param :steps, default: -> { EMPTY_ARRAY.dup }
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
@
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
@__definition__ ||= nil
|
57
|
-
end
|
35
|
+
class << self
|
36
|
+
# @!attribute [r] definition
|
37
|
+
# Return DSL configured via #define
|
38
|
+
# @return [DSL]
|
39
|
+
# @api private
|
40
|
+
attr_reader :definition
|
41
|
+
|
42
|
+
# Define a schema for your processor class
|
43
|
+
#
|
44
|
+
# @see Params
|
45
|
+
# @see JSON
|
46
|
+
#
|
47
|
+
# @return [Class]
|
48
|
+
#
|
49
|
+
# @api public
|
50
|
+
def define(&block)
|
51
|
+
@definition ||= DSL.new(
|
52
|
+
processor_type: self, parent: superclass.definition, **config, &block
|
53
|
+
)
|
54
|
+
self
|
55
|
+
end
|
58
56
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
57
|
+
# Build a new processor object
|
58
|
+
#
|
59
|
+
# @return [Processor]
|
60
|
+
#
|
61
|
+
# @api public
|
62
|
+
def new(&block)
|
63
|
+
if block
|
64
|
+
super.tap(&block)
|
65
|
+
elsif definition
|
66
|
+
definition.call
|
67
|
+
else
|
68
|
+
raise ArgumentError, 'Cannot create a schema without a definition'
|
69
|
+
end
|
71
70
|
end
|
72
71
|
end
|
73
72
|
|
@@ -96,6 +95,16 @@ module Dry
|
|
96
95
|
end
|
97
96
|
end
|
98
97
|
end
|
98
|
+
alias_method :[], :call
|
99
|
+
|
100
|
+
# Return a proc that acts like a schema object
|
101
|
+
#
|
102
|
+
# @return [Proc]
|
103
|
+
#
|
104
|
+
# @api public
|
105
|
+
def to_proc
|
106
|
+
->(input) { call(input) }
|
107
|
+
end
|
99
108
|
|
100
109
|
# Return the key map
|
101
110
|
#
|
@@ -103,7 +112,18 @@ module Dry
|
|
103
112
|
#
|
104
113
|
# @api public
|
105
114
|
def key_map
|
106
|
-
@
|
115
|
+
@key_map ||= steps.detect { |s| s.is_a?(KeyCoercer) }.key_map
|
116
|
+
end
|
117
|
+
|
118
|
+
# Return string represntation
|
119
|
+
#
|
120
|
+
# @return [String]
|
121
|
+
#
|
122
|
+
# @api public
|
123
|
+
def inspect
|
124
|
+
<<~STR.strip
|
125
|
+
#<#{self.class.name} keys=#{key_map.map(&:dump)} rules=#{rules.map { |k, v| [k, v.to_s] }.to_h}>
|
126
|
+
STR
|
107
127
|
end
|
108
128
|
|
109
129
|
# Return the type schema
|
@@ -112,7 +132,7 @@ module Dry
|
|
112
132
|
#
|
113
133
|
# @api private
|
114
134
|
def type_schema
|
115
|
-
@
|
135
|
+
@type_schema ||= steps.detect { |s| s.is_a?(ValueCoercer) }.type_schema
|
116
136
|
end
|
117
137
|
|
118
138
|
# Return the rules config
|
@@ -121,7 +141,7 @@ module Dry
|
|
121
141
|
#
|
122
142
|
# @api private
|
123
143
|
def config
|
124
|
-
@
|
144
|
+
@config ||= rule_applier.config
|
125
145
|
end
|
126
146
|
|
127
147
|
# Return AST representation of the rules
|
@@ -153,8 +173,7 @@ module Dry
|
|
153
173
|
#
|
154
174
|
# @api private
|
155
175
|
def rule_applier
|
156
|
-
|
157
|
-
@__rule_applier__ ||= steps.last
|
176
|
+
@rule_applier ||= steps.last
|
158
177
|
end
|
159
178
|
alias_method :to_rule, :rule_applier
|
160
179
|
end
|
data/lib/dry/schema/result.rb
CHANGED
@@ -107,7 +107,7 @@ module Dry
|
|
107
107
|
#
|
108
108
|
# @api public
|
109
109
|
def errors(options = EMPTY_HASH)
|
110
|
-
message_set(options)
|
110
|
+
message_set(options)
|
111
111
|
end
|
112
112
|
|
113
113
|
# Return the message set
|
@@ -130,7 +130,7 @@ module Dry
|
|
130
130
|
#
|
131
131
|
# @api public
|
132
132
|
def inspect
|
133
|
-
"#<#{self.class}#{to_h.inspect} errors=#{errors.inspect}>"
|
133
|
+
"#<#{self.class}#{to_h.inspect} errors=#{errors.to_h.inspect}>"
|
134
134
|
end
|
135
135
|
|
136
136
|
private
|
@@ -20,10 +20,10 @@ module Dry
|
|
20
20
|
param :rules
|
21
21
|
|
22
22
|
# @api private
|
23
|
-
option :config, default:
|
23
|
+
option :config, default: -> { Config.new }
|
24
24
|
|
25
25
|
# @api private
|
26
|
-
option :message_compiler, default:
|
26
|
+
option :message_compiler, default: -> { MessageCompiler.new(Messages.setup(config.messages)) }
|
27
27
|
|
28
28
|
# @api private
|
29
29
|
def call(input)
|
@@ -31,6 +31,7 @@ module Dry
|
|
31
31
|
|
32
32
|
rules.each do |name, rule|
|
33
33
|
next if input.error?(name)
|
34
|
+
|
34
35
|
result = rule.(input)
|
35
36
|
results << result if result.failure?
|
36
37
|
end
|
@@ -40,8 +41,8 @@ module Dry
|
|
40
41
|
|
41
42
|
# @api private
|
42
43
|
def to_ast
|
43
|
-
if config.namespace
|
44
|
-
[:namespace, [config.namespace, [:set, rules.values.map(&:to_ast)]]]
|
44
|
+
if config.messages.namespace
|
45
|
+
[:namespace, [config.messages.namespace, [:set, rules.values.map(&:to_ast)]]]
|
45
46
|
else
|
46
47
|
[:set, rules.values.map(&:to_ast)]
|
47
48
|
end
|
data/lib/dry/schema/trace.rb
CHANGED
@@ -29,13 +29,15 @@ module Dry
|
|
29
29
|
end
|
30
30
|
|
31
31
|
# @api private
|
32
|
-
def evaluate(*predicates, **opts
|
32
|
+
def evaluate(*predicates, **opts)
|
33
33
|
pred_opts = opts.dup
|
34
34
|
pred_opts.delete(:type_spec)
|
35
35
|
|
36
36
|
predicates.each do |predicate|
|
37
37
|
if predicate.respond_to?(:call)
|
38
38
|
append(predicate)
|
39
|
+
elsif predicate.is_a?(::Hash)
|
40
|
+
evaluate_hash_predicates(predicate)
|
39
41
|
elsif predicate.is_a?(::Array)
|
40
42
|
append(predicate.map { |pred| __send__(pred) }.reduce(:|))
|
41
43
|
else
|
@@ -43,10 +45,16 @@ module Dry
|
|
43
45
|
end
|
44
46
|
end
|
45
47
|
|
46
|
-
pred_opts
|
48
|
+
evaluate_hash_predicates(pred_opts)
|
49
|
+
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
# @api private
|
54
|
+
def evaluate_hash_predicates(predicates)
|
55
|
+
predicates.each do |predicate, *args|
|
47
56
|
append(__send__(predicate, *args))
|
48
57
|
end
|
49
|
-
|
50
58
|
self
|
51
59
|
end
|
52
60
|
|
@@ -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 = :nominal)
|
22
22
|
super
|
23
23
|
end
|
24
24
|
|
25
25
|
# @api private
|
26
|
-
def initialize(types, namespace =
|
26
|
+
def initialize(types, namespace = :nominal)
|
27
27
|
@types = types
|
28
28
|
@namespace = namespace
|
29
29
|
end
|
data/lib/dry/schema/types.rb
CHANGED
@@ -20,9 +20,11 @@ module Dry
|
|
20
20
|
if input.success?
|
21
21
|
type_schema[Hash(input)]
|
22
22
|
else
|
23
|
-
type_schema.
|
24
|
-
|
25
|
-
|
23
|
+
type_schema.each_with_object(EMPTY_HASH.dup) do |key, hash|
|
24
|
+
name = key.name
|
25
|
+
value = input[name]
|
26
|
+
|
27
|
+
hash[name] = input.error?(name) ? value : key[value]
|
26
28
|
end
|
27
29
|
end
|
28
30
|
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: 0.
|
4
|
+
version: 0.4.0
|
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-03-
|
11
|
+
date: 2019-03-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -28,22 +28,22 @@ dependencies:
|
|
28
28
|
name: dry-configurable
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "~>"
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0.1'
|
34
31
|
- - ">="
|
35
32
|
- !ruby/object:Gem::Version
|
36
|
-
version: 0.
|
33
|
+
version: 0.8.0
|
34
|
+
- - "~>"
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '0.8'
|
37
37
|
type: :runtime
|
38
38
|
prerelease: false
|
39
39
|
version_requirements: !ruby/object:Gem::Requirement
|
40
40
|
requirements:
|
41
|
-
- - "~>"
|
42
|
-
- !ruby/object:Gem::Version
|
43
|
-
version: '0.1'
|
44
41
|
- - ">="
|
45
42
|
- !ruby/object:Gem::Version
|
46
|
-
version: 0.
|
43
|
+
version: 0.8.0
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0.8'
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: dry-core
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -118,20 +118,20 @@ dependencies:
|
|
118
118
|
requirements:
|
119
119
|
- - ">="
|
120
120
|
- !ruby/object:Gem::Version
|
121
|
-
version: 0.
|
121
|
+
version: 0.15.0
|
122
122
|
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version: '0.
|
124
|
+
version: '0.15'
|
125
125
|
type: :runtime
|
126
126
|
prerelease: false
|
127
127
|
version_requirements: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
129
|
- - ">="
|
130
130
|
- !ruby/object:Gem::Version
|
131
|
-
version: 0.
|
131
|
+
version: 0.15.0
|
132
132
|
- - "~>"
|
133
133
|
- !ruby/object:Gem::Version
|
134
|
-
version: '0.
|
134
|
+
version: '0.15'
|
135
135
|
- !ruby/object:Gem::Dependency
|
136
136
|
name: bundler
|
137
137
|
requirement: !ruby/object:Gem::Requirement
|
@@ -257,7 +257,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
257
257
|
- !ruby/object:Gem::Version
|
258
258
|
version: '0'
|
259
259
|
requirements: []
|
260
|
-
rubygems_version: 3.0.
|
260
|
+
rubygems_version: 3.0.3
|
261
261
|
signing_key:
|
262
262
|
specification_version: 4
|
263
263
|
summary: Schema coercion and validation
|