yaml-merge 7.1.1 → 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 +10 -18
- data/lib/yaml/merge/version.rb +1 -1
- data/lib/yaml/merge.rb +249 -142
- data.tar.gz.sig +0 -0
- metadata +20 -21
- metadata.gz.sig +0 -0
- data/sig/yaml/merge.rbs +0 -8
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
|
|
|
@@ -57,7 +57,7 @@ ranges, comments, and edit plans rather than native Ruby Hash/Array round-trips.
|
|
|
57
57
|
|
|
58
58
|
### Compatibility
|
|
59
59
|
|
|
60
|
-
Compatible with MRI Ruby 4.0.0+,
|
|
60
|
+
Compatible with MRI Ruby 4.0.0+, JRuby, and TruffleRuby.
|
|
61
61
|
CI workflows and Appraisals are generated for MRI Ruby 4.0.0+.
|
|
62
62
|
This test floor is configured by `ruby.test_minimum` in `.kettle-jem.yml` and
|
|
63
63
|
may be higher than the gem's runtime compatibility floor when legacy Rubies are
|
|
@@ -132,20 +132,12 @@ See [SECURITY.md][🔐security].
|
|
|
132
132
|
## 🤝 Contributing
|
|
133
133
|
|
|
134
134
|
If you need some ideas of where to help, you could work on adding more code coverage,
|
|
135
|
-
|
|
136
|
-
or use the gem and think about how it could be better.
|
|
135
|
+
check [issues][🤝gh-issues] or [PRs][🤝gh-pulls], or use the gem and think about how it could be better.
|
|
137
136
|
|
|
138
137
|
We [![Keep A Changelog][📗keep-changelog-img]][📗keep-changelog] so if you make changes, remember to update it.
|
|
139
138
|
|
|
140
139
|
See [CONTRIBUTING.md][🤝contributing] for more detailed instructions.
|
|
141
140
|
|
|
142
|
-
### Code Coverage
|
|
143
|
-
|
|
144
|
-
<details markdown="1">
|
|
145
|
-
<summary>Coverage service badges</summary>
|
|
146
|
-
|
|
147
|
-
</details>
|
|
148
|
-
|
|
149
141
|
## 📌 Versioning
|
|
150
142
|
|
|
151
143
|
This library follows [![Semantic Versioning 2.0.0][📌semver-img]][📌semver] for its public API where practical.
|
|
@@ -285,7 +277,7 @@ If none of the available licenses suit your use case, please [contact us](mailto
|
|
|
285
277
|
[📌gitmoji]: https://gitmoji.dev
|
|
286
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
|
|
287
279
|
[🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
|
|
288
|
-
[🧮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
|
|
289
281
|
[🔐security]: https://github.com/structuredmerge/structuredmerge-ruby/blob/main/SECURITY.md
|
|
290
282
|
[🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
|
|
291
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
|
@@ -6,6 +6,9 @@ require 'ast/merge'
|
|
|
6
6
|
require_relative 'merge/version'
|
|
7
7
|
|
|
8
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
|
|
9
12
|
module Merge
|
|
10
13
|
PACKAGE_NAME = 'yaml-merge'
|
|
11
14
|
DESTINATION_WINS_ARRAY_POLICY = {
|
|
@@ -14,6 +17,22 @@ module Yaml
|
|
|
14
17
|
}.freeze
|
|
15
18
|
BACKEND_REFERENCE = TreeHaver::KREUZBERG_LANGUAGE_PACK_BACKEND
|
|
16
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
|
|
17
36
|
|
|
18
37
|
class Error < Ast::Merge::Error; end
|
|
19
38
|
class ParseError < Ast::Merge::ParseError; end
|
|
@@ -89,48 +108,58 @@ module Yaml
|
|
|
89
108
|
return unsupported_feature_parse_result("Unsupported YAML backend #{diagnostic_backend}.")
|
|
90
109
|
end
|
|
91
110
|
|
|
92
|
-
|
|
93
|
-
collect_parse_errors(tree.root_node)
|
|
94
|
-
|
|
95
|
-
analyze_yaml_document(yaml_value_from_tree(tree.root_node, source), dialect)
|
|
111
|
+
parse_yaml_tree(source, dialect, requested)
|
|
96
112
|
rescue TreeHaver::Error, StandardError => e
|
|
97
113
|
parse_error_result(e.message)
|
|
98
114
|
end
|
|
99
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
|
+
|
|
100
123
|
def analyze_yaml_document(parsed, dialect)
|
|
101
124
|
return unsupported_feature_parse_result("Unsupported YAML dialect #{dialect}.") unless dialect == 'yaml'
|
|
102
125
|
return parse_error_result('YAML documents must parse to a mapping root.') unless parsed.is_a?(Hash)
|
|
103
126
|
|
|
104
127
|
validated = validate_yaml_node(parsed, '')
|
|
105
|
-
return
|
|
128
|
+
return invalid_yaml_analysis(validated) unless validated[:ok]
|
|
129
|
+
|
|
130
|
+
yaml_analysis_result(validated[:value])
|
|
131
|
+
end
|
|
106
132
|
|
|
133
|
+
def yaml_analysis_result(value)
|
|
107
134
|
{
|
|
108
135
|
ok: true,
|
|
109
136
|
diagnostics: [],
|
|
110
|
-
analysis:
|
|
111
|
-
kind: 'yaml',
|
|
112
|
-
dialect: 'yaml',
|
|
113
|
-
normalized_source: canonical_yaml(validated[:value]),
|
|
114
|
-
document: validated[:value],
|
|
115
|
-
root_kind: 'mapping',
|
|
116
|
-
owners: collect_yaml_owners(validated[:value])
|
|
117
|
-
},
|
|
137
|
+
analysis: yaml_document_analysis(value),
|
|
118
138
|
policies: []
|
|
119
139
|
}
|
|
120
140
|
end
|
|
141
|
+
private_class_method :yaml_analysis_result
|
|
121
142
|
|
|
122
|
-
def
|
|
123
|
-
destination_paths = destination[:owners].to_h { |owner| [owner[:path], true] }
|
|
124
|
-
template_paths = template[:owners].to_h { |owner| [owner[:path], true] }
|
|
125
|
-
|
|
143
|
+
def yaml_document_analysis(value)
|
|
126
144
|
{
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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)
|
|
132
151
|
}
|
|
133
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
|
+
|
|
160
|
+
def match_yaml_owners(template, destination)
|
|
161
|
+
Ast::Merge::OwnerSelection.match_by_path(template, destination)
|
|
162
|
+
end
|
|
134
163
|
|
|
135
164
|
def merge_yaml(template_source, destination_source, dialect, backend: nil)
|
|
136
165
|
requested = backend.to_s.empty? ? nil : backend.to_s
|
|
@@ -147,45 +176,71 @@ module Yaml
|
|
|
147
176
|
|
|
148
177
|
def merge_yaml_with_parser(template_source, destination_source, dialect, backend: nil)
|
|
149
178
|
template = yield(template_source, dialect)
|
|
150
|
-
return
|
|
179
|
+
return parser_failure_result(template, :template) unless template[:ok]
|
|
151
180
|
|
|
152
181
|
destination = yield(destination_source, dialect)
|
|
153
|
-
unless destination[:ok]
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
diagnostic[:category] == 'parse_error' ? diagnostic.merge(category: 'destination_parse_error') : diagnostic
|
|
158
|
-
end,
|
|
159
|
-
policies: []
|
|
160
|
-
}
|
|
161
|
-
end
|
|
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)
|
|
162
186
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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
|
|
200
|
+
|
|
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
|
|
167
207
|
end
|
|
168
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)
|
|
169
224
|
{
|
|
170
225
|
ok: true,
|
|
171
226
|
diagnostics: [],
|
|
172
|
-
output:
|
|
173
|
-
SmartMerger.new(
|
|
174
|
-
template_source,
|
|
175
|
-
destination_source,
|
|
176
|
-
add_template_only_nodes: true,
|
|
177
|
-
merge_sequences: false
|
|
178
|
-
).merge_result.to_yaml
|
|
179
|
-
end,
|
|
227
|
+
output: yaml_merge_output(template_source, destination_source, backend),
|
|
180
228
|
policies: [DESTINATION_WINS_ARRAY_POLICY]
|
|
181
229
|
}
|
|
182
|
-
rescue StandardError => e
|
|
183
|
-
{
|
|
184
|
-
ok: false,
|
|
185
|
-
diagnostics: [{ severity: 'error', category: 'destination_parse_error', message: e.message }],
|
|
186
|
-
policies: []
|
|
187
|
-
}
|
|
188
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
|
|
189
244
|
|
|
190
245
|
def resolve_backend(backend)
|
|
191
246
|
return backend.to_s unless backend.to_s.empty?
|
|
@@ -236,30 +291,61 @@ module Yaml
|
|
|
236
291
|
|
|
237
292
|
def yaml_value_from_tree(node, source)
|
|
238
293
|
named = yaml_named_children(node)
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
yaml_value_from_tree(named.first, source)
|
|
244
|
-
when 'block_mapping', 'flow_mapping'
|
|
245
|
-
yaml_mapping_from_node(node, source)
|
|
246
|
-
when 'block_sequence', 'flow_sequence'
|
|
247
|
-
yaml_sequence_from_node(node, source)
|
|
248
|
-
when 'block_sequence_item'
|
|
249
|
-
value_node = named.first
|
|
250
|
-
value_node ? yaml_value_from_tree(value_node, source) : nil
|
|
251
|
-
when 'plain_scalar', 'string_scalar', 'double_quote_scalar', 'single_quote_scalar'
|
|
252
|
-
yaml_scalar_from_text(source[node.start_byte...node.end_byte])
|
|
253
|
-
else
|
|
254
|
-
if named.length == 1
|
|
255
|
-
yaml_value_from_tree(named.first, source)
|
|
256
|
-
else
|
|
257
|
-
yaml_scalar_from_text(source[node.start_byte...node.end_byte])
|
|
258
|
-
end
|
|
259
|
-
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)
|
|
260
298
|
end
|
|
261
299
|
private_class_method :yaml_value_from_tree
|
|
262
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
|
+
|
|
263
349
|
def yaml_mapping_from_node(node, source)
|
|
264
350
|
yaml_named_children(node).each_with_object({}) do |child, mapping|
|
|
265
351
|
next unless child.type.end_with?('mapping_pair')
|
|
@@ -293,12 +379,18 @@ module Yaml
|
|
|
293
379
|
|
|
294
380
|
def yaml_scalar_from_text(text)
|
|
295
381
|
stripped = text.to_s.strip
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
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)
|
|
299
392
|
return stripped.to_i if stripped.match?(/\A[-+]?\d+\z/)
|
|
300
393
|
return stripped.to_f if stripped.match?(/\A[-+]?(?:\d+\.\d*|\d*\.\d+)(?:[eE][-+]?\d+)?\z/)
|
|
301
|
-
|
|
302
394
|
if stripped.start_with?('"') && stripped.end_with?('"')
|
|
303
395
|
return unescape_yaml_double_quoted_scalar(stripped[1...-1])
|
|
304
396
|
end
|
|
@@ -306,47 +398,49 @@ module Yaml
|
|
|
306
398
|
|
|
307
399
|
stripped
|
|
308
400
|
end
|
|
309
|
-
private_class_method :
|
|
401
|
+
private_class_method :yaml_numeric_or_quoted_scalar
|
|
310
402
|
|
|
311
403
|
def unescape_yaml_double_quoted_scalar(value)
|
|
312
|
-
value.gsub(%r{\\(["\\/bfnrt])})
|
|
313
|
-
{
|
|
314
|
-
'"' => '"',
|
|
315
|
-
'\\' => '\\',
|
|
316
|
-
'/' => '/',
|
|
317
|
-
'b' => "\b",
|
|
318
|
-
'f' => "\f",
|
|
319
|
-
'n' => "\n",
|
|
320
|
-
'r' => "\r",
|
|
321
|
-
't' => "\t"
|
|
322
|
-
}.fetch(Regexp.last_match(1))
|
|
323
|
-
end
|
|
404
|
+
value.gsub(%r{\\(["\\/bfnrt])}) { YAML_DOUBLE_QUOTE_ESCAPES.fetch(Regexp.last_match(1)) }
|
|
324
405
|
end
|
|
325
406
|
private_class_method :unescape_yaml_double_quoted_scalar
|
|
326
407
|
|
|
327
408
|
def validate_yaml_node(value, path)
|
|
328
|
-
if scalar?(value)
|
|
329
|
-
|
|
330
|
-
elsif value.is_a?(Array)
|
|
331
|
-
value.each_with_index.each_with_object({ ok: true, value: [] }) do |(item, index), memo|
|
|
332
|
-
validated = validate_yaml_node(item, "#{path}/#{index}")
|
|
333
|
-
return validated unless validated[:ok]
|
|
334
|
-
|
|
335
|
-
memo[:value] << validated[:value]
|
|
336
|
-
end
|
|
337
|
-
elsif value.is_a?(Hash)
|
|
338
|
-
value.keys.each_with_object({ ok: true, value: {} }) do |key, memo|
|
|
339
|
-
validated = validate_yaml_node(value[key], "#{path}/#{key}")
|
|
340
|
-
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)
|
|
341
411
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
end
|
|
412
|
+
unsupported_feature_result(
|
|
413
|
+
"Unsupported YAML value at #{display_path(path)}. " \
|
|
414
|
+
'Only mappings, scalar values, and sequences are supported.'
|
|
415
|
+
)
|
|
347
416
|
end
|
|
348
417
|
private_class_method :validate_yaml_node
|
|
349
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
|
+
|
|
350
444
|
def scalar?(value)
|
|
351
445
|
value.nil? || value.is_a?(String) || value.is_a?(Numeric) || value == true || value == false
|
|
352
446
|
end
|
|
@@ -393,60 +487,72 @@ module Yaml
|
|
|
393
487
|
|
|
394
488
|
def render_yaml_sequence(sequence, indent)
|
|
395
489
|
prefix = ' ' * indent
|
|
396
|
-
sequence.flat_map
|
|
397
|
-
if scalar?(item)
|
|
398
|
-
["#{prefix}- #{render_yaml_scalar(item)}"]
|
|
399
|
-
elsif item.is_a?(Hash)
|
|
400
|
-
["#{prefix}-"] + render_yaml_mapping(item, indent + 2)
|
|
401
|
-
elsif item.is_a?(Array)
|
|
402
|
-
["#{prefix}-"] + render_yaml_sequence(item, indent + 2)
|
|
403
|
-
else
|
|
404
|
-
["#{prefix}- #{render_yaml_scalar(item)}"]
|
|
405
|
-
end
|
|
406
|
-
end
|
|
490
|
+
sequence.flat_map { |item| render_yaml_sequence_item(item, prefix, indent) }
|
|
407
491
|
end
|
|
408
492
|
private_class_method :render_yaml_sequence
|
|
409
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
|
+
|
|
410
507
|
def canonical_yaml(mapping)
|
|
411
508
|
"#{render_yaml_mapping(mapping).join("\n")}\n"
|
|
412
509
|
end
|
|
413
510
|
private_class_method :canonical_yaml
|
|
414
511
|
|
|
415
512
|
def collect_yaml_owners(mapping, prefix = '')
|
|
416
|
-
mapping.keys.sort.flat_map
|
|
417
|
-
path = "#{prefix}/#{key}"
|
|
418
|
-
value = mapping[key]
|
|
419
|
-
if value.is_a?(Array)
|
|
420
|
-
[{ path: path, owner_kind: 'key_value', match_key: key }] +
|
|
421
|
-
value.each_with_index.flat_map do |item, index|
|
|
422
|
-
item_path = "#{path}/#{index}"
|
|
423
|
-
nested = item.is_a?(Hash) ? collect_yaml_owners(item, item_path) : []
|
|
424
|
-
[{ path: item_path, owner_kind: 'sequence_item' }] + nested
|
|
425
|
-
end
|
|
426
|
-
elsif value.is_a?(Hash)
|
|
427
|
-
[{ path: path, owner_kind: 'mapping', match_key: key }] + collect_yaml_owners(value, path)
|
|
428
|
-
else
|
|
429
|
-
[{ path: path, owner_kind: 'key_value', match_key: key }]
|
|
430
|
-
end
|
|
431
|
-
end
|
|
513
|
+
mapping.keys.sort.flat_map { |key| collect_yaml_owners_for_value(key, mapping[key], prefix) }
|
|
432
514
|
end
|
|
433
515
|
private_class_method :collect_yaml_owners
|
|
434
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
|
+
|
|
435
537
|
def merge_yaml_mappings(template, destination)
|
|
436
538
|
ordered_merge_keys(template, destination).each_with_object({}) do |key, merged|
|
|
437
|
-
merged[key] =
|
|
438
|
-
destination[key]
|
|
439
|
-
elsif !destination.key?(key)
|
|
440
|
-
template[key]
|
|
441
|
-
elsif template[key].is_a?(Hash) && destination[key].is_a?(Hash)
|
|
442
|
-
merge_yaml_mappings(template[key], destination[key])
|
|
443
|
-
else
|
|
444
|
-
destination[key]
|
|
445
|
-
end
|
|
539
|
+
merged[key] = merge_yaml_mapping_value(template, destination, key)
|
|
446
540
|
end
|
|
447
541
|
end
|
|
448
542
|
private_class_method :merge_yaml_mappings
|
|
449
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
|
+
|
|
450
556
|
def ordered_merge_keys(template, destination)
|
|
451
557
|
template.keys + destination.keys.reject { |key| template.key?(key) }
|
|
452
558
|
end
|
|
@@ -479,6 +585,7 @@ module Yaml
|
|
|
479
585
|
end
|
|
480
586
|
private_class_method :unsupported_feature_result
|
|
481
587
|
end
|
|
588
|
+
# rubocop:enable Metrics/ModuleLength
|
|
482
589
|
end
|
|
483
590
|
|
|
484
591
|
Yaml::Merge.register_backend!
|
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,28 +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.
|
|
53
|
+
version: 7.1.4
|
|
54
54
|
- !ruby/object:Gem::Dependency
|
|
55
55
|
name: tree_haver
|
|
56
56
|
requirement: !ruby/object:Gem::Requirement
|
|
57
57
|
requirements:
|
|
58
58
|
- - '='
|
|
59
59
|
- !ruby/object:Gem::Version
|
|
60
|
-
version: 7.1.
|
|
60
|
+
version: 7.1.4
|
|
61
61
|
type: :runtime
|
|
62
62
|
prerelease: false
|
|
63
63
|
version_requirements: !ruby/object:Gem::Requirement
|
|
64
64
|
requirements:
|
|
65
65
|
- - '='
|
|
66
66
|
- !ruby/object:Gem::Version
|
|
67
|
-
version: 7.1.
|
|
67
|
+
version: 7.1.4
|
|
68
68
|
- !ruby/object:Gem::Dependency
|
|
69
69
|
name: version_gem
|
|
70
70
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -91,20 +91,20 @@ dependencies:
|
|
|
91
91
|
requirements:
|
|
92
92
|
- - "~>"
|
|
93
93
|
- !ruby/object:Gem::Version
|
|
94
|
-
version: '
|
|
94
|
+
version: '3.0'
|
|
95
95
|
- - ">="
|
|
96
96
|
- !ruby/object:Gem::Version
|
|
97
|
-
version:
|
|
97
|
+
version: 3.0.7
|
|
98
98
|
type: :development
|
|
99
99
|
prerelease: false
|
|
100
100
|
version_requirements: !ruby/object:Gem::Requirement
|
|
101
101
|
requirements:
|
|
102
102
|
- - "~>"
|
|
103
103
|
- !ruby/object:Gem::Version
|
|
104
|
-
version: '
|
|
104
|
+
version: '3.0'
|
|
105
105
|
- - ">="
|
|
106
106
|
- !ruby/object:Gem::Version
|
|
107
|
-
version:
|
|
107
|
+
version: 3.0.7
|
|
108
108
|
- !ruby/object:Gem::Dependency
|
|
109
109
|
name: bundler-audit
|
|
110
110
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -182,7 +182,7 @@ dependencies:
|
|
|
182
182
|
version: '3.2'
|
|
183
183
|
- - ">="
|
|
184
184
|
- !ruby/object:Gem::Version
|
|
185
|
-
version: 3.2.
|
|
185
|
+
version: 3.2.2
|
|
186
186
|
type: :development
|
|
187
187
|
prerelease: false
|
|
188
188
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -192,7 +192,7 @@ dependencies:
|
|
|
192
192
|
version: '3.2'
|
|
193
193
|
- - ">="
|
|
194
194
|
- !ruby/object:Gem::Version
|
|
195
|
-
version: 3.2.
|
|
195
|
+
version: 3.2.2
|
|
196
196
|
- !ruby/object:Gem::Dependency
|
|
197
197
|
name: kettle-test
|
|
198
198
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -202,7 +202,7 @@ dependencies:
|
|
|
202
202
|
version: '2.0'
|
|
203
203
|
- - ">="
|
|
204
204
|
- !ruby/object:Gem::Version
|
|
205
|
-
version: 2.0.
|
|
205
|
+
version: 2.0.20
|
|
206
206
|
type: :development
|
|
207
207
|
prerelease: false
|
|
208
208
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -212,7 +212,7 @@ dependencies:
|
|
|
212
212
|
version: '2.0'
|
|
213
213
|
- - ">="
|
|
214
214
|
- !ruby/object:Gem::Version
|
|
215
|
-
version: 2.0.
|
|
215
|
+
version: 2.0.20
|
|
216
216
|
- !ruby/object:Gem::Dependency
|
|
217
217
|
name: turbo_tests2
|
|
218
218
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -222,7 +222,7 @@ dependencies:
|
|
|
222
222
|
version: '3.2'
|
|
223
223
|
- - ">="
|
|
224
224
|
- !ruby/object:Gem::Version
|
|
225
|
-
version: 3.2.
|
|
225
|
+
version: 3.2.6
|
|
226
226
|
type: :development
|
|
227
227
|
prerelease: false
|
|
228
228
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -232,7 +232,7 @@ dependencies:
|
|
|
232
232
|
version: '3.2'
|
|
233
233
|
- - ">="
|
|
234
234
|
- !ruby/object:Gem::Version
|
|
235
|
-
version: 3.2.
|
|
235
|
+
version: 3.2.6
|
|
236
236
|
- !ruby/object:Gem::Dependency
|
|
237
237
|
name: ruby-progressbar
|
|
238
238
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -276,7 +276,7 @@ dependencies:
|
|
|
276
276
|
version: '2.0'
|
|
277
277
|
- - ">="
|
|
278
278
|
- !ruby/object:Gem::Version
|
|
279
|
-
version: 2.0.
|
|
279
|
+
version: 2.0.12
|
|
280
280
|
type: :development
|
|
281
281
|
prerelease: false
|
|
282
282
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -286,7 +286,7 @@ dependencies:
|
|
|
286
286
|
version: '2.0'
|
|
287
287
|
- - ">="
|
|
288
288
|
- !ruby/object:Gem::Version
|
|
289
|
-
version: 2.0.
|
|
289
|
+
version: 2.0.12
|
|
290
290
|
description: "☯️ Portable YAML analysis, owner matching, and merge behavior for Structured
|
|
291
291
|
Merge."
|
|
292
292
|
email:
|
|
@@ -306,17 +306,16 @@ files:
|
|
|
306
306
|
- lib/yaml/merge/node_wrapper.rb
|
|
307
307
|
- lib/yaml/merge/smart_merger.rb
|
|
308
308
|
- lib/yaml/merge/version.rb
|
|
309
|
-
- sig/yaml/merge.rbs
|
|
310
309
|
homepage: https://github.com/structuredmerge/structuredmerge-ruby
|
|
311
310
|
licenses:
|
|
312
311
|
- AGPL-3.0-only
|
|
313
312
|
- PolyForm-Small-Business-1.0.0
|
|
314
313
|
metadata:
|
|
315
314
|
homepage_uri: https://structuredmerge.org
|
|
316
|
-
source_code_uri: https://github.com/structuredmerge/structuredmerge-ruby/tree/v7.1.
|
|
317
|
-
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
|
|
318
317
|
bug_tracker_uri: https://github.com/structuredmerge/structuredmerge-ruby/issues
|
|
319
|
-
documentation_uri: https://www.rubydoc.info/gems/yaml-merge/7.1.
|
|
318
|
+
documentation_uri: https://www.rubydoc.info/gems/yaml-merge/7.1.4
|
|
320
319
|
funding_uri: https://github.com/sponsors/pboling
|
|
321
320
|
wiki_uri: https://github.com/structuredmerge/structuredmerge-ruby/wiki
|
|
322
321
|
news_uri: https://www.railsbling.com/tags/yaml-merge
|
metadata.gz.sig
CHANGED
|
Binary file
|