1

I am currently searching for the best way to store configuration data especially for security credentials in my rails 4.2 app.

It seems to me, that there are two good solutions.

  1. Gem Figaro (Simple, Heroku-friendly Rails app configuration using ENV and a single YAML file).
  2. A custom yml file in combination with Rails 4.2 feature config_for added to gitignore to keep it out of your version control system.

Both solutions are offering nearly the same benefits. Simple and securely storing security credentials with simple access (ENv["my_variable"] vs Rails.application.config_for(:my_app)["my_variable"]) while keeping your configuration out of git.

I think it would be better to avoid an additional dependency if it isn't necessary. So I would tend to use a custom yml file in combination with config_for because it is a built-in Rails feature. But I am not that experienced with environment variables. Are there any advantages that would justify the use of an additional dependency?

coderuby
  • 1,188
  • 1
  • 11
  • 26

3 Answers3

1

My solution is the following one:

I will pick figaro because of the following two reasons.

  1. figaro has a require_keys method to raise an error during initialization in case you didn't specify all neccessary configuration values.
  2. with figaro I keep more flexibilty to change the configuration implementation based on environment variables.

To keep the flexibility I will not use the Figaro.env proxy but pure ENV variables. This way I could change the implementation of environment variables more easily in the future without touching the codebase.

Additionally I benchmarked four solutions with the following results:

2.2.1 :016 >   n = 50000
2.2.1 :017 > Benchmark.bm do |x|
2.2.1 :018 >   x.report { n.times do ; ENV["mail_address"] ; end }
2.2.1 :019?>   x.report { n.times do ; Rails.application.config_for(:app)["mail_address"]; end }
2.2.1 :020?>   x.report { n.times do ; Figaro.env.mail_address ; end }
2.2.1 :021?>   x.report { n.times do ; Rails.configuration.x.mail_address ; end }
2.2.1 :022?> end
       user     system      total        real
   0.070000   0.010000   0.080000 (  0.078491)
  11.060000   1.260000  12.320000 ( 12.497704)
   5.600000   0.070000   5.670000 (  5.758400)
   0.050000   0.000000   0.050000 (  0.056211)

I made the following findings:

  1. Using environment variables directly is way faster than using the Figaro.env proxy.
  2. Using the Figaro.env proxy takes only have the time of using rails config_for
  3. Using the Rails configuration namespace x is the fastest of them all.

So my best solution would be: Use figaro gem to set environment variables according to your environment. Get them via ENV['your_key']. Use them in an initializer via Rails configuration namespace x:

e.g.

# config/application.yml

custom_app_id: "2954"
custom_key: "7381a978f7dd7f9a1117"
custom_secret: "abdc3b896a0ffb85d373"

and

# config/initializer/custom_config.rb

App::Application.configure do
  config.x.custom_app_id = ENV['custom_app_id']
  config.x.custom_key  = ENV['custom_key']
  config.x.custom_secret = ENV['custom_secret']
end

In case you need dynamic values you could use Rails config_for, because you could put ERB inside the yml files.

Update

I did more benchmarking with the following results:

2.2.2 :015 > Benchmark.bm do |x|
2.2.2 :016 >     x.report { n.times do ; ENV["logo_file"] ; end }
2.2.2 :017?>   x.report { n.times do ; ENV["logo_file_login"] ; end }
2.2.2 :018?>   x.report { n.times do ; ENV["company_name"] ; end }
2.2.2 :019?>   x.report { n.times do ; Rails.configuration.x.logo_file ; end }
2.2.2 :020?>   x.report { n.times do ; Rails.configuration.x.logo_file_login ; end }
2.2.2 :021?>   x.report { n.times do ; Rails.configuration.x.company_name ; end }
2.2.2 :022?>   end
       user     system      total        real
   3.430000   0.030000   3.460000 (  3.503262)
   3.570000   0.030000   3.600000 (  3.642426)
   5.020000   0.040000   5.060000 (  5.133344)
   4.380000   0.040000   4.420000 (  4.508829)
   4.390000   0.030000   4.420000 (  4.470256)
   4.360000   0.040000   4.400000 (  4.442839)

It seems to me, that there is not much difference between using the ENV variables and using the Rails configuration namespace x. Therefore I will skip the step with the additional configuration file and will use plain ENV variables where I need the configuration values.

Community
  • 1
  • 1
coderuby
  • 1,188
  • 1
  • 11
  • 26
  • 2
    You can achieve the benefit of `require_keys` in any configurator if you just use `ENV.fetch('var')` which `raises KeyError` or use `ENV.fetch('var', 'default value')` when there is a reasonable default. Personally, I use https://github.com/railsconfig/config which isn't mentioned in this SO thread. – Chris Beck Oct 18 '16 at 21:23
1

I use choices and, for me, it is absolutely worth the additional dependency. The gem is very lightweight and it makes handling configuration super easy.

I don't have to worry about setting a bunch of development environment variables for each different app. I just keep it all in my setting.local.yml (which has been added to my .gitignore). It pulls in the environment variables if they exist but also provides a dead simple way to define/override them for the local machine.

Here's an example from an app I have...

config/setting.yml:

defaults: &defaults
  aws:
    username: <%= ENV['AWS_USERNAME'] %>
    access_key_id: <%= ENV['AWS_ACCESS_KEY_ID'] %>
    secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %>
  database_url: <%= ENV['DATABASE_URL'] %>
  postmark:
    api_key: <%= ENV['POSTMARK_API_KEY'] %>

development:
  <<: *defaults

test:
  <<: *defaults

staging:
  <<: *defaults

production:
  <<: *defaults

config/setting.local.yml:

development:
  database_url: postgresql://myapp_dev:myapp_dev@localhost:5432/myapp_dev

config/application.rb:

module MyApp
  class Application < Rails::Application
    config.from_file 'settings.yml'
  end
end

config/initializers/postmark.rb:

if Rails.configuration.postmark.api_key.present?
  Rails.application.config.action_mailer.postmark_settings = { api_key: Rails.configuration.postmark.api_key }
end
Karl Wilbur
  • 5,898
  • 3
  • 44
  • 54
0
  • The 12-factor app recommends storing configuration in environment variables so that they can be managed individually, not in "environments" (production, joes-staging...), but that applies only to larger or growing apps, or apps with many servers.
  • Then there's the risk of mistakenly checking in the file. Changing this config file or environment variables is similar in this case and environment variables are still not only managed in one place (also on the developer's machine).
  • Figaro has Heroku support to easily update the production system, that could be a plus.
  • The config_for feature keeps the variables all in Rails, so there's no risk of disclosing it to sub-processes as it might happen with environment variables, but there are solutions (see the last section).
  • Still config_for wouldn't be the only place where you store configuration, there's at least also config/database.yml.
  • So a combination of environment variables (managed e.g. via figaro) in Rails config files is probably a good solution to manage all configuration in one place.
hawe
  • 109
  • 4