psych 1.3.4 → 2.0.0

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