123456789_123456789_123456789_123456789_123456789_

Class: Psych::Visitors::YAMLTree

Relationships & Source Files
Extension / Inclusion / Inheritance Descendants
Subclasses:
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
self, Visitor
Instance Chain:
self, Visitor
Inherits: Psych::Visitors::Visitor
Defined in: ext/psych/lib/psych/visitors/yaml_tree.rb,
ext/psych/psych_yaml_tree.c

Overview

YAMLTree builds a YAML ast given a Ruby object. For example:

builder = Psych::Visitors::YAMLTree.new
builder << { :foo => 'bar' }
builder.tree # => #<Psych::Nodes::Stream .. }

Constant Summary

Visitor - Inherited

DISPATCH

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Constructor Details

.new(emitter = nil, ss = nil, options = nil) ⇒ YAMLTree

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 56

def self.new emitter = nil, ss = nil, options = nil
  return super if emitter && ss && options

  if $VERBOSE
    warn "This API is deprecated, please pass an emitter, scalar scanner, and options or call #{self}.create() (#{caller.first})"
  end
  create emitter, ss
end

#initialize(emitter, ss, options) ⇒ YAMLTree

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 65

def initialize emitter, ss, options
  super()
  @started    = false
  @finished   = false
  @emitter    = emitter
  @st         = Registrar.new
  @ss         = ss
  @options    = options
  @line_width = options[:line_width]
  if @line_width && @line_width < 0
    if @line_width == -1
      # Treat -1 as unlimited line-width, same as libyaml does.
      @line_width = nil
    else
      fail(ArgumentError, "Invalid line_width #{@line_width}, must be non-negative or -1 for unlimited.")
    end
  end
  @coders     = []

  @dispatch_cache = Hash.new do |h,klass|
    method = "visit_#{(klass.name || '').split('::').join('_')}"

    method = respond_to?(method) ? method : h[klass.superclass]

    raise(TypeError, "Can't dump #{target.class}") unless method

    h[klass] = method
  end
end

Class Method Details

.create(options = {}, emitter = nil)

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 49

def self.create options = {}, emitter = nil
  emitter      ||= TreeBuilder.new
  class_loader = ClassLoader.new
  ss           = ScalarScanner.new class_loader
  new(emitter, ss, options)
end

Instance Attribute Details

#finished? (readonly)

Alias for #finished.

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 46

alias :finished? :finished

#started? (readonly)

Alias for #started.

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 47

alias :started? :started

Instance Method Details

#<<(object)

Alias for #push.

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 130

alias :<< :push

#accept(target)

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 132

def accept target
  # return any aliases we find
  if @st.key? target
    oid         = @st.id_for target
    node        = @st.node_for target
    anchor      = oid.to_s
    node.anchor = anchor
    return @emitter.alias anchor
  end

  if target.respond_to?(:to_yaml)
    begin
      loc = target.method(:to_yaml).source_location.first
      if loc !~ /(syck\/rubytypes.rb|psych\/core_ext.rb)/
        unless target.respond_to?(:encode_with)
          if $VERBOSE
            warn "implementing to_yaml is deprecated, please implement \"encode_with\""
          end

          target.to_yaml(:nodump => true)
        end
      end
    rescue
      # public_method or source_location might be overridden,
      # and it's OK to skip it since it's only to emit a warning
    end
  end

  if target.respond_to?(:encode_with)
    dump_coder target
  else
    send(@dispatch_cache[target.class], target)
  end
end

#binary?(string) ⇒ Boolean (private)

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 444

def binary? string
  (string.encoding == Encoding::ASCII_8BIT && !string.ascii_only?) ||
    string.index(NULL) ||
    string.count(BINARY_RANGE, WS_RANGE).fdiv(string.length) > 0.3
end

#dump_coder(o) (private)

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 553

def dump_coder o
  @coders << o
  tag = Psych.dump_tags[o.class]
  unless tag
    klass = o.class == Object ? nil : o.class.name
    tag   = ['!ruby/object', klass].compact.join(':')
  end

  c = Psych::Coder.new(tag)
  o.encode_with(c)
  emit_coder c, o
end

#dump_ivars(target) (private)

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 588

def dump_ivars target
  ivars = find_ivars target

  ivars.each do |iv|
    @emitter.scalar("#{iv.to_s.sub(/^@/, '')}", nil, nil, true, false, Nodes::Scalar::ANY)
    accept target.instance_variable_get(iv)
  end
end

#dump_list(o) (private)

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 519

def dump_list o
end

#emit_coder(c, o) (private)

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 566

def emit_coder c, o
  case c.type
  when :scalar
    @emitter.scalar c.scalar, nil, c.tag, c.tag.nil?, false, Nodes::Scalar::ANY
  when :seq
    @emitter.start_sequence nil, c.tag, c.tag.nil?, Nodes::Sequence::BLOCK
    c.seq.each do |thing|
      accept thing
    end
    @emitter.end_sequence
  when :map
    register o, @emitter.start_mapping(nil, c.tag, c.implicit, c.style)
    c.map.each do |k,v|
      accept k
      accept v
    end
    @emitter.end_mapping
  when :object
    accept c.object
  end
end

#find_ivars(target, is_primitive = false) (private)

FIXME: remove this method once “to_yaml_properties” is removed

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 531

def find_ivars target, is_primitive=false
  begin
    loc = target.method(:to_yaml_properties).source_location.first
    unless loc.start_with?(Psych::DEPRECATED) || loc.end_with?('rubytypes.rb')
      if $VERBOSE
        warn "#{loc}: to_yaml_properties is deprecated, please implement \"encode_with(coder)\""
      end
      return target.to_yaml_properties
    end
  rescue
    # public_method or source_location might be overridden,
    # and it's OK to skip it since it's only to emit a warning.
  end

  is_primitive ? [] : target.instance_variables
end

#finish

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 101

def finish
  @emitter.end_stream.tap do
    @finished = true
  end
end

#finished (readonly) Also known as: #finished?

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 45

attr_reader :started, :finished

#format_time(time) (private)

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 522

def format_time time
  if time.utc?
    time.strftime("%Y-%m-%d %H:%M:%S.%9N Z")
  else
    time.strftime("%Y-%m-%d %H:%M:%S.%9N %:z")
  end
end

#private_iv_get(target, prop) (private)

Get the private instance variable prop from target

#push(object) Also known as: #<<

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 112

def push object
  start unless started?
  version = []
  version = [1,1] if @options[:header]

  case @options[:version]
  when Array
    version = @options[:version]
  when String
    version = @options[:version].split('.').map { |x| x.to_i }
  else
    version = [1,1]
  end if @options.key? :version

  @emitter.start_document version, [], false
  accept object
  @emitter.end_document !@emitter.streaming?
end

#register(target, yaml_obj) (private)

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 548

def register target, yaml_obj
  @st.register target, yaml_obj
  yaml_obj
end

#start(encoding = Nodes::Stream::UTF8)

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 95

def start encoding = Nodes::Stream::UTF8
  @emitter.start_stream(encoding).tap do
    @started = true
  end
end

#started (readonly) Also known as: #started?

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 45

attr_reader :started, :finished

#tree

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 107

def tree
  finish unless finished?
  @emitter.root
end

#visit_Array(o)

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 404

def visit_Array o
  if o.class == ::Array
    register o, @emitter.start_sequence(nil, nil, true, Nodes::Sequence::BLOCK)
    o.each { |c| accept c }
    @emitter.end_sequence
  else
    visit_array_subclass o
  end
end

#visit_array_subclass(o) (private)

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 450

def visit_array_subclass o
  tag = "!ruby/array:#{o.class}"
  ivars = o.instance_variables
  if ivars.empty?
    node = @emitter.start_sequence(nil, tag, false, Nodes::Sequence::BLOCK)
    register o, node
    o.each { |c| accept c }
    @emitter.end_sequence
  else
    node = @emitter.start_mapping(nil, tag, false, Nodes::Sequence::BLOCK)
    register o, node

    # Dump the internal list
    accept 'internal'
    @emitter.start_sequence(nil, nil, true, Nodes::Sequence::BLOCK)
    o.each { |c| accept c }
    @emitter.end_sequence

    # Dump the ivars
    accept 'ivars'
    @emitter.start_mapping(nil, nil, true, Nodes::Sequence::BLOCK)
    ivars.each do |ivar|
      accept ivar
      accept o.instance_variable_get ivar
    end
    @emitter.end_mapping

    @emitter.end_mapping
  end
end

#visit_BasicObject(o)

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 426

def visit_BasicObject o
  tag = Psych.dump_tags[o.class]
  tag ||= "!ruby/marshalable:#{o.class.name}"

  map = @emitter.start_mapping(nil, tag, false, Nodes::Mapping::BLOCK)
  register(o, map)

  o.marshal_dump.each(&method(:accept))

  @emitter.end_mapping
end

#visit_BigDecimal(o)

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 306

def visit_BigDecimal o
  @emitter.scalar o._dump, nil, '!ruby/object:BigDecimal', false, false, Nodes::Scalar::ANY
end

#visit_Class(o)

Raises:

  • (TypeError)
[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 367

def visit_Class o
  raise TypeError, "can't dump anonymous class: #{o}" unless o.name
  register o, @emitter.scalar(o.name, nil, '!ruby/class', false, false, Nodes::Scalar::SINGLE_QUOTED)
end

#visit_Complex(o)

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 278

def visit_Complex o
  register o, @emitter.start_mapping(nil, '!ruby/object:Complex', false, Nodes::Mapping::BLOCK)

  ['real', o.real.to_s, 'image', o.imag.to_s].each do |m|
    @emitter.scalar m, nil, nil, true, false, Nodes::Scalar::ANY
  end

  @emitter.end_mapping
end

#visit_Date(o)

Alias for #visit_Integer.

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 293

alias :visit_Date :visit_Integer

#visit_DateTime(o)

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 250

def visit_DateTime o
  formatted = if o.offset.zero?
                o.strftime("%Y-%m-%d %H:%M:%S.%9N Z".freeze)
              else
                o.strftime("%Y-%m-%d %H:%M:%S.%9N %:z".freeze)
              end
  tag = '!ruby/object:DateTime'
  register o, @emitter.scalar(formatted, nil, tag, false, false, Nodes::Scalar::ANY)
end

#visit_Encoding(o)

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 175

def visit_Encoding o
  tag = "!ruby/encoding"
  @emitter.scalar o.name, nil, tag, false, false, Nodes::Scalar::ANY
end

#visit_Exception(o)

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 208

def visit_Exception o
  tag = ['!ruby/exception', o.class.name].join ':'

  @emitter.start_mapping nil, tag, false, Nodes::Mapping::BLOCK

  {
    'message'   => private_iv_get(o, 'mesg'),
    'backtrace' => private_iv_get(o, 'backtrace'),
  }.each do |k,v|
    next unless v
    @emitter.scalar k, nil, nil, true, false, Nodes::Scalar::ANY
    accept v
  end

  dump_ivars o

  @emitter.end_mapping
end

#visit_FalseClass(o)

Alias for #visit_Integer.

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 292

alias :visit_FalseClass :visit_Integer

#visit_Float(o)

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 295

def visit_Float o
  if o.nan?
    @emitter.scalar '.nan', nil, nil, true, false, Nodes::Scalar::ANY
  elsif o.infinite?
    @emitter.scalar((o.infinite? > 0 ? '.inf' : '-.inf'),
      nil, nil, true, false, Nodes::Scalar::ANY)
  else
    @emitter.scalar o.to_s, nil, nil, true, false, Nodes::Scalar::ANY
  end
end

#visit_Hash(o)

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 380

def visit_Hash o
  if o.class == ::Hash
    register(o, @emitter.start_mapping(nil, nil, true, Psych::Nodes::Mapping::BLOCK))
    o.each do |k,v|
      accept k
      accept v
    end
    @emitter.end_mapping
  else
    visit_hash_subclass o
  end
end

#visit_hash_subclass(o) (private)

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 481

def visit_hash_subclass o
  ivars = o.instance_variables
  if ivars.any?
    tag = "!ruby/hash-with-ivars:#{o.class}"
    node = @emitter.start_mapping(nil, tag, false, Psych::Nodes::Mapping::BLOCK)
    register(o, node)

    # Dump the elements
    accept 'elements'
    @emitter.start_mapping nil, nil, true, Nodes::Mapping::BLOCK
    o.each do |k,v|
      accept k
      accept v
    end
    @emitter.end_mapping

    # Dump the ivars
    accept 'ivars'
    @emitter.start_mapping nil, nil, true, Nodes::Mapping::BLOCK
    o.instance_variables.each do |ivar|
      accept ivar
      accept o.instance_variable_get ivar
    end
    @emitter.end_mapping

    @emitter.end_mapping
  else
    tag = "!ruby/hash:#{o.class}"
    node = @emitter.start_mapping(nil, tag, false, Psych::Nodes::Mapping::BLOCK)
    register(o, node)
    o.each do |k,v|
      accept k
      accept v
    end
    @emitter.end_mapping
  end
end

#visit_Integer(o) Also known as: #visit_TrueClass, #visit_FalseClass, #visit_Date

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 288

def visit_Integer o
  @emitter.scalar o.to_s, nil, nil, true, false, Nodes::Scalar::ANY
end

#visit_Module(o)

Raises:

  • (TypeError)
[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 362

def visit_Module o
  raise TypeError, "can't dump anonymous module: #{o}" unless o.name
  register o, @emitter.scalar(o.name, nil, '!ruby/module', false, false, Nodes::Scalar::SINGLE_QUOTED)
end

#visit_NameError(o)

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 227

def visit_NameError o
  tag = ['!ruby/exception', o.class.name].join ':'

  @emitter.start_mapping nil, tag, false, Nodes::Mapping::BLOCK

  {
    'message'   => o.message.to_s,
    'backtrace' => private_iv_get(o, 'backtrace'),
  }.each do |k,v|
    next unless v
    @emitter.scalar k, nil, nil, true, false, Nodes::Scalar::ANY
    accept v
  end

  dump_ivars o

  @emitter.end_mapping
end

#visit_NilClass(o)

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 414

def visit_NilClass o
  @emitter.scalar('', nil, 'tag:yaml.org,2002:null', true, false, Nodes::Scalar::ANY)
end

#visit_Object(o)

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 180

def visit_Object o
  tag = Psych.dump_tags[o.class]
  unless tag
    klass = o.class == Object ? nil : o.class.name
    tag   = ['!ruby/object', klass].compact.join(':')
  end

  map = @emitter.start_mapping(nil, tag, false, Nodes::Mapping::BLOCK)
  register(o, map)

  dump_ivars o
  @emitter.end_mapping
end

#visit_Psych_Omap(o)

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 167

def visit_Psych_Omap o
  seq = @emitter.start_sequence(nil, 'tag:yaml.org,2002:omap', false, Nodes::Sequence::BLOCK)
  register(o, seq)

  o.each { |k,v| visit_Hash k => v }
  @emitter.end_sequence
end

#visit_Psych_Set(o)

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 393

def visit_Psych_Set o
  register(o, @emitter.start_mapping(nil, '!set', false, Psych::Nodes::Mapping::BLOCK))

  o.each do |k,v|
    accept k
    accept v
  end

  @emitter.end_mapping
end

#visit_Range(o)

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 372

def visit_Range o
  register o, @emitter.start_mapping(nil, '!ruby/range', false, Nodes::Mapping::BLOCK)
  ['begin', o.begin, 'end', o.end, 'excl', o.exclude_end?].each do |m|
    accept m
  end
  @emitter.end_mapping
end

#visit_Rational(o)

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 265

def visit_Rational o
  register o, @emitter.start_mapping(nil, '!ruby/object:Rational', false, Nodes::Mapping::BLOCK)

  [
    'denominator', o.denominator.to_s,
    'numerator', o.numerator.to_s
  ].each do |m|
    @emitter.scalar m, nil, nil, true, false, Nodes::Scalar::ANY
  end

  @emitter.end_mapping
end

#visit_Regexp(o)

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 246

def visit_Regexp o
  register o, @emitter.scalar(o.inspect, nil, '!ruby/regexp', false, false, Nodes::Scalar::ANY)
end

#visit_String(o)

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 310

def visit_String o
  plain = true
  quote = true
  style = Nodes::Scalar::PLAIN
  tag   = nil

  if binary?(o)
    o     = [o].pack('m0')
    tag   = '!binary' # FIXME: change to below when syck is removed
    #tag   = 'tag:yaml.org,2002:binary'
    style = Nodes::Scalar::LITERAL
    plain = false
    quote = false
  elsif o =~ /\n(?!\Z)/  # match \n except blank line at the end of string
    style = Nodes::Scalar::LITERAL
  elsif o == '<<'
    style = Nodes::Scalar::SINGLE_QUOTED
    tag   = 'tag:yaml.org,2002:str'
    plain = false
    quote = false
  elsif @line_width && o.length > @line_width
    style = Nodes::Scalar::FOLDED
  elsif o =~ /^[^[:word:]][^"]*$/
    style = Nodes::Scalar::DOUBLE_QUOTED
  elsif not String === @ss.tokenize(o) or /\A0[0-7]*[89]/ =~ o
    style = Nodes::Scalar::SINGLE_QUOTED
  end

  is_primitive = o.class == ::String
  ivars = find_ivars o, is_primitive

  if ivars.empty?
    unless is_primitive
      tag = "!ruby/string:#{o.class}"
      plain = false
      quote = false
    end
    @emitter.scalar o, nil, tag, plain, quote, style
  else
    maptag = '!ruby/string'
    maptag << ":#{o.class}" unless o.class == ::String

    register o, @emitter.start_mapping(nil, maptag, false, Nodes::Mapping::BLOCK)
    @emitter.scalar 'str', nil, nil, true, false, Nodes::Scalar::ANY
    @emitter.scalar o, nil, tag, plain, quote, style

    dump_ivars o

    @emitter.end_mapping
  end
end

#visit_Struct(o)

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 194

def visit_Struct o
  tag = ['!ruby/struct', o.class.name].compact.join(':')

  register o, @emitter.start_mapping(nil, tag, false, Nodes::Mapping::BLOCK)
  o.members.each do |member|
    @emitter.scalar member.to_s, nil, nil, true, false, Nodes::Scalar::ANY
    accept o[member]
  end

  dump_ivars o

  @emitter.end_mapping
end

#visit_Symbol(o)

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 418

def visit_Symbol o
  if o.empty?
    @emitter.scalar "", nil, '!ruby/symbol', false, false, Nodes::Scalar::ANY
  else
    @emitter.scalar ":#{o}", nil, nil, true, false, Nodes::Scalar::ANY
  end
end

#visit_Time(o)

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 260

def visit_Time o
  formatted = format_time o
  register o, @emitter.scalar(formatted, nil, nil, true, false, Nodes::Scalar::ANY)
end

#visit_TrueClass(o)

Alias for #visit_Integer.

[ GitHub ]

  
# File 'ext/psych/lib/psych/visitors/yaml_tree.rb', line 291

alias :visit_TrueClass :visit_Integer