diff --git a/CHANGELOG.md b/CHANGELOG.md index b71183e5..ccc00a11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# 2.1.0 +* Add `loadStandaloneConfiguration`. + # 2.0.0 * Use Angel 2. diff --git a/lib/angel_configuration.dart b/lib/angel_configuration.dart index 62262c11..7bb9a17b 100644 --- a/lib/angel_configuration.dart +++ b/lib/angel_configuration.dart @@ -6,25 +6,26 @@ import 'package:file/file.dart'; import 'package:merge_map/merge_map.dart'; import 'package:yaml/yaml.dart'; -_loadYamlFile(Angel app, File yamlFile, Map env) async { +_loadYamlFile(Map map, File yamlFile, Map env, + void warn(String msg)) async { if (await yamlFile.exists()) { var config = loadYaml(await yamlFile.readAsString()); if (config is! Map) { - app.logger?.warning( - 'WARNING: The configuration at "${yamlFile.absolute.path}" is not a Map. Refusing to load it.'); + warn( + 'The configuration at "${yamlFile.absolute.path}" is not a Map. Refusing to load it.'); return; } Map out = {}; for (String key in config.keys) { - out[key] = _applyEnv(config[key], env ?? {}, app); + out[key] = _applyEnv(config[key], env ?? {}, warn); } - app.configuration.addAll(mergeMap( + map.addAll(mergeMap( [ - app.configuration, + map, out, ], acceptNull: true, @@ -32,28 +33,64 @@ _loadYamlFile(Angel app, File yamlFile, Map env) async { } } -_applyEnv(var v, Map env, Angel app) { +_applyEnv(var v, Map env, void warn(String msg)) { if (v is String) { if (v.startsWith(r'$') && v.length > 1) { var key = v.substring(1); if (env.containsKey(key)) return env[key]; else { - app.logger?.warning( + warn( 'Your configuration calls for loading the value of "$key" from the system environment, but it is not defined. Defaulting to `null`.'); return null; } } else return v; } else if (v is Iterable) { - return v.map((x) => _applyEnv(x, env ?? {}, app)).toList(); + return v.map((x) => _applyEnv(x, env ?? {}, warn)).toList(); } else if (v is Map) { return v.keys - .fold({}, (out, k) => out..[k] = _applyEnv(v[k], env ?? {}, app)); + .fold({}, (out, k) => out..[k] = _applyEnv(v[k], env ?? {}, warn)); } else return v; } +/// Loads [configuration], and returns a [Map]. +/// +/// You can override [onWarning]; otherwise, configuration errors will throw. +Future loadStandaloneConfiguration(FileSystem fileSystem, + {String directoryPath: "./config", + String overrideEnvironmentName, + String envPath, + void onWarning(String message)}) async { + Directory sourceDirectory = fileSystem.directory(directoryPath); + var env = dotenv.env; + var envFile = sourceDirectory.childFile(envPath ?? '.env'); + + if (await envFile.exists()) { + dotenv.load(envFile.absolute.uri.toFilePath()); + } + + String environmentName = env['ANGEL_ENV'] ?? 'development'; + + if (overrideEnvironmentName != null) { + environmentName = overrideEnvironmentName; + } + + onWarning ??= (String message) => throw new StateError(message); + var out = {}; + + var defaultYaml = sourceDirectory.childFile('default.yaml'); + await _loadYamlFile(out, defaultYaml, env, onWarning); + + String configFilePath = "$environmentName.yaml"; + var configFile = sourceDirectory.childFile(configFilePath); + + await _loadYamlFile(out, configFile, env, onWarning); + + return out; +} + /// Dynamically loads application configuration from configuration files. /// /// You can modify which [directoryPath] to search in, or explicitly @@ -84,12 +121,16 @@ AngelConfigurer configuration(FileSystem fileSystem, environmentName = overrideEnvironmentName; } + void warn(String message) { + app.logger?.warning('WARNING: $message'); + } + var defaultYaml = sourceDirectory.childFile('default.yaml'); - await _loadYamlFile(app, defaultYaml, env); + await _loadYamlFile(app.configuration, defaultYaml, env, warn); String configFilePath = "$environmentName.yaml"; var configFile = sourceDirectory.childFile(configFilePath); - await _loadYamlFile(app, configFile, env); + await _loadYamlFile(app.configuration, configFile, env, warn); }; } diff --git a/pubspec.yaml b/pubspec.yaml index 1dc22f7c..a3b01721 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: angel_configuration description: Automatic YAML configuration loader for Angel. -version: 2.0.0 +version: 2.1.0 author: Tobe O homepage: https://github.com/angel-dart/angel_configuration environment: @@ -12,4 +12,5 @@ dependencies: merge_map: ^1.0.0 yaml: ^2.0.0 dev_dependencies: + io: ^0.3.2 test: ^1.0.0 diff --git a/test/all_test.dart b/test/all_test.dart index 62f70bc5..2d10d0b9 100644 --- a/test/all_test.dart +++ b/test/all_test.dart @@ -1,6 +1,7 @@ import 'package:angel_framework/angel_framework.dart'; import 'package:angel_configuration/angel_configuration.dart'; import 'package:file/local.dart'; +import 'package:io/ansi.dart'; import 'package:test/test.dart'; main() async { @@ -13,6 +14,26 @@ main() async { directoryPath: './test/config', )); + test('standalone', () async { + var config = await loadStandaloneConfiguration( + fileSystem, + directoryPath: './test/config', + onWarning: (msg) { + print(yellow.wrap('STANDALONE WARNING: $msg')); + }, + ); + print('Standalone: $config'); + expect(config, { + "angel": {"framework": "cool"}, + "must_be_null": null, + "artist": "Timberlake", + "merge": {"map": true, "hello": "world"}, + "set_via": "default", + "hello": "world", + "foo": {"version": "bar"} + }); + }); + test('can load based on ANGEL_ENV', () async { expect(app.configuration['hello'], equals('world')); expect(app.configuration['foo']['version'], equals('bar'));