immosquare-yaml 0.1.27 → 0.1.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/immosquare-yaml/shared_methods.rb +1 -1
- data/lib/immosquare-yaml/version.rb +1 -1
- data/lib/immosquare-yaml.rb +74 -78
- metadata +4 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a3b992a868778a9c56accd519704c486fe36d61628d99f5494f92f0400f1cde5
|
4
|
+
data.tar.gz: ff1f1567cd7bd741a73f10ce5378375e0d464da5a33d7a4cf7e332d4c3c602a6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8bcef442f5fe2c707f674807421071a3d8c948569f753bedcb2b77002fbe9c8c076ce000a1b461fae25f3228cfed282c25cdd2d461ae0f02cb9e2664fdb3633a
|
7
|
+
data.tar.gz: 98f6c75b116d1bb6d216fb9d5fab628b2c7a1b6894e0b1d7d8309c2b351ad10a3c06311c365b94b6389ed5659d83bf90d48941c1e774effc33940e67e43c70da
|
@@ -8,7 +8,7 @@ module ImmosquareYaml
|
|
8
8
|
DOUBLE_QUOTE = '"'.freeze
|
9
9
|
DOUBLE_SIMPLE_QUOTE = "''".freeze
|
10
10
|
CUSTOM_SEPARATOR = "_#_#_".freeze
|
11
|
-
WEIRD_QUOTES_REGEX = /‘|’|“|”|‛|‚|„|‟|#{Regexp.quote(DOUBLE_SIMPLE_QUOTE)}
|
11
|
+
WEIRD_QUOTES_REGEX = /‘|’|“|”|‛|‚|„|‟|#{Regexp.quote(DOUBLE_SIMPLE_QUOTE)}/
|
12
12
|
YML_SPECIAL_CHARS = ["-", "`", "{", "}", "|", "[", "]", ">", ":", "\"", "'", "*", "=", "%", ",", "!", "?", "&", "#", "@"].freeze
|
13
13
|
RESERVED_KEYS = [
|
14
14
|
"yes", "no", "on", "off", "true", "false",
|
data/lib/immosquare-yaml.rb
CHANGED
@@ -1,25 +1,26 @@
|
|
1
1
|
require "English"
|
2
2
|
require "psych"
|
3
3
|
require "date"
|
4
|
+
require "fileutils"
|
4
5
|
require "immosquare-extensions"
|
5
6
|
require_relative "immosquare-yaml/configuration"
|
6
7
|
require_relative "immosquare-yaml/shared_methods"
|
7
8
|
require_relative "immosquare-yaml/railtie" if defined?(Rails)
|
8
9
|
|
9
|
-
|
10
|
+
##============================================================##
|
10
11
|
## Importing the 'English' library allows us to use more human-readable
|
11
12
|
## global variables, such as $INPUT_RECORD_SEPARATOR instead of $/,
|
12
13
|
## which enhances code clarity and makes it easier to understand
|
13
14
|
## the purpose of these variables in our code.
|
14
|
-
|
15
|
+
##============================================================##
|
15
16
|
module ImmosquareYaml
|
16
17
|
extend SharedMethods
|
17
18
|
|
18
19
|
class << self
|
19
20
|
|
20
|
-
|
21
|
+
##============================================================##
|
21
22
|
## Gem configuration
|
22
|
-
|
23
|
+
##============================================================##
|
23
24
|
attr_writer :configuration
|
24
25
|
|
25
26
|
def configuration
|
@@ -30,7 +31,7 @@ module ImmosquareYaml
|
|
30
31
|
yield(configuration)
|
31
32
|
end
|
32
33
|
|
33
|
-
|
34
|
+
##============================================================##
|
34
35
|
## This method cleans a specified YAML file by processing it line by line.
|
35
36
|
## It executes a comprehensive cleaning routine, which involves parsing the
|
36
37
|
## YAML content to a hash, optionally sorting it, and then dumping it back
|
@@ -42,7 +43,7 @@ module ImmosquareYaml
|
|
42
43
|
##
|
43
44
|
## Returns:
|
44
45
|
## Boolean indicating the success (true) or failure (false) of the operation.
|
45
|
-
|
46
|
+
##============================================================##
|
46
47
|
def clean(file_path, **options)
|
47
48
|
##============================================================##
|
48
49
|
## Default options
|
@@ -56,41 +57,41 @@ module ImmosquareYaml
|
|
56
57
|
output_file_path = nil
|
57
58
|
raise("File not found") if !File.exist?(file_path)
|
58
59
|
|
59
|
-
|
60
|
+
##============================================================##
|
60
61
|
## Setup variables
|
61
|
-
|
62
|
+
##============================================================##
|
62
63
|
output_file_path = options[:output]
|
63
64
|
|
64
|
-
|
65
|
+
##============================================================##
|
65
66
|
## Backup original content for restoration after parsing if necessary
|
66
|
-
|
67
|
+
##============================================================##
|
67
68
|
original_content = File.read(file_path) if output_file_path != file_path
|
68
69
|
|
69
|
-
|
70
|
+
##============================================================##
|
70
71
|
## The cleaning procedure is initialized with a comprehensive clean, transforming
|
71
72
|
## the YAML content to a hash to facilitate optional sorting, before
|
72
73
|
## rewriting it to the YAML file in its cleaned and optionally sorted state.
|
73
|
-
|
74
|
+
##============================================================##
|
74
75
|
clean_yml(file_path)
|
75
76
|
parsed_yml = parse(file_path)
|
76
77
|
parsed_yml = parsed_yml.sort_by_key
|
77
78
|
parsed_yml = dump(parsed_yml)
|
78
79
|
|
79
|
-
|
80
|
+
##============================================================##
|
80
81
|
## Restore original content if necessary
|
81
|
-
|
82
|
+
##============================================================##
|
82
83
|
File.write(file_path, original_content) if output_file_path != file_path
|
83
84
|
|
84
|
-
|
85
|
+
##============================================================##
|
85
86
|
## Write the cleaned YAML content to the specified output file
|
86
|
-
|
87
|
+
##============================================================##
|
87
88
|
FileUtils.mkdir_p(File.dirname(output_file_path))
|
88
89
|
File.write(output_file_path, parsed_yml)
|
89
90
|
true
|
90
91
|
rescue StandardError => e
|
91
|
-
|
92
|
+
##============================================================##
|
92
93
|
## Restore original content if necessary
|
93
|
-
|
94
|
+
##============================================================##
|
94
95
|
File.write(file_path, original_content) if output_file_path != file_path && !original_content.nil?
|
95
96
|
puts(e.message)
|
96
97
|
puts(e.backtrace)
|
@@ -98,7 +99,7 @@ module ImmosquareYaml
|
|
98
99
|
end
|
99
100
|
end
|
100
101
|
|
101
|
-
|
102
|
+
##============================================================##
|
102
103
|
## This method parses a specified YAML file, carrying out a preliminary
|
103
104
|
## cleaning operation to ensure a smooth parsing process. Following this,
|
104
105
|
## the cleaned file is transformed into a hash, which can optionally be sorted.
|
@@ -110,7 +111,7 @@ module ImmosquareYaml
|
|
110
111
|
##
|
111
112
|
## Returns:
|
112
113
|
## A hash representation of the YAML file or false if an error occurs.
|
113
|
-
|
114
|
+
##============================================================##
|
114
115
|
def parse(file_path, **options)
|
115
116
|
options = {:sort => true}.merge(options)
|
116
117
|
|
@@ -118,35 +119,35 @@ module ImmosquareYaml
|
|
118
119
|
original_content = nil
|
119
120
|
raise("File not found") if !File.exist?(file_path)
|
120
121
|
|
121
|
-
|
122
|
+
##============================================================##
|
122
123
|
## Backup original content for restoration after parsing
|
123
|
-
|
124
|
+
##============================================================##
|
124
125
|
original_content = File.read(file_path)
|
125
126
|
|
126
|
-
|
127
|
+
##============================================================##
|
127
128
|
## clean the file
|
128
|
-
|
129
|
+
##============================================================##
|
129
130
|
clean_yml(file_path)
|
130
131
|
|
131
|
-
|
132
|
+
##============================================================##
|
132
133
|
## parse the file & sort if necessary
|
133
|
-
|
134
|
+
##============================================================##
|
134
135
|
parsed_xml = parse_xml(file_path)
|
135
136
|
parsed_xml = parsed_xml.sort_by_key if options[:sort]
|
136
137
|
|
137
|
-
|
138
|
+
##============================================================##
|
138
139
|
## Restore original content
|
139
|
-
|
140
|
+
##============================================================##
|
140
141
|
File.write(file_path, original_content) if !original_content.nil?
|
141
142
|
|
142
|
-
|
143
|
+
##============================================================##
|
143
144
|
## Return the parsed YAML file
|
144
|
-
|
145
|
+
##============================================================##
|
145
146
|
parsed_xml
|
146
147
|
rescue StandardError => e
|
147
|
-
|
148
|
+
##============================================================##
|
148
149
|
## Restore original content
|
149
|
-
|
150
|
+
##============================================================##
|
150
151
|
File.write(file_path, original_content) if !original_content.nil?
|
151
152
|
puts(e.message)
|
152
153
|
puts(e.backtrace)
|
@@ -154,7 +155,7 @@ module ImmosquareYaml
|
|
154
155
|
end
|
155
156
|
end
|
156
157
|
|
157
|
-
|
158
|
+
##============================================================##
|
158
159
|
## This method performs a dump operation to obtain a well-structured
|
159
160
|
## YAML file from a hash input. It iterates through each key-value pair in the
|
160
161
|
## hash and constructs a series of lines representing the YAML file, with
|
@@ -168,13 +169,13 @@ module ImmosquareYaml
|
|
168
169
|
##
|
169
170
|
## Returns:
|
170
171
|
## A string representing the YAML representation of the input hash.
|
171
|
-
|
172
|
+
##============================================================##
|
172
173
|
def dump(hash, lines = [], indent = 0)
|
173
174
|
hash.each do |key, value|
|
174
|
-
|
175
|
+
##============================================================##
|
175
176
|
## Preparing the key with the proper indentation before identifying
|
176
177
|
## the type of the value to handle it appropriately in the YAML representation.
|
177
|
-
|
178
|
+
##============================================================##
|
178
179
|
line = "#{SPACE * indent}#{clean_key(key)}:"
|
179
180
|
|
180
181
|
case value
|
@@ -182,12 +183,12 @@ module ImmosquareYaml
|
|
182
183
|
lines << "#{line} null"
|
183
184
|
when String
|
184
185
|
if value.include?(NEWLINE) || value.include?('\n')
|
185
|
-
|
186
|
+
##============================================================##
|
186
187
|
## We display the line with the key
|
187
188
|
## then the indentation if necessary
|
188
189
|
## then - if necessary (the + is not displayed because it is
|
189
190
|
## the default behavior)
|
190
|
-
|
191
|
+
##============================================================##
|
191
192
|
line += "#{SPACE}|"
|
192
193
|
indent_level = value[/\A */].size
|
193
194
|
line += (indent_level + INDENT_SIZE).to_s if indent_level > 0
|
@@ -201,9 +202,9 @@ module ImmosquareYaml
|
|
201
202
|
value = value[1..-2] while (value.start_with?(DOUBLE_QUOTE) && value.end_with?(DOUBLE_QUOTE)) || (value.start_with?(SIMPLE_QUOTE) && value.end_with?(SIMPLE_QUOTE))
|
202
203
|
|
203
204
|
|
204
|
-
|
205
|
+
##============================================================##
|
205
206
|
## We parse on the 2 types of line breaks
|
206
|
-
|
207
|
+
##============================================================##
|
207
208
|
value.split(/\\n|\n/).each do |subline|
|
208
209
|
lines << "#{SPACE * (indent + INDENT_SIZE)}#{subline}"
|
209
210
|
end
|
@@ -228,10 +229,10 @@ module ImmosquareYaml
|
|
228
229
|
end
|
229
230
|
end
|
230
231
|
|
231
|
-
|
232
|
+
##============================================================##
|
232
233
|
## Finalizing the construction by adding a newline at the end and
|
233
234
|
## removing whitespace from empty lines.
|
234
|
-
|
235
|
+
##============================================================##
|
235
236
|
lines += [NOTHING]
|
236
237
|
lines = lines.map {|l| l.strip.empty? ? NOTHING : l }
|
237
238
|
lines.join("\n")
|
@@ -253,20 +254,20 @@ module ImmosquareYaml
|
|
253
254
|
weirdblock = false
|
254
255
|
line_index = 1
|
255
256
|
|
256
|
-
|
257
|
+
##============================================================##
|
257
258
|
## First, we normalize the file by ensuring it always ends with an empty line
|
258
259
|
## This also allows us to get the total number of lines in the file,
|
259
260
|
## helping us to determine when we are processing the last line
|
260
|
-
|
261
|
+
##============================================================##
|
261
262
|
line_count = File.normalize_last_line(file_path)
|
262
263
|
|
263
264
|
|
264
265
|
File.foreach(file_path) do |current_line|
|
265
266
|
last_line = line_index == line_count
|
266
267
|
|
267
|
-
|
268
|
+
##============================================================##
|
268
269
|
## Cleaning the current line by removing multiple spaces occurring after a non-space character
|
269
|
-
|
270
|
+
##============================================================##
|
270
271
|
current_line = current_line.to_s.gsub(/(?<=\S)\s+/, SPACE)
|
271
272
|
|
272
273
|
##============================================================##
|
@@ -274,10 +275,10 @@ module ImmosquareYaml
|
|
274
275
|
##============================================================##
|
275
276
|
current_line = current_line.rstrip
|
276
277
|
|
277
|
-
|
278
|
+
##============================================================##
|
278
279
|
## Detecting blank lines to specially handle the last line within a block;
|
279
280
|
## if we are inside a block or it's the last line, we avoid skipping
|
280
|
-
|
281
|
+
##============================================================##
|
281
282
|
blank_line = current_line.gsub(NEWLINE, NOTHING).empty?
|
282
283
|
next if !(last_line || inblock || !blank_line)
|
283
284
|
|
@@ -289,10 +290,10 @@ module ImmosquareYaml
|
|
289
290
|
need_to_clean_prev_inblock = inblock == true && ((!blank_line && indent_level <= inblock_indent) || last_line)
|
290
291
|
need_to_clen_prev_weirdblock = weirdblock == true && (indent_level <= weirdblock_indent || last_line)
|
291
292
|
|
292
|
-
|
293
|
+
##============================================================##
|
293
294
|
## Handling the exit from a block:
|
294
295
|
## if we are exiting a block, we clean the entire block
|
295
|
-
|
296
|
+
##============================================================##
|
296
297
|
if need_to_clean_prev_inblock
|
297
298
|
inblock = false
|
298
299
|
##============================================================##
|
@@ -325,7 +326,6 @@ module ImmosquareYaml
|
|
325
326
|
## transforming the block of text into a single line.
|
326
327
|
## However, it preserves newlines that follow an empty line.
|
327
328
|
## Final new line: A new line is added at the end of the text.
|
328
|
-
## ===
|
329
329
|
## We can also have |4- or |4+ to say with indentation 4
|
330
330
|
##============================================================##
|
331
331
|
block_lines = block_lines.reverse
|
@@ -345,17 +345,17 @@ module ImmosquareYaml
|
|
345
345
|
end
|
346
346
|
end
|
347
347
|
|
348
|
-
|
348
|
+
##============================================================##
|
349
349
|
## Handling 'weirdblocks': cases where multi-line values are enclosed in quotes,
|
350
350
|
## which should actually be single-line values
|
351
|
-
##
|
352
|
-
##
|
353
|
-
##
|
354
|
-
##
|
355
|
-
##
|
356
|
-
##
|
357
|
-
##
|
358
|
-
##
|
351
|
+
## key: "
|
352
|
+
## line1
|
353
|
+
## line2
|
354
|
+
## line3"
|
355
|
+
## key: '
|
356
|
+
## line1
|
357
|
+
## line2
|
358
|
+
## line3'
|
359
359
|
##============================================================##
|
360
360
|
if need_to_clen_prev_weirdblock
|
361
361
|
weirdblock = false
|
@@ -363,10 +363,10 @@ module ImmosquareYaml
|
|
363
363
|
lines[-1] = "#{key}: #{clean_value(value)}"
|
364
364
|
end
|
365
365
|
|
366
|
-
|
366
|
+
##============================================================##
|
367
367
|
## Handling keys without values: if the previous line ends with a colon (:) and is not
|
368
368
|
## followed by a value, we assign 'null' as the value
|
369
|
-
|
369
|
+
##============================================================##
|
370
370
|
if inblock == false && weirdblock == false && lines[-1] && lines[-1].end_with?(":") && last_inblock == false
|
371
371
|
prev_indent = lines[-1][/\A */].size
|
372
372
|
lines[-1] += " null" if prev_indent >= indent_level
|
@@ -381,35 +381,33 @@ module ImmosquareYaml
|
|
381
381
|
split = inblock || weirdblock ? [current_line] : current_line.strip.split(":", 2)
|
382
382
|
key = inblock || weirdblock ? nil : split[0].to_s.strip
|
383
383
|
|
384
|
-
|
384
|
+
##============================================================##
|
385
385
|
## Line processing based on various conditions such as being inside a block,
|
386
386
|
## starting with a comment symbol (#), or being a part of a 'weirdblock'
|
387
387
|
## Each case has its specific line cleaning strategy
|
388
|
-
## ----
|
389
388
|
## If the line is commented out, we keep and we remove newlines
|
390
389
|
##============================================================##
|
391
390
|
if current_line.lstrip.start_with?("#")
|
392
391
|
lines << current_line.gsub(NEWLINE, NOTHING)
|
393
|
-
|
392
|
+
##============================================================##
|
394
393
|
## If is in a block (multiline > | or |-), we clean
|
395
394
|
## the line because it can start with spaces tabs etc.
|
396
395
|
## and put it with the block indenter
|
397
|
-
|
396
|
+
##============================================================##
|
398
397
|
elsif inblock == true
|
399
398
|
current_line = current_line.gsub(NEWLINE, NOTHING).strip
|
400
399
|
lines << "#{SPACE * (inblock_indent + INDENT_SIZE)}#{current_line}"
|
401
|
-
|
400
|
+
##============================================================##
|
402
401
|
## if the line ends with a multi-line character and we have a key.
|
403
402
|
## we start a block
|
404
403
|
## The regex works as follows:
|
405
|
-
##=========================================================
|
406
404
|
## \S+ : All non-space characters at the start of the line.
|
407
405
|
## : : Matches the string ": " literally (space included).
|
408
406
|
## [>|] : Matches a single character that is either ">" or "|".
|
409
407
|
## (\d*) : Capture group that matches zero or more digits (0-9).
|
410
408
|
## [-+]? : Matches zero or a character that is either "-" or "+".
|
411
409
|
## $ : Matches the end of the line/string.
|
412
|
-
|
410
|
+
##============================================================##
|
413
411
|
elsif current_line.rstrip.match?(/\S+: [>|](\d*)[-+]?$/)
|
414
412
|
lines << current_line.gsub(NEWLINE, NOTHING)
|
415
413
|
inblock_indent = indent_level
|
@@ -419,9 +417,9 @@ module ImmosquareYaml
|
|
419
417
|
## but without > | or |- at the end of the line
|
420
418
|
## which should actually be inline.
|
421
419
|
## mykey:
|
422
|
-
##
|
423
|
-
##
|
424
|
-
##
|
420
|
+
## line1
|
421
|
+
## line2
|
422
|
+
## line3
|
425
423
|
## my key: line1 line2 line3
|
426
424
|
##============================================================##
|
427
425
|
elsif split.size < 2
|
@@ -484,7 +482,6 @@ module ImmosquareYaml
|
|
484
482
|
##============================================================##
|
485
483
|
## clean_key Function
|
486
484
|
## Purpose: Clean up and standardize YAML keys
|
487
|
-
##============================================================##
|
488
485
|
## Strategy:
|
489
486
|
## 1. Forcefully convert the key to a string to handle gsub operations, especially if it's an integer.
|
490
487
|
## 2. Remove quotes if they are present.
|
@@ -510,7 +507,6 @@ module ImmosquareYaml
|
|
510
507
|
is_int = key =~ /\A[-+]?\d+\z/
|
511
508
|
|
512
509
|
##============================================================##
|
513
|
-
##
|
514
510
|
## Re-add quotes if the key is in the list of reserved keys or is an integer
|
515
511
|
##============================================================##
|
516
512
|
key = "\"#{key}\"" if RESERVED_KEYS.include?(key) || is_int
|
@@ -534,7 +530,7 @@ module ImmosquareYaml
|
|
534
530
|
## Purpose: Sanitize and standardize YAML values
|
535
531
|
## In YAML "inblock" scenarios, there's no need to add quotes
|
536
532
|
## around values as it's inherently handled.
|
537
|
-
|
533
|
+
##============================================================##
|
538
534
|
def clean_value(values, with_quotes_verif = true)
|
539
535
|
##============================================================##
|
540
536
|
## Convert key to array if not
|
@@ -555,7 +551,7 @@ module ImmosquareYaml
|
|
555
551
|
## Remove newline characters at the end of the value if present.
|
556
552
|
## This should be done prior to strip operation to handle scenarios
|
557
553
|
## where the value ends with a space followed by a newline.
|
558
|
-
|
554
|
+
##============================================================##
|
559
555
|
value = value[0..-2] if value.end_with?(NEWLINE)
|
560
556
|
|
561
557
|
|
@@ -597,7 +593,7 @@ module ImmosquareYaml
|
|
597
593
|
##============================================================##
|
598
594
|
value = value.gsub(/\\U([0-9A-Fa-f]{8})/) { [::Regexp.last_match(1).to_i(16)].pack("U*") }
|
599
595
|
|
600
|
-
|
596
|
+
##============================================================##
|
601
597
|
## Handling cases where the value must be surrounded by quotes
|
602
598
|
## if:
|
603
599
|
## management of "" and " ". Not possible to have more spaces
|
@@ -612,7 +608,7 @@ module ImmosquareYaml
|
|
612
608
|
## RESERVED_KEYS.include?(value) => key: YES
|
613
609
|
## value.start_with?(SPACE) => key: 'text'
|
614
610
|
## value.end_with?(SPACE) => key: text '
|
615
|
-
|
611
|
+
##============================================================##
|
616
612
|
if value.empty?
|
617
613
|
value = "\"#{value}\""
|
618
614
|
elsif with_quotes_verif == true
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: immosquare-yaml
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.28
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- immosquare
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: immosquare-extensions
|
@@ -49,7 +48,6 @@ homepage: https://github.com/immosquare/immosquare-yaml
|
|
49
48
|
licenses:
|
50
49
|
- MIT
|
51
50
|
metadata: {}
|
52
|
-
post_install_message:
|
53
51
|
rdoc_options: []
|
54
52
|
require_paths:
|
55
53
|
- lib
|
@@ -57,15 +55,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
57
55
|
requirements:
|
58
56
|
- - ">="
|
59
57
|
- !ruby/object:Gem::Version
|
60
|
-
version: 2.
|
58
|
+
version: 3.2.6
|
61
59
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
60
|
requirements:
|
63
61
|
- - ">="
|
64
62
|
- !ruby/object:Gem::Version
|
65
63
|
version: '0'
|
66
64
|
requirements: []
|
67
|
-
rubygems_version: 3.
|
68
|
-
signing_key:
|
65
|
+
rubygems_version: 3.7.1
|
69
66
|
specification_version: 4
|
70
67
|
summary: A YAML parser optimized for translation files.
|
71
68
|
test_files: []
|