NOTE: this readme shows doc for the upcoming 2.0 version (2.0.0.pre
is available on RubyGems).
For version 1.x see 1-4-stable branch.
Rails/Ruby plugin/application configuration tool which allows you to load parameters from different sources: YAML, Rails secrets/credentials, environment.
Allows you to easily follow the twelve-factor application principles and adds zero complexity to your development process.
Libraries using Anyway Config:
Adding to a gem:
# my-cool-gem.gemspec
Gem::Specification.new do |spec|
# ...
spec.add_dependency "anyway_config", "2.0.0.pre"
# ...
end
Or adding to your project:
# Gemfile
gem "anyway_config", "2.0.0.pre"
-
Ruby (MRI) >= 2.5.0
-
JRuby >= 9.2.7
Create configuration class:
require "anyway"
module MyCoolGem
class Config < Anyway::Config
attr_config user: "root", password: "root", host: "localhost"
end
end
attr_config
creates accessors and default values. If you don't need default values just write:
attr_config :user, :password, host: "localhost", options: {}
Then create an instance of the config class and use it:
module MyCoolGem
def self.config
@config ||= Config.new
end
end
MyCoolGem.config.user #=> "root"
Anyway Config relies on the notion of config name to populate data.
By default, Anyway Config uses the config class name to infer the config name using the following rules:
- if the class name has a form of
<Module>::Config
then use the module name (SomeModule::Config => "somemodule"
) - if the class name has a form of
<Something>Config
then use the class name prefix (SomeConfig => "some"
)
NOTE: in both cases the config name is a downcased module/class prefix, not underscored.
You can also specify the config name explicitly (it's required in cases when you class name doesn't match any of the patterns above):
module MyCoolGem
class Config < Anyway::Config
config_name :cool
attr_config user: "root", password: "root", host: "localhost", options: {}
end
end
By default, Anyway Config uses upper-cased config name as a prefix for env variable names (e.g.
config_name :my_app
will result to parsing MY_APP_
prefix).
You can set env prefix explicitly:
module MyCoolGem
class Config < Anyway::Config
config_name :cool_gem
env_prefix :really_cool # now variables, starting wih `REALLY_COOL_`, will be parsed
attr_config user: "root", password: "root", host: "localhost", options: {}
end
end
Sometimes it's useful to set some parameters explicitly during config initialization.
You can do that by passing a Hash into .new
method:
config = MyCoolGem::Config.new(
user: "john",
password: "rubyisnotdead"
)
# The value would not be overriden from other sources (such as YML file, env)
config.user == "john"
You can also create configuration objects without pre-defined schema (just like Rails.application.config_for
but more powerful):
# load data from config/my_app.yml, secrets.my_app (if using Rails), ENV["MY_APP_*"]
# MY_APP_VALUE=42
config = Anyway::Config.for(:my_app)
config["value"] #=> 42
# you can specify the config file path or env prefix
config = Anyway::Config.for(:my_app, config_path: "my_config.yml", env_prefix: "MYAPP")
NOTE: version 2.x supports Rails >= 5.0; for Rails 4.x use version 1.x of the gem.
Your config will be filled up with values from the following sources (ordered by priority from low to high):
RAILS_ROOT/config/my_cool_gem.yml
(for the currentRAILS_ENV
, supportsERB
):
test:
host: localhost
port: 3002
development:
host: localhost
port: 3000
NOTE: you can override the default YML lookup path by setting MYCOOLGEM_CONF
env variable.
Rails.application.secrets.my_cool_gem
(ifsecrets.yml
present):
# config/secrets.yml
development:
my_cool_gem:
port: 4444
Rails.application.credentials
(if supported):
my_cool_gem:
host: secret.host
NOTE: You can backport Rails 6 per-environment credentials to Rails 5.2 app using this patch.
ENV['MYCOOLGEM_*']
.
You can store application-level config classes in app/configs
folder.
Anyway Config automatically adds this folder to Rails autoloading system to make it possible to autoload configs even during the configuration phase.
Consider an example: setting the Action Mailer host name for Heroku review apps.
We have the following config to fetch the Heroku provided metadata:
# This data is provided by Heroku Dyno Metadadata add-on.
class HerokuConfig < Anyway::Config
attr_config :app_id, :app_name,
:dyno_id, :release_version,
:slug_commit
def hostname
"#{app_name}.herokuapp.com"
end
end
Then in config/application.rb
you can do the following:
config.action_mailer.default_url_options = {host: HerokuConfig.new.hostname}
When you're using Anyway Config in non-Rails environment, we're looking for a YAML config file
at ./config/<config-name>.yml
.
You can override this setting through special environment variable – 'MYCOOLGEM_CONF' – containing the path to the YAML file.
NOTE: in pure Ruby apps we have no knowledge of environments (test
, development
, production
, etc.); thus we assume that the YAML contains values for a single environment:
host: localhost
port: 3000
Environmental variables work the same way as with Rails.
It's useful to have personal, user-specific configuration in development, which extends the project-wide one.
We support this by looking at local files when loading the configuration data:
<config_name>.local.yml
files (next to* the global<config_name>.yml
)config/credentials/local.yml.enc
(for Rails >= 6, generate it viarails credentials:edit --environment local
).
* If the YAML config path is not default (i.e. set via <CONFIG_NAME>_CONF
), we lookup the local
config at this location, too.
Local configs are meant for using in development and only loaded if Anyway::Settings.use_local_files
is true
(which is true by default if RACK_ENV
or RAILS_ENV
env variable is equal to "development"
).
NOTE: in Rails apps you can use Rails.application.configuration.anyway_config.use_local_files
.
Don't forget to add *.local.yml
(and config/credentials/local.*
) to your .gitignore
.
NOTE: local YAML configs for Rails app must be environment-free (i.e. you shouldn't have top-level development:
key).
There are #clear
and #reload
methods which do exactly what they state.
Note: #reload
also accepts overrides
key to provide explicit values (see above).
It's possible to use config as option parser (e.g. for CLI apps/libraries). It uses
optparse
under the hood.
Example usage:
class MyConfig < Anyway::Config
attr_config :host, :log_level, :concurrency, :debug, server_args: {}
# specify which options shouldn't be handled by option parser
ignore_options :server_args
# provide description for options
describe_options(
concurrency: "number of threads to use"
)
# mark some options as flag
flag_options :debug
# extend an option parser object (i.e. add banner or version/help handlers)
extend_options do |parser, config|
parser.banner = "mycli [options]"
parser.on("--server-args VALUE") do |value|
config.server_args = JSON.parse(value)
end
parser.on_tail "-h", "--help" do
puts parser
end
end
end
config = MyConfig.new
config.parse_options!(%w[--host localhost --port 3333 --log-level debug])
config.host # => "localhost"
config.port # => 3333
config.log_level # => "debug"
# Get the instance of OptionParser
config.option_parser
Rails 4.2 introduced new feature: Rails.application.config_for
. It looks very similar to
Anyway::Config.for
, but there are some differences:
Feature | Rails | Anyway Config |
---|---|---|
load data from config/app.yml |
yes | yes |
load data from secrets |
no | yes |
load data from credentials |
no | yes |
load data from environment | no | yes |
local config files | no | yes |
return Hash with indifferent access | no | yes |
support ERB within config/app.yml |
yes | yes* |
raise errors if file doesn't exist | yes | no |
*make sure that ERB is loaded
But the main advantage of Anyway::Config is that it can be used without Rails!)
Environmental variables for your config should start with your config name, upper-cased.
For example, if your config name is "mycoolgem" then the env var "MYCOOLGEM_PASSWORD" is used as config.password
.
Environment variables are automatically serialized:
"True"
,"t"
and"yes"
totrue
;"False"
,"f"
and"no"
tofalse
;"nil"
and"null"
tonil
(do you really need it?);"123"
to 123 and"3.14"
to 3.14.
Anyway Config supports nested (hashed) env variables. Just separate keys with double-underscore.
For example, "MYCOOLGEM_OPTIONS__VERBOSE" is parsed as config.options["verbose"]
.
Array values are also supported:
# Suppose ENV["MYCOOLGEM_IDS"] = '1,2,3'
config.ids #=> [1,2,3]
If you want to provide a text-like env variable which contains commas then wrap it into quotes:
MYCOOLGEM = "Nif-Nif, Naf-Naf and Nouf-Nouf"
We provide the with_env
test helper to test code in the context of the specified environment variables values:
describe HerokuConfig, type: :config do
subject { described_class.new }
specify do
# Ensure that the env vars are set to the specified
# values within the block and reset to the previous values
# outside of it.
with_env(
"HEROKU_APP_NAME" => "kin-web-staging",
"HEROKU_APP_ID" => "abc123",
"HEROKU_DYNO_ID" => "ddyy",
"HEROKU_RELEASE_VERSION" => "v0",
"HEROKU_SLUG_COMMIT" => "3e4d5a"
) do
is_expected.to have_attributes(
app_name: "kin-web-staging",
app_id: "abc123",
dyno_id: "ddyy",
release_version: "v0",
slug_commit: "3e4d5a"
)
end
end
end
If you want to delete the env var, pass nil
as the value.
This helper is automatically included to RSpec if RAILS_ENV
or RACK_ENV
env variable is equal to "test". It's only available for the example with the tag type: :config
or with the path spec/configs/...
.
You can add it manually by requiring "anyway/testing/helpers"
and including the Anyway::Test::Helpers
module (into RSpec configuration or Minitest test class).
Bug reports and pull requests are welcome on GitHub at https://github.com/palkan/anyway_config.
The gem is available as open source under the terms of the MIT License.