class Psych::Visitors::ToRuby

This class walks a YAML AST, converting each node to ruby

Public Class Methods

new(ss = ScalarScanner.new) click to toggle source
Calls superclass method BasicObject.new
# File ext/psych/lib/psych/visitors/to_ruby.rb, line 12
def initialize ss = ScalarScanner.new
  super()
  @st = {}
  @ss = ss
  @domain_types = Psych.domain_types
end

Public Instance Methods

accept(target) click to toggle source
Calls superclass method Psych::Visitors::Visitor#accept
# File ext/psych/lib/psych/visitors/to_ruby.rb, line 19
def accept target
  result = super
  return result if @domain_types.empty? || !target.tag

  key = target.tag.sub(/^[!\/]*/, '').sub(/(,\d+)\//, '\1:')
  key = "tag:#{key}" unless key =~ /^(tag:|x-private)/

  if @domain_types.key? key
    value, block = @domain_types[key]
    return block.call value, result
  end

  result
end
visit_Psych_Nodes_Alias(o) click to toggle source
# File ext/psych/lib/psych/visitors/to_ruby.rb, line 247
def visit_Psych_Nodes_Alias o
  @st.fetch(o.anchor) { raise BadAlias, "Unknown alias: #{o.anchor}" }
end
visit_Psych_Nodes_Document(o) click to toggle source
# File ext/psych/lib/psych/visitors/to_ruby.rb, line 239
def visit_Psych_Nodes_Document o
  accept o.root
end
visit_Psych_Nodes_Mapping(o) click to toggle source
# File ext/psych/lib/psych/visitors/to_ruby.rb, line 139
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

  case o.tag
  when /^!ruby\/struct:?(.*)?$/
    klass = resolve_class($1)

    if klass
      s = register(o, klass.allocate)

      members = {}
      struct_members = s.members.map { |x| x.to_sym }
      o.children.each_slice(2) do |k,v|
        member = accept(k)
        value  = accept(v)
        if struct_members.include?(member.to_sym)
          s.send("#{member}=", value)
        else
          members[member.to_s.sub(/^@/, '')] = value
        end
      end
      init_with(s, members, o)
    else
      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 })
    end

  when /^!ruby\/object:?(.*)?$/
    name = $1 || 'Object'

    if name == 'Complex'
      h = Hash[*o.children.map { |c| accept c }]
      register o, Complex(h['real'], h['image'])
    elsif name == 'Rational'
      h = Hash[*o.children.map { |c| accept c }]
      register o, Rational(h['numerator'], h['denominator'])
    else
      obj = revive((resolve_class(name) || Object), o)
      obj
    end

  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.replace string
      register(o, 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/range'
    h = Hash[*o.children.map { |c| accept c }]
    register o, Range.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),
                        h.delete('message'))
    init_with(e, h, o)

  when '!set', 'tag:yaml.org,2002:set'
    set = 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 /^!map:(.*)$/, /^!ruby\/hash:(.*)$/
    revive_hash resolve_class($1).new, o

  when '!omap', 'tag:yaml.org,2002:omap'
    map = register(o, Psych::Omap.new)
    o.children.each_slice(2) do |l,r|
      map[accept(l)] = accept r
    end
    map

  else
    revive_hash({}, o)
  end
end
visit_Psych_Nodes_Scalar(o) click to toggle source
# File ext/psych/lib/psych/visitors/to_ruby.rb, line 103
def visit_Psych_Nodes_Scalar o
  register o, deserialize(o)
end
visit_Psych_Nodes_Sequence(o) click to toggle source
# File ext/psych/lib/psych/visitors/to_ruby.rb, line 107
def visit_Psych_Nodes_Sequence o
  if klass = Psych.load_tags[o.tag]
    instance = klass.allocate

    if instance.respond_to?(:init_with)
      coder = Psych::Coder.new(o.tag)
      coder.seq = o.children.map { |c| accept c }
      instance.init_with coder
    end

    return instance
  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|
      map[accept(a.children.first)] = accept a.children.last
    }
    map
  when /^!(?:seq|ruby\/array):(.*)$/
    klass = resolve_class($1)
    list  = register(o, klass.allocate)
    o.children.each { |c| list.push accept c }
    list
  else
    register_empty(o)
  end
end
visit_Psych_Nodes_Stream(o) click to toggle source
# File ext/psych/lib/psych/visitors/to_ruby.rb, line 243
def visit_Psych_Nodes_Stream o
  o.children.map { |c| accept c }
end

Private Instance Methods

deserialize(o) click to toggle source
# File ext/psych/lib/psych/visitors/to_ruby.rb, line 34
def deserialize o
  if klass = Psych.load_tags[o.tag]
    instance = klass.allocate

    if instance.respond_to?(:init_with)
      coder = Psych::Coder.new(o.tag)
      coder.scalar = o.value
      instance.init_with coder
    end

    return instance
  end

  return o.value if o.quoted
  return @ss.tokenize(o.value) unless o.tag

  case o.tag
  when '!binary', 'tag:yaml.org,2002:binary'
    o.value.unpack('m').first
  when /^!(?:str|ruby\/string)(?::(.*))?/, 'tag:yaml.org,2002:str'
    klass = resolve_class($1)
    if klass
      klass.allocate.replace o.value
    else
      o.value
    end
  when '!ruby/object:BigDecimal'
    require 'bigdecimal'
    BigDecimal._load o.value
  when "!ruby/object:DateTime"
    require 'date'
    @ss.parse_time(o.value).to_datetime
  when "!ruby/object:Complex"
    Complex(o.value)
  when "!ruby/object: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"
    o.value =~ /^\/(.*)\/([mixn]*)$/
    source  = $1
    options = 0
    lang    = nil
    ($2 || '').split('').each do |option|
      case option
      when 'x' then options |= Regexp::EXTENDED
      when 'i' then options |= Regexp::IGNORECASE
      when 'm' then options |= Regexp::MULTILINE
      when 'n' then options |= Regexp::NOENCODING
      else lang = option
      end
    end
    Regexp.new(*[source, options, lang].compact)
  when "!ruby/range"
    args = o.value.split(/([.]{2,3})/, 2).map { |s|
      accept Nodes::Scalar.new(s)
    }
    args.push(args.delete_at(1) == '...')
    Range.new(*args)
  when /^!ruby\/sym(bol)?:?(.*)?$/
    o.value.to_sym
  else
    @ss.tokenize o.value
  end
end
init_with(o, h, node) click to toggle source
# File ext/psych/lib/psych/visitors/to_ruby.rb, line 309
def init_with o, h, node
  c = Psych::Coder.new(node.tag)
  c.map = h

  if o.respond_to?(:init_with)
    o.init_with c
  elsif o.respond_to?(:yaml_initialize)
    if $VERBOSE
      warn "Implementing #{o.class}#yaml_initialize is deprecated, please implement \"init_with(coder)\""
    end
    o.yaml_initialize c.tag, c.map
  else
    h.each { |k,v| o.instance_variable_set(:"@#{k}", v) }
  end
  o
end
merge_key(hash, key, val) click to toggle source
# File ext/psych/lib/psych/visitors/to_ruby.rb, line 299
def merge_key hash, key, val
end
register(node, object) click to toggle source
# File ext/psych/lib/psych/visitors/to_ruby.rb, line 252
def register node, object
  @st[node.anchor] = object if node.anchor
  object
end
register_empty(object) click to toggle source
# File ext/psych/lib/psych/visitors/to_ruby.rb, line 257
def register_empty object
  list = register(object, [])
  object.children.each { |c| list.push accept c }
  list
end
resolve_class(klassname) click to toggle source

Convert klassname to a Class

# File ext/psych/lib/psych/visitors/to_ruby.rb, line 327
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
end
revive(klass, node) click to toggle source
# File ext/psych/lib/psych/visitors/to_ruby.rb, line 302
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)
end
revive_hash(hash, o) click to toggle source
# File ext/psych/lib/psych/visitors/to_ruby.rb, line 263
def revive_hash hash, o
  @st[o.anchor] = hash if o.anchor

  o.children.each_slice(2) { |k,v|
    key = accept(k)
    val = accept(v)

    if key == '<<'
      case v
      when Nodes::Alias
        begin
          hash.merge! val
        rescue TypeError
          hash[key] = val
        end
      when Nodes::Sequence
        begin
          h = {}
          val.reverse_each do |value|
            h.merge! value
          end
          hash.merge! h
        rescue TypeError
          hash[key] = val
        end
      else
        hash[key] = val
      end
    else
      hash[key] = val
    end

  }
  hash
end