Reputation: 4753
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
Upvotes: 3
Views: 6088
Reputation: 361
Changing your code the following should fix the issue.
# ...
game2.each{ |server, data|
if server == 'server3'
puts data['url']
end
}
# ...
You are encountering the type error because the yielded value server
is an array, not a hash. This is happening because you are calling each on the game2
variable, which is a hash, and only yielding to a single variable.
hash = { one: 1, two: 2, three: 3 }
When Hash#each is called with only one variable, the current key and value are assigned to that variable as an array in the order [key, value]
hash.each do |number|
puts number.inspect
end
# Prints
# [:one, 1]
# [:two, 2]
# [:three, 3]
When Hash#each is called with two variables, the current key will be assigned to the first variable, and the current value will be assigned to the second.
hash.each do |key, value|
puts "Key: #{key}; Value: #{value}"
end
# Prints:
# Key: one; Value: 1
# Key: two; Value: 2
# Key: three; Value: 3
Upvotes: 1
Reputation: 27875
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:
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'
Upvotes: 5
Reputation: 1273
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
Upvotes: 1