summaryrefslogtreecommitdiff
path: root/lib/yaml
diff options
context:
space:
mode:
Diffstat (limited to 'lib/yaml')
-rw-r--r--lib/yaml/basenode.rb216
-rw-r--r--lib/yaml/constants.rb51
-rw-r--r--lib/yaml/dbm.rb111
-rw-r--r--lib/yaml/emitter.rb330
-rw-r--r--lib/yaml/encoding.rb123
-rw-r--r--lib/yaml/error.rb33
-rw-r--r--lib/yaml/rubytypes.rb558
-rw-r--r--lib/yaml/store.rb75
-rw-r--r--lib/yaml/stream.rb44
-rw-r--r--lib/yaml/stringio.rb83
-rw-r--r--lib/yaml/syck.rb19
-rw-r--r--lib/yaml/types.rb195
-rw-r--r--lib/yaml/yamlnode.rb54
-rw-r--r--lib/yaml/ypath.rb52
14 files changed, 1944 insertions, 0 deletions
diff --git a/lib/yaml/basenode.rb b/lib/yaml/basenode.rb
new file mode 100644
index 0000000000..341177a979
--- /dev/null
+++ b/lib/yaml/basenode.rb
@@ -0,0 +1,216 @@
+#
+# YAML::BaseNode class
+#
+require 'yaml/ypath'
+
+module YAML
+
+ #
+ # YAML Generic Model container
+ #
+ module BaseNode
+
+ #
+ # Search for YPath entry and return
+ # qualified nodes.
+ #
+ def select( ypath_str )
+ matches = match_path( ypath_str )
+
+ #
+ # Create a new generic view of the elements selected
+ #
+ if matches
+ result = []
+ matches.each { |m|
+ result.push m.last
+ }
+ YamlNode.new( 'seq', result )
+ end
+ end
+
+ #
+ # Search for YPath entry and return
+ # transformed nodes.
+ #
+ def select!( ypath_str )
+ 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 )
+ 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 )
+ if Hash === @value and @value.has_key?( seg )
+ @value[seg][1]
+ 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 )
+ 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 )
+ 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], v[1][1]]
+ match_deep = v[1][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], h[1][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? YamlNode
+ 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 []( *k )
+ if Hash === @value
+ v = @value.[]( *k )
+ v[1] if v
+ elsif Array === @value
+ @value.[]( *k )
+ end
+ end
+
+ def children
+ if Hash === @value
+ @value.values.collect { |c| c[1] }
+ elsif Array === @value
+ @value
+ end
+ end
+
+ def children_with_index
+ 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/lib/yaml/constants.rb b/lib/yaml/constants.rb
new file mode 100644
index 0000000000..21aee7e0a6
--- /dev/null
+++ b/lib/yaml/constants.rb
@@ -0,0 +1,51 @@
+#
+# Constants used throughout the library
+#
+module YAML
+
+ #
+ # 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-\\x08\\x0b-\\x0d\\x0e-\\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{\z \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 = {
+ 'z' => "\x00", '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
+ }
+ TRANSFER_DOMAINS = {
+ 'yaml.org,2002' => {},
+ 'ruby.yaml.org,2002' => {}
+ }
+ PRIVATE_TYPES = {}
+ IMPLICIT_TYPES = [ 'null', 'bool', 'time', 'int', 'float' ]
+
+end
diff --git a/lib/yaml/dbm.rb b/lib/yaml/dbm.rb
new file mode 100644
index 0000000000..b1474a736a
--- /dev/null
+++ b/lib/yaml/dbm.rb
@@ -0,0 +1,111 @@
+require 'yaml'
+require 'dbm'
+#
+# YAML + DBM = YDBM
+# - Same interface as DBM class
+#
+module YAML
+
+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 YAML::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 indexes( *keys )
+ keys.collect { |k| fetch( k ) }
+ end
+ def delete( key )
+ v = super( key )
+ if String === v
+ v = YAML::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 YAML::load( v ) }
+ self
+ end
+ def values
+ super.collect { |v| YAML::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] = YAML::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
+ indexes( *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/lib/yaml/emitter.rb b/lib/yaml/emitter.rb
new file mode 100644
index 0000000000..4b8541c2c3
--- /dev/null
+++ b/lib/yaml/emitter.rb
@@ -0,0 +1,330 @@
+#
+# Output classes and methods
+#
+
+require 'yaml/constants'
+require 'yaml/encoding'
+require 'yaml/error'
+
+module YAML
+
+ #
+ # Emit a set of values
+ #
+
+ class Emitter
+ attr_accessor :options
+ def initialize( opts )
+ opts = {} if opts.class != Hash
+ @options = YAML::DEFAULTS.dup.update( opts )
+ @headless = 0
+ @seq_map = false
+ @anchors = {}
+ @anchor_extras = {}
+ @active_anchors = []
+ @level = -1
+ self.clear
+ end
+
+ def clear
+ @buffer = []
+ end
+
+ #
+ # Version string
+ #
+ def version_s
+ " %YAML:#{@options[:Version]}" if @options[:UseVersion]
+ end
+
+ #
+ # Header
+ #
+ def header
+ if @headless.nonzero?
+ ""
+ else
+ "---#{version_s} "
+ end
+ 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 = '>' )
+ 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
+ if valx =~ /#{YAML::ESCAPE_CHAR}/
+ valx = YAML::escape( valx )
+ end
+ if valx =~ /\A[ \t#]/
+ str << @options[:Indent].to_s
+ end
+ if block == '>'
+ valx = fold( valx )
+ end
+ self << str + indent_text( valx ) + "\n"
+ end
+
+ #
+ # Emit a simple, unqouted string
+ #
+ def simple( value )
+ self << value.to_s
+ end
+
+ #
+ # Emit double-quoted string
+ #
+ def double( value )
+ "\"#{YAML.escape( value )}\""
+ end
+
+ #
+ # Emit single-quoted string
+ #
+ def single( value )
+ "'#{value}'"
+ end
+
+ #
+ # Write a text block with the current indent
+ #
+ def indent_text( text )
+ return "" if text.to_s.empty?
+ spacing = " " * ( @level * @options[:Indent] )
+ return "\n" + text.gsub( /^([^\n])/, "#{spacing}\\1" )
+ end
+
+ #
+ # Write a current indent
+ #
+ def indent
+ #p [ self.id, @level, :INDENT ]
+ return " " * ( @level * @options[:Indent] )
+ end
+
+ #
+ # Add indent to the buffer
+ #
+ def indent!
+ self << indent
+ end
+
+ #
+ # Folding paragraphs within a column
+ #
+ def fold( value )
+ value.gsub!( /\A\n+/, '' )
+ folded = $&.to_s
+ width = (0..@options[:BestWidth])
+ while not value.empty?
+ last = value.index( /(\n+)/ )
+ chop_s = false
+ if width.include?( last )
+ last += $1.length - 1
+ elsif width.include?( value.length )
+ last = value.length
+ else
+ last = value.rindex( /[ \t]/, @options[:BestWidth] )
+ chop_s = true
+ end
+ folded += value.slice!( 0, width.include?( last ) ? last + 1 : @options[:BestWidth] )
+ folded.chop! if chop_s
+ folded += "\n" unless value.empty?
+ end
+ folded
+ 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 << "{}"
+ else
+ 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
+ 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 )
+ val = Sequence.new
+ e.call( val )
+ self << "#{type} " if type.length.nonzero?
+
+ #
+ # Empty arrays
+ #
+ if val.length.zero?
+ self << "[]"
+ else
+ 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
+
+ #
+ # Concatenate to the buffer
+ #
+ def <<( str )
+ #p [ self.id, @level, str ]
+ @buffer.last << str
+ end
+
+ #
+ # Monitor objects and allow references
+ #
+ def start_object( oid )
+ @level += 1
+ @buffer.push( "" )
+ #p [ self.id, @level, :OPEN ]
+ idx = nil
+ if oid
+ if @anchors.has_key?( oid )
+ idx = @active_anchors.index( oid )
+ unless idx
+ idx = @active_anchors.length
+ af_str = "&#{@options[:AnchorFormat]} " % [ idx + 1 ]
+ af_str += @anchor_extras[ @anchors[ oid ] ].to_s
+ @buffer[ @anchors[ oid ] ][0,0] = af_str
+ @headless = 0 if @anchors[ oid ].zero?
+ end
+ idx += 1
+ @active_anchors.push( oid )
+ else
+ @anchors[ oid ] = @buffer.length - 1
+ end
+ end
+ return idx
+ end
+
+ #
+ # Output method
+ #
+ def end_object
+ @level -= 1
+ @buffer.push( "" )
+ #p [ self.id, @level, :END ]
+ if @level < 0
+ YAML.internal_to_utf( header + @buffer.to_s[@headless..-1], @options[:Encoding] )
+ 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
+
+ #
+ # Allocate an Emitter if needed
+ #
+ def YAML.quick_emit( oid, opts = {}, &e )
+ old_opt = nil
+ if opts[:Emitter].is_a? YAML::Emitter
+ out = opts.delete( :Emitter )
+ old_opt = out.options.dup
+ out.options.update( opts )
+ else
+ out = YAML::Emitter.new( opts )
+ end
+ aidx = out.start_object( oid )
+ if aidx
+ out.simple( "*#{out.options[:AnchorFormat]} " % [ aidx ] )
+ else
+ e.call( out )
+ end
+ if old_opt.is_a? Hash
+ out.options = old_opt
+ end
+ out.end_object
+ end
+
+end
+
diff --git a/lib/yaml/encoding.rb b/lib/yaml/encoding.rb
new file mode 100644
index 0000000000..59491b53b7
--- /dev/null
+++ b/lib/yaml/encoding.rb
@@ -0,0 +1,123 @@
+#
+# Handle Unicode-to-Internal conversion
+#
+
+module YAML
+
+ #
+ # Encodings ( $-K to ICONV )
+ #
+ CHARSETS = {
+ 'NONE' => 'LATIN1',
+ 'ASCII' => 'US-ASCII',
+ 'UTF-8' => 'UTF-8',
+ 'EUC' => 'EUC-JP',
+ 'SJIS' => 'SHIFT-JIS'
+ }
+
+ #
+ # YAML documents can be in UTF-8, UTF-16 or UTF-32
+ # So let's read and write in Unicode
+ #
+
+ @@unicode = false
+ begin
+ require 'iconv'
+ DEFAULTS[:Encoding] = :Utf8
+ rescue LoadError
+ end
+
+ def YAML.unicode; @@unicode; end
+ def YAML.unicode=( bool ); @@unicode = bool; end
+
+ #
+ # Unicode conversion
+ #
+
+ def YAML.utf_to_internal( str, from_enc )
+ return unless str
+ to_enc = CHARSETS[$-K]
+ case from_enc
+ when :Utf32
+ Iconv.iconv( to_enc, 'UTF-32', str )[0]
+ when :Utf16
+ Iconv.iconv( to_enc, 'UTF-16', str )[0]
+ when :Utf8
+ Iconv.iconv( to_enc, 'UTF-8', str )[0]
+ when :None
+ str
+ else
+ raise YAML::Error, ERROR_UNSUPPORTED_ENCODING % from_enc.inspect
+ end
+ end
+
+ def YAML.internal_to_utf( str, to_enc )
+ return unless str
+ from_enc = CHARSETS[$-K]
+ case to_enc
+ when :Utf32
+ Iconv.iconv( 'UTF-32', from_enc, str )[0]
+ when :Utf16
+ Iconv.iconv( 'UTF-16', from_enc, str )[0]
+ when :Utf8
+ Iconv.iconv( 'UTF-8', from_enc, str )[0]
+ when :None
+ str
+ else
+ raise YAML::Error, ERROR_UNSUPPORTED_ENCODING % to_enc.inspect
+ end
+ end
+
+ def YAML.sniff_encoding( str )
+ unless YAML::unicode
+ :None
+ else
+ case str
+ when /^\x00\x00\xFE\xFF/ # UTF-32
+ :Utf32
+ when /^\xFE\xFF/ # UTF-32BE
+ :Utf16
+ else
+ :Utf8
+ end
+ end
+ end
+
+ def YAML.enc_separator( enc )
+ case enc
+ when :Utf32
+ "\000\000\000\n"
+ when :Utf16
+ "\000\n"
+ when :Utf8
+ "\n"
+ when :None
+ "\n"
+ else
+ raise YAML::Error, ERROR_UNSUPPORTED_ENCODING % enc.inspect
+ end
+ end
+
+ #
+ # Escape the string, condensing common escapes
+ #
+ def YAML.escape( value )
+ value.gsub( /\\/, "\\\\\\" ).gsub( /"/, "\\\"" ).gsub( /([\x00-\x1f])/ ) { |x| ESCAPES[ x.unpack("C")[0] ] }
+ end
+
+ #
+ # Unescape the condenses escapes
+ #
+ def YAML.unescape( value )
+ value.gsub( /\\(?:([nevbr\\fartz])|0?x([0-9a-fA-F]{2})|u([0-9a-fA-F]{4}))/ ) { |x|
+ if $3
+ ["#$3".hex ].pack('U*')
+ elsif $2
+ [$2].pack( "H2" )
+ else
+ UNESCAPES[$1]
+ end
+ }
+ end
+
+end
diff --git a/lib/yaml/error.rb b/lib/yaml/error.rb
new file mode 100644
index 0000000000..a9df22749b
--- /dev/null
+++ b/lib/yaml/error.rb
@@ -0,0 +1,33 @@
+#
+# Error messages and exception class
+#
+
+module YAML
+
+ #
+ # 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
+
+end
diff --git a/lib/yaml/rubytypes.rb b/lib/yaml/rubytypes.rb
new file mode 100644
index 0000000000..b6f1e37705
--- /dev/null
+++ b/lib/yaml/rubytypes.rb
@@ -0,0 +1,558 @@
+require 'date'
+#
+# Type conversions
+#
+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
+ def to_yaml( opts = {} )
+ YAML::quick_emit( self.id, opts ) { |out|
+ out.map( self.to_yaml_type ) { |map|
+ to_yaml_properties.each { |m|
+ map.add( m[1..-1], instance_eval( m ) )
+ }
+ }
+ }
+ 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"
+ else
+ "!ruby/hash:#{self.class}"
+ end
+ end
+ def to_yaml( opts = {} )
+ opts[:DocType] = self.class if Hash === opts
+ YAML::quick_emit( self.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 )
+ 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.new
+ o.update( val )
+ val = o
+ end
+ else
+ raise YAML::Error, "Invalid map explicitly tagged !map: " + val.inspect
+ end
+ val
+}
+YAML.add_builtin_type( /^map/, &hash_proc )
+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.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
+
+#
+# Structs: export as a !map
+#
+class Struct
+ def is_complex_yaml?
+ true
+ end
+ def to_yaml( opts = {} )
+ YAML::quick_emit( self.id, opts ) { |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|
+ map.add( m, self[m] )
+ }
+ }
+ }
+ end
+end
+
+YAML.add_ruby_type( Struct ) { |type, val|
+ type =~ /^struct:(\w+)/
+ if Hash === val
+ type = $1
+ struct_type = nil
+ struct_def = []
+ struct_name = ""
+ if $1.to_s.length > 1
+ struct_name = $1[0..$1.length]
+ struct_def << struct_name
+ end
+
+ #
+ # Use existing Struct if it exists
+ #
+ begin
+ struct_type = Struct.const_get( struct_name )
+ rescue NameError
+ end
+ if not struct_type
+ struct_type = Struct.new( *struct_def.concat( val.keys.collect { |k| k.intern } ) )
+ end
+
+ #
+ # Set the Struct properties
+ #
+ st = struct_type.new
+ st.members.each { |m|
+ st.send( "#{m}=", val[m] )
+ }
+ 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
+ def to_yaml( opts = {} )
+ opts[:DocType] = self.class if Hash === opts
+ YAML::quick_emit( self.id, opts ) { |out|
+ array_type = to_yaml_type
+ if not out.options[:ExplicitTypes] and array_type == "!seq"
+ array_type = ""
+ end
+
+ out.seq( array_type ) { |seq|
+ seq.concat( self )
+ }
+ }
+ 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.new
+ o.concat( val )
+ val = o
+ end
+ val
+ else
+ val.to_a
+ end
+}
+YAML.add_builtin_type( /^seq/, &array_proc )
+YAML.add_ruby_type( Array, &array_proc )
+
+#
+# String#to_yaml
+#
+class String
+ def is_complex_yaml?
+ ( self =~ /\n.+/ ? true : false )
+ end
+ def is_binary_data?
+ ( self.count( "^ -~", "^\r\n" ) / self.size > 0.3 || self.count( "\x00" ) > 0 )
+ 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.id : nil, opts ) { |out|
+ if complex
+ if self.is_binary_data?
+ out.binary_base64( self )
+ else
+ out.node_text( self )
+ end
+ else
+ ostr = if out.options[:KeepValue]
+ self
+ elsif empty?
+ "''"
+ elsif YAML.detect_implicit( self ) != 'str'
+ "\"#{YAML.escape( self )}\""
+ elsif self =~ /#{YAML::ESCAPE_CHAR}|[#{YAML::SPACE_INDICATORS}] |\n|\'/
+ "\"#{YAML.escape( self )}\""
+ elsif self =~ /^[^#{YAML::WORD_CHAR}]/
+ "\"#{YAML.escape( self )}\""
+ else
+ self
+ end
+ out.simple( ostr )
+ end
+ }
+ end
+end
+
+YAML.add_builtin_type( 'str' ) { |type,val| val.to_s }
+YAML.add_builtin_type( 'binary' ) { |type,val|
+ enctype = "m"
+ if String === val
+ val.gsub( /\s+/, '' ).unpack( enctype )[0]
+ else
+ raise YAML::Error, "Binary data must be represented by a string: " + val.inspect
+ end
+}
+
+#
+# Symbol#to_yaml
+#
+class Symbol
+ def is_complex_yaml?
+ false
+ end
+ def to_yaml( opts = {} )
+ YAML::quick_emit( nil, opts ) { |out|
+ out << "!ruby/sym "
+ self.id2name.to_yaml( :Emitter => out )
+ }
+ end
+end
+
+symbol_proc = Proc.new { |type, val|
+ if String === 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
+#
+class Range
+ def is_complex_yaml?
+ false
+ end
+ def to_yaml( opts = {} )
+ YAML::quick_emit( nil, opts ) { |out|
+ out << "!ruby/range "
+ self.inspect.to_yaml( :Emitter => out )
+ }
+ end
+end
+
+YAML.add_ruby_type( Range ) { |type, val|
+ if String === val and val =~ /^(.*[^.])(\.{2,3})([^.].*)$/
+ r1, rdots, r2 = $1, $2, $3
+ Range.new( YAML.try_implicit( r1 ), YAML.try_implicit( r2 ), rdots.length == 3 )
+ elsif Hash === val
+ Range.new( val['begin'], val['end'], val['exclude_end?'] )
+ else
+ raise YAML::Error, "Invalid Range: " + val.inspect
+ end
+}
+
+#
+# Make an RegExp
+#
+class Regexp
+ def is_complex_yaml?
+ false
+ end
+ def to_yaml( opts = {} )
+ YAML::quick_emit( nil, opts ) { |out|
+ out << "!ruby/regexp "
+ self.inspect.to_yaml( :Emitter => out )
+ }
+ end
+end
+
+regexp_proc = Proc.new { |type, val|
+ if String === val and val =~ /^\/(.*)\/([mix]*)$/
+ val = { 'REGEXP' => $1, 'MODIFIERS' => $2 }
+ end
+ if Hash === val
+ mods = nil
+ unless val['MODIFIERS'].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
+ end
+ Regexp::compile( val['REGEXP'], mods )
+ 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?
+ false
+ 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
+ end
+ difference_minutes = (absolute_difference/60).round
+ tz = "%s%02d:%02d" % [ difference_sign, difference_minutes / 60, difference_minutes % 60]
+ end
+ ( self.strftime( "%Y-%m-%d %H:%M:%S." ) +
+ "%06d %s" % [usec, tz] ).
+ to_yaml( :Emitter => out, :KeepValue => true )
+ }
+ end
+end
+
+YAML.add_builtin_type( 'time' ) { |type, val|
+ if val =~ /\A(\d{4})\-(\d{1,2})\-(\d{1,2})[Tt](\d{2})\:(\d{2})\:(\d{2})(\.\d{1,2})?(Z|[-+][0-9][0-9](?:\:[0-9][0-9])?)\Z/
+ YAML.mktime( *$~.to_a[1,8] )
+ elsif val =~ /\A(\d{4})\-(\d{1,2})\-(\d{1,2})[ \t]+(\d{2})\:(\d{2})\:(\d{2})(\.\d+)?[ \t]+(Z|[-+][0-9][0-9](?:\:[0-9][0-9])?)\Z/
+ YAML.mktime( *$~.to_a[1,8] )
+ elsif val =~ /\A(\d{4})\-(\d{1,2})\-(\d{1,2})[ \t]+(\d{2})\:(\d{2})\:(\d{2})(\.\d{1,2})?\Z/
+ YAML.mktime( *$~.to_a[1,7] )
+ elsif val =~ /\A(\d{4})\-(\d{1,2})\-(\d{1,2})\Z/
+ Date.new($1.to_i, $2.to_i, $3.to_i)
+ elsif type == :Implicit
+ :InvalidType
+ else
+ raise YAML::TypeError, "Invalid !time string: " + val.inspect
+ end
+}
+
+#
+# Emit a Date object as a simple implicit
+#
+class Date
+ def is_complex_yaml?
+ false
+ end
+ def to_yaml( opts = {} )
+ opts[:KeepValue] = true
+ self.to_s.to_yaml( opts )
+ end
+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 )
+ end
+end
+
+YAML.add_builtin_type( 'float' ) { |type, val|
+ if val =~ /\A[-+]?[\d][\d,]*\.[\d,]*[eE][-+][0-9]+\Z/ # Float (exponential)
+ $&.tr( ',', '' ).to_f
+ elsif val =~ /\A[-+]?[\d][\d,]*\.[\d,]*\Z/ # Float (fixed)
+ $&.tr( ',', '' ).to_f
+ elsif val =~ /\A([-+]?)\.(inf|Inf|INF)\Z/ # Float (english)
+ ( $1 == "-" ? -1.0/0.0 : 1.0/0.0 )
+ elsif val =~ /\A\.(nan|NaN|NAN)\Z/
+ 0.0/0.0
+ elsif type == :Implicit
+ :InvalidType
+ else
+ val.to_f
+ end
+}
+
+YAML.add_builtin_type( 'int' ) { |type, val|
+ if val =~ /\A[-+]?0[0-7,]+\Z/ # Integer (octal)
+ $&.oct
+ elsif val =~ /\A[-+]?0x[0-9a-fA-F,]+\Z/ # Integer (hex)
+ $&.hex
+ elsif val =~ /\A[-+]?\d[\d,]*\Z/ # Integer (canonical)
+ $&.tr( ',', '' ).to_i
+ elsif val =~ /\A([-+]?)(\d[\d,]*(?::[0-5]?[0-9])+)\Z/
+ sign = ( $1 == '-' ? -1 : 1 )
+ digits = $2.split( /:/ ).collect { |x| x.to_i }
+ val = 0; digits.each { |x| val = ( val * 60 ) + x }; val *= sign
+ elsif type == :Implicit
+ :InvalidType
+ else
+ val.to_i
+ end
+}
+
+class TrueClass
+ def is_complex_yaml?
+ false
+ end
+ def to_yaml( opts = {} )
+ opts[:KeepValue] = true
+ "true".to_yaml( opts )
+ end
+end
+
+class FalseClass
+ def is_complex_yaml?
+ false
+ end
+ def to_yaml( opts = {} )
+ opts[:KeepValue] = true
+ "false".to_yaml( opts )
+ end
+end
+
+YAML.add_builtin_type( 'bool' ) { |type, val|
+ if val =~ /\A(\+|true|True|TRUE|yes|Yes|YES|on|On|ON)\Z/
+ true
+ elsif val =~ /\A(\-|false|False|FALSE|no|No|NO|off|Off|OFF)\Z/
+ false
+ elsif type == :Implicit
+ :InvalidType
+ else
+ raise YAML::TypeError, "Invalid !bool string: " + val.inspect
+ end
+}
+
+class NilClass
+ def is_complex_yaml?
+ false
+ end
+ def to_yaml( opts = {} )
+ opts[:KeepValue] = true
+ "".to_yaml( opts )
+ end
+end
+
+YAML.add_builtin_type( 'null' ) { |type, val|
+ if val =~ /\A(\~|null|Null|NULL)\Z/
+ nil
+ elsif val.empty?
+ nil
+ elsif type == :Implicit
+ :InvalidType
+ else
+ raise YAML::TypeError, "Invalid !null string: " + val.inspect
+ end
+}
+
diff --git a/lib/yaml/store.rb b/lib/yaml/store.rb
new file mode 100644
index 0000000000..b2924b0660
--- /dev/null
+++ b/lib/yaml/store.rb
@@ -0,0 +1,75 @@
+#
+# YAML::Store
+#
+require 'yaml'
+require 'pstore'
+
+module YAML
+
+ class Store < PStore
+ #
+ # Constructor
+ #
+ def initialize( *o )
+ @opt = YAML::DEFAULTS.dup
+ if String === o.first
+ super(o.pop)
+ end
+ if o.last.is_a? Hash
+ @opt.update(o.pop)
+ end
+ end
+
+ #
+ # Override Pstore#transaction
+ #
+ def transaction
+ raise YAML::Error, "nested transaction" if @transaction
+ raise YAML::Error, "no filename for transaction" unless @filename
+ begin
+ @transaction = true
+ value = nil
+ backup = @filename+"~"
+ if File::exist?(@filename)
+ file = File::open(@filename, "rb+")
+ orig = true
+ else
+ @table = {}
+ file = File::open(@filename, "wb+")
+ file.write( @table.to_yaml( @opt ) )
+ end
+ file.flock(File::LOCK_EX)
+ if orig
+ File::copy @filename, backup
+ @table = YAML::load( file )
+ end
+ begin
+ catch(:pstore_abort_transaction) do
+ value = yield(self)
+ end
+ rescue Exception
+ @abort = true
+ raise
+ ensure
+ unless @abort
+ begin
+ file.rewind
+ file.write( @table.to_yaml( @opt ) )
+ file.truncate(file.pos)
+ rescue
+ File::rename backup, @filename if File::exist?(backup)
+ raise
+ end
+ end
+ @abort = false
+ end
+ ensure
+ @table = nil
+ @transaction = false
+ file.close if file
+ end
+ value
+ end
+ end
+
+end
diff --git a/lib/yaml/stream.rb b/lib/yaml/stream.rb
new file mode 100644
index 0000000000..3b6919b5dd
--- /dev/null
+++ b/lib/yaml/stream.rb
@@ -0,0 +1,44 @@
+module YAML
+
+ #
+ # 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 )
+ @documents[ doc_num ] = doc
+ end
+
+ def emit
+ opts = @options.dup
+ opts[:UseHeader] = true if @documents.length > 1
+ ct = 0
+ out = Emitter.new( opts )
+ @documents.each { |v|
+ if ct > 0
+ out << "\n--- "
+ end
+ v.to_yaml( :Emitter => out )
+ ct += 1
+ }
+ out.end_object
+ end
+
+ end
+
+end
diff --git a/lib/yaml/stringio.rb b/lib/yaml/stringio.rb
new file mode 100644
index 0000000000..8ad949fa2b
--- /dev/null
+++ b/lib/yaml/stringio.rb
@@ -0,0 +1,83 @@
+#
+# 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 YAML.make_stream( io )
+ if String === io
+ io = StringIO.new( io )
+ elsif not IO === io
+ raise YAML::Error, "YAML stream must be an IO or String object."
+ end
+ if YAML::unicode
+ def io.readline
+ YAML.utf_to_internal( readline( @ln_sep ), @utf_encoding )
+ end
+ def io.check_unicode
+ @utf_encoding = YAML.sniff_encoding( read( 4 ) )
+ @ln_sep = YAML.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/lib/yaml/syck.rb b/lib/yaml/syck.rb
new file mode 100644
index 0000000000..faf57e8036
--- /dev/null
+++ b/lib/yaml/syck.rb
@@ -0,0 +1,19 @@
+#
+# YAML::Syck module
+# .. glues syck and yaml.rb together ..
+#
+require 'syck'
+require 'yaml/basenode'
+
+module YAML
+ module Syck
+
+ #
+ # Mixin BaseNode functionality
+ #
+ class Node
+ include YAML::BaseNode
+ end
+
+ end
+end
diff --git a/lib/yaml/types.rb b/lib/yaml/types.rb
new file mode 100644
index 0000000000..7602c436e7
--- /dev/null
+++ b/lib/yaml/types.rb
@@ -0,0 +1,195 @@
+#
+# Classes required by the full core typeset
+#
+module YAML
+
+ #
+ # Default private type
+ #
+ class PrivateType
+ attr_accessor :type_id, :value
+ def initialize( type, val )
+ @type_id = type; @value = val
+ end
+ def to_yaml( opts = {} )
+ YAML::quick_emit( self.id, opts ) { |out|
+ out << " !!#{@type_id}"
+ value.to_yaml( :Emitter => out )
+ }
+ end
+ end
+
+ #
+ # Default domain type
+ #
+ class DomainType
+ attr_accessor :domain, :type_id, :value
+ def initialize( domain, type, val )
+ @domain = domain; @type_id = type; @value = val
+ 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.id, opts ) { |out|
+ out << " !#{to_yaml_type} "
+ value.to_yaml( :Emitter => out )
+ }
+ end
+ end
+
+ #
+ # YAML Hash class to support comments and defaults
+ #
+ class SpecialHash < Kernel::Hash
+ attr_accessor :default
+ def inspect
+ self.default.to_s
+ end
+ def to_s
+ self.default.to_s
+ end
+ def update( h )
+ if YAML::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
+ def self.[]( *vals )
+ o = Omap.new
+ 0.step( vals.length - 1, 2 ) { |i|
+ o[vals[i]] = vals[i+1]
+ }
+ 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 = {} )
+ YAML::quick_emit( self.id, opts ) { |out|
+ out.seq( "!omap" ) { |seq|
+ self.each { |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
+ }
+ else
+ raise YAML::Error, "Invalid !omap: " + val.inspect
+ end
+ p
+ }
+
+ #
+ # Builtin collection: !pairs
+ #
+ class Pairs < Array
+ 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 = {} )
+ YAML::quick_emit( self.id, opts ) { |out|
+ out.seq( "!pairs" ) { |seq|
+ self.each { |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
+ }
+ else
+ raise YAML::Error, "Invalid !pairs: " + val.inspect
+ end
+ p
+ }
+
+ #
+ # Builtin collection: !set
+ #
+ class Set < Hash
+ def to_yaml_type
+ "!set"
+ end
+ 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
diff --git a/lib/yaml/yamlnode.rb b/lib/yaml/yamlnode.rb
new file mode 100644
index 0000000000..e36a18e694
--- /dev/null
+++ b/lib/yaml/yamlnode.rb
@@ -0,0 +1,54 @@
+#
+# YAML::YamlNode class
+#
+require 'yaml/basenode'
+
+module YAML
+
+ #
+ # 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 { |k,v|
+ @value[ k.transform ] = [ k, v ]
+ }
+ 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
+ YAML.transfer_method( @type_id, t )
+ end
+
+ end
+
+end
diff --git a/lib/yaml/ypath.rb b/lib/yaml/ypath.rb
new file mode 100644
index 0000000000..713c4de76e
--- /dev/null
+++ b/lib/yaml/ypath.rb
@@ -0,0 +1,52 @@
+#
+# YAML::YPath
+#
+
+module YAML
+
+ 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 YPath.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