summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorwhy <why@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2005-09-13 03:58:33 +0000
committerwhy <why@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2005-09-13 03:58:33 +0000
commitf1827a2fafaa50ba89f35c8c05beffd28b1dd6e6 (patch)
tree095d144b8790cb1d1a99fbe40e0e79e31f65fcac /lib
parent2007d73102b29e7bf50d08f6690ae5e6ffa5ea9a (diff)
* lib/yaml.rb: reworking YAML::Stream to use the new
emitter. * lib/yaml/stream.rb: ditto. * lib/yaml/rubytypes.rb: added Object#yaml_new. * lib/yaml/tag.rb: the tag_subclasses? method now shows up in the class. allow taguri to be set using an accessor. continue support of Object#to_yaml_type. * ext/syck/rubyext.c: new emitter code. yaml_new and yaml_initialize get called, should they be present. consolidated all the diaspora of internal node types into the family below YAML::Syck::Node -- Map, Seq, Scalar -- all of whom are SyckNode structs pointing to Ruby data. moved Object#yaml_new into the node_import and made it the default behavior. the target_class is always called wih yaml_new, prepended a parameter, which is the klass. loaded nodes through GenericResolver show their style. new Resolver#tagurize converts type ids to taguris. * ext/syck/implicit.re: were 'y' and 'n' seriously omitted?? * ext/syck/emitter.c: renovated emitter, walks the tree in advance. consolidated redundant block_styles struct into the scalar_style struct. (this means loaded nodes can now be sent back to emitter and preserve at least its very basic formatting.) * ext/syck/gram.c: headless documents of any kind allowed. * ext/syck/node.c: new syck_replace_str methods and syck_empty_* methods for rewriting node contents, while keeping the ID and other setup info. added syck_seq_assign. * ext/syck/syck.h: reflect block_styles and new node functions. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@9141 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib')
-rw-r--r--lib/yaml.rb119
-rw-r--r--lib/yaml/basenode.rb2
-rw-r--r--lib/yaml/error.rb1
-rw-r--r--lib/yaml/rubytypes.rb742
-rw-r--r--lib/yaml/stream.rb18
-rw-r--r--lib/yaml/syck.rb8
-rw-r--r--lib/yaml/tag.rb86
-rw-r--r--lib/yaml/types.rb151
8 files changed, 514 insertions, 613 deletions
diff --git a/lib/yaml.rb b/lib/yaml.rb
index 6837cc239d..5d409cfae3 100644
--- a/lib/yaml.rb
+++ b/lib/yaml.rb
@@ -6,8 +6,10 @@
# Author:: why the lucky stiff
#
+require 'stringio'
+require 'yaml/error'
require 'yaml/syck'
-require 'yaml/loader'
+require 'yaml/tag'
require 'yaml/stream'
# == YAML
@@ -73,7 +75,7 @@ require 'yaml/stream'
# ruby_obj == test_obj
# # => true
#
-# To register your custom types with the global loader, use +add_domain_type+.
+# To register your custom types with the global resolver, use +add_domain_type+.
#
# YAML::add_domain_type( "your-site.com,2004", "widget" ) do |type, val|
# Widget.new( val )
@@ -81,9 +83,21 @@ require 'yaml/stream'
#
module YAML
- @@parser = YAML::Syck::Parser
- @@loader = YAML::Syck::DefaultLoader
- @@emitter = YAML::Syck::Emitter
+ Resolver = YAML::Syck::Resolver
+ DefaultResolver = YAML::Syck::DefaultResolver
+ DefaultResolver.use_types_at( @@tagged_classes )
+ GenericResolver = YAML::Syck::GenericResolver
+ Parser = YAML::Syck::Parser
+ Emitter = YAML::Syck::Emitter
+
+ # Returns a new default parser
+ def YAML.parser; Parser.new.set_resolver( YAML.resolver ); end
+ # Returns a new generic parser
+ def YAML.generic_parser; Parser.new.set_resolver( GenericResolver ); end
+ # Returns the default resolver
+ def YAML.resolver; DefaultResolver; end
+ # Returns a new default emitter
+ def YAML.emitter; Emitter.new.set_resolver( YAML.resolver ); end
#
# Converts _obj_ to YAML and writes the YAML result to _io_.
@@ -99,9 +113,8 @@ module YAML
# #=> "--- :locked"
#
def YAML.dump( obj, io = nil )
- io ||= ""
- io << obj.to_yaml
- io
+ obj.to_yaml( io || io2 = StringIO.new )
+ io || ( io2.rewind; io2.read )
end
#
@@ -116,7 +129,7 @@ module YAML
# #=> :locked
#
def YAML.load( io )
- yp = @@parser.new.load( io )
+ yp = parser.load( io )
end
#
@@ -153,13 +166,13 @@ module YAML
#
# Can also load from a string.
#
- # YAML.load( "--- :locked" )
+ # YAML.parse( "--- :locked" )
# #=> #<YAML::Syck::Node:0x82edddc
# @type_id="tag:ruby.yaml.org,2002:sym",
# @value=":locked", @kind=:scalar>
#
def YAML.parse( io )
- yp = @@parser.new( :Model => :Generic ).load( io )
+ yp = generic_parser.load( io )
end
#
@@ -200,7 +213,7 @@ module YAML
# end
#
def YAML.each_document( io, &block )
- yp = @@parser.new.load_documents( io, &block )
+ yp = parser.load_documents( io, &block )
end
#
@@ -230,7 +243,7 @@ module YAML
# end
#
def YAML.each_node( io, &doc_proc )
- yp = @@parser.new( :Model => :Generic ).load_documents( io, &doc_proc )
+ yp = generic_parser.load_documents( io, &doc_proc )
end
#
@@ -254,12 +267,11 @@ module YAML
# loaded documents.
#
def YAML.load_stream( io )
- yp = @@parser.new
d = nil
- yp.load_documents( io ) { |doc|
- d = YAML::Stream.new( yp.options ) if not d
+ parser.load_documents( io ) do |doc|
+ d = YAML::Stream.new if not d
d.add( doc )
- }
+ end
return d
end
@@ -283,43 +295,50 @@ module YAML
#
# Add a global handler for a YAML domain type.
#
- def YAML.add_domain_type( domain, type_re, &transfer_proc )
- @@loader.add_domain_type( domain, type_re, &transfer_proc )
+ def YAML.add_domain_type( domain, type_tag, &transfer_proc )
+ resolver.add_type( "tag:#{ domain }:#{ type_tag }", transfer_proc )
end
#
# Add a transfer method for a builtin type
#
- def YAML.add_builtin_type( type_re, &transfer_proc )
- @@loader.add_builtin_type( type_re, &transfer_proc )
+ def YAML.add_builtin_type( type_tag, &transfer_proc )
+ resolver.add_type( "tag:yaml.org,2002:#{ type_tag }", transfer_proc )
end
#
# Add a transfer method for a builtin type
#
def YAML.add_ruby_type( type, &transfer_proc )
- @@loader.add_ruby_type( type, &transfer_proc )
+ resolver.add_type( "tag:ruby.yaml.org,2002:#{ type_tag }", transfer_proc )
end
#
# Add a private document type
#
def YAML.add_private_type( type_re, &transfer_proc )
- @@loader.add_private_type( type_re, &transfer_proc )
+ resolver.add_type( "x-private:" + type_re, transfer_proc )
end
#
# Detect typing of a string
#
def YAML.detect_implicit( val )
- @@loader.detect_implicit( val )
+ resolver.detect_implicit( val )
+ end
+
+ #
+ # Convert a type_id to a taguri
+ #
+ def YAML.tagurize( val )
+ resolver.tagurize( val )
end
#
# Apply a transfer method to a Ruby object
#
def YAML.transfer( type_id, obj )
- @@loader.transfer( type_id, obj )
+ resolver.transfer( YAML.tagurize( type_id ), obj )
end
#
@@ -358,24 +377,13 @@ module YAML
# Allocate an Emitter if needed
#
def YAML.quick_emit( oid, opts = {}, &e )
- old_opt = nil
- if opts[:Emitter].is_a? @@emitter
- out = opts.delete( :Emitter )
- old_opt = out.options.dup
- out.options.update( opts )
- else
- out = @@emitter.new( opts )
- end
- aidx = out.start_object( oid )
- if aidx
- out.simple( "*#{ aidx }" )
- else
- e.call( out )
- end
- if old_opt.is_a? Hash
- out.options = old_opt
- end
- out.end_object
+ out =
+ if opts.is_a? YAML::Emitter
+ opts
+ else
+ emitter.reset( opts )
+ end
+ out.emit( oid, &e )
end
end
@@ -383,7 +391,7 @@ end
require 'yaml/rubytypes'
require 'yaml/types'
-module Kernel
+module Kernel # :nodoc:
#
# ryan:: You know how Kernel.p is a really convenient way to dump ruby
# structures? The only downside is that it's not as legible as
@@ -400,12 +408,25 @@ module Kernel
# ryan:: Either way, I certainly will have a pony parade.
#
- def y( o, *x )
- x.unshift o
- puts( if x.length == 1
- YAML::dump( *x )
+ # Prints any supplied _objects_ out in YAML. Intended as
+ # a variation on +Kernel::p+.
+ #
+ # S = Struct.new(:name, :state)
+ # s = S['dave', 'TX']
+ # y s
+ #
+ # _produces:_
+ #
+ # --- !ruby/struct:S
+ # name: dave
+ # state: TX
+ #
+ def y( object, *objects )
+ objects.unshift object
+ puts( if objects.length == 1
+ YAML::dump( *objects )
else
- YAML::dump_stream( *x )
+ YAML::dump_stream( *objects )
end )
end
private :y
diff --git a/lib/yaml/basenode.rb b/lib/yaml/basenode.rb
index e88a76f3d2..a98f6d890a 100644
--- a/lib/yaml/basenode.rb
+++ b/lib/yaml/basenode.rb
@@ -25,7 +25,7 @@ module YAML
matches.each { |m|
result.push m.last
}
- self.class.new( 'seq', result )
+ YAML.transfer( 'seq', result )
end
end
diff --git a/lib/yaml/error.rb b/lib/yaml/error.rb
index a9df22749b..15865a9aa9 100644
--- a/lib/yaml/error.rb
+++ b/lib/yaml/error.rb
@@ -29,5 +29,6 @@ module YAML
class Error < StandardError; end
class ParseError < Error; end
+ class TypeError < StandardError; end
end
diff --git a/lib/yaml/rubytypes.rb b/lib/yaml/rubytypes.rb
index 643b30b22c..ca7529217e 100644
--- a/lib/yaml/rubytypes.rb
+++ b/lib/yaml/rubytypes.rb
@@ -1,9 +1,9 @@
# -*- mode: ruby; ruby-indent-level: 4; tab-width: 4 -*- vim: sw=4 ts=4
require 'date'
+
#
# Type conversions
#
-
class Class
def to_yaml( opts = {} )
raise TypeError, "can't dump anonymous class %s" % self.class
@@ -11,564 +11,377 @@ class Class
end
class Object
- def is_complex_yaml?
- true
- end
- def to_yaml_type
- "!ruby/object:#{self.class}"
- end
- def to_yaml_properties
- instance_variables.sort
- end
+ yaml_as "tag:ruby.yaml.org,2002:object"
+ def to_yaml_style; end
+ def to_yaml_properties; instance_variables.sort; end
def to_yaml( opts = {} )
- YAML::quick_emit( self.object_id, opts ) { |out|
- out.map( self.to_yaml_type ) { |map|
- to_yaml_properties.each { |m|
+ YAML::quick_emit( object_id, opts ) do |out|
+ out.map( taguri, to_yaml_style ) do |map|
+ to_yaml_properties.each do |m|
map.add( m[1..-1], instance_variable_get( m ) )
- }
- }
- }
+ end
+ end
+ end
end
end
-YAML.add_ruby_type( /^object/ ) { |type, val|
- type, obj_class = YAML.read_type_class( type, Object )
- YAML.object_maker( obj_class, val )
-}
-
#
# Maps: Hash#to_yaml
#
class Hash
- def is_complex_yaml?
- true
- end
- def to_yaml_type
- if self.class == Hash or self.class == YAML::SpecialHash
- "!map"
+ yaml_as "tag:ruby.yaml.org,2002:hash"
+ yaml_as "tag:yaml.org,2002:map"
+ def yaml_initialize( tag, val )
+ if Array === val
+ update Hash.[]( *val ) # Convert the map to a sequence
+ elsif Hash === val
+ update val
else
- "!ruby/hash:#{self.class}"
+ raise YAML::TypeError, "Invalid map explicitly tagged #{ tag }: " + val.inspect
end
end
def to_yaml( opts = {} )
- opts[:DocType] = self.class if Hash === opts
- YAML::quick_emit( self.object_id, opts ) { |out|
- hash_type = to_yaml_type
- if not out.options(:ExplicitTypes) and hash_type == "!map"
- hash_type = ""
- end
- out.map( hash_type ) { |map|
- #
- # Sort the hash
- #
- if out.options(:SortKeys)
- map.concat( self.sort )
- else
- map.concat( self.to_a )
+ YAML::quick_emit( object_id, opts ) do |out|
+ out.map( taguri, to_yaml_style ) do |map|
+ each do |k, v|
+ map.add( k, v )
end
- }
- }
- end
-end
-
-hash_proc = Proc.new { |type, val|
- if Array === val
- val = Hash.[]( *val ) # Convert the map to a sequence
- elsif Hash === val
- type, obj_class = YAML.read_type_class( type, Hash )
- if obj_class != Hash
- o = obj_class.allocate
- o.update( val )
- val = o
- end
- else
- raise YAML::Error, "Invalid map explicitly tagged !map: " + val.inspect
- end
- val
-}
-YAML.add_ruby_type( /^hash/, &hash_proc )
-
-module YAML
-
- #
- # Ruby-specific collection: !ruby/flexhash
- #
- class FlexHash < Array
- def []( k )
- self.assoc( k ).to_a[1]
- end
- def []=( k, *rest )
- val, set = rest.reverse
- if ( tmp = self.assoc( k ) ) and not set
- tmp[1] = val
- else
- self << [ k, val ]
end
- val
- end
- def has_key?( k )
- self.assoc( k ) ? true : false
- end
- def is_complex_yaml?
- true
- end
- def to_yaml( opts = {} )
- YAML::quick_emit( self.object_id, opts ) { |out|
- out.seq( "!ruby/flexhash" ) { |seq|
- self.each { |v|
- if v[1]
- seq.add( Hash.[]( *v ) )
- else
- seq.add( v[0] )
- end
- }
- }
- }
end
- end
-
- YAML.add_ruby_type( 'flexhash' ) { |type, val|
- if Array === val
- p = FlexHash.new
- val.each { |v|
- if Hash === v
- p.concat( v.to_a ) # Convert the map to a sequence
- else
- p << [ v, nil ]
- end
- }
- p
- else
- raise YAML::Error, "Invalid !ruby/flexhash: " + val.inspect
- end
- }
+ end
end
#
# Structs: export as a !map
#
class Struct
- def is_complex_yaml?
- true
+ yaml_as "tag:ruby.yaml.org,2002:struct"
+ def self.yaml_tag_class_name; self.name.gsub( "Struct::", "" ); end
+ def self.yaml_tag_read_class( name ); "Struct::#{ name }"; end
+ def self.yaml_new( klass, tag, val )
+ if Hash === val
+ struct_type = nil
+
+ #
+ # Use existing Struct if it exists
+ #
+ props = {}
+ val.delete_if { |k,v| props[k] = v if k =~ /^@/ }
+ begin
+ struct_name, struct_type = YAML.read_type_class( tag, Struct )
+ rescue NameError
+ end
+ if not struct_type
+ struct_def = [ tag.split( ':', 4 ).last ]
+ struct_type = Struct.new( *struct_def.concat( val.keys.collect { |k| k.intern } ) )
+ end
+
+ #
+ # Set the Struct properties
+ #
+ st = YAML::object_maker( struct_type, {} )
+ st.members.each do |m|
+ st.send( "#{m}=", val[m] )
+ end
+ props.each do |k,v|
+ st.instance_variable_set(k, v)
+ end
+ st
+ else
+ raise YAML::TypeError, "Invalid Ruby Struct: " + val.inspect
+ end
end
def to_yaml( opts = {} )
- YAML::quick_emit( self.object_id, opts ) { |out|
+ YAML::quick_emit( object_id, opts ) do |out|
#
# Basic struct is passed as a YAML map
#
- struct_name = self.class.name.gsub( "Struct::", "" )
- out.map( "!ruby/struct:#{struct_name}" ) { |map|
- self.members.each { |m|
+ out.map( taguri, to_yaml_style ) do |map|
+ self.members.each do |m|
map.add( m, self[m] )
- }
- self.to_yaml_properties.each { |m|
+ end
+ self.to_yaml_properties.each do |m|
map.add( m, instance_variable_get( m ) )
- }
- }
- }
+ end
+ end
+ end
end
end
-YAML.add_ruby_type( /^struct/ ) { |type, val|
- if Hash === val
- struct_type = nil
-
- #
- # Use existing Struct if it exists
- #
- props = {}
- val.delete_if { |k,v| props[k] = v if k =~ /^@/ }
- begin
- struct_name, struct_type = YAML.read_type_class( type, Struct )
- rescue NameError
- end
- if not struct_type
- struct_def = [ type.split( ':', 4 ).last ]
- struct_type = Struct.new( *struct_def.concat( val.keys.collect { |k| k.intern } ) )
- end
-
- #
- # Set the Struct properties
- #
- st = YAML::object_maker( struct_type, {} )
- st.members.each { |m|
- st.send( "#{m}=", val[m] )
- }
- props.each { |k,v|
- st.instance_variable_set(k, v)
- }
- st
- else
- raise YAML::Error, "Invalid Ruby Struct: " + val.inspect
- end
-}
-
#
# Sequences: Array#to_yaml
#
class Array
- def is_complex_yaml?
- true
- end
- def to_yaml_type
- if self.class == Array
- "!seq"
- else
- "!ruby/array:#{self.class}"
- end
- end
+ yaml_as "tag:ruby.yaml.org,2002:array"
+ yaml_as "tag:yaml.org,2002:seq"
+ def yaml_initialize( tag, val ); concat( val.to_a ); end
def to_yaml( opts = {} )
- opts[:DocType] = self.class if Hash === opts
- YAML::quick_emit( self.object_id, opts ) { |out|
- array_type = to_yaml_type
- if not out.options(:ExplicitTypes) and array_type == "!seq"
- array_type = ""
+ YAML::quick_emit( object_id, opts ) do |out|
+ out.seq( taguri, to_yaml_style ) do |seq|
+ each do |x|
+ seq.add( x )
+ end
end
-
- out.seq( array_type ) { |seq|
- seq.concat( self )
- }
- }
+ end
end
end
-array_proc = Proc.new { |type, val|
- if Array === val
- type, obj_class = YAML.read_type_class( type, Array )
- if obj_class != Array
- o = obj_class.allocate
- o.concat( val )
- val = o
- end
- val
- else
- val.to_a
- end
-}
-YAML.add_ruby_type( /^array/, &array_proc )
-
#
# Exception#to_yaml
#
class Exception
- def is_complex_yaml?
- true
- end
- def to_yaml_type
- "!ruby/exception:#{self.class}"
+ yaml_as "tag:ruby.yaml.org,2002:exception"
+ def Exception.yaml_new( klass, tag, val )
+ o = YAML.object_maker( klass, { 'mesg' => val.delete( 'message' ) } )
+ val.each_pair do |k,v|
+ o.instance_variable_set("@#{k}", v)
+ end
+ o
end
def to_yaml( opts = {} )
- YAML::quick_emit( self.object_id, opts ) { |out|
- out.map( self.to_yaml_type ) { |map|
- map.add( 'message', self.message )
- to_yaml_properties.each { |m|
+ YAML::quick_emit( object_id, opts ) do |out|
+ out.map( taguri, to_yaml_style ) do |map|
+ map.add( 'message', message )
+ to_yaml_properties.each do |m|
map.add( m[1..-1], instance_variable_get( m ) )
- }
- }
- }
+ end
+ end
+ end
end
end
-YAML.add_ruby_type( /^exception/ ) { |type, val|
- type, obj_class = YAML.read_type_class( type, Exception )
- o = YAML.object_maker( obj_class, { 'mesg' => val.delete( 'message' ) } )
- val.each_pair { |k,v|
- o.instance_variable_set("@#{k}", v)
- }
- o
-}
-
#
# String#to_yaml
#
class String
+ yaml_as "tag:ruby.yaml.org,2002:string"
+ yaml_as "tag:yaml.org,2002:str"
def is_complex_yaml?
- to_yaml_fold or not to_yaml_properties.empty? or self =~ /\n.+/
+ to_yaml_style or not to_yaml_properties.empty? or self =~ /\n.+/
end
def is_binary_data?
- ( self.count( "^ -~", "^\r\n" ) / self.size > 0.3 || self.count( "\x00" ) > 0 )
- end
- def to_yaml_type
- "!ruby/string#{ ":#{ self.class }" if self.class != ::String }"
+ ( self.count( "^ -~", "^\r\n" ) / self.size > 0.3 || self.count( "\x00" ) > 0 ) unless empty?
end
- def to_yaml_fold
- nil
+ def String.yaml_new( klass, tag, val )
+ val = { 'str' => val } if String === val
+ if Hash === val
+ s = klass.allocate
+ # Thank you, NaHi
+ String.instance_method(:initialize).
+ bind(s).
+ call( val.delete( 'str' ) )
+ val.each { |k,v| s.instance_variable_set( k, v ) }
+ s
+ else
+ raise YAML::TypeError, "Invalid String: " + val.inspect
+ end
end
def to_yaml( opts = {} )
- complex = false
- if self.is_complex_yaml?
- complex = true
- elsif opts[:BestWidth].to_i > 0
- if self.length > opts[:BestWidth] and opts[:UseFold]
- complex = true
- end
- end
- YAML::quick_emit( complex ? self.object_id : nil, opts ) { |out|
- if complex
- if not to_yaml_properties.empty?
- out.map( self.to_yaml_type ) { |map|
- map.add( 'str', "#{self}" )
- to_yaml_properties.each { |m|
- map.add( m, instance_variable_get( m ) )
- }
- }
- elsif self.is_binary_data?
- out.binary_base64( self )
- elsif self =~ /#{YAML::ESCAPE_CHAR}/
- out.node_text( self, '"' )
- else
- out.node_text( self, to_yaml_fold )
- end
+ YAML::quick_emit( is_complex_yaml? ? object_id : nil, opts ) do |out|
+ if is_binary_data?
+ out.scalar( "tag:yaml.org,2002:binary", [self].pack("m"), :literal )
+ elsif to_yaml_properties.empty?
+ out.scalar( taguri, self, to_yaml_style )
else
- ostr = if out.options(:KeepValue)
- self
- elsif empty?
- "''"
- elsif self =~ /^[^#{YAML::WORD_CHAR}\/]| \#|#{YAML::ESCAPE_CHAR}|[#{YAML::SPACE_INDICATORS}]( |$)| $|\n|\'/
- out.node_text( self, '"' ); nil
- elsif YAML.detect_implicit( self ) != 'str'
- out.node_text( self, '"' ); nil
- else
- self
- end
- out.simple( ostr ) unless ostr.nil?
+ out.map( taguri, to_yaml_style ) do |map|
+ map.add( 'str', "#{self}" )
+ to_yaml_properties.each do |m|
+ map.add( m, instance_variable_get( m ) )
+ end
+ end
end
- }
+ end
end
end
-
-YAML.add_ruby_type( /^string/ ) { |type, val|
- type, obj_class = YAML.read_type_class( type, ::String )
- if Hash === val
- s = YAML::object_maker( obj_class, {} )
- # Thank you, NaHi
- String.instance_method(:initialize).
- bind(s).
- call( val.delete( 'str' ) )
- val.each { |k,v| s.instance_variable_set( k, v ) }
- s
- else
- raise YAML::Error, "Invalid String: " + val.inspect
- end
-}
-
#
# Symbol#to_yaml
#
class Symbol
- def is_complex_yaml?
- false
+ yaml_as "tag:ruby.yaml.org,2002:symbol"
+ yaml_as "tag:ruby.yaml.org,2002:sym"
+ # yaml_implicit /^:/, :yaml_new
+ def Symbol.yaml_new( klass, tag, val )
+ if String === val
+ val.intern
+ else
+ raise YAML::TypeError, "Invalid Symbol: " + val.inspect
+ end
end
def to_yaml( opts = {} )
- YAML::quick_emit( nil, opts ) { |out|
- out << ":"
- self.id2name.to_yaml( :Emitter => out )
- }
+ YAML::quick_emit( nil, opts ) do |out|
+ out.scalar( taguri, self.id2name, :plain )
+ end
end
end
-symbol_proc = Proc.new { |type, val|
- if String === val
- val = YAML::load( "--- #{val}") if val =~ /^["'].*['"]$/
- val.intern
- else
- raise YAML::Error, "Invalid Symbol: " + val.inspect
- end
-}
-YAML.add_ruby_type( 'symbol', &symbol_proc )
-YAML.add_ruby_type( 'sym', &symbol_proc )
-
#
# Range#to_yaml
+# TODO: Rework the Range as a sequence (simpler)
#
class Range
- def is_complex_yaml?
- true
- end
- def to_yaml_type
- "!ruby/range#{ if self.class != ::Range; ":#{ self.class }"; end }"
+ yaml_as "tag:ruby.yaml.org,2002:range"
+ def Range.yaml_new( klass, tag, val )
+ inr = %r'(\w+|[+-]?\d+(?:\.\d+)?(?:e[+-]\d+)?|"(?:[^\\"]|\\.)*")'
+ opts = {}
+ if String === val and val =~ /^#{inr}(\.{2,3})#{inr}$/o
+ r1, rdots, r2 = $1, $2, $3
+ opts = {
+ 'begin' => YAML.load( "--- #{r1}" ),
+ 'end' => YAML.load( "--- #{r2}" ),
+ 'excl' => rdots.length == 3
+ }
+ val = {}
+ elsif Hash === val
+ opts['begin'] = val.delete('begin')
+ opts['end'] = val.delete('end')
+ opts['excl'] = val.delete('excl')
+ end
+ if Hash === opts
+ r = YAML::object_maker( klass, {} )
+ # Thank you, NaHi
+ Range.instance_method(:initialize).
+ bind(r).
+ call( opts['begin'], opts['end'], opts['excl'] )
+ val.each { |k,v| r.instance_variable_set( k, v ) }
+ r
+ else
+ raise YAML::TypeError, "Invalid Range: " + val.inspect
+ end
end
def to_yaml( opts = {} )
- YAML::quick_emit( self.object_id, opts ) { |out|
- if self.begin.is_complex_yaml? or self.begin.respond_to? :to_str or
- self.end.is_complex_yaml? or self.end.respond_to? :to_str or
- not to_yaml_properties.empty?
- out.map( to_yaml_type ) { |map|
+ YAML::quick_emit( object_id, opts ) do |out|
+ # if self.begin.is_complex_yaml? or self.begin.respond_to? :to_str or
+ # self.end.is_complex_yaml? or self.end.respond_to? :to_str or
+ # not to_yaml_properties.empty?
+ out.map( taguri, to_yaml_style ) do |map|
map.add( 'begin', self.begin )
map.add( 'end', self.end )
map.add( 'excl', self.exclude_end? )
- to_yaml_properties.each { |m|
+ to_yaml_properties.each do |m|
map.add( m, instance_variable_get( m ) )
- }
- }
- else
- out << "#{ to_yaml_type } '"
- self.begin.to_yaml(:Emitter => out)
- out << ( self.exclude_end? ? "..." : ".." )
- self.end.to_yaml(:Emitter => out)
- out << "'"
- end
- }
+ end
+ end
+ # else
+ # out.scalar( taguri ) do |sc|
+ # sc.embed( self.begin )
+ # sc.concat( self.exclude_end? ? "..." : ".." )
+ # sc.embed( self.end )
+ # end
+ # end
+ end
end
end
-YAML.add_ruby_type( /^range/ ) { |type, val|
- type, obj_class = YAML.read_type_class( type, ::Range )
- inr = %r'(\w+|[+-]?\d+(?:\.\d+)?(?:e[+-]\d+)?|"(?:[^\\"]|\\.)*")'
- opts = {}
- if String === val and val =~ /^#{inr}(\.{2,3})#{inr}$/o
- r1, rdots, r2 = $1, $2, $3
- opts = {
- 'begin' => YAML.load( "--- #{r1}" ),
- 'end' => YAML.load( "--- #{r2}" ),
- 'excl' => rdots.length == 3
- }
- val = {}
- elsif Hash === val
- opts['begin'] = val.delete('begin')
- opts['end'] = val.delete('end')
- opts['excl'] = val.delete('excl')
- end
- if Hash === opts
- r = YAML::object_maker( obj_class, {} )
- # Thank you, NaHi
- Range.instance_method(:initialize).
- bind(r).
- call( opts['begin'], opts['end'], opts['excl'] )
- val.each { |k,v| r.instance_variable_set( k, v ) }
- r
- else
- raise YAML::Error, "Invalid Range: " + val.inspect
- end
-}
-
#
# Make an Regexp
#
class Regexp
- def is_complex_yaml?
- self.class != Regexp or not to_yaml_properties.empty?
- end
- def to_yaml_type
- "!ruby/regexp#{ if self.class != ::Regexp; ":#{ self.class }"; end }"
+ yaml_as "tag:ruby.yaml.org,2002:regexp"
+ def Regexp.yaml_new( klass, tag, val )
+ if String === val and val =~ /^\/(.*)\/([mix]*)$/
+ val = { 'regexp' => $1, 'mods' => $2 }
+ end
+ if Hash === val
+ mods = nil
+ unless val['mods'].to_s.empty?
+ mods = 0x00
+ mods |= Regexp::EXTENDED if val['mods'].include?( 'x' )
+ mods |= Regexp::IGNORECASE if val['mods'].include?( 'i' )
+ mods |= Regexp::MULTILINE if val['mods'].include?( 'm' )
+ end
+ val.delete( 'mods' )
+ r = YAML::object_maker( klass, {} )
+ Regexp.instance_method(:initialize).
+ bind(r).
+ call( val.delete( 'regexp' ), mods )
+ val.each { |k,v| r.instance_variable_set( k, v ) }
+ r
+ else
+ raise YAML::TypeError, "Invalid Regular expression: " + val.inspect
+ end
end
def to_yaml( opts = {} )
- YAML::quick_emit( nil, opts ) { |out|
- if self.is_complex_yaml?
- out.map( self.to_yaml_type ) { |map|
+ YAML::quick_emit( nil, opts ) do |out|
+ if to_yaml_properties.empty?
+ out.scalar( taguri, self.inspect, :plain )
+ else
+ out.map( taguri, to_yaml_style ) do |map|
src = self.inspect
if src =~ /\A\/(.*)\/([a-z]*)\Z/
map.add( 'regexp', $1 )
map.add( 'mods', $2 )
else
- raise YAML::Error, "Invalid Regular expression: " + src
+ raise YAML::TypeError, "Invalid Regular expression: " + src
end
- to_yaml_properties.each { |m|
+ to_yaml_properties.each do |m|
map.add( m, instance_variable_get( m ) )
- }
- }
- else
- out << "#{ to_yaml_type } "
- self.inspect.to_yaml( :Emitter => out )
+ end
+ end
end
- }
+ end
end
end
-regexp_proc = Proc.new { |type, val|
- type, obj_class = YAML.read_type_class( type, ::Regexp )
- if String === val and val =~ /^\/(.*)\/([mix]*)$/
- val = { 'regexp' => $1, 'mods' => $2 }
- end
- if Hash === val
- mods = nil
- unless val['mods'].to_s.empty?
- mods = 0x00
- mods |= Regexp::EXTENDED if val['mods'].include?( 'x' )
- mods |= Regexp::IGNORECASE if val['mods'].include?( 'i' )
- mods |= Regexp::MULTILINE if val['mods'].include?( 'm' )
- end
- val.delete( 'mods' )
- r = YAML::object_maker( obj_class, {} )
- Regexp.instance_method(:initialize).
- bind(r).
- call( val.delete( 'regexp' ), mods )
- val.each { |k,v| r.instance_variable_set( k, v ) }
- r
- else
- raise YAML::Error, "Invalid Regular expression: " + val.inspect
- end
-}
-YAML.add_domain_type( "perl.yaml.org,2002", /^regexp/, &regexp_proc )
-YAML.add_ruby_type( /^regexp/, &regexp_proc )
-
#
# Emit a Time object as an ISO 8601 timestamp
#
class Time
- def is_complex_yaml?
- self.class != Time or not to_yaml_properties.empty?
- end
- def to_yaml_type
- "!ruby/time#{ if self.class != ::Time; ":#{ self.class }"; end }"
+ yaml_as "tag:ruby.yaml.org,2002:time"
+ yaml_as "tag:yaml.org,2002:timestamp"
+ def Time.yaml_new( klass, tag, val )
+ if Hash === val
+ t = val.delete( 'at' )
+ val.each { |k,v| t.instance_variable_set( k, v ) }
+ t
+ else
+ raise YAML::TypeError, "Invalid Time: " + val.inspect
+ end
end
def to_yaml( opts = {} )
- YAML::quick_emit( nil, opts ) { |out|
- if self.is_complex_yaml?
- out.map( self.to_yaml_type ) { |map|
- map.add( 'at', ::Time.at( self ) )
- to_yaml_properties.each { |m|
- map.add( m, instance_variable_get( m ) )
- }
- }
+ YAML::quick_emit( object_id, opts ) do |out|
+ tz = "Z"
+ # from the tidy Tobias Peters <t-peters@gmx.de> Thanks!
+ unless self.utc?
+ utc_same_instant = self.dup.utc
+ utc_same_writing = Time.utc(year,month,day,hour,min,sec,usec)
+ difference_to_utc = utc_same_writing - utc_same_instant
+ if (difference_to_utc < 0)
+ difference_sign = '-'
+ absolute_difference = -difference_to_utc
+ else
+ difference_sign = '+'
+ absolute_difference = difference_to_utc
+ end
+ difference_minutes = (absolute_difference/60).round
+ tz = "%s%02d:%02d" % [ difference_sign, difference_minutes / 60, difference_minutes % 60]
+ end
+ standard = self.strftime( "%Y-%m-%d %H:%M:%S" )
+ standard += ".%06d" % [usec] if usec.nonzero?
+ standard += " %s" % [tz]
+ if to_yaml_properties.empty?
+ out.scalar( taguri, standard, :plain )
else
- tz = "Z"
- # from the tidy Tobias Peters <t-peters@gmx.de> Thanks!
- unless self.utc?
- utc_same_instant = self.dup.utc
- utc_same_writing = Time.utc(year,month,day,hour,min,sec,usec)
- difference_to_utc = utc_same_writing - utc_same_instant
- if (difference_to_utc < 0)
- difference_sign = '-'
- absolute_difference = -difference_to_utc
- else
- difference_sign = '+'
- absolute_difference = difference_to_utc
+ out.map( taguri, to_yaml_style ) do |map|
+ map.add( 'at', standard )
+ to_yaml_properties.each do |m|
+ map.add( m, instance_variable_get( m ) )
end
- difference_minutes = (absolute_difference/60).round
- tz = "%s%02d:%02d" % [ difference_sign, difference_minutes / 60, difference_minutes % 60]
end
- standard = self.strftime( "%Y-%m-%d %H:%M:%S" )
- standard += ".%06d" % [usec] if usec.nonzero?
- standard += " %s" % [tz]
- standard.to_yaml( :Emitter => out, :KeepValue => true )
end
- }
+ end
end
end
-YAML.add_ruby_type( /^time/ ) { |type, val|
- type, obj_class = YAML.read_type_class( type, ::Time )
- if Hash === val
- t = obj_class.at( val.delete( 'at' ) )
- val.each { |k,v| t.instance_variable_set( k, v ) }
- t
- else
- raise YAML::Error, "Invalid Time: " + val.inspect
- end
-}
-
#
# Emit a Date object as a simple implicit
#
class Date
- def is_complex_yaml?
- false
- end
+ yaml_as "tag:yaml.org,2002:timestamp#ymd"
def to_yaml( opts = {} )
- opts[:KeepValue] = true
- self.to_s.to_yaml( opts )
+ YAML::quick_emit( object_id, opts ) do |out|
+ out.scalar( "tag:yaml.org,2002:timestamp", self.to_s, :plain )
+ end
end
end
@@ -576,50 +389,51 @@ end
# Send Integer, Booleans, NilClass to String
#
class Numeric
- def is_complex_yaml?
- false
- end
def to_yaml( opts = {} )
- str = self.to_s
- if str == "Infinity"
- str = ".Inf"
- elsif str == "-Infinity"
- str = "-.Inf"
- elsif str == "NaN"
- str = ".NaN"
- end
- opts[:KeepValue] = true
- str.to_yaml( opts )
+ YAML::quick_emit( nil, opts ) do |out|
+ str = self.to_s
+ if str == "Infinity"
+ str = ".Inf"
+ elsif str == "-Infinity"
+ str = "-.Inf"
+ elsif str == "NaN"
+ str = ".NaN"
+ end
+ out.scalar( taguri, str, :plain )
+ end
end
end
+class Fixnum
+ yaml_as "tag:yaml.org,2002:int"
+end
+class Float
+ yaml_as "tag:yaml.org,2002:float"
+end
class TrueClass
- def is_complex_yaml?
- false
- end
+ yaml_as "tag:yaml.org,2002:bool#yes"
def to_yaml( opts = {} )
- opts[:KeepValue] = true
- "true".to_yaml( opts )
+ YAML::quick_emit( nil, opts ) do |out|
+ out.scalar( taguri, "true", :plain )
+ end
end
end
class FalseClass
- def is_complex_yaml?
- false
- end
+ yaml_as "tag:yaml.org,2002:bool#no"
def to_yaml( opts = {} )
- opts[:KeepValue] = true
- "false".to_yaml( opts )
+ YAML::quick_emit( nil, opts ) do |out|
+ out.scalar( taguri, "false", :plain )
+ end
end
end
class NilClass
- def is_complex_yaml?
- false
- end
+ yaml_as "tag:yaml.org,2002:null"
def to_yaml( opts = {} )
- opts[:KeepValue] = true
- "".to_yaml( opts )
+ YAML::quick_emit( nil, opts ) do |out|
+ out.scalar( taguri, "", :plain )
+ end
end
end
diff --git a/lib/yaml/stream.rb b/lib/yaml/stream.rb
index 943c51526b..060fbc4200 100644
--- a/lib/yaml/stream.rb
+++ b/lib/yaml/stream.rb
@@ -24,19 +24,15 @@ module YAML
@documents[ doc_num ] = doc
end
- def emit
- opts = @options.dup
- opts[:UseHeader] = true if @documents.length > 1
- ct = 0
- out = YAML::Syck::Emitter.new( opts )
+ def emit( io = nil )
+ # opts = @options.dup
+ # opts[:UseHeader] = true if @documents.length > 1
+ out = YAML.emitter
+ out.reset( io || io2 = StringIO.new )
@documents.each { |v|
- if ct > 0
- out << "\n--- "
- end
- v.to_yaml( :Emitter => out )
- ct += 1
+ v.to_yaml( out )
}
- out.end_object
+ io || ( io2.rewind; io2.read )
end
end
diff --git a/lib/yaml/syck.rb b/lib/yaml/syck.rb
index 267067feb5..faf57e8036 100644
--- a/lib/yaml/syck.rb
+++ b/lib/yaml/syck.rb
@@ -4,7 +4,6 @@
#
require 'syck'
require 'yaml/basenode'
-require 'yaml/baseemitter'
module YAML
module Syck
@@ -16,12 +15,5 @@ module YAML
include YAML::BaseNode
end
- #
- # Mixin BaseEmitter functionality
- #
- class Emitter
- include YAML::BaseEmitter
- end
-
end
end
diff --git a/lib/yaml/tag.rb b/lib/yaml/tag.rb
new file mode 100644
index 0000000000..0e25e1f85a
--- /dev/null
+++ b/lib/yaml/tag.rb
@@ -0,0 +1,86 @@
+# -*- mode: ruby; ruby-indent-level: 4; tab-width: 4 -*- vim: sw=4 ts=4
+# $Id$
+#
+# = yaml/tag.rb: methods for associating a taguri to a class.
+#
+# Author:: why the lucky stiff
+#
+module YAML
+ # A dictionary of taguris which map to
+ # Ruby classes.
+ @@tagged_classes = {}
+
+ #
+ # Associates a taguri _tag_ with a Ruby class _cls_. The taguri is used to give types
+ # to classes when loading YAML. Taguris are of the form:
+ #
+ # tag:authorityName,date:specific
+ #
+ # The +authorityName+ is a domain name or email address. The +date+ is the date the type
+ # was issued in YYYY or YYYY-MM or YYYY-MM-DD format. The +specific+ is a name for
+ # the type being added.
+ #
+ # For example, built-in YAML types have 'yaml.org' as the +authorityName+ and '2002' as the
+ # +date+. The +specific+ is simply the name of the type:
+ #
+ # tag:yaml.org,2002:int
+ # tag:yaml.org,2002:float
+ # tag:yaml.org,2002:timestamp
+ #
+ # The domain must be owned by you on the +date+ declared. If you don't own any domains on the
+ # date you declare the type, you can simply use an e-mail address.
+ #
+ # tag:why@ruby-lang.org,2004:notes/personal
+ #
+ def YAML.tag_class( tag, cls )
+ if @@tagged_classes.has_key? tag
+ warn "class #{ @@tagged_classes[tag] } held ownership of the #{ tag } tag"
+ end
+ @@tagged_classes[tag] = cls
+ end
+
+ # Returns the complete dictionary of taguris, paired with classes. The key for
+ # the dictionary is the full taguri. The value for each key is the class constant
+ # associated to that taguri.
+ #
+ # YAML.tagged_classes["tag:yaml.org,2002:int"] => Integer
+ #
+ def YAML.tagged_classes
+ @@tagged_classes
+ end
+end
+
+class Module # :nodoc: all
+ # Adds a taguri _tag_ to a class, used when dumping or loading the class
+ # in YAML. See YAML::tag_class for detailed information on typing and
+ # taguris.
+ def yaml_as( tag, sc = true )
+ class_eval <<-"end;"
+ attr_accessor :taguri
+ def taguri
+ if respond_to? :to_yaml_type
+ YAML::tagurize( to_yaml_type[1..-1] )
+ else
+ return @taguri if @taguri
+ tag = #{ tag.dump }
+ if self.class.yaml_tag_subclasses? and self.class != YAML::tagged_classes[tag]
+ tag = "\#{ tag }:\#{ self.class.yaml_tag_class_name }"
+ end
+ tag
+ end
+ end
+ def self.yaml_tag_subclasses?; #{ sc ? 'true' : 'false' }; end
+ end;
+ YAML::tag_class tag, self
+ end
+ # Transforms the subclass name into a name suitable for display
+ # in a subclassed tag.
+ def yaml_tag_class_name
+ self.name
+ end
+ # Transforms the subclass name found in the tag into a Ruby
+ # constant name.
+ def yaml_tag_read_class( name )
+ name
+ end
+end
diff --git a/lib/yaml/types.rb b/lib/yaml/types.rb
index f7772cb3a0..c890f22bfa 100644
--- a/lib/yaml/types.rb
+++ b/lib/yaml/types.rb
@@ -8,15 +8,14 @@ module YAML
# Default private type
#
class PrivateType
+ def self.tag_subclasses?; false; end
attr_accessor :type_id, :value
def initialize( type, val )
@type_id = type; @value = val
+ @value.taguri = "x-private:#{ @type_id }"
end
def to_yaml( opts = {} )
- YAML::quick_emit( self.object_id, opts ) { |out|
- out << " !!#{@type_id}"
- value.to_yaml( :Emitter => out )
- }
+ @value.to_yaml( opts )
end
end
@@ -24,29 +23,37 @@ module YAML
# Default domain type
#
class DomainType
+ def self.tag_subclasses?; false; end
attr_accessor :domain, :type_id, :value
def initialize( domain, type, val )
@domain = domain; @type_id = type; @value = val
+ @value.taguri = "tag:#{ @domain }:#{ @type_id }"
end
- def to_yaml_type
- dom = @domain.dup
- if dom =~ /\.yaml\.org,2002$/
- dom = $`
- end
- "#{dom}/#{@type_id}"
- end
def to_yaml( opts = {} )
- YAML::quick_emit( self.object_id, opts ) { |out|
- out << " !#{to_yaml_type} "
- value.to_yaml( :Emitter => out )
- }
+ @value.to_yaml( opts )
+ end
+ end
+
+ #
+ # Unresolved objects
+ #
+ class Object
+ def self.tag_subclasses?; false; end
+ def to_yaml( opts = {} )
+ YAML::quick_emit( object_id, opts ) do |out|
+ out.map( "tag:ruby.yaml.org,2002:object:#{ @class }", to_yaml_style ) do |map|
+ @ivars.each do |k,v|
+ map.add( k, v )
+ end
+ end
+ end
end
end
#
# YAML Hash class to support comments and defaults
#
- class SpecialHash < Object::Hash
+ class SpecialHash < ::Hash
attr_accessor :default
def inspect
self.default.to_s
@@ -69,12 +76,27 @@ module YAML
#
# Builtin collection: !omap
#
- class Omap < Array
+ class Omap < ::Array
+ yaml_as "tag:yaml.org,2002:omap"
+ def yaml_initialize( tag, val )
+ if Array === val
+ val.each do |v|
+ if Hash === v
+ concat( v.to_a ) # Convert the map to a sequence
+ else
+ raise YAML::Error, "Invalid !omap entry: " + val.inspect
+ end
+ end
+ else
+ raise YAML::Error, "Invalid !omap: " + val.inspect
+ end
+ self
+ end
def self.[]( *vals )
o = Omap.new
- 0.step( vals.length - 1, 2 ) { |i|
+ 0.step( vals.length - 1, 2 ) do |i|
o[vals[i]] = vals[i+1]
- }
+ end
o
end
def []( k )
@@ -96,36 +118,35 @@ module YAML
true
end
def to_yaml( opts = {} )
- YAML::quick_emit( self.object_id, opts ) { |out|
- out.seq( "!omap" ) { |seq|
- self.each { |v|
+ YAML::quick_emit( self.object_id, opts ) do |out|
+ out.seq( taguri, to_yaml_style ) do |seq|
+ self.each do |v|
seq.add( Hash[ *v ] )
- }
- }
- }
- end
- end
-
- YAML.add_builtin_type( "omap" ) { |type, val|
- if Array === val
- p = Omap.new
- val.each { |v|
- if Hash === v
- p.concat( v.to_a ) # Convert the map to a sequence
- else
- raise YAML::Error, "Invalid !omap entry: " + val.inspect
+ end
end
- }
- else
- raise YAML::Error, "Invalid !omap: " + val.inspect
+ end
end
- p
- }
+ end
#
# Builtin collection: !pairs
#
- class Pairs < Array
+ class Pairs < ::Array
+ yaml_as "tag:yaml.org,2002:pairs"
+ def yaml_initialize( tag, val )
+ if Array === val
+ val.each do |v|
+ if Hash === v
+ concat( v.to_a ) # Convert the map to a sequence
+ else
+ raise YAML::Error, "Invalid !pairs entry: " + val.inspect
+ end
+ end
+ else
+ raise YAML::Error, "Invalid !pairs: " + val.inspect
+ end
+ self
+ end
def self.[]( *vals )
p = Pairs.new
0.step( vals.length - 1, 2 ) { |i|
@@ -147,50 +168,20 @@ module YAML
true
end
def to_yaml( opts = {} )
- YAML::quick_emit( self.object_id, opts ) { |out|
- out.seq( "!pairs" ) { |seq|
- self.each { |v|
+ YAML::quick_emit( self.object_id, opts ) do |out|
+ out.seq( taguri, to_yaml_style ) do |seq|
+ self.each do |v|
seq.add( Hash[ *v ] )
- }
- }
- }
- end
- end
-
- YAML.add_builtin_type( "pairs" ) { |type, val|
- if Array === val
- p = Pairs.new
- val.each { |v|
- if Hash === v
- p.concat( v.to_a ) # Convert the map to a sequence
- else
- raise YAML::Error, "Invalid !pairs entry: " + val.inspect
+ end
end
- }
- else
- raise YAML::Error, "Invalid !pairs: " + val.inspect
+ end
end
- p
- }
+ end
#
# Builtin collection: !set
#
- class Set < Hash
- def to_yaml_type
- "!set"
- end
+ class Set < ::Hash
+ yaml_as "tag:yaml.org,2002:set"
end
-
- YAML.add_builtin_type( 'set' ) { |type, val|
- if Array === val
- val = Set[ *val ]
- elsif Hash === val
- Set[ val ]
- else
- raise YAML::Error, "Invalid map explicitly tagged !map: " + val.inspect
- end
- val
- }
-
end