yaml-merge 7.1.3 → 7.1.4
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
- checksums.yaml.gz.sig +0 -0
- data/README.md +8 -8
- data/lib/yaml/merge/version.rb +1 -1
- data/lib/yaml/merge.rb +260 -129
- data.tar.gz.sig +0 -0
- metadata +16 -32
- metadata.gz.sig +0 -0
- data/lib/yaml/merge/provider.rb +0 -78
- data/sig/yaml/merge.rbs +0 -21
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 48a5059a57b0ae4e4a6c114f2a867611725094229e1d813d1db3ae648ac011af
|
|
4
|
+
data.tar.gz: e9b8d83835c84f4ffdf1c0699cdc6c81c3737a9c5948711d6cf690934456fec4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 680650bcce9925958c92b042b9873d71b33fd3509e847d8dfed3f6c4d3fad7041b359f1310b30b4a4f00828c73414ef02e1538e1e8894568f71b3ce7fb30577f
|
|
7
|
+
data.tar.gz: 33843a20386ebc9c5d59e5815b952f814880706a38bbe6c45f9295469438e3e69d664dc69200e09113985f2d326262ed8e388092a37eee5eb0080d23455e7548
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/README.md
CHANGED
|
@@ -23,22 +23,22 @@ I've summarized my thoughts in [this blog post](https://dev.to/galtzo/hostile-ta
|
|
|
23
23
|
|
|
24
24
|
`Yaml::Merge` is the canonical YAML family package. It is the YAML substrate for
|
|
25
25
|
StructuredMerge Ruby: it registers the tree-sitter YAML path and should own the
|
|
26
|
-
shared YAML merge behavior used by
|
|
27
|
-
|
|
26
|
+
shared YAML merge behavior used by provider implementations. YAML parsing and
|
|
27
|
+
backend selection enter through `tree_haver`; source-preserving
|
|
28
28
|
partial document insertion, replacement, and removal should enter through
|
|
29
29
|
`ast-crispr`; merge orchestration should use `ast-merge`.
|
|
30
30
|
|
|
31
|
-
Direct parser calls in YAML merge behavior are a smell.
|
|
32
|
-
belong behind the
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
Direct parser calls in YAML merge behavior are a smell. Parser-specific details
|
|
32
|
+
belong behind the provider boundary. YAML-family code should operate on
|
|
33
|
+
normalized owners, ranges, comments, and edit plans rather than native Ruby
|
|
34
|
+
Hash/Array round-trips.
|
|
35
35
|
|
|
36
36
|
### Key Features
|
|
37
37
|
|
|
38
38
|
- YAML mapping-root validation.
|
|
39
39
|
- Backend feature profiles for the shared language-pack parser.
|
|
40
40
|
- Path-based owner matching for mapping entries.
|
|
41
|
-
- Shared YAML-family merge semantics for provider gems
|
|
41
|
+
- Shared YAML-family merge semantics for provider gems.
|
|
42
42
|
- Destination-wins array policy.
|
|
43
43
|
- Hash result API for fixture runners and higher-level tools.
|
|
44
44
|
|
|
@@ -277,7 +277,7 @@ If none of the available licenses suit your use case, please [contact us](mailto
|
|
|
277
277
|
[📌gitmoji]: https://gitmoji.dev
|
|
278
278
|
[📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
|
|
279
279
|
[🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
|
|
280
|
-
[🧮kloc-img]: https://img.shields.io/badge/KLOC-0.
|
|
280
|
+
[🧮kloc-img]: https://img.shields.io/badge/KLOC-0.667-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
|
|
281
281
|
[🔐security]: https://github.com/structuredmerge/structuredmerge-ruby/blob/main/SECURITY.md
|
|
282
282
|
[🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
|
|
283
283
|
[📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
|
data/lib/yaml/merge/version.rb
CHANGED
|
@@ -5,7 +5,7 @@ module Yaml
|
|
|
5
5
|
# Version namespace for this gem.
|
|
6
6
|
module Version
|
|
7
7
|
# Current gem version.
|
|
8
|
-
VERSION = '7.1.
|
|
8
|
+
VERSION = '7.1.4'
|
|
9
9
|
end
|
|
10
10
|
# Current gem version exposed at the traditional constant location.
|
|
11
11
|
VERSION = Version::VERSION # Traditional Constant Location
|
data/lib/yaml/merge.rb
CHANGED
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
require 'json'
|
|
4
4
|
require 'tree_haver'
|
|
5
5
|
require 'ast/merge'
|
|
6
|
-
require 'psych/merge'
|
|
7
6
|
require_relative 'merge/version'
|
|
8
7
|
|
|
9
8
|
module Yaml
|
|
9
|
+
# The YAML language layer owns this parser-neutral contract. Keep this
|
|
10
|
+
# facade co-located so psych-merge can depend on it without a reverse edge.
|
|
11
|
+
# rubocop:disable Metrics/ModuleLength -- the public YAML facade owns this structural boundary
|
|
10
12
|
module Merge
|
|
11
13
|
PACKAGE_NAME = 'yaml-merge'
|
|
12
14
|
DESTINATION_WINS_ARRAY_POLICY = {
|
|
@@ -15,6 +17,22 @@ module Yaml
|
|
|
15
17
|
}.freeze
|
|
16
18
|
BACKEND_REFERENCE = TreeHaver::KREUZBERG_LANGUAGE_PACK_BACKEND
|
|
17
19
|
BACKEND_REGISTRY = Struct.new(:registered, :mutex).new(false, Mutex.new)
|
|
20
|
+
YAML_TREE_NODE_HANDLERS = {
|
|
21
|
+
child: :yaml_value_from_tree_child_node,
|
|
22
|
+
mapping: :yaml_mapping_from_tree_node,
|
|
23
|
+
sequence: :yaml_sequence_from_tree_node,
|
|
24
|
+
scalar: :yaml_scalar_from_tree_node
|
|
25
|
+
}.freeze
|
|
26
|
+
YAML_DOUBLE_QUOTE_ESCAPES = {
|
|
27
|
+
'"' => '"',
|
|
28
|
+
'\\' => '\\',
|
|
29
|
+
'/' => '/',
|
|
30
|
+
'b' => "\b",
|
|
31
|
+
'f' => "\f",
|
|
32
|
+
'n' => "\n",
|
|
33
|
+
'r' => "\r",
|
|
34
|
+
't' => "\t"
|
|
35
|
+
}.freeze
|
|
18
36
|
|
|
19
37
|
class Error < Ast::Merge::Error; end
|
|
20
38
|
class ParseError < Ast::Merge::ParseError; end
|
|
@@ -90,20 +108,57 @@ module Yaml
|
|
|
90
108
|
return unsupported_feature_parse_result("Unsupported YAML backend #{diagnostic_backend}.")
|
|
91
109
|
end
|
|
92
110
|
|
|
93
|
-
|
|
94
|
-
collect_parse_errors(tree.root_node)
|
|
95
|
-
|
|
96
|
-
analyze_yaml_document(yaml_value_from_tree(tree.root_node, source), dialect)
|
|
111
|
+
parse_yaml_tree(source, dialect, requested)
|
|
97
112
|
rescue TreeHaver::Error, StandardError => e
|
|
98
113
|
parse_error_result(e.message)
|
|
99
114
|
end
|
|
100
115
|
|
|
116
|
+
def parse_yaml_tree(source, dialect, backend)
|
|
117
|
+
tree = parse_tree_sitter_source(:yaml, source, backend: backend)
|
|
118
|
+
collect_parse_errors(tree.root_node)
|
|
119
|
+
analyze_yaml_document(yaml_value_from_tree(tree.root_node, source), dialect)
|
|
120
|
+
end
|
|
121
|
+
private_class_method :parse_yaml_tree
|
|
122
|
+
|
|
101
123
|
def analyze_yaml_document(parsed, dialect)
|
|
102
|
-
|
|
124
|
+
return unsupported_feature_parse_result("Unsupported YAML dialect #{dialect}.") unless dialect == 'yaml'
|
|
125
|
+
return parse_error_result('YAML documents must parse to a mapping root.') unless parsed.is_a?(Hash)
|
|
126
|
+
|
|
127
|
+
validated = validate_yaml_node(parsed, '')
|
|
128
|
+
return invalid_yaml_analysis(validated) unless validated[:ok]
|
|
129
|
+
|
|
130
|
+
yaml_analysis_result(validated[:value])
|
|
103
131
|
end
|
|
104
132
|
|
|
133
|
+
def yaml_analysis_result(value)
|
|
134
|
+
{
|
|
135
|
+
ok: true,
|
|
136
|
+
diagnostics: [],
|
|
137
|
+
analysis: yaml_document_analysis(value),
|
|
138
|
+
policies: []
|
|
139
|
+
}
|
|
140
|
+
end
|
|
141
|
+
private_class_method :yaml_analysis_result
|
|
142
|
+
|
|
143
|
+
def yaml_document_analysis(value)
|
|
144
|
+
{
|
|
145
|
+
kind: 'yaml',
|
|
146
|
+
dialect: 'yaml',
|
|
147
|
+
normalized_source: canonical_yaml(value),
|
|
148
|
+
document: value,
|
|
149
|
+
root_kind: 'mapping',
|
|
150
|
+
owners: collect_yaml_owners(value)
|
|
151
|
+
}
|
|
152
|
+
end
|
|
153
|
+
private_class_method :yaml_document_analysis
|
|
154
|
+
|
|
155
|
+
def invalid_yaml_analysis(validated)
|
|
156
|
+
{ ok: false, diagnostics: [validated[:diagnostic]], policies: [] }
|
|
157
|
+
end
|
|
158
|
+
private_class_method :invalid_yaml_analysis
|
|
159
|
+
|
|
105
160
|
def match_yaml_owners(template, destination)
|
|
106
|
-
Ast::Merge::
|
|
161
|
+
Ast::Merge::OwnerSelection.match_by_path(template, destination)
|
|
107
162
|
end
|
|
108
163
|
|
|
109
164
|
def merge_yaml(template_source, destination_source, dialect, backend: nil)
|
|
@@ -121,45 +176,71 @@ module Yaml
|
|
|
121
176
|
|
|
122
177
|
def merge_yaml_with_parser(template_source, destination_source, dialect, backend: nil)
|
|
123
178
|
template = yield(template_source, dialect)
|
|
124
|
-
return
|
|
179
|
+
return parser_failure_result(template, :template) unless template[:ok]
|
|
125
180
|
|
|
126
181
|
destination = yield(destination_source, dialect)
|
|
127
|
-
unless destination[:ok]
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
182
|
+
return parser_failure_result(destination, :destination) unless destination[:ok]
|
|
183
|
+
|
|
184
|
+
return parse_error_merge_result('YAML documents must parse to a mapping root.') unless
|
|
185
|
+
yaml_parse_results_valid?(template, destination)
|
|
186
|
+
|
|
187
|
+
yaml_merge_result(template_source, destination_source, backend)
|
|
188
|
+
rescue StandardError => e
|
|
189
|
+
parser_exception_result(e)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def parser_exception_result(error)
|
|
193
|
+
{
|
|
194
|
+
ok: false,
|
|
195
|
+
diagnostics: [{ severity: 'error', category: 'destination_parse_error', message: error.message }],
|
|
196
|
+
policies: []
|
|
197
|
+
}
|
|
198
|
+
end
|
|
199
|
+
private_class_method :parser_exception_result
|
|
136
200
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
201
|
+
def parser_failure_result(result, role)
|
|
202
|
+
diagnostics = result[:diagnostics]
|
|
203
|
+
if role == :destination
|
|
204
|
+
diagnostics = diagnostics.map do |diagnostic|
|
|
205
|
+
diagnostic[:category] == 'parse_error' ? diagnostic.merge(category: 'destination_parse_error') : diagnostic
|
|
206
|
+
end
|
|
141
207
|
end
|
|
142
208
|
|
|
209
|
+
{ ok: false, diagnostics: diagnostics, policies: [] }
|
|
210
|
+
end
|
|
211
|
+
private_class_method :parser_failure_result
|
|
212
|
+
|
|
213
|
+
def yaml_documents?(template_document, destination_document)
|
|
214
|
+
template_document.is_a?(Hash) && destination_document.is_a?(Hash)
|
|
215
|
+
end
|
|
216
|
+
private_class_method :yaml_documents?
|
|
217
|
+
|
|
218
|
+
def yaml_parse_results_valid?(template, destination)
|
|
219
|
+
yaml_documents?(template.dig(:analysis, :document), destination.dig(:analysis, :document))
|
|
220
|
+
end
|
|
221
|
+
private_class_method :yaml_parse_results_valid?
|
|
222
|
+
|
|
223
|
+
def yaml_merge_result(template_source, destination_source, backend)
|
|
143
224
|
{
|
|
144
225
|
ok: true,
|
|
145
226
|
diagnostics: [],
|
|
146
|
-
output:
|
|
147
|
-
SmartMerger.new(
|
|
148
|
-
template_source,
|
|
149
|
-
destination_source,
|
|
150
|
-
add_template_only_nodes: true,
|
|
151
|
-
merge_sequences: false
|
|
152
|
-
).merge_result.to_yaml
|
|
153
|
-
end,
|
|
227
|
+
output: yaml_merge_output(template_source, destination_source, backend),
|
|
154
228
|
policies: [DESTINATION_WINS_ARRAY_POLICY]
|
|
155
229
|
}
|
|
156
|
-
rescue StandardError => e
|
|
157
|
-
{
|
|
158
|
-
ok: false,
|
|
159
|
-
diagnostics: [{ severity: 'error', category: 'destination_parse_error', message: e.message }],
|
|
160
|
-
policies: []
|
|
161
|
-
}
|
|
162
230
|
end
|
|
231
|
+
private_class_method :yaml_merge_result
|
|
232
|
+
|
|
233
|
+
def yaml_merge_output(template_source, destination_source, backend)
|
|
234
|
+
with_tree_sitter_backend(backend) do
|
|
235
|
+
SmartMerger.new(
|
|
236
|
+
template_source,
|
|
237
|
+
destination_source,
|
|
238
|
+
add_template_only_nodes: true,
|
|
239
|
+
merge_sequences: false
|
|
240
|
+
).merge_result.to_yaml
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
private_class_method :yaml_merge_output
|
|
163
244
|
|
|
164
245
|
def resolve_backend(backend)
|
|
165
246
|
return backend.to_s unless backend.to_s.empty?
|
|
@@ -210,30 +291,61 @@ module Yaml
|
|
|
210
291
|
|
|
211
292
|
def yaml_value_from_tree(node, source)
|
|
212
293
|
named = yaml_named_children(node)
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
yaml_value_from_tree(named.first, source)
|
|
218
|
-
when 'block_mapping', 'flow_mapping'
|
|
219
|
-
yaml_mapping_from_node(node, source)
|
|
220
|
-
when 'block_sequence', 'flow_sequence'
|
|
221
|
-
yaml_sequence_from_node(node, source)
|
|
222
|
-
when 'block_sequence_item'
|
|
223
|
-
value_node = named.first
|
|
224
|
-
value_node ? yaml_value_from_tree(value_node, source) : nil
|
|
225
|
-
when 'plain_scalar', 'string_scalar', 'double_quote_scalar', 'single_quote_scalar'
|
|
226
|
-
yaml_scalar_from_text(source[node.start_byte...node.end_byte])
|
|
227
|
-
else
|
|
228
|
-
if named.length == 1
|
|
229
|
-
yaml_value_from_tree(named.first, source)
|
|
230
|
-
else
|
|
231
|
-
yaml_scalar_from_text(source[node.start_byte...node.end_byte])
|
|
232
|
-
end
|
|
233
|
-
end
|
|
294
|
+
handler = YAML_TREE_NODE_HANDLERS[yaml_tree_node_kind(node.type)]
|
|
295
|
+
return send(handler, node, named, source) if handler
|
|
296
|
+
|
|
297
|
+
yaml_value_from_unknown_tree(node, named, source)
|
|
234
298
|
end
|
|
235
299
|
private_class_method :yaml_value_from_tree
|
|
236
300
|
|
|
301
|
+
def yaml_tree_node_kind(type)
|
|
302
|
+
case type
|
|
303
|
+
when 'stream', 'document', 'block_node', 'flow_node', 'block_sequence_item' then :child
|
|
304
|
+
when 'block_mapping', 'flow_mapping' then :mapping
|
|
305
|
+
when 'block_sequence', 'flow_sequence' then :sequence
|
|
306
|
+
when 'plain_scalar', 'string_scalar', 'double_quote_scalar', 'single_quote_scalar' then :scalar
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
private_class_method :yaml_tree_node_kind
|
|
310
|
+
|
|
311
|
+
def yaml_value_from_tree_child(named, source)
|
|
312
|
+
value_node = named.first
|
|
313
|
+
value_node ? yaml_value_from_tree(value_node, source) : nil
|
|
314
|
+
end
|
|
315
|
+
private_class_method :yaml_value_from_tree_child
|
|
316
|
+
|
|
317
|
+
def yaml_value_from_tree_child_node(_node, named, source)
|
|
318
|
+
yaml_value_from_tree_child(named, source)
|
|
319
|
+
end
|
|
320
|
+
private_class_method :yaml_value_from_tree_child_node
|
|
321
|
+
|
|
322
|
+
def yaml_mapping_from_tree_node(node, _named, source)
|
|
323
|
+
yaml_mapping_from_node(node, source)
|
|
324
|
+
end
|
|
325
|
+
private_class_method :yaml_mapping_from_tree_node
|
|
326
|
+
|
|
327
|
+
def yaml_sequence_from_tree_node(node, _named, source)
|
|
328
|
+
yaml_sequence_from_node(node, source)
|
|
329
|
+
end
|
|
330
|
+
private_class_method :yaml_sequence_from_tree_node
|
|
331
|
+
|
|
332
|
+
def yaml_scalar_from_tree_node(node, _named, source)
|
|
333
|
+
yaml_scalar_from_tree(node, source)
|
|
334
|
+
end
|
|
335
|
+
private_class_method :yaml_scalar_from_tree_node
|
|
336
|
+
|
|
337
|
+
def yaml_scalar_from_tree(node, source)
|
|
338
|
+
yaml_scalar_from_text(source[node.start_byte...node.end_byte])
|
|
339
|
+
end
|
|
340
|
+
private_class_method :yaml_scalar_from_tree
|
|
341
|
+
|
|
342
|
+
def yaml_value_from_unknown_tree(node, named, source)
|
|
343
|
+
return yaml_value_from_tree(named.first, source) if named.length == 1
|
|
344
|
+
|
|
345
|
+
yaml_scalar_from_text(source[node.start_byte...node.end_byte])
|
|
346
|
+
end
|
|
347
|
+
private_class_method :yaml_value_from_unknown_tree
|
|
348
|
+
|
|
237
349
|
def yaml_mapping_from_node(node, source)
|
|
238
350
|
yaml_named_children(node).each_with_object({}) do |child, mapping|
|
|
239
351
|
next unless child.type.end_with?('mapping_pair')
|
|
@@ -267,12 +379,18 @@ module Yaml
|
|
|
267
379
|
|
|
268
380
|
def yaml_scalar_from_text(text)
|
|
269
381
|
stripped = text.to_s.strip
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
382
|
+
case stripped.downcase
|
|
383
|
+
when '', '~', 'null' then nil
|
|
384
|
+
when 'true' then true
|
|
385
|
+
when 'false' then false
|
|
386
|
+
else yaml_numeric_or_quoted_scalar(stripped)
|
|
387
|
+
end
|
|
388
|
+
end
|
|
389
|
+
private_class_method :yaml_scalar_from_text
|
|
390
|
+
|
|
391
|
+
def yaml_numeric_or_quoted_scalar(stripped)
|
|
273
392
|
return stripped.to_i if stripped.match?(/\A[-+]?\d+\z/)
|
|
274
393
|
return stripped.to_f if stripped.match?(/\A[-+]?(?:\d+\.\d*|\d*\.\d+)(?:[eE][-+]?\d+)?\z/)
|
|
275
|
-
|
|
276
394
|
if stripped.start_with?('"') && stripped.end_with?('"')
|
|
277
395
|
return unescape_yaml_double_quoted_scalar(stripped[1...-1])
|
|
278
396
|
end
|
|
@@ -280,47 +398,49 @@ module Yaml
|
|
|
280
398
|
|
|
281
399
|
stripped
|
|
282
400
|
end
|
|
283
|
-
private_class_method :
|
|
401
|
+
private_class_method :yaml_numeric_or_quoted_scalar
|
|
284
402
|
|
|
285
403
|
def unescape_yaml_double_quoted_scalar(value)
|
|
286
|
-
value.gsub(%r{\\(["\\/bfnrt])})
|
|
287
|
-
{
|
|
288
|
-
'"' => '"',
|
|
289
|
-
'\\' => '\\',
|
|
290
|
-
'/' => '/',
|
|
291
|
-
'b' => "\b",
|
|
292
|
-
'f' => "\f",
|
|
293
|
-
'n' => "\n",
|
|
294
|
-
'r' => "\r",
|
|
295
|
-
't' => "\t"
|
|
296
|
-
}.fetch(Regexp.last_match(1))
|
|
297
|
-
end
|
|
404
|
+
value.gsub(%r{\\(["\\/bfnrt])}) { YAML_DOUBLE_QUOTE_ESCAPES.fetch(Regexp.last_match(1)) }
|
|
298
405
|
end
|
|
299
406
|
private_class_method :unescape_yaml_double_quoted_scalar
|
|
300
407
|
|
|
301
408
|
def validate_yaml_node(value, path)
|
|
302
|
-
if scalar?(value)
|
|
303
|
-
|
|
304
|
-
elsif value.is_a?(Array)
|
|
305
|
-
value.each_with_index.each_with_object({ ok: true, value: [] }) do |(item, index), memo|
|
|
306
|
-
validated = validate_yaml_node(item, "#{path}/#{index}")
|
|
307
|
-
return validated unless validated[:ok]
|
|
308
|
-
|
|
309
|
-
memo[:value] << validated[:value]
|
|
310
|
-
end
|
|
311
|
-
elsif value.is_a?(Hash)
|
|
312
|
-
value.keys.each_with_object({ ok: true, value: {} }) do |key, memo|
|
|
313
|
-
validated = validate_yaml_node(value[key], "#{path}/#{key}")
|
|
314
|
-
return validated unless validated[:ok]
|
|
409
|
+
return { ok: true, value: value } if scalar?(value)
|
|
410
|
+
return validate_yaml_collection(value, path) if value.is_a?(Array) || value.is_a?(Hash)
|
|
315
411
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
end
|
|
412
|
+
unsupported_feature_result(
|
|
413
|
+
"Unsupported YAML value at #{display_path(path)}. " \
|
|
414
|
+
'Only mappings, scalar values, and sequences are supported.'
|
|
415
|
+
)
|
|
321
416
|
end
|
|
322
417
|
private_class_method :validate_yaml_node
|
|
323
418
|
|
|
419
|
+
def validate_yaml_collection(value, path)
|
|
420
|
+
value.is_a?(Array) ? validate_yaml_array(value, path) : validate_yaml_hash(value, path)
|
|
421
|
+
end
|
|
422
|
+
private_class_method :validate_yaml_collection
|
|
423
|
+
|
|
424
|
+
def validate_yaml_array(value, path)
|
|
425
|
+
value.each_with_index.each_with_object({ ok: true, value: [] }) do |(item, index), memo|
|
|
426
|
+
validated = validate_yaml_node(item, "#{path}/#{index}")
|
|
427
|
+
return validated unless validated[:ok]
|
|
428
|
+
|
|
429
|
+
memo[:value] << validated[:value]
|
|
430
|
+
end
|
|
431
|
+
end
|
|
432
|
+
private_class_method :validate_yaml_array
|
|
433
|
+
|
|
434
|
+
def validate_yaml_hash(value, path)
|
|
435
|
+
value.each_with_object({ ok: true, value: {} }) do |(key, item), memo|
|
|
436
|
+
validated = validate_yaml_node(item, "#{path}/#{key}")
|
|
437
|
+
return validated unless validated[:ok]
|
|
438
|
+
|
|
439
|
+
memo[:value][key] = validated[:value]
|
|
440
|
+
end
|
|
441
|
+
end
|
|
442
|
+
private_class_method :validate_yaml_hash
|
|
443
|
+
|
|
324
444
|
def scalar?(value)
|
|
325
445
|
value.nil? || value.is_a?(String) || value.is_a?(Numeric) || value == true || value == false
|
|
326
446
|
end
|
|
@@ -367,60 +487,72 @@ module Yaml
|
|
|
367
487
|
|
|
368
488
|
def render_yaml_sequence(sequence, indent)
|
|
369
489
|
prefix = ' ' * indent
|
|
370
|
-
sequence.flat_map
|
|
371
|
-
if scalar?(item)
|
|
372
|
-
["#{prefix}- #{render_yaml_scalar(item)}"]
|
|
373
|
-
elsif item.is_a?(Hash)
|
|
374
|
-
["#{prefix}-"] + render_yaml_mapping(item, indent + 2)
|
|
375
|
-
elsif item.is_a?(Array)
|
|
376
|
-
["#{prefix}-"] + render_yaml_sequence(item, indent + 2)
|
|
377
|
-
else
|
|
378
|
-
["#{prefix}- #{render_yaml_scalar(item)}"]
|
|
379
|
-
end
|
|
380
|
-
end
|
|
490
|
+
sequence.flat_map { |item| render_yaml_sequence_item(item, prefix, indent) }
|
|
381
491
|
end
|
|
382
492
|
private_class_method :render_yaml_sequence
|
|
383
493
|
|
|
494
|
+
def render_yaml_sequence_item(item, prefix, indent)
|
|
495
|
+
if scalar?(item)
|
|
496
|
+
["#{prefix}- #{render_yaml_scalar(item)}"]
|
|
497
|
+
elsif item.is_a?(Hash)
|
|
498
|
+
["#{prefix}-"] + render_yaml_mapping(item, indent + 2)
|
|
499
|
+
elsif item.is_a?(Array)
|
|
500
|
+
["#{prefix}-"] + render_yaml_sequence(item, indent + 2)
|
|
501
|
+
else
|
|
502
|
+
["#{prefix}- #{render_yaml_scalar(item)}"]
|
|
503
|
+
end
|
|
504
|
+
end
|
|
505
|
+
private_class_method :render_yaml_sequence_item
|
|
506
|
+
|
|
384
507
|
def canonical_yaml(mapping)
|
|
385
508
|
"#{render_yaml_mapping(mapping).join("\n")}\n"
|
|
386
509
|
end
|
|
387
510
|
private_class_method :canonical_yaml
|
|
388
511
|
|
|
389
512
|
def collect_yaml_owners(mapping, prefix = '')
|
|
390
|
-
mapping.keys.sort.flat_map
|
|
391
|
-
path = "#{prefix}/#{key}"
|
|
392
|
-
value = mapping[key]
|
|
393
|
-
if value.is_a?(Array)
|
|
394
|
-
[{ path: path, owner_kind: 'key_value', match_key: key }] +
|
|
395
|
-
value.each_with_index.flat_map do |item, index|
|
|
396
|
-
item_path = "#{path}/#{index}"
|
|
397
|
-
nested = item.is_a?(Hash) ? collect_yaml_owners(item, item_path) : []
|
|
398
|
-
[{ path: item_path, owner_kind: 'sequence_item' }] + nested
|
|
399
|
-
end
|
|
400
|
-
elsif value.is_a?(Hash)
|
|
401
|
-
[{ path: path, owner_kind: 'mapping', match_key: key }] + collect_yaml_owners(value, path)
|
|
402
|
-
else
|
|
403
|
-
[{ path: path, owner_kind: 'key_value', match_key: key }]
|
|
404
|
-
end
|
|
405
|
-
end
|
|
513
|
+
mapping.keys.sort.flat_map { |key| collect_yaml_owners_for_value(key, mapping[key], prefix) }
|
|
406
514
|
end
|
|
407
515
|
private_class_method :collect_yaml_owners
|
|
408
516
|
|
|
517
|
+
def collect_yaml_owners_for_value(key, value, prefix)
|
|
518
|
+
path = "#{prefix}/#{key}"
|
|
519
|
+
case value
|
|
520
|
+
when Array then collect_yaml_sequence_owners(key, value, path)
|
|
521
|
+
when Hash then [{ path: path, owner_kind: 'mapping', match_key: key }] + collect_yaml_owners(value, path)
|
|
522
|
+
else [{ path: path, owner_kind: 'key_value', match_key: key }]
|
|
523
|
+
end
|
|
524
|
+
end
|
|
525
|
+
private_class_method :collect_yaml_owners_for_value
|
|
526
|
+
|
|
527
|
+
def collect_yaml_sequence_owners(key, value, path)
|
|
528
|
+
[{ path: path, owner_kind: 'key_value', match_key: key }] +
|
|
529
|
+
value.each_with_index.flat_map do |item, index|
|
|
530
|
+
item_path = "#{path}/#{index}"
|
|
531
|
+
nested = item.is_a?(Hash) ? collect_yaml_owners(item, item_path) : []
|
|
532
|
+
[{ path: item_path, owner_kind: 'sequence_item' }] + nested
|
|
533
|
+
end
|
|
534
|
+
end
|
|
535
|
+
private_class_method :collect_yaml_sequence_owners
|
|
536
|
+
|
|
409
537
|
def merge_yaml_mappings(template, destination)
|
|
410
538
|
ordered_merge_keys(template, destination).each_with_object({}) do |key, merged|
|
|
411
|
-
merged[key] =
|
|
412
|
-
destination[key]
|
|
413
|
-
elsif !destination.key?(key)
|
|
414
|
-
template[key]
|
|
415
|
-
elsif template[key].is_a?(Hash) && destination[key].is_a?(Hash)
|
|
416
|
-
merge_yaml_mappings(template[key], destination[key])
|
|
417
|
-
else
|
|
418
|
-
destination[key]
|
|
419
|
-
end
|
|
539
|
+
merged[key] = merge_yaml_mapping_value(template, destination, key)
|
|
420
540
|
end
|
|
421
541
|
end
|
|
422
542
|
private_class_method :merge_yaml_mappings
|
|
423
543
|
|
|
544
|
+
def merge_yaml_mapping_value(template, destination, key)
|
|
545
|
+
return destination[key] unless template.key?(key)
|
|
546
|
+
return template[key] unless destination.key?(key)
|
|
547
|
+
if template[key].is_a?(Hash) && destination[key].is_a?(Hash)
|
|
548
|
+
return merge_yaml_mappings(template[key],
|
|
549
|
+
destination[key])
|
|
550
|
+
end
|
|
551
|
+
|
|
552
|
+
destination[key]
|
|
553
|
+
end
|
|
554
|
+
private_class_method :merge_yaml_mapping_value
|
|
555
|
+
|
|
424
556
|
def ordered_merge_keys(template, destination)
|
|
425
557
|
template.keys + destination.keys.reject { |key| template.key?(key) }
|
|
426
558
|
end
|
|
@@ -453,8 +585,7 @@ module Yaml
|
|
|
453
585
|
end
|
|
454
586
|
private_class_method :unsupported_feature_result
|
|
455
587
|
end
|
|
588
|
+
# rubocop:enable Metrics/ModuleLength
|
|
456
589
|
end
|
|
457
590
|
|
|
458
591
|
Yaml::Merge.register_backend!
|
|
459
|
-
require_relative 'merge/provider'
|
|
460
|
-
Yaml::Merge.register_provider!
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: yaml-merge
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 7.1.
|
|
4
|
+
version: 7.1.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Peter H. Boling
|
|
@@ -43,42 +43,28 @@ dependencies:
|
|
|
43
43
|
requirements:
|
|
44
44
|
- - '='
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: 7.1.
|
|
46
|
+
version: 7.1.4
|
|
47
47
|
type: :runtime
|
|
48
48
|
prerelease: false
|
|
49
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
50
|
requirements:
|
|
51
51
|
- - '='
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: 7.1.
|
|
54
|
-
- !ruby/object:Gem::Dependency
|
|
55
|
-
name: psych-merge
|
|
56
|
-
requirement: !ruby/object:Gem::Requirement
|
|
57
|
-
requirements:
|
|
58
|
-
- - '='
|
|
59
|
-
- !ruby/object:Gem::Version
|
|
60
|
-
version: 7.1.3
|
|
61
|
-
type: :runtime
|
|
62
|
-
prerelease: false
|
|
63
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
-
requirements:
|
|
65
|
-
- - '='
|
|
66
|
-
- !ruby/object:Gem::Version
|
|
67
|
-
version: 7.1.3
|
|
53
|
+
version: 7.1.4
|
|
68
54
|
- !ruby/object:Gem::Dependency
|
|
69
55
|
name: tree_haver
|
|
70
56
|
requirement: !ruby/object:Gem::Requirement
|
|
71
57
|
requirements:
|
|
72
58
|
- - '='
|
|
73
59
|
- !ruby/object:Gem::Version
|
|
74
|
-
version: 7.1.
|
|
60
|
+
version: 7.1.4
|
|
75
61
|
type: :runtime
|
|
76
62
|
prerelease: false
|
|
77
63
|
version_requirements: !ruby/object:Gem::Requirement
|
|
78
64
|
requirements:
|
|
79
65
|
- - '='
|
|
80
66
|
- !ruby/object:Gem::Version
|
|
81
|
-
version: 7.1.
|
|
67
|
+
version: 7.1.4
|
|
82
68
|
- !ruby/object:Gem::Dependency
|
|
83
69
|
name: version_gem
|
|
84
70
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -108,7 +94,7 @@ dependencies:
|
|
|
108
94
|
version: '3.0'
|
|
109
95
|
- - ">="
|
|
110
96
|
- !ruby/object:Gem::Version
|
|
111
|
-
version: 3.0.
|
|
97
|
+
version: 3.0.7
|
|
112
98
|
type: :development
|
|
113
99
|
prerelease: false
|
|
114
100
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -118,7 +104,7 @@ dependencies:
|
|
|
118
104
|
version: '3.0'
|
|
119
105
|
- - ">="
|
|
120
106
|
- !ruby/object:Gem::Version
|
|
121
|
-
version: 3.0.
|
|
107
|
+
version: 3.0.7
|
|
122
108
|
- !ruby/object:Gem::Dependency
|
|
123
109
|
name: bundler-audit
|
|
124
110
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -216,7 +202,7 @@ dependencies:
|
|
|
216
202
|
version: '2.0'
|
|
217
203
|
- - ">="
|
|
218
204
|
- !ruby/object:Gem::Version
|
|
219
|
-
version: 2.0.
|
|
205
|
+
version: 2.0.20
|
|
220
206
|
type: :development
|
|
221
207
|
prerelease: false
|
|
222
208
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -226,7 +212,7 @@ dependencies:
|
|
|
226
212
|
version: '2.0'
|
|
227
213
|
- - ">="
|
|
228
214
|
- !ruby/object:Gem::Version
|
|
229
|
-
version: 2.0.
|
|
215
|
+
version: 2.0.20
|
|
230
216
|
- !ruby/object:Gem::Dependency
|
|
231
217
|
name: turbo_tests2
|
|
232
218
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -236,7 +222,7 @@ dependencies:
|
|
|
236
222
|
version: '3.2'
|
|
237
223
|
- - ">="
|
|
238
224
|
- !ruby/object:Gem::Version
|
|
239
|
-
version: 3.2.
|
|
225
|
+
version: 3.2.6
|
|
240
226
|
type: :development
|
|
241
227
|
prerelease: false
|
|
242
228
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -246,7 +232,7 @@ dependencies:
|
|
|
246
232
|
version: '3.2'
|
|
247
233
|
- - ">="
|
|
248
234
|
- !ruby/object:Gem::Version
|
|
249
|
-
version: 3.2.
|
|
235
|
+
version: 3.2.6
|
|
250
236
|
- !ruby/object:Gem::Dependency
|
|
251
237
|
name: ruby-progressbar
|
|
252
238
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -290,7 +276,7 @@ dependencies:
|
|
|
290
276
|
version: '2.0'
|
|
291
277
|
- - ">="
|
|
292
278
|
- !ruby/object:Gem::Version
|
|
293
|
-
version: 2.0.
|
|
279
|
+
version: 2.0.12
|
|
294
280
|
type: :development
|
|
295
281
|
prerelease: false
|
|
296
282
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -300,7 +286,7 @@ dependencies:
|
|
|
300
286
|
version: '2.0'
|
|
301
287
|
- - ">="
|
|
302
288
|
- !ruby/object:Gem::Version
|
|
303
|
-
version: 2.0.
|
|
289
|
+
version: 2.0.12
|
|
304
290
|
description: "☯️ Portable YAML analysis, owner matching, and merge behavior for Structured
|
|
305
291
|
Merge."
|
|
306
292
|
email:
|
|
@@ -318,20 +304,18 @@ files:
|
|
|
318
304
|
- lib/yaml/merge/file_analysis.rb
|
|
319
305
|
- lib/yaml/merge/merge_result.rb
|
|
320
306
|
- lib/yaml/merge/node_wrapper.rb
|
|
321
|
-
- lib/yaml/merge/provider.rb
|
|
322
307
|
- lib/yaml/merge/smart_merger.rb
|
|
323
308
|
- lib/yaml/merge/version.rb
|
|
324
|
-
- sig/yaml/merge.rbs
|
|
325
309
|
homepage: https://github.com/structuredmerge/structuredmerge-ruby
|
|
326
310
|
licenses:
|
|
327
311
|
- AGPL-3.0-only
|
|
328
312
|
- PolyForm-Small-Business-1.0.0
|
|
329
313
|
metadata:
|
|
330
314
|
homepage_uri: https://structuredmerge.org
|
|
331
|
-
source_code_uri: https://github.com/structuredmerge/structuredmerge-ruby/tree/v7.1.
|
|
332
|
-
changelog_uri: https://github.com/structuredmerge/structuredmerge-ruby/blob/v7.1.
|
|
315
|
+
source_code_uri: https://github.com/structuredmerge/structuredmerge-ruby/tree/v7.1.4
|
|
316
|
+
changelog_uri: https://github.com/structuredmerge/structuredmerge-ruby/blob/v7.1.4/CHANGELOG.md
|
|
333
317
|
bug_tracker_uri: https://github.com/structuredmerge/structuredmerge-ruby/issues
|
|
334
|
-
documentation_uri: https://www.rubydoc.info/gems/yaml-merge/7.1.
|
|
318
|
+
documentation_uri: https://www.rubydoc.info/gems/yaml-merge/7.1.4
|
|
335
319
|
funding_uri: https://github.com/sponsors/pboling
|
|
336
320
|
wiki_uri: https://github.com/structuredmerge/structuredmerge-ruby/wiki
|
|
337
321
|
news_uri: https://www.railsbling.com/tags/yaml-merge
|
metadata.gz.sig
CHANGED
|
Binary file
|
data/lib/yaml/merge/provider.rb
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Yaml
|
|
4
|
-
# Portable YAML workflow integration.
|
|
5
|
-
module Merge
|
|
6
|
-
# YAML workflow selector delegating source-preserving operations to Psych.
|
|
7
|
-
class Provider
|
|
8
|
-
BACKEND = :'kreuzberg-language-pack'
|
|
9
|
-
DELEGATED_BACKEND = :psych
|
|
10
|
-
DELEGATED_PROVIDER_ID = 'ruby.yaml.psych'
|
|
11
|
-
DEFAULT_PROFILE = :source_preserving
|
|
12
|
-
|
|
13
|
-
def provider_id = 'ruby.yaml'
|
|
14
|
-
def family = 'yaml'
|
|
15
|
-
|
|
16
|
-
def capabilities
|
|
17
|
-
{
|
|
18
|
-
operations: Ast::Merge::ProviderContract::OPERATIONS,
|
|
19
|
-
dialects: %i[yaml],
|
|
20
|
-
backends: [BACKEND],
|
|
21
|
-
profiles: [DEFAULT_PROFILE],
|
|
22
|
-
role: :workflow,
|
|
23
|
-
source_preservation: Psych::Merge.merge_provider.capabilities.fetch(:source_preservation)
|
|
24
|
-
}.freeze
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
Ast::Merge::ProviderContract::OPERATIONS.each do |operation|
|
|
28
|
-
define_method(operation) do |request|
|
|
29
|
-
delegated = Ast::Merge.dispatch_provider(operation, delegated_request(request))
|
|
30
|
-
delegated.merge(provider: workflow_identity(request, delegated))
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
private
|
|
35
|
-
|
|
36
|
-
def delegated_request(request)
|
|
37
|
-
request.merge(
|
|
38
|
-
provider_id: DELEGATED_PROVIDER_ID,
|
|
39
|
-
family: :yaml,
|
|
40
|
-
dialect: :yaml,
|
|
41
|
-
backend: DELEGATED_BACKEND,
|
|
42
|
-
profile_id: DEFAULT_PROFILE
|
|
43
|
-
)
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def workflow_identity(request, delegated)
|
|
47
|
-
{
|
|
48
|
-
provider_id: provider_id,
|
|
49
|
-
family: family,
|
|
50
|
-
dialect: request[:dialect] || :yaml,
|
|
51
|
-
backend: request[:backend] || BACKEND,
|
|
52
|
-
package: PACKAGE_NAME,
|
|
53
|
-
package_version: Yaml::Merge::Version::VERSION,
|
|
54
|
-
delegated_provider: delegated_identity(delegated.fetch(:provider))
|
|
55
|
-
}
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def delegated_identity(identity)
|
|
59
|
-
{
|
|
60
|
-
provider_id: identity.fetch(:provider_id),
|
|
61
|
-
backend: identity.fetch(:backend),
|
|
62
|
-
package: identity[:package],
|
|
63
|
-
package_version: identity[:package_version]
|
|
64
|
-
}.compact
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
class << self
|
|
69
|
-
def merge_provider
|
|
70
|
-
@merge_provider ||= Provider.new
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
def register_provider!(replace: false)
|
|
74
|
-
Ast::Merge.register_provider(merge_provider, replace: replace)
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
end
|
|
78
|
-
end
|
data/sig/yaml/merge.rbs
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
module Yaml
|
|
2
|
-
module Merge
|
|
3
|
-
class Provider
|
|
4
|
-
def provider_id: () -> String
|
|
5
|
-
def family: () -> String
|
|
6
|
-
def capabilities: () -> Hash[Symbol, untyped]
|
|
7
|
-
def analyze: (Hash[Symbol, untyped] request) -> Hash[Symbol, untyped]
|
|
8
|
-
def diff2: (Hash[Symbol, untyped] request) -> Hash[Symbol, untyped]
|
|
9
|
-
def merge2: (Hash[Symbol, untyped] request) -> Hash[Symbol, untyped]
|
|
10
|
-
def merge3: (Hash[Symbol, untyped] request) -> Hash[Symbol, untyped]
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def self.merge_provider: () -> Provider
|
|
14
|
-
def self.register_provider!: (?replace: bool) -> untyped
|
|
15
|
-
|
|
16
|
-
module Version
|
|
17
|
-
VERSION: String
|
|
18
|
-
end
|
|
19
|
-
VERSION: String
|
|
20
|
-
end
|
|
21
|
-
end
|