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