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