Yaml Cookbook
Welcome to the Yaml Cookbook for Ruby. This version of the Yaml Cookbook focuses on the
Ruby implementation of Yaml by comparing Yaml documents with their Ruby counterparts.
YAML(tm) is a readable text format for data structures. As you'll
see below, YAML can handle many common data types and structures. And what
YAML can't handle natively can be supported through flexible type families.
For example, YAML for Ruby uses type families to support storage of regular
expressions, ranges and object instances.
You can learn more about YAML at YAML.org or the
YAML WikiWiki.
Brief
You can specify a list in YAML by placing each
member of the list on a new line with an opening
dash. These lists are called sequences.
Yaml
|
- apple
- banana
- carrot
|
|
|
Ruby
|
['apple', 'banana', 'carrot']
|
|
|
Brief
You can include a sequence within another
sequence by giving the sequence an empty
dash, followed by an indented list.
Yaml
Ruby
Brief
Sequences can contain any YAML data,
including strings and other sequences.
Yaml
|
- apple
-
- foo
- bar
- x123
- banana
- carrot
|
|
|
Ruby
|
['apple', ['foo', 'bar', 'x123'], 'banana', 'carrot']
|
|
|
Brief
Sequences can be nested even deeper, with each
level of indentation representing a level of
depth.
Yaml
Ruby
Brief
You can add a keyed list (also known as a dictionary or
hash) to your document by placing each member of the
list on a new line, with a colon seperating the key
from its value. In YAML, this type of list is called
a mapping.
Yaml
Ruby
|
{ 'foo' => 'whatever', 'bar' => 'stuff' }
|
|
|
Brief
A value in a mapping can be a sequence.
Yaml
|
foo: whatever
bar:
- uno
- dos
|
|
|
Ruby
|
{ 'foo' => 'whatever', 'bar' => [ 'uno', 'dos' ] }
|
|
|
Brief
A value in a mapping can be another mapping.
Yaml
|
foo: whatever
bar:
fruit: apple
name: steve
sport: baseball
|
|
|
Ruby
|
{ 'foo' => 'whatever',
'bar' => {
'fruit' => 'apple',
'name' => 'steve',
'sport' => 'baseball'
}
}
|
|
|
Brief
A mapping can contain any assortment
of mappings and sequences as values.
Yaml
|
foo: whatever
bar:
-
fruit: apple
name: steve
sport: baseball
- more
-
python: rocks
perl: papers
ruby: scissorses
|
|
|
Ruby
|
{ 'foo' => 'whatever',
'bar' => [
{
'fruit' => 'apple',
'name' => 'steve',
'sport' => 'baseball'
},
'more',
{
'python' => 'rocks',
'perl' => 'papers',
'ruby' => 'scissorses'
}
]
}
|
|
|
Brief
If you are adding a mapping to a sequence, you
can place the mapping on the same line as the
dash as a shortcut.
Yaml
|
- work on YAML.py:
- work on Store
|
|
|
Ruby
|
[ { 'work on YAML.py' => ['work on Store'] } ]
|
|
|
Brief
The dash in a sequence counts as indentation, so
you can add a sequence inside of a mapping without
needing spaces as indentation.
Yaml
|
allow:
- 'localhost'
- '%.sourceforge.net'
- '%.freepan.org'
|
|
|
Ruby
|
{ 'allow' => [ 'localhost', '%.sourceforge.net', '%.freepan.org' ] }
|
|
|
Brief
A merge key ('<<') can be used in a mapping to insert other mappings. If
the value associated with the merge key is a mapping, each of its key/value
pairs is inserted into the current mapping.
Yaml
|
mapping:
name: Joe
job: Accountant
<<:
age: 38
|
|
|
Ruby
|
{ 'mapping' =>
{ 'name' => 'Joe',
'job' => 'Accountant',
'age' => 38
}
}
|
|
|
Brief
Sequences can be contained on a single line, using the inline syntax. Separate each entry with commas and enclose in square brackets.
Yaml
Ruby
|
{ 'seq' => [ 'a', 'b', 'c' ] }
|
|
|
Brief
Mapping can also be contained on a single line, using the inline syntax. Each key-value pair is separated by a colon, with a comma between each entry in the mapping. Enclose with curly braces.
Yaml
|
---
hash: { name: Steve, foo: bar }
|
|
|
Ruby
|
{ 'hash' => { 'name' => 'Steve', 'foo' => 'bar' } }
|
|
|
Brief
Both inline sequences and inline mappings can span multiple lines, provided that you indent the additional lines.
Yaml
|
languages: [ Ruby,
Perl,
Python ]
websites: { YAML: yaml.org,
Ruby: ruby-lang.org,
Python: python.org,
Perl: use.perl.org }
|
|
|
Ruby
|
{ 'languages' => [ 'Ruby', 'Perl', 'Python' ],
'websites' => {
'YAML' => 'yaml.org',
'Ruby' => 'ruby-lang.org',
'Python' => 'python.org',
'Perl' => 'use.perl.org'
}
}
|
|
|
Brief
List items in collections are delimited by commas, but there must be a space after each comma. This allows you to add numbers without quoting.
Yaml
|
attendances: [ 45,123, 70,000, 17,222 ]
|
|
|
Ruby
|
{ 'attendances' => [ 45123, 70000, 17222 ] }
|
|
|
Brief
Any group of characters beginning with an alphabetic or numeric character is a string, unless it belongs to one of the groups below (such as an Integer or Time).
Yaml
Ruby
Brief
A string can contain any alphabetic or numeric character, along with many punctuation characters, including the period, dash, space, quotes, exclamation, and question mark.
Yaml
|
- What's Yaml?
- It's for writing data structures in plain text.
- And?
- And what? That's not good enough for you?
- No, I mean, "And what about Yaml?"
- Oh, oh yeah. Uh.. Yaml for Ruby.
|
|
|
Ruby
|
[
"What's Yaml?",
"It's for writing data structures in plain text.",
"And?",
"And what? That's not good enough for you?",
"No, I mean, \"And what about Yaml?\"",
"Oh, oh yeah. Uh.. Yaml for Ruby."
]
|
|
|
Brief
Be careful using indicators in strings. In particular, the comma, colon, and pound sign must be used carefully.
Yaml
|
the colon followed by space is an indicator: but is a string:right here
same for the pound sign: here we have it#in a string
the comma can, honestly, be used in most cases: [ but not in, inline collections ]
|
|
|
Ruby
|
{
'the colon followed by space is an indicator' => 'but is a string:right here',
'same for the pound sign' => 'here we have it#in a string',
'the comma can, honestly, be used in most cases' => [ 'but not in', 'inline collections' ]
}
|
|
|
Brief
Any YAML type can be forced into a string using the explicit !str method.
Yaml
|
date string: !str 2001-08-01
number string: !str 192
|
|
|
Ruby
|
{
'date string' => '2001-08-01',
'number string' => '192'
}
|
|
|
Brief
You can also enclose your strings within single quotes, which allows use of slashes, colons, and other indicators freely. Inside single quotes, you can represent a single quote in your string by using two single quotes next to each other.
Yaml
|
all my favorite symbols: '#:!/%.)'
a few i hate: '&(*'
why do i hate them?: 'it''s very hard to explain'
|
|
|
Ruby
|
{
'all my favorite symbols' => '#:!/%.)',
'a few i hate' => '&(*',
'why do i hate them?' => 'it\'s very hard to explain'
}
|
|
|
Brief
Enclosing strings in double quotes allows you to use escapings to represent ASCII and Unicode characters.
Yaml
|
i know where i want my line breaks: "one here\nand another here\n"
|
|
|
Ruby
|
{
'i know where i want my line breaks' => "one here\nand another here\n"
}
|
|
|
Brief
Both single- and double-quoted strings may be carried on to new lines in your YAML document. They must be indented a step and indentation is interpreted as a single space.
Yaml
|
i want a long string: "so i'm going to
let it go on and on to other lines
until i end it with a quote."
|
|
|
Ruby
|
{ 'i want a long string' => "so i'm going to " +
"let it go on and on to other lines " +
"until i end it with a quote."
}
|
|
|
Brief
Unquoted strings may also span multiple lines, if they are free of YAML space indicators and indented.
Yaml
|
- My little toe is broken in two places;
- I'm crazy to have skied this way;
- I'm not the craziest he's seen, since there was always the German guy
who skied for 3 hours on a broken shin bone (just below the kneecap);
- Nevertheless, second place is respectable, and he doesn't
recommend going for the record;
- He's going to put my foot in plaster for a month;
- This would impair my skiing ability somewhat for the
duration, as can be imagined.
|
|
|
Ruby
|
[
"My little toe is broken in two places;",
"I'm crazy to have skied this way;",
"I'm not the craziest he's seen, since there was always " +
"the German guy who skied for 3 hours on a broken shin " +
"bone (just below the kneecap);",
"Nevertheless, second place is respectable, and he doesn't " +
"recommend going for the record;",
"He's going to put my foot in plaster for a month;",
"This would impair my skiing ability somewhat for the duration, " +
"as can be imagined."
]
|
|
|
Brief
You can use the tilde '~' character for a null value.
Yaml
|
name: Mr. Show
hosted by: Bob and David
date of next season: ~
|
|
|
Ruby
|
{
'name' => 'Mr. Show',
'hosted by' => 'Bob and David',
'date of next season' => nil
}
|
|
|
Brief
You can use 'true' and 'false' for boolean values.
Yaml
|
Is Gus a Liar?: true
Do I rely on Gus for Sustenance?: false
|
|
|
Ruby
|
{
'Is Gus a Liar?' => true,
'Do I rely on Gus for Sustenance?' => false
}
|
|
|
Brief
An integer is a series of numbers, optionally starting with a positive or negative sign. Integers may also contain commas for readability.
Yaml
|
zero: 0
simple: 12
one-thousand: 1,000
negative one-thousand: -1,000
|
|
|
Ruby
|
{
'zero' => 0,
'simple' => 12,
'one-thousand' => 1000,
'negative one-thousand' => -1000
}
|
|
|
Brief
An integer can be used a dictionary key.
Yaml
Ruby
|
{
1 => 'one',
2 => 'two',
3 => 'three'
}
|
|
|
Brief
Floats are represented by numbers with decimals, allowing for scientific notation, as well as positive and negative infinity and "not a number."
Yaml
|
a simple float: 2.00
larger float: 1,000.09
scientific notation: 1.00009e+3
|
|
|
Ruby
|
{
'a simple float' => 2.0,
'larger float' => 1000.09,
'scientific notation' => 1000.09
}
|
|
|
Brief
You can represent timestamps by using ISO8601 format, or a variation which allows spaces between the date, time and time zone.
Yaml
|
iso8601: 2001-12-14t21:59:43.10-05:00
space seperated: 2001-12-14 21:59:43.10 -05:00
|
|
|
Ruby
|
{
'iso8601' => YAML::mktime( 2001, 12, 14, 21, 59, 43, 0.10, "-05:00" ),
'space seperated' => YAML::mktime( 2001, 12, 14, 21, 59, 43, 0.10, "-05:00" )
}
|
|
|
Brief
A date can be represented by its year, month and day in ISO8601 order.
Yaml
Ruby
Brief
A pipe character, followed by an indented block of text is treated as a literal block, in which newlines are preserved throughout the block, including the final newline.
Yaml
Ruby
|
{ 'this' => "Foo\nBar\n" }
|
|
|
Brief
The '+' indicator says to keep newlines at the end of text blocks.
Yaml
|
normal: |
extra new lines not kept
preserving: |+
extra new lines are kept
dummy: value
|
|
|
Ruby
|
{
'normal' => "extra new lines not kept\n",
'preserving' => "extra new lines are kept\n\n\n",
'dummy' => 'value'
}
|
|
|
Brief
To give you more control over how space is preserved in text blocks, YAML has the keep '+' and chomp '-' indicators. The keep indicator will preserve all ending newlines, while the chomp indicator will strip all ending newlines.
Yaml
|
clipped: |
This has one newline.
same as "clipped" above: "This has one newline.\n"
stripped: |-
This has no newline.
same as "stripped" above: "This has no newline."
kept: |+
This has four newlines.
same as "kept" above: "This has four newlines.\n\n\n\n"
|
|
|
Ruby
|
{
'clipped' => "This has one newline.\n",
'same as "clipped" above' => "This has one newline.\n",
'stripped' => 'This has no newline.',
'same as "stripped" above' => 'This has no newline.',
'kept' => "This has four newlines.\n\n\n\n",
'same as "kept" above' => "This has four newlines.\n\n\n\n"
}
|
|
|
Brief
Normally, only a single newline is kept from the end of a literal block, unless the keep '+' character is used in combination with the pipe. The following example will preserve all ending whitespace since the last line of both literal blocks contains spaces which extend past the indentation level.
Yaml
|
---
this: |
Foo
kept: |+
Foo
|
|
|
Ruby
|
{ 'this' => "Foo\n\n \n",
'kept' => "Foo\n\n \n" }
|
|
|
Brief
A greater-then character, followed by an indented block of text is treated as a folded block, in which lines of text separated by a single newline are concatenated as a single line.
Yaml
|
---
- apple
- banana
- >
can't you see
the beauty of yaml?
hmm
- dog
|
|
|
Ruby
|
[
'apple',
'banana',
"can't you see the beauty of yaml? hmm\n",
'dog'
]
|
|
|
Brief
Both literal and folded blocks can be used in collections, as values in a sequence or a mapping.
Yaml
|
---
quote: >
Mark McGwire's
year was crippled
by a knee injury.
source: espn
|
|
|
Ruby
|
{
'quote' => "Mark McGwire's year was crippled by a knee injury.\n",
'source' => 'espn'
}
|
|
|
Brief
The keep and chomp indicators can also be applied to folded blocks.
Yaml
|
clipped: >
This has one newline.
same as "clipped" above: "This has one newline.\n"
stripped: >-
This has no newline.
same as "stripped" above: "This has no newline."
kept: >+
This has four newlines.
same as "kept" above: "This has four newlines.\n\n\n\n"
|
|
|
Ruby
|
{
'clipped' => "This has one newline.\n",
'same as "clipped" above' => "This has one newline.\n",
'stripped' => 'This has no newline.',
'same as "stripped" above' => 'This has no newline.',
'kept' => "This has four newlines.\n\n\n\n",
'same as "kept" above' => "This has four newlines.\n\n\n\n"
}
|
|
|
Brief
If you need to refer to the same item of data twice, you can give that item an alias. The alias is a plain string, starting with an ampersand. The item may then be referred to by the alias throughout your document by using an asterisk before the name of the alias. This is called an anchor.
Yaml
|
- &showell Steve
- Clark
- Brian
- Oren
- *showell
|
|
|
Ruby
|
[ showell, 'Clark', 'Brian', 'Oren', showell ]
|
|
|
Brief
An alias can be used on any item of data, including sequences, mappings, and other complex data types.
Yaml
|
- &hello
Meat: pork
Starch: potato
- banana
- *hello
|
|
|
Ruby
|
hello = { 'Meat' => 'pork', 'Starch' => 'potato' }
[
hello,
'banana',
hello
]
|
|
|
Brief
You can separate YAML documents with a string of three dashes.
Yaml
|
- foo: 1
bar: 2
---
more: stuff
|
|
|
Ruby
|
[ { 'foo' => 1, 'bar' => 2 } ]
|
|
|
Brief
You can explicity give an opening document separator to your YAML stream.
Yaml
|
---
- foo: 1
bar: 2
---
more: stuff
|
|
|
Ruby
|
[ { 'foo' => 1, 'bar' => 2 } ]
|
|
|
Brief
The opening separator can contain directives to the YAML parser, such as the version number.
Yaml
|
--- %YAML:1.0
foo: 1
bar: 2
|
|
|
Ruby
|
y = Stream.new
y.add( { 'foo' => 1, 'bar' => 2 } )
|
|
|
Brief
Separators included in blocks or strings are treated as blocks or strings, as the document separator should have no indentation preceding it.
Yaml
Ruby
Brief
This technique allows you to embed other YAML documents within literal blocks.
Yaml
|
foo: |
---
foo: bar
---
yo: baz
bar: |
fooness
|
|
|
Ruby
|
{
'foo' => "---\nfoo: bar\n---\nyo: baz\n",
'bar' => "fooness\n"
}
|
|
|
Brief
Ruby Symbols can be simply serialized using the !ruby/symbol transfer method, or the abbreviated !ruby/sym.
Yaml
|
simple symbol: !ruby/symbol Simple
shortcut syntax: !ruby/sym Simple
symbols in seqs:
- !ruby/symbol ValOne
- !ruby/symbol ValTwo
- !ruby/symbol ValThree
symbols in maps:
- !ruby/symbol MapKey: !ruby/symbol MapValue
|
|
|
Ruby
|
{ 'simple symbol' => :Simple,
'shortcut syntax' => :Simple,
'symbols in seqs' => [ :ValOne, :ValTwo, :ValThree ],
'symbols in maps' => [ { :MapKey => :MapValue } ]
}
|
|
|
Brief
Ranges are serialized with the !ruby/range type family.
Yaml
|
normal range: !ruby/range 10..20
exclusive range: !ruby/range 11...20
negative range: !ruby/range -1..-5
? !ruby/range 0..40
: range as a map key
|
|
|
Ruby
|
{ 'normal range' => (10..20),
'exclusive range' => (11...20),
'negative range' => (-1..-5),
(0..40) => 'range as a map key'
}
|
|
|
Brief
Regexps may be serialized to YAML, both its syntax and any modifiers.
Yaml
|
case-insensitive: !ruby/regexp "/George McFly/i"
complex: !ruby/regexp "/\\A\"((?:[^\"]|\\\")+)\"/"
simple: !ruby/regexp '/a.b/'
|
|
|
Ruby
|
{ 'simple' => /a.b/, 'complex' => /\A"((?:[^"]|\")+)"/,
'case-insensitive' => /George McFly/i }
|
|
|
Brief
Regexps may also be imported from serialized Perl.
Yaml
|
--- !perl/regexp:
REGEXP: "R[Uu][Bb][Yy]$"
MODIFIERS: i
|
|
|
Ruby
Brief
The Ruby Struct class is registered as a YAML builtin type through Ruby, so it can safely be serialized. To use it, first make sure you define your Struct with Struct::new. Then, you are able to serialize with Struct#to_yaml and unserialize from a YAML stream.
Yaml
|
--- !ruby/struct:BookStruct
author: Yukihiro Matsumoto
title: Ruby in a Nutshell
year: 2002
isbn: 0-596-00214-9
|
|
|
Ruby
|
book_struct = Struct::new( "BookStruct", :author, :title, :year, :isbn )
book_struct.new( "Yukihiro Matsumoto", "Ruby in a Nutshell", 2002, "0-596-00214-9" )
|
|
|
Brief
As with other YAML builtins, you may nest the Struct inside of other Structs or other data types.
Yaml
|
- !ruby/struct:FoodStruct
name: Nachos
ingredients:
- Mission Chips
- !ruby/struct:FoodStruct
name: Tostitos Nacho Cheese
ingredients:
- Milk and Enzymes
- Jack Cheese
- Some Volatile Chemicals
taste: Angelic
- Sour Cream
taste: Zesty
- !ruby/struct:FoodStruct
name: Banana Cream Pie
ingredients:
- Bananas
- Creamy Stuff
- And Such
taste: Puffy
|
|
|
Ruby
|
food_struct = Struct::new( "FoodStruct", :name, :ingredients, :taste )
[
food_struct.new( 'Nachos', [ 'Mission Chips',
food_struct.new( 'Tostitos Nacho Cheese', [ 'Milk and Enzymes', 'Jack Cheese', 'Some Volatile Chemicals' ], 'Angelic' ),
'Sour Cream' ], 'Zesty' ),
food_struct.new( 'Banana Cream Pie', [ 'Bananas', 'Creamy Stuff', 'And Such' ], 'Puffy' )
]
|
|
|
Brief
YAML has generic support for serializing objects from any class available in Ruby. If using the generic object serialization, no extra code is needed.
Yaml
|
--- !ruby/object:YAML::Zoolander
name: Derek
look: Blue Steel
|
|
|
Ruby
|
class Zoolander
attr_accessor :name, :look
def initialize( look )
@name = "Derek"
@look = look
end
def ==( z )
self.name == z.name and self.look == z.look
end
end
Zoolander.new( "Blue Steel" )
|
|
|
Brief
When extending the Array class, your instances of such a class will dump as YAML sequences, tagged with a class name.
Yaml
|
--- !ruby/array:YAML::MyArray
- jacket
- sweater
- windbreaker
|
|
|
Ruby
|
class MyArray < Kernel::Array; end
outerwear = MyArray.new
outerwear << 'jacket'
outerwear << 'sweater'
outerwear << 'windbreaker'
outerwear
|
|
|
Brief
When extending the Hash class, your instances of such a class will dump as YAML maps, tagged with a class name.
Yaml
|
--- !ruby/hash:YAML::MyHash
Black Francis: Frank Black
Kim Deal: Breeders
Joey Santiago: Martinis
|
|
|
Ruby
|
# Note that the @me attribute isn't dumped
# because the default to_yaml is trained
# to dump as a regular Hash.
class MyHash < Kernel::Hash
attr_accessor :me
def initialize
@me = "Why"
end
end
pixies = MyHash.new
pixies['Black Francis'] = 'Frank Black'
pixies['Kim Deal'] = 'Breeders'
pixies['Joey Santiago'] = 'Martinis'
pixies
|
|
|