0

I'm facing a problem that I couldn't find a working solution yet.

I have my YAML config file for the environment, let's call it development.yml.

This file is used to create the hash that should be updated:

data = YAML.load_file(File.join(Rails.root, 'config', 'environments', 'development.yml'))

What I'm trying to accomplish is something along these lines. Let's suppose we have an element of the sort

data['server']['test']['user']
data['server']['test']['password']

What I want to have is:

data['server']['test']['user'] = #{Server.Test.User}
data['server']['test']['password'] = #{Server.Test.Password}

The idea is to create a placeholder for each value that is the key mapping for that value dynamically, going until the last level of the hash and replacing the value with the mapping to this value, concatenating the keys.

Sorry, it doesn't solve my problem. The location data['server']['test']['user'] will be built dynamically, via a loop that will go through a nested Hash. The only way I found to do it was to append to the string the key for the current iteration of the Hash. At the end, I have a string like "data['server']['test']['name']", which I was thinking on converting to a variable data['server']['test']['name'] and then assigning to this variable the value #{Server.Test.Name}. Reading my question I'm not sure if this is clear, I hope this helps to clarify it.

Input sample:

api: 'active'
server:
  test:
    user: 'test'
    password: 'passwordfortest'
  prod:
    user: 'nottest'
    password: 'morecomplicatedthantest'

In this case, the final result should be to update this yml file in this way:

api: #{Api}
server:
  test:
    user: #{Server.Test.User}
    password: #{Server.Test.Password}
  prod:
    user: #{Server.Prod.User}
    password: #{Server.Prod.Password}

It sounds silly, but I couldn't figure out a way to do it.

2
  • 2
    Sidenote: data['server']['host'] = #{Server.Host} and data['server']['host']['user'] = #{Server.Host.User} is not possible simultaneously. Commented Apr 11, 2018 at 8:56
  • Thanks, @mudasobwa. I fixed my question. Commented Apr 12, 2018 at 12:56

3 Answers 3

1

I am posting another answer now since I realize what the question is all about.

Use Iteraptor gem:

require 'iteraptor'
require 'yaml'

# or load from file
yaml = <<-YAML.squish
api: 'active'
server:
  test:
    user: 'test'
    password: 'passwordfortest'
  prod:
    user: 'nottest'
    password: 'morecomplicatedthantest'
YAML

mapped =
  yaml.iteraptor.map(full_parent: true) do |parent, (k, _)|
    v = parent.map(&:capitalize).join('.')
    [k, "\#{#{v}}"]
  end

puts YAML.dump(mapped)
#⇒ ---
#  api: "#{Api}"
#  server:
#    test:
#      user: "#{Server.Test.User}"
#      password: "#{Server.Test.Password}"
#    prod:
#      user: "#{Server.Prod.User}"
#      password: "#{Server.Prod.Password}"

puts YAML.dump(mapped).delete('"')
#⇒ ---
#  api: #{Api}
#  server:
#    test:
#      user: #{Server.Test.User}
#      password: #{Server.Test.Password}
#    prod:
#      user: #{Server.Prod.User}
#      password: #{Server.Prod.Password}
Sign up to request clarification or add additional context in comments.

2 Comments

For me this is enough for a solution. Just one question about your solution: is there a way to achieve that without adding a gem?
@Harry sure, this gem is open source; feel free to borrow any code that is needed from it (I am the author of this gem, so I proudly grant you an ability to do whatever you want with it.)
1

Use String#%:

input = %|
  data['server']['host']['name'] = %{server_host}
  data['server']['host']['user'] = %{server_host_user}
  data['server']['host']['password'] = %{server_host_password}
|
puts (
  input % {server_host: "Foo",
           server_host_user: "Bar",
           server_host_password: "Baz"})
#⇒ data['server']['host']['name'] = Foo
#  data['server']['host']['user'] = Bar
#  data['server']['host']['password'] = Baz

4 Comments

Sorry, it doesn't solve my problem. The location data['server']['host']['name'] will be built dynamically, via a loop that will go through a nested Hash. The only way I found to do it was to append to the string the last key of the Hash. At the end, I have a string "data['server']['host']['name']", which I was thinking on converting to a variable data['server']['host']['name'] and then assigning to this variable the value #{Server.Host.Name}. Reading my question I'm not sure if this is clear, I hope this helps to clarify it. I have updated my question.
Please show a development.yml part that is responsible for these keys.
It did not get cleaner. You constantly mention 'host' key in your hash and your sample does not have any trail of it. Please take a time to state clearly, what is the input, and what is the expected output. For the example posted there is no need to create any hash keys dynamically, they don’t differ in input and in output.
Ok, lemme puts some details here. First, the examples above and below in the question were not from the same set. It's fixed now. Second, if you think they don't differ from each other, it's not clear what I'm asking. user: #{Server.Test.User} is an example of the output, there's no interpolation there. #{Server.Test.User} is a placeholder that will be completely replaced by another tool, which in the end will be user: 'test'. This is not part of the question, but this is the whole reason for this script.
0

You can not add key-value pair to a string.

data['server']['host'] # => which results in a string

Option 1:

You can either save Server.Host as host name in the hash

data['server']['host']['name'] = "#{Server.Host}"
data['server']['host']['user'] = "#{Server.Host.User}"
data['server']['host']['password'] = "#{Server.Host.Password}"

Option 2:

You can construct the hash in a single step with Host as key.

data['server']['host'] = { "#{Server.Host}" => { 
                              'user' => "#{Server.Host.User}",
                              'password' => "#{Server.Host.Password}"
                            } 
                          }

1 Comment

Sorry, I think my question was not clear. The map to the location will be built dynamically and the value to be updated there will be also dynamically created based on the mapping.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.