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
- TOP_PROFILE_NAME =
Name of top level profile. ^top profile is implicit it automaticly merged on top of resulting configuration
'^top'.freeze
- BASE_PROFILE_NAME =
Name of base level profile. ^base profile is implicit resulting configuration automaticly merged on top of it.
'^base'.freeze
- DEFAULT_PROFILE_NAME =
Any configuration set always contains
defaultprofile which is loaded when no profile requested. 'default'.freeze
- BASIC_PROFILES =
List of default profiles. Having this list we can easily create default configuration.
[ TOP_PROFILE_NAME, BASE_PROFILE_NAME, DEFAULT_PROFILE_NAME ].freeze
- EXTENDS_KEY =
Key where all inherited profiles listed
'extends'.freeze
- 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.
492 493 494 495 496 497 498 499 |
# File 'lib/jac/configuration.rb', line 492 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
482 483 484 485 |
# File 'lib/jac/configuration.rb', line 482 def read(profile, *streams) profile = [Configuration::DEFAULT_PROFILE_NAME] if profile.empty? ConfigurationReader.new(streams).read(*profile) end |