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