Gonfic

Tool-oriented configuration: merge config from ~/, ./, ENV, and OptionParser, access as Hashie::Mash

Installation

Add gem 'gonfic' to your application's Gemfile, then run bundle install.

Usage

CONFIGURATION_FILENAME = '.my_tool' # YAML config file format, by default
CONFIGURATION_DEFAULTS = {a: 1, b: {c: 2}} # will deep_merge! using Hashie magic
CONFIG = Gonfic.new(filename: CONFIGURATION_FILENAME, defaults: CONFIGURATION_DEFAULTS)
puts CONFIG.b.c

Design

Gonfic is intended to be used with tools - small, often commandline programs. It will merge configuration from multiple sources, in increasing order of specificity. You can also think about these sources in terms of how long-lived they are:

  • config_defaults:: this is what you determine when writing the tool - lasts as long as this particular tool,
  • ~/$CONFIGURATION_FILENAME: this will be applied every time a particular user uses your tool,
  • ./$CONFIGURATION_FILENAME: every time that user uses your tool, in a particular directory (e.g. source code checkout),
  • ENV variables: every time your tool is used during a particular session (so it can override directory settings, but persists over multiple runs),
  • OptionParser'd settings: during a single, particular run.

In the intended scenario (where you allow the config to be slightly flexible) you will barely need to configure your tool's config, aside from providing config_defaults: once. No listing of allowed setting readers, no spelling out options and types, no mapping to formats - Gonfic takes care of all that's needed.

Why not...?

https://github.com/bkeepers/dotenv or https://github.com/rubyconfig/config

These are application-oriented: they have robust support for multiple environments and integration with frameworks.

https://dry-rb.org/gems/dry-configurable/main/

This is more for library developers: allows you to add configurable settings to an object, which other code will later use.

https://github.com/cjbottaro/config_spartan

This one is actually kind of cool, in its minimalism - and intended for similar usage scenarios. But it doesn't seem to be actively developed nowadays, and I wanted a few more features, mostly around some opinionated/automagical defaults.

Advanced scenarios

As 90+% of the functionality is configured by different ways of calling Gonfic.new, this section delves into possible variants you might want to consider - to fine-tune Gonfic to your needs.

Deferring dangerous operations

The Gonfic.new call will peform file I/O and other operations, which can potentially fail and raise errors. Doing things like that is generally frowned upon in initializers and immediately when files are required - so CONFIG = Gonfic.new might not be the best practice ever, succinct as it is. A more prudent approach would be:

module MyTool
    def self.config
        @config ||= Gonfic.new(...)
    end
end

In this way you are lazily deferring the "dangerous" operations until first usage, then memoizing. Additionally, with access to your config via a method that's easy to stub, your test suite will be happy :)

Providing custom list of directories

By default, Gonfic.new will look for filename: in ~/ and ./ (merging them, if both are present). If you want to load from other directories, just set the directories: parameter to your list.

Loading configuration from ENV

If you provide filename:, Gonfic.new will also check ENV for variables it considers relevant. See EnvExtractor.env_prefix_from_filename for what it will scan - either in source code, or in bin/console. The pattern, in general, is that .tool-name becomes TOOL_NAME_* - and any variables matching the pattern, and present in defaults: (again, converted to upcase with underscores), will be merged into resulting configuration.

To disable this behavior, set env_variable_prefix: to falsey (nil will do).

You can also override the prefix. Underscore at the end is optional.

Source code

https://gitlab.com/tanstaafl/gonfic

Development

Your first steps should be:

git clone git@gitlab.com:tanstaafl/gonfic.git
cd gonfic
bundle install
bundle exec rake

This will run tests (Minitest) and linter (Rubocop, custom config, see .rubocop.yml).

Note: the repo does not force the bundle to be vendorized, but should work fine if you set BUNDLE_PATH: "vendor" in your ~/.bundle/config. Hint, hint ;)

You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine from sources, run bundle exec rake install.

Contributing

Bug reports and merge requests are welcome on GitLab at https://gitlab.com/tanstaafl/gonfic.

Name

Yeah, it's not great. As in: not obvious. If you have an idea how to make it better:

  1. Go to https://rubygems.org/gems?letter=C
  2. Binary-search to conf* (at this stage you might begin to appreciate how hard naming is in this particular case, I hope).
  3. Send me an issue (or a merge request, or something) with a better name that's available on RubyGems! :D

License

The gem is available as open source under the terms of the MIT License.