Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extracting data from yml files with ruby

Tags:

parsing

ruby

yaml

I read some quick tutorials on Yaml or yml file format. I made a yaml document to represent my data. I saw some ruby tutorials which tell you how to extract yaml with ruby. Unfortunately, they just print the whole data or just keys and values. It does not meet my needs. Please help.

yaml file -

dev:
  game1:
    server1:
        url: 'dev-game1-a-srv01.gamer.com'
        log-path: '/srv/logs'
    server2:
        url: 'dev-game1-a-srv02.gamer.com'
        log-path: '/srv/logs'

  game2:
    server1:
        url: 'dev-game2-a-srv01.gamer.com'
        log-path: '/srv/logs'
    server2:
        url: 'dev-game2-b-srv02.gamer.com'
        log-path: '/srv/logs'
    server3:
        url: 'dev-game2-b-srv01.gamer.com'
        log-path: '/srv/logs'
prod: 
etc....

How do I select dev, game2, server 3, url using ruby code ?

Using the code below, I get an exception -

require 'yaml'

def server_info
    path = 'C:\Code\demo-srv.yml'
    yml = YAML::load(File.open(path))
    game2 = yml['dev']['game2'] 
    game2.each{|server|
        if server['server3']
            puts server['server3']['url']
        end
    }
end

server_info

error -

server.rb:8:in `[]': can't convert String into Integer (TypeError)
        from server.rb:8:in `server_info'
        from server.rb:7:in `each'
        from server.rb:7:in `server_info'
        from server.rb:14
like image 880
Erran Morad Avatar asked Nov 29 '25 01:11

Erran Morad


2 Answers

Did you define the yaml-data or are you only the consumer of an existing yaml-file?

If you defined it, I would replace the array of servers with a Hash (see the missing - before the server names):

dev:
  game1:
    server1:
        url: 'dev-game1-a-srv01.gamer.com'
        log-path: '/srv/logs'
    server2:
        url: 'dev-game1-a-srv02.gamer.com'
        log-path: '/srv/logs'

  game2:
    server1:
        url: 'dev-game2-a-srv01.gamer.com'
        log-path: '/srv/logs'
    server2:
        url: 'dev-game2-b-srv02.gamer.com'
        log-path: '/srv/logs'
    server3:
        url: 'dev-game2-b-srv01.gamer.com'
        log-path: '/srv/logs'

Then you can try yml['dev']['game2']['server3']['url'].

Attention: There are no checks for missing/wrong data. if the entry for game2 would miss, this code will raise an exception.

So, maybe you shoudl do something like

if yml['dev'] and yml['dev'].kind_of?(Hash)
  if yml['dev']['game2'] and ....
  ...
else
  puts "No dev-branch defined"
end

Else you can try something like:

def server_info
    yml = YAML::load(DATA)
    yml['dev']['game2'].each{|server|
      if server['server3']
        p server['server3']['url']
      end
    }
end

Attention (for both solutions):

There are no checks for missing/wrong data. The existence of server['server3'] is checked here. For real code, you should also check the existence of the dev and game2 data.


Answer continuation after edit:

The error convert String into Integer is often thrown if you have an array but expect a hash and you try to access an array element with a string.

You can try the following code. There are two changes:

  • line 8 contains the output of server - you will see it is an array, no hash.
  • line 9+10: The array is checked and used by its two elements (via #first and #last)

    require 'yaml'
    
    def server_info
        path = 'C:\Code\demo-srv.yml'
        #~ yml = YAML::load(File.open(path))
        yml = YAML::load(DATA)
        game2 = yml['dev']['game2'] 
        game2.each{|server|
            p server    #-> you get an array
            if server.first == 'server3'
                puts server.last['url']
            end
        }
    end
    
    server_info
    

The file -

dev:
  game1:
    server1:
        url: 'dev-game1-a-srv01.gamer.com'
        log-path: '/srv/logs'
    server2:
        url: 'dev-game1-a-srv02.gamer.com'
        log-path: '/srv/logs'

  game2:
    server1:
        url: 'dev-game2-a-srv01.gamer.com'
        log-path: '/srv/logs'
    server2:
        url: 'dev-game2-b-srv02.gamer.com'
        log-path: '/srv/logs'
    server3:
        url: 'dev-game2-b-srv01.gamer.com'
        log-path: '/srv/logs'
like image 84
knut Avatar answered Dec 02 '25 04:12

knut


I don't know why but yml['dev']['game2']=>

[{"server1"=>{"url"=>"dev-game2-a-srv01.gamer.com", "log-path"=>"/srv/logs"}},
{"server2"=>{"url"=>"dev-game2-b-srv02.gamer.com", "log-path"=>"/srv/logs"}},
 {"server3"=>{"url"=>"dev-game2-b-srv01.gamer.com", "log-path"=>"/srv/logs"}}]

So you have to use find on this Array to have the key.

require 'yaml'
# require 'pry'

def server3_url
    yml = YAML::load(File.read('yaml.yml'))
    # binding.pry
    begin
      yml['dev']['game2'].find{|x| x['server3']}['server3']['url']
    rescue
    end
end


puts server3_url

server3_url will return nil if it doesn't find a key

like image 28
Rivenfall Avatar answered Dec 02 '25 04:12

Rivenfall



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!