psych 3.1.0 → 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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/CONTRIBUTING.md +24 -0
  3. data/{ext/psych/yaml/LICENSE → LICENSE} +9 -7
  4. data/README.md +22 -17
  5. data/ext/psych/depend +14 -0
  6. data/ext/psych/extconf.rb +42 -28
  7. data/ext/psych/psych.c +6 -4
  8. data/ext/psych/psych_emitter.c +155 -121
  9. data/ext/psych/psych_parser.c +274 -302
  10. data/ext/psych/psych_to_ruby.c +0 -1
  11. data/ext/psych/psych_yaml_tree.c +0 -13
  12. data/lib/psych/class_loader.rb +10 -8
  13. data/lib/psych/core_ext.rb +1 -1
  14. data/lib/psych/exception.rb +16 -2
  15. data/lib/psych/handler.rb +1 -1
  16. data/lib/psych/handlers/document_stream.rb +1 -1
  17. data/lib/psych/handlers/recorder.rb +1 -1
  18. data/lib/psych/json/stream.rb +2 -2
  19. data/lib/psych/json/tree_builder.rb +1 -1
  20. data/lib/psych/nodes/node.rb +5 -5
  21. data/lib/psych/nodes/scalar.rb +1 -1
  22. data/lib/psych/nodes.rb +7 -7
  23. data/lib/psych/parser.rb +13 -0
  24. data/lib/psych/scalar_scanner.rb +39 -47
  25. data/lib/psych/syntax_error.rb +1 -1
  26. data/lib/psych/tree_builder.rb +3 -3
  27. data/lib/psych/versions.rb +3 -3
  28. data/lib/psych/visitors/json_tree.rb +1 -1
  29. data/lib/psych/visitors/to_ruby.rb +60 -26
  30. data/lib/psych/visitors/visitor.rb +17 -3
  31. data/lib/psych/visitors/yaml_tree.rb +103 -71
  32. data/lib/psych/visitors.rb +6 -6
  33. data/lib/psych.rb +260 -138
  34. metadata +20 -53
  35. data/.gitignore +0 -16
  36. data/.travis.yml +0 -22
  37. data/CHANGELOG.rdoc +0 -583
  38. data/Gemfile +0 -3
  39. data/Mavenfile +0 -7
  40. data/Rakefile +0 -48
  41. data/bin/console +0 -7
  42. data/bin/setup +0 -6
  43. data/ext/psych/yaml/api.c +0 -1393
  44. data/ext/psych/yaml/config.h +0 -10
  45. data/ext/psych/yaml/dumper.c +0 -394
  46. data/ext/psych/yaml/emitter.c +0 -2324
  47. data/ext/psych/yaml/loader.c +0 -444
  48. data/ext/psych/yaml/parser.c +0 -1370
  49. data/ext/psych/yaml/reader.c +0 -469
  50. data/ext/psych/yaml/scanner.c +0 -3578
  51. data/ext/psych/yaml/writer.c +0 -141
  52. data/ext/psych/yaml/yaml.h +0 -1971
  53. data/ext/psych/yaml/yaml_private.h +0 -688
  54. data/psych.gemspec +0 -75
@@ -36,4 +36,3 @@ void Init_psych_to_ruby(void)
36
36
  rb_define_private_method(cPsychVisitorsToRuby, "build_exception", build_exception, 2);
37
37
  rb_define_private_method(class_loader, "path2class", path2class, 1);
38
38
  }
39
- /* vim: set noet sws=4 sw=4: */
@@ -2,23 +2,10 @@
2
2
 
3
3
  VALUE cPsychVisitorsYamlTree;
4
4
 
5
- /*
6
- * call-seq: private_iv_get(target, prop)
7
- *
8
- * Get the private instance variable +prop+ from +target+
9
- */
10
- static VALUE private_iv_get(VALUE self, VALUE target, VALUE prop)
11
- {
12
- return rb_attr_get(target, rb_intern(StringValueCStr(prop)));
13
- }
14
-
15
5
  void Init_psych_yaml_tree(void)
16
6
  {
17
7
  VALUE psych = rb_define_module("Psych");
18
8
  VALUE visitors = rb_define_module_under(psych, "Visitors");
19
9
  VALUE visitor = rb_define_class_under(visitors, "Visitor", rb_cObject);
20
10
  cPsychVisitorsYamlTree = rb_define_class_under(visitors, "YAMLTree", visitor);
21
-
22
- rb_define_private_method(cPsychVisitorsYamlTree, "private_iv_get", private_iv_get, 2);
23
11
  }
24
- /* vim: set noet sws=4 sw=4: */
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
- require 'psych/omap'
3
- require 'psych/set'
2
+ require_relative 'omap'
3
+ require_relative 'set'
4
4
 
5
5
  module Psych
6
6
  class ClassLoader # :nodoc:
@@ -35,9 +35,11 @@ module Psych
35
35
 
36
36
  constants.each do |const|
37
37
  konst = const_get const
38
- define_method(const.to_s.downcase) do
39
- load konst
40
- end
38
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
39
+ def #{const.to_s.downcase}
40
+ load #{konst.inspect}
41
+ end
42
+ RUBY
41
43
  end
42
44
 
43
45
  private
@@ -69,7 +71,7 @@ module Psych
69
71
  rescue
70
72
  nil
71
73
  end
72
- }.compact]
74
+ }.compact].freeze
73
75
 
74
76
  class Restricted < ClassLoader
75
77
  def initialize classes, symbols
@@ -84,7 +86,7 @@ module Psych
84
86
  if @symbols.include? sym
85
87
  super
86
88
  else
87
- raise DisallowedClass, 'Symbol'
89
+ raise DisallowedClass.new('load', 'Symbol')
88
90
  end
89
91
  end
90
92
 
@@ -94,7 +96,7 @@ module Psych
94
96
  if @classes.include? klassname
95
97
  super
96
98
  else
97
- raise DisallowedClass, klassname
99
+ raise DisallowedClass.new('load', klassname)
98
100
  end
99
101
  end
100
102
  end
@@ -15,5 +15,5 @@ class Object
15
15
  end
16
16
 
17
17
  if defined?(::IRB)
18
- require 'psych/y'
18
+ require_relative 'y'
19
19
  end
@@ -6,9 +6,23 @@ module Psych
6
6
  class BadAlias < Exception
7
7
  end
8
8
 
9
+ # Subclasses `BadAlias` for backwards compatibility
10
+ class AliasesNotEnabled < BadAlias
11
+ def initialize
12
+ super "Alias parsing was not enabled. To enable it, pass `aliases: true` to `Psych::load` or `Psych::safe_load`."
13
+ end
14
+ end
15
+
16
+ # Subclasses `BadAlias` for backwards compatibility
17
+ class AnchorNotDefined < BadAlias
18
+ def initialize anchor_name
19
+ super "An alias referenced an unknown anchor: #{anchor_name}"
20
+ end
21
+ end
22
+
9
23
  class DisallowedClass < Exception
10
- def initialize klass_name
11
- super "Tried to load unspecified class: #{klass_name}"
24
+ def initialize action, klass_name
25
+ super "Tried to #{action} unspecified class: #{klass_name}"
12
26
  end
13
27
  end
14
28
  end
data/lib/psych/handler.rb CHANGED
@@ -119,7 +119,7 @@ module Psych
119
119
  # +tag+ is an associated tag or nil
120
120
  # +plain+ is a boolean value
121
121
  # +quoted+ is a boolean value
122
- # +style+ is an integer idicating the string style
122
+ # +style+ is an integer indicating the string style
123
123
  #
124
124
  # See the constants in Psych::Nodes::Scalar for the possible values of
125
125
  # +style+
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- require 'psych/tree_builder'
2
+ require_relative '../tree_builder'
3
3
 
4
4
  module Psych
5
5
  module Handlers
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- require 'psych/handler'
2
+ require_relative '../handler'
3
3
 
4
4
  module Psych
5
5
  module Handlers
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
- require 'psych/json/ruby_events'
3
- require 'psych/json/yaml_events'
2
+ require_relative 'ruby_events'
3
+ require_relative 'yaml_events'
4
4
 
5
5
  module Psych
6
6
  module JSON
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- require 'psych/json/yaml_events'
2
+ require_relative 'yaml_events'
3
3
 
4
4
  module Psych
5
5
  module JSON
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
- require 'stringio'
3
- require 'psych/class_loader'
4
- require 'psych/scalar_scanner'
2
+ require_relative '../class_loader'
3
+ require_relative '../scalar_scanner'
5
4
 
6
5
  module Psych
7
6
  module Nodes
@@ -46,8 +45,8 @@ module Psych
46
45
  # Convert this node to Ruby.
47
46
  #
48
47
  # See also Psych::Visitors::ToRuby
49
- def to_ruby
50
- Visitors::ToRuby.create.accept(self)
48
+ def to_ruby(symbolize_names: false, freeze: false, strict_integer: false)
49
+ Visitors::ToRuby.create(symbolize_names: symbolize_names, freeze: freeze, strict_integer: strict_integer).accept(self)
51
50
  end
52
51
  alias :transform :to_ruby
53
52
 
@@ -56,6 +55,7 @@ module Psych
56
55
  #
57
56
  # See also Psych::Visitors::Emitter
58
57
  def yaml io = nil, options = {}
58
+ require "stringio"
59
59
  real_io = io || StringIO.new(''.encode('utf-8'))
60
60
 
61
61
  Visitors::Emitter.new(real_io, options).accept self
@@ -50,7 +50,7 @@ module Psych
50
50
  # +tag+ is an associated tag or nil
51
51
  # +plain+ is a boolean value
52
52
  # +quoted+ is a boolean value
53
- # +style+ is an integer idicating the string style
53
+ # +style+ is an integer indicating the string style
54
54
  #
55
55
  # == See Also
56
56
  #
data/lib/psych/nodes.rb CHANGED
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
- require 'psych/nodes/node'
3
- require 'psych/nodes/stream'
4
- require 'psych/nodes/document'
5
- require 'psych/nodes/sequence'
6
- require 'psych/nodes/scalar'
7
- require 'psych/nodes/mapping'
8
- require 'psych/nodes/alias'
2
+ require_relative 'nodes/node'
3
+ require_relative 'nodes/stream'
4
+ require_relative 'nodes/document'
5
+ require_relative 'nodes/sequence'
6
+ require_relative 'nodes/scalar'
7
+ require_relative 'nodes/mapping'
8
+ require_relative 'nodes/alias'
9
9
 
10
10
  module Psych
11
11
  ###
data/lib/psych/parser.rb CHANGED
@@ -48,5 +48,18 @@ module Psych
48
48
  @handler = handler
49
49
  @external_encoding = ANY
50
50
  end
51
+
52
+ ###
53
+ # call-seq:
54
+ # parser.parse(yaml)
55
+ #
56
+ # Parse the YAML document contained in +yaml+. Events will be called on
57
+ # the handler set on the parser instance.
58
+ #
59
+ # See Psych::Parser and Psych::Parser#handler
60
+
61
+ def parse yaml, path = yaml.respond_to?(:path) ? yaml.path : "<unknown>"
62
+ _native_parse @handler, yaml, path
63
+ end
51
64
  end
52
65
  end
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
- require 'strscan'
3
2
 
4
3
  module Psych
5
4
  ###
@@ -9,103 +8,97 @@ module Psych
9
8
  TIME = /^-?\d{4}-\d{1,2}-\d{1,2}(?:[Tt]|\s+)\d{1,2}:\d\d:\d\d(?:\.\d*)?(?:\s*(?:Z|[-+]\d{1,2}:?(?:\d\d)?))?$/
10
9
 
11
10
  # Taken from http://yaml.org/type/float.html
12
- FLOAT = /^(?:[-+]?([0-9][0-9_,]*)?\.[0-9]*([eE][-+][0-9]+)?(?# base 10)
13
- |[-+]?\.(inf|Inf|INF)(?# infinity)
14
- |\.(nan|NaN|NAN)(?# not a number))$/x
15
-
16
- # Taken from http://yaml.org/type/int.html
17
- INTEGER = /^(?:[-+]?0b[0-1_]+ (?# base 2)
18
- |[-+]?0[0-7_]+ (?# base 8)
19
- |[-+]?(?:0|[1-9][0-9_]*) (?# base 10)
20
- |[-+]?0x[0-9a-fA-F_]+ (?# base 16))$/x
11
+ # Base 60, [-+]inf and NaN are handled separately
12
+ FLOAT = /^(?:[-+]?([0-9][0-9_,]*)?\.[0-9]*([eE][-+][0-9]+)?(?# base 10))$/x
13
+
14
+ # Taken from http://yaml.org/type/int.html and modified to ensure at least one numerical symbol exists
15
+ INTEGER_STRICT = /^(?:[-+]?0b[_]*[0-1][0-1_]* (?# base 2)
16
+ |[-+]?0[_]*[0-7][0-7_]* (?# base 8)
17
+ |[-+]?(0|[1-9][0-9_]*) (?# base 10)
18
+ |[-+]?0x[_]*[0-9a-fA-F][0-9a-fA-F_]* (?# base 16))$/x
19
+
20
+ # Same as above, but allows commas.
21
+ # Not to YML spec, but kept for backwards compatibility
22
+ INTEGER_LEGACY = /^(?:[-+]?0b[_,]*[0-1][0-1_,]* (?# base 2)
23
+ |[-+]?0[_,]*[0-7][0-7_,]* (?# base 8)
24
+ |[-+]?(?:0|[1-9](?:[0-9]|,[0-9]|_[0-9])*) (?# base 10)
25
+ |[-+]?0x[_,]*[0-9a-fA-F][0-9a-fA-F_,]* (?# base 16))$/x
21
26
 
22
27
  attr_reader :class_loader
23
28
 
24
29
  # Create a new scanner
25
- def initialize class_loader
26
- @string_cache = {}
30
+ def initialize class_loader, strict_integer: false
27
31
  @symbol_cache = {}
28
32
  @class_loader = class_loader
33
+ @strict_integer = strict_integer
29
34
  end
30
35
 
31
36
  # Tokenize +string+ returning the Ruby object
32
37
  def tokenize string
33
38
  return nil if string.empty?
34
- return string if @string_cache.key?(string)
35
39
  return @symbol_cache[string] if @symbol_cache.key?(string)
36
-
37
- case string
40
+ integer_regex = @strict_integer ? INTEGER_STRICT : INTEGER_LEGACY
38
41
  # Check for a String type, being careful not to get caught by hash keys, hex values, and
39
42
  # special floats (e.g., -.inf).
40
- when /^[^\d\.:-]?[A-Za-z_\s!@#\$%\^&\*\(\)\{\}\<\>\|\/\\~;=]+/, /\n/
41
- if string.length > 5
42
- @string_cache[string] = true
43
- return string
44
- end
43
+ if string.match?(%r{^[^\d.:-]?[[:alpha:]_\s!@#$%\^&*(){}<>|/\\~;=]+}) || string.match?(/\n/)
44
+ return string if string.length > 5
45
45
 
46
- case string
47
- when /^[^ytonf~]/i
48
- @string_cache[string] = true
46
+ if string.match?(/^[^ytonf~]/i)
49
47
  string
50
- when '~', /^null$/i
48
+ elsif string == '~' || string.match?(/^null$/i)
51
49
  nil
52
- when /^(yes|true|on)$/i
50
+ elsif string.match?(/^(yes|true|on)$/i)
53
51
  true
54
- when /^(no|false|off)$/i
52
+ elsif string.match?(/^(no|false|off)$/i)
55
53
  false
56
54
  else
57
- @string_cache[string] = true
58
55
  string
59
56
  end
60
- when TIME
57
+ elsif string.match?(TIME)
61
58
  begin
62
59
  parse_time string
63
60
  rescue ArgumentError
64
61
  string
65
62
  end
66
- when /^\d{4}-(?:1[012]|0\d|\d)-(?:[12]\d|3[01]|0\d|\d)$/
67
- require 'date'
63
+ elsif string.match?(/^\d{4}-(?:1[012]|0\d|\d)-(?:[12]\d|3[01]|0\d|\d)$/)
68
64
  begin
69
- class_loader.date.strptime(string, '%Y-%m-%d')
65
+ class_loader.date.strptime(string, '%F', Date::GREGORIAN)
70
66
  rescue ArgumentError
71
67
  string
72
68
  end
73
- when /^\.inf$/i
69
+ elsif string.match?(/^\+?\.inf$/i)
74
70
  Float::INFINITY
75
- when /^-\.inf$/i
71
+ elsif string.match?(/^-\.inf$/i)
76
72
  -Float::INFINITY
77
- when /^\.nan$/i
73
+ elsif string.match?(/^\.nan$/i)
78
74
  Float::NAN
79
- when /^:./
75
+ elsif string.match?(/^:./)
80
76
  if string =~ /^:(["'])(.*)\1/
81
77
  @symbol_cache[string] = class_loader.symbolize($2.sub(/^:/, ''))
82
78
  else
83
79
  @symbol_cache[string] = class_loader.symbolize(string.sub(/^:/, ''))
84
80
  end
85
- when /^[-+]?[0-9][0-9_]*(:[0-5]?[0-9]){1,2}$/
81
+ elsif string.match?(/^[-+]?[0-9][0-9_]*(:[0-5]?[0-9]){1,2}$/)
86
82
  i = 0
87
83
  string.split(':').each_with_index do |n,e|
88
84
  i += (n.to_i * 60 ** (e - 2).abs)
89
85
  end
90
86
  i
91
- when /^[-+]?[0-9][0-9_]*(:[0-5]?[0-9]){1,2}\.[0-9_]*$/
87
+ elsif string.match?(/^[-+]?[0-9][0-9_]*(:[0-5]?[0-9]){1,2}\.[0-9_]*$/)
92
88
  i = 0
93
89
  string.split(':').each_with_index do |n,e|
94
90
  i += (n.to_f * 60 ** (e - 2).abs)
95
91
  end
96
92
  i
97
- when FLOAT
98
- if string =~ /\A[-+]?\.\Z/
99
- @string_cache[string] = true
93
+ elsif string.match?(FLOAT)
94
+ if string.match?(/\A[-+]?\.\Z/)
100
95
  string
101
96
  else
102
- Float(string.gsub(/[,_]|\.([Ee]|$)/, '\1'))
97
+ Float(string.delete(',_').gsub(/\.([Ee]|$)/, '\1'))
103
98
  end
99
+ elsif string.match?(integer_regex)
100
+ parse_int string
104
101
  else
105
- int = parse_int string.gsub(/[,_]/, '')
106
- return int if int
107
-
108
- @string_cache[string] = true
109
102
  string
110
103
  end
111
104
  end
@@ -113,8 +106,7 @@ module Psych
113
106
  ###
114
107
  # Parse and return an int from +string+
115
108
  def parse_int string
116
- return unless INTEGER === string
117
- Integer(string)
109
+ Integer(string.delete(',_'))
118
110
  end
119
111
 
120
112
  ###
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- require 'psych/exception'
2
+ require_relative 'exception'
3
3
 
4
4
  module Psych
5
5
  class SyntaxError < Psych::Exception
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- require 'psych/handler'
2
+ require_relative 'handler'
3
3
 
4
4
  module Psych
5
5
  ###
@@ -41,7 +41,7 @@ module Psych
41
41
  Sequence
42
42
  Mapping
43
43
  }.each do |node|
44
- class_eval %{
44
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
45
45
  def start_#{node.downcase}(anchor, tag, implicit, style)
46
46
  n = Nodes::#{node}.new(anchor, tag, implicit, style)
47
47
  set_start_location(n)
@@ -54,7 +54,7 @@ module Psych
54
54
  set_end_location(n)
55
55
  n
56
56
  end
57
- }
57
+ RUBY
58
58
  end
59
59
 
60
60
  ###
@@ -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 = '5.2.3'
6
6
 
7
7
  if RUBY_ENGINE == 'jruby'
8
- DEFAULT_SNAKEYAML_VERSION = '1.23'.freeze
8
+ DEFAULT_SNAKEYAML_VERSION = '2.9'.freeze
9
9
  end
10
10
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- require 'psych/json/ruby_events'
2
+ require_relative '../json/ruby_events'
3
3
 
4
4
  module Psych
5
5
  module Visitors
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
- require 'psych/scalar_scanner'
3
- require 'psych/class_loader'
4
- require 'psych/exception'
2
+ require_relative '../scalar_scanner'
3
+ require_relative '../class_loader'
4
+ require_relative '../exception'
5
5
 
6
6
  unless defined?(Regexp::NOENCODING)
7
7
  Regexp::NOENCODING = 32
@@ -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, strict_integer: false)
16
16
  class_loader = ClassLoader.new
17
- scanner = ScalarScanner.new class_loader
18
- new(scanner, class_loader)
17
+ scanner = ScalarScanner.new class_loader, strict_integer: strict_integer
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.match?(/^(?: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)
@@ -74,8 +79,9 @@ module Psych
74
79
  class_loader.big_decimal._load o.value
75
80
  when "!ruby/object:DateTime"
76
81
  class_loader.date_time
77
- require 'date' unless defined? DateTime
78
- @ss.parse_time(o.value).to_datetime
82
+ t = @ss.parse_time(o.value)
83
+ DateTime.civil(*t.to_a[0, 6].reverse, Rational(t.utc_offset, 86400)) +
84
+ (t.subsec/86400)
79
85
  when '!ruby/encoding'
80
86
  ::Encoding.find o.value
81
87
  when "!ruby/object:Complex"
@@ -94,7 +100,7 @@ module Psych
94
100
  source = $1
95
101
  options = 0
96
102
  lang = nil
97
- ($2 || '').split('').each do |option|
103
+ $2&.each_char do |option|
98
104
  case option
99
105
  when 'x' then options |= Regexp::EXTENDED
100
106
  when 'i' then options |= Regexp::IGNORECASE
@@ -124,7 +130,7 @@ module Psych
124
130
  end
125
131
 
126
132
  def visit_Psych_Nodes_Sequence o
127
- if klass = resolve_class(Psych.load_tags[o.tag])
133
+ if klass = resolve_class(@load_tags[o.tag])
128
134
  instance = klass.allocate
129
135
 
130
136
  if instance.respond_to?(:init_with)
@@ -156,8 +162,8 @@ module Psych
156
162
  end
157
163
 
158
164
  def visit_Psych_Nodes_Mapping o
159
- if Psych.load_tags[o.tag]
160
- return revive(resolve_class(Psych.load_tags[o.tag]), o)
165
+ if @load_tags[o.tag]
166
+ return revive(resolve_class(@load_tags[o.tag]), o)
161
167
  end
162
168
  return revive_hash(register(o, {}), o) unless o.tag
163
169
 
@@ -252,6 +258,8 @@ module Psych
252
258
 
253
259
  e = build_exception((resolve_class($1) || class_loader.exception),
254
260
  h.delete('message'))
261
+
262
+ e.set_backtrace h.delete('backtrace') if h.key? 'backtrace'
255
263
  init_with(e, h, o)
256
264
 
257
265
  when '!set', 'tag:yaml.org,2002:set'
@@ -316,10 +324,11 @@ module Psych
316
324
  end
317
325
 
318
326
  def visit_Psych_Nodes_Alias o
319
- @st.fetch(o.anchor) { raise BadAlias, "Unknown alias: #{o.anchor}" }
327
+ @st.fetch(o.anchor) { raise AnchorNotDefined, o.anchor }
320
328
  end
321
329
 
322
330
  private
331
+
323
332
  def register node, object
324
333
  @st[node.anchor] = object if node.anchor
325
334
  object
@@ -331,13 +340,12 @@ module Psych
331
340
  list
332
341
  end
333
342
 
334
- SHOVEL = '<<'
335
- def revive_hash hash, o
343
+ def revive_hash hash, o, tagged= false
336
344
  o.children.each_slice(2) { |k,v|
337
345
  key = accept(k)
338
346
  val = accept(v)
339
347
 
340
- if key == SHOVEL && k.tag != "tag:yaml.org,2002:str"
348
+ if key == '<<' && k.tag != "tag:yaml.org,2002:str"
341
349
  case v
342
350
  when Nodes::Alias, Nodes::Mapping
343
351
  begin
@@ -359,6 +367,12 @@ module Psych
359
367
  hash[key] = val
360
368
  end
361
369
  else
370
+ if !tagged && @symbolize_names && key.is_a?(String)
371
+ key = key.to_sym
372
+ elsif !@freeze
373
+ key = deduplicate(key)
374
+ end
375
+
362
376
  hash[key] = val
363
377
  end
364
378
 
@@ -366,12 +380,32 @@ module Psych
366
380
  hash
367
381
  end
368
382
 
383
+ if RUBY_VERSION < '2.7'
384
+ def deduplicate key
385
+ if key.is_a?(String)
386
+ # It is important to untaint the string, otherwise it won't
387
+ # be deduplicated into an fstring, but simply frozen.
388
+ -(key.untaint)
389
+ else
390
+ key
391
+ end
392
+ end
393
+ else
394
+ def deduplicate key
395
+ if key.is_a?(String)
396
+ -key
397
+ else
398
+ key
399
+ end
400
+ end
401
+ end
402
+
369
403
  def merge_key hash, key, val
370
404
  end
371
405
 
372
406
  def revive klass, node
373
407
  s = register(node, klass.allocate)
374
- init_with(s, revive_hash({}, node), node)
408
+ init_with(s, revive_hash({}, node, true), node)
375
409
  end
376
410
 
377
411
  def init_with o, h, node
@@ -394,7 +428,7 @@ module Psych
394
428
 
395
429
  class NoAliasRuby < ToRuby
396
430
  def visit_Psych_Nodes_Alias o
397
- raise BadAlias, "Unknown alias: #{o.anchor}"
431
+ raise AliasesNotEnabled
398
432
  end
399
433
  end
400
434
  end