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
“‘yml
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
“‘yml
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:
“‘yml 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
“‘yml # .application.yml base:
user: deployer
debug:
extends: base
# ... other values
“‘
“‘yml # .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:
“‘yml foo:
build_time: "#{Time.now}" # Will be evaluated at configuration resolving step
“‘
Configuration values are available to and can be referenced with ‘c`:
“‘yml 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 have multiple defenitions it will be overwritten by topmost value. Except several cases where Jac handles value resolution specialy
### Merging hashes
Hashes inside profile are recurseively merged automaticly. This rule works for profile extensions and value redefenitions too.
Example:
“‘yml base:
servers:
release: http://release.com
debug:
extends: base
debug: http://debug.com
“‘
### Merging sets
Sets allowed to be merged with ‘nil`s and any instance of `Enumerable`. Merge result is always new `Set` instance. “`yml release:
extends:
- no_rtti
- no_debug
flags: !set {} # empty set
no_rtti:
flags:
- -fno-rtti
no_debug:
flags:
- -DNDEBUG
“‘
Resulting profile will have ‘-fno-rtti, -DNDEBUG` in `release profile` ## 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
-
‘jac.yml` - expected to be main configuration file.
Usually it placed under version control.
-
‘jac.user.yml` - user defined overrides for main
configuration, sensitive data which can’t be placed under version control.
-
‘jac.override.yml` - final overrides.
-
%w[jac.yml jac.user.yml jac.override.yml].freeze
Class Method Summary collapse
-
.load(profile, files: CONFIGURATION_FILES, dir: nil) ⇒ OpenStruct
Read configuration from configuration files.
-
.read(profile, *streams) ⇒ OpenStruct
Generates configuration object for given profile and list of streams with YAML document names to read.
Class Method Details
.load(profile, files: CONFIGURATION_FILES, dir: nil) ⇒ OpenStruct
Read configuration from configuration files.
436 437 438 439 440 441 442 443 |
# File 'lib/jac/configuration.rb', line 436 def load(profile, files: CONFIGURATION_FILES, dir: nil) # Read all known files streams = files .map { |f| [dir ? File.join(dir, f) : 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
426 427 428 429 |
# File 'lib/jac/configuration.rb', line 426 def read(profile, *streams) profile = [ConfigurationReader::DEFAULT_PROFILE_NAME] if profile.empty? ConfigurationReader.new(streams).read(*profile) end |