psych 3.1.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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