FlexConf
FlexConf is a simple configuration utility that does its job and gets out of
your way. It reads settings from a hash or YAML file (config.yml
by default)
with optional overrides from a *_local.yml
file and from environment
variables. Settings can be retrieved as indifferent hash values (config['foo']
or config[:foo]) or method calls (config.foo).
Simplicity and portability are major design goals. The code is a single
lightweight class (roughly 100 lines of code) with no external gem
dependencies. The spec suite passes in Ruby 1.8.7, 1.9.2, 1.9.3, and current
(October 2011) versions of JRuby and Rubunius.
Installation
Come on kids, you know this one:
gem install flexconf
Or do what the sophisticated urbanite does and use Bundler:
# In your Gemfile:
gem 'flexconf'
Loading From a Hash
This is the simplest approach for small use cases. Just create a Ruby file
and declare your values:
require 'flexconf'
CONFIG = FlexConf.new {
my_arbitrary_username: 'joeschmoe', # Ruby 1.9 syntax
my_arbitrary_password: 'blah1234',
:another_thing => 'here', # Classic 'hashrocket' syntax
'some_number' => 24,
nested: {
'mommy_bird' => 'Tweet!',
'baby_bird' => 'Facebook.'
}
}
This usage mode has no options and no overrides. If you need to calculate
or merge anything, just do it before you create the FlexConf object.
All keys are converted to symbols on creation, regardless of their original
type. Yes, that means numbers and arrays and other crazy objects too. This is
for configuration data; we're not trying to be a general-purpose hash.
Values are left alone, except that nested hashes are converted into nested
FlexConf objects.
Loading from YAML
This is the more flexible usage, with multiple options for scoping and
overrides:
require 'flexconf'
CONFIG = FlexConf.new '../mysettings.yml', # Core filename (include path if needed)
scope: :development, # Only load values from the 'development' key
local: 'mysettings_local.yml', # Merge in values from this other YAML file
environment: true, # Allow overrides from environment variables
override: { :cache => 'Dummy' } # Also override from the provided hash
Or, if it suits your needs, you could simply take the defaults:
require 'flexconf'
CONFIG = FlexConf.new
...which is equivalent to:
CONFIG = FlexConf.new 'config.yml', local: 'config_local.yml', environment: true
As with hash loading, all keys are converted to symbols on creation and the
object is read-only after it's created. Options are described in detail after
the "Using It" section.
Using It
The FlexConf object supports both the 'indifferent hash' style and the 'method
call' style for accessing configuration values. Both are interchangeable and
recursive:
CONFIG[:some_number] #=> 24
CONFIG['some_number'] #=> 24
CONFIG.some_number #=> 24
CONFIG[:nested]['mommy_bird'] #=> 'Tweet!'
CONFIG['nested'][:mommy_bird] #=> 'Tweet!'
CONFIG.nested.mommy_bird #=> 'Tweet!'
CONFIG.nested[:baby_bird] #=> 'Facebook.'
If you've spent a little time working with Chef,
this level of looseness will probably seem familiar. Their attribute access
patterns were a direct inspiration for FlexConf. (If you've spent a lot of
time working with Chef, you're probably at the sanatorium and nothing seems
familiar.)
FlexConf objects are very loosely duck-typed to Hashes, but are not subclasses of Hash. They publicly support the following methods:
[]
(your friend the accessor)has_key?
each
(returns key and value, just like Hash)- the Enumerable mixin
FlexConf objects are read-only once they're initialized. You can't change
any values later. This is by deliberate design decision. (If you need to muck
with your application's configuration after it's up and running, it's not
'configuration' any more, it's mutable state and you should be paying more
attention to it than this.)
Options
Options that can be passed at initialization are as follows (and only work in YAML mode):
:scope
Use this for Rails-style environments. The provided value must be a top-level
key in the main YAML file. The key/value pairs nested beneath it will become
top-level structures for the FlexConf configuration, and the rest of the file
will be ignored. Note that this happens after the YAML library does its
processing, so if your file looks like:
defaults: &defaults
foo: 'bar'
yoo: 'yar'
something_else: 17
test:
<< *defaults
foo: 'car'
...and so forth, a :scope => :test
option will still do the right thing.
Scope limiting applies only to the main YAML file and occurs before any
other options are handled. Overrides from a 'local' YAML file, environment
variables, or a supplied hash are assumed already to be in the desired scope
and won't be transformed.
:local
This option addresses the common use case of supplying sensitive data
(passwords, etc.) or user-specific development machine settings in a secondary
YAML file that is not checked into source control. The options from this
secondary file are merged into the main configuration. There are two ways to
use it:
-
:local => 'path/somefile.yml'
looks for the given file and loads it if it
exists.
-
:local => true
appends '_local' to the base filename of the main YAML
file, and loads it if it exists in the same directory. (E.g., if your
primary file was /conf/amazon_settings.yml
, it would look for
/conf/amazon_settings_local.yml
.)
In either case, no error will be raised if the file is not found.
:override
This option takes a hash value and merges it into the configuration after
the main YAML file and :local
file are processed. It works just like the
"Loading From a Hash" section described above. 'Nuff said.
:environment
This option allows values to be passed in from the command line or from external processes (your Web server, et cetera) by means of environment variables. The environment variable name is lowercased and symbolized, and nested keys can be pointed to by separating them with a double underscore (__
). The intended use case is something like:
$ AMAZON__ACCESS_ID=blahblah AMAZON__SECRET_KEY=yaddayadda rake deploy:thingy
If the Rake task is using a FlexConf anywhere with the :environment
option set, then those two variables will automatically be merged into CONFIG[:amazon][:access_id]
and CONFIG[:amazon][:secret_key]
. As with the :local
option, there are two ways to use it:
-
:environment => ['SOME_ENV_VAR', 'ANOTHER_ENV_VAR']
merges in only the
environment variables specified in the given array. Keys that didn't
previously exist are created (including nested paths). The rest of the
environment is ignored, and no error is raised if the variables aren't
actually set. This is the most secure way to use it if you can plan ahead
for which configuration values may need changing at runtime.
-
:environment => true
scans the entire environment, but only updates keys
that already exist (from the main YAML file or some other override). New
keys are not created, to prevent polluting your configuration with settings
like [:home]
and [:_]
and [:grep_options]
. This is a useful shortcut
if you want your entire configuration to be alterable at runtime, but make
sure you don't have any top-level key names that may collide with common
Unix names.
Support and Such
Documentation can be found at the usual RDoc.info spot.
If you have questions, problems, or suggestions please start by leaving an Issue here, but feel free to email me at sfeley@gmail.com if you don't get a timely response. I'm sporadic about staying on top of my Github stuff, but I'm trying to get better about it.
I encourage pull requests, forks, etc. There's probably more that could be done with this. (But not much more, lest we lose the "simple" part.)
FlexConf is licensed under the Don't Be a Dick license, v0.3. The specific license for the project can be found here. (In brief: do anything you want with this, so long as you aren't a dick about it.)