Question

Here is my YAML file, 'test.yml':

---
alpha: 100.0
beta: 200.0
gama: 300.0
--- 3
...

The first document is a hash.

The second document is an integer.

I am trying to load these to a Ruby program as a hash and an integer.

Here is my current attempt:

require 'yaml'

variables = YAML.load_file('test.yml')
puts variables.inspect
Was it helpful?

Solution 3

Don't use multiple documents; They're not a substitute for defining individual elements in your data:

require 'yaml'

yaml = <<EOT
---
hash:
  alpha: 100.0
  beta: 200.0
  gama: 300.0
int: 3
EOT

YAML.load(yaml)   
# => {"hash"=>{"alpha"=>100.0, "beta"=>200.0, "gama"=>300.0}, "int"=>3}

You can access the contents by assigning YAML.load(yaml) to a variable:

data = YAML.load(yaml)
data['hash'] # => {"alpha"=>100.0, "beta"=>200.0, "gama"=>300.0}
data['int'] # => 3

Think of it this way, you're asking for an object of some sort from YAML after it parses the YAML file. You need to be able to extract specific values from it, so make it easy on yourself and define an array or a hash that contains what you want, in a way that works the way your brain does, within YAML's limitations and specs.

If I'm going to be creating a complicated structure, I do it in Ruby first and have YAML dump the format for me:

require 'yaml'

data = {
  "hash" => {
    "alpha" => 100.0,
    "beta" => 200.0,
    "gama" => 300.0
  },
  "int" => 3
}

puts data.to_yaml

# >> ---
# >> hash:
# >>   alpha: 100.0
# >>   beta: 200.0
# >>   gama: 300.0
# >> int: 3

I can put the Ruby code into a script and run it, redirecting it to a YAML file:

ruby test.rb > test.yaml

Then I can expand the structure:

require 'yaml'

data = {
  "hash" => {
    "alpha" => 100.0,
    "beta" => 200.0,
    "gama" => 300.0
  },
  "int" => 3,
  "array" => %w[a b c]
}

puts data.to_yaml

# >> ---
# >> hash:
# >>   alpha: 100.0
# >>   beta: 200.0
# >>   gama: 300.0
# >> int: 3
# >> array:
# >> - a
# >> - b
# >> - c

Testing it round-trip:

require 'yaml'

yaml = <<EOT
---
hash:
  alpha: 100.0
  beta: 200.0
  gama: 300.0
int: 3
array:
- a
- b
- c
EOT

YAML.load(yaml)
# => {"hash"=>{"alpha"=>100.0, "beta"=>200.0, "gama"=>300.0}, "int"=>3, "array"=>["a", "b", "c"]}

Iteratively do that until you're comfortable with YAML syntax, then you can build/tweak your YAML file by hand.

Now, here's how smart it is. The YAML spec supports aliases, which let us define a variable, then reuse it multiple times using & and * respectively. Creating those by hand is a pain when your document gets large, but the YAML driver is smart and will output them for you:

require 'yaml'

FOO = ['foo']
BAR = ['bar']

foobar = [FOO, BAR]

data = {
  "foo" => FOO,
  'bar' => BAR,
  'foobar' => foobar,
}

puts data.to_yaml


# >> ---
# >> foo: &1
# >> - foo
# >> bar: &2
# >> - bar
# >> foobar:
# >> - *1
# >> - *2

foo: &1 defines ["foo"], and gets reused at the bottom as *1.

"Yaml Cookbook at the YamlForRuby site" is a great reference for working with YAML.

OTHER TIPS

To access multiple YAML documents in a single file, use the load_stream method (as already mentioned by "matt" in a comment to one of the other answers):

YAML.load_stream(File.read('test.yml')) do |document|
  puts document
end

The Tin Man is correct that the OP should not be using multiple documents for his specific problem; however, the situation of multiple documents in a YAML stream does occur in practice, for example when multiple YAML documents are appended to a single file, so it's worth knowing how to deal with it.

require 'yaml'

yaml = <<EOT
---
alpha: 100.0
beta: 200.0
gama: 300.0
---
int: 3
...
EOT

loop do
  puts YAML.load(yaml)
  break if !yaml.sub!(/(?<!#)(-{3}.+?(?=-{3})\n?){1}/m,'')
  break if yaml.empty?
end

# >> {"alpha"=>100.0, "beta"=>200.0, "gama"=>300.0}
# >> {"int"=>3}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top