Configuration Manager - coolsamson7/inject GitHub Wiki
Overview
The purpose of this configuration mechanism is to provide a uniform api to access configuration values and at the same time allows different implementations to act as providers - process info, plists, etc. - for concrete values. In addition to static values, dynamic configuration values are supported by letting the programmer install callbacks that react on changes of particular values.
Let's look at an example first:
let configurationManager = ConfigurationManager()
configurationManager.addSource(PlistConfigurationSource(name: "Info")) // Info.Plist
let url = try configurationManager.getValue(String.self, key: "url")
let port = try configurationManager.getValue(Int.self, key: "port", defaultValue: 8080) // a default value
As you can see, the int value is automatically covered to the requested type ( if possible )
Every configuration value is defined by several properties
namespacean optional "." separated path or ""keythe configuration nametypethe configuration typevaluethe configuration valuedynamica boolean property that specifies if the value is considered static or dynamic
The namespace and key properties uniquely identify a configuration item. In addition a property scope - a "." separated path - may be supplied that is also part of the key.
The idea is that when fetching configuration values given a specific scope, the exact key is looked up. If not available all parent scopes are traversed and the first result will be returned. The mechanism allows for inheritance of configuration values and simplifies configuration of similar processes - e.g. nodes in a cluster - by defining common values and overriding values that are bound to a specific environment which would be coded as a specific scope.
Example:
scope: ""( default ) namespace "a.b" key: "key" value: 0 scope: "foo" namespace "a.b" key: "key" value: 1scope "foo.bar"namespace "a.b" key: "key" value: 2
retrieving the configuration value "a.b:key" ( ':' simply separates the namespace from the key) would result in different values for a supplied scope
scope="": 0, the defaultscope="foo": 1scope="foo.bar": 2scope="foo.bazong": 1, the inherited value from scope "foo"
Normally it is treated as an exception if identical configuration value specifications are encountered. As an exception system variables are always allowed to override existing values!
Let's look at the involved protools.
ConfigurationProvider
protocol ConfigurationProvider {
func getConfigurationItem(namespace : String, key : String) -> ConfigurationItem?
func hasValue(namespace : String, key : String, scope : Scope?) -> Bool
func getValue(type : Any.Type, namespace : String, key : String, defaultValue: Any?, scope : Scope?) throws -> Any
func addListener(namespace : String, key : String, listener : ConfigurationListener , expectedType : Any.Type, scope : Scope) -> Void
}
defines the protocol that is used to retrieve configuration values.
ConfigurationSource
public protocol ConfigurationSource {
func load(configurationManager : ConfigurationManager) throws -> Void
func startListening(configurationManager : ConfigurationManager, seconds : Int) -> Void
var url : String { get }
var mutable : Bool { get }
var canOverrule: Bool { get }
}
defines a source of configuration values.
ProcessInfoConfigurationSource
ProcessInfoConfigurationSource is a source that collects process info values ( NSProcessInfo.processInfo() )
PlistConfigurationSource
PlistConfigurationSource is a source that reads a plist file. The constructor is
init(name : String) where
name: is the file name excluding the ".plist" which will be looked up in the main bundle
ConfigurationManager
ConfigurationManager is the central class that collects different sources and i used to retrieve values. It implements the interface ConfigurationProvider and the administrative protocol:
public protocol ConfigurationAdministration {
func addSource(source : ConfigurationSource) throws -> Void
func configurationAdded(item: ConfigurationItem , source : ConfigurationSource) throws -> Void
func configurationChanged(item : ConfigurationItem) throws -> Void
}
and also adds generic functions.