psych 1.3.4 → 2.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.
Files changed (88) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.rdoc +138 -0
  3. data/Manifest.txt +27 -8
  4. data/README.rdoc +23 -2
  5. data/Rakefile +1 -1
  6. data/ext/psych/depend +3 -0
  7. data/ext/psych/extconf.rb +27 -11
  8. data/ext/psych/psych.h +4 -4
  9. data/ext/psych/{emitter.c → psych_emitter.c} +0 -0
  10. data/ext/psych/{emitter.h → psych_emitter.h} +0 -0
  11. data/ext/psych/{parser.c → psych_parser.c} +1 -1
  12. data/ext/psych/{parser.h → psych_parser.h} +0 -0
  13. data/ext/psych/{to_ruby.c → psych_to_ruby.c} +3 -1
  14. data/ext/psych/{to_ruby.h → psych_to_ruby.h} +0 -0
  15. data/ext/psych/{yaml_tree.c → psych_yaml_tree.c} +0 -0
  16. data/ext/psych/{yaml_tree.h → psych_yaml_tree.h} +0 -0
  17. data/ext/psych/yaml/LICENSE +19 -0
  18. data/ext/psych/yaml/api.c +1392 -0
  19. data/ext/psych/yaml/config.h +11 -0
  20. data/ext/psych/yaml/dumper.c +394 -0
  21. data/ext/psych/yaml/emitter.c +2329 -0
  22. data/ext/psych/yaml/loader.c +432 -0
  23. data/ext/psych/yaml/parser.c +1374 -0
  24. data/ext/psych/yaml/reader.c +465 -0
  25. data/ext/psych/yaml/scanner.c +3570 -0
  26. data/ext/psych/yaml/writer.c +141 -0
  27. data/ext/psych/yaml/yaml.h +1971 -0
  28. data/ext/psych/yaml/yaml_private.h +643 -0
  29. data/lib/psych.rb +217 -51
  30. data/lib/psych/class_loader.rb +101 -0
  31. data/lib/psych/core_ext.rb +1 -8
  32. data/lib/psych/deprecated.rb +3 -1
  33. data/lib/psych/exception.rb +13 -0
  34. data/lib/psych/handler.rb +13 -0
  35. data/lib/psych/handlers/recorder.rb +39 -0
  36. data/lib/psych/json/stream.rb +1 -0
  37. data/lib/psych/nodes/node.rb +3 -1
  38. data/lib/psych/scalar_scanner.rb +46 -25
  39. data/lib/psych/stream.rb +1 -0
  40. data/lib/psych/streaming.rb +10 -5
  41. data/lib/psych/syntax_error.rb +3 -1
  42. data/lib/psych/visitors/json_tree.rb +5 -2
  43. data/lib/psych/visitors/to_ruby.rb +123 -75
  44. data/lib/psych/visitors/yaml_tree.rb +59 -17
  45. data/lib/psych/y.rb +9 -0
  46. data/test/psych/handlers/test_recorder.rb +25 -0
  47. data/test/psych/helper.rb +30 -1
  48. data/test/psych/test_alias_and_anchor.rb +1 -1
  49. data/test/psych/test_array.rb +1 -1
  50. data/test/psych/test_boolean.rb +1 -1
  51. data/test/psych/test_class.rb +1 -1
  52. data/test/psych/test_coder.rb +3 -3
  53. data/test/psych/test_date_time.rb +1 -1
  54. data/test/psych/test_deprecated.rb +6 -2
  55. data/test/psych/test_document.rb +1 -1
  56. data/test/psych/test_emitter.rb +1 -1
  57. data/test/psych/test_encoding.rb +51 -65
  58. data/test/psych/test_engine_manager.rb +1 -11
  59. data/test/psych/test_exception.rb +40 -19
  60. data/test/psych/test_hash.rb +1 -1
  61. data/test/psych/test_json_tree.rb +1 -1
  62. data/test/psych/test_merge_keys.rb +52 -1
  63. data/test/psych/test_nil.rb +1 -1
  64. data/test/psych/test_null.rb +1 -1
  65. data/test/psych/test_numeric.rb +21 -1
  66. data/test/psych/test_object.rb +1 -1
  67. data/test/psych/test_object_references.rb +3 -3
  68. data/test/psych/test_omap.rb +1 -1
  69. data/test/psych/test_parser.rb +1 -1
  70. data/test/psych/test_psych.rb +15 -15
  71. data/test/psych/test_safe_load.rb +97 -0
  72. data/test/psych/test_scalar.rb +1 -1
  73. data/test/psych/test_scalar_scanner.rb +17 -2
  74. data/test/psych/test_serialize_subclasses.rb +1 -1
  75. data/test/psych/test_set.rb +1 -1
  76. data/test/psych/test_stream.rb +1 -1
  77. data/test/psych/test_string.rb +51 -3
  78. data/test/psych/test_struct.rb +1 -1
  79. data/test/psych/test_symbol.rb +1 -1
  80. data/test/psych/test_tainted.rb +8 -8
  81. data/test/psych/test_to_yaml_properties.rb +1 -1
  82. data/test/psych/test_tree_builder.rb +1 -1
  83. data/test/psych/test_yaml.rb +22 -2
  84. data/test/psych/test_yamldbm.rb +1 -1
  85. data/test/psych/test_yamlstore.rb +1 -1
  86. data/test/psych/visitors/test_to_ruby.rb +5 -4
  87. data/test/psych/visitors/test_yaml_tree.rb +19 -1
  88. metadata +45 -34
@@ -31,12 +31,5 @@ class Module
31
31
  end
32
32
 
33
33
  if defined?(::IRB)
34
- module Kernel
35
- def psych_y *objects
36
- puts Psych.dump_stream(*objects)
37
- end
38
- remove_method :y rescue nil
39
- alias y psych_y
40
- private :y
41
- end
34
+ require 'psych/y'
42
35
  end
@@ -21,6 +21,7 @@ module Psych
21
21
  target.psych_to_yaml unless opts[:nodump]
22
22
  end
23
23
 
24
+ # This method is deprecated, use Psych.load_stream instead.
24
25
  def self.load_documents yaml, &block
25
26
  if $VERBOSE
26
27
  warn "#{caller[0]}: load_documents is deprecated, use load_stream"
@@ -34,7 +35,8 @@ module Psych
34
35
  warn "#{caller[0]}: detect_implicit is deprecated" if $VERBOSE
35
36
  return '' unless String === thing
36
37
  return 'null' if '' == thing
37
- ScalarScanner.new.tokenize(thing).class.name.downcase
38
+ ss = ScalarScanner.new(ClassLoader.new)
39
+ ss.tokenize(thing).class.name.downcase
38
40
  end
39
41
 
40
42
  def self.add_ruby_type type_tag, &block
@@ -0,0 +1,13 @@
1
+ module Psych
2
+ class Exception < RuntimeError
3
+ end
4
+
5
+ class BadAlias < Exception
6
+ end
7
+
8
+ class DisallowedClass < Exception
9
+ def initialize klass_name
10
+ super "Tried to load unspecified class: #{klass_name}"
11
+ end
12
+ end
13
+ end
@@ -25,6 +25,19 @@ module Psych
25
25
  # Default dumping options
26
26
  OPTIONS = DumperOptions.new
27
27
 
28
+ # Events that a Handler should respond to.
29
+ EVENTS = [ :alias,
30
+ :empty,
31
+ :end_document,
32
+ :end_mapping,
33
+ :end_sequence,
34
+ :end_stream,
35
+ :scalar,
36
+ :start_document,
37
+ :start_mapping,
38
+ :start_sequence,
39
+ :start_stream ]
40
+
28
41
  ###
29
42
  # Called with +encoding+ when the YAML stream starts. This method is
30
43
  # called once per stream. A stream may contain multiple documents.
@@ -0,0 +1,39 @@
1
+ require 'psych/handler'
2
+
3
+ module Psych
4
+ module Handlers
5
+ ###
6
+ # This handler will capture an event and record the event. Recorder events
7
+ # are available vial Psych::Handlers::Recorder#events.
8
+ #
9
+ # For example:
10
+ #
11
+ # recorder = Psych::Handlers::Recorder.new
12
+ # parser = Psych::Parser.new recorder
13
+ # parser.parse '--- foo'
14
+ #
15
+ # recorder.events # => [list of events]
16
+ #
17
+ # # Replay the events
18
+ #
19
+ # emitter = Psych::Emitter.new $stdout
20
+ # recorder.events.each do |m, args|
21
+ # emitter.send m, *args
22
+ # end
23
+
24
+ class Recorder < Psych::Handler
25
+ attr_reader :events
26
+
27
+ def initialize
28
+ @events = []
29
+ super
30
+ end
31
+
32
+ EVENTS.each do |event|
33
+ define_method event do |*args|
34
+ @events << [event, args]
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -6,6 +6,7 @@ module Psych
6
6
  class Stream < Psych::Visitors::JSONTree
7
7
  include Psych::JSON::RubyEvents
8
8
  include Psych::Streaming
9
+ extend Psych::Streaming::ClassMethods
9
10
 
10
11
  class Emitter < Psych::Stream::Emitter # :nodoc:
11
12
  include Psych::JSON::YAMLEvents
@@ -1,4 +1,6 @@
1
1
  require 'stringio'
2
+ require 'psych/class_loader'
3
+ require 'psych/scalar_scanner'
2
4
 
3
5
  module Psych
4
6
  module Nodes
@@ -32,7 +34,7 @@ module Psych
32
34
  #
33
35
  # See also Psych::Visitors::ToRuby
34
36
  def to_ruby
35
- Visitors::ToRuby.new.accept self
37
+ Visitors::ToRuby.create.accept(self)
36
38
  end
37
39
  alias :transform :to_ruby
38
40
 
@@ -8,23 +8,36 @@ module Psych
8
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)?)?/
9
9
 
10
10
  # Taken from http://yaml.org/type/float.html
11
- FLOAT = /^(?:[-+]?([0-9][0-9_,]*)?\.[0-9.]*([eE][-+][0-9]+)?(?# base 10)
11
+ FLOAT = /^(?:[-+]?([0-9][0-9_,]*)?\.[0-9]*([eE][-+][0-9]+)?(?# base 10)
12
12
  |[-+]?[0-9][0-9_,]*(:[0-5]?[0-9])+\.[0-9_]*(?# base 60)
13
13
  |[-+]?\.(inf|Inf|INF)(?# infinity)
14
14
  |\.(nan|NaN|NAN)(?# not a number))$/x
15
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
21
+
22
+ attr_reader :class_loader
23
+
16
24
  # Create a new scanner
17
- def initialize
25
+ def initialize class_loader
18
26
  @string_cache = {}
27
+ @symbol_cache = {}
28
+ @class_loader = class_loader
19
29
  end
20
30
 
21
31
  # Tokenize +string+ returning the ruby object
22
32
  def tokenize string
23
33
  return nil if string.empty?
24
34
  return string if @string_cache.key?(string)
35
+ return @symbol_cache[string] if @symbol_cache.key?(string)
25
36
 
26
37
  case string
27
- when /^[A-Za-z~]/
38
+ # Check for a String type, being careful not to get caught by hash keys, hex values, and
39
+ # special floats (e.g., -.inf).
40
+ when /^[^\d\.:-]?[A-Za-z_\s!@#\$%\^&\*\(\)\{\}\<\>\|\/\\~;=]+/
28
41
  if string.length > 5
29
42
  @string_cache[string] = true
30
43
  return string
@@ -45,25 +58,29 @@ module Psych
45
58
  string
46
59
  end
47
60
  when TIME
48
- parse_time string
61
+ begin
62
+ parse_time string
63
+ rescue ArgumentError
64
+ string
65
+ end
49
66
  when /^\d{4}-(?:1[012]|0\d|\d)-(?:[12]\d|3[01]|0\d|\d)$/
50
67
  require 'date'
51
68
  begin
52
- Date.strptime(string, '%Y-%m-%d')
69
+ class_loader.date.strptime(string, '%Y-%m-%d')
53
70
  rescue ArgumentError
54
71
  string
55
72
  end
56
73
  when /^\.inf$/i
57
- 1 / 0.0
74
+ Float::INFINITY
58
75
  when /^-\.inf$/i
59
- -1 / 0.0
76
+ -Float::INFINITY
60
77
  when /^\.nan$/i
61
- 0.0 / 0.0
78
+ Float::NAN
62
79
  when /^:./
63
80
  if string =~ /^:(["'])(.*)\1/
64
- $2.sub(/^:/, '').to_sym
81
+ @symbol_cache[string] = class_loader.symbolize($2.sub(/^:/, ''))
65
82
  else
66
- string.sub(/^:/, '').to_sym
83
+ @symbol_cache[string] = class_loader.symbolize(string.sub(/^:/, ''))
67
84
  end
68
85
  when /^[-+]?[0-9][0-9_]*(:[0-5]?[0-9])+$/
69
86
  i = 0
@@ -78,29 +95,33 @@ module Psych
78
95
  end
79
96
  i
80
97
  when FLOAT
81
- begin
82
- return Float(string.gsub(/[,_]/, ''))
83
- rescue ArgumentError
98
+ if string == '.'
99
+ @string_cache[string] = true
100
+ string
101
+ else
102
+ Float(string.gsub(/[,_]|\.$/, ''))
84
103
  end
85
-
86
- @string_cache[string] = true
87
- string
88
104
  else
89
- if string.count('.') < 2
90
- begin
91
- return Integer(string.gsub(/[,_]/, ''))
92
- rescue ArgumentError
93
- end
94
- end
105
+ int = parse_int string.gsub(/[,_]/, '')
106
+ return int if int
95
107
 
96
108
  @string_cache[string] = true
97
109
  string
98
110
  end
99
111
  end
100
112
 
113
+ ###
114
+ # Parse and return an int from +string+
115
+ def parse_int string
116
+ return unless INTEGER === string
117
+ Integer(string)
118
+ end
119
+
101
120
  ###
102
121
  # Parse and return a Time from +string+
103
122
  def parse_time string
123
+ klass = class_loader.load 'Time'
124
+
104
125
  date, time = *(string.split(/[ tT]/, 2))
105
126
  (yy, m, dd) = date.split('-').map { |x| x.to_i }
106
127
  md = time.match(/(\d+:\d+:\d+)(?:\.(\d*))?\s*(Z|[-+]\d+(:\d\d)?)?/)
@@ -108,10 +129,10 @@ module Psych
108
129
  (hh, mm, ss) = md[1].split(':').map { |x| x.to_i }
109
130
  us = (md[2] ? Rational("0.#{md[2]}") : 0) * 1000000
110
131
 
111
- time = Time.utc(yy, m, dd, hh, mm, ss, us)
132
+ time = klass.utc(yy, m, dd, hh, mm, ss, us)
112
133
 
113
134
  return time if 'Z' == md[3]
114
- return Time.at(time.to_i, us) unless md[3]
135
+ return klass.at(time.to_i, us) unless md[3]
115
136
 
116
137
  tz = md[3].match(/^([+\-]?\d{1,2})\:?(\d{1,2})?$/)[1..-1].compact.map { |digit| Integer(digit, 10) }
117
138
  offset = tz.first * 3600
@@ -122,7 +143,7 @@ module Psych
122
143
  offset += ((tz[1] || 0) * 60)
123
144
  end
124
145
 
125
- Time.at((time - offset).to_i, us)
146
+ klass.at((time - offset).to_i, us)
126
147
  end
127
148
  end
128
149
  end
@@ -32,5 +32,6 @@ module Psych
32
32
  end
33
33
 
34
34
  include Psych::Streaming
35
+ extend Psych::Streaming::ClassMethods
35
36
  end
36
37
  end
@@ -1,10 +1,15 @@
1
1
  module Psych
2
2
  module Streaming
3
- ###
4
- # Create a new streaming emitter. Emitter will print to +io+. See
5
- # Psych::Stream for an example.
6
- def initialize io
7
- super({}, self.class.const_get(:Emitter).new(io))
3
+ module ClassMethods
4
+ ###
5
+ # Create a new streaming emitter. Emitter will print to +io+. See
6
+ # Psych::Stream for an example.
7
+ def new io
8
+ emitter = const_get(:Emitter).new(io)
9
+ class_loader = ClassLoader.new
10
+ ss = ScalarScanner.new class_loader
11
+ super(emitter, ss, {})
12
+ end
8
13
  end
9
14
 
10
15
  ###
@@ -1,5 +1,7 @@
1
+ require 'psych/exception'
2
+
1
3
  module Psych
2
- class SyntaxError < ::SyntaxError
4
+ class SyntaxError < Psych::Exception
3
5
  attr_reader :file, :line, :column, :offset, :problem, :context
4
6
 
5
7
  def initialize file, line, col, offset, problem, context
@@ -5,8 +5,11 @@ module Psych
5
5
  class JSONTree < YAMLTree
6
6
  include Psych::JSON::RubyEvents
7
7
 
8
- def initialize options = {}, emitter = Psych::JSON::TreeBuilder.new
9
- super
8
+ def self.create options = {}
9
+ emitter = Psych::JSON::TreeBuilder.new
10
+ class_loader = ClassLoader.new
11
+ ss = ScalarScanner.new class_loader
12
+ new(emitter, ss, options)
10
13
  end
11
14
 
12
15
  def accept target
@@ -1,4 +1,6 @@
1
1
  require 'psych/scalar_scanner'
2
+ require 'psych/class_loader'
3
+ require 'psych/exception'
2
4
 
3
5
  unless defined?(Regexp::NOENCODING)
4
6
  Regexp::NOENCODING = 32
@@ -9,11 +11,20 @@ module Psych
9
11
  ###
10
12
  # This class walks a YAML AST, converting each node to ruby
11
13
  class ToRuby < Psych::Visitors::Visitor
12
- def initialize ss = ScalarScanner.new
14
+ def self.create
15
+ class_loader = ClassLoader.new
16
+ scanner = ScalarScanner.new class_loader
17
+ new(scanner, class_loader)
18
+ end
19
+
20
+ attr_reader :class_loader
21
+
22
+ def initialize ss, class_loader
13
23
  super()
14
24
  @st = {}
15
25
  @ss = ss
16
26
  @domain_types = Psych.domain_types
27
+ @class_loader = class_loader
17
28
  end
18
29
 
19
30
  def accept target
@@ -32,7 +43,7 @@ module Psych
32
43
  end
33
44
 
34
45
  def deserialize o
35
- if klass = Psych.load_tags[o.tag]
46
+ if klass = resolve_class(Psych.load_tags[o.tag])
36
47
  instance = klass.allocate
37
48
 
38
49
  if instance.respond_to?(:init_with)
@@ -59,19 +70,23 @@ module Psych
59
70
  end
60
71
  when '!ruby/object:BigDecimal'
61
72
  require 'bigdecimal'
62
- BigDecimal._load o.value
73
+ class_loader.big_decimal._load o.value
63
74
  when "!ruby/object:DateTime"
75
+ class_loader.date_time
64
76
  require 'date'
65
77
  @ss.parse_time(o.value).to_datetime
66
78
  when "!ruby/object:Complex"
79
+ class_loader.complex
67
80
  Complex(o.value)
68
81
  when "!ruby/object:Rational"
82
+ class_loader.rational
69
83
  Rational(o.value)
70
84
  when "!ruby/class", "!ruby/module"
71
85
  resolve_class o.value
72
86
  when "tag:yaml.org,2002:float", "!float"
73
87
  Float(@ss.tokenize(o.value))
74
88
  when "!ruby/regexp"
89
+ klass = class_loader.regexp
75
90
  o.value =~ /^\/(.*)\/([mixn]*)$/
76
91
  source = $1
77
92
  options = 0
@@ -85,15 +100,16 @@ module Psych
85
100
  else lang = option
86
101
  end
87
102
  end
88
- Regexp.new(*[source, options, lang].compact)
103
+ klass.new(*[source, options, lang].compact)
89
104
  when "!ruby/range"
105
+ klass = class_loader.range
90
106
  args = o.value.split(/([.]{2,3})/, 2).map { |s|
91
107
  accept Nodes::Scalar.new(s)
92
108
  }
93
109
  args.push(args.delete_at(1) == '...')
94
- Range.new(*args)
110
+ klass.new(*args)
95
111
  when /^!ruby\/sym(bol)?:?(.*)?$/
96
- o.value.to_sym
112
+ class_loader.symbolize o.value
97
113
  else
98
114
  @ss.tokenize o.value
99
115
  end
@@ -105,7 +121,7 @@ module Psych
105
121
  end
106
122
 
107
123
  def visit_Psych_Nodes_Sequence o
108
- if klass = Psych.load_tags[o.tag]
124
+ if klass = resolve_class(Psych.load_tags[o.tag])
109
125
  instance = klass.allocate
110
126
 
111
127
  if instance.respond_to?(:init_with)
@@ -118,6 +134,8 @@ module Psych
118
134
  end
119
135
 
120
136
  case o.tag
137
+ when nil
138
+ register_empty(o)
121
139
  when '!omap', 'tag:yaml.org,2002:omap'
122
140
  map = register(o, Psych::Omap.new)
123
141
  o.children.each { |a|
@@ -130,51 +148,29 @@ module Psych
130
148
  o.children.each { |c| list.push accept c }
131
149
  list
132
150
  else
133
- list = register(o, [])
134
- o.children.each { |c| list.push accept c }
135
- list
151
+ register_empty(o)
136
152
  end
137
153
  end
138
154
 
139
155
  def visit_Psych_Nodes_Mapping o
140
- return revive(Psych.load_tags[o.tag], o) if Psych.load_tags[o.tag]
156
+ if Psych.load_tags[o.tag]
157
+ return revive(resolve_class(Psych.load_tags[o.tag]), o)
158
+ end
141
159
  return revive_hash({}, o) unless o.tag
142
160
 
143
161
  case o.tag
144
- when /^!(?:str|ruby\/string)(?::(.*))?/, 'tag:yaml.org,2002:str'
145
- klass = resolve_class($1)
146
- members = Hash[*o.children.map { |c| accept c }]
147
- string = members.delete 'str'
148
-
149
- if klass
150
- string = klass.allocate.replace string
151
- register(o, string)
152
- end
153
-
154
- init_with(string, members.map { |k,v| [k.to_s.sub(/^@/, ''),v] }, o)
155
- when /^!ruby\/array:(.*)$/
156
- klass = resolve_class($1)
157
- list = register(o, klass.allocate)
158
-
159
- members = Hash[o.children.map { |c| accept c }.each_slice(2).to_a]
160
- list.replace members['internal']
161
-
162
- members['ivars'].each do |ivar, v|
163
- list.instance_variable_set ivar, v
164
- end
165
- list
166
162
  when /^!ruby\/struct:?(.*)?$/
167
- klass = resolve_class($1)
163
+ klass = resolve_class($1) if $1
168
164
 
169
165
  if klass
170
166
  s = register(o, klass.allocate)
171
167
 
172
168
  members = {}
173
- struct_members = s.members.map { |x| x.to_sym }
169
+ struct_members = s.members.map { |x| class_loader.symbolize x }
174
170
  o.children.each_slice(2) do |k,v|
175
171
  member = accept(k)
176
172
  value = accept(v)
177
- if struct_members.include?(member.to_sym)
173
+ if struct_members.include?(class_loader.symbolize(member))
178
174
  s.send("#{member}=", value)
179
175
  else
180
176
  members[member.to_s.sub(/^@/, '')] = value
@@ -182,48 +178,88 @@ module Psych
182
178
  end
183
179
  init_with(s, members, o)
184
180
  else
181
+ klass = class_loader.struct
185
182
  members = o.children.map { |c| accept c }
186
183
  h = Hash[*members]
187
- Struct.new(*h.map { |k,v| k.to_sym }).new(*h.map { |k,v| v })
184
+ klass.new(*h.map { |k,v|
185
+ class_loader.symbolize k
186
+ }).new(*h.map { |k,v| v })
187
+ end
188
+
189
+ when /^!ruby\/object:?(.*)?$/
190
+ name = $1 || 'Object'
191
+
192
+ if name == 'Complex'
193
+ class_loader.complex
194
+ h = Hash[*o.children.map { |c| accept c }]
195
+ register o, Complex(h['real'], h['image'])
196
+ elsif name == 'Rational'
197
+ class_loader.rational
198
+ h = Hash[*o.children.map { |c| accept c }]
199
+ register o, Rational(h['numerator'], h['denominator'])
200
+ else
201
+ obj = revive((resolve_class(name) || class_loader.object), o)
202
+ obj
203
+ end
204
+
205
+ when /^!(?:str|ruby\/string)(?::(.*))?/, 'tag:yaml.org,2002:str'
206
+ klass = resolve_class($1)
207
+ members = {}
208
+ string = nil
209
+
210
+ o.children.each_slice(2) do |k,v|
211
+ key = accept k
212
+ value = accept v
213
+
214
+ if key == 'str'
215
+ if klass
216
+ string = klass.allocate.replace value
217
+ else
218
+ string = value
219
+ end
220
+ register(o, string)
221
+ else
222
+ members[key] = value
223
+ end
224
+ end
225
+ init_with(string, members.map { |k,v| [k.to_s.sub(/^@/, ''),v] }, o)
226
+ when /^!ruby\/array:(.*)$/
227
+ klass = resolve_class($1)
228
+ list = register(o, klass.allocate)
229
+
230
+ members = Hash[o.children.map { |c| accept c }.each_slice(2).to_a]
231
+ list.replace members['internal']
232
+
233
+ members['ivars'].each do |ivar, v|
234
+ list.instance_variable_set ivar, v
188
235
  end
236
+ list
189
237
 
190
238
  when '!ruby/range'
239
+ klass = class_loader.range
191
240
  h = Hash[*o.children.map { |c| accept c }]
192
- register o, Range.new(h['begin'], h['end'], h['excl'])
241
+ register o, klass.new(h['begin'], h['end'], h['excl'])
193
242
 
194
243
  when /^!ruby\/exception:?(.*)?$/
195
244
  h = Hash[*o.children.map { |c| accept c }]
196
245
 
197
- e = build_exception((resolve_class($1) || Exception),
246
+ e = build_exception((resolve_class($1) || class_loader.exception),
198
247
  h.delete('message'))
199
248
  init_with(e, h, o)
200
249
 
201
250
  when '!set', 'tag:yaml.org,2002:set'
202
- set = Psych::Set.new
251
+ set = class_loader.psych_set.new
203
252
  @st[o.anchor] = set if o.anchor
204
253
  o.children.each_slice(2) do |k,v|
205
254
  set[accept(k)] = accept(v)
206
255
  end
207
256
  set
208
257
 
209
- when '!ruby/object:Complex'
210
- h = Hash[*o.children.map { |c| accept c }]
211
- register o, Complex(h['real'], h['image'])
212
-
213
- when '!ruby/object:Rational'
214
- h = Hash[*o.children.map { |c| accept c }]
215
- register o, Rational(h['numerator'], h['denominator'])
216
-
217
- when /^!ruby\/object:?(.*)?$/
218
- name = $1 || 'Object'
219
- obj = revive((resolve_class(name) || Object), o)
220
- obj
221
-
222
258
  when /^!map:(.*)$/, /^!ruby\/hash:(.*)$/
223
259
  revive_hash resolve_class($1).new, o
224
260
 
225
261
  when '!omap', 'tag:yaml.org,2002:omap'
226
- map = register(o, Psych::Omap.new)
262
+ map = register(o, class_loader.psych_omap.new)
227
263
  o.children.each_slice(2) do |l,r|
228
264
  map[accept(l)] = accept r
229
265
  end
@@ -252,31 +288,51 @@ module Psych
252
288
  object
253
289
  end
254
290
 
291
+ def register_empty object
292
+ list = register(object, [])
293
+ object.children.each { |c| list.push accept c }
294
+ list
295
+ end
296
+
255
297
  def revive_hash hash, o
256
298
  @st[o.anchor] = hash if o.anchor
257
299
 
258
- o.children.each_slice(2) { |k,v|
300
+ o.children.each_slice(2) { |k,v|
259
301
  key = accept(k)
302
+ val = accept(v)
260
303
 
261
304
  if key == '<<'
262
305
  case v
263
306
  when Nodes::Alias
264
- hash.merge! accept(v)
307
+ begin
308
+ hash.merge! val
309
+ rescue TypeError
310
+ hash[key] = val
311
+ end
265
312
  when Nodes::Sequence
266
- accept(v).reverse_each do |value|
267
- hash.merge! value
313
+ begin
314
+ h = {}
315
+ val.reverse_each do |value|
316
+ h.merge! value
317
+ end
318
+ hash.merge! h
319
+ rescue TypeError
320
+ hash[key] = val
268
321
  end
269
322
  else
270
- hash[key] = accept(v)
323
+ hash[key] = val
271
324
  end
272
325
  else
273
- hash[key] = accept(v)
326
+ hash[key] = val
274
327
  end
275
328
 
276
329
  }
277
330
  hash
278
331
  end
279
332
 
333
+ def merge_key hash, key, val
334
+ end
335
+
280
336
  def revive klass, node
281
337
  s = klass.allocate
282
338
  @st[node.anchor] = s if node.anchor
@@ -303,21 +359,13 @@ module Psych
303
359
 
304
360
  # Convert +klassname+ to a Class
305
361
  def resolve_class klassname
306
- return nil unless klassname and not klassname.empty?
307
-
308
- name = klassname
309
- retried = false
310
-
311
- begin
312
- path2class(name)
313
- rescue ArgumentError, NameError => ex
314
- unless retried
315
- name = "Struct::#{name}"
316
- retried = ex
317
- retry
318
- end
319
- raise retried
320
- end
362
+ class_loader.load klassname
363
+ end
364
+ end
365
+
366
+ class NoAliasRuby < ToRuby
367
+ def visit_Psych_Nodes_Alias o
368
+ raise BadAlias, "Unknown alias: #{o.anchor}"
321
369
  end
322
370
  end
323
371
  end