1require 'psych.so'
2require 'psych/nodes'
3require 'psych/streaming'
4require 'psych/visitors'
5require 'psych/handler'
6require 'psych/tree_builder'
7require 'psych/parser'
8require 'psych/omap'
9require 'psych/set'
10require 'psych/coder'
11require 'psych/core_ext'
12require 'psych/deprecated'
13require 'psych/stream'
14require 'psych/json/tree_builder'
15require 'psych/json/stream'
16require 'psych/handlers/document_stream'
17
18###
19# = Overview
20#
21# Psych is a YAML parser and emitter.
22# Psych leverages libyaml [Home page: http://pyyaml.org/wiki/LibYAML]
23# or [Git repo: https://github.com/zerotao/libyaml] for its YAML parsing
24# and emitting capabilities. In addition to wrapping libyaml, Psych also
25# knows how to serialize and de-serialize most Ruby objects to and from
26# the YAML format.
27#
28# = I NEED TO PARSE OR EMIT YAML RIGHT NOW!
29#
30#   # Parse some YAML
31#   Psych.load("--- foo") # => "foo"
32#
33#   # Emit some YAML
34#   Psych.dump("foo")     # => "--- foo\n...\n"
35#   { :a => 'b'}.to_yaml  # => "---\n:a: b\n"
36#
37# Got more time on your hands?  Keep on reading!
38#
39# == YAML Parsing
40#
41# Psych provides a range of interfaces for parsing a YAML document ranging from
42# low level to high level, depending on your parsing needs.  At the lowest
43# level, is an event based parser.  Mid level is access to the raw YAML AST,
44# and at the highest level is the ability to unmarshal YAML to ruby objects.
45#
46# === Low level parsing
47#
48# The lowest level parser should be used when the YAML input is already known,
49# and the developer does not want to pay the price of building an AST or
50# automatic detection and conversion to ruby objects.  See Psych::Parser for
51# more information on using the event based parser.
52#
53# === Mid level parsing
54#
55# Psych provides access to an AST produced from parsing a YAML document.  This
56# tree is built using the Psych::Parser and Psych::TreeBuilder.  The AST can
57# be examined and manipulated freely.  Please see Psych::parse_stream,
58# Psych::Nodes, and Psych::Nodes::Node for more information on dealing with
59# YAML syntax trees.
60#
61# === High level parsing
62#
63# The high level YAML parser provided by Psych simply takes YAML as input and
64# returns a Ruby data structure.  For information on using the high level parser
65# see Psych.load
66#
67# == YAML Emitting
68#
69# Psych provides a range of interfaces ranging from low to high level for
70# producing YAML documents.  Very similar to the YAML parsing interfaces, Psych
71# provides at the lowest level, an event based system, mid-level is building
72# a YAML AST, and the highest level is converting a Ruby object straight to
73# a YAML document.
74#
75# === Low level emitting
76#
77# The lowest level emitter is an event based system.  Events are sent to a
78# Psych::Emitter object.  That object knows how to convert the events to a YAML
79# document.  This interface should be used when document format is known in
80# advance or speed is a concern.  See Psych::Emitter for more information.
81#
82# === Mid level emitting
83#
84# At the mid level is building an AST.  This AST is exactly the same as the AST
85# used when parsing a YAML document.  Users can build an AST by hand and the
86# AST knows how to emit itself as a YAML document.  See Psych::Nodes,
87# Psych::Nodes::Node, and Psych::TreeBuilder for more information on building
88# a YAML AST.
89#
90# === High level emitting
91#
92# The high level emitter has the easiest interface.  Psych simply takes a Ruby
93# data structure and converts it to a YAML document.  See Psych.dump for more
94# information on dumping a Ruby data structure.
95
96module Psych
97  # The version is Psych you're using
98  VERSION         = '2.0.0'
99
100  # The version of libyaml Psych is using
101  LIBYAML_VERSION = Psych.libyaml_version.join '.'
102
103  class Exception < RuntimeError
104  end
105
106  class BadAlias < Exception
107  end
108
109  ###
110  # Load +yaml+ in to a Ruby data structure.  If multiple documents are
111  # provided, the object contained in the first document will be returned.
112  # +filename+ will be used in the exception message if any exception is raised
113  # while parsing.
114  #
115  # Raises a Psych::SyntaxError when a YAML syntax error is detected.
116  #
117  # Example:
118  #
119  #   Psych.load("--- a")             # => 'a'
120  #   Psych.load("---\n - a\n - b")   # => ['a', 'b']
121  #
122  #   begin
123  #     Psych.load("--- `", "file.txt")
124  #   rescue Psych::SyntaxError => ex
125  #     ex.file    # => 'file.txt'
126  #     ex.message # => "(file.txt): found character that cannot start any token"
127  #   end
128  def self.load yaml, filename = nil
129    result = parse(yaml, filename)
130    result ? result.to_ruby : result
131  end
132
133  ###
134  # Parse a YAML string in +yaml+.  Returns the first object of a YAML AST.
135  # +filename+ is used in the exception message if a Psych::SyntaxError is
136  # raised.
137  #
138  # Raises a Psych::SyntaxError when a YAML syntax error is detected.
139  #
140  # Example:
141  #
142  #   Psych.parse("---\n - a\n - b") # => #<Psych::Nodes::Sequence:0x00>
143  #
144  #   begin
145  #     Psych.parse("--- `", "file.txt")
146  #   rescue Psych::SyntaxError => ex
147  #     ex.file    # => 'file.txt'
148  #     ex.message # => "(file.txt): found character that cannot start any token"
149  #   end
150  #
151  # See Psych::Nodes for more information about YAML AST.
152  def self.parse yaml, filename = nil
153    parse_stream(yaml, filename) do |node|
154      return node
155    end
156    false
157  end
158
159  ###
160  # Parse a file at +filename+. Returns the YAML AST.
161  #
162  # Raises a Psych::SyntaxError when a YAML syntax error is detected.
163  def self.parse_file filename
164    File.open filename, 'r:bom|utf-8' do |f|
165      parse f, filename
166    end
167  end
168
169  ###
170  # Returns a default parser
171  def self.parser
172    Psych::Parser.new(TreeBuilder.new)
173  end
174
175  ###
176  # Parse a YAML string in +yaml+.  Returns the full AST for the YAML document.
177  # This method can handle multiple YAML documents contained in +yaml+.
178  # +filename+ is used in the exception message if a Psych::SyntaxError is
179  # raised.
180  #
181  # If a block is given, a Psych::Nodes::Document node will be yielded to the
182  # block as it's being parsed.
183  #
184  # Raises a Psych::SyntaxError when a YAML syntax error is detected.
185  #
186  # Example:
187  #
188  #   Psych.parse_stream("---\n - a\n - b") # => #<Psych::Nodes::Stream:0x00>
189  #
190  #   Psych.parse_stream("--- a\n--- b") do |node|
191  #     node # => #<Psych::Nodes::Document:0x00>
192  #   end
193  #
194  #   begin
195  #     Psych.parse_stream("--- `", "file.txt")
196  #   rescue Psych::SyntaxError => ex
197  #     ex.file    # => 'file.txt'
198  #     ex.message # => "(file.txt): found character that cannot start any token"
199  #   end
200  #
201  # See Psych::Nodes for more information about YAML AST.
202  def self.parse_stream yaml, filename = nil, &block
203    if block_given?
204      parser = Psych::Parser.new(Handlers::DocumentStream.new(&block))
205      parser.parse yaml, filename
206    else
207      parser = self.parser
208      parser.parse yaml, filename
209      parser.handler.root
210    end
211  end
212
213  ###
214  # call-seq:
215  #   Psych.dump(o)               -> string of yaml
216  #   Psych.dump(o, options)      -> string of yaml
217  #   Psych.dump(o, io)           -> io object passed in
218  #   Psych.dump(o, io, options)  -> io object passed in
219  #
220  # Dump Ruby object +o+ to a YAML string.  Optional +options+ may be passed in
221  # to control the output format.  If an IO object is passed in, the YAML will
222  # be dumped to that IO object.
223  #
224  # Example:
225  #
226  #   # Dump an array, get back a YAML string
227  #   Psych.dump(['a', 'b'])  # => "---\n- a\n- b\n"
228  #
229  #   # Dump an array to an IO object
230  #   Psych.dump(['a', 'b'], StringIO.new)  # => #<StringIO:0x000001009d0890>
231  #
232  #   # Dump an array with indentation set
233  #   Psych.dump(['a', ['b']], :indentation => 3) # => "---\n- a\n-  - b\n"
234  #
235  #   # Dump an array to an IO with indentation set
236  #   Psych.dump(['a', ['b']], StringIO.new, :indentation => 3)
237  def self.dump o, io = nil, options = {}
238    if Hash === io
239      options = io
240      io      = nil
241    end
242
243    visitor = Psych::Visitors::YAMLTree.new options
244    visitor << o
245    visitor.tree.yaml io, options
246  end
247
248  ###
249  # Dump a list of objects as separate documents to a document stream.
250  #
251  # Example:
252  #
253  #   Psych.dump_stream("foo\n  ", {}) # => "--- ! \"foo\\n  \"\n--- {}\n"
254  def self.dump_stream *objects
255    visitor = Psych::Visitors::YAMLTree.new {}
256    objects.each do |o|
257      visitor << o
258    end
259    visitor.tree.yaml
260  end
261
262  ###
263  # Dump Ruby object +o+ to a JSON string.
264  def self.to_json o
265    visitor = Psych::Visitors::JSONTree.new
266    visitor << o
267    visitor.tree.yaml
268  end
269
270  ###
271  # Load multiple documents given in +yaml+.  Returns the parsed documents
272  # as a list.  If a block is given, each document will be converted to ruby
273  # and passed to the block during parsing
274  #
275  # Example:
276  #
277  #   Psych.load_stream("--- foo\n...\n--- bar\n...") # => ['foo', 'bar']
278  #
279  #   list = []
280  #   Psych.load_stream("--- foo\n...\n--- bar\n...") do |ruby|
281  #     list << ruby
282  #   end
283  #   list # => ['foo', 'bar']
284  #
285  def self.load_stream yaml, filename = nil
286    if block_given?
287      parse_stream(yaml, filename) do |node|
288        yield node.to_ruby
289      end
290    else
291      parse_stream(yaml, filename).children.map { |child| child.to_ruby }
292    end
293  end
294
295  ###
296  # Load the document contained in +filename+.  Returns the yaml contained in
297  # +filename+ as a ruby object
298  def self.load_file filename
299    File.open(filename, 'r:bom|utf-8') { |f| self.load f, filename }
300  end
301
302  # :stopdoc:
303  @domain_types = {}
304  def self.add_domain_type domain, type_tag, &block
305    key = ['tag', domain, type_tag].join ':'
306    @domain_types[key] = [key, block]
307    @domain_types["tag:#{type_tag}"] = [key, block]
308  end
309
310  def self.add_builtin_type type_tag, &block
311    domain = 'yaml.org,2002'
312    key = ['tag', domain, type_tag].join ':'
313    @domain_types[key] = [key, block]
314  end
315
316  def self.remove_type type_tag
317    @domain_types.delete type_tag
318  end
319
320  @load_tags = {}
321  @dump_tags = {}
322  def self.add_tag tag, klass
323    @load_tags[tag] = klass
324    @dump_tags[klass] = tag
325  end
326
327  class << self
328    attr_accessor :load_tags
329    attr_accessor :dump_tags
330    attr_accessor :domain_types
331  end
332  # :startdoc:
333end
334