Configuring Rails Environments
eng.joingrouper.com
eng.joingrouper.com
Then you can centralize all your configuration in config/environments/* and config/initializers/. The best practice being that you set the configuration in config/environments/ and you read that configuration point and do something with it in config/initializers/*.
IMO, much nicer than messing with ENVs.
I think a key aspect to make this successful is to make configurations 'inherit' from each other (as you explained on the post, using `require Rails.root.join("config/environments/production")`). It really makes our staging environment configuration only very slightly different from production and is very DRY.
[0] https://signalvnoise.com/posts/3535-beyond-the-default-rails...
I admit while I agree with many parts of 12-factor, I have never full understood the rational for this. What is the rationale of moving from versioned, concise config files, to un-versioned ENV variables?
- "Due to Ruby’s lack of type checking, the following code has a full code path that is not tested."
Add this to your specs: Rails.stub(:env).and_return('staging') ?
- "Bundler won’t install ‘production’ grouped gems on your staging server"
Use identical gems under staging, or override Capistrano's bundle install
- "It’s easy to make mistakes when configuring code by environment."
How about defining a new config value in staging.rb? Your::Application.config.mount_admin
I may be missing something, but how can ENV variables scale beyond a few config variables? I can see an argument that we should all be striving for fleets of micro-services that will only require a few, but many codebases cannot and won't ever realistically meet this goal.
It does require more fancy footwork when deploying new features that need environment variables, but it also lets us share our codebase with non-technical users and the occasional freelancer/potential FT hire without needing to scrub confidential information.
allow(Rails).to receive(:env).and_return 'staging'.inquiry
This preserves the Rails.env.staging? behaviour. Rails.env is an ActiveSupport::StringInquirer num_retires = Configuration.retry_attempts_count
which could fail immediately if the ENV['RETRY_ATTEMPTS_COUNT'] is not set.While for configuring boolean values, we could do something like:
show_confidant if Configuration.display_confidential_info?
Which could handle the extra care around true/false ENV parameter parsing by taking advantage of the question mark in the method name convention that Ruby has.For bonus points wrap that into the existing Rails.application mechanism, so that it's an obvious place for new developers on the project to go to find app parameters.
defaults: &defaults
secret_key_base: <%= ENV.fetch('SECRET_KEY_BASE') %>
default_hostname: <%= ENV.fetch('DEFAULT_HOSTNAME') %>
redis_url: <%= ENV.fetch(ENV['REDIS_PROVIDER'] || 'REDIS_URL') %>
debug: <%= ENV.fetch('DEBUG') == "true" %>
development:
<<: *defaults
production:
<<: *defaults
Though not as convenient as Figaro, I've been pretty happy with the setup so far.Env-parser does a few things to make handling ENV easier. First, it, through virtue of Haskell's IO-concentration properties, forces you to pre-load your relevant environment variables and thus fail-fast due to misspellings and poor parses. Second, it tries to force you to interpret the ENV strings as typed values so that there's a limitation of the syntactic space allowable in ENV files.
Finally, and most importantly, it uses an "applicative functor structure" so that the exact code which parses your ENV can be used to produce documentation automatically. This is nice because it will always be in-sync with your program (modulo errors in my own coding of the library).
If anyone is interested in using this in their Haskell code, I'd be more than happy to push out the newer version. It has much nicer parsers and better reporting.
[0] http://hackage.haskell.org/package/env-parser [1] https://github.com/tel/env-parser-talk
ENV.default_proc = ->(_,key) { raise KeyError.new "Key not found: #{key.inspect}" }
Alas, ENV is not a Hash and doesn't have default_proc. It's an instance of Object, i.e. literally Object.new, decorated with extra methods, see https://github.com/ruby/ruby/blob/d738e3e15533e0f500789faaed.... Wow, TIL.Anyway, a real simple way to have ENV[] behave like ENV.fetch is to define a method on ENV's metaclass:
def ENV.[](key)
fetch key
end
This worked fine in irb but use fetch, intent is clearer.joingrouper getsomething getsomethingapp ... you get the point.
I don't think this is actually a good idea to name your startup this way. Paul graham had a good blog on startup names (see note). SEO space is polluted, people naturally can't remember what your URL is. While it might be ok for mobile apps, I certainly don't think this works for webbased apps.
Thoughts?
SAFE_ENV = Proc.new do |name|
ENV[name].tap{ |value| raise "Environment variable #{name} is missing" unless value }
end
and then for any required env vars I use SAFE_ENV instead of ENV e.g. SAFE_ENV['AWS_KEY'] # throws error if missing
ENV['AWS_KEY'] # silently continueshttp://www.wizard.codes/code-review-cleaning-up-the-environm...
Your production app settings live in a separate repository that is kept much more secure than the other project files. These exist as host_vars files that you can simply drop in the host_vars directory in your ansible configuration and run the deploy script.
A sample host_vars can be included for either setting up a dev vagrant box or even your staging setup, if you don't have any really secret API keys.
That way you can move things around, spin up new boxes or what have you, simply by creating new host_vars files or moving them around.