How to generate .yml file from Ruby on Rails? - ruby-on-rails

I would like generate the following result in a .yml file from Ruby on Rails:
include:
- template: ./my_folder/my_file.txt
But I am generating the following result with single quote (''):
include:
- 'template: ./my_folder/my_file.txt'
I have tried generate the .yml file with this ruby code:
my_variable = {"include" => ["template: ./my_folder/my_file.txt"]}

I would do this:
my_variable = { "include" => [{ "template" => "./my_folder/my_file.txt" }] }
my_variable.to_yaml
# ---
# include:
# - template: "./my_folder/my_file.txt"

my_variable = {"include" => ["template: ./my_folder/my_file.txt"]}
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The Array contains a String, not a Hash. It will produce a YAML array with a single string value, not an array with a key/value pair.
Either you have a typo in your data, or you need to split the values in the Array on : to produce a Hash.

Related

Retrieve data from YAML and convert each value as its own array

I have this in my yml file
members:
- id: 1
name: Sam
- id: 18
name: tom
After retrieving data from this file in a rails application I want to convert it to an array.
for example
id=[1,18]
name=[sam,tom]
how can I achieve this?
Currently This is how I am retrieving the data.
yml = YAML.load_file("mem.yml")
And this is how i get my data
[{"id":1, "name":"Sam"},{"id":18, "name":"tom"}]
if I use yml["members"][1]["id"] I get the first id.
I also tried writing id and name separately like below. This does give me what I want when I use yml["id"]but I don't want to use it because of its readability. BTW my data is static.
id:
- 1
- 18
name:
- Sam
- tom
Try the below:
yml = [{"id":1, "name":"Sam"},{"id":18, "name":"tom"}]
result = {}.tap do |result|
yml.each do |hash| # Iterate over array of hashes on parsed input from YAML file
hash.each do |key, value| # Iterate over each keys in the hash
result[key] ||= []
result[key] << value # Append element in the array
end
end
end
This will return the result as a hash:
{:id=>[1, 18], :name=>["Sam", "tom"]}
You can access the ids and names as
result[:id] # [1, 18]
result[:name] # ["Sam", "tom"]

How to turn a translations table into YAML translation files in ruby on rails

My app is currently using the gem i18n-active_record for translations, storing them in a table translations which has more than 5000 records.
To improve performance, I want to move the translations to YAML files, e.g. en.yml and ar.yml.
I tried File.open(Rails.root+'config/locales/en.yml', 'w') {|f| f.write translation.to_yaml } but the output generated is the following:
raw_attributes:
id: 1
locale: en
key: footer.new_car_make
value: New %{title} Cars
interpolations:
is_proc: 0
created_at: &4 2012-08-15 06:25:59.000000000 Z
updated_at: &5 2012-08-15 06:25:59.000000000 Z
Is there any easy way to make the conversion?
You can try something like this (I don't have any DB backed Rails Translations to try this)
def assign(parts, value, data)
key, *rest = parts
if rest.empty?
data[key] = value
else
data[key] ||= {}
assign(rest, value, data[key])
end
end
translations = {}
Translation.all.each do |translation|
path = [translation.locale] + translation.key.split(".")
assign(path, translation.value, translations)
end
File.write("translations.yml", translations.to_yaml)
Of course you can modify to only export the translations of a single locale or specific keys (by changing the all to a where query).
The code works as following:
It iterates all the Translations, and builds a hash with all the translations.
The key footer.new_car_make of the en translation will end up in a nested hash like:
{
"en" => {
"footer" => {
"new_car_make" => "Whatever text you have in the DB"
}
}
}
and then this hash is saved as YAML format in a file.
The assign method is called with the full key (it contains the locale as well) represented as an array (['en', 'footer', 'new_car_make']) and deconstructs into a head (first value in the array) and the rest (remaining parts).
If the remaining part is empty, it means we've reached the last element and can assign the value, otherwise we add a nested hash and recurse.
You can try this in the console (just copy paste it). As mentioned this might not run right out of the box, let me know if you have troubles.

Chef, ruby hashes and templates

I know this is more a ruby question than chef, but...
I have some attributes like:
default['my_cookbook']['some_namespace1']['some_attribute1'] = 'some_value1'
default['my_cookbook']['some_namespace1']['some_attribute2'] = 'some_value2'
default['my_cookbook']['some_namespace1']['some_attribute2'] = 'some_value3'
...
default['my_cookbook']['some_namespace2']['some_attribute1'] = 'some_value1'
default['my_cookbook']['some_namespace2']['some_attribute2'] = 'some_value2'
default['my_cookbook']['some_namespace2']['some_attribute2'] = 'some_value3'
...
On the other hand, I am creating a template resource like this:
template 'template_name' do
source 'template_source.erb'
variables (
my_namespace_1: node['my_cookbook']['some_namespace1'],
my_namespace_2: node['my_cookbook']['some_namespace2']
)
end
Then in the template_source.erb I try:
...
<%= #my_namespace_1['some_attribute1'] %> #=> 'some_value1'
...
However when I run Kitchen I get this, instead of 'some_value1':
Chef::Mixin::Template::TemplateError
------------------------------------
undefined method `[]' for nil:NilClass
How should I send the template variable to use it this way?
EDIT: This applies only to Ruby in general and not to Chef in particular.
Pass a nested hash:
template 'template_name' do
source 'template_source.erb'
variables (
my_namespace_1: {
some_attribute1: node['my_cookbook']['some_namespace1']['some_attribute1']
}
)
end
But rather than copying the values verbatim you can use the full power of the Hash class to slice, dice and merge together whatever you want:
template 'template_name' do
source 'template_source.erb'
variables (
node['my_cookbook'].slice('some_namespace1', 'some_namespace2')
)
end
One gotcha in Ruby that you have tripped on is that symbols are usually used as hash keys:
# newer literal syntax
a_hash = {
foo: 'bar'
}
# or with the older hash-rocket syntax
a_hash = {
:foo => 'bar'
}
Symbols are extemly efficient since they are interned strings that are stored in table - when comparing symbols you compare the object ID instead of comparing each character in the string.
In fact strings are only really used when you want keys in the hash that are not valid Ruby symbols - like when building a hash of HTTP headers.
Ruby does not treat symbol and string keys indifferently:
{
foo: 'bar'
}[:foo]
# => bar
{
foo: 'bar'
}['foo']
# => nil
So to access the passed variable in the template you would use:
<%= #my_namespace_1[:some_attribute1] %>
What you have in the example should be working. I am guessing you have a typo somewhere in your original recipe that you corrected when generic-ifying the code.

How to check for value while creating JSON output

I am creating JSON output and want to check the value before I create the key value pair. Is there a way to do it while creating the JSON rather than adding it later?
I am creating this JSON output:
sample_json = {
abc: "abc",
xyz: "xyz",
pqr: some_value unless some_value.blank?
}
In this example I cannot check for unless some_value.blank? while creating the JSON string, so I first create the JSON and then add pqr: some_value separately after checking for a blank value, something like this:
sample_json = {
abc: "abc",
xyz: "xyz"
}
sample_json[:pqr] = some_value unless some_value.blank?
Is there is a way to add the check while creating the JSON itself like in the first example?
I don't think you can.
But, some other ways to deal with it, assuming you have active_support, you could use a hash compact (sample_json.compact), which will remove key-value pairs with nil values.
But If you do need !v.blank?, you could do:
sample_json.select { |_, value| !value.blank? }
I'd go about it like this:
require 'active_support/core_ext/object/blank'
require 'json'
some_value = 'foo'
hash = {
abc: "abc",
xyz: "xyz"
}
hash.merge!(pqr: some_value) unless some_value.blank?
puts hash.to_json
# >> {"abc":"abc","xyz":"xyz","pqr":"foo"}
If some_value = '' it'll result in:
# >> {"abc":"abc","xyz":"xyz"}
Don't create the JSON then try to change it. Instead create the base object, make the test then merge in the change, then serialize to JSON.

How to iterate through a yaml hash structure in ruby?

I have a hash mapping in my yaml file as below. How Can I iterate through it in simple ruby script? I would like to store the key in a variable and value in another variable in my ruby program during the iteration.
source_and_target_cols_map:
-
com_id: community_id
report_dt: note_date
sitesection: site_section
visitor_cnt: visitors
visit_cnt: visits
view_cnt: views
new_visitor_cnt: new_visitors
the way i am getting the data from the yaml file is below:
#!/usr/bin/env ruby
require 'yaml'
config_options = YAML.load_file(file_name)
#source_and_target_cols_map = config_options['source_and_target_cols_map']
puts #source_and_target_cols_map
The YAML.load_file method should return a ruby hash, so you can iterate over it in the same way you would normally, using the each method:
require 'yaml'
config_options = YAML.load_file(file_name)
config_options.each do |key, value|
# do whatever you want with key and value here
end
As per your yaml file it you should get the below Hash from the line config_options = YAML.load_file(file_name)
config_options = { 'source_and_target_cols_map' =>
[ { 'com_id' => 'community_id',
'report_dt' => 'note_date',
'sitesection' => 'site_section',
'visitor_cnt' => 'visitors',
'visit_cnt' => 'visits',
'view_cnt' => 'views',
'new_visitor_cnt' => 'new_visitors' }
]}
Then to iterate through you can take the below approach:
config_options['source_and_target_cols_map'][0].each {|k,v| key = k,value = v}

Resources