From eb71e5cd67f4ce4da5ba331f93a8e36c65457b94 Mon Sep 17 00:00:00 2001 From: tenderlove Date: Sat, 3 Apr 2010 21:50:47 +0000 Subject: * lib/yaml: Moved to ext/syck/lib, Syck only uses Syck constant. * lib/yaml.rb: Added an engine manager for choosing YAML engine. * ext/syck/lib/syck/rubytypes.rb: squashed warnings when using Psych git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@27212 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ext/syck/lib/syck/baseemitter.rb | 242 ++++++++++++++++++++ ext/syck/lib/syck/basenode.rb | 223 +++++++++++++++++++ ext/syck/lib/syck/constants.rb | 45 ++++ ext/syck/lib/syck/dbm.rb | 111 ++++++++++ ext/syck/lib/syck/encoding.rb | 35 +++ ext/syck/lib/syck/error.rb | 34 +++ ext/syck/lib/syck/loader.rb | 14 ++ ext/syck/lib/syck/rubytypes.rb | 464 +++++++++++++++++++++++++++++++++++++++ ext/syck/lib/syck/store.rb | 43 ++++ ext/syck/lib/syck/stream.rb | 41 ++++ ext/syck/lib/syck/stringio.rb | 85 +++++++ ext/syck/lib/syck/syck.rb | 17 ++ ext/syck/lib/syck/tag.rb | 92 ++++++++ ext/syck/lib/syck/types.rb | 192 ++++++++++++++++ ext/syck/lib/syck/yamlnode.rb | 54 +++++ ext/syck/lib/syck/ypath.rb | 54 +++++ 16 files changed, 1746 insertions(+) create mode 100644 ext/syck/lib/syck/baseemitter.rb create mode 100644 ext/syck/lib/syck/basenode.rb create mode 100644 ext/syck/lib/syck/constants.rb create mode 100644 ext/syck/lib/syck/dbm.rb create mode 100644 ext/syck/lib/syck/encoding.rb create mode 100644 ext/syck/lib/syck/error.rb create mode 100644 ext/syck/lib/syck/loader.rb create mode 100644 ext/syck/lib/syck/rubytypes.rb create mode 100644 ext/syck/lib/syck/store.rb create mode 100644 ext/syck/lib/syck/stream.rb create mode 100644 ext/syck/lib/syck/stringio.rb create mode 100644 ext/syck/lib/syck/syck.rb create mode 100644 ext/syck/lib/syck/tag.rb create mode 100644 ext/syck/lib/syck/types.rb create mode 100644 ext/syck/lib/syck/yamlnode.rb create mode 100644 ext/syck/lib/syck/ypath.rb (limited to 'ext/syck/lib/syck') diff --git a/ext/syck/lib/syck/baseemitter.rb b/ext/syck/lib/syck/baseemitter.rb new file mode 100644 index 0000000000..5e39e450de --- /dev/null +++ b/ext/syck/lib/syck/baseemitter.rb @@ -0,0 +1,242 @@ +# +# BaseEmitter +# + +require 'syck/constants' +require 'syck/encoding' +require 'syck/error' + +module Syck + module BaseEmitter + def options( opt = nil ) + if opt + @options[opt] || DEFAULTS[opt] + else + @options + end + end + + def options=( opt ) + @options = opt + end + + # + # Emit binary data + # + def binary_base64( value ) + self << "!binary " + self.node_text( [value].pack("m"), '|' ) + end + + # + # Emit plain, normal flowing text + # + def node_text( value, block = nil ) + @seq_map = false + valx = value.dup + unless block + block = + if options(:UseBlock) + '|' + elsif not options(:UseFold) and valx =~ /\n[ \t]/ and not valx =~ /#{ESCAPE_CHAR}/ + '|' + else + '>' + end + indt = $&.to_i if block =~ /\d+/ + if valx =~ /(\A\n*[ \t#]|^---\s+)/ + indt = options(:Indent) unless indt.to_i > 0 + block += indt.to_s + end + + block += + if valx =~ /\n\Z\n/ + "+" + elsif valx =~ /\Z\n/ + "" + else + "-" + end + end + block += "\n" + if block[0] == ?" + esc_skip = ( "\t\n" unless valx =~ /^[ \t]/ ) || "" + valx = fold( Syck.escape( valx, esc_skip ) + "\"" ).chomp + self << '"' + indent_text( valx, indt, false ) + else + if block[0] == ?> + valx = fold( valx ) + end + #p [block, indt] + self << block + indent_text( valx, indt ) + end + end + + # + # Emit a simple, unqouted string + # + def simple( value ) + @seq_map = false + self << value.to_s + end + + # + # Emit double-quoted string + # + def double( value ) + "\"#{Syck.escape( value )}\"" + end + + # + # Emit single-quoted string + # + def single( value ) + "'#{value}'" + end + + # + # Write a text block with the current indent + # + def indent_text( text, mod, first_line = true ) + return "" if text.to_s.empty? + spacing = indent( mod ) + text = text.gsub( /\A([^\n])/, "#{ spacing }\\1" ) if first_line + return text.gsub( /\n^([^\n])/, "\n#{spacing}\\1" ) + end + + # + # Write a current indent + # + def indent( mod = nil ) + #p [ self.id, level, mod, :INDENT ] + if level <= 0 + mod ||= 0 + else + mod ||= options(:Indent) + mod += ( level - 1 ) * options(:Indent) + end + return " " * mod + end + + # + # Add indent to the buffer + # + def indent! + self << indent + end + + # + # Folding paragraphs within a column + # + def fold( value ) + value.gsub( /(^[ \t]+.*$)|(\S.{0,#{options(:BestWidth) - 1}})(?:[ \t]+|(\n+(?=[ \t]|\Z))|$)/ ) do + $1 || $2 + ( $3 || "\n" ) + end + end + + # + # Quick mapping + # + def map( type, &e ) + val = Mapping.new + e.call( val ) + self << "#{type} " if type.length.nonzero? + + # + # Empty hashes + # + if val.length.zero? + self << "{}" + @seq_map = false + else + # FIXME + # if @buffer.length == 1 and options(:UseHeader) == false and type.length.zero? + # @headless = 1 + # end + + defkey = @options.delete( :DefaultKey ) + if defkey + seq_map_shortcut + self << "= : " + defkey.to_yaml( :Emitter => self ) + end + + # + # Emit the key and value + # + val.each { |v| + seq_map_shortcut + if v[0].is_complex_yaml? + self << "? " + end + v[0].to_yaml( :Emitter => self ) + if v[0].is_complex_yaml? + self << "\n" + indent! + end + self << ": " + v[1].to_yaml( :Emitter => self ) + } + end + end + + def seq_map_shortcut + # FIXME: seq_map needs to work with the new anchoring system + # if @seq_map + # @anchor_extras[@buffer.length - 1] = "\n" + indent + # @seq_map = false + # else + self << "\n" + indent! + # end + end + + # + # Quick sequence + # + def seq( type, &e ) + @seq_map = false + val = Sequence.new + e.call( val ) + self << "#{type} " if type.length.nonzero? + + # + # Empty arrays + # + if val.length.zero? + self << "[]" + else + # FIXME + # if @buffer.length == 1 and options(:UseHeader) == false and type.length.zero? + # @headless = 1 + # end + + # + # Emit the key and value + # + val.each { |v| + self << "\n" + indent! + self << "- " + @seq_map = true if v.class == Hash + v.to_yaml( :Emitter => self ) + } + end + end + end + + # + # Emitter helper classes + # + class Mapping < Array + def add( k, v ) + push [k, v] + end + end + + class Sequence < Array + def add( v ) + push v + end + end +end diff --git a/ext/syck/lib/syck/basenode.rb b/ext/syck/lib/syck/basenode.rb new file mode 100644 index 0000000000..fc701a99f3 --- /dev/null +++ b/ext/syck/lib/syck/basenode.rb @@ -0,0 +1,223 @@ +# +# YAML::BaseNode class +# + +module Syck + + # + # YAML Generic Model container + # + module BaseNode + + # + # Search for YPath entry and return + # qualified nodes. + # + def select( ypath_str ) + warn "#{caller[0]}: select is deprecated" if $VERBOSE + matches = match_path( ypath_str ) + + # + # Create a new generic view of the elements selected + # + if matches + result = [] + matches.each { |m| + result.push m.last + } + Syck.transfer( 'seq', result ) + end + end + + # + # Search for YPath entry and return + # transformed nodes. + # + def select!( ypath_str ) + warn "#{caller[0]}: select!() is deprecated" if $VERBOSE + matches = match_path( ypath_str ) + + # + # Create a new generic view of the elements selected + # + if matches + result = [] + matches.each { |m| + result.push m.last.transform + } + result + end + end + + # + # Search for YPath entry and return a list of + # qualified paths. + # + def search( ypath_str ) + warn "#{caller[0]}: search() is deprecated" if $VERBOSE + matches = match_path( ypath_str ) + + if matches + matches.collect { |m| + path = [] + m.each_index { |i| + path.push m[i] if ( i % 2 ).zero? + } + "/" + path.compact.join( "/" ) + } + end + end + + def at( seg ) + warn "#{caller[0]}: at() is deprecated" if $VERBOSE + if Hash === @value + self[seg] + elsif Array === @value and seg =~ /\A\d+\Z/ and @value[seg.to_i] + @value[seg.to_i] + end + end + + # + # YPath search returning a complete depth array + # + def match_path( ypath_str ) + warn "#{caller[0]}: match_path is deprecated" if $VERBOSE + require 'yaml/ypath' + depth = 0 + matches = [] + YPath.each_path( ypath_str ) do |ypath| + seg = match_segment( ypath, 0 ) + matches += seg if seg + end + matches.uniq + end + + # + # Search a node for a single YPath segment + # + def match_segment( ypath, depth ) + warn "#{caller[0]}: match_segment is deprecated" if $VERBOSE + deep_nodes = [] + seg = ypath.segments[ depth ] + if seg == "/" + unless String === @value + idx = -1 + @value.collect { |v| + idx += 1 + if Hash === @value + match_init = [v[0].transform, v[1]] + match_deep = v[1].match_segment( ypath, depth ) + else + match_init = [idx, v] + match_deep = v.match_segment( ypath, depth ) + end + if match_deep + match_deep.each { |m| + deep_nodes.push( match_init + m ) + } + end + } + end + depth += 1 + seg = ypath.segments[ depth ] + end + match_nodes = + case seg + when "." + [[nil, self]] + when ".." + [["..", nil]] + when "*" + if @value.is_a? Enumerable + idx = -1 + @value.collect { |h| + idx += 1 + if Hash === @value + [h[0].transform, h[1]] + else + [idx, h] + end + } + end + else + if seg =~ /^"(.*)"$/ + seg = $1 + elsif seg =~ /^'(.*)'$/ + seg = $1 + end + if ( v = at( seg ) ) + [[ seg, v ]] + end + end + return deep_nodes unless match_nodes + pred = ypath.predicates[ depth ] + if pred + case pred + when /^\.=/ + pred = $' # ' + match_nodes.reject! { |n| + n.last.value != pred + } + else + match_nodes.reject! { |n| + n.last.at( pred ).nil? + } + end + end + return match_nodes + deep_nodes unless ypath.segments.length > depth + 1 + + #puts "DEPTH: #{depth + 1}" + deep_nodes = [] + match_nodes.each { |n| + if n[1].is_a? BaseNode + match_deep = n[1].match_segment( ypath, depth + 1 ) + if match_deep + match_deep.each { |m| + deep_nodes.push( n + m ) + } + end + else + deep_nodes = [] + end + } + deep_nodes = nil if deep_nodes.length == 0 + deep_nodes + end + + # + # We want the node to act like as Hash + # if it is. + # + def []( *key ) + if Hash === @value + v = @value.detect { |k,| k.transform == key.first } + v[1] if v + elsif Array === @value + @value.[]( *key ) + end + end + + def children + if Hash === @value + @value.values.collect { |c| c[1] } + elsif Array === @value + @value + end + end + + def children_with_index + warn "#{caller[0]}: children_with_index is deprecated, use children" if $VERBOSE + if Hash === @value + @value.keys.collect { |i| [self[i], i] } + elsif Array === @value + i = -1; @value.collect { |v| i += 1; [v, i] } + end + end + + def emit + transform.to_yaml + end + end + +end + diff --git a/ext/syck/lib/syck/constants.rb b/ext/syck/lib/syck/constants.rb new file mode 100644 index 0000000000..19fe42ef85 --- /dev/null +++ b/ext/syck/lib/syck/constants.rb @@ -0,0 +1,45 @@ +# +# Constants used throughout the library +# +module Syck + + # + # Constants + # + VERSION = '0.60' + SUPPORTED_YAML_VERSIONS = ['1.0'] + + # + # Parser tokens + # + WORD_CHAR = 'A-Za-z0-9' + PRINTABLE_CHAR = '-_A-Za-z0-9!?/()$\'". ' + NOT_PLAIN_CHAR = '\x7f\x0-\x1f\x80-\x9f' + ESCAPE_CHAR = '[\\x00-\\x09\\x0b-\\x1f]' + INDICATOR_CHAR = '*&!|\\\\^@%{}[]=' + SPACE_INDICATORS = '-#:,?' + RESTRICTED_INDICATORS = '#:,}]' + DNS_COMP_RE = "\\w(?:[-\\w]*\\w)?" + DNS_NAME_RE = "(?:(?:#{DNS_COMP_RE}\\.)+#{DNS_COMP_RE}|#{DNS_COMP_RE})" + ESCAPES = %w{\x00 \x01 \x02 \x03 \x04 \x05 \x06 \a + \x08 \t \n \v \f \r \x0e \x0f + \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 + \x18 \x19 \x1a \e \x1c \x1d \x1e \x1f + } + UNESCAPES = { + 'a' => "\x07", 'b' => "\x08", 't' => "\x09", + 'n' => "\x0a", 'v' => "\x0b", 'f' => "\x0c", + 'r' => "\x0d", 'e' => "\x1b", '\\' => '\\', + } + + # + # Default settings + # + DEFAULTS = { + :Indent => 2, :UseHeader => false, :UseVersion => false, :Version => '1.0', + :SortKeys => false, :AnchorFormat => 'id%03d', :ExplicitTypes => false, + :WidthType => 'absolute', :BestWidth => 80, + :UseBlock => false, :UseFold => false, :Encoding => :None + } + +end diff --git a/ext/syck/lib/syck/dbm.rb b/ext/syck/lib/syck/dbm.rb new file mode 100644 index 0000000000..95611afb04 --- /dev/null +++ b/ext/syck/lib/syck/dbm.rb @@ -0,0 +1,111 @@ +require 'syck' +require 'dbm' +# +# YAML + DBM = YDBM +# - Same interface as DBM class +# +module Syck + +class DBM < ::DBM + VERSION = "0.1" + def []( key ) + fetch( key ) + end + def []=( key, val ) + store( key, val ) + end + def fetch( keystr, ifnone = nil ) + begin + val = super( keystr ) + return Syck.load( val ) if String === val + rescue IndexError + end + if block_given? + yield keystr + else + ifnone + end + end + def index( keystr ) + super( keystr.to_yaml ) + end + def values_at( *keys ) + keys.collect { |k| fetch( k ) } + end + def delete( key ) + v = super( key ) + if String === v + v = Syck.load( v ) + end + v + end + def delete_if + del_keys = keys.dup + del_keys.delete_if { |k| yield( k, fetch( k ) ) == false } + del_keys.each { |k| delete( k ) } + self + end + def reject + hsh = self.to_hash + hsh.reject { |k,v| yield k, v } + end + def each_pair + keys.each { |k| yield k, fetch( k ) } + self + end + def each_value + super { |v| yield Syck.load( v ) } + self + end + def values + super.collect { |v| Syck.load( v ) } + end + def has_value?( val ) + each_value { |v| return true if v == val } + return false + end + def invert + h = {} + keys.each { |k| h[ self.fetch( k ) ] = k } + h + end + def replace( hsh ) + clear + update( hsh ) + end + def shift + a = super + a[1] = Syck.load( a[1] ) if a + a + end + def select( *keys ) + if block_given? + self.keys.collect { |k| v = self[k]; [k, v] if yield k, v }.compact + else + values_at( *keys ) + end + end + def store( key, val ) + super( key, val.to_yaml ) + val + end + def update( hsh ) + hsh.keys.each do |k| + self.store( k, hsh.fetch( k ) ) + end + self + end + def to_a + a = [] + keys.each { |k| a.push [ k, self.fetch( k ) ] } + a + end + def to_hash + h = {} + keys.each { |k| h[ k ] = self.fetch( k ) } + h + end + alias :each :each_pair +end + +end diff --git a/ext/syck/lib/syck/encoding.rb b/ext/syck/lib/syck/encoding.rb new file mode 100644 index 0000000000..dad062994c --- /dev/null +++ b/ext/syck/lib/syck/encoding.rb @@ -0,0 +1,35 @@ +# +# Handle Unicode-to-Internal conversion +# + +module Syck + + # + # Escape the string, condensing common escapes + # + def self.escape( value, skip = "" ) + warn "#{caller[0]}: YAML.escape is deprecated" if $VERBOSE + value.gsub( /\\/, "\\\\\\" ). + gsub( /"/, "\\\"" ). + gsub( /([\x00-\x1f])/ ) do + skip[$&] || ESCAPES[ $&.unpack("C")[0] ] + end + end + + # + # Unescape the condenses escapes + # + def self.unescape( value ) + warn "#{caller[0]}: YAML.unescape is deprecated" if $VERBOSE + value.gsub( /\\(?:([nevfbart\\])|0?x([0-9a-fA-F]{2})|u([0-9a-fA-F]{4}))/ ) { + if $3 + ["#$3".hex ].pack('U*') + elsif $2 + [$2].pack( "H2" ) + else + UNESCAPES[$1] + end + } + end + +end diff --git a/ext/syck/lib/syck/error.rb b/ext/syck/lib/syck/error.rb new file mode 100644 index 0000000000..0bac872411 --- /dev/null +++ b/ext/syck/lib/syck/error.rb @@ -0,0 +1,34 @@ +# +# Error messages and exception class +# + +module Syck + + # + # Error messages + # + + ERROR_NO_HEADER_NODE = "With UseHeader=false, the node Array or Hash must have elements" + ERROR_NEED_HEADER = "With UseHeader=false, the node must be an Array or Hash" + ERROR_BAD_EXPLICIT = "Unsupported explicit transfer: '%s'" + ERROR_MANY_EXPLICIT = "More than one explicit transfer" + ERROR_MANY_IMPLICIT = "More than one implicit request" + ERROR_NO_ANCHOR = "No anchor for alias '%s'" + ERROR_BAD_ANCHOR = "Invalid anchor: %s" + ERROR_MANY_ANCHOR = "More than one anchor" + ERROR_ANCHOR_ALIAS = "Can't define both an anchor and an alias" + ERROR_BAD_ALIAS = "Invalid alias: %s" + ERROR_MANY_ALIAS = "More than one alias" + ERROR_ZERO_INDENT = "Can't use zero as an indentation width" + ERROR_UNSUPPORTED_VERSION = "This release of YAML.rb does not support YAML version %s" + ERROR_UNSUPPORTED_ENCODING = "Attempt to use unsupported encoding: %s" + + # + # YAML Error classes + # + + class Error < StandardError; end + class ParseError < Error; end + class TypeError < StandardError; end + +end diff --git a/ext/syck/lib/syck/loader.rb b/ext/syck/lib/syck/loader.rb new file mode 100644 index 0000000000..925c9ee4b2 --- /dev/null +++ b/ext/syck/lib/syck/loader.rb @@ -0,0 +1,14 @@ +# +# YAML::Loader class +# .. type handling .. +# +module Syck + class Loader + TRANSFER_DOMAINS = { + 'yaml.org,2002' => {}, + 'ruby.yaml.org,2002' => {} + } + PRIVATE_TYPES = {} + IMPLICIT_TYPES = [ 'null', 'bool', 'time', 'int', 'float' ] + end +end diff --git a/ext/syck/lib/syck/rubytypes.rb b/ext/syck/lib/syck/rubytypes.rb new file mode 100644 index 0000000000..6b2937bba0 --- /dev/null +++ b/ext/syck/lib/syck/rubytypes.rb @@ -0,0 +1,464 @@ +# -*- 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 + alias :syck_to_yaml :to_yaml +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 = {} ) + return super unless YAML::ENGINE.syck? + 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.to_s] ) + 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 = {} ) + return super unless YAML::ENGINE.syck? + 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.to_s, self[m.to_s] ) + 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 = {} ) + return super unless YAML::ENGINE.syck? + 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 = {} ) + return super unless YAML::ENGINE.syck? + 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("^ -~\t\r\n").fdiv(self.size) > 0.3 || self.index("\x00") unless self.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 = {} ) + return super unless YAML::ENGINE.syck? + 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 = {} ) + return super unless YAML::ENGINE.syck? + 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 = {} ) + return super unless YAML::ENGINE.syck? + 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 = {} ) + return super unless YAML::ENGINE.syck? + 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 = {} ) + return super unless YAML::ENGINE.syck? + 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 = {} ) + return super unless YAML::ENGINE.syck? + 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 = {} ) + return super unless YAML::ENGINE.syck? + 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 = {} ) + return super unless YAML::ENGINE.syck? + 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 Rational + yaml_as "tag:ruby.yaml.org,2002:object:Rational" + def Rational.yaml_new( klass, tag, val ) + if val.is_a? String + Rational( val ) + else + Rational( val['numerator'], val['denominator'] ) + end + end + def to_yaml( opts = {} ) + return super unless YAML::ENGINE.syck? + YAML::quick_emit( self, opts ) do |out| + out.map( taguri, nil ) do |map| + map.add( 'denominator', denominator ) + map.add( 'numerator', numerator ) + end + end + end +end + +class Complex + yaml_as "tag:ruby.yaml.org,2002:object:Complex" + def Complex.yaml_new( klass, tag, val ) + if val.is_a? String + Complex( val ) + else + Complex( val['real'], val['image'] ) + end + end + def to_yaml( opts = {} ) + return super unless YAML::ENGINE.syck? + YAML::quick_emit( self, opts ) do |out| + out.map( taguri, nil ) do |map| + map.add( 'image', imaginary ) + map.add( 'real', real ) + end + end + end +end + +class TrueClass + yaml_as "tag:yaml.org,2002:bool#yes" + def to_yaml( opts = {} ) + return super unless YAML::ENGINE.syck? + 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 = {} ) + return super unless YAML::ENGINE.syck? + 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 = {} ) + return super unless YAML::ENGINE.syck? + YAML::quick_emit( nil, opts ) do |out| + out.scalar( taguri, "", :plain ) + end + end +end + diff --git a/ext/syck/lib/syck/store.rb b/ext/syck/lib/syck/store.rb new file mode 100644 index 0000000000..8f27cf49c0 --- /dev/null +++ b/ext/syck/lib/syck/store.rb @@ -0,0 +1,43 @@ +# +# YAML::Store +# +require 'syck' +require 'pstore' + +class Syck::Store < PStore + def initialize( *o ) + @opt = Syck::DEFAULTS.dup + if String === o.first + super(o.shift) + end + if o.last.is_a? Hash + @opt.update(o.pop) + end + end + + def dump(table) + @table.to_yaml(@opt) + end + + def load(content) + table = Syck::load(content) + if table == false + {} + else + table + end + end + + def marshal_dump_supports_canonical_option? + false + end + + EMPTY_MARSHAL_DATA = {}.to_yaml + EMPTY_MARSHAL_CHECKSUM = Digest::MD5.digest(EMPTY_MARSHAL_DATA) + def empty_marshal_data + EMPTY_MARSHAL_DATA + end + def empty_marshal_checksum + EMPTY_MARSHAL_CHECKSUM + end +end diff --git a/ext/syck/lib/syck/stream.rb b/ext/syck/lib/syck/stream.rb new file mode 100644 index 0000000000..cd77a033c6 --- /dev/null +++ b/ext/syck/lib/syck/stream.rb @@ -0,0 +1,41 @@ +module Syck + + # + # YAML::Stream -- for emitting many documents + # + class Stream + + attr_accessor :documents, :options + + def initialize( opts = {} ) + @options = opts + @documents = [] + end + + def []( i ) + @documents[ i ] + end + + def add( doc ) + @documents << doc + end + + def edit( doc_num, doc ) + warn "#{caller[0]}: edit is deprecated" if $VERBOSE + @documents[ doc_num ] = doc + end + + def emit( io = nil ) + # opts = @options.dup + # opts[:UseHeader] = true if @documents.length > 1 + out = Syck.emitter + out.reset( io || io2 = StringIO.new ) + @documents.each { |v| + v.to_yaml( out ) + } + io || ( io2.rewind; io2.read ) + end + + end + +end diff --git a/ext/syck/lib/syck/stringio.rb b/ext/syck/lib/syck/stringio.rb new file mode 100644 index 0000000000..77a2b827e5 --- /dev/null +++ b/ext/syck/lib/syck/stringio.rb @@ -0,0 +1,85 @@ +warn "#{caller[0]}: yaml/stringio is deprecated" if $VERBOSE + +# +# Limited StringIO if no core lib is available +# +begin +require 'stringio' +rescue LoadError + # StringIO based on code by MoonWolf + class StringIO + def initialize(string="") + @string=string + @pos=0 + @eof=(string.size==0) + end + def pos + @pos + end + def eof + @eof + end + alias eof? eof + def readline(rs=$/) + if @eof + raise EOFError + else + if p = @string[@pos..-1]=~rs + line = @string[@pos,p+1] + else + line = @string[@pos..-1] + end + @pos+=line.size + @eof =true if @pos==@string.size + $_ = line + end + end + def rewind + seek(0,0) + end + def seek(offset,whence) + case whence + when 0 + @pos=offset + when 1 + @pos+=offset + when 2 + @pos=@string.size+offset + end + @eof=(@pos>=@string.size) + 0 + end + end + + # + # Class method for creating streams + # + def Syck.make_stream( io ) + if String === io + io = StringIO.new( io ) + elsif not IO === io + raise Syck::Error, "YAML stream must be an IO or String object." + end + if Syck::unicode + def io.readline + Syck.utf_to_internal( readline( @ln_sep ), @utf_encoding ) + end + def io.check_unicode + @utf_encoding = Syck.sniff_encoding( read( 4 ) ) + @ln_sep = Syck.enc_separator( @utf_encoding ) + seek( -4, IO::SEEK_CUR ) + end + def io.utf_encoding + @utf_encoding + end + io.check_unicode + else + def io.utf_encoding + :None + end + end + io + end + +end + diff --git a/ext/syck/lib/syck/syck.rb b/ext/syck/lib/syck/syck.rb new file mode 100644 index 0000000000..2bd44e9f2a --- /dev/null +++ b/ext/syck/lib/syck/syck.rb @@ -0,0 +1,17 @@ +# +# YAML::Syck module +# .. glues syck and yaml.rb together .. +# +require 'syck' +require 'syck/basenode' + +module Syck + + # + # Mixin BaseNode functionality + # + class Node + include Syck::BaseNode + end + +end diff --git a/ext/syck/lib/syck/tag.rb b/ext/syck/lib/syck/tag.rb new file mode 100644 index 0000000000..bfb4dc714d --- /dev/null +++ b/ext/syck/lib/syck/tag.rb @@ -0,0 +1,92 @@ +# -*- 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 Syck + # 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 self.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 self.tagged_classes + @@tagged_classes + end +end + +class Module + # :stopdoc: + + # 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 ) + verbose, $VERBOSE = $VERBOSE, nil + class_eval <<-"END", __FILE__, __LINE__+1 + attr_writer :taguri + def taguri + if respond_to? :to_yaml_type + Syck.tagurize( to_yaml_type[1..-1] ) + else + return @taguri if defined?(@taguri) and @taguri + tag = #{ tag.dump } + if self.class.yaml_tag_subclasses? and self.class != Syck.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 + Syck.tag_class tag, self + ensure + $VERBOSE = verbose + 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 + # :startdoc: +end diff --git a/ext/syck/lib/syck/types.rb b/ext/syck/lib/syck/types.rb new file mode 100644 index 0000000000..5c129acba4 --- /dev/null +++ b/ext/syck/lib/syck/types.rb @@ -0,0 +1,192 @@ +# -*- mode: ruby; ruby-indent-level: 4 -*- vim: sw=4 +# +# Classes required by the full core typeset +# + +module Syck + + # + # Default private type + # + class PrivateType + def self.tag_subclasses?; false; end + verbose, $VERBOSE = $VERBOSE, nil + def initialize( type, val ) + @type_id = type; @value = val + @value.taguri = "x-private:#{ @type_id }" + end + def to_yaml( opts = {} ) + @value.to_yaml( opts ) + end + ensure + $VERBOSE = verbose + end + + # + # Default domain type + # + class DomainType + def self.tag_subclasses?; false; end + verbose, $VERBOSE = $VERBOSE, nil + def initialize( domain, type, val ) + @domain = domain; @type_id = type; @value = val + @value.taguri = "tag:#{ @domain }:#{ @type_id }" + end + def to_yaml( opts = {} ) + @value.to_yaml( opts ) + end + ensure + $VERBOSE = verbose + end + + # + # Unresolved objects + # + class Object + def self.tag_subclasses?; false; end + def to_yaml( opts = {} ) + Syck.quick_emit( self, 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 < ::Hash + attr_accessor :default + def inspect + self.default.to_s + end + def to_s + self.default.to_s + end + def update( h ) + if Syck::SpecialHash === h + @default = h.default if h.default + end + super( h ) + end + def to_yaml( opts = {} ) + opts[:DefaultKey] = self.default + super( opts ) + end + end + + # + # Builtin collection: !omap + # + 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 Syck::Error, "Invalid !omap entry: " + val.inspect + end + end + else + raise Syck::Error, "Invalid !omap: " + val.inspect + end + self + end + def self.[]( *vals ) + o = Omap.new + 0.step( vals.length - 1, 2 ) do |i| + o[vals[i]] = vals[i+1] + end + o + end + 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 = {} ) + Syck.quick_emit( self, opts ) do |out| + out.seq( taguri, to_yaml_style ) do |seq| + self.each do |v| + seq.add( Hash[ *v ] ) + end + end + end + end + end + + # + # Builtin collection: !pairs + # + 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 Syck::Error, "Invalid !pairs entry: " + val.inspect + end + end + else + raise Syck::Error, "Invalid !pairs: " + val.inspect + end + self + end + def self.[]( *vals ) + p = Pairs.new + 0.step( vals.length - 1, 2 ) { |i| + p[vals[i]] = vals[i+1] + } + p + end + def []( k ) + self.assoc( k ).to_a + end + def []=( k, val ) + self << [ k, val ] + val + end + def has_key?( k ) + self.assoc( k ) ? true : false + end + def is_complex_yaml? + true + end + def to_yaml( opts = {} ) + Syck.quick_emit( self, opts ) do |out| + out.seq( taguri, to_yaml_style ) do |seq| + self.each do |v| + seq.add( Hash[ *v ] ) + end + end + end + end + end + + # + # Builtin collection: !set + # + class Set < ::Hash + yaml_as "tag:yaml.org,2002:set" + end +end diff --git a/ext/syck/lib/syck/yamlnode.rb b/ext/syck/lib/syck/yamlnode.rb new file mode 100644 index 0000000000..2fa57b1f97 --- /dev/null +++ b/ext/syck/lib/syck/yamlnode.rb @@ -0,0 +1,54 @@ +# +# YAML::YamlNode class +# +require 'syck/basenode' + +module Syck + + # + # YAML Generic Model container + # + class YamlNode + include BaseNode + attr_accessor :kind, :type_id, :value, :anchor + def initialize(t, v) + @type_id = t + if Hash === v + @kind = 'map' + @value = {} + v.each {|key,val| + @value[key.transform] = [key, val] + } + elsif Array === v + @kind = 'seq' + @value = v + elsif String === v + @kind = 'scalar' + @value = v + end + end + + # + # Transform this node fully into a native type + # + def transform + t = nil + if @value.is_a? Hash + t = {} + @value.each { |k,v| + t[ k ] = v[1].transform + } + elsif @value.is_a? Array + t = [] + @value.each { |v| + t.push v.transform + } + else + t = @value + end + Syck.transfer_method( @type_id, t ) + end + + end + +end diff --git a/ext/syck/lib/syck/ypath.rb b/ext/syck/lib/syck/ypath.rb new file mode 100644 index 0000000000..024dcb7f4e --- /dev/null +++ b/ext/syck/lib/syck/ypath.rb @@ -0,0 +1,54 @@ +# +# YAML::YPath +# + +warn "#{caller[0]}: YAML::YPath is deprecated" if $VERBOSE + +module Syck + + class YPath + attr_accessor :segments, :predicates, :flags + def initialize( str ) + @segments = [] + @predicates = [] + @flags = nil + while str =~ /^\/?(\/|[^\/\[]+)(?:\[([^\]]+)\])?/ + @segments.push $1 + @predicates.push $2 + str = $' + end + unless str.to_s.empty? + @segments += str.split( "/" ) + end + if @segments.length == 0 + @segments.push "." + end + end + def self.each_path( str ) + # + # Find choices + # + paths = [] + str = "(#{ str })" + while str.sub!( /\(([^()]+)\)/, "\n#{ paths.length }\n" ) + paths.push $1.split( '|' ) + end + + # + # Construct all possible paths + # + all = [ str ] + ( paths.length - 1 ).downto( 0 ) do |i| + all = all.collect do |a| + paths[i].collect do |p| + a.gsub( /\n#{ i }\n/, p ) + end + end.flatten.uniq + end + all.collect do |path| + yield YPath.new( path ) + end + end + end + +end -- cgit v1.2.3