# class Psych::Visitors::ToRuby

This class walks a `YAML` AST, converting each node to Ruby

### Attributes

class_loader[R]

### Public Class Methods

create(symbolize_names: false, freeze: false, strict_integer: false) click to toggle source

    # File ext/psych/lib/psych/visitors/to_ruby.rb, line 15
    def self.create(symbolize_names: false, freeze: false, strict_integer: false)
      class_loader = ClassLoader.new
      scanner      = ScalarScanner.new class_loader, strict_integer: strict_integer
      new(scanner, class_loader, symbolize_names: symbolize_names, freeze: freeze)
    end

new(ss, class_loader, symbolize_names: false, freeze: false) click to toggle source
Calls superclass method `BasicObject::new`

    # File ext/psych/lib/psych/visitors/to_ruby.rb, line 23
    def initialize ss, class_loader, symbolize_names: false, freeze: false
      super()
      @st = {}
      @ss = ss
      @load_tags = Psych.load_tags
      @domain_types = Psych.domain_types
      @class_loader = class_loader
      @symbolize_names = symbolize_names
      @freeze = freeze
    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 34
    def accept target
      result = super

      unless @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]
          result = block.call value, result
        end
      end

      result = deduplicate(result).freeze if @freeze
      result
    end

visit_Psych_Nodes_Alias(o) click to toggle source

    # File ext/psych/lib/psych/visitors/to_ruby.rb, line 327
    def visit_Psych_Nodes_Alias o
      @st.fetch(o.anchor) { raise AnchorNotDefined, o.anchor }
    end

visit_Psych_Nodes_Document(o) click to toggle source

    # File ext/psych/lib/psych/visitors/to_ruby.rb, line 319
    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 165
    def visit_Psych_Nodes_Mapping o
      if @load_tags[o.tag]
        return revive(resolve_class(@load_tags[o.tag]), o)
      end
      return revive_hash(register(o, {}), o) unless o.tag

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

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

          members = {}
          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?(class_loader.symbolize(member))
              s.send("#{member}=", value)
            else
              members[member.to_s.sub(/^@/, '')] = value
            end
          end
          init_with(s, members, o)
        else
          klass = class_loader.struct
          members = o.children.map { |c| accept c }
          h = Hash[*members]
          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, 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) || class_loader.exception),
                            h.delete('message'))

        e.set_backtrace h.delete('backtrace') if h.key? 'backtrace'
        init_with(e, h, o)

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

      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 /^!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(register(o, {}), o)
      end
    end

visit_Psych_Nodes_Scalar(o) click to toggle source

    # File ext/psych/lib/psych/visitors/to_ruby.rb, line 129
    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 133
    def visit_Psych_Nodes_Sequence o
      if klass = resolve_class(@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 323
    def visit_Psych_Nodes_Stream o
      o.children.map { |c| accept c }
    end

### Private Instance Methods

build_exception(klass, message) click to toggle source

Create an exception with class `klass` and `message`

    static VALUE build_exception(VALUE self, VALUE klass, VALUE mesg)
    {
        VALUE e = rb_obj_alloc(klass);

        rb_iv_set(e, "mesg", mesg);

        return e;
    }

deduplicate(key) click to toggle source

    # File ext/psych/lib/psych/visitors/to_ruby.rb, line 385
    def deduplicate key
      if key.is_a?(String)
        # It is important to untaint the string, otherwise it won't
        # be deduplicated into an fstring, but simply frozen.
        -(key.untaint)
      else
        key
      end
    end

deserialize(o) click to toggle source

    # File ext/psych/lib/psych/visitors/to_ruby.rb, line 51
    def deserialize o
      if klass = resolve_class(@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' unless defined? BigDecimal
        class_loader.big_decimal._load o.value
      when "!ruby/object:DateTime"
        class_loader.date_time
        require 'date' unless defined? DateTime
        t = @ss.parse_time(o.value)
        DateTime.civil(*t.to_a[0, 6].reverse, Rational(t.utc_offset, 86400)) +
          (t.subsec/86400)
      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]*)$/m
        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
        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) == '...')
        klass.new(*args)
      when /^!ruby\/sym(bol)?:?(.*)?$/
        class_loader.symbolize o.value
      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 412
    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
      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 404
    def merge_key hash, key, val
    end

register(node, object) click to toggle source

    # File ext/psych/lib/psych/visitors/to_ruby.rb, line 333
    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 338
    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 425
    def resolve_class klassname
      class_loader.load klassname
    end

revive(klass, node) click to toggle source

    # File ext/psych/lib/psych/visitors/to_ruby.rb, line 407
    def revive klass, node
      s = register(node, klass.allocate)
      init_with(s, revive_hash({}, node, true), node)
    end

revive_hash(hash, o, tagged= false) click to toggle source

    # File ext/psych/lib/psych/visitors/to_ruby.rb, line 344
    def revive_hash hash, o, tagged= false
      o.children.each_slice(2) { |k,v|
        key = accept(k)
        val = accept(v)

        if key == '<<' && k.tag != "tag:yaml.org,2002:str"
          case v
          when Nodes::Alias, Nodes::Mapping
            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
          if !tagged && @symbolize_names && key.is_a?(String)
            key = key.to_sym
          elsif !@freeze
            key = deduplicate(key)
          end

          hash[key] = val
        end

      }
      hash
    end