(linenum→info "unix/slp.c:2238")

ruby/1.9.0/lib/yaml.rb

    1: # -*- mode: ruby; ruby-indent-level: 4; tab-width: 4 -*- vim: sw=4 ts=4
    2: # $Id: yaml.rb 13940 2007-11-15 17:54:32Z why $
    3: #
    4: # = yaml.rb: top-level module with methods for loading and parsing YAML documents
    5: #
    6: # Author:: why the lucky stiff
    7: # 
    8: 
    9: require 'stringio'
   10: require 'yaml/error'
   11: require 'yaml/syck'
   12: require 'yaml/tag'
   13: require 'yaml/stream'
   14: require 'yaml/constants'
   15: 
   16: # == YAML
   17: #
   18: # YAML(tm) (rhymes with 'camel') is a
   19: # straightforward machine parsable data serialization format designed for
   20: # human readability and interaction with scripting languages such as Perl
   21: # and Python. YAML is optimized for data serialization, formatted
   22: # dumping, configuration files, log files, Internet messaging and
   23: # filtering. This specification describes the YAML information model and
   24: # serialization format. Together with the Unicode standard for characters, it
   25: # provides all the information necessary to understand YAML Version 1.0
   26: # and construct computer programs to process it.
   27: #                         
   28: # See http://yaml.org/ for more information.  For a quick tutorial, please
   29: # visit YAML In Five Minutes (http://yaml.kwiki.org/?YamlInFiveMinutes).
   30: #                              
   31: # == About This Library
   32: #                         
   33: # The YAML 1.0 specification outlines four stages of YAML loading and dumping.
   34: # This library honors all four of those stages, although data is really only
   35: # available to you in three stages.
   36: #     
   37: # The four stages are: native, representation, serialization, and presentation.
   38: #     
   39: # The native stage refers to data which has been loaded completely into Ruby's
   40: # own types. (See +YAML::load+.)
   41: #
   42: # The representation stage means data which has been composed into
   43: # +YAML::BaseNode+ objects.  In this stage, the document is available as a
   44: # tree of node objects.  You can perform YPath queries and transformations
   45: # at this level.  (See +YAML::parse+.)
   46: #   
   47: # The serialization stage happens inside the parser.  The YAML parser used in
   48: # Ruby is called Syck.  Serialized nodes are available in the extension as
   49: # SyckNode structs.
   50: #       
   51: # The presentation stage is the YAML document itself.  This is accessible
   52: # to you as a string.  (See +YAML::dump+.)
   53: #   
   54: # For more information about the various information models, see Chapter
   55: # 3 of the YAML 1.0 Specification (http://yaml.org/spec/#id2491269).
   56: #
   57: # The YAML module provides quick access to the most common loading (YAML::load)
   58: # and dumping (YAML::dump) tasks.  This module also provides an API for registering
   59: # global types (YAML::add_domain_type).
   60: #
   61: # == Example
   62: #
   63: # A simple round-trip (load and dump) of an object.
   64: #
   65: #     require "yaml"
   66: #
   67: #     test_obj = ["dogs", "cats", "badgers"]
   68: #
   69: #     yaml_obj = YAML::dump( test_obj )
   70: #                         # -> ---
   71: #                              - dogs
   72: #                              - cats
   73: #                              - badgers
   74: #     ruby_obj = YAML::load( yaml_obj )
   75: #                         # => ["dogs", "cats", "badgers"]
   76: #     ruby_obj == test_obj
   77: #                         # => true
   78: #
   79: # To register your custom types with the global resolver, use +add_domain_type+.
   80: #
   81: #     YAML::add_domain_type( "your-site.com,2004", "widget" ) do |type, val|
   82: #         Widget.new( val )
   83: #     end
   84: #
   85: module YAML
   86: 
   87:     Resolver = YAML::Syck::Resolver
   88:     DefaultResolver = YAML::Syck::DefaultResolver
   89:     DefaultResolver.use_types_at( @@tagged_classes )
   90:     GenericResolver = YAML::Syck::GenericResolver
   91:     Parser = YAML::Syck::Parser
   92:     Emitter = YAML::Syck::Emitter
   93: 
   94:     # Returns a new default parser
   95:     def YAML.parser; Parser.new.set_resolver( YAML.resolver ); end
   96:     # Returns a new generic parser
   97:     def YAML.generic_parser; Parser.new.set_resolver( GenericResolver ); end
   98:     # Returns the default resolver
   99:     def YAML.resolver; DefaultResolver; end
  100:     # Returns a new default emitter
  101:     def YAML.emitter; Emitter.new.set_resolver( YAML.resolver ); end
  102: 
  103:     #
  104:     # Converts _obj_ to YAML and writes the YAML result to _io_.
  105:     #     
  106:     #   File.open( 'animals.yaml', 'w' ) do |out|
  107:     #     YAML.dump( ['badger', 'elephant', 'tiger'], out )
  108:     #   end
  109:     #
  110:     # If no _io_ is provided, a string containing the dumped YAML
  111:     # is returned.
  112:     #
  113:     #   YAML.dump( :locked )
  114:     #      #=> "--- :locked"
  115:     #
  116:     def YAML.dump( obj, io = nil )
  117:         obj.to_yaml( io || io2 = StringIO.new )
  118:         io || ( io2.rewind; io2.read )
  119:     end
  120: 
  121:     #
  122:     # Load a document from the current _io_ stream.
  123:     #
  124:     #   File.open( 'animals.yaml' ) { |yf| YAML::load( yf ) }
  125:     #      #=> ['badger', 'elephant', 'tiger']
  126:     #
  127:     # Can also load from a string.
  128:     #
  129:     #   YAML.load( "--- :locked" )
  130:     #      #=> :locked
  131:     #
  132:     def YAML.load( io )
  133:         yp = parser.load( io )
  134:     end
  135: 
  136:     #
  137:     # Load a document from the file located at _filepath_.
  138:     #
  139:     #   YAML.load_file( 'animals.yaml' )
  140:     #      #=> ['badger', 'elephant', 'tiger']
  141:     #
  142:     def YAML.load_file( filepath )
  143:         File.open( filepath ) do |f|
  144:             load( f )
  145:         end
  146:     end
  147: 
  148:     #
  149:     # Parse the first document from the current _io_ stream
  150:     #
  151:     #   File.open( 'animals.yaml' ) { |yf| YAML::load( yf ) }
  152:     #      #=> #<YAML::Syck::Node:0x82ccce0
  153:     #           @kind=:seq,
  154:     #           @value=
  155:     #            [#<YAML::Syck::Node:0x82ccd94
  156:     #              @kind=:scalar,
  157:     #              @type_id="str",
  158:     #              @value="badger">,
  159:     #             #<YAML::Syck::Node:0x82ccd58
  160:     #              @kind=:scalar,
  161:     #              @type_id="str",
  162:     #              @value="elephant">,
  163:     #             #<YAML::Syck::Node:0x82ccd1c
  164:     #              @kind=:scalar,
  165:     #              @type_id="str",
  166:     #              @value="tiger">]>
  167:     #
  168:     # Can also load from a string.
  169:     #
  170:     #   YAML.parse( "--- :locked" )
  171:     #      #=> #<YAML::Syck::Node:0x82edddc 
  172:     #            @type_id="tag:ruby.yaml.org,2002:sym", 
  173:     #            @value=":locked", @kind=:scalar>
  174:     #
  175:     def YAML.parse( io )
  176:         yp = generic_parser.load( io )
  177:     end
  178: 
  179:     #
  180:     # Parse a document from the file located at _filepath_.
  181:     #
  182:     #   YAML.parse_file( 'animals.yaml' )
  183:     #      #=> #<YAML::Syck::Node:0x82ccce0
  184:     #           @kind=:seq,
  185:     #           @value=
  186:     #            [#<YAML::Syck::Node:0x82ccd94
  187:     #              @kind=:scalar,
  188:     #              @type_id="str",
  189:     #              @value="badger">,
  190:     #             #<YAML::Syck::Node:0x82ccd58
  191:     #              @kind=:scalar,
  192:     #              @type_id="str",
  193:     #              @value="elephant">,
  194:     #             #<YAML::Syck::Node:0x82ccd1c
  195:     #              @kind=:scalar,
  196:     #              @type_id="str",
  197:     #              @value="tiger">]>
  198:     #
  199:     def YAML.parse_file( filepath )
  200:         File.open( filepath ) do |f|
  201:             parse( f )
  202:         end
  203:     end
  204: 
  205:     #
  206:     # Calls _block_ with each consecutive document in the YAML
  207:     # stream contained in _io_.
  208:     #
  209:     #   File.open( 'many-docs.yaml' ) do |yf|
  210:     #     YAML.each_document( yf ) do |ydoc|
  211:     #       ## ydoc contains the single object
  212:     #       ## from the YAML document
  213:     #     end
  214:     #   end
  215:     #
  216:     def YAML.each_document( io, &block )
  217:         yp = parser.load_documents( io, &block )
  218:     end
  219: 
  220:     #
  221:     # Calls _block_ with each consecutive document in the YAML
  222:     # stream contained in _io_.
  223:     #
  224:     #   File.open( 'many-docs.yaml' ) do |yf|
  225:     #     YAML.load_documents( yf ) do |ydoc|
  226:     #       ## ydoc contains the single object
  227:     #       ## from the YAML document
  228:     #     end
  229:     #   end
  230:     #
  231:     def YAML.load_documents( io, &doc_proc )
  232:         YAML.each_document( io, &doc_proc )
  233:     end
  234: 
  235:     #
  236:     # Calls _block_ with a tree of +YAML::BaseNodes+, one tree for
  237:     # each consecutive document in the YAML stream contained in _io_.
  238:     #
  239:     #   File.open( 'many-docs.yaml' ) do |yf|
  240:     #     YAML.each_node( yf ) do |ydoc|
  241:     #       ## ydoc contains a tree of nodes
  242:     #       ## from the YAML document
  243:     #     end
  244:     #   end
  245:     #
  246:     def YAML.each_node( io, &doc_proc )
  247:         yp = generic_parser.load_documents( io, &doc_proc )
  248:     end
  249: 
  250:     #
  251:     # Calls _block_ with a tree of +YAML::BaseNodes+, one tree for
  252:     # each consecutive document in the YAML stream contained in _io_.
  253:     #
  254:     #   File.open( 'many-docs.yaml' ) do |yf|
  255:     #     YAML.parse_documents( yf ) do |ydoc|
  256:     #       ## ydoc contains a tree of nodes
  257:     #       ## from the YAML document
  258:     #     end
  259:     #   end
  260:     #
  261:     def YAML.parse_documents( io, &doc_proc )
  262:         YAML.each_node( io, &doc_proc )
  263:     end
  264: 
  265:     #
  266:     # Loads all documents from the current _io_ stream, 
  267:     # returning a +YAML::Stream+ object containing all
  268:     # loaded documents.
  269:     #
  270:     def YAML.load_stream( io )
  271:         d = nil
  272:         parser.load_documents( io ) do |doc|
  273:             d = YAML::Stream.new if not d
  274:             d.add( doc ) 
  275:         end
  276:         return d
  277:     end
  278: 
  279:     #
  280:     # Returns a YAML stream containing each of the items in +objs+,
  281:     # each having their own document.
  282:     #
  283:     #   YAML.dump_stream( 0, [], {} )
  284:     #     #=> --- 0
  285:     #         --- []
  286:     #         --- {}
  287:     #
  288:     def YAML.dump_stream( *objs )
  289:         d = YAML::Stream.new
  290:         objs.each do |doc|
  291:             d.add( doc ) 
  292:         end
  293:         d.emit
  294:     end
  295: 
  296:     #
  297:     # Add a global handler for a YAML domain type.
  298:     #
  299:     def YAML.add_domain_type( domain, type_tag, &transfer_proc )
  300:         resolver.add_type( "tag:#{ domain }:#{ type_tag }", transfer_proc )
  301:     end
  302: 
  303:     #
  304:     # Add a transfer method for a builtin type
  305:     #
  306:     def YAML.add_builtin_type( type_tag, &transfer_proc )
  307:         resolver.add_type( "tag:yaml.org,2002:#{ type_tag }", transfer_proc )
  308:     end
  309: 
  310:     #
  311:     # Add a transfer method for a builtin type
  312:     #
  313:     def YAML.add_ruby_type( type_tag, &transfer_proc )
  314:         resolver.add_type( "tag:ruby.yaml.org,2002:#{ type_tag }", transfer_proc )
  315:     end
  316: 
  317:     #
  318:     # Add a private document type
  319:     #
  320:     def YAML.add_private_type( type_re, &transfer_proc )
  321:         resolver.add_type( "x-private:" + type_re, transfer_proc )
  322:     end
  323: 
  324:     #
  325:     # Detect typing of a string
  326:     #
  327:     def YAML.detect_implicit( val )
  328:         resolver.detect_implicit( val )
  329:     end
  330: 
  331:     #
  332:     # Convert a type_id to a taguri
  333:     #
  334:     def YAML.tagurize( val )
  335:         resolver.tagurize( val )
  336:     end
  337: 
  338:     #
  339:     # Apply a transfer method to a Ruby object
  340:     #
  341:     def YAML.transfer( type_id, obj )
  342:         resolver.transfer( YAML.tagurize( type_id ), obj )
  343:     end
  344: 
  345:     #
  346:     # Apply any implicit a node may qualify for
  347:     #
  348:     def YAML.try_implicit( obj )
  349:         YAML.transfer( YAML.detect_implicit( obj ), obj )
  350:     end
  351: 
  352:     #
  353:     # Method to extract colon-seperated type and class, returning
  354:     # the type and the constant of the class
  355:     #
  356:     def YAML.read_type_class( type, obj_class )
  357:         scheme, domain, type, tclass = type.split( ':', 4 )
  358:         tclass.split( "::" ).each { |c| obj_class = obj_class.const_get( c ) } if tclass
  359:         return [ type, obj_class ]
  360:     end
  361: 
  362:     #
  363:     # Allocate blank object
  364:     #
  365:     def YAML.object_maker( obj_class, val )
  366:         if Hash === val
  367:             o = obj_class.allocate
  368:             val.each_pair { |k,v|
  369:                 o.instance_variable_set("@#{k}", v)
  370:             }
  371:             o
  372:         else
  373:             raise YAML::Error, "Invalid object explicitly tagged !ruby/Object: " + val.inspect
  374:         end
  375:     end
  376: 
  377:     #
  378:     # Allocate an Emitter if needed
  379:     #
  380:     def YAML.quick_emit( oid, opts = {}, &e )
  381:         out = 
  382:             if opts.is_a? YAML::Emitter
  383:                 opts
  384:             else
  385:                 emitter.reset( opts )
  386:             end
  387:         oid =
  388:             case oid when Fixnum, NilClass; oid
  389:             else oid = "#{oid.object_id}-#{oid.hash}"
  390:             end
  391:         out.emit( oid, &e )
  392:     end
  393:     
  394: end
  395: 
  396: require 'yaml/rubytypes'
  397: require 'yaml/types'
  398: 
  399: module Kernel
  400:     #
  401:     # ryan:: You know how Kernel.p is a really convenient way to dump ruby
  402:     #        structures?  The only downside is that it's not as legible as
  403:     #        YAML.
  404:     #
  405:     # _why:: (listening)
  406:     #
  407:     # ryan:: I know you don't want to urinate all over your users' namespaces.
  408:     #        But, on the other hand, convenience of dumping for debugging is,
  409:     #        IMO, a big YAML use case.
  410:     #
  411:     # _why:: Go nuts!  Have a pony parade!
  412:     #
  413:     # ryan:: Either way, I certainly will have a pony parade.
  414:     #
  415: 
  416:     # Prints any supplied _objects_ out in YAML.  Intended as
  417:     # a variation on +Kernel::p+.
  418:     #
  419:     #   S = Struct.new(:name, :state)
  420:     #   s = S['dave', 'TX']
  421:     #   y s
  422:     #
  423:     # _produces:_
  424:     #
  425:     #   --- !ruby/struct:S 
  426:     #   name: dave
  427:     #   state: TX
  428:     #
  429:     def y( object, *objects )
  430:         objects.unshift object
  431:         puts( if objects.length == 1
  432:                   YAML::dump( *objects )
  433:               else
  434:                   YAML::dump_stream( *objects )
  435:               end )
  436:     end
  437:     private :y
  438: end
  439: 
  440: 
Syntax (Markdown)