diff options
author | why <why@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2004-05-06 06:29:56 +0000 |
---|---|---|
committer | why <why@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2004-05-06 06:29:56 +0000 |
commit | dd70745ec9c7bf9e57eccabbcb20c1de39166119 (patch) | |
tree | 0f783680b6f2a9d2cd9848b98bfd0bce55d81993 /lib/yaml | |
parent | 87e29e40db6e0743c54fbfc07f30397682e81a59 (diff) | |
download | ruby-dd70745ec9c7bf9e57eccabbcb20c1de39166119.tar.gz ruby-dd70745ec9c7bf9e57eccabbcb20c1de39166119.tar.xz ruby-dd70745ec9c7bf9e57eccabbcb20c1de39166119.zip |
* lib/yaml/rubytypes.rb (to_yaml): added instance variable handling
for Ranges, Strings, Structs, Regexps.
* lib/yaml/rubytypes.rb (to_yaml_fold): new method for setting a
String's flow style.
* lib/yaml.rb (YAML::object_maker): now uses Object.allocate.
* ext/syck/gram.c: fixed transfer methods on structs, broke it
last commit.
git-svn-id: http://svn.ruby-lang.org/repos/ruby/trunk@6252 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib/yaml')
-rw-r--r-- | lib/yaml/baseemitter.rb | 46 | ||||
-rw-r--r-- | lib/yaml/rubytypes.rb | 212 |
2 files changed, 190 insertions, 68 deletions
diff --git a/lib/yaml/baseemitter.rb b/lib/yaml/baseemitter.rb index 007ee7be5..1072f7553 100644 --- a/lib/yaml/baseemitter.rb +++ b/lib/yaml/baseemitter.rb @@ -36,28 +36,36 @@ module YAML def node_text( value, block = '>' ) @seq_map = false valx = value.dup - if options(:UseBlock) - block = '|' - elsif not options(:UseFold) and valx =~ /\n[ \t]/ and not valx =~ /#{YAML::ESCAPE_CHAR}/ - block = '|' - end - str = block.dup - if valx =~ /\n\Z\n/ - str << "+" - elsif valx =~ /\Z\n/ - else - str << "-" - end + unless block + block = + if options(:UseBlock) + '|' + elsif not options(:UseFold) and valx =~ /\n[ \t]/ and not valx =~ /#{YAML::ESCAPE_CHAR}/ + '|' + else + '>' + end + block += + if valx =~ /\n\Z\n/ + "+" + elsif valx =~ /\Z\n/ + "" + else + "-" + end + if valx =~ /\A[ \t#]/ + block += options(:Indent).to_s + end + end if valx =~ /#{YAML::ESCAPE_CHAR}/ valx = YAML::escape( valx ) end - if valx =~ /\A[ \t#]/ - str << options(:Indent).to_s - end - if block == '>' + if block[0] == ?> valx = fold( valx ) end - self << str + indent_text( valx ) + "\n" + indt = nil + indt = $&.to_i if block =~ /\d+/ + self << block + indent_text( valx, indt ) + "\n" end # @@ -85,9 +93,9 @@ module YAML # # Write a text block with the current indent # - def indent_text( text ) + def indent_text( text, indt = nil ) return "" if text.to_s.empty? - spacing = " " * ( level * options(:Indent) ) + spacing = " " * ( level * ( indt || options(:Indent) ) ) return "\n" + text.gsub( /^([^\n])/, "#{spacing}\\1" ) end diff --git a/lib/yaml/rubytypes.rb b/lib/yaml/rubytypes.rb index 7d36b468d..adebf9439 100644 --- a/lib/yaml/rubytypes.rb +++ b/lib/yaml/rubytypes.rb @@ -6,7 +6,7 @@ require 'date' class Class def to_yaml( opts = {} ) - raise ArgumentError, "can't dump anonymous class %s" % self.class + raise TypeError, "can't dump anonymous class %s" % self.class end end @@ -161,6 +161,9 @@ class Struct self.members.each { |m| map.add( m, self[m] ) } + self.to_yaml_properties.each { |m| + map.add( m, instance_variable_get( m ) ) + } } } end @@ -173,6 +176,8 @@ YAML.add_ruby_type( /^struct/ ) { |type, val| # # 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 @@ -185,10 +190,13 @@ YAML.add_ruby_type( /^struct/ ) { |type, val| # # Set the Struct properties # - st = struct_type.new + 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 @@ -276,11 +284,17 @@ YAML.add_ruby_type( /^exception/ ) { |type, val| # class String def is_complex_yaml? - ( self =~ /\n.+/ ? true : false ) + to_yaml_fold 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#{ if self.class != ::String; ":#{ self.class }"; end }" + end + def to_yaml_fold + nil + end def to_yaml( opts = {} ) complex = false if self.is_complex_yaml? @@ -292,12 +306,19 @@ class String end YAML::quick_emit( complex ? self.object_id : nil, opts ) { |out| if complex - if self.is_binary_data? + 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}| $/ complex = false else - out.node_text( self ) + out.node_text( self, to_yaml_fold ) end end if not complex @@ -318,6 +339,21 @@ class String 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 # @@ -335,6 +371,7 @@ 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 @@ -348,102 +385,179 @@ YAML.add_ruby_type( 'sym', &symbol_proc ) # class Range def is_complex_yaml? - false + true + end + def to_yaml_type + "!ruby/range#{ if self.class != ::Range; ":#{ self.class }"; end }" end def to_yaml( opts = {} ) - YAML::quick_emit( nil, opts ) { |out| - out << "!ruby/range '" - self.begin.to_yaml(:Emitter => out) - out << ( self.exclude_end? ? "..." : ".." ) - self.end.to_yaml(:Emitter => out) - out << "'" + YAML::quick_emit( self.object_id, opts ) { |out| + if self.begin.is_complex_yaml? or self.end.is_complex_yaml? or not to_yaml_properties.empty? + out.map( to_yaml_type ) { |map| + map.add( 'begin', self.begin ) + map.add( 'end', self.end ) + map.add( 'excl', self.exclude_end? ) + to_yaml_properties.each { |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 -YAML.add_ruby_type( 'range' ) { |type, val| +YAML.add_ruby_type( /^range/ ) { |type, val| + type, obj_class = YAML.read_type_class( type, ::Range ) inr = '(\w+|[+-]*\d+(?:\.\d+)?|"(?:[^\\"]|\\.)*")' + opts = {} if String === val and val =~ /^#{inr}(\.{2,3})#{inr}$/ r1, rdots, r2 = $1, $2, $3 - Range.new( YAML.load( "--- #{r1}" ), YAML.load( "--- #{r2}" ), rdots.length == 3 ) - elsif Hash === val - Range.new( val['begin'], val['end'], val['exclude_end?'] ) + 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 +# Make an Regexp # class Regexp def is_complex_yaml? - false + self.class != Regexp or not to_yaml_properties.empty? + end + def to_yaml_type + "!ruby/regexp#{ if self.class != ::Regexp; ":#{ self.class }"; end }" end def to_yaml( opts = {} ) YAML::quick_emit( nil, opts ) { |out| - out << "!ruby/regexp " - self.inspect.to_yaml( :Emitter => out ) + if self.is_complex_yaml? + out.map( self.to_yaml_type ) { |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 + end + to_yaml_properties.each { |m| + map.add( m, instance_variable_get( m ) ) + } + } + else + out << "#{ to_yaml_type } " + self.inspect.to_yaml( :Emitter => out ) + 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, 'MODIFIERS' => $2 } + val = { 'regexp' => $1, 'mods' => $2 } end if Hash === val mods = nil - unless val['MODIFIERS'].to_s.empty? + unless val['mods'].to_s.empty? mods = 0x00 - if val['MODIFIERS'].include?( 'x' ) - mods |= Regexp::EXTENDED - elsif val['MODIFIERS'].include?( 'i' ) - mods |= Regexp::IGNORECASE - elsif val['MODIFIERS'].include?( 'm' ) - mods |= Regexp::POSIXLINE - end + 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 - Regexp::compile( val['REGEXP'], mods ) + 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/, ®exp_proc ) -YAML.add_ruby_type( 'regexp', ®exp_proc ) +YAML.add_ruby_type( /^regexp/, ®exp_proc ) # # Emit a Time object as an ISO 8601 timestamp # class Time def is_complex_yaml? - false + self.class != Time or not to_yaml_properties.empty? + end + def to_yaml_type + "!ruby/time#{ if self.class != ::Time; ":#{ self.class }"; end }" end def to_yaml( opts = {} ) YAML::quick_emit( nil, opts ) { |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 + 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 ) ) + } + } + 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 + end + difference_minutes = (absolute_difference/60).round + tz = "%s%02d:%02d" % [ difference_sign, difference_minutes / 60, difference_minutes % 60] end - difference_minutes = (absolute_difference/60).round - tz = "%s%02d:%02d" % [ difference_sign, difference_minutes / 60, difference_minutes % 60] + 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 - ( self.strftime( "%Y-%m-%d %H:%M:%S." ) + - "%06d %s" % [usec, tz] ). - to_yaml( :Emitter => out, :KeepValue => true ) } 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 # |