psych 3.1.0 → 4.0.0

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.
@@ -1,10 +1,10 @@
1
-
2
1
  # frozen_string_literal: true
2
+
3
3
  module Psych
4
4
  # The version of Psych you are using
5
- VERSION = '3.1.0' unless defined?(::Psych::VERSION)
5
+ VERSION = '4.0.0'
6
6
 
7
7
  if RUBY_ENGINE == 'jruby'
8
- DEFAULT_SNAKEYAML_VERSION = '1.23'.freeze
8
+ DEFAULT_SNAKEYAML_VERSION = '1.28'.freeze
9
9
  end
10
10
  end
@@ -12,39 +12,44 @@ module Psych
12
12
  ###
13
13
  # This class walks a YAML AST, converting each node to Ruby
14
14
  class ToRuby < Psych::Visitors::Visitor
15
- def self.create
15
+ def self.create(symbolize_names: false, freeze: false)
16
16
  class_loader = ClassLoader.new
17
17
  scanner = ScalarScanner.new class_loader
18
- new(scanner, class_loader)
18
+ new(scanner, class_loader, symbolize_names: symbolize_names, freeze: freeze)
19
19
  end
20
20
 
21
21
  attr_reader :class_loader
22
22
 
23
- def initialize ss, class_loader
23
+ def initialize ss, class_loader, symbolize_names: false, freeze: false
24
24
  super()
25
25
  @st = {}
26
26
  @ss = ss
27
+ @load_tags = Psych.load_tags
27
28
  @domain_types = Psych.domain_types
28
29
  @class_loader = class_loader
30
+ @symbolize_names = symbolize_names
31
+ @freeze = freeze
29
32
  end
30
33
 
31
34
  def accept target
32
35
  result = super
33
- return result if @domain_types.empty? || !target.tag
34
36
 
35
- key = target.tag.sub(/^[!\/]*/, '').sub(/(,\d+)\//, '\1:')
36
- key = "tag:#{key}" unless key =~ /^(?:tag:|x-private)/
37
+ unless @domain_types.empty? || !target.tag
38
+ key = target.tag.sub(/^[!\/]*/, '').sub(/(,\d+)\//, '\1:')
39
+ key = "tag:#{key}" unless key =~ /^(?:tag:|x-private)/
37
40
 
38
- if @domain_types.key? key
39
- value, block = @domain_types[key]
40
- return block.call value, result
41
+ if @domain_types.key? key
42
+ value, block = @domain_types[key]
43
+ result = block.call value, result
44
+ end
41
45
  end
42
46
 
47
+ result = deduplicate(result).freeze if @freeze
43
48
  result
44
49
  end
45
50
 
46
51
  def deserialize o
47
- if klass = resolve_class(Psych.load_tags[o.tag])
52
+ if klass = resolve_class(@load_tags[o.tag])
48
53
  instance = klass.allocate
49
54
 
50
55
  if instance.respond_to?(:init_with)
@@ -124,7 +129,7 @@ module Psych
124
129
  end
125
130
 
126
131
  def visit_Psych_Nodes_Sequence o
127
- if klass = resolve_class(Psych.load_tags[o.tag])
132
+ if klass = resolve_class(@load_tags[o.tag])
128
133
  instance = klass.allocate
129
134
 
130
135
  if instance.respond_to?(:init_with)
@@ -156,8 +161,8 @@ module Psych
156
161
  end
157
162
 
158
163
  def visit_Psych_Nodes_Mapping o
159
- if Psych.load_tags[o.tag]
160
- return revive(resolve_class(Psych.load_tags[o.tag]), o)
164
+ if @load_tags[o.tag]
165
+ return revive(resolve_class(@load_tags[o.tag]), o)
161
166
  end
162
167
  return revive_hash(register(o, {}), o) unless o.tag
163
168
 
@@ -252,6 +257,8 @@ module Psych
252
257
 
253
258
  e = build_exception((resolve_class($1) || class_loader.exception),
254
259
  h.delete('message'))
260
+
261
+ e.set_backtrace h.delete('backtrace') if h.key? 'backtrace'
255
262
  init_with(e, h, o)
256
263
 
257
264
  when '!set', 'tag:yaml.org,2002:set'
@@ -320,6 +327,7 @@ module Psych
320
327
  end
321
328
 
322
329
  private
330
+
323
331
  def register node, object
324
332
  @st[node.anchor] = object if node.anchor
325
333
  object
@@ -331,13 +339,12 @@ module Psych
331
339
  list
332
340
  end
333
341
 
334
- SHOVEL = '<<'
335
- def revive_hash hash, o
342
+ def revive_hash hash, o, tagged= false
336
343
  o.children.each_slice(2) { |k,v|
337
344
  key = accept(k)
338
345
  val = accept(v)
339
346
 
340
- if key == SHOVEL && k.tag != "tag:yaml.org,2002:str"
347
+ if key == '<<' && k.tag != "tag:yaml.org,2002:str"
341
348
  case v
342
349
  when Nodes::Alias, Nodes::Mapping
343
350
  begin
@@ -359,6 +366,12 @@ module Psych
359
366
  hash[key] = val
360
367
  end
361
368
  else
369
+ if !tagged && @symbolize_names && key.is_a?(String)
370
+ key = key.to_sym
371
+ elsif !@freeze
372
+ key = deduplicate(key)
373
+ end
374
+
362
375
  hash[key] = val
363
376
  end
364
377
 
@@ -366,12 +379,32 @@ module Psych
366
379
  hash
367
380
  end
368
381
 
382
+ if RUBY_VERSION < '2.7'
383
+ def deduplicate key
384
+ if key.is_a?(String)
385
+ # It is important to untaint the string, otherwise it won't
386
+ # be deduplicated into an fstring, but simply frozen.
387
+ -(key.untaint)
388
+ else
389
+ key
390
+ end
391
+ end
392
+ else
393
+ def deduplicate key
394
+ if key.is_a?(String)
395
+ -key
396
+ else
397
+ key
398
+ end
399
+ end
400
+ end
401
+
369
402
  def merge_key hash, key, val
370
403
  end
371
404
 
372
405
  def revive klass, node
373
406
  s = register(node, klass.allocate)
374
- init_with(s, revive_hash({}, node), node)
407
+ init_with(s, revive_hash({}, node, true), node)
375
408
  end
376
409
 
377
410
  def init_with o, h, node
@@ -8,12 +8,26 @@ module Psych
8
8
 
9
9
  private
10
10
 
11
- DISPATCH = Hash.new do |hash, klass|
12
- hash[klass] = "visit_#{klass.name.gsub('::', '_')}"
11
+ # @api private
12
+ def self.dispatch_cache
13
+ Hash.new do |hash, klass|
14
+ hash[klass] = :"visit_#{klass.name.gsub('::', '_')}"
15
+ end.compare_by_identity
16
+ end
17
+
18
+ if defined?(Ractor)
19
+ def dispatch
20
+ @dispatch_cache ||= (Ractor.current[:Psych_Visitors_Visitor] ||= Visitor.dispatch_cache)
21
+ end
22
+ else
23
+ DISPATCH = dispatch_cache
24
+ def dispatch
25
+ DISPATCH
26
+ end
13
27
  end
14
28
 
15
29
  def visit target
16
- send DISPATCH[target.class], target
30
+ send dispatch[target.class], target
17
31
  end
18
32
  end
19
33
  end
@@ -80,7 +80,7 @@ module Psych
80
80
  raise(TypeError, "Can't dump #{target.class}") unless method
81
81
 
82
82
  h[klass] = method
83
- end
83
+ end.compare_by_identity
84
84
  end
85
85
 
86
86
  def start encoding = Nodes::Stream::UTF8
@@ -181,41 +181,11 @@ module Psych
181
181
  end
182
182
 
183
183
  def visit_Exception o
184
- tag = ['!ruby/exception', o.class.name].join ':'
185
-
186
- @emitter.start_mapping nil, tag, false, Nodes::Mapping::BLOCK
187
-
188
- {
189
- 'message' => private_iv_get(o, 'mesg'),
190
- 'backtrace' => private_iv_get(o, 'backtrace'),
191
- }.each do |k,v|
192
- next unless v
193
- @emitter.scalar k, nil, nil, true, false, Nodes::Scalar::ANY
194
- accept v
195
- end
196
-
197
- dump_ivars o
198
-
199
- @emitter.end_mapping
184
+ dump_exception o, o.message.to_s
200
185
  end
201
186
 
202
187
  def visit_NameError o
203
- tag = ['!ruby/exception', o.class.name].join ':'
204
-
205
- @emitter.start_mapping nil, tag, false, Nodes::Mapping::BLOCK
206
-
207
- {
208
- 'message' => o.message.to_s,
209
- 'backtrace' => private_iv_get(o, 'backtrace'),
210
- }.each do |k,v|
211
- next unless v
212
- @emitter.scalar k, nil, nil, true, false, Nodes::Scalar::ANY
213
- accept v
214
- end
215
-
216
- dump_ivars o
217
-
218
- @emitter.end_mapping
188
+ dump_exception o, o.message.to_s
219
189
  end
220
190
 
221
191
  def visit_Regexp o
@@ -458,15 +428,6 @@ module Psych
458
428
  node = @emitter.start_mapping(nil, tag, false, Psych::Nodes::Mapping::BLOCK)
459
429
  register(o, node)
460
430
 
461
- # Dump the elements
462
- accept 'elements'
463
- @emitter.start_mapping nil, nil, true, Nodes::Mapping::BLOCK
464
- o.each do |k,v|
465
- accept k
466
- accept v
467
- end
468
- @emitter.end_mapping
469
-
470
431
  # Dump the ivars
471
432
  accept 'ivars'
472
433
  @emitter.start_mapping nil, nil, true, Nodes::Mapping::BLOCK
@@ -476,6 +437,15 @@ module Psych
476
437
  end
477
438
  @emitter.end_mapping
478
439
 
440
+ # Dump the elements
441
+ accept 'elements'
442
+ @emitter.start_mapping nil, nil, true, Nodes::Mapping::BLOCK
443
+ o.each do |k,v|
444
+ accept k
445
+ accept v
446
+ end
447
+ @emitter.end_mapping
448
+
479
449
  @emitter.end_mapping
480
450
  else
481
451
  tag = "!ruby/hash:#{o.class}"
@@ -492,6 +462,24 @@ module Psych
492
462
  def dump_list o
493
463
  end
494
464
 
465
+ def dump_exception o, msg
466
+ tag = ['!ruby/exception', o.class.name].join ':'
467
+
468
+ @emitter.start_mapping nil, tag, false, Nodes::Mapping::BLOCK
469
+
470
+ if msg
471
+ @emitter.scalar 'message', nil, nil, true, false, Nodes::Scalar::ANY
472
+ accept msg
473
+ end
474
+
475
+ @emitter.scalar 'backtrace', nil, nil, true, false, Nodes::Scalar::ANY
476
+ accept o.backtrace
477
+
478
+ dump_ivars o
479
+
480
+ @emitter.end_mapping
481
+ end
482
+
495
483
  def format_time time
496
484
  if time.utc?
497
485
  time.strftime("%Y-%m-%d %H:%M:%S.%9N Z")
@@ -521,9 +509,9 @@ module Psych
521
509
  def emit_coder c, o
522
510
  case c.type
523
511
  when :scalar
524
- @emitter.scalar c.scalar, nil, c.tag, c.tag.nil?, false, Nodes::Scalar::ANY
512
+ @emitter.scalar c.scalar, nil, c.tag, c.tag.nil?, false, c.style
525
513
  when :seq
526
- @emitter.start_sequence nil, c.tag, c.tag.nil?, Nodes::Sequence::BLOCK
514
+ @emitter.start_sequence nil, c.tag, c.tag.nil?, c.style
527
515
  c.seq.each do |thing|
528
516
  accept thing
529
517
  end
data/lib/psych.rb CHANGED
@@ -10,11 +10,7 @@ when 'jruby'
10
10
  org.jruby.ext.psych.PsychLibrary.new.load(JRuby.runtime, false)
11
11
  end
12
12
  else
13
- begin
14
- require "#{RUBY_VERSION[/\d+\.\d+/]}/psych.so"
15
- rescue LoadError
16
- require 'psych.so'
17
- end
13
+ require 'psych.so'
18
14
  end
19
15
  require 'psych/nodes'
20
16
  require 'psych/streaming'
@@ -78,12 +74,15 @@ require 'psych/class_loader'
78
74
  #
79
75
  # ==== Reading from a string
80
76
  #
81
- # Psych.load("--- a") # => 'a'
82
- # Psych.load("---\n - a\n - b") # => ['a', 'b']
77
+ # Psych.safe_load("--- a") # => 'a'
78
+ # Psych.safe_load("---\n - a\n - b") # => ['a', 'b']
79
+ # # From a trusted string:
80
+ # Psych.load("--- !ruby/range\nbegin: 0\nend: 42\nexcl: false\n") # => 0..42
83
81
  #
84
82
  # ==== Reading from a file
85
83
  #
86
- # Psych.load_file("database.yml")
84
+ # Psych.safe_load_file("data.yml", permitted_classes: [Date])
85
+ # Psych.load_file("trusted_database.yml")
87
86
  #
88
87
  # ==== Exception handling
89
88
  #
@@ -234,10 +233,7 @@ require 'psych/class_loader'
234
233
 
235
234
  module Psych
236
235
  # The version of libyaml Psych is using
237
- LIBYAML_VERSION = Psych.libyaml_version.join '.'
238
- # Deprecation guard
239
- NOT_GIVEN = Object.new
240
- private_constant :NOT_GIVEN
236
+ LIBYAML_VERSION = Psych.libyaml_version.join('.').freeze
241
237
 
242
238
  ###
243
239
  # Load +yaml+ in to a Ruby data structure. If multiple documents are
@@ -250,11 +246,11 @@ module Psych
250
246
  #
251
247
  # Example:
252
248
  #
253
- # Psych.load("--- a") # => 'a'
254
- # Psych.load("---\n - a\n - b") # => ['a', 'b']
249
+ # Psych.unsafe_load("--- a") # => 'a'
250
+ # Psych.unsafe_load("---\n - a\n - b") # => ['a', 'b']
255
251
  #
256
252
  # begin
257
- # Psych.load("--- `", filename: "file.txt")
253
+ # Psych.unsafe_load("--- `", filename: "file.txt")
258
254
  # rescue Psych::SyntaxError => ex
259
255
  # ex.file # => 'file.txt'
260
256
  # ex.message # => "(file.txt): found character that cannot start any token"
@@ -263,23 +259,21 @@ module Psych
263
259
  # When the optional +symbolize_names+ keyword argument is set to a
264
260
  # true value, returns symbols for keys in Hash objects (default: strings).
265
261
  #
266
- # Psych.load("---\n foo: bar") # => {"foo"=>"bar"}
267
- # Psych.load("---\n foo: bar", symbolize_names: true) # => {:foo=>"bar"}
262
+ # Psych.unsafe_load("---\n foo: bar") # => {"foo"=>"bar"}
263
+ # Psych.unsafe_load("---\n foo: bar", symbolize_names: true) # => {:foo=>"bar"}
268
264
  #
269
265
  # Raises a TypeError when `yaml` parameter is NilClass
270
266
  #
271
- def self.load yaml, legacy_filename = NOT_GIVEN, filename: nil, fallback: false, symbolize_names: false
272
- if legacy_filename != NOT_GIVEN
273
- warn_with_uplevel 'Passing filename with the 2nd argument of Psych.load is deprecated. Use keyword argument like Psych.load(yaml, filename: ...) instead.', uplevel: 1 if $VERBOSE
274
- filename = legacy_filename
275
- end
276
-
267
+ # NOTE: This method *should not* be used to parse untrusted documents, such as
268
+ # YAML documents that are supplied via user input. Instead, please use the
269
+ # load method or the safe_load method.
270
+ #
271
+ def self.unsafe_load yaml, filename: nil, fallback: false, symbolize_names: false, freeze: false
277
272
  result = parse(yaml, filename: filename)
278
273
  return fallback unless result
279
- result = result.to_ruby if result
280
- symbolize_names!(result) if symbolize_names
281
- result
274
+ result.to_ruby(symbolize_names: symbolize_names, freeze: freeze)
282
275
  end
276
+ class << self; alias :load :unsafe_load; end
283
277
 
284
278
  ###
285
279
  # Safely load the yaml string in +yaml+. By default, only the following
@@ -325,27 +319,7 @@ module Psych
325
319
  # Psych.safe_load("---\n foo: bar") # => {"foo"=>"bar"}
326
320
  # Psych.safe_load("---\n foo: bar", symbolize_names: true) # => {:foo=>"bar"}
327
321
  #
328
- def self.safe_load yaml, legacy_permitted_classes = NOT_GIVEN, legacy_permitted_symbols = NOT_GIVEN, legacy_aliases = NOT_GIVEN, legacy_filename = NOT_GIVEN, permitted_classes: [], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false
329
- if legacy_permitted_classes != NOT_GIVEN
330
- warn_with_uplevel 'Passing permitted_classes with the 2nd argument of Psych.safe_load is deprecated. Use keyword argument like Psych.safe_load(yaml, permitted_classes: ...) instead.', uplevel: 1 if $VERBOSE
331
- permitted_classes = legacy_permitted_classes
332
- end
333
-
334
- if legacy_permitted_symbols != NOT_GIVEN
335
- warn_with_uplevel 'Passing permitted_symbols with the 3rd argument of Psych.safe_load is deprecated. Use keyword argument like Psych.safe_load(yaml, permitted_symbols: ...) instead.', uplevel: 1 if $VERBOSE
336
- permitted_symbols = legacy_permitted_symbols
337
- end
338
-
339
- if legacy_aliases != NOT_GIVEN
340
- warn_with_uplevel 'Passing aliases with the 4th argument of Psych.safe_load is deprecated. Use keyword argument like Psych.safe_load(yaml, aliases: ...) instead.', uplevel: 1 if $VERBOSE
341
- aliases = legacy_aliases
342
- end
343
-
344
- if legacy_filename != NOT_GIVEN
345
- warn_with_uplevel 'Passing filename with the 5th argument of Psych.safe_load is deprecated. Use keyword argument like Psych.safe_load(yaml, filename: ...) instead.', uplevel: 1 if $VERBOSE
346
- filename = legacy_filename
347
- end
348
-
322
+ def self.safe_load yaml, permitted_classes: [], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false
349
323
  result = parse(yaml, filename: filename)
350
324
  return fallback unless result
351
325
 
@@ -353,15 +327,54 @@ module Psych
353
327
  permitted_symbols.map(&:to_s))
354
328
  scanner = ScalarScanner.new class_loader
355
329
  visitor = if aliases
356
- Visitors::ToRuby.new scanner, class_loader
330
+ Visitors::ToRuby.new scanner, class_loader, symbolize_names: symbolize_names, freeze: freeze
357
331
  else
358
- Visitors::NoAliasRuby.new scanner, class_loader
332
+ Visitors::NoAliasRuby.new scanner, class_loader, symbolize_names: symbolize_names, freeze: freeze
359
333
  end
360
334
  result = visitor.accept result
361
- symbolize_names!(result) if symbolize_names
362
335
  result
363
336
  end
364
337
 
338
+ ###
339
+ # Load +yaml+ in to a Ruby data structure. If multiple documents are
340
+ # provided, the object contained in the first document will be returned.
341
+ # +filename+ will be used in the exception message if any exception
342
+ # is raised while parsing. If +yaml+ is empty, it returns
343
+ # the specified +fallback+ return value, which defaults to +false+.
344
+ #
345
+ # Raises a Psych::SyntaxError when a YAML syntax error is detected.
346
+ #
347
+ # Example:
348
+ #
349
+ # Psych.load("--- a") # => 'a'
350
+ # Psych.load("---\n - a\n - b") # => ['a', 'b']
351
+ #
352
+ # begin
353
+ # Psych.load("--- `", filename: "file.txt")
354
+ # rescue Psych::SyntaxError => ex
355
+ # ex.file # => 'file.txt'
356
+ # ex.message # => "(file.txt): found character that cannot start any token"
357
+ # end
358
+ #
359
+ # When the optional +symbolize_names+ keyword argument is set to a
360
+ # true value, returns symbols for keys in Hash objects (default: strings).
361
+ #
362
+ # Psych.load("---\n foo: bar") # => {"foo"=>"bar"}
363
+ # Psych.load("---\n foo: bar", symbolize_names: true) # => {:foo=>"bar"}
364
+ #
365
+ # Raises a TypeError when `yaml` parameter is NilClass. This method is
366
+ # similar to `safe_load` except that `Symbol` objects are allowed by default.
367
+ #
368
+ def self.load yaml, permitted_classes: [Symbol], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false
369
+ safe_load yaml, permitted_classes: permitted_classes,
370
+ permitted_symbols: permitted_symbols,
371
+ aliases: aliases,
372
+ filename: filename,
373
+ fallback: fallback,
374
+ symbolize_names: symbolize_names,
375
+ freeze: freeze
376
+ end
377
+
365
378
  ###
366
379
  # Parse a YAML string in +yaml+. Returns the Psych::Nodes::Document.
367
380
  # +filename+ is used in the exception message if a Psych::SyntaxError is
@@ -381,22 +394,12 @@ module Psych
381
394
  # end
382
395
  #
383
396
  # See Psych::Nodes for more information about YAML AST.
384
- def self.parse yaml, legacy_filename = NOT_GIVEN, filename: nil, fallback: NOT_GIVEN
385
- if legacy_filename != NOT_GIVEN
386
- warn_with_uplevel 'Passing filename with the 2nd argument of Psych.parse is deprecated. Use keyword argument like Psych.parse(yaml, filename: ...) instead.', uplevel: 1 if $VERBOSE
387
- filename = legacy_filename
388
- end
389
-
397
+ def self.parse yaml, filename: nil
390
398
  parse_stream(yaml, filename: filename) do |node|
391
399
  return node
392
400
  end
393
401
 
394
- if fallback != NOT_GIVEN
395
- warn_with_uplevel 'Passing the `fallback` keyword argument of Psych.parse is deprecated.', uplevel: 1 if $VERBOSE
396
- fallback
397
- else
398
- false
399
- end
402
+ false
400
403
  end
401
404
 
402
405
  ###
@@ -445,12 +448,7 @@ module Psych
445
448
  # Raises a TypeError when NilClass is passed.
446
449
  #
447
450
  # See Psych::Nodes for more information about YAML AST.
448
- def self.parse_stream yaml, legacy_filename = NOT_GIVEN, filename: nil, &block
449
- if legacy_filename != NOT_GIVEN
450
- warn_with_uplevel 'Passing filename with the 2nd argument of Psych.parse_stream is deprecated. Use keyword argument like Psych.parse_stream(yaml, filename: ...) instead.', uplevel: 1 if $VERBOSE
451
- filename = legacy_filename
452
- end
453
-
451
+ def self.parse_stream yaml, filename: nil, &block
454
452
  if block_given?
455
453
  parser = Psych::Parser.new(Handlers::DocumentStream.new(&block))
456
454
  parser.parse yaml, filename
@@ -551,18 +549,13 @@ module Psych
551
549
  # end
552
550
  # list # => ['foo', 'bar']
553
551
  #
554
- def self.load_stream yaml, legacy_filename = NOT_GIVEN, filename: nil, fallback: []
555
- if legacy_filename != NOT_GIVEN
556
- warn_with_uplevel 'Passing filename with the 2nd argument of Psych.load_stream is deprecated. Use keyword argument like Psych.load_stream(yaml, filename: ...) instead.', uplevel: 1 if $VERBOSE
557
- filename = legacy_filename
558
- end
559
-
552
+ def self.load_stream yaml, filename: nil, fallback: [], **kwargs
560
553
  result = if block_given?
561
554
  parse_stream(yaml, filename: filename) do |node|
562
- yield node.to_ruby
555
+ yield node.to_ruby(**kwargs)
563
556
  end
564
557
  else
565
- parse_stream(yaml, filename: filename).children.map(&:to_ruby)
558
+ parse_stream(yaml, filename: filename).children.map { |node| node.to_ruby(**kwargs) }
566
559
  end
567
560
 
568
561
  return fallback if result.is_a?(Array) && result.empty?
@@ -573,49 +566,50 @@ module Psych
573
566
  # Load the document contained in +filename+. Returns the yaml contained in
574
567
  # +filename+ as a Ruby object, or if the file is empty, it returns
575
568
  # the specified +fallback+ return value, which defaults to +false+.
576
- def self.load_file filename, fallback: false
569
+ #
570
+ # NOTE: This method *should not* be used to parse untrusted documents, such as
571
+ # YAML documents that are supplied via user input. Instead, please use the
572
+ # safe_load_file method.
573
+ def self.unsafe_load_file filename, **kwargs
577
574
  File.open(filename, 'r:bom|utf-8') { |f|
578
- self.load f, filename: filename, fallback: fallback
575
+ self.unsafe_load f, filename: filename, **kwargs
579
576
  }
580
577
  end
578
+ class << self; alias :load_file :unsafe_load_file; end
579
+
580
+ ###
581
+ # Safely loads the document contained in +filename+. Returns the yaml contained in
582
+ # +filename+ as a Ruby object, or if the file is empty, it returns
583
+ # the specified +fallback+ return value, which defaults to +false+.
584
+ # See safe_load for options.
585
+ def self.safe_load_file filename, **kwargs
586
+ File.open(filename, 'r:bom|utf-8') { |f|
587
+ self.safe_load f, filename: filename, **kwargs
588
+ }
589
+ end
590
+ class << self; alias load_file safe_load_file end
581
591
 
582
592
  # :stopdoc:
583
- @domain_types = {}
584
593
  def self.add_domain_type domain, type_tag, &block
585
594
  key = ['tag', domain, type_tag].join ':'
586
- @domain_types[key] = [key, block]
587
- @domain_types["tag:#{type_tag}"] = [key, block]
595
+ domain_types[key] = [key, block]
596
+ domain_types["tag:#{type_tag}"] = [key, block]
588
597
  end
589
598
 
590
599
  def self.add_builtin_type type_tag, &block
591
600
  domain = 'yaml.org,2002'
592
601
  key = ['tag', domain, type_tag].join ':'
593
- @domain_types[key] = [key, block]
602
+ domain_types[key] = [key, block]
594
603
  end
595
604
 
596
605
  def self.remove_type type_tag
597
- @domain_types.delete type_tag
606
+ domain_types.delete type_tag
598
607
  end
599
608
 
600
- @load_tags = {}
601
- @dump_tags = {}
602
609
  def self.add_tag tag, klass
603
- @load_tags[tag] = klass.name
604
- @dump_tags[klass] = tag
605
- end
606
-
607
- def self.symbolize_names!(result)
608
- case result
609
- when Hash
610
- result.keys.each do |key|
611
- result[key.to_sym] = symbolize_names!(result.delete(key))
612
- end
613
- when Array
614
- result.map! { |r| symbolize_names!(r) }
615
- end
616
- result
610
+ load_tags[tag] = klass.name
611
+ dump_tags[klass] = tag
617
612
  end
618
- private_class_method :symbolize_names!
619
613
 
620
614
  # Workaround for emulating `warn '...', uplevel: 1` in Ruby 2.4 or lower.
621
615
  def self.warn_with_uplevel(message, uplevel: 1)
@@ -633,9 +627,32 @@ module Psych
633
627
  private_class_method :warn_with_uplevel, :parse_caller
634
628
 
635
629
  class << self
636
- attr_accessor :load_tags
637
- attr_accessor :dump_tags
638
- attr_accessor :domain_types
630
+ if defined?(Ractor)
631
+ require 'forwardable'
632
+ extend Forwardable
633
+
634
+ class Config
635
+ attr_accessor :load_tags, :dump_tags, :domain_types
636
+ def initialize
637
+ @load_tags = {}
638
+ @dump_tags = {}
639
+ @domain_types = {}
640
+ end
641
+ end
642
+
643
+ def config
644
+ Ractor.current[:PsychConfig] ||= Config.new
645
+ end
646
+
647
+ def_delegators :config, :load_tags, :dump_tags, :domain_types, :load_tags=, :dump_tags=, :domain_types=
648
+ else
649
+ attr_accessor :load_tags
650
+ attr_accessor :dump_tags
651
+ attr_accessor :domain_types
652
+ end
639
653
  end
654
+ self.load_tags = {}
655
+ self.dump_tags = {}
656
+ self.domain_types = {}
640
657
  # :startdoc:
641
658
  end