* io.c (rb_open_file): encoding in mode string was ignored if perm is
[ruby-svn.git] / lib / yaml.rb
bloba8da42a32179b4bed49c6b74ef01b1e79147630a
1 # -*- mode: ruby; ruby-indent-level: 4; tab-width: 4 -*- vim: sw=4 ts=4
2 # $Id$
4 # = yaml.rb: top-level module with methods for loading and parsing YAML documents
6 # Author:: why the lucky stiff
7
9 require 'stringio'
10 require 'yaml/error'
11 require 'yaml/syck'
12 require 'yaml/tag'
13 require 'yaml/stream'
14 require 'yaml/constants'
16 # == YAML
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+.)
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).
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).
61 # == Example
63 # A simple round-trip (load and dump) of an object.
65 #     require "yaml"
67 #     test_obj = ["dogs", "cats", "badgers"]
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
79 # To register your custom types with the global resolver, use +add_domain_type+.
81 #     YAML::add_domain_type( "your-site.com,2004", "widget" ) do |type, val|
82 #         Widget.new( val )
83 #     end
85 module YAML
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
324     #
325     # Detect typing of a string
326     #
327     def YAML.detect_implicit( val )
328         resolver.detect_implicit( val )
329     end
331     #
332     # Convert a type_id to a taguri
333     #
334     def YAML.tagurize( val )
335         resolver.tagurize( val )
336     end
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
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
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
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
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         
396 require 'yaml/rubytypes'
397 require 'yaml/types'
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     #
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