How to parse a yaml file into ruby hashs and/or arrays? - ruby-on-rails

I need to load a yaml file into Hash,
What should I do?

I would use something like:
hash = YAML.load(File.read("file_path"))

A simpler version of venables' answer:
hash = YAML.load_file("file_path")

Use the YAML module:
http://ruby-doc.org/stdlib-1.9.3/libdoc/yaml/rdoc/YAML.html
node = YAML::parse( <<EOY )
one: 1
two: 2
EOY
puts node.type_id
# prints: 'map'
p node.value['one']
# prints key and value nodes:
# [ #<YAML::YamlNode:0x8220278 #type_id="str", #value="one", #kind="scalar">,
# #<YAML::YamlNode:0x821fcd8 #type_id="int", #value="1", #kind="scalar"> ]'
# Mappings can also be accessed for just the value by accessing as a Hash directly
p node['one']
# prints: #<YAML::YamlNode:0x821fcd8 #type_id="int", #value="1", #kind="scalar">
http://yaml4r.sourceforge.net/doc/page/parsing_yaml_documents.htm

You may run into a problem mentioned at this related question, namely, that the YAML file or stream specifies an object into which the YAML loader will attempt to convert the data into. The problem is that you will need a related Gem that knows about the object in question.
My solution was quite trivial and is provided as an answer to that question. Do this:
yamltext = File.read("somefile","r")
yamltext.sub!(/^--- \!.*$/,'---')
hash = YAML.load(yamltext)
In essence, you strip the object-classifier text from the yaml-text. Then you parse/load it.

Related

Refer to a hash where the key is an ip_address

I've got a model that I need to group by the :sending_ip, which is a "cidr" column in the database.
#count_hash = Webhook.group('sending_ip').count
Resulting in this hash:
{#<IPAddr: IPv4:213.32.165.239/255.255.255.255>=>127000, #<IPAddr: IPv4:153.92.251.118/255.255.255.255>=>228000}
I cannot figure out how to reference this type of key. Below are some examples of the ways that I've tried to call these keys. All of them return nil or error.
#count_hash[#<IPAddr: IPv4:213.32.165.239/255.255.255.255>]
#count_hash["#<IPAddr: IPv4:213.32.165.239/255.255.255.255>"]
#count_hash[<IPAddr: IPv4:213.32.165.239/255.255.255.255>]
#count_hash["#<IPAddr: IPv4:213.32.165.239/255.255.255.255>"]
Elsewhere in my app, I've got a simpler example that works great. The other example groups by esp, which results in this hash:
{"hotmail"=>1000, "gmail"=>354000}
The second hash, I can refer to easily
#count_hash["gmail"]
To obtain the expected result of 354000
How can I achieve this same functionality with the previous hash that was grouped by sending_ip? Thank you in advance for your insight.
This:
#<IPAddr: IPv4:213.32.165.239/255.255.255.255>
is the result of calling #inspect on an instance of IPAddr. So the keys are IPAddr instances and you can say:
ip = IPAddr.new('213.32.165.239')
#count_hash[ip]
# 127000
Or you could iterate over the hash:
#count_hash.each { |ip, n| ... }
or over its keys:
#count_hash.keys.each { |ip| ... }
depending on what you need to do. You could even convert the keys to strings if that's more convenient:
#count_hash = #count_hash.transform_keys(&:to_s)
# or
#count_hash.transform_keys!(&:to_s)

Rails params to array

I am sending a list of checkbox selected from PHP file to our Rails API server. All checked items' ID's will be sent in json format (campaign_ids in json_encode from PHP):
I got a URL being passed to our API like this
Started PUT "/campaigns/function.json?campaign_ids=["6","7"]&user_id=0090000007"
I need to get the campaign_ids ["6","7"] and process it like any other array using array.each do || end
How can I convert this to an array so I can use array.each?
The following sample code can achieve it but I think there could be a better way?
campaign_ids = params[:campaign_ids].to_s # [\"6\",\"7\"]
campaign_ids = campaign_ids.gsub(/[^0-9,]/,'') # 6,7
if campaign_ids.size.to_i > 0 # 3 ??
campaign_ids.split(",").each do |campaign_id|
...
end
end
The correct format of the URL should've been campaign_ids[]=6&campaign_ids[]=7. That would automatically yield an array of [6, 7] when you do params[:campaign_ids].
But assuming you can't change the format of the incorrect parameters, you can still get it via JSON.parse(params[:campaign_ids])
Try this
campaign_ids = JSON.parse(params[:campaign_ids])
You get params[:campaign_ids] as a string.
So, you will have to parse that json string to get array elements.
params[:campaign_ids] is already in your desired array format, you need not convert that to string using to_s.
You can do something like this
campaign_ids = params[:campaign_ids]
campaign_ids.each do |campaign_id|
# do the computation here
end

Generation of table and accessing elements of a Array of Hashes

I have the following Array of hashes in a rails application:
a = ["{\"ROW1\"=>{\"correct\"=>{\"h\"=>\"10\", \"m\"=>\"11\", \"l\"=>
\"12\"}, \"wrong\"=>{\"h\"=>\"2\", \"m\"=>\"2\", \"l\"=>\"4\"}, \"blank
\"=>{\"h\"=>\"2\", \"m\"=>\"4\", \"l\"=>\"3\"}}, \"ROW2\"=>{\"correct
\"=>{\"h\"=>\"2\", \"m\"=>\"4\", \"l\"=>\"4\"}, \"wrong\"=>{\"h
\"=>\"4\", \"m\"=>\"6\", \"l\"=>\"6\"}, \"blank\"=>{\"h\"=>\"7\",
\"m\"=>\"5\", \"l\"=>\"6\"}}, \"ROW3\"=>{\"correct\"=>{\"h\"=>\"4\",
\"m\"=>\"6\", \"l\"=>\"7\"}, \"wrong\"=>{\"h\"=>\"6\", \"m\"=>\"7\",
\"l\"=>\"5\"}, \"blank\"=>{\"h\"=>\"7\", \"m\"=>\"9\", \"l\"=>
\"3\"}}}"]
I want to access its elements and create a database table from it, in the following format
ROW1 correct h=10, m=11,l=12
wrong h=2, m=2,l=4
blank h=2, m=4,l=3
...and similar for ROW2 and ROW3.
How can I do that?
I tried to access a value using
a["ROW1"]["Correct"]["h"]
...but it returns a nil value.
How to access the values of this array of hashes?
you need to first convert the string to hash which can be done as follows:
require 'json'
a = ["{\"ROW1\"=>{\"correct\"=>{\"h\"=>\"10\", \"m\"=>\"11\", \"l\"=>
\"12\"}, \"wrong\"=>{\"h\"=>\"2\", \"m\"=>\"2\", \"l\"=>\"4\"}, \"blank
\"=>{\"h\"=>\"2\", \"m\"=>\"4\", \"l\"=>\"3\"}}, \"ROW2\"=>{\"correct
\"=>{\"h\"=>\"2\", \"m\"=>\"4\", \"l\"=>\"4\"}, \"wrong\"=>{\"h
\"=>\"4\", \"m\"=>\"6\", \"l\"=>\"6\"}, \"blank\"=>{\"h\"=>\"7\",
\"m\"=>\"5\", \"l\"=>\"6\"}}, \"ROW3\"=>{\"correct\"=>{\"h\"=>\"4\",
\"m\"=>\"6\", \"l\"=>\"7\"}, \"wrong\"=>{\"h\"=>\"6\", \"m\"=>\"7\",
\"l\"=>\"5\"}, \"blank\"=>{\"h\"=>\"7\", \"m\"=>\"9\", \"l\"=>
\"3\"}}}"
]
hash_string = a[0]
hash = JSON.parse hash_string.gsub("\n", '').gsub('=>', ':')
# you access the hash now:
hash["ROW1"]["correct"]["h"]
# => 10
Btw, please note that there is a typo. Instead of Correct, the key is correct with small c instead of capital C.
Hope it helps : )

How to make Rails.logger.debug print hash more readable

I'm using Rails.logger.debug print variables for debugging purposes. The issue is it prints hashes in an impossible to read format (can't distinguish keys from values). For example, I add the following lines to my code base
#code_base.rb
my_hash = {'a' => 'alligator', 'b'=>'baboon'}
Rails.logger.debug my_hash
Then I launch my rails app and type
tail -f log/development.log
But when my_hash gets printed, it looks like
bbaboonaalligator
The key and values are scrunched up, making it impossible to parse. Do you guys know what I should do to fix this?
Nevermind, I found the answer to my own question. I need to use
my_hash = {'a' => 'alligator', 'b'=>'baboon'}
Rails.logger.debug "#{my_hash.inspect}"
Then, it looks like
{"b"=>"baboon", "a"=>"aligator"}
It's even easier to read it when you use to_yaml eg:
logger.debug my_hash.to_yaml
Which is an easy to read format over multiple lines. The inspect method simply spews out a string.
my_hash = {'a' => 'alligator', 'b'=>'baboon'}
logger.debug "#{my_hash}"
Then, it looks like
{"b"=>"baboon", "a"=>"aligator"}
do not need inspect
There is an another way to do this. There is a ruby built in module pp.rb that is Pretty-printer for Ruby objects.
non-pretty-printed output by p is:
#<PP:0x81fedf0 #genspace=#<Proc:0x81feda0>, #group_queue=#<PrettyPrint::GroupQueue:0x81fed3c #queue=[[#<PrettyPrint::Group:0x81fed78 #breakables=[], #depth=0, #break=false>], []]>, #buffer=[], #newline="\n", #group_stack=[#<PrettyPrint::Group:0x81fed78 #breakables=[], #depth=0, #break=false>], #buffer_width=0, #indent=0, #maxwidth=79, #output_width=2, #output=#<IO:0x8114ee4>>
pretty-printed output by pp is:
#<PP:0x81fedf0
#buffer=[],
#buffer_width=0,
#genspace=#<Proc:0x81feda0>,
#group_queue=
#<PrettyPrint::GroupQueue:0x81fed3c
#queue=
[[#<PrettyPrint::Group:0x81fed78 #break=false, #breakables=[], #depth=0>],
[]]>,
#group_stack=
[#<PrettyPrint::Group:0x81fed78 #break=false, #breakables=[], #depth=0>],
#indent=0,
#maxwidth=79,
#newline="\n",
#output=#<IO:0x8114ee4>,
#output_width=2>
For more complex objects even with ActiveRecord, this could be achieved with a JSON.pretty_generate
Rails.logger.debug JSON.pretty_generate(my_hash.as_json)

Hash with indifferent access

I have a non-Rails project in which I am loading some settings from a YAML file:
config = YAML::load File.open("#{LOG_ROOT}/config/database.yml")
I can only access this hash like config["host"], config["username"] etc.
I want indifferent access so I can use both :host and "host".
The reason is, that one of the gems in the project to which I am passing this hash seems to be accessing it using symbols and it fails currently.
What is the best way to create a hash with indifferent access in this scenario?
You lose nothing except a few kB of disk space by installing the Active Support gem. In your code, you require only the function you want:
require 'active_support/core_ext/hash/indifferent_access'
That way, you can be sure you are not getting anything else to mess up your namespace.
Let the config hash return the value for the stringified version of the key:
config = {"host"=>"value1", "Username"=>"Tom"}
config.default_proc = proc{|h, k| h.key?(k.to_s) ? h[k.to_s] : nil}
p config[:host] #=> "value1"
The default_proc runs everytime when a key is not found in the hash. Note this is only half of indifferent access: config["host"] will result in nil if the key :host is present. If that has to work too:
config.default_proc = proc do |h, k|
case k
when String then sym = k.to_sym; h[sym] if h.key?(sym)
when Symbol then str = k.to_s; h[str] if h.key?(str)
end
end
See the comments about limitations of this approach (tltr: separate values for :a and 'a' are possible, does not take into account Hash.delete and others).

Resources