PageRenderTime 95ms CodeModel.GetById 20ms app.highlight 54ms RepoModel.GetById 16ms app.codeStats 1ms

/tools/Ruby/lib/ruby/1.8/yaml.rb

http://github.com/agross/netopenspace
Ruby | 440 lines | 131 code | 32 blank | 277 comment | 9 complexity | 9353e044a12eab8a75b8221bba5cc8c9 MD5 | raw file
  1# -*- mode: ruby; ruby-indent-level: 4; tab-width: 4 -*- vim: sw=4 ts=4
  2# $Id: yaml.rb 16084 2008-04-19 11:45:39Z knu $
  3#
  4# = yaml.rb: top-level module with methods for loading and parsing YAML documents
  5#
  6# Author:: why the lucky stiff
  7# 
  8
  9require 'stringio'
 10require 'yaml/error'
 11require 'yaml/syck'
 12require 'yaml/tag'
 13require 'yaml/stream'
 14require '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#
 85module 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	
394end
395
396require 'yaml/rubytypes'
397require 'yaml/types'
398
399module 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
438end
439
440