psych 2.0.12 → 5.2.3

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.
Files changed (115) hide show
  1. checksums.yaml +5 -5
  2. data/CONTRIBUTING.md +24 -0
  3. data/{ext/psych/yaml/LICENSE → LICENSE} +9 -7
  4. data/README.md +80 -0
  5. data/ext/psych/depend +14 -0
  6. data/ext/psych/extconf.rb +43 -28
  7. data/ext/psych/psych.c +6 -4
  8. data/ext/psych/psych.h +0 -3
  9. data/ext/psych/psych_emitter.c +165 -132
  10. data/ext/psych/psych_parser.c +298 -331
  11. data/ext/psych/psych_to_ruby.c +0 -5
  12. data/ext/psych/psych_yaml_tree.c +0 -13
  13. data/lib/psych/class_loader.rb +11 -8
  14. data/lib/psych/coder.rb +1 -0
  15. data/lib/psych/core_ext.rb +3 -19
  16. data/lib/psych/exception.rb +17 -2
  17. data/lib/psych/handler.rb +8 -2
  18. data/lib/psych/handlers/document_stream.rb +2 -1
  19. data/lib/psych/handlers/recorder.rb +2 -1
  20. data/lib/psych/json/ruby_events.rb +1 -0
  21. data/lib/psych/json/stream.rb +3 -2
  22. data/lib/psych/json/tree_builder.rb +2 -1
  23. data/lib/psych/json/yaml_events.rb +1 -0
  24. data/lib/psych/nodes/alias.rb +3 -0
  25. data/lib/psych/nodes/document.rb +3 -0
  26. data/lib/psych/nodes/mapping.rb +3 -0
  27. data/lib/psych/nodes/node.rb +25 -5
  28. data/lib/psych/nodes/scalar.rb +4 -1
  29. data/lib/psych/nodes/sequence.rb +3 -0
  30. data/lib/psych/nodes/stream.rb +3 -0
  31. data/lib/psych/nodes.rb +8 -7
  32. data/lib/psych/omap.rb +1 -0
  33. data/lib/psych/parser.rb +14 -0
  34. data/lib/psych/scalar_scanner.rb +41 -49
  35. data/lib/psych/set.rb +1 -0
  36. data/lib/psych/stream.rb +1 -0
  37. data/lib/psych/streaming.rb +1 -0
  38. data/lib/psych/syntax_error.rb +2 -1
  39. data/lib/psych/tree_builder.rb +48 -7
  40. data/lib/psych/versions.rb +10 -0
  41. data/lib/psych/visitors/depth_first.rb +1 -0
  42. data/lib/psych/visitors/emitter.rb +1 -0
  43. data/lib/psych/visitors/json_tree.rb +2 -1
  44. data/lib/psych/visitors/to_ruby.rb +64 -33
  45. data/lib/psych/visitors/visitor.rb +18 -3
  46. data/lib/psych/visitors/yaml_tree.rb +128 -149
  47. data/lib/psych/visitors.rb +7 -6
  48. data/lib/psych/y.rb +1 -0
  49. data/lib/psych.rb +360 -95
  50. metadata +36 -169
  51. data/.autotest +0 -18
  52. data/.gemtest +0 -0
  53. data/.travis.yml +0 -11
  54. data/CHANGELOG.rdoc +0 -562
  55. data/Manifest.txt +0 -112
  56. data/README.rdoc +0 -71
  57. data/Rakefile +0 -74
  58. data/ext/psych/yaml/api.c +0 -1415
  59. data/ext/psych/yaml/config.h +0 -10
  60. data/ext/psych/yaml/dumper.c +0 -394
  61. data/ext/psych/yaml/emitter.c +0 -2329
  62. data/ext/psych/yaml/loader.c +0 -459
  63. data/ext/psych/yaml/parser.c +0 -1370
  64. data/ext/psych/yaml/reader.c +0 -469
  65. data/ext/psych/yaml/scanner.c +0 -3583
  66. data/ext/psych/yaml/writer.c +0 -141
  67. data/ext/psych/yaml/yaml.h +0 -1971
  68. data/ext/psych/yaml/yaml_private.h +0 -664
  69. data/lib/psych/deprecated.rb +0 -85
  70. data/test/psych/handlers/test_recorder.rb +0 -25
  71. data/test/psych/helper.rb +0 -114
  72. data/test/psych/json/test_stream.rb +0 -109
  73. data/test/psych/nodes/test_enumerable.rb +0 -43
  74. data/test/psych/test_alias_and_anchor.rb +0 -96
  75. data/test/psych/test_array.rb +0 -57
  76. data/test/psych/test_boolean.rb +0 -36
  77. data/test/psych/test_class.rb +0 -36
  78. data/test/psych/test_coder.rb +0 -184
  79. data/test/psych/test_date_time.rb +0 -38
  80. data/test/psych/test_deprecated.rb +0 -214
  81. data/test/psych/test_document.rb +0 -46
  82. data/test/psych/test_emitter.rb +0 -93
  83. data/test/psych/test_encoding.rb +0 -259
  84. data/test/psych/test_exception.rb +0 -157
  85. data/test/psych/test_hash.rb +0 -94
  86. data/test/psych/test_json_tree.rb +0 -65
  87. data/test/psych/test_marshalable.rb +0 -54
  88. data/test/psych/test_merge_keys.rb +0 -180
  89. data/test/psych/test_nil.rb +0 -18
  90. data/test/psych/test_null.rb +0 -19
  91. data/test/psych/test_numeric.rb +0 -45
  92. data/test/psych/test_object.rb +0 -44
  93. data/test/psych/test_object_references.rb +0 -71
  94. data/test/psych/test_omap.rb +0 -75
  95. data/test/psych/test_parser.rb +0 -339
  96. data/test/psych/test_psych.rb +0 -168
  97. data/test/psych/test_safe_load.rb +0 -97
  98. data/test/psych/test_scalar.rb +0 -11
  99. data/test/psych/test_scalar_scanner.rb +0 -106
  100. data/test/psych/test_serialize_subclasses.rb +0 -38
  101. data/test/psych/test_set.rb +0 -49
  102. data/test/psych/test_stream.rb +0 -93
  103. data/test/psych/test_string.rb +0 -226
  104. data/test/psych/test_struct.rb +0 -49
  105. data/test/psych/test_symbol.rb +0 -25
  106. data/test/psych/test_tainted.rb +0 -130
  107. data/test/psych/test_to_yaml_properties.rb +0 -63
  108. data/test/psych/test_tree_builder.rb +0 -79
  109. data/test/psych/test_yaml.rb +0 -1288
  110. data/test/psych/test_yamldbm.rb +0 -193
  111. data/test/psych/test_yamlstore.rb +0 -85
  112. data/test/psych/visitors/test_depth_first.rb +0 -49
  113. data/test/psych/visitors/test_emitter.rb +0 -144
  114. data/test/psych/visitors/test_to_ruby.rb +0 -326
  115. data/test/psych/visitors/test_yaml_tree.rb +0 -173
data/lib/psych.rb CHANGED
@@ -1,27 +1,41 @@
1
- require 'psych.so'
2
- require 'psych/nodes'
3
- require 'psych/streaming'
4
- require 'psych/visitors'
5
- require 'psych/handler'
6
- require 'psych/tree_builder'
7
- require 'psych/parser'
8
- require 'psych/omap'
9
- require 'psych/set'
10
- require 'psych/coder'
11
- require 'psych/core_ext'
12
- require 'psych/deprecated'
13
- require 'psych/stream'
14
- require 'psych/json/tree_builder'
15
- require 'psych/json/stream'
16
- require 'psych/handlers/document_stream'
17
- require 'psych/class_loader'
1
+ # frozen_string_literal: true
2
+ require 'date'
3
+
4
+ require_relative 'psych/versions'
5
+ case RUBY_ENGINE
6
+ when 'jruby'
7
+ require_relative 'psych_jars'
8
+ if JRuby::Util.respond_to?(:load_ext)
9
+ JRuby::Util.load_ext('org.jruby.ext.psych.PsychLibrary')
10
+ else
11
+ require 'java'; require 'jruby'
12
+ org.jruby.ext.psych.PsychLibrary.new.load(JRuby.runtime, false)
13
+ end
14
+ else
15
+ require 'psych.so'
16
+ end
17
+ require_relative 'psych/nodes'
18
+ require_relative 'psych/streaming'
19
+ require_relative 'psych/visitors'
20
+ require_relative 'psych/handler'
21
+ require_relative 'psych/tree_builder'
22
+ require_relative 'psych/parser'
23
+ require_relative 'psych/omap'
24
+ require_relative 'psych/set'
25
+ require_relative 'psych/coder'
26
+ require_relative 'psych/core_ext'
27
+ require_relative 'psych/stream'
28
+ require_relative 'psych/json/tree_builder'
29
+ require_relative 'psych/json/stream'
30
+ require_relative 'psych/handlers/document_stream'
31
+ require_relative 'psych/class_loader'
18
32
 
19
33
  ###
20
34
  # = Overview
21
35
  #
22
36
  # Psych is a YAML parser and emitter.
23
- # Psych leverages libyaml [Home page: http://pyyaml.org/wiki/LibYAML]
24
- # or [HG repo: https://bitbucket.org/xi/libyaml] for its YAML parsing
37
+ # Psych leverages libyaml [Home page: https://pyyaml.org/wiki/LibYAML]
38
+ # or [git repo: https://github.com/yaml/libyaml] for its YAML parsing
25
39
  # and emitting capabilities. In addition to wrapping libyaml, Psych also
26
40
  # knows how to serialize and de-serialize most Ruby objects to and from
27
41
  # the YAML format.
@@ -62,14 +76,17 @@ require 'psych/class_loader'
62
76
  #
63
77
  # ==== Reading from a string
64
78
  #
65
- # Psych.load("--- a") # => 'a'
66
- # Psych.load("---\n - a\n - b") # => ['a', 'b']
79
+ # Psych.safe_load("--- a") # => 'a'
80
+ # Psych.safe_load("---\n - a\n - b") # => ['a', 'b']
81
+ # # From a trusted string:
82
+ # Psych.load("--- !ruby/range\nbegin: 0\nend: 42\nexcl: false\n") # => 0..42
67
83
  #
68
84
  # ==== Reading from a file
69
85
  #
70
- # Psych.load_file("database.yml")
86
+ # Psych.safe_load_file("data.yml", permitted_classes: [Date])
87
+ # Psych.load_file("trusted_database.yml")
71
88
  #
72
- # ==== Exception handling
89
+ # ==== \Exception handling
73
90
  #
74
91
  # begin
75
92
  # # The second argument changes only the exception contents
@@ -133,7 +150,7 @@ require 'psych/class_loader'
133
150
  # # Returns Psych::Nodes::Document
134
151
  # Psych.parse_file('database.yml')
135
152
  #
136
- # ==== Exception handling
153
+ # ==== \Exception handling
137
154
  #
138
155
  # begin
139
156
  # # The second argument changes only the exception contents
@@ -187,12 +204,13 @@ require 'psych/class_loader'
187
204
  #
188
205
  # ==== Receiving an events stream
189
206
  #
190
- # parser = Psych::Parser.new(Psych::Handlers::Recorder.new)
207
+ # recorder = Psych::Handlers::Recorder.new
208
+ # parser = Psych::Parser.new(recorder)
191
209
  #
192
210
  # parser.parse("---\n - a\n - b")
193
- # parser.events # => [list of [event, args] lists]
194
- # # event is one of: Psych::Handler::EVENTS
195
- # # args are the arguments passed to the event
211
+ # recorder.events # => [list of [event, args] lists]
212
+ # # event is one of: Psych::Handler::EVENTS
213
+ # # args are the arguments passed to the event
196
214
  #
197
215
  # === Emitting
198
216
  #
@@ -216,34 +234,46 @@ require 'psych/class_loader'
216
234
  # # => "a"
217
235
 
218
236
  module Psych
219
- # The version is Psych you're using
220
- VERSION = '2.0.12'
221
-
222
237
  # The version of libyaml Psych is using
223
- LIBYAML_VERSION = Psych.libyaml_version.join '.'
238
+ LIBYAML_VERSION = Psych.libyaml_version.join('.').freeze
224
239
 
225
240
  ###
226
241
  # Load +yaml+ in to a Ruby data structure. If multiple documents are
227
242
  # provided, the object contained in the first document will be returned.
228
- # +filename+ will be used in the exception message if any exception is raised
229
- # while parsing.
243
+ # +filename+ will be used in the exception message if any exception
244
+ # is raised while parsing. If +yaml+ is empty, it returns
245
+ # the specified +fallback+ return value, which defaults to +false+.
230
246
  #
231
247
  # Raises a Psych::SyntaxError when a YAML syntax error is detected.
232
248
  #
233
249
  # Example:
234
250
  #
235
- # Psych.load("--- a") # => 'a'
236
- # Psych.load("---\n - a\n - b") # => ['a', 'b']
251
+ # Psych.unsafe_load("--- a") # => 'a'
252
+ # Psych.unsafe_load("---\n - a\n - b") # => ['a', 'b']
237
253
  #
238
254
  # begin
239
- # Psych.load("--- `", "file.txt")
255
+ # Psych.unsafe_load("--- `", filename: "file.txt")
240
256
  # rescue Psych::SyntaxError => ex
241
257
  # ex.file # => 'file.txt'
242
258
  # ex.message # => "(file.txt): found character that cannot start any token"
243
259
  # end
244
- def self.load yaml, filename = nil
245
- result = parse(yaml, filename)
246
- result ? result.to_ruby : result
260
+ #
261
+ # When the optional +symbolize_names+ keyword argument is set to a
262
+ # true value, returns symbols for keys in Hash objects (default: strings).
263
+ #
264
+ # Psych.unsafe_load("---\n foo: bar") # => {"foo"=>"bar"}
265
+ # Psych.unsafe_load("---\n foo: bar", symbolize_names: true) # => {:foo=>"bar"}
266
+ #
267
+ # Raises a TypeError when `yaml` parameter is NilClass
268
+ #
269
+ # NOTE: This method *should not* be used to parse untrusted documents, such as
270
+ # YAML documents that are supplied via user input. Instead, please use the
271
+ # load method or the safe_load method.
272
+ #
273
+ def self.unsafe_load yaml, filename: nil, fallback: false, symbolize_names: false, freeze: false, strict_integer: false
274
+ result = parse(yaml, filename: filename)
275
+ return fallback unless result
276
+ result.to_ruby(symbolize_names: symbolize_names, freeze: freeze, strict_integer: strict_integer)
247
277
  end
248
278
 
249
279
  ###
@@ -253,46 +283,99 @@ module Psych
253
283
  # * TrueClass
254
284
  # * FalseClass
255
285
  # * NilClass
256
- # * Numeric
286
+ # * Integer
287
+ # * Float
257
288
  # * String
258
289
  # * Array
259
290
  # * Hash
260
291
  #
261
292
  # Recursive data structures are not allowed by default. Arbitrary classes
262
- # can be allowed by adding those classes to the +whitelist+. They are
293
+ # can be allowed by adding those classes to the +permitted_classes+ keyword argument. They are
263
294
  # additive. For example, to allow Date deserialization:
264
295
  #
265
- # Psych.safe_load(yaml, [Date])
296
+ # Psych.safe_load(yaml, permitted_classes: [Date])
266
297
  #
267
298
  # Now the Date class can be loaded in addition to the classes listed above.
268
299
  #
269
- # Aliases can be explicitly allowed by changing the +aliases+ parameter.
300
+ # Aliases can be explicitly allowed by changing the +aliases+ keyword argument.
270
301
  # For example:
271
302
  #
272
303
  # x = []
273
304
  # x << x
274
305
  # yaml = Psych.dump x
275
306
  # Psych.safe_load yaml # => raises an exception
276
- # Psych.safe_load yaml, [], [], true # => loads the aliases
307
+ # Psych.safe_load yaml, aliases: true # => loads the aliases
277
308
  #
278
309
  # A Psych::DisallowedClass exception will be raised if the yaml contains a
279
- # class that isn't in the whitelist.
280
- #
281
- # A Psych::BadAlias exception will be raised if the yaml contains aliases
282
- # but the +aliases+ parameter is set to false.
283
- def self.safe_load yaml, whitelist_classes = [], whitelist_symbols = [], aliases = false, filename = nil
284
- result = parse(yaml, filename)
285
- return unless result
286
-
287
- class_loader = ClassLoader::Restricted.new(whitelist_classes.map(&:to_s),
288
- whitelist_symbols.map(&:to_s))
289
- scanner = ScalarScanner.new class_loader
290
- if aliases
291
- visitor = Visitors::ToRuby.new scanner, class_loader
292
- else
293
- visitor = Visitors::NoAliasRuby.new scanner, class_loader
294
- end
295
- visitor.accept result
310
+ # class that isn't in the +permitted_classes+ list.
311
+ #
312
+ # A Psych::AliasesNotEnabled exception will be raised if the yaml contains aliases
313
+ # but the +aliases+ keyword argument is set to false.
314
+ #
315
+ # +filename+ will be used in the exception message if any exception is raised
316
+ # while parsing.
317
+ #
318
+ # When the optional +symbolize_names+ keyword argument is set to a
319
+ # true value, returns symbols for keys in Hash objects (default: strings).
320
+ #
321
+ # Psych.safe_load("---\n foo: bar") # => {"foo"=>"bar"}
322
+ # Psych.safe_load("---\n foo: bar", symbolize_names: true) # => {:foo=>"bar"}
323
+ #
324
+ def self.safe_load yaml, permitted_classes: [], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false, strict_integer: false
325
+ result = parse(yaml, filename: filename)
326
+ return fallback unless result
327
+
328
+ class_loader = ClassLoader::Restricted.new(permitted_classes.map(&:to_s),
329
+ permitted_symbols.map(&:to_s))
330
+ scanner = ScalarScanner.new class_loader, strict_integer: strict_integer
331
+ visitor = if aliases
332
+ Visitors::ToRuby.new scanner, class_loader, symbolize_names: symbolize_names, freeze: freeze
333
+ else
334
+ Visitors::NoAliasRuby.new scanner, class_loader, symbolize_names: symbolize_names, freeze: freeze
335
+ end
336
+ result = visitor.accept result
337
+ result
338
+ end
339
+
340
+ ###
341
+ # Load +yaml+ in to a Ruby data structure. If multiple documents are
342
+ # provided, the object contained in the first document will be returned.
343
+ # +filename+ will be used in the exception message if any exception
344
+ # is raised while parsing. If +yaml+ is empty, it returns
345
+ # the specified +fallback+ return value, which defaults to +nil+.
346
+ #
347
+ # Raises a Psych::SyntaxError when a YAML syntax error is detected.
348
+ #
349
+ # Example:
350
+ #
351
+ # Psych.load("--- a") # => 'a'
352
+ # Psych.load("---\n - a\n - b") # => ['a', 'b']
353
+ #
354
+ # begin
355
+ # Psych.load("--- `", filename: "file.txt")
356
+ # rescue Psych::SyntaxError => ex
357
+ # ex.file # => 'file.txt'
358
+ # ex.message # => "(file.txt): found character that cannot start any token"
359
+ # end
360
+ #
361
+ # When the optional +symbolize_names+ keyword argument is set to a
362
+ # true value, returns symbols for keys in Hash objects (default: strings).
363
+ #
364
+ # Psych.load("---\n foo: bar") # => {"foo"=>"bar"}
365
+ # Psych.load("---\n foo: bar", symbolize_names: true) # => {:foo=>"bar"}
366
+ #
367
+ # Raises a TypeError when `yaml` parameter is NilClass. This method is
368
+ # similar to `safe_load` except that `Symbol` objects are allowed by default.
369
+ #
370
+ def self.load yaml, permitted_classes: [Symbol], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false, strict_integer: false
371
+ safe_load yaml, permitted_classes: permitted_classes,
372
+ permitted_symbols: permitted_symbols,
373
+ aliases: aliases,
374
+ filename: filename,
375
+ fallback: fallback,
376
+ symbolize_names: symbolize_names,
377
+ freeze: freeze,
378
+ strict_integer: strict_integer
296
379
  end
297
380
 
298
381
  ###
@@ -307,17 +390,18 @@ module Psych
307
390
  # Psych.parse("---\n - a\n - b") # => #<Psych::Nodes::Document:0x00>
308
391
  #
309
392
  # begin
310
- # Psych.parse("--- `", "file.txt")
393
+ # Psych.parse("--- `", filename: "file.txt")
311
394
  # rescue Psych::SyntaxError => ex
312
395
  # ex.file # => 'file.txt'
313
396
  # ex.message # => "(file.txt): found character that cannot start any token"
314
397
  # end
315
398
  #
316
399
  # See Psych::Nodes for more information about YAML AST.
317
- def self.parse yaml, filename = nil
318
- parse_stream(yaml, filename) do |node|
400
+ def self.parse yaml, filename: nil
401
+ parse_stream(yaml, filename: filename) do |node|
319
402
  return node
320
403
  end
404
+
321
405
  false
322
406
  end
323
407
 
@@ -325,10 +409,11 @@ module Psych
325
409
  # Parse a file at +filename+. Returns the Psych::Nodes::Document.
326
410
  #
327
411
  # Raises a Psych::SyntaxError when a YAML syntax error is detected.
328
- def self.parse_file filename
329
- File.open filename, 'r:bom|utf-8' do |f|
330
- parse f, filename
412
+ def self.parse_file filename, fallback: false
413
+ result = File.open filename, 'r:bom|utf-8' do |f|
414
+ parse f, filename: filename
331
415
  end
416
+ result || fallback
332
417
  end
333
418
 
334
419
  ###
@@ -357,14 +442,16 @@ module Psych
357
442
  # end
358
443
  #
359
444
  # begin
360
- # Psych.parse_stream("--- `", "file.txt")
445
+ # Psych.parse_stream("--- `", filename: "file.txt")
361
446
  # rescue Psych::SyntaxError => ex
362
447
  # ex.file # => 'file.txt'
363
448
  # ex.message # => "(file.txt): found character that cannot start any token"
364
449
  # end
365
450
  #
451
+ # Raises a TypeError when NilClass is passed.
452
+ #
366
453
  # See Psych::Nodes for more information about YAML AST.
367
- def self.parse_stream yaml, filename = nil, &block
454
+ def self.parse_stream yaml, filename: nil, &block
368
455
  if block_given?
369
456
  parser = Psych::Parser.new(Handlers::DocumentStream.new(&block))
370
457
  parser.parse yaml, filename
@@ -386,6 +473,29 @@ module Psych
386
473
  # to control the output format. If an IO object is passed in, the YAML will
387
474
  # be dumped to that IO object.
388
475
  #
476
+ # Currently supported options are:
477
+ #
478
+ # [<tt>:indentation</tt>] Number of space characters used to indent.
479
+ # Acceptable value should be in <tt>0..9</tt> range,
480
+ # otherwise option is ignored.
481
+ #
482
+ # Default: <tt>2</tt>.
483
+ # [<tt>:line_width</tt>] Max character to wrap line at.
484
+ # For unlimited line width use <tt>-1</tt>.
485
+ #
486
+ # Default: <tt>0</tt> (meaning "wrap at 81").
487
+ # [<tt>:canonical</tt>] Write "canonical" YAML form (very verbose, yet
488
+ # strictly formal).
489
+ #
490
+ # Default: <tt>false</tt>.
491
+ # [<tt>:header</tt>] Write <tt>%YAML [version]</tt> at the beginning of document.
492
+ #
493
+ # Default: <tt>false</tt>.
494
+ #
495
+ # [<tt>:stringify_names</tt>] Dump symbol keys in Hash objects as string.
496
+ #
497
+ # Default: <tt>false</tt>.
498
+ #
389
499
  # Example:
390
500
  #
391
501
  # # Dump an array, get back a YAML string
@@ -395,10 +505,13 @@ module Psych
395
505
  # Psych.dump(['a', 'b'], StringIO.new) # => #<StringIO:0x000001009d0890>
396
506
  #
397
507
  # # Dump an array with indentation set
398
- # Psych.dump(['a', ['b']], :indentation => 3) # => "---\n- a\n- - b\n"
508
+ # Psych.dump(['a', ['b']], indentation: 3) # => "---\n- a\n- - b\n"
399
509
  #
400
510
  # # Dump an array to an IO with indentation set
401
- # Psych.dump(['a', ['b']], StringIO.new, :indentation => 3)
511
+ # Psych.dump(['a', ['b']], StringIO.new, indentation: 3)
512
+ #
513
+ # # Dump hash with symbol keys as string
514
+ # Psych.dump({a: "b"}, stringify_names: true) # => "---\na: b\n"
402
515
  def self.dump o, io = nil, options = {}
403
516
  if Hash === io
404
517
  options = io
@@ -410,6 +523,87 @@ module Psych
410
523
  visitor.tree.yaml io, options
411
524
  end
412
525
 
526
+ ###
527
+ # call-seq:
528
+ # Psych.safe_dump(o) -> string of yaml
529
+ # Psych.safe_dump(o, options) -> string of yaml
530
+ # Psych.safe_dump(o, io) -> io object passed in
531
+ # Psych.safe_dump(o, io, options) -> io object passed in
532
+ #
533
+ # Safely dump Ruby object +o+ to a YAML string. Optional +options+ may be passed in
534
+ # to control the output format. If an IO object is passed in, the YAML will
535
+ # be dumped to that IO object. By default, only the following
536
+ # classes are allowed to be serialized:
537
+ #
538
+ # * TrueClass
539
+ # * FalseClass
540
+ # * NilClass
541
+ # * Integer
542
+ # * Float
543
+ # * String
544
+ # * Array
545
+ # * Hash
546
+ #
547
+ # Arbitrary classes can be allowed by adding those classes to the +permitted_classes+
548
+ # keyword argument. They are additive. For example, to allow Date serialization:
549
+ #
550
+ # Psych.safe_dump(yaml, permitted_classes: [Date])
551
+ #
552
+ # Now the Date class can be dumped in addition to the classes listed above.
553
+ #
554
+ # A Psych::DisallowedClass exception will be raised if the object contains a
555
+ # class that isn't in the +permitted_classes+ list.
556
+ #
557
+ # Currently supported options are:
558
+ #
559
+ # [<tt>:indentation</tt>] Number of space characters used to indent.
560
+ # Acceptable value should be in <tt>0..9</tt> range,
561
+ # otherwise option is ignored.
562
+ #
563
+ # Default: <tt>2</tt>.
564
+ # [<tt>:line_width</tt>] Max character to wrap line at.
565
+ # For unlimited line width use <tt>-1</tt>.
566
+ #
567
+ # Default: <tt>0</tt> (meaning "wrap at 81").
568
+ # [<tt>:canonical</tt>] Write "canonical" YAML form (very verbose, yet
569
+ # strictly formal).
570
+ #
571
+ # Default: <tt>false</tt>.
572
+ # [<tt>:header</tt>] Write <tt>%YAML [version]</tt> at the beginning of document.
573
+ #
574
+ # Default: <tt>false</tt>.
575
+ #
576
+ # [<tt>:stringify_names</tt>] Dump symbol keys in Hash objects as string.
577
+ #
578
+ # Default: <tt>false</tt>.
579
+ #
580
+ # Example:
581
+ #
582
+ # # Dump an array, get back a YAML string
583
+ # Psych.safe_dump(['a', 'b']) # => "---\n- a\n- b\n"
584
+ #
585
+ # # Dump an array to an IO object
586
+ # Psych.safe_dump(['a', 'b'], StringIO.new) # => #<StringIO:0x000001009d0890>
587
+ #
588
+ # # Dump an array with indentation set
589
+ # Psych.safe_dump(['a', ['b']], indentation: 3) # => "---\n- a\n- - b\n"
590
+ #
591
+ # # Dump an array to an IO with indentation set
592
+ # Psych.safe_dump(['a', ['b']], StringIO.new, indentation: 3)
593
+ #
594
+ # # Dump hash with symbol keys as string
595
+ # Psych.dump({a: "b"}, stringify_names: true) # => "---\na: b\n"
596
+ def self.safe_dump o, io = nil, options = {}
597
+ if Hash === io
598
+ options = io
599
+ io = nil
600
+ end
601
+
602
+ visitor = Psych::Visitors::RestrictedYAMLTree.create options
603
+ visitor << o
604
+ visitor.tree.yaml io, options
605
+ end
606
+
413
607
  ###
414
608
  # Dump a list of objects as separate documents to a document stream.
415
609
  #
@@ -447,52 +641,123 @@ module Psych
447
641
  # end
448
642
  # list # => ['foo', 'bar']
449
643
  #
450
- def self.load_stream yaml, filename = nil
451
- if block_given?
452
- parse_stream(yaml, filename) do |node|
453
- yield node.to_ruby
454
- end
455
- else
456
- parse_stream(yaml, filename).children.map { |child| child.to_ruby }
457
- end
644
+ def self.load_stream yaml, filename: nil, fallback: [], **kwargs
645
+ result = if block_given?
646
+ parse_stream(yaml, filename: filename) do |node|
647
+ yield node.to_ruby(**kwargs)
648
+ end
649
+ else
650
+ parse_stream(yaml, filename: filename).children.map { |node| node.to_ruby(**kwargs) }
651
+ end
652
+
653
+ return fallback if result.is_a?(Array) && result.empty?
654
+ result
458
655
  end
459
656
 
460
657
  ###
461
658
  # Load the document contained in +filename+. Returns the yaml contained in
462
- # +filename+ as a Ruby object
463
- def self.load_file filename
464
- File.open(filename, 'r:bom|utf-8') { |f| self.load f, filename }
659
+ # +filename+ as a Ruby object, or if the file is empty, it returns
660
+ # the specified +fallback+ return value, which defaults to +false+.
661
+ #
662
+ # NOTE: This method *should not* be used to parse untrusted documents, such as
663
+ # YAML documents that are supplied via user input. Instead, please use the
664
+ # safe_load_file method.
665
+ def self.unsafe_load_file filename, **kwargs
666
+ File.open(filename, 'r:bom|utf-8') { |f|
667
+ self.unsafe_load f, filename: filename, **kwargs
668
+ }
669
+ end
670
+
671
+ ###
672
+ # Safely loads the document contained in +filename+. Returns the yaml contained in
673
+ # +filename+ as a Ruby object, or if the file is empty, it returns
674
+ # the specified +fallback+ return value, which defaults to +nil+.
675
+ # See safe_load for options.
676
+ def self.safe_load_file filename, **kwargs
677
+ File.open(filename, 'r:bom|utf-8') { |f|
678
+ self.safe_load f, filename: filename, **kwargs
679
+ }
680
+ end
681
+
682
+ ###
683
+ # Loads the document contained in +filename+. Returns the yaml contained in
684
+ # +filename+ as a Ruby object, or if the file is empty, it returns
685
+ # the specified +fallback+ return value, which defaults to +nil+.
686
+ # See load for options.
687
+ def self.load_file filename, **kwargs
688
+ File.open(filename, 'r:bom|utf-8') { |f|
689
+ self.load f, filename: filename, **kwargs
690
+ }
465
691
  end
466
692
 
467
693
  # :stopdoc:
468
- @domain_types = {}
469
694
  def self.add_domain_type domain, type_tag, &block
470
695
  key = ['tag', domain, type_tag].join ':'
471
- @domain_types[key] = [key, block]
472
- @domain_types["tag:#{type_tag}"] = [key, block]
696
+ domain_types[key] = [key, block]
697
+ domain_types["tag:#{type_tag}"] = [key, block]
473
698
  end
474
699
 
475
700
  def self.add_builtin_type type_tag, &block
476
701
  domain = 'yaml.org,2002'
477
702
  key = ['tag', domain, type_tag].join ':'
478
- @domain_types[key] = [key, block]
703
+ domain_types[key] = [key, block]
479
704
  end
480
705
 
481
706
  def self.remove_type type_tag
482
- @domain_types.delete type_tag
707
+ domain_types.delete type_tag
483
708
  end
484
709
 
485
- @load_tags = {}
486
- @dump_tags = {}
487
710
  def self.add_tag tag, klass
488
- @load_tags[tag] = klass.name
489
- @dump_tags[klass] = tag
711
+ load_tags[tag] = klass.name
712
+ dump_tags[klass] = tag
490
713
  end
491
714
 
492
715
  class << self
493
- attr_accessor :load_tags
494
- attr_accessor :dump_tags
495
- attr_accessor :domain_types
716
+ if defined?(Ractor)
717
+ class Config
718
+ attr_accessor :load_tags, :dump_tags, :domain_types
719
+ def initialize
720
+ @load_tags = {}
721
+ @dump_tags = {}
722
+ @domain_types = {}
723
+ end
724
+ end
725
+
726
+ def config
727
+ Ractor.current[:PsychConfig] ||= Config.new
728
+ end
729
+
730
+ def load_tags
731
+ config.load_tags
732
+ end
733
+
734
+ def dump_tags
735
+ config.dump_tags
736
+ end
737
+
738
+ def domain_types
739
+ config.domain_types
740
+ end
741
+
742
+ def load_tags=(value)
743
+ config.load_tags = value
744
+ end
745
+
746
+ def dump_tags=(value)
747
+ config.dump_tags = value
748
+ end
749
+
750
+ def domain_types=(value)
751
+ config.domain_types = value
752
+ end
753
+ else
754
+ attr_accessor :load_tags
755
+ attr_accessor :dump_tags
756
+ attr_accessor :domain_types
757
+ end
496
758
  end
759
+ self.load_tags = {}
760
+ self.dump_tags = {}
761
+ self.domain_types = {}
497
762
  # :startdoc:
498
763
  end