class Psych::Visitors::YAMLTree

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

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

Attributes

finished [R]
finished? [R]
started [R]
started? [R]

Public Class Methods

create (options = {})
# File ext/psych/lib/psych/visitors/yaml_tree.rb, line 44
def self.create options = {}, emitter = nil
  emitter      ||= TreeBuilder.new
  class_loader = ClassLoader.new
  ss           = ScalarScanner.new class_loader
  new(emitter, ss, options)
end
new (emitter, ss, options)
Calls superclass method BasicObject::new
# File ext/psych/lib/psych/visitors/yaml_tree.rb, line 51
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
  @stringify_names = options[:stringify_names]
  @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.compare_by_identity
end

Public Instance Methods

<< (object)
Alias for: push
accept (target)
# File ext/psych/lib/psych/visitors/yaml_tree.rb, line 119
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?(:encode_with)
    dump_coder target
  else
    send(@dispatch_cache[target.class], target)
  end
end
finish ()
# File ext/psych/lib/psych/visitors/yaml_tree.rb, line 88
def finish
  @emitter.end_stream.tap do
    @finished = true
  end
end
push (object)
# File ext/psych/lib/psych/visitors/yaml_tree.rb, line 99
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
Also aliased as: <<
start (encoding = Nodes::Stream::UTF8)
# File ext/psych/lib/psych/visitors/yaml_tree.rb, line 82
def start encoding = Nodes::Stream::UTF8
  @emitter.start_stream(encoding).tap do
    @started = true
  end
end
tree ()
# File ext/psych/lib/psych/visitors/yaml_tree.rb, line 94
def tree
  finish unless finished?
  @emitter.root
end
visit_Array (o)
# File ext/psych/lib/psych/visitors/yaml_tree.rb, line 347
def visit_Array o
  if o.class == ::Array
    visit_Enumerator o
  else
    visit_array_subclass o
  end
end
visit_BasicObject (o)
# File ext/psych/lib/psych/visitors/yaml_tree.rb, line 373
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)
# File ext/psych/lib/psych/visitors/yaml_tree.rb, line 247
def visit_BigDecimal o
  @emitter.scalar o._dump, nil, '!ruby/object:BigDecimal', false, false, Nodes::Scalar::ANY
end
visit_Class (o)
# File ext/psych/lib/psych/visitors/yaml_tree.rb, line 310
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)
# File ext/psych/lib/psych/visitors/yaml_tree.rb, line 220
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)
# File ext/psych/lib/psych/visitors/yaml_tree.rb, line 191
def visit_Date o
  register o, visit_Integer(o.gregorian)
end
visit_DateTime (o)
# File ext/psych/lib/psych/visitors/yaml_tree.rb, line 195
def visit_DateTime o
  t = o.italy
  formatted = format_time t, t.offset.zero?
  tag = '!ruby/object:DateTime'
  register o, @emitter.scalar(formatted, nil, tag, false, false, Nodes::Scalar::ANY)
end
visit_Delegator (o)
Alias for: visit_Object
visit_Encoding (o)
# File ext/psych/lib/psych/visitors/yaml_tree.rb, line 144
def visit_Encoding o
  tag = "!ruby/encoding"
  @emitter.scalar o.name, nil, tag, false, false, Nodes::Scalar::ANY
end
visit_Enumerator (o)
# File ext/psych/lib/psych/visitors/yaml_tree.rb, line 355
def visit_Enumerator o
  register o, @emitter.start_sequence(nil, nil, true, Nodes::Sequence::BLOCK)
  o.each { |c| accept c }
  @emitter.end_sequence
end
visit_Exception (o)
# File ext/psych/lib/psych/visitors/yaml_tree.rb, line 179
def visit_Exception o
  dump_exception o, o.message.to_s
end
visit_FalseClass (o)
Alias for: visit_Integer
visit_Float (o)
# File ext/psych/lib/psych/visitors/yaml_tree.rb, line 236
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)
# File ext/psych/lib/psych/visitors/yaml_tree.rb, line 323
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(@stringify_names && Symbol === k ? k.to_s : k)
      accept v
    end
    @emitter.end_mapping
  else
    visit_hash_subclass o
  end
end
visit_Integer (o)
# File ext/psych/lib/psych/visitors/yaml_tree.rb, line 230
def visit_Integer o
  @emitter.scalar o.to_s, nil, nil, true, false, Nodes::Scalar::ANY
end
Also aliased as: visit_TrueClass, visit_FalseClass
visit_Module (o)
# File ext/psych/lib/psych/visitors/yaml_tree.rb, line 305
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)
# File ext/psych/lib/psych/visitors/yaml_tree.rb, line 183
def visit_NameError o
  dump_exception o, o.message.to_s
end
visit_NilClass (o)
# File ext/psych/lib/psych/visitors/yaml_tree.rb, line 361
def visit_NilClass o
  @emitter.scalar('', nil, 'tag:yaml.org,2002:null', true, false, Nodes::Scalar::ANY)
end
visit_Object (o)
# File ext/psych/lib/psych/visitors/yaml_tree.rb, line 149
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
Also aliased as: visit_Delegator
visit_Psych_Omap (o)
# File ext/psych/lib/psych/visitors/yaml_tree.rb, line 136
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)
# File ext/psych/lib/psych/visitors/yaml_tree.rb, line 336
def visit_Psych_Set o
  register(o, @emitter.start_mapping(nil, '!set', false, Psych::Nodes::Mapping::BLOCK))

  o.each do |k,v|
    accept(@stringify_names && Symbol === k ? k.to_s : k)
    accept v
  end

  @emitter.end_mapping
end
visit_Range (o)
# File ext/psych/lib/psych/visitors/yaml_tree.rb, line 315
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)
# File ext/psych/lib/psych/visitors/yaml_tree.rb, line 207
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)
# File ext/psych/lib/psych/visitors/yaml_tree.rb, line 187
def visit_Regexp o
  register o, @emitter.scalar(o.inspect, nil, '!ruby/regexp', false, false, Nodes::Scalar::ANY)
end
visit_String (o)
# File ext/psych/lib/psych/visitors/yaml_tree.rb, line 251
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.match?(/\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 o == 'y' || o == 'Y' || o == 'n' || o == 'N'
    style = Nodes::Scalar::DOUBLE_QUOTED
  elsif @line_width && o.length > @line_width
    style = Nodes::Scalar::FOLDED
  elsif o.match?(/^[^[:word:]][^"]*$/)
    style = Nodes::Scalar::DOUBLE_QUOTED
  elsif not String === @ss.tokenize(o) or /\A0[0-7]*[89]/.match?(o)
    style = Nodes::Scalar::SINGLE_QUOTED
  end

  is_primitive = o.class == ::String
  ivars = is_primitive ? [] : o.instance_variables

  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'.dup
    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)
# File ext/psych/lib/psych/visitors/yaml_tree.rb, line 165
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)
# File ext/psych/lib/psych/visitors/yaml_tree.rb, line 365
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)
# File ext/psych/lib/psych/visitors/yaml_tree.rb, line 202
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