yaml_tools 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: fe18af8ef1c5ccad515bc83b88c2a663d143e56d49a30efe748b772b9ff3572b
4
+ data.tar.gz: 6fc99c8e0aa6ef9385ba82a1999b763fcab26f1a6298f6f02c4228c932e8b23a
5
+ SHA512:
6
+ metadata.gz: 1c771e0e390a84bf7bac523e6a81f630ee46d20540b1b4bcd251c6840a6747b0d4a54059d4325f6c655ddbec95395730db98aae2dbcee67153d94e57a96b8b01
7
+ data.tar.gz: eb91e0ccc7c759123cee799b7b5422975a55dff273b6e965aba62a438f64aab246e9418a2e6ccff1fe9bea2eedb2bade562dcbaa668c2e2ea7f4640f4d6fc6da
@@ -0,0 +1,214 @@
1
+ require 'yaml'
2
+
3
+ module YAMLTools
4
+ class Combiner
5
+ def combine_levels (s, d)
6
+ level = []
7
+
8
+ # Split into key/value pairs
9
+ sourceChildren = s.each_slice(2).to_a
10
+ differenceChildren = d.each_slice(2).to_a
11
+
12
+ sourceChildren.each {|sourcePair|
13
+ sourceKey = sourcePair[0]
14
+ sourceValue = sourcePair[1]
15
+
16
+ # Find difference pair
17
+ differencePairs = differenceChildren.find_all {|i| i[0].value == sourceKey.value}
18
+
19
+ if (differencePairs.length > 1) then
20
+ if (sourceKey.value == "<<") then
21
+ # Find merge key with matching alias
22
+ differencePair = differencePairs.find {|i|
23
+ (i[1].is_a?(Psych::Nodes::Alias) && i[1].anchor == sourceValue.anchor) ||
24
+ (i[1].is_a?(Psych::Nodes::Sequence) && i[1].children.any? {|a| a.is_a?(Psych::Nodes::Alias) && a.anchor == sourceValue.anchor})}
25
+ else
26
+ # Some ArchivesSpace files have duplicate keys so use the last one
27
+ differencePair = differencePairs.last
28
+ end
29
+ else
30
+ differencePair = differencePairs.first
31
+ end
32
+
33
+ if (differencePair == nil) then
34
+ # difference not found so copy node
35
+ level << sourceKey
36
+ level << sourceValue
37
+ else
38
+ differenceKey = differencePair[0]
39
+ differenceValue = differencePair[1]
40
+
41
+ if (differenceKey.value == '<<') then
42
+ # If aliases or sequences of aliases then merge
43
+ if ((sourceValue.is_a?(Psych::Nodes::Alias) || sourceValue.is_a?(Psych::Nodes::Sequence)) && (differenceValue.is_a?(Psych::Nodes::Alias) || differenceValue.is_a?(Psych::Nodes::Sequence))) then
44
+ if (sourceValue.is_a?(Psych::Nodes::Alias) && (differenceValue.is_a?(Psych::Nodes::Sequence) && differenceValue.children.all? {|a| a.is_a?(Psych::Nodes::Alias)})) then
45
+ # Merge aliases
46
+ if (differenceValue.children.none? {|a| a.anchor == sourceValue.anchor}) then
47
+ # Add alias to sequence
48
+ differenceValue.children = differenceValue.children << sourceValue
49
+ end
50
+
51
+ level.insert(0, sourceKey)
52
+ level.insert(1, differenceValue)
53
+
54
+ elsif (differenceValue.is_a?(Psych::Nodes::Alias) && (sourceValue.is_a?(Psych::Nodes::Sequence) && sourceValue.children.all? {|a| a.is_a?(Psych::Nodes::Alias)})) then
55
+ # Merge aliases
56
+ if (sourceValue.children.none? {|a| a.anchor == differenceValue.anchor}) then
57
+ # Add alias to sequence
58
+ sourceValue.children = sourceValue.children << differenceValue
59
+ end
60
+
61
+ level.insert(0, sourceKey)
62
+ level.insert(1, sourceValue)
63
+ elsif (sourceValue.is_a?(Psych::Nodes::Sequence) && differenceValue.is_a?(Psych::Nodes::Sequence))
64
+ newSequence = Psych::Nodes::Sequence.new(sourceValue.anchor, sourceValue.tag, sourceValue.implicit, Psych::Nodes::Sequence::FLOW)
65
+
66
+ newSequence.children
67
+ .concat(sourceValue.children, differenceValue.children)
68
+ .uniq! {|a| a.anchor}
69
+
70
+ level.insert(0, sourceKey)
71
+ level.insert(1, newSequence)
72
+ elsif (sourceValue.is_a?(Psych::Nodes::Alias) && differenceValue.is_a?(Psych::Nodes::Alias))
73
+ if (sourceValue.anchor == differenceValue.anchor) then
74
+ level.insert(0, sourceKey)
75
+ level.insert(1, sourceValue)
76
+ else
77
+ newSequence = Psych::Nodes::Sequence.new(sourceValue.anchor, sourceValue.tag, sourceValue.implicit, Psych::Nodes::Sequence::FLOW)
78
+
79
+ newSequence.children
80
+ .concat([sourceValue, differenceValue])
81
+ .uniq! {|a| a.anchor}
82
+
83
+ level.insert(0, sourceKey)
84
+ level.insert(1, newSequence)
85
+ end
86
+ end
87
+ end
88
+ elsif (differenceValue.is_a?(Psych::Nodes::Mapping)) then
89
+ childLevel = combine_levels(sourceValue.children, differenceValue.children)
90
+
91
+ newMapping = Psych::Nodes::Mapping.new(sourceValue.anchor, sourceValue.tag, sourceValue.implicit, sourceValue.style)
92
+
93
+ if (childLevel.length > 0) then
94
+ newMapping.children.push(*childLevel)
95
+ end
96
+
97
+ level << differenceKey
98
+ level << newMapping
99
+ else
100
+ level << differenceKey
101
+ level << differenceValue
102
+ end
103
+ end
104
+ }
105
+
106
+ # Add all difference pairs that don't exist in the source
107
+
108
+ differenceChildren.each {|differencePair|
109
+ differenceKey = differencePair[0]
110
+ differenceValue = differencePair[1]
111
+
112
+ if (sourceChildren.none? {|i| i[0].value == differenceKey.value}) then
113
+ if (differenceKey.value == '<<') then
114
+ level.insert(0, differenceKey)
115
+ level.insert(1, differenceValue)
116
+ else
117
+ level << differenceKey
118
+ level << differenceValue
119
+ end
120
+ end
121
+ }
122
+
123
+ level
124
+ end
125
+
126
+ # def flatten_merge_keys(s)
127
+ # level = []
128
+
129
+ # sourceChildren = s.each_slice(2).to_a
130
+
131
+ # mergeKeys = sourceChildren.find_all {|i| i[0].value == "<<" }
132
+
133
+ # if (mergeKeys.length > 0) then
134
+ # if (mergeKeys.length == 1) then
135
+ # # Add merge key
136
+ # level << mergeKeys.first[0]
137
+ # level << mergeKeys.first[1]
138
+ # else
139
+ # newSequence = Psych::Nodes::Sequence.new(nil, nil, true, Psych::Nodes::Sequence::FLOW)
140
+
141
+ # mergeKeys.each {|m|
142
+ # if (m[1].is_a?(Psych::Nodes::Alias)) then
143
+ # newSequence.children << m[1]
144
+ # elsif (m[1].is_a?(Psych::Nodes::Sequence)) then
145
+ # newSequence.children.concat(m[1].children)
146
+ # end
147
+ # }
148
+
149
+ # level << Psych::Nodes::Scalar.new("<<")
150
+ # level << newSequence
151
+ # end
152
+ # end
153
+
154
+ # sourceChildren.each {|sourcePair|
155
+ # sourceKey = sourcePair[0]
156
+ # sourceValue = sourcePair[1]
157
+
158
+ # if (sourceValue.is_a?(Psych::Nodes::Mapping)) then
159
+ # childLevel = flatten_merge_keys(sourceValue.children)
160
+
161
+ # newMapping = Psych::Nodes::Mapping.new(sourceValue.anchor, sourceValue.tag, sourceValue.implicit, sourceValue.style)
162
+
163
+ # if (childLevel.length > 0) then
164
+ # newMapping.children.push(*childLevel)
165
+ # end
166
+
167
+ # level << sourceKey
168
+ # level << newMapping
169
+ # else
170
+ # if (sourceKey.value != "<<") then
171
+ # level << sourceKey
172
+ # level << sourceValue
173
+ # end
174
+ # end
175
+ # }
176
+
177
+ # level
178
+ # end
179
+
180
+ def combine_files(sourceFilePath, differenceFilePath)
181
+ sourceFile = File.open(sourceFilePath, "r")
182
+ differenceFile = File.open(differenceFilePath, "r")
183
+
184
+ begin
185
+ output = combine(sourceFile, differenceFile)
186
+ ensure
187
+ sourceFile.close
188
+ differenceFile.close
189
+ end
190
+
191
+ output
192
+ end
193
+
194
+ def combine(source, difference)
195
+ # Load files
196
+ sourceDocument = YAML.parse(source)
197
+ differenceDocument = YAML.parse(difference)
198
+
199
+ # Flatten merge keys for older ArchivesSpace files
200
+ sourceDocument = YAMLTools.flatten_merge_keys(sourceDocument.root.children)
201
+ differenceDocument = YAMLTools.flatten_merge_keys(differenceDocument.root.children)
202
+
203
+ @combined = combine_levels(sourceDocument, differenceDocument)
204
+
205
+ if (@combined.length > 0) then
206
+ output = YAMLTools.createDocument(@combined)
207
+ else
208
+ output = ''
209
+ end
210
+
211
+ output
212
+ end
213
+ end
214
+ end
@@ -0,0 +1,267 @@
1
+ require 'yaml'
2
+
3
+ module YAMLTools
4
+ class Comparer
5
+ @source_anchors = {}
6
+ @modified_anchors = []
7
+
8
+ def processLevel (s, d, includeAnchorDependencies)
9
+ level = []
10
+
11
+ # Split into key/value pairs
12
+ sourceChildren = s.each_slice(2).to_a
13
+ destinationChildren = d.each_slice(2).to_a
14
+
15
+ # Add all anchors for this level for use when performing potential comparisons later
16
+ sourceChildren.find_all {|i| !i[1].is_a?(Psych::Nodes::Alias) && !i[1].anchor.nil?}.each {|i| @source_anchors[i[1].anchor] = i[1]}
17
+
18
+ destinationChildren.each {|destinationPair|
19
+ destinationKey = destinationPair[0]
20
+ destinationValue = destinationPair[1]
21
+
22
+ # Find source pair
23
+ sourcePairs = sourceChildren.find_all {|i| i[0].value == destinationKey.value}
24
+
25
+ if (sourcePairs.length > 1) then
26
+ if (destinationKey.value == "<<") then
27
+ # Find merge key with matching alias
28
+ sourcePair = sourcePairs.find {|i| i[1].is_a?(Psych::Nodes::Alias) && i[1].anchor == destinationValue.anchor}
29
+ else
30
+ # Some ArchivesSpace files have duplicate keys so use the last one
31
+ sourcePair = sourcePairs.last
32
+ end
33
+ else
34
+ sourcePair = sourcePairs.first
35
+ end
36
+
37
+ if (sourcePair == nil) then
38
+ # Source not found so copy node
39
+ level << destinationKey
40
+ level << destinationValue
41
+ else
42
+ sourceKey = sourcePair[0]
43
+ sourceValue = sourcePair[1]
44
+
45
+ if (sourceKey.value == "<<") then
46
+ # Handle merge keys specifically since they can be an individual alias or a sequence or aliases
47
+
48
+ if (sourceValue.is_a?(Psych::Nodes::Sequence)) then
49
+ if (destinationValue.is_a?(Psych::Nodes::Sequence)) then
50
+ if (sourceValue.children.length == destinationValue.children.length) then
51
+ # Compare sequences
52
+ sourceValue.children.each_with_index {|a, index|
53
+ if (a.anchor != destinationValue.children[index].anchor || (includeAnchorDependencies && @modified_anchors.include?(destinationValue.children[index].anchor))) then
54
+ # Different aliases or ordering
55
+ level << destinationKey
56
+ level << destinationValue
57
+ break
58
+ end
59
+ }
60
+ else
61
+ # Different number of aliases
62
+ level << destinationKey
63
+ level << destinationValue
64
+ end
65
+ elsif (destinationValue.is_a?(Psych::Nodes::Alias)) then
66
+ # Sequence of aliases overriden by single alias
67
+ level << destinationKey
68
+ level << destinationValue
69
+ else
70
+ # Bad data
71
+ end
72
+ elsif (sourceValue.is_a?(Psych::Nodes::Alias)) then
73
+ if (destinationValue.is_a?(Psych::Nodes::Sequence)) then
74
+ # Single alias overriden by sequence of aliases
75
+ level << destinationKey
76
+ level << destinationValue
77
+ elsif (destinationValue.is_a?(Psych::Nodes::Alias)) then
78
+ # Compare aliases
79
+ if (sourceValue.anchor != destinationValue.anchor || (includeAnchorDependencies && @modified_anchors.include?(destinationValue.anchor))) then
80
+ # Different aliases
81
+ level << destinationKey
82
+ level << destinationValue
83
+ end
84
+ else
85
+ # Bad data
86
+ end
87
+ else
88
+ # Bad data
89
+ end
90
+ elsif (destinationValue.is_a?(Psych::Nodes::Mapping)) then
91
+ childLevel = processLevel(sourceValue.children, destinationValue.children, includeAnchorDependencies)
92
+
93
+ if (childLevel.length > 0) then
94
+ newMapping = Psych::Nodes::Mapping.new(destinationValue.anchor, destinationValue.tag, destinationValue.implicit, destinationValue.style)
95
+ newMapping.children.push(*childLevel)
96
+
97
+ level << destinationKey
98
+ level << newMapping
99
+
100
+ if (destinationValue.anchor != nil) then
101
+ @modified_anchors << destinationValue.anchor
102
+ end
103
+ end
104
+ elsif (destinationValue.is_a?(Psych::Nodes::Scalar)) then
105
+ if (sourceValue.is_a?(Psych::Nodes::Scalar) && (destinationValue.value != sourceValue.value)) then
106
+ level << destinationKey
107
+ level << destinationValue
108
+
109
+ if destinationValue.anchor != nil then
110
+ @modified_anchors << destinationValue.anchor
111
+ end
112
+ elsif (sourceValue.is_a?(Psych::Nodes::Alias || (includeAnchorDependencies && @modified_anchors.include?(destinationValue.anchor)))) then
113
+ level << destinationKey
114
+ level << destinationValue
115
+ end
116
+ elsif (destinationValue.is_a?(Psych::Nodes::Alias)) then
117
+ if (sourceValue.is_a?(Psych::Nodes::Alias) && (destinationValue.anchor != sourceValue.anchor)) then
118
+ level << destinationKey
119
+ level << destinationValue
120
+ elsif (sourceValue.is_a?(Psych::Nodes::Scalar)) then
121
+ level << destinationKey
122
+ level << destinationValue
123
+ end
124
+ end
125
+ end
126
+ }
127
+
128
+ if (includeAnchorDependencies)
129
+ # Find source pairs where value is alias that don't exist in destination
130
+ # Look for aliases whose anchor was modified
131
+ sourceChildren.find_all {|i|
132
+ i[1].is_a?(Psych::Nodes::Alias) &&
133
+ !i[1].anchor.nil? &&
134
+ @modified_anchors.include?(i[1].anchor)
135
+ }.each {|sp|
136
+ dPair = destinationChildren.find_all {|i| i[0].value == sp[0].value}.last
137
+
138
+ if (dPair == nil) then
139
+ level << sp[0]
140
+ level << sp[1]
141
+ end
142
+ }
143
+ end
144
+
145
+ level
146
+ end
147
+
148
+ def processModifiedAnchors(s, anchors)
149
+ level = []
150
+
151
+ # Split into key/value pairs
152
+ sourceChildren = s.each_slice(2).to_a
153
+
154
+ sourceChildren.each {|sourcePair|
155
+ sourceKey = sourcePair[0]
156
+ sourceValue = sourcePair[1]
157
+
158
+ # Check if merge key
159
+ if (sourceKey.value == "<<") then
160
+ # Check if in modified_anchors
161
+
162
+ if (sourceValue.is_a?(Psych::Nodes::Sequence)) then
163
+ # Check each anchor in sequence
164
+ aliases = sourceValue.children.find_all {|a| anchors.include?(a.anchor)}
165
+
166
+ if (aliases.length > 0) then
167
+ if (aliases.length == 1) then
168
+ level << sourceKey
169
+ level << aliases[0]
170
+ else
171
+ newSequence = Psych::Nodes::Sequence.new(sourceValue.anchor, sourceValue.tag, sourceValue.implicit, sourceValue.style)
172
+ newSequence.children.push(*aliases)
173
+
174
+ level << sourceKey
175
+ level << newSequence
176
+ end
177
+ end
178
+ else
179
+ if (anchors.include?(sourceValue.anchor)) then
180
+ # Add the merge key and include in the level
181
+ level << sourceKey
182
+ level << sourceValue
183
+ end
184
+ end
185
+
186
+ next
187
+ elsif (sourceValue.is_a?(Psych::Nodes::Mapping)) then
188
+ childLevel = processModifiedAnchors(sourceValue.children, anchors)
189
+
190
+ if (childLevel.length > 0) then
191
+ newMapping = Psych::Nodes::Mapping.new(sourceValue.anchor, sourceValue.tag, sourceValue.implicit, sourceValue.style)
192
+ newMapping.children.push(*childLevel)
193
+
194
+ level << sourceKey
195
+ level << newMapping
196
+
197
+ # Add new anchors that are located
198
+ if (sourceValue.anchor != nil) then
199
+ @modified_anchors << sourceValue.anchor
200
+ end
201
+ end
202
+ elsif (sourceValue.is_a?(Psych::Nodes::Alias) && anchors.include?(sourceValue.anchor)) then
203
+ # Add the merge key and include in the level
204
+ level << sourceKey
205
+ level << sourceValue
206
+ end
207
+ }
208
+
209
+ level
210
+ end
211
+
212
+ def compare_files(sourceFilePath, destinationFilePath, includeAnchorDependencies = false)
213
+ sourceFile = File.open(sourceFilePath, "r")
214
+ differenceFile = File.open(destinationFilePath, "r")
215
+
216
+ begin
217
+ result = compare(sourceFile, differenceFile, includeAnchorDependencies)
218
+ ensure
219
+ sourceFile.close
220
+ differenceFile.close
221
+ end
222
+
223
+ result
224
+ end
225
+
226
+ def compare(source, destination, includeAnchorDependencies = false)
227
+ @source_anchors = {}
228
+ @modified_anchors = []
229
+
230
+ sourceDocument = YAML.parse(source)
231
+ destinationDocument = YAML.parse(destination)
232
+
233
+ # Flatten merge keys for older ArchivesSpace files
234
+ sourceRootLevel = YAMLTools.flatten_merge_keys(sourceDocument.root.children)
235
+ destinationRootLevel = YAMLTools.flatten_merge_keys(destinationDocument.root.children)
236
+
237
+ @differences = processLevel(sourceRootLevel, destinationRootLevel, includeAnchorDependencies)
238
+
239
+ if (includeAnchorDependencies) then
240
+ @current_modified_anchors = []
241
+
242
+ combiner = Combiner.new
243
+
244
+ loop do
245
+ @current_modified_anchors = @modified_anchors.clone
246
+ @modified_anchors = []
247
+ modified_anchor_additions = processModifiedAnchors(sourceRootLevel, @current_modified_anchors)
248
+
249
+ if (modified_anchor_additions.length > 0) then
250
+ mergedDifferences = combiner.combine_levels(@differences, modified_anchor_additions)
251
+ @differences = mergedDifferences
252
+ end
253
+
254
+ break if (@current_modified_anchors.length == 0)
255
+ end
256
+ end
257
+
258
+ if (@differences.length > 0) then
259
+ output = YAMLTools.createDocument(@differences)
260
+ else
261
+ output = ''
262
+ end
263
+
264
+ output
265
+ end
266
+ end
267
+ end
@@ -0,0 +1,74 @@
1
+ require 'yaml'
2
+
3
+ module YAMLTools
4
+ def self.createDocument(levels)
5
+ document = Psych::Nodes::Document.new()
6
+ documentRoot = Psych::Nodes::Mapping.new();
7
+ documentRoot.children.push(*levels)
8
+ document.children << documentRoot
9
+
10
+ stream = Psych::Nodes::Stream.new
11
+ stream.children << document
12
+ output = stream.to_yaml
13
+
14
+ # Remove initial document start
15
+ document_start = (output.index("---\n") || size - 1) + 4
16
+ output.slice!(0, document_start)
17
+
18
+ output
19
+ end
20
+
21
+ def self.flatten_merge_keys(s)
22
+ level = []
23
+
24
+ sourceChildren = s.each_slice(2).to_a
25
+
26
+ mergeKeys = sourceChildren.find_all {|i| i[0].value == "<<" }
27
+
28
+ if (mergeKeys.length > 0) then
29
+ if (mergeKeys.length == 1) then
30
+ # Add merge key
31
+ level << mergeKeys.first[0]
32
+ level << mergeKeys.first[1]
33
+ else
34
+ newSequence = Psych::Nodes::Sequence.new(nil, nil, true, Psych::Nodes::Sequence::FLOW)
35
+
36
+ mergeKeys.each {|m|
37
+ if (m[1].is_a?(Psych::Nodes::Alias)) then
38
+ newSequence.children << m[1]
39
+ elsif (m[1].is_a?(Psych::Nodes::Sequence)) then
40
+ newSequence.children.concat(m[1].children)
41
+ end
42
+ }
43
+
44
+ level << Psych::Nodes::Scalar.new("<<")
45
+ level << newSequence
46
+ end
47
+ end
48
+
49
+ sourceChildren.each {|sourcePair|
50
+ sourceKey = sourcePair[0]
51
+ sourceValue = sourcePair[1]
52
+
53
+ if (sourceValue.is_a?(Psych::Nodes::Mapping)) then
54
+ childLevel = flatten_merge_keys(sourceValue.children)
55
+
56
+ newMapping = Psych::Nodes::Mapping.new(sourceValue.anchor, sourceValue.tag, sourceValue.implicit, sourceValue.style)
57
+
58
+ if (childLevel.length > 0) then
59
+ newMapping.children.push(*childLevel)
60
+ end
61
+
62
+ level << sourceKey
63
+ level << newMapping
64
+ else
65
+ if (sourceKey.value != "<<") then
66
+ level << sourceKey
67
+ level << sourceValue
68
+ end
69
+ end
70
+ }
71
+
72
+ level
73
+ end
74
+ end
data/lib/yaml_tools.rb ADDED
@@ -0,0 +1,5 @@
1
+ module YAMLTools
2
+ require 'tools/combine'
3
+ require 'tools/compare'
4
+ require 'tools/utility'
5
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: yaml_tools
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.4
5
+ platform: ruby
6
+ authors:
7
+ - Atlas Systems, Inc.
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-02-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.12'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.12'
27
+ description:
28
+ email:
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - lib/tools/combine.rb
34
+ - lib/tools/compare.rb
35
+ - lib/tools/utility.rb
36
+ - lib/yaml_tools.rb
37
+ homepage: https://github.com/AtlasSystems/yaml_tools
38
+ licenses:
39
+ - MIT
40
+ metadata: {}
41
+ post_install_message:
42
+ rdoc_options: []
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '2.5'
50
+ - - "<"
51
+ - !ruby/object:Gem::Version
52
+ version: '4'
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ requirements: []
59
+ rubygems_version: 3.4.6
60
+ signing_key:
61
+ specification_version: 4
62
+ summary: Tools for YAML files.
63
+ test_files: []