Psych
is a YAML
parser and emitter. Psych
leverages libyaml [Home page: pyyaml.org/wiki/LibYAML] or [git repo: github.com/yaml/libyaml] for its YAML
parsing and emitting capabilities. In addition to wrapping libyaml, Psych
also knows how to serialize and de-serialize most Ruby objects to and from the YAML
format.
YAML
RIGHT NOW! # Parse some YAML Psych.load("--- foo") # => "foo" # Emit some YAML Psych.dump("foo") # => "--- foo\n...\n" { :a => 'b'}.to_yaml # => "---\n:a: b\n"
Got more time on your hands? Keep on reading!
YAML
Parsing Psych
provides a range of interfaces for parsing a YAML
document ranging from low level to high level, depending on your parsing needs. At the lowest level, is an event based parser. Mid level is access to the raw YAML
AST, and at the highest level is the ability to unmarshal YAML
to Ruby objects.
YAML
Emitting Psych
provides a range of interfaces ranging from low to high level for producing YAML
documents. Very similar to the YAML
parsing interfaces, Psych
provides at the lowest level, an event based system, mid-level is building a YAML
AST, and the highest level is converting a Ruby object straight to a YAML
document.
The high level YAML
parser provided by Psych
simply takes YAML
as input and returns a Ruby data structure. For information on using the high level parser see Psych.load
Psych.safe_load("--- a") # => 'a' Psych.safe_load("---\n - a\n - b") # => ['a', 'b'] # From a trusted string: Psych.load("--- !ruby/range\nbegin: 0\nend: 42\nexcl: false\n") # => 0..42
Psych.safe_load_file("data.yml", permitted_classes: [Date]) Psych.load_file("trusted_database.yml")
Exception
handling begin # The second argument changes only the exception contents Psych.parse("--- `", "file.txt") rescue Psych::SyntaxError => ex ex.file # => 'file.txt' ex.message # => "(file.txt): found character that cannot start any token" end
The high level emitter has the easiest interface. Psych
simply takes a Ruby data structure and converts it to a YAML
document. See Psych.dump
for more information on dumping a Ruby data structure.
# Dump an array, get back a YAML string Psych.dump(['a', 'b']) # => "---\n- a\n- b\n" # Dump an array to an IO object Psych.dump(['a', 'b'], StringIO.new) # => #<StringIO:0x000001009d0890> # Dump an array with indentation set Psych.dump(['a', ['b']], :indentation => 3) # => "---\n- a\n- - b\n" # Dump an array to an IO with indentation set Psych.dump(['a', ['b']], StringIO.new, :indentation => 3)
Currently there is no direct API for dumping Ruby structure to file:
File.open('database.yml', 'w') do |file| file.write(Psych.dump(['a', 'b'])) end
Psych
provides access to an AST produced from parsing a YAML
document. This tree is built using the Psych::Parser
and Psych::TreeBuilder
. The AST can be examined and manipulated freely. Please see Psych::parse_stream
, Psych::Nodes
, and Psych::Nodes::Node
for more information on dealing with YAML
syntax trees.
# Returns Psych::Nodes::Stream Psych.parse_stream("---\n - a\n - b") # Returns Psych::Nodes::Document Psych.parse("---\n - a\n - b")
# Returns Psych::Nodes::Stream Psych.parse_stream(File.read('database.yml')) # Returns Psych::Nodes::Document Psych.parse_file('database.yml')
Exception
handling begin # The second argument changes only the exception contents Psych.parse("--- `", "file.txt") rescue Psych::SyntaxError => ex ex.file # => 'file.txt' ex.message # => "(file.txt): found character that cannot start any token" end
At the mid level is building an AST. This AST is exactly the same as the AST used when parsing a YAML
document. Users can build an AST by hand and the AST knows how to emit itself as a YAML
document. See Psych::Nodes
, Psych::Nodes::Node
, and Psych::TreeBuilder
for more information on building a YAML
AST.
# We need Psych::Nodes::Stream (not Psych::Nodes::Document) stream = Psych.parse_stream("---\n - a\n - b") stream.to_yaml # => "---\n- a\n- b\n"
# We need Psych::Nodes::Stream (not Psych::Nodes::Document) stream = Psych.parse_stream(File.read('database.yml')) File.open('database.yml', 'w') do |file| file.write(stream.to_yaml) end
The lowest level parser should be used when the YAML
input is already known, and the developer does not want to pay the price of building an AST or automatic detection and conversion to Ruby objects. See Psych::Parser
for more information on using the event based parser.
Psych::Nodes::Stream
structure parser = Psych::Parser.new(TreeBuilder.new) # => #<Psych::Parser> parser = Psych.parser # it's an alias for the above parser.parse("---\n - a\n - b") # => #<Psych::Parser> parser.handler # => #<Psych::TreeBuilder> parser.handler.root # => #<Psych::Nodes::Stream>
recorder = Psych::Handlers::Recorder.new parser = Psych::Parser.new(recorder) parser.parse("---\n - a\n - b") recorder.events # => [list of [event, args] lists] # event is one of: Psych::Handler::EVENTS # args are the arguments passed to the event
The lowest level emitter is an event based system. Events are sent to a Psych::Emitter
object. That object knows how to convert the events to a YAML
document. This interface should be used when document format is known in advance or speed is a concern. See Psych::Emitter
for more information.
Psych.parser.parse("--- a") # => #<Psych::Parser> parser.handler.first # => #<Psych::Nodes::Stream> parser.handler.first.to_ruby # => ["a"] parser.handler.root.first # => #<Psych::Nodes::Document> parser.handler.root.first.to_ruby # => "a" # You can instantiate an Emitter manually Psych::Visitors::ToRuby.new.accept(parser.handler.root.first) # => "a"
If an object defines encode_with
, then an instance of Psych::Coder
will be passed to the method when the object is being serialized. The Coder
automatically assumes a Psych::Nodes::Mapping
is being emitted. Other objects like Sequence and Scalar may be emitted if seq=
or scalar=
are called, respectively.
Psych::Handler
is an abstract base class that defines the events used when dealing with Psych::Parser
. Clients who want to use Psych::Parser
should implement a class that inherits from Psych::Handler
and define events that they can handle.
Psych::Handler
defines all events that Psych::Parser
can possibly send to event handlers.
See Psych::Parser
for more details
YAML
event parser class. This class parses a YAML
document and calls events on the handler that is passed to the constructor. The events can be used for things such as constructing a YAML
AST or deserializing YAML
documents. It can even be fed back to Psych::Emitter
to emit the same document that was parsed.
See Psych::Handler
for documentation on the events that Psych::Parser
emits.
Here is an example that prints out ever scalar found in a YAML
document:
# Handler for detecting scalar values class ScalarHandler < Psych::Handler def scalar value, anchor, tag, plain, quoted, style puts value end end parser = Psych::Parser.new(ScalarHandler.new) parser.parse(yaml_document)
Here is an example that feeds the parser back in to Psych::Emitter
. The YAML
document is read from STDIN and written back out to STDERR:
parser = Psych::Parser.new(Psych::Emitter.new($stderr)) parser.parse($stdin)
Psych
uses Psych::Parser
in combination with Psych::TreeBuilder
to construct an AST of the parsed YAML
document.
Scan scalars for built in types
Psych::Stream
is a streaming YAML
emitter. It will not buffer your YAML
, but send it straight to an IO
.
Here is an example use:
stream = Psych::Stream.new($stdout) stream.start stream.push({:foo => 'bar'}) stream.finish
YAML
will be immediately emitted to $stdout with no buffering.
Psych::Stream#start
will take a block and ensure that Psych::Stream#finish
is called, so you can do this form:
stream = Psych::Stream.new($stdout) stream.start do |em| em.push(:foo => 'bar') end
This class works in conjunction with Psych::Parser
to build an in-memory parse tree that represents a YAML
document.
parser = Psych::Parser.new Psych::TreeBuilder.new parser.parse('--- foo') tree = parser.handler.root
See Psych::Handler
for documentation on the event methods used in this class.
When using Psych.load
to deserialize a YAML
document, the document is translated to an intermediary AST. That intermediary AST is then translated in to a Ruby object graph.
In the opposite direction, when using Psych.dump
, the Ruby object graph is translated to an intermediary AST which is then converted to a YAML
document.
Psych::Nodes
contains all of the classes that make up the nodes of a YAML
AST. You can manually build an AST and use one of the visitors (see Psych::Visitors
) to convert that AST to either a YAML
document or to a Ruby object graph.
Here is an example of building an AST that represents a list with one scalar:
# Create our nodes stream = Psych::Nodes::Stream.new doc = Psych::Nodes::Document.new seq = Psych::Nodes::Sequence.new scalar = Psych::Nodes::Scalar.new('foo') # Build up our tree stream.children << doc doc.children << seq seq.children << scalar
The stream is the root of the tree. We can then convert the tree to YAML:
stream.to_yaml => "---\n- foo\n"
Or convert it to Ruby:
stream.to_ruby => [["foo"]]
YAML
AST Requirements A valid YAML
AST must have one Psych::Nodes::Stream
at the root. A Psych::Nodes::Stream
node must have 1 or more Psych::Nodes::Document
nodes as children.
Psych::Nodes::Document
nodes must have one and only one child. That child may be one of:
Psych::Nodes::Sequence
and Psych::Nodes::Mapping
nodes may have many children, but Psych::Nodes::Mapping
nodes should have an even number of children.
All of these are valid children for Psych::Nodes::Sequence
and Psych::Nodes::Mapping
nodes:
Psych::Nodes::Scalar
and Psych::Nodes::Alias
are both terminal nodes and should not have any children.
Configuration options for dumping YAML
.
This handler will capture an event and record the event. Recorder
events are available vial Psych::Handlers::Recorder#events
.
For example:
recorder = Psych::Handlers::Recorder.new parser = Psych::Parser.new recorder parser.parse '--- foo' recorder.events # => [list of events] # Replay the events emitter = Psych::Emitter.new $stdout recorder.events.each do |m, args| emitter.send m, *args end