summaryrefslogtreecommitdiff
path: root/jni/ruby/ext/psych/lib/psych/visitors
diff options
context:
space:
mode:
Diffstat (limited to 'jni/ruby/ext/psych/lib/psych/visitors')
-rw-r--r--jni/ruby/ext/psych/lib/psych/visitors/depth_first.rb26
-rw-r--r--jni/ruby/ext/psych/lib/psych/visitors/emitter.rb51
-rw-r--r--jni/ruby/ext/psych/lib/psych/visitors/json_tree.rb24
-rw-r--r--jni/ruby/ext/psych/lib/psych/visitors/to_ruby.rb389
-rw-r--r--jni/ruby/ext/psych/lib/psych/visitors/visitor.rb19
-rw-r--r--jni/ruby/ext/psych/lib/psych/visitors/yaml_tree.rb565
6 files changed, 1074 insertions, 0 deletions
diff --git a/jni/ruby/ext/psych/lib/psych/visitors/depth_first.rb b/jni/ruby/ext/psych/lib/psych/visitors/depth_first.rb
new file mode 100644
index 0000000..c6eb814
--- /dev/null
+++ b/jni/ruby/ext/psych/lib/psych/visitors/depth_first.rb
@@ -0,0 +1,26 @@
+module Psych
+ module Visitors
+ class DepthFirst < Psych::Visitors::Visitor
+ def initialize block
+ @block = block
+ end
+
+ private
+
+ def nary o
+ o.children.each { |x| visit x }
+ @block.call o
+ end
+ alias :visit_Psych_Nodes_Stream :nary
+ alias :visit_Psych_Nodes_Document :nary
+ alias :visit_Psych_Nodes_Sequence :nary
+ alias :visit_Psych_Nodes_Mapping :nary
+
+ def terminal o
+ @block.call o
+ end
+ alias :visit_Psych_Nodes_Scalar :terminal
+ alias :visit_Psych_Nodes_Alias :terminal
+ end
+ end
+end
diff --git a/jni/ruby/ext/psych/lib/psych/visitors/emitter.rb b/jni/ruby/ext/psych/lib/psych/visitors/emitter.rb
new file mode 100644
index 0000000..c886e50
--- /dev/null
+++ b/jni/ruby/ext/psych/lib/psych/visitors/emitter.rb
@@ -0,0 +1,51 @@
+module Psych
+ module Visitors
+ class Emitter < Psych::Visitors::Visitor
+ def initialize io, options = {}
+ opts = [:indentation, :canonical, :line_width].find_all { |opt|
+ options.key?(opt)
+ }
+
+ if opts.empty?
+ @handler = Psych::Emitter.new io
+ else
+ du = Handler::DumperOptions.new
+ opts.each { |option| du.send :"#{option}=", options[option] }
+ @handler = Psych::Emitter.new io, du
+ end
+ end
+
+ def visit_Psych_Nodes_Stream o
+ @handler.start_stream o.encoding
+ o.children.each { |c| accept c }
+ @handler.end_stream
+ end
+
+ def visit_Psych_Nodes_Document o
+ @handler.start_document o.version, o.tag_directives, o.implicit
+ o.children.each { |c| accept c }
+ @handler.end_document o.implicit_end
+ end
+
+ def visit_Psych_Nodes_Scalar o
+ @handler.scalar o.value, o.anchor, o.tag, o.plain, o.quoted, o.style
+ end
+
+ def visit_Psych_Nodes_Sequence o
+ @handler.start_sequence o.anchor, o.tag, o.implicit, o.style
+ o.children.each { |c| accept c }
+ @handler.end_sequence
+ end
+
+ def visit_Psych_Nodes_Mapping o
+ @handler.start_mapping o.anchor, o.tag, o.implicit, o.style
+ o.children.each { |c| accept c }
+ @handler.end_mapping
+ end
+
+ def visit_Psych_Nodes_Alias o
+ @handler.alias o.anchor
+ end
+ end
+ end
+end
diff --git a/jni/ruby/ext/psych/lib/psych/visitors/json_tree.rb b/jni/ruby/ext/psych/lib/psych/visitors/json_tree.rb
new file mode 100644
index 0000000..0127ac8
--- /dev/null
+++ b/jni/ruby/ext/psych/lib/psych/visitors/json_tree.rb
@@ -0,0 +1,24 @@
+require 'psych/json/ruby_events'
+
+module Psych
+ module Visitors
+ class JSONTree < YAMLTree
+ include Psych::JSON::RubyEvents
+
+ def self.create options = {}
+ emitter = Psych::JSON::TreeBuilder.new
+ class_loader = ClassLoader.new
+ ss = ScalarScanner.new class_loader
+ new(emitter, ss, options)
+ end
+
+ def accept target
+ if target.respond_to?(:encode_with)
+ dump_coder target
+ else
+ send(@dispatch_cache[target.class], target)
+ end
+ end
+ end
+ end
+end
diff --git a/jni/ruby/ext/psych/lib/psych/visitors/to_ruby.rb b/jni/ruby/ext/psych/lib/psych/visitors/to_ruby.rb
new file mode 100644
index 0000000..e696ebd
--- /dev/null
+++ b/jni/ruby/ext/psych/lib/psych/visitors/to_ruby.rb
@@ -0,0 +1,389 @@
+require 'psych/scalar_scanner'
+require 'psych/class_loader'
+require 'psych/exception'
+
+unless defined?(Regexp::NOENCODING)
+ Regexp::NOENCODING = 32
+end
+
+module Psych
+ module Visitors
+ ###
+ # This class walks a YAML AST, converting each node to Ruby
+ class ToRuby < Psych::Visitors::Visitor
+ 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
+ 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
+
+ def deserialize o
+ if klass = resolve_class(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'
+ 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
+ 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
+ private :deserialize
+
+ def visit_Psych_Nodes_Scalar o
+ register o, deserialize(o)
+ end
+
+ def visit_Psych_Nodes_Sequence o
+ if klass = resolve_class(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
+
+ def visit_Psych_Nodes_Mapping o
+ 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 /^!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'))
+ 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 /^!map:(.*)$/, /^!ruby\/hash:(.*)$/
+ revive_hash register(o, resolve_class($1).new), 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
+
+ def visit_Psych_Nodes_Document o
+ accept o.root
+ end
+
+ def visit_Psych_Nodes_Stream o
+ o.children.map { |c| accept c }
+ end
+
+ def visit_Psych_Nodes_Alias o
+ @st.fetch(o.anchor) { raise BadAlias, "Unknown alias: #{o.anchor}" }
+ end
+
+ private
+ def register node, object
+ @st[node.anchor] = object if node.anchor
+ object
+ end
+
+ def register_empty object
+ list = register(object, [])
+ object.children.each { |c| list.push accept c }
+ list
+ end
+
+ def revive_hash hash, o
+ 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
+ hash[key] = val
+ end
+
+ }
+ hash
+ end
+
+ def merge_key hash, key, val
+ end
+
+ def revive klass, node
+ s = register(node, klass.allocate)
+ init_with(s, revive_hash({}, node), node)
+ end
+
+ 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
+
+ # Convert +klassname+ to a Class
+ def resolve_class klassname
+ class_loader.load klassname
+ end
+ end
+
+ class NoAliasRuby < ToRuby
+ def visit_Psych_Nodes_Alias o
+ raise BadAlias, "Unknown alias: #{o.anchor}"
+ end
+ end
+ end
+end
diff --git a/jni/ruby/ext/psych/lib/psych/visitors/visitor.rb b/jni/ruby/ext/psych/lib/psych/visitors/visitor.rb
new file mode 100644
index 0000000..4d7772f
--- /dev/null
+++ b/jni/ruby/ext/psych/lib/psych/visitors/visitor.rb
@@ -0,0 +1,19 @@
+module Psych
+ module Visitors
+ class Visitor
+ def accept target
+ visit target
+ end
+
+ private
+
+ DISPATCH = Hash.new do |hash, klass|
+ hash[klass] = "visit_#{klass.name.gsub('::', '_')}"
+ end
+
+ def visit target
+ send DISPATCH[target.class], target
+ end
+ end
+ end
+end
diff --git a/jni/ruby/ext/psych/lib/psych/visitors/yaml_tree.rb b/jni/ruby/ext/psych/lib/psych/visitors/yaml_tree.rb
new file mode 100644
index 0000000..989e1f0
--- /dev/null
+++ b/jni/ruby/ext/psych/lib/psych/visitors/yaml_tree.rb
@@ -0,0 +1,565 @@
+require 'psych/tree_builder'
+require 'psych/scalar_scanner'
+require 'psych/class_loader'
+
+module Psych
+ module Visitors
+ ###
+ # YAMLTree builds a YAML ast given a Ruby object. For example:
+ #
+ # builder = Psych::Visitors::YAMLTree.new
+ # builder << { :foo => 'bar' }
+ # builder.tree # => #<Psych::Nodes::Stream .. }
+ #
+ class YAMLTree < Psych::Visitors::Visitor
+ class Registrar # :nodoc:
+ def initialize
+ @obj_to_id = {}
+ @obj_to_node = {}
+ @targets = []
+ @counter = 0
+ end
+
+ def register target, node
+ @targets << target
+ @obj_to_node[target.object_id] = node
+ end
+
+ def key? target
+ @obj_to_node.key? target.object_id
+ rescue NoMethodError
+ false
+ end
+
+ def id_for target
+ @obj_to_id[target.object_id] ||= (@counter += 1)
+ end
+
+ def node_for target
+ @obj_to_node[target.object_id]
+ end
+ end
+
+ attr_reader :started, :finished
+ alias :finished? :finished
+ alias :started? :started
+
+ def self.create options = {}, emitter = nil
+ emitter ||= TreeBuilder.new
+ class_loader = ClassLoader.new
+ ss = ScalarScanner.new class_loader
+ new(emitter, ss, options)
+ end
+
+ 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
+
+ def initialize emitter, ss, options
+ super()
+ @started = false
+ @finished = false
+ @emitter = emitter
+ @st = Registrar.new
+ @ss = ss
+ @options = options
+ @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
+
+ def start encoding = Nodes::Stream::UTF8
+ @emitter.start_stream(encoding).tap do
+ @started = true
+ end
+ end
+
+ def finish
+ @emitter.end_stream.tap do
+ @finished = true
+ end
+ end
+
+ def tree
+ finish unless finished?
+ @emitter.root
+ end
+
+ 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
+ alias :<< :push
+
+ 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
+
+ def visit_Psych_Omap o
+ seq = @emitter.start_sequence(nil, '!omap', false, Nodes::Sequence::BLOCK)
+ register(o, seq)
+
+ o.each { |k,v| visit_Hash k => v }
+ @emitter.end_sequence
+ end
+
+ def visit_Encoding o
+ tag = "!ruby/encoding"
+ @emitter.scalar o.name, nil, tag, false, false, Nodes::Scalar::ANY
+ end
+
+ 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
+
+ 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
+
+ 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
+
+ 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
+
+ def visit_Regexp o
+ register o, @emitter.scalar(o.inspect, nil, '!ruby/regexp', false, false, Nodes::Scalar::ANY)
+ end
+
+ 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
+
+ def visit_Time o
+ formatted = format_time o
+ register o, @emitter.scalar(formatted, nil, nil, true, false, Nodes::Scalar::ANY)
+ end
+
+ 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
+
+ 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
+
+ def visit_Integer o
+ @emitter.scalar o.to_s, nil, nil, true, false, Nodes::Scalar::ANY
+ end
+ alias :visit_TrueClass :visit_Integer
+ alias :visit_FalseClass :visit_Integer
+ alias :visit_Date :visit_Integer
+
+ 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
+
+ def visit_BigDecimal o
+ @emitter.scalar o._dump, nil, '!ruby/object:BigDecimal', false, false, Nodes::Scalar::ANY
+ end
+
+ def visit_String o
+ plain = true
+ quote = true
+ style = Nodes::Scalar::PLAIN
+ tag = nil
+ str = o
+
+ if binary?(o)
+ str = [o].pack('m').chomp
+ 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/
+ style = Nodes::Scalar::LITERAL
+ elsif o == '<<'
+ style = Nodes::Scalar::SINGLE_QUOTED
+ tag = 'tag:yaml.org,2002:str'
+ plain = false
+ quote = false
+ elsif o =~ /^\W[^"]*$/
+ style = Nodes::Scalar::DOUBLE_QUOTED
+ else
+ unless String === @ss.tokenize(o)
+ style = Nodes::Scalar::SINGLE_QUOTED
+ end
+ end
+
+ ivars = find_ivars o
+
+ if ivars.empty?
+ unless o.class == ::String
+ tag = "!ruby/string:#{o.class}"
+ plain = false
+ quote = false
+ end
+ @emitter.scalar str, 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 str, nil, tag, plain, quote, style
+
+ dump_ivars o
+
+ @emitter.end_mapping
+ end
+ end
+
+ 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
+
+ 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
+
+ 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
+
+ def visit_Hash o
+ tag = o.class == ::Hash ? nil : "!ruby/hash:#{o.class}"
+ implicit = !tag
+
+ register(o, @emitter.start_mapping(nil, tag, implicit, Psych::Nodes::Mapping::BLOCK))
+
+ o.each do |k,v|
+ accept k
+ accept v
+ end
+
+ @emitter.end_mapping
+ end
+
+ 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
+
+ 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
+
+ def visit_NilClass o
+ @emitter.scalar('', nil, 'tag:yaml.org,2002:null', true, false, Nodes::Scalar::ANY)
+ end
+
+ 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
+
+ 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
+
+ private
+ # FIXME: Remove the index and count checks in Psych 3.0
+ NULL = "\x00"
+ BINARY_RANGE = "\x00-\x7F"
+ WS_RANGE = "^ -~\t\r\n"
+
+ 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
+
+ def visit_array_subclass o
+ tag = "!ruby/array:#{o.class}"
+ if o.instance_variables.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)
+ o.instance_variables.each do |ivar|
+ accept ivar
+ accept o.instance_variable_get ivar
+ end
+ @emitter.end_mapping
+
+ @emitter.end_mapping
+ end
+ end
+
+ def dump_list o
+ end
+
+ # '%:z' was no defined until 1.9.3
+ if RUBY_VERSION < '1.9.3'
+ def format_time time
+ formatted = time.strftime("%Y-%m-%d %H:%M:%S.%9N")
+
+ if time.utc?
+ formatted += " Z"
+ else
+ zone = time.strftime('%z')
+ formatted += " #{zone[0,3]}:#{zone[3,5]}"
+ end
+
+ formatted
+ end
+ else
+ 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
+ end
+
+ # FIXME: remove this method once "to_yaml_properties" is removed
+ def find_ivars target
+ 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
+
+ target.instance_variables
+ end
+
+ def register target, yaml_obj
+ @st.register target, yaml_obj
+ yaml_obj
+ end
+
+ 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
+ end
+
+ def emit_coder c
+ 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
+ @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
+
+ 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
+ end
+ end
+end