Module: Jac::Configuration

Defined in:
lib/jac/configuration.rb,
lib/jac/merger.rb

Overview

Configuration is loaded from well formed YAML streams. Each document expected to be key-value mapping where keys a ‘profile` names and values is a profile content. Profile itself is key-value mapping too. Except reserved key names (i.e. `extends`) each key in profile is a configuration field. For example following yaml document

“‘

foo:
  bar: 42
qoo:
  bar: 32

“‘

represents description of two profiles, ‘foo` and `qoo`, where field `bar` is set to `42` and `32` respectively.

Profile can be constructed using combination of other profiles for example having ‘debug` and `release` profiles for testing and production. And having `remote` and `local` profiles for building on local or remote machine. We cant get `debug,local`, `debug,remote`, `release,local` and `release,remote` profiles. Each of such profiles is a result of merging values of listed profiles. When merging profile with another configuration resolver overwrites existing fields. For example if `debug` and `local` for some reason have same field. In profile `debug,local` value from `debug` profile will be overwritten with value from `local` profile.

## Extending profiles

One profile can ‘extend` another. Or any amount of other profiles. To specify this one should use `extends` field like that

“‘

base:

application_name: my-awesome-app
port: 80

version:

version_name: 0.0.0
version_code: 42

debug:

extends: [base, version]
port: 9292

“‘

In this example ‘debug` will receive following fields:

“‘ application_name: my-awesome-app # from base profile port: 9292 # from debug profile version_name: 0.0.0 # from version profile version_code: 42 # from version profile “`

## Merging multiple configuration files

Configuration can be loaded from multiple YAML documents. Before resolve requested profile all described profiles are merged down together. Having sequence of files like ‘.application.yml`, `.application.user.yml` with following content

“‘ # .application.yml base:

user: deployer

debug:

extends: base
# ... other values

“‘

“‘ # .application.user.yml base:

user: developer

“‘

We’ll get ‘user` field overwritten with value from `.application.user.yml`. And only after that construction of resulting profile will begin (for example `debug`)

## String evaluation

Configuration resolver comes with powerful yet dangerous feature: it allows evaluate strings as ruby expressions like this:

“‘ foo:

build_time: "#{Time.now}" # Will be evaluated at configuration resolving step

“‘

Configuration values are available to and can be referenced with ‘c`:

“‘ base:

application_name: my-awesome-app

debug:

extends: base
server_name: "#{c.application_name}-debug"   # yields to my-awesome-app-debug

release:

extends: base
server_name: "#{c.application_name}-release" # yields to my-awesome-app-release

“‘

All strings evaluated after profile is constructed thus you don’t need to have declared values in current profile but be ready to get ‘nil`.

## Merging values

By default if one value definition overwrites another ### Merging hashes

### Merging sets

## Generic profiles

Same result as shown above can be achieved with generic profiles. Generic profile is a profile which name is regex (i.e. contained in ‘/…/`):

“‘ base:

application_name: my-awesome-app

/(release|debug)/: # Profile name is a regex, with single capture (1)

extends: base
server_name: "#{c.application_name}-#{c.captures[1]}"  # yields  my-awesome-app-release or  my-awesome-app-debug

“‘

If profile name matches multiple generic profiles it not defined which profile will be used.

Defined Under Namespace

Classes: ConfigurationEvaluator, ConfigurationReader, EvaluationContext, Merger, ProfileResolver

Constant Summary collapse

CONFIGURATION_FILES =

List of files where configuration can be placed

  • ‘application.yml` - expected to be main configuration file.

Usually it placed under version control.

  • ‘application.user.yml` - user defined overrides for main

configuration, sensitive data which can’t be placed under version control.

  • ‘application.override.yml` - final overrides.

%w[application.yml application.user.yml application.override.yml].freeze

Class Method Summary collapse

Class Method Details

.load(profile, files: CONFIGURATION_FILES, dir: Dir.pwd) ⇒ Object

Read configuration from configuration files.



397
398
399
400
401
402
403
404
# File 'lib/jac/configuration.rb', line 397

def load(profile, files: CONFIGURATION_FILES, dir: Dir.pwd)
  # Read all known files
  streams = files
            .map { |f| [File.join(dir, f), f] }
            .select { |path, _name| File.exist?(path) }
            .map { |path, name| [IO.read(path), name] }
  read(profile, *streams)
end

.read(profile, *streams) ⇒ OpenStruct

Generates configuration object for given profile and list of streams with YAML document names to read

Parameters:

  • profile (Array)

    list of profile names to merge

  • streams (Array)

    list of YAML documents and their

Returns:

  • (OpenStruct)

    instance which contains all resolved profile fields



391
392
393
394
# File 'lib/jac/configuration.rb', line 391

def read(profile, *streams)
  profile = [ConfigurationReader::DEFAULT_PROFILE_NAME] if profile.empty?
  ConfigurationReader.new(streams).read(*profile)
end