mega-sharp-sys 0.0.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 +7 -0
- data/dry-schema-1.16.0/CHANGELOG.md +901 -0
- data/dry-schema-1.16.0/LICENSE +20 -0
- data/dry-schema-1.16.0/README.md +17 -0
- data/dry-schema-1.16.0/config/errors.yml +127 -0
- data/dry-schema-1.16.0/dry-schema.gemspec +50 -0
- data/dry-schema-1.16.0/lib/dry/schema/compiler.rb +51 -0
- data/dry-schema-1.16.0/lib/dry/schema/config.rb +83 -0
- data/dry-schema-1.16.0/lib/dry/schema/constants.rb +47 -0
- data/dry-schema-1.16.0/lib/dry/schema/dsl.rb +517 -0
- data/dry-schema-1.16.0/lib/dry/schema/extensions/hints/compiler_methods.rb +19 -0
- data/dry-schema-1.16.0/lib/dry/schema/extensions/hints/message_compiler_methods.rb +89 -0
- data/dry-schema-1.16.0/lib/dry/schema/extensions/hints/message_set_methods.rb +44 -0
- data/dry-schema-1.16.0/lib/dry/schema/extensions/hints/result_methods.rb +47 -0
- data/dry-schema-1.16.0/lib/dry/schema/extensions/hints.rb +65 -0
- data/dry-schema-1.16.0/lib/dry/schema/extensions/info/schema_compiler.rb +123 -0
- data/dry-schema-1.16.0/lib/dry/schema/extensions/info.rb +27 -0
- data/dry-schema-1.16.0/lib/dry/schema/extensions/json_schema/schema_compiler.rb +314 -0
- data/dry-schema-1.16.0/lib/dry/schema/extensions/json_schema.rb +29 -0
- data/dry-schema-1.16.0/lib/dry/schema/extensions/monads.rb +32 -0
- data/dry-schema-1.16.0/lib/dry/schema/extensions/struct.rb +62 -0
- data/dry-schema-1.16.0/lib/dry/schema/extensions.rb +21 -0
- data/dry-schema-1.16.0/lib/dry/schema/json.rb +17 -0
- data/dry-schema-1.16.0/lib/dry/schema/key.rb +184 -0
- data/dry-schema-1.16.0/lib/dry/schema/key_coercer.rb +41 -0
- data/dry-schema-1.16.0/lib/dry/schema/key_map.rb +136 -0
- data/dry-schema-1.16.0/lib/dry/schema/key_validator.rb +117 -0
- data/dry-schema-1.16.0/lib/dry/schema/macros/array.rb +54 -0
- data/dry-schema-1.16.0/lib/dry/schema/macros/core.rb +55 -0
- data/dry-schema-1.16.0/lib/dry/schema/macros/dsl.rb +282 -0
- data/dry-schema-1.16.0/lib/dry/schema/macros/each.rb +31 -0
- data/dry-schema-1.16.0/lib/dry/schema/macros/filled.rb +58 -0
- data/dry-schema-1.16.0/lib/dry/schema/macros/hash.rb +36 -0
- data/dry-schema-1.16.0/lib/dry/schema/macros/key.rb +50 -0
- data/dry-schema-1.16.0/lib/dry/schema/macros/maybe.rb +38 -0
- data/dry-schema-1.16.0/lib/dry/schema/macros/optional.rb +22 -0
- data/dry-schema-1.16.0/lib/dry/schema/macros/required.rb +17 -0
- data/dry-schema-1.16.0/lib/dry/schema/macros/schema.rb +103 -0
- data/dry-schema-1.16.0/lib/dry/schema/macros/value.rb +125 -0
- data/dry-schema-1.16.0/lib/dry/schema/message/or/abstract.rb +28 -0
- data/dry-schema-1.16.0/lib/dry/schema/message/or/multi_path.rb +100 -0
- data/dry-schema-1.16.0/lib/dry/schema/message/or/single_path.rb +72 -0
- data/dry-schema-1.16.0/lib/dry/schema/message/or.rb +30 -0
- data/dry-schema-1.16.0/lib/dry/schema/message.rb +116 -0
- data/dry-schema-1.16.0/lib/dry/schema/message_compiler/visitor_opts.rb +56 -0
- data/dry-schema-1.16.0/lib/dry/schema/message_compiler.rb +234 -0
- data/dry-schema-1.16.0/lib/dry/schema/message_set.rb +144 -0
- data/dry-schema-1.16.0/lib/dry/schema/messages/abstract.rb +202 -0
- data/dry-schema-1.16.0/lib/dry/schema/messages/i18n.rb +139 -0
- data/dry-schema-1.16.0/lib/dry/schema/messages/namespaced.rb +93 -0
- data/dry-schema-1.16.0/lib/dry/schema/messages/template.rb +43 -0
- data/dry-schema-1.16.0/lib/dry/schema/messages/yaml.rb +209 -0
- data/dry-schema-1.16.0/lib/dry/schema/messages.rb +32 -0
- data/dry-schema-1.16.0/lib/dry/schema/namespaced_rule.rb +38 -0
- data/dry-schema-1.16.0/lib/dry/schema/params.rb +17 -0
- data/dry-schema-1.16.0/lib/dry/schema/path.rb +107 -0
- data/dry-schema-1.16.0/lib/dry/schema/predicate.rb +102 -0
- data/dry-schema-1.16.0/lib/dry/schema/predicate_inferrer.rb +25 -0
- data/dry-schema-1.16.0/lib/dry/schema/predicate_registry.rb +22 -0
- data/dry-schema-1.16.0/lib/dry/schema/primitive_inferrer.rb +22 -0
- data/dry-schema-1.16.0/lib/dry/schema/processor.rb +213 -0
- data/dry-schema-1.16.0/lib/dry/schema/processor_steps.rb +141 -0
- data/dry-schema-1.16.0/lib/dry/schema/result.rb +203 -0
- data/dry-schema-1.16.0/lib/dry/schema/rule_applier.rb +47 -0
- data/dry-schema-1.16.0/lib/dry/schema/step.rb +59 -0
- data/dry-schema-1.16.0/lib/dry/schema/trace.rb +114 -0
- data/dry-schema-1.16.0/lib/dry/schema/type_container.rb +27 -0
- data/dry-schema-1.16.0/lib/dry/schema/type_registry.rb +44 -0
- data/dry-schema-1.16.0/lib/dry/schema/types.rb +14 -0
- data/dry-schema-1.16.0/lib/dry/schema/types_merger.rb +139 -0
- data/dry-schema-1.16.0/lib/dry/schema/value_coercer.rb +36 -0
- data/dry-schema-1.16.0/lib/dry/schema/version.rb +7 -0
- data/dry-schema-1.16.0/lib/dry/schema.rb +114 -0
- data/dry-schema-1.16.0/lib/dry-schema.rb +3 -0
- data/mega-sharp-sys.gemspec +11 -0
- metadata +114 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dry
|
|
4
|
+
module Schema
|
|
5
|
+
# Key objects used by key maps
|
|
6
|
+
#
|
|
7
|
+
# @api public
|
|
8
|
+
class Key
|
|
9
|
+
extend ::Dry::Core::Cache
|
|
10
|
+
|
|
11
|
+
DEFAULT_COERCER = :itself.to_proc.freeze
|
|
12
|
+
|
|
13
|
+
include ::Dry.Equalizer(:name, :coercer)
|
|
14
|
+
|
|
15
|
+
# @return [Symbol] The key identifier
|
|
16
|
+
attr_reader :id
|
|
17
|
+
|
|
18
|
+
# @return [Symbol, String, Object] The actual key name expected in an input hash
|
|
19
|
+
attr_reader :name
|
|
20
|
+
|
|
21
|
+
# @return [Proc, #call] A key name coercer function
|
|
22
|
+
attr_reader :coercer
|
|
23
|
+
|
|
24
|
+
# @api private
|
|
25
|
+
def self.[](name, **opts)
|
|
26
|
+
new(name, **opts)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# @api private
|
|
30
|
+
def self.new(*args, **kwargs)
|
|
31
|
+
fetch_or_store(args, kwargs) { super }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @api private
|
|
35
|
+
def initialize(id, name: id, coercer: DEFAULT_COERCER)
|
|
36
|
+
@id = id
|
|
37
|
+
@name = name
|
|
38
|
+
@coercer = coercer
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# @api private
|
|
42
|
+
def read(source)
|
|
43
|
+
return unless source.is_a?(::Hash)
|
|
44
|
+
|
|
45
|
+
if source.key?(name)
|
|
46
|
+
yield(source[name])
|
|
47
|
+
elsif source.key?(coerced_name)
|
|
48
|
+
yield(source[coerced_name])
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# @api private
|
|
53
|
+
def write(source, target)
|
|
54
|
+
read(source) { |value| target[coerced_name] = value }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# @api private
|
|
58
|
+
def coercible(&coercer)
|
|
59
|
+
new(coercer: coercer)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# @api private
|
|
63
|
+
def stringified
|
|
64
|
+
new(name: name.to_s)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# @api private
|
|
68
|
+
def to_dot_notation
|
|
69
|
+
[name.to_s]
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# @api private
|
|
73
|
+
def new(**new_opts)
|
|
74
|
+
self.class.new(id, name: name, coercer: coercer, **new_opts)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# @api private
|
|
78
|
+
def dump
|
|
79
|
+
name
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
private
|
|
83
|
+
|
|
84
|
+
# @api private
|
|
85
|
+
def coerced_name
|
|
86
|
+
@__coerced_name__ ||= coercer[name]
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# A specialized key type which handles nested hashes
|
|
90
|
+
#
|
|
91
|
+
# @api private
|
|
92
|
+
class Hash < self
|
|
93
|
+
include ::Dry.Equalizer(:name, :members, :coercer)
|
|
94
|
+
|
|
95
|
+
# @api private
|
|
96
|
+
attr_reader :members
|
|
97
|
+
|
|
98
|
+
# @api private
|
|
99
|
+
def initialize(id, members:, **opts)
|
|
100
|
+
super(id, **opts)
|
|
101
|
+
@members = members
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# @api private
|
|
105
|
+
def read(source)
|
|
106
|
+
super if source.is_a?(::Hash)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def write(source, target)
|
|
110
|
+
read(source) { |value|
|
|
111
|
+
target[coerced_name] = value.is_a?(::Hash) ? members.write(value) : value
|
|
112
|
+
}
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# @api private
|
|
116
|
+
def coercible(&coercer)
|
|
117
|
+
new(coercer: coercer, members: members.coercible(&coercer))
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# @api private
|
|
121
|
+
def stringified
|
|
122
|
+
new(name: name.to_s, members: members.stringified)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# @api private
|
|
126
|
+
def to_dot_notation
|
|
127
|
+
[name].product(members.flat_map(&:to_dot_notation)).map { |e| e.join(DOT) }
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# @api private
|
|
131
|
+
def dump
|
|
132
|
+
{name => members.map(&:dump)}
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# A specialized key type which handles nested arrays
|
|
137
|
+
#
|
|
138
|
+
# @api private
|
|
139
|
+
class Array < self
|
|
140
|
+
include ::Dry.Equalizer(:name, :member, :coercer)
|
|
141
|
+
|
|
142
|
+
attr_reader :member
|
|
143
|
+
|
|
144
|
+
# @api private
|
|
145
|
+
def initialize(id, member:, **opts)
|
|
146
|
+
super(id, **opts)
|
|
147
|
+
@member = member
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# @api private
|
|
151
|
+
def write(source, target)
|
|
152
|
+
read(source) { |value|
|
|
153
|
+
target[coerced_name] =
|
|
154
|
+
if value.is_a?(::Array)
|
|
155
|
+
value.map { |el| el.is_a?(::Hash) ? member.write(el) : el }
|
|
156
|
+
else
|
|
157
|
+
value
|
|
158
|
+
end
|
|
159
|
+
}
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# @api private
|
|
163
|
+
def coercible(&coercer)
|
|
164
|
+
new(coercer: coercer, member: member.coercible(&coercer))
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# @api private
|
|
168
|
+
def stringified
|
|
169
|
+
new(name: name.to_s, member: member.stringified)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# @api private
|
|
173
|
+
def to_dot_notation
|
|
174
|
+
[:"#{name}[]"].product(member.to_dot_notation).map { |el| el.join(DOT) }
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# @api private
|
|
178
|
+
def dump
|
|
179
|
+
[name, member.dump]
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dry/core/cache"
|
|
4
|
+
require "dry/core/equalizer"
|
|
5
|
+
|
|
6
|
+
module Dry
|
|
7
|
+
module Schema
|
|
8
|
+
# Coerces keys in a hash using provided coercer function
|
|
9
|
+
#
|
|
10
|
+
# @api private
|
|
11
|
+
class KeyCoercer
|
|
12
|
+
extend ::Dry::Core::Cache
|
|
13
|
+
include ::Dry::Equalizer(:key_map, :coercer)
|
|
14
|
+
|
|
15
|
+
TO_SYM = :to_sym.to_proc.freeze
|
|
16
|
+
|
|
17
|
+
attr_reader :key_map, :coercer
|
|
18
|
+
|
|
19
|
+
# @api private
|
|
20
|
+
def self.new(*args)
|
|
21
|
+
fetch_or_store(*args) { super }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @api private
|
|
25
|
+
def self.symbolized(*args)
|
|
26
|
+
new(*args, &TO_SYM)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# @api private
|
|
30
|
+
def initialize(key_map, &)
|
|
31
|
+
@key_map = key_map.coercible(&)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @api private
|
|
35
|
+
def call(result)
|
|
36
|
+
key_map.write(result.to_h)
|
|
37
|
+
end
|
|
38
|
+
alias_method :[], :call
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dry/schema/constants"
|
|
4
|
+
|
|
5
|
+
module Dry
|
|
6
|
+
module Schema
|
|
7
|
+
# Represents a list of keys defined by the DSL
|
|
8
|
+
#
|
|
9
|
+
# KeyMap objects expose an API for introspecting schema keys and the ability
|
|
10
|
+
# to rebuild an input hash using configured coercer function.
|
|
11
|
+
#
|
|
12
|
+
# Instances of this class are used as the very first step by the schema processors.
|
|
13
|
+
#
|
|
14
|
+
# @api public
|
|
15
|
+
class KeyMap
|
|
16
|
+
extend ::Dry::Core::Cache
|
|
17
|
+
|
|
18
|
+
include ::Dry.Equalizer(:keys)
|
|
19
|
+
include ::Enumerable
|
|
20
|
+
|
|
21
|
+
# @return [Array<Key>] A list of defined key objects
|
|
22
|
+
attr_reader :keys
|
|
23
|
+
|
|
24
|
+
# Coerce a list of key specs into a key map
|
|
25
|
+
#
|
|
26
|
+
# @example
|
|
27
|
+
# KeyMap[:id, :name]
|
|
28
|
+
# KeyMap[:title, :artist, tags: [:name]]
|
|
29
|
+
# KeyMap[:title, :artist, [:tags]]
|
|
30
|
+
#
|
|
31
|
+
# @return [KeyMap]
|
|
32
|
+
#
|
|
33
|
+
# @api public
|
|
34
|
+
def self.[](*keys)
|
|
35
|
+
new(keys)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Build new, or returned a cached instance of a key map
|
|
39
|
+
#
|
|
40
|
+
# @param [Array<Symbol>, Array, Hash<Symbol=>Array>] args
|
|
41
|
+
#
|
|
42
|
+
# @return [KeyMap]
|
|
43
|
+
def self.new(*args)
|
|
44
|
+
fetch_or_store(*args) { super }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Set key objects
|
|
48
|
+
#
|
|
49
|
+
# @api private
|
|
50
|
+
def initialize(keys)
|
|
51
|
+
@keys = keys.map { |key|
|
|
52
|
+
case key
|
|
53
|
+
when Hash
|
|
54
|
+
root, rest = key.flatten
|
|
55
|
+
Key::Hash[root, members: KeyMap[*rest]]
|
|
56
|
+
when Array
|
|
57
|
+
root, rest = key
|
|
58
|
+
Key::Array[root, member: KeyMap[*rest]]
|
|
59
|
+
when Key
|
|
60
|
+
key
|
|
61
|
+
else
|
|
62
|
+
Key[key]
|
|
63
|
+
end
|
|
64
|
+
}
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Write a new hash based on the source hash
|
|
68
|
+
#
|
|
69
|
+
# @param [Hash] source The source hash
|
|
70
|
+
# @param [Hash] target The target hash
|
|
71
|
+
#
|
|
72
|
+
# @return [Hash]
|
|
73
|
+
#
|
|
74
|
+
# @api public
|
|
75
|
+
def write(source, target = EMPTY_HASH.dup)
|
|
76
|
+
each { |key| key.write(source, target) }
|
|
77
|
+
target
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Return a new key map that is configured to coerce keys using provided coercer function
|
|
81
|
+
#
|
|
82
|
+
# @return [KeyMap]
|
|
83
|
+
#
|
|
84
|
+
# @api public
|
|
85
|
+
def coercible(&coercer)
|
|
86
|
+
self.class.new(map { |key| key.coercible(&coercer) })
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Return a new key map with stringified keys
|
|
90
|
+
#
|
|
91
|
+
# A stringified key map is suitable for reading hashes with string keys
|
|
92
|
+
#
|
|
93
|
+
# @return [KeyMap]
|
|
94
|
+
#
|
|
95
|
+
# @api public
|
|
96
|
+
def stringified
|
|
97
|
+
self.class.new(map(&:stringified))
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# @api private
|
|
101
|
+
def to_dot_notation
|
|
102
|
+
@to_dot_notation ||= map(&:to_dot_notation).flatten
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Iterate over keys
|
|
106
|
+
#
|
|
107
|
+
# @api public
|
|
108
|
+
def each(&)
|
|
109
|
+
keys.each(&)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Return a new key map merged with the provided one
|
|
113
|
+
#
|
|
114
|
+
# @param [KeyMap, Array] other Either a key map or an array with key specs
|
|
115
|
+
#
|
|
116
|
+
# @return [KeyMap]
|
|
117
|
+
def +(other)
|
|
118
|
+
self.class.new(keys + other.to_a)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Return a string representation of a key map
|
|
122
|
+
#
|
|
123
|
+
# @return [String]
|
|
124
|
+
def inspect
|
|
125
|
+
"#<#{self.class}[#{keys.map(&:dump).map(&:inspect).join(", ")}]>"
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Dump keys to their spec format
|
|
129
|
+
#
|
|
130
|
+
# @return [Array]
|
|
131
|
+
def dump
|
|
132
|
+
keys.map(&:dump)
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dry/initializer"
|
|
4
|
+
require "dry/schema/constants"
|
|
5
|
+
|
|
6
|
+
module Dry
|
|
7
|
+
module Schema
|
|
8
|
+
# @api private
|
|
9
|
+
class KeyValidator
|
|
10
|
+
extend ::Dry::Initializer
|
|
11
|
+
|
|
12
|
+
MULTI_INDEX_REGEX = /(\[\d+\])+/
|
|
13
|
+
INDEX_REGEX = /\[\d+\]/
|
|
14
|
+
DIGIT_REGEX = /\A\d+\z/
|
|
15
|
+
BRACKETS = "[]"
|
|
16
|
+
|
|
17
|
+
# @api private
|
|
18
|
+
option :key_map
|
|
19
|
+
|
|
20
|
+
# @api private
|
|
21
|
+
def call(result)
|
|
22
|
+
input = result.to_h
|
|
23
|
+
|
|
24
|
+
input_paths = key_paths(input)
|
|
25
|
+
key_paths = key_map.to_dot_notation.sort
|
|
26
|
+
|
|
27
|
+
input_paths.each do |path|
|
|
28
|
+
error_path = validate_path(key_paths, path)
|
|
29
|
+
|
|
30
|
+
next unless error_path
|
|
31
|
+
|
|
32
|
+
result.add_error([:unexpected_key, [error_path, input]])
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
result
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
# @api private
|
|
41
|
+
def validate_path(key_paths, path)
|
|
42
|
+
if path[MULTI_INDEX_REGEX]
|
|
43
|
+
key = path.gsub(MULTI_INDEX_REGEX, BRACKETS)
|
|
44
|
+
if none_key_paths_match?(key_paths, key)
|
|
45
|
+
arr = path.gsub(INDEX_REGEX) { ".#{_1[1...-1]}" }
|
|
46
|
+
arr.split(DOT).map { DIGIT_REGEX.match?(_1) ? Integer(_1, 10) : _1.to_sym }
|
|
47
|
+
end
|
|
48
|
+
elsif none_key_paths_match?(key_paths, path)
|
|
49
|
+
path
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# @api private
|
|
54
|
+
def none_key_paths_match?(key_paths, path)
|
|
55
|
+
!any_key_paths_match?(key_paths, path)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# @api private
|
|
59
|
+
def any_key_paths_match?(key_paths, path)
|
|
60
|
+
find_path(key_paths, path, false) ||
|
|
61
|
+
find_path(key_paths, path + DOT, true) ||
|
|
62
|
+
find_path(key_paths, path + BRACKETS, true)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# @api private
|
|
66
|
+
def find_path(key_paths, path, prefix_match)
|
|
67
|
+
key = key_paths.bsearch { |key_path| key_path >= path }
|
|
68
|
+
prefix_match ? key&.start_with?(path) : key == path
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# @api private
|
|
72
|
+
def key_paths(hash)
|
|
73
|
+
hash.flat_map { |key, value|
|
|
74
|
+
case value
|
|
75
|
+
when ::Hash
|
|
76
|
+
if value.empty?
|
|
77
|
+
[key.to_s]
|
|
78
|
+
else
|
|
79
|
+
[key].product(key_paths(hash[key])).map { _1.join(DOT) }
|
|
80
|
+
end
|
|
81
|
+
when ::Array
|
|
82
|
+
hashes_or_arrays = hashes_or_arrays(value)
|
|
83
|
+
|
|
84
|
+
if hashes_or_arrays.empty?
|
|
85
|
+
[key.to_s]
|
|
86
|
+
else
|
|
87
|
+
traverse_array(value).map { |sub_path| [key, sub_path].join("") }
|
|
88
|
+
end
|
|
89
|
+
else
|
|
90
|
+
key.to_s
|
|
91
|
+
end
|
|
92
|
+
}
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# @api private
|
|
96
|
+
def hashes_or_arrays(xs)
|
|
97
|
+
xs.select { |x|
|
|
98
|
+
(x.is_a?(::Array) || x.is_a?(::Hash)) && !x.empty?
|
|
99
|
+
}
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# @api private
|
|
103
|
+
def traverse_array(arr)
|
|
104
|
+
arr.each_with_index.flat_map do |el, idx|
|
|
105
|
+
case el
|
|
106
|
+
when ::Hash
|
|
107
|
+
key_paths(el).map { ["[#{idx}]", *_1].join(DOT) }
|
|
108
|
+
when ::Array
|
|
109
|
+
traverse_array(el).map { ["[#{idx}]", *_1].join("") }
|
|
110
|
+
else
|
|
111
|
+
[]
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dry
|
|
4
|
+
module Schema
|
|
5
|
+
module Macros
|
|
6
|
+
# Macro used to specify predicates for each element of an array
|
|
7
|
+
#
|
|
8
|
+
# @api private
|
|
9
|
+
class Array < DSL
|
|
10
|
+
# @api private
|
|
11
|
+
# rubocop: disable Metrics/PerceivedComplexity
|
|
12
|
+
# rubocop: disable Metrics/AbcSize
|
|
13
|
+
def value(*args, **opts, &block)
|
|
14
|
+
type(:array)
|
|
15
|
+
|
|
16
|
+
extract_type_spec(args, set_type: false) do |*predicates, type_spec:, type_rule:|
|
|
17
|
+
type(schema_dsl.array[type_spec]) if type_spec
|
|
18
|
+
|
|
19
|
+
is_hash_block = type_spec.equal?(:hash)
|
|
20
|
+
|
|
21
|
+
if predicates.any? || opts.any? || !is_hash_block
|
|
22
|
+
super(
|
|
23
|
+
*predicates, type_spec: type_spec, type_rule: type_rule, **opts,
|
|
24
|
+
&(is_hash_block ? nil : block)
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
is_op = args.size.equal?(2) && args[1].is_a?(Logic::Operations::Abstract)
|
|
29
|
+
|
|
30
|
+
if is_hash_block && !is_op
|
|
31
|
+
hash(&block)
|
|
32
|
+
elsif is_op
|
|
33
|
+
hash = Value.new(schema_dsl: schema_dsl.new, name: name).hash(args[1])
|
|
34
|
+
|
|
35
|
+
trace.captures.concat(hash.trace.captures)
|
|
36
|
+
|
|
37
|
+
type(schema_dsl.types[name].of(hash.schema_dsl.types[name]))
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
self
|
|
42
|
+
end
|
|
43
|
+
# rubocop: enable Metrics/AbcSize
|
|
44
|
+
# rubocop: enable Metrics/PerceivedComplexity
|
|
45
|
+
|
|
46
|
+
# @api private
|
|
47
|
+
def to_ast(*)
|
|
48
|
+
[:and, [trace.array?.to_ast, [:each, trace.to_ast]]]
|
|
49
|
+
end
|
|
50
|
+
alias_method :ast, :to_ast
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dry/initializer"
|
|
4
|
+
require "dry/schema/constants"
|
|
5
|
+
|
|
6
|
+
module Dry
|
|
7
|
+
module Schema
|
|
8
|
+
module Macros
|
|
9
|
+
# Abstract macro class
|
|
10
|
+
#
|
|
11
|
+
# @api private
|
|
12
|
+
class Core
|
|
13
|
+
extend Dry::Initializer
|
|
14
|
+
|
|
15
|
+
# @api private
|
|
16
|
+
option :name, default: proc {}, optional: true
|
|
17
|
+
|
|
18
|
+
# @api private
|
|
19
|
+
option :compiler, default: proc { Compiler.new }
|
|
20
|
+
|
|
21
|
+
# @api private
|
|
22
|
+
option :schema_dsl, optional: true
|
|
23
|
+
|
|
24
|
+
# @api private
|
|
25
|
+
option :trace, default: proc { Trace.new(schema_dsl&.compiler || Compiler.new) }
|
|
26
|
+
|
|
27
|
+
# @api private
|
|
28
|
+
def new(**options)
|
|
29
|
+
self.class.new(name: name, compiler: compiler, schema_dsl: schema_dsl, **options)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @api private
|
|
33
|
+
def path
|
|
34
|
+
schema_dsl.path
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @api private
|
|
38
|
+
def to_rule
|
|
39
|
+
compiler.visit(to_ast)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# @api private
|
|
43
|
+
def to_ast(*)
|
|
44
|
+
trace.to_ast
|
|
45
|
+
end
|
|
46
|
+
alias_method :ast, :to_ast
|
|
47
|
+
|
|
48
|
+
# @api private
|
|
49
|
+
def operation
|
|
50
|
+
raise NotImplementedError
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|