summaryrefslogtreecommitdiff
path: root/lib/psych/visitors/to_ruby.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/psych/visitors/to_ruby.rb')
-rw-r--r--lib/psych/visitors/to_ruby.rb254
1 files changed, 170 insertions, 84 deletions
diff --git a/lib/psych/visitors/to_ruby.rb b/lib/psych/visitors/to_ruby.rb
index 2e082f9..f353e9c 100644
--- a/lib/psych/visitors/to_ruby.rb
+++ b/lib/psych/visitors/to_ruby.rb
@@ -1,4 +1,6 @@
require 'psych/scalar_scanner'
+require 'psych/class_loader'
+require 'psych/exception'
unless defined?(Regexp::NOENCODING)
Regexp::NOENCODING = 32
@@ -7,13 +9,22 @@ end
module Psych
module Visitors
###
- # This class walks a YAML AST, converting each node to ruby
+ # This class walks a YAML AST, converting each node to Ruby
class ToRuby < Psych::Visitors::Visitor
- def initialize ss = ScalarScanner.new
+ def self.create
+ class_loader = ClassLoader.new
+ scanner = ScalarScanner.new class_loader
+ new(scanner, class_loader)
+ end
+
+ attr_reader :class_loader
+
+ def initialize ss, class_loader
super()
@st = {}
@ss = ss
@domain_types = Psych.domain_types
+ @class_loader = class_loader
end
def accept target
@@ -32,7 +43,7 @@ module Psych
end
def deserialize o
- if klass = Psych.load_tags[o.tag]
+ if klass = resolve_class(Psych.load_tags[o.tag])
instance = klass.allocate
if instance.respond_to?(:init_with)
@@ -59,19 +70,25 @@ module Psych
end
when '!ruby/object:BigDecimal'
require 'bigdecimal'
- BigDecimal._load o.value
+ class_loader.big_decimal._load o.value
when "!ruby/object:DateTime"
+ class_loader.date_time
require 'date'
@ss.parse_time(o.value).to_datetime
+ when '!ruby/encoding'
+ ::Encoding.find o.value
when "!ruby/object:Complex"
+ class_loader.complex
Complex(o.value)
when "!ruby/object:Rational"
+ class_loader.rational
Rational(o.value)
when "!ruby/class", "!ruby/module"
resolve_class o.value
when "tag:yaml.org,2002:float", "!float"
Float(@ss.tokenize(o.value))
when "!ruby/regexp"
+ klass = class_loader.regexp
o.value =~ /^\/(.*)\/([mixn]*)$/
source = $1
options = 0
@@ -85,15 +102,16 @@ module Psych
else lang = option
end
end
- Regexp.new(*[source, options, lang].compact)
+ klass.new(*[source, options, lang].compact)
when "!ruby/range"
+ klass = class_loader.range
args = o.value.split(/([.]{2,3})/, 2).map { |s|
accept Nodes::Scalar.new(s)
}
args.push(args.delete_at(1) == '...')
- Range.new(*args)
+ klass.new(*args)
when /^!ruby\/sym(bol)?:?(.*)?$/
- o.value.to_sym
+ class_loader.symbolize o.value
else
@ss.tokenize o.value
end
@@ -105,7 +123,7 @@ module Psych
end
def visit_Psych_Nodes_Sequence o
- if klass = Psych.load_tags[o.tag]
+ if klass = resolve_class(Psych.load_tags[o.tag])
instance = klass.allocate
if instance.respond_to?(:init_with)
@@ -118,6 +136,8 @@ module Psych
end
case o.tag
+ when nil
+ register_empty(o)
when '!omap', 'tag:yaml.org,2002:omap'
map = register(o, Psych::Omap.new)
o.children.each { |a|
@@ -130,51 +150,29 @@ module Psych
o.children.each { |c| list.push accept c }
list
else
- list = register(o, [])
- o.children.each { |c| list.push accept c }
- list
+ register_empty(o)
end
end
def visit_Psych_Nodes_Mapping o
- return revive(Psych.load_tags[o.tag], o) if Psych.load_tags[o.tag]
- return revive_hash({}, o) unless o.tag
+ if Psych.load_tags[o.tag]
+ return revive(resolve_class(Psych.load_tags[o.tag]), o)
+ end
+ return revive_hash(register(o, {}), o) unless o.tag
case o.tag
- when /^!(?:str|ruby\/string)(?::(.*))?/, 'tag:yaml.org,2002:str'
- klass = resolve_class($1)
- members = Hash[*o.children.map { |c| accept c }]
- string = members.delete 'str'
-
- if klass
- string = klass.allocate
- string.replace string
- end
-
- init_with(string, members.map { |k,v| [k.to_s.sub(/^@/, ''),v] }, o)
- when /^!ruby\/array:(.*)$/
- klass = resolve_class($1)
- list = register(o, klass.allocate)
-
- members = Hash[o.children.map { |c| accept c }.each_slice(2).to_a]
- list.replace members['internal']
-
- members['ivars'].each do |ivar, v|
- list.instance_variable_set ivar, v
- end
- list
when /^!ruby\/struct:?(.*)?$/
- klass = resolve_class($1)
+ klass = resolve_class($1) if $1
if klass
s = register(o, klass.allocate)
members = {}
- struct_members = s.members.map { |x| x.to_sym }
+ struct_members = s.members.map { |x| class_loader.symbolize x }
o.children.each_slice(2) do |k,v|
member = accept(k)
value = accept(v)
- if struct_members.include?(member.to_sym)
+ if struct_members.include?(class_loader.symbolize(member))
s.send("#{member}=", value)
else
members[member.to_s.sub(/^@/, '')] = value
@@ -182,48 +180,128 @@ module Psych
end
init_with(s, members, o)
else
+ klass = class_loader.struct
members = o.children.map { |c| accept c }
h = Hash[*members]
- Struct.new(*h.map { |k,v| k.to_sym }).new(*h.map { |k,v| v })
+ s = klass.new(*h.map { |k,v|
+ class_loader.symbolize k
+ }).new(*h.map { |k,v| v })
+ register(o, s)
+ s
+ end
+
+ when /^!ruby\/object:?(.*)?$/
+ name = $1 || 'Object'
+
+ if name == 'Complex'
+ class_loader.complex
+ h = Hash[*o.children.map { |c| accept c }]
+ register o, Complex(h['real'], h['image'])
+ elsif name == 'Rational'
+ class_loader.rational
+ h = Hash[*o.children.map { |c| accept c }]
+ register o, Rational(h['numerator'], h['denominator'])
+ elsif name == 'Hash'
+ revive_hash(register(o, {}), o)
+ else
+ obj = revive((resolve_class(name) || class_loader.object), o)
+ obj
end
+ when /^!(?:str|ruby\/string)(?::(.*))?/, 'tag:yaml.org,2002:str'
+ klass = resolve_class($1)
+ members = {}
+ string = nil
+
+ o.children.each_slice(2) do |k,v|
+ key = accept k
+ value = accept v
+
+ if key == 'str'
+ if klass
+ string = klass.allocate.replace value
+ else
+ string = value
+ end
+ register(o, string)
+ else
+ members[key] = value
+ end
+ end
+ init_with(string, members.map { |k,v| [k.to_s.sub(/^@/, ''),v] }, o)
+ when /^!ruby\/array:(.*)$/
+ klass = resolve_class($1)
+ list = register(o, klass.allocate)
+
+ members = Hash[o.children.map { |c| accept c }.each_slice(2).to_a]
+ list.replace members['internal']
+
+ members['ivars'].each do |ivar, v|
+ list.instance_variable_set ivar, v
+ end
+ list
+
when '!ruby/range'
+ klass = class_loader.range
h = Hash[*o.children.map { |c| accept c }]
- register o, Range.new(h['begin'], h['end'], h['excl'])
+ register o, klass.new(h['begin'], h['end'], h['excl'])
when /^!ruby\/exception:?(.*)?$/
h = Hash[*o.children.map { |c| accept c }]
- e = build_exception((resolve_class($1) || Exception),
+ e = build_exception((resolve_class($1) || class_loader.exception),
h.delete('message'))
init_with(e, h, o)
when '!set', 'tag:yaml.org,2002:set'
- set = Psych::Set.new
+ set = class_loader.psych_set.new
@st[o.anchor] = set if o.anchor
o.children.each_slice(2) do |k,v|
set[accept(k)] = accept(v)
end
set
- when '!ruby/object:Complex'
- h = Hash[*o.children.map { |c| accept c }]
- register o, Complex(h['real'], h['image'])
+ when /^!ruby\/hash-with-ivars(?::(.*))?$/
+ hash = $1 ? resolve_class($1).allocate : {}
+ o.children.each_slice(2) do |key, value|
+ case key.value
+ when 'elements'
+ revive_hash hash, value
+ when 'ivars'
+ value.children.each_slice(2) do |k,v|
+ hash.instance_variable_set accept(k), accept(v)
+ end
+ end
+ end
+ hash
- when '!ruby/object:Rational'
- h = Hash[*o.children.map { |c| accept c }]
- register o, Rational(h['numerator'], h['denominator'])
+ when /^!map:(.*)$/, /^!ruby\/hash:(.*)$/
+ revive_hash register(o, resolve_class($1).allocate), o
- when /^!ruby\/object:?(.*)?$/
- name = $1 || 'Object'
- obj = revive((resolve_class(name) || Object), o)
- obj
+ when '!omap', 'tag:yaml.org,2002:omap'
+ map = register(o, class_loader.psych_omap.new)
+ o.children.each_slice(2) do |l,r|
+ map[accept(l)] = accept r
+ end
+ map
- when /^!map:(.*)$/, /^!ruby\/hash:(.*)$/
- revive_hash resolve_class($1).new, o
+ when /^!ruby\/marshalable:(.*)$/
+ name = $1
+ klass = resolve_class(name)
+ obj = register(o, klass.allocate)
+
+ if obj.respond_to?(:init_with)
+ init_with(obj, revive_hash({}, o), o)
+ elsif obj.respond_to?(:marshal_load)
+ marshal_data = o.children.map(&method(:accept))
+ obj.marshal_load(marshal_data)
+ obj
+ else
+ raise ArgumentError, "Cannot deserialize #{name}"
+ end
else
- revive_hash({}, o)
+ revive_hash(register(o, {}), o)
end
end
@@ -245,36 +323,52 @@ module Psych
object
end
- def revive_hash hash, o
- @st[o.anchor] = hash if o.anchor
+ def register_empty object
+ list = register(object, [])
+ object.children.each { |c| list.push accept c }
+ list
+ end
- o.children.each_slice(2) { |k,v|
+ def revive_hash hash, o
+ o.children.each_slice(2) { |k,v|
key = accept(k)
+ val = accept(v)
- if key == '<<'
+ if key == '<<' && k.tag != "tag:yaml.org,2002:str"
case v
- when Nodes::Alias
- hash.merge! accept(v)
+ when Nodes::Alias, Nodes::Mapping
+ begin
+ hash.merge! val
+ rescue TypeError
+ hash[key] = val
+ end
when Nodes::Sequence
- accept(v).reverse_each do |value|
- hash.merge! value
+ begin
+ h = {}
+ val.reverse_each do |value|
+ h.merge! value
+ end
+ hash.merge! h
+ rescue TypeError
+ hash[key] = val
end
else
- hash[key] = accept(v)
+ hash[key] = val
end
else
- hash[key] = accept(v)
+ hash[key] = val
end
}
hash
end
+ def merge_key hash, key, val
+ end
+
def revive klass, node
- s = klass.allocate
- @st[node.anchor] = s if node.anchor
- h = Hash[*node.children.map { |c| accept c }]
- init_with(s, h, node)
+ s = register(node, klass.allocate)
+ init_with(s, revive_hash({}, node), node)
end
def init_with o, h, node
@@ -296,21 +390,13 @@ module Psych
# Convert +klassname+ to a Class
def resolve_class klassname
- return nil unless klassname and not klassname.empty?
-
- name = klassname
- retried = false
-
- begin
- path2class(name)
- rescue ArgumentError, NameError => ex
- unless retried
- name = "Struct::#{name}"
- retried = ex
- retry
- end
- raise retried
- end
+ class_loader.load klassname
+ end
+ end
+
+ class NoAliasRuby < ToRuby
+ def visit_Psych_Nodes_Alias o
+ raise BadAlias, "Unknown alias: #{o.anchor}"
end
end
end