# -*- mode: ruby; ruby-indent-level: 4; tab-width: 4 -*- vim: sw=4 ts=4 require 'date' class Class def to_yaml( opts = {} ) raise TypeError, "can't dump anonymous class %s" % self.class end end class Object 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, 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 class Hash 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 raise YAML::TypeError, "Invalid map explicitly tagged #{ tag }: " + val.inspect end end def to_yaml( opts = {} ) YAML::quick_emit( self, opts ) do |out| out.map( taguri, to_yaml_style ) do |map| each do |k, v| map.add( k, v ) end end end end end class Struct 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, opts ) do |out| # # Basic struct is passed as a YAML map # out.map( taguri, to_yaml_style ) do |map| self.members.each do |m| map.add( m, self[m] ) end self.to_yaml_properties.each do |m| map.add( m, instance_variable_get( m ) ) end end end end end class Array 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 = {} ) YAML::quick_emit( self, opts ) do |out| out.seq( taguri, to_yaml_style ) do |seq| each do |x| seq.add( x ) end end end end end class Exception 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, 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 class String yaml_as "tag:ruby.yaml.org,2002:string" yaml_as "tag:yaml.org,2002:binary" yaml_as "tag:yaml.org,2002:str" def is_complex_yaml? 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 ) unless empty? end def String.yaml_new( klass, tag, val ) val = val.unpack("m")[0] if tag == "tag:yaml.org,2002:binary" 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 = {} ) YAML::quick_emit( is_complex_yaml? ? self : 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, self =~ /^:/ ? :quote2 : to_yaml_style ) else 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 class Symbol yaml_as "tag:ruby.yaml.org,2002:symbol" yaml_as "tag:ruby.yaml.org,2002:sym" def Symbol.yaml_new( klass, tag, val ) if String === val val = YAML::load( val ) if val =~ /\A(["']).*\1\z/ val.intern else raise YAML::TypeError, "Invalid Symbol: " + val.inspect end end def to_yaml( opts = {} ) YAML::quick_emit( nil, opts ) do |out| out.scalar( "tag:yaml.org,2002:str", self.inspect, :plain ) end end end class Range 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, 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 do |m| map.add( m, instance_variable_get( m ) ) 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 class Regexp 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 ) 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::TypeError, "Invalid Regular expression: " + src end to_yaml_properties.each do |m| map.add( m, instance_variable_get( m ) ) end end end end end end class Time 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( self, opts ) do |out| tz = "Z" # from the tidy Tobias Peters 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 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 end end end end end class Date yaml_as "tag:yaml.org,2002:timestamp#ymd" def to_yaml( opts = {} ) YAML::quick_emit( self, opts ) do |out| out.scalar( "tag:yaml.org,2002:timestamp", self.to_s, :plain ) end end end class Integer yaml_as "tag:yaml.org,2002:int" def to_yaml( opts = {} ) YAML::quick_emit( nil, opts ) do |out| out.scalar( "tag:yaml.org,2002:int", self.to_s, :plain ) end end end class Float yaml_as "tag:yaml.org,2002:float" def 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( "tag:yaml.org,2002:float", str, :plain ) end end end class TrueClass yaml_as "tag:yaml.org,2002:bool#yes" def to_yaml( opts = {} ) YAML::quick_emit( nil, opts ) do |out| out.scalar( taguri, "true", :plain ) end end end class FalseClass yaml_as "tag:yaml.org,2002:bool#no" def to_yaml( opts = {} ) YAML::quick_emit( nil, opts ) do |out| out.scalar( taguri, "false", :plain ) end end end class NilClass yaml_as "tag:yaml.org,2002:null" def to_yaml( opts = {} ) YAML::quick_emit( nil, opts ) do |out| out.scalar( taguri, "", :plain ) end end end