247

I would like to know how to parse a YAML file with the following contents:

--- 
javascripts: 
- fo_global:
  - lazyload-min
  - holla-min

Currently I am trying to parse it this way:

@custom_asset_packages_yml = (File.exists?("#{RAILS_ROOT}/config/asset_packages.yml") ? YAML.load_file("#{RAILS_ROOT}/config/asset_packages.yml") : nil)
    if !@custom_asset_packages_yml.nil?
      @custom_asset_packages_yml['javascripts'].each{ |js|
        js['fo_global'].each{ |script|
         script
        }
      }
    end

But it doesn't seem to work and gives me an error that the value is nil.

You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.each

If I try this, it puts out the entire string (fo_globallazyload-minholla-min):

if !@custom_asset_packages_yml.nil?
          @custom_asset_packages_yml['javascripts'].each{ |js|
            js['fo_global']
          }
        end
Dmitriy Vinokurov
  • 365
  • 1
  • 6
  • 28
alvincrespo
  • 9,224
  • 13
  • 46
  • 60
  • 1
    can you give the output of the script when you run it? files in the right place? you can always fire up a Rails console and see if ruby can see that path as well. – Lukas Oct 06 '10 at 21:23
  • Yeah, the file does exist and is in the right location. Ive updated my post with the error. – alvincrespo Oct 06 '10 at 21:27

3 Answers3

520

Maybe I'm missing something, but why try to parse the file? Why not just load the YAML and examine the object(s) that result?

If your sample YAML is in some.yml, then this:

require 'yaml'
thing = YAML.load_file('some.yml')
puts thing.inspect

gives me

{"javascripts"=>[{"fo_global"=>["lazyload-min", "holla-min"]}]}
Nathan
  • 11,938
  • 12
  • 55
  • 62
Mike Woodhouse
  • 51,832
  • 12
  • 88
  • 127
  • 10
    I agree, that's the wonderful about YAML - we can serialize something then read it back in later, so why not use that capability. – the Tin Man Oct 07 '10 at 03:27
  • Ah, I was unaware that you could do that with a YAML file. Thanks so much! – alvincrespo Oct 07 '10 at 17:34
  • For some reason, I can require yaml, but I get a parse error, then I see something about Psych. Then when I do Psych.load("file.yml"), it just outputs the "file.yml" part and not the actual contents of the file. – FilBot3 Mar 20 '14 at 19:57
  • 1
    Huh, it was because I was using tabs. It seems that it only wants spaces instead of tabs, and then throws all sorts of errors. – FilBot3 Mar 20 '14 at 20:02
  • 3
    What is the difference between #load and #load_file in YAML library? – Jwan622 Mar 16 '16 at 15:48
  • 3
    `YAML.load` takes a YAML string, `YAML.load_file` takes a relative file path. – Narfanator Jul 06 '16 at 22:30
  • The `load_file` method does not exist according to the docs of [YAML](https://ruby-doc.org/stdlib-2.5.0/libdoc/yaml/rdoc/YAML.html). This method as well as others, are documented over [here](https://ruby-doc.org/stdlib-2.5.0/libdoc/psych/rdoc/Psych.html). I found out which version of Psych is included with Ruby over on [this site](https://stdgems.org/psych/). lol seriously Ruby, you need to get your shit together - like literally. – Martin Andersson Jan 10 '18 at 15:37
13

I had the same problem but also wanted to get the content of the file (after the YAML front-matter).

This is the best solution I have found:

if (md = contents.match(/^(?<metadata>---\s*\n.*?\n?)^(---\s*$\n?)/m))
  self.contents = md.post_match
  self.metadata = YAML.load(md[:metadata])
end

Source and discussion: https://practicingruby.com/articles/tricks-for-working-with-text-and-files

sarfata
  • 4,625
  • 4
  • 28
  • 36
5

Here is the one liner i use, from terminal, to test the content of yml file(s):

$ ruby  -r yaml -r pp  -e 'pp YAML.load_file("/Users/za/project/application.yml")'
{"logging"=>
  {"path"=>"/var/logs/",
   "file"=>"TacoCloud.log",
   "level"=>
    {"root"=>"WARN", "org"=>{"springframework"=>{"security"=>"DEBUG"}}}}}
z atef
  • 7,138
  • 3
  • 55
  • 50