Improper indentation in converting ruby hash to yaml - ruby-on-rails

I am trying to convert ruby hash object to YAML format using YAML.dump(obj) but I am getting improper indentation even after using dump options.
I have below executable ruby script :
#!/usr/bin/ruby
require "yaml"
require "erb"
context_path = ARGV[0]
context = YAML.load_file(context_path)['context']
def get_yaml(obj)
YAML.dump( obj['imports']['external_repositories']['credentials'] ).sub(/.*?\n/,'')
end
The value of - obj['imports']['external_repositories']['credentials'] is
{"iacbox"=>{"basic"=>{"name"=>"", "password"=>""}}, "nexus"=>{"basic"=>{"name"=>"cpreader", "password"=>"swordfish"}}}
Note : I used the sub method to remove "---" at the start of the output
The ERB template calls the above get_yaml method as :
credentials:
<%= get_yaml( context ) %>
The output that is coming is :
credentials:
iacbox:
basic:
name: ''
password: ''
nexus:
basic:
name: cpreader
password: swordfish
while I am expecting the output as :
credentials:
iacbox:
basic:
name: ''
password: ''
nexus:
basic:
name: cpreader
password: swordfish
How can I get the expected output from a dump?

I think the easiest thing for you to do here is just put the credentials key also in the Hash, i.e. change your template snippet so that it is one line:
<%= get_yaml( context ) %>
And change your get_yaml method to be:
def get_yaml(obj)
YAML.dump({'credentials' => obj['imports']['external_repositories']['credentials']})
.sub(/.*?\n/,'')
end
If that doesn't work for you, for example, if you have additional keys underneath the credentials key that you haven't mentioned, you could also do something like this:
def get_yaml(obj)
YAML.dump(obj['imports']['external_repositories']['credentials'])
.sub(/^---\n/,'')
.gsub(/\n/m,"\n ")
end
Where gsub(/\n/m,"\n ") replaces all newlines with a newline plus two spaces.

Related

How to make my YAML produces actual value of my Ruby code

I have a yaml file,
---
- time1: <%= Date.today %>
value: "string"
After I load my YAML, the Ruby code is printed as string. How I get the actual Date.today value?
{"time1"=>"<%= Date.today %>"}
yaml =
<<~YAML
---
- time1: <%= Date.today %>
value: "string"
YAML
Firstly you need to apply ERB to your string using result to execute Ruby code
And only after that parse it with YAML
YAML.load(ERB.new(yaml).result)
# => [{"time1"=>Thu, 08 Sep 2022, "value"=>"string"}]
Probably you skipped first step to execute embedded Ruby and interpolate into original string
ERB.new(yaml).result
# => "---\n - time1: 2022-09-08\n value: \"string\"\n"
Of course you need
require 'erb'
require 'yaml'
if don't use Rails

Extract new keys from .yml file using Ruby and git

I have a Rails project with a .yml file containing I18n translation keys. I want to create a rake task (or similar) which extracts the paths for added keys (lines git recognizes as added). It doesn't matter if the result is being written to the terminal or a file.
Example .yml file:
en:
index: # <-- new key
greeting: "Hello world!" # <-- new key
show:
title: "Old text"
body: "This is a text" # <-- new key
Example output/result of rake task:
en.index.greeting
en.show.body
Is this somehow possible? Thanks!
Yes, you can. This function will print all of I18n keys
def print_translations(prefix, x)
if x.is_a? Hash
prefix += "." if prefix.present?
x.each do |key, value|
print_translations(prefix + key.to_s, value)
end
else
puts prefix
end
end
I18n.translate(:foo)
translations_hash = I18n.backend.send :translations
print_translations "", translations_hash

How to read ERb when importing a .yml file in an initializer?

I'm seeing behavior where an encrypted AD ID login works for a database, but not for an AD connection. Both are using the exact same code to decrypt before passing the passwords to their respective endpoints. The encryption itself is confirmed to be working.
Is the ruby <%= DM::Encryption.decrypt(:staging, 'nsad;nasdnvoasidnv;asoin') %> is not being evaluated before being assigned in adauth.rb?
config/database.yml:
#...
staging:
adapter: jdbcmssql
driver: net.sourceforge.jtds.jdbc.Driver
url: 'jdbc:jtds:sqlserver://server/db;domain=DM'
username: some_id
password: <%= DM::Encryption.decrypt(:staging, 'nsad;nasdnvoasidnv;asoin') %>
pool: 10
wait-timeout: 10
#...
config/ad.yml:
#...
staging:
<<: *default
ad.bind_id: some_id
ad.bind_password: <%= DM::Encryption.decrypt(:staging, 'nsad;nasdnvoasidnv;asoin') %>
#...
initializers/adauth.rb:
AD_CONF = YAML.load_file(Rails.root.join('config/ad.yml'))[Rails.env]
Adauth.configure do |c|
c.domain = AD_CONF["ad.domain"]
c.query_user = AD_CONF["ad.bind_id"]
c.query_password = AD_CONF["ad.bind_password"]
c.server = AD_CONF["ad.host"]
c.base = AD_CONF["ad.user_base"]
end
Your question is a little unclear, but it seems like you suspect the ERb (<%= ...) in your YAML file isn't being evaluated before the YAML is parsed in adauth.rb.
It would be easy enough to find out just by printing the value of AD_CONF["ad.bind_password"] in adauth.rb—but it does seem likely, since you're just calling YAML.load_file and never doing anything to parse the ERb. If you want to parse the ERb, you can see how Rails does it in Rails::Application::Configuration.database_configuration. The most important part is this:
yaml = Pathname.new(paths["config/database"].existent.first || "")
# ...snip...
YAML.load(ERB.new(yaml.read).result) || {}
Following this example, you would change the first line in adauth.rb to something like this:
ad_yaml_path = Rails.root.join('config/ad.yml') # The path to the .yml file
ad_yaml = ERB.new( ad_yaml_path.read ).result # Read the file and evaluate the ERB
ad_hash = YAML.load(ad_yaml) # Parse the resulting YAML
AD_CONF = ad_hash[Rails.env]
(The first line works because Rails.root is a Pathname object, and Pathname#join also returns a Pathname, and Pathname#read works like File#read, returning the contents of the file.)
Of course, this can be shortened (you could make it a one-liner but that'd be pretty hard to read):
ad_yaml = ERB.new( Rails.root.join('config/ad.yml').read ).result
AD_CONF = YAML.load(ad_yaml)[Rails.env]
One more thing: Rails 4.2, which is now in beta, has a config_for method that does exactly this. Instead of the above you would just do this:
AD_CONF = Rails.application.config_for(Rails.root + 'config/ad.yml')
So that's neat.

How do you use fixtures with attr_encrypted

I want to test a model that uses attr_encrypted to encrypt a secret in the database
class Thing
attr_encrypted :secret, encode: true
end
But when I define the secret in a fixture the encoded newline character gets escaped out.
one:
encrypted_secret: '<%= Thing.encrypt_secret(SecureRandom.uuid) %>'
That is:
'axZFZEknxUSYdUlPhwLBbj8CwSeCW5at2INA98EcCcY7MVFdmXvk7Sb4DZhC\nm6qD\n'
Is stored in the database as:
'axZFZEknxUSYdUlPhwLBbj8CwSeCW5at2INA98EcCcY7MVFdmXvk7Sb4DZhC
m6qD'
The problem with this is that this then fails:
thing = things(:one)
assert_equal thing, Thing.find_by_secret(thing.secret)
Thing.find_by_secret(thing.secret) returns nil because the resulting SQL query tries to match the two versions of the encryped secret and fails to get a match.
I have tried:
one:
encrypted_secret: 'axZFZEknxUSYdUlPhwLBbj8CwSeCW5at2INA98EcCcY7MVFdmXvk7Sb4DZhC\nm6qD\n'
but get the same result.
How can I configure my fixtures to work with attr_encrypted?
A solution that works is to replace all '\n' with '\\n' and use double quotes. This works:
one:
encryped_secret: "<%= Thing.encrypt_secret(SecureRandom.uuid).gsub(/\n/, '\\\\n') %>"
Is there a tidier way to do this?
I faced the same situation under Rails4 + attr_encrypted + fixture + Minitest environment, and here my workaround is.
In summary, I had the following steps:
write plain (= unencrypted) text fixture with a specific file extention (in my case, it is *.yml.noenc).
write rake-task to convert from the plain fixture (.yml.noenc) to encrypted fixture (.yml).
Let me explain the detail below.
For example, "Message" model has two attributes 'name' and 'body' which are required to be encrypted as follows:
class Message < ActiveRecord::Base
attr_encrypted :name, key: ...
attr_encrypted :body, key: ...
...
end
write test/fixtures/messages.yml.noenc as follows, which has plain name and body text:
msg1:
name: Hello
body: Hello, I am here...
msg2:
name: How are you
body: Good morning, ...
write like the following rake-task (e.g. lib/tasks/encrypt_fixture.rake) to convert messages.yml.noenc to messages.yml:
require 'active_record/fixtures'
src_yml = 'test/fixtures/messages.yml.noenc'
dest_yml = 'test/fixtures/messages.yml'
task 'test' => dest_yml
namespace :[MY_APP] do
desc "generate encrypted fixture"
file dest_yml => src_yml do |t|
require Rails.root + 'config/environment'
encrypted_hash = {}
for k, v in YAML.load(ERB.new(File.read(Rails.root + src_yml)).result) do
msg = Message.new(v.merge([ANY ADDITIONAL ATTRS]))
encrypted_hash[k] = {
'encrypted_name' => msg.encrypted_name,
'encrypted_name_iv' => msg.encrypted_name_iv,
'encrypted_body' => msg.encrypted_body,
'encrypted_body_iv' => msg.encrypted_body_iv,
[ANY ADDITIONAL KEY_N_VALUE]
}
end
File.open(Rails.root + t.name, 'w') do |f|
f.write(<<EOH)
#----------------------------------------------------------------------
# DO NOT MODIFY THIS FILE!!
#
# This file is generated from #{src_yml} by:
#
# (edit #{src_yml})
# $ rake [MY_APP]:generate_fixture, or
# $ rake
#----------------------------------------------------------------------
EOH
f.write(encrypted_hash.to_yaml)
end
end
end
Please substitute [MY_APP], [ANY ADDITIONAL ATTRS], and [ANY ADDITIONAL KEY_N_VALUE] to actual values.
Then, 'rake' or 'rake test' checks file dependency between messages.yml.noenc and messages.yml, and generate messages.yml when necessary before 'rake test'.

Why does yaml.dump add quotes this key-value pair

I'm trying to write a new entry to a rails database.yml and for some reason I'm getting quotes around this entry
db_yml => {'new_env' => {'database' => 'database_name', '<<' => '*defaults' }}
File.open("#{RAILS_ROOT}/config/database.yml", "a") {|f| YAML.dump(db_yml, f)}
returns
---
new_env:
database: database_name
"<<": "*defaults"
I don't know why the "---" and the quotes around the defaults are returned, any thoughts on how to prevent?
thanks!
<< and * have special meaning in YAML.
Quotes are used to show that << is not merge and * is not an alias.
the --- is just to mark the start of YAML dump.
The double quote around << it's because can be interpretate in YAML format. So it's escape.

Resources