From 649f25b2652b8089cf7657ab1781d317681ffeff Mon Sep 17 00:00:00 2001 From: Sambo Chea Date: Wed, 24 Mar 2021 16:39:10 +0700 Subject: [PATCH] - Add mutable configuration provider - Split tests file - Fixed and nullable functions - Able to set or remove config from system config --- .env | 2 +- CHANGELOG.md | 8 ++- README.md | 76 ++++++++++++++++++++++++-- example/example.dart | 20 +++++-- lib/configurable.dart | 8 +++ lib/configuration_provider.dart | 7 +++ lib/dotenv/dotenv_parser.dart | 10 +++- lib/dotenv_configuration_provider.dart | 6 ++ lib/mutable_configurable_provider.dart | 19 +++++++ lib/simple_configuration_provider.dart | 29 +++++++++- lib/system_config.dart | 32 +++++++++-- pubspec.yaml | 2 +- test/config_test.dart | 22 +++----- test/dotenv_test.dart | 2 +- test/system_dotenv_test.dart | 18 ++++++ 15 files changed, 225 insertions(+), 36 deletions(-) create mode 100644 lib/mutable_configurable_provider.dart create mode 100644 test/system_dotenv_test.dart diff --git a/.env b/.env index b36b49a..952098a 100644 --- a/.env +++ b/.env @@ -1,2 +1,2 @@ -app.name = 'CUBETIQ Solution' +app.title = 'CUBETIQ Solution' export NAME='Sambo Chea' \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 48868e0..0d2e6a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,4 +9,10 @@ - Fixed dart test in dev deps ## 1.0.2 -- Add support null-safety \ No newline at end of file +- Add support null-safety + +## 1.0.3 +- Add mutable configuration provider +- Split tests file +- Fixed and nullable functions +- Able to set or remove config from system config \ No newline at end of file diff --git a/README.md b/README.md index a64d907..ffe2ca8 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,16 @@ # Dart Configurable Environment + - [x] Allow to get property from env file - [x] Cache property for runtime - [x] Dotenv file support (use DotenvConfigurationProvider) -- [x] Support functions (getConfig, getConfigOrNull, hasConfigKey) +- [x] Support functions (getConfig, getConfigOrNull, hasConfigKey, setConfig) - [x] Support nullsafety (dart 2.12.2+) +- [x] Custom configuration provider # Example + ```dart +import 'package:configurable/configurable.dart'; import 'package:configurable/dotenv_configuration_provider.dart'; import 'package:configurable/simple_configuration_provider.dart'; import 'package:configurable/system_config.dart'; @@ -15,22 +19,82 @@ void main() { var key = 'app.name'; var value = 'CUBETIQ Solution'; - // in-memory provider (built-in) + /// in-memory provider (built-in) var simpleProvider = SimpleConfigurationProvider(); SystemConfig.setProvider(simpleProvider); var result1 = SystemConfig.getOrNull(key, defaultValue: value); - // output: CUBETIQ Solution + + /// output: CUBETIQ Solution print(result1); - // dotenv provider (from file .env) + /// get config functions + getConfig('app.title', defaultValue: 'My App Title!'); + getConfigOrNull('app.null.ignore'); + + /// set config functions + /// support only mutable configuration provider + setConfig('my.app', 'Hello My App'); + + /// dotenv provider (from file .env) var dotenvProvider = DotenvConfigurationProvider(); SystemConfig.setProvider(dotenvProvider); var result2 = SystemConfig.getOrNull('HOME'); - // output: user's home directory + /// output: user's home directory print(result2); } ``` +# Implement custom configuration provider + +```dart +class MyCustomProvider implements ConfigurationProvider { + Map configDataSet = {}; + + @override + bool containsKey(String key) { + return configDataSet.containsKey(key); + } + + @override + String? getOrNull(String key, {String? defaultValue}) { + var value = configDataSet[key]; + + if (value == null) { + return defaultValue; + } + + return value; + } +} +``` + # Contributors -- Sambo Chea \ No newline at end of file + +- Sambo Chea + +# License + +```text +MIT License + +Copyright (c) 2021 CUBETIQ Solution + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` diff --git a/example/example.dart b/example/example.dart index 1ad7ed4..e47a1e2 100644 --- a/example/example.dart +++ b/example/example.dart @@ -1,3 +1,4 @@ +import 'package:configurable/configurable.dart'; import 'package:configurable/dotenv_configuration_provider.dart'; import 'package:configurable/simple_configuration_provider.dart'; import 'package:configurable/system_config.dart'; @@ -6,18 +7,27 @@ void main() { var key = 'app.name'; var value = 'CUBETIQ Solution'; - // in-memory provider (built-in) + /// in-memory provider (built-in) var simpleProvider = SimpleConfigurationProvider(); SystemConfig.setProvider(simpleProvider); var result1 = SystemConfig.getOrNull(key, defaultValue: value); - // output: CUBETIQ Solution + + /// output: CUBETIQ Solution print(result1); - // dotenv provider (from file .env) + /// get config functions + getConfig('app.title', defaultValue: 'My App Title!'); + getConfigOrNull('app.null.ignore'); + + /// set config functions + /// support only mutable configuration provider + setConfig('my.app', 'Hello My App'); + + /// dotenv provider (from file .env) var dotenvProvider = DotenvConfigurationProvider(); SystemConfig.setProvider(dotenvProvider); var result2 = SystemConfig.getOrNull('HOME'); - // output: user's home directory + /// output: user's home directory print(result2); -} \ No newline at end of file +} diff --git a/lib/configurable.dart b/lib/configurable.dart index 994641a..fece0d8 100644 --- a/lib/configurable.dart +++ b/lib/configurable.dart @@ -2,8 +2,16 @@ library configurable; import 'package:configurable/system_config.dart'; +/// get config or null from system config String? getConfigOrNull(String key, {String? defaultValue}) => SystemConfig.getOrNull(key, defaultValue: defaultValue); + +/// get config with non-null from system config String getConfig(String key, {String? defaultValue}) => SystemConfig.get(key, defaultValue: defaultValue); + +/// check has config key or not from system config bool hasConfigkey(String key) => SystemConfig.containsKey(key); + +/// set config into memory +void setConfig(String key, String? value) => SystemConfig.set(key, value); diff --git a/lib/configuration_provider.dart b/lib/configuration_provider.dart index 0682a76..8b3883f 100644 --- a/lib/configuration_provider.dart +++ b/lib/configuration_provider.dart @@ -1,4 +1,11 @@ +/// Configuration Provider +/// +/// @author sombochea +/// @since 1.0.0 abstract class ConfigurationProvider { + /// check has key or not in implemented classes bool containsKey(String key); + + /// get or null in implemented classes String? getOrNull(String key, {String? defaultValue}); } diff --git a/lib/dotenv/dotenv_parser.dart b/lib/dotenv/dotenv_parser.dart index 9f2d478..ac2d053 100644 --- a/lib/dotenv/dotenv_parser.dart +++ b/lib/dotenv/dotenv_parser.dart @@ -1,5 +1,9 @@ part of dotenv; +/// Dotenv Parser +/// +/// @author sombochea +/// @since 1.0.0 class DotenvParser { static const _singleQuot = "'"; static const _keyword = 'export'; @@ -8,9 +12,10 @@ class DotenvParser { static final _surroundQuotes = RegExp(r'''^(['"])(.*)\1$'''); static final _bashVar = RegExp(r'(?:\\)?(\$)(?:{)?([a-zA-Z_][\w]*)+(?:})?'); - // constructor for parser + /// constructor for parser const DotenvParser(); + /// Parse the env lines into map values and return it back Map parse(Iterable lines) { var out = {}; lines.forEach((line) { @@ -73,11 +78,14 @@ class DotenvParser { @visibleForTesting String swallow(String line) => line.replaceAll(_keyword, '').trim(); + /// Check key is valid or not bool _isValid(String s) => s.isNotEmpty && s.contains('='); + /// Has key in map values bool _has(Map map, String key) => map.containsKey(key) && map[key] != null; + /// Try to check on platform env String? _tryPlatformEnv(String key) { if (!_has(Platform.environment, key)) { return ''; diff --git a/lib/dotenv_configuration_provider.dart b/lib/dotenv_configuration_provider.dart index 40f6d24..47a892d 100644 --- a/lib/dotenv_configuration_provider.dart +++ b/lib/dotenv_configuration_provider.dart @@ -1,7 +1,12 @@ import 'package:configurable/configuration_provider.dart'; import 'package:configurable/dotenv/dotenv.dart' show env, load; +/// Dotenv Configuration Provider +/// +/// @author sombochea +/// @since 1.0.0 class DotenvConfigurationProvider implements ConfigurationProvider { + /// default constructor to load the platform environment DotenvConfigurationProvider() { load(); } @@ -15,6 +20,7 @@ class DotenvConfigurationProvider implements ConfigurationProvider { String? getOrNull(String key, {String? defaultValue}) { var value = env[key]; + /// if value is null, then return default value if (value == null) { return defaultValue; } diff --git a/lib/mutable_configurable_provider.dart b/lib/mutable_configurable_provider.dart new file mode 100644 index 0000000..b20b438 --- /dev/null +++ b/lib/mutable_configurable_provider.dart @@ -0,0 +1,19 @@ +import 'package:configurable/configuration_provider.dart'; + +/// Mutable Configuration Provider +/// +/// @author sombochea +/// @since 1.0.3 +abstract class MutableConfigurationProvider implements ConfigurationProvider { + /// Allow to set the value into config values + void set(String key, String? value); + + /// Allow to set all values into config values + void setAll(Map configs); + + /// Allow to remove the key from config values + void remove(String key); + + /// Allow to remove all values from config values + void removeAll(); +} \ No newline at end of file diff --git a/lib/simple_configuration_provider.dart b/lib/simple_configuration_provider.dart index 2a9104d..9c6fd69 100644 --- a/lib/simple_configuration_provider.dart +++ b/lib/simple_configuration_provider.dart @@ -1,12 +1,17 @@ -import 'package:configurable/configuration_provider.dart'; +import 'package:configurable/mutable_configurable_provider.dart'; -class SimpleConfigurationProvider implements ConfigurationProvider { +/// Simple Configuration Provider +/// +/// @author sombochea +/// @since 1.0.0 +class SimpleConfigurationProvider implements MutableConfigurationProvider { Map configs = {}; @override String? getOrNull(String key, {String? defaultValue}) { var value = configs[key]; + /// if value is null, then set the default value and return it back if (value == null) { configs[key] = defaultValue; return defaultValue; @@ -19,4 +24,24 @@ class SimpleConfigurationProvider implements ConfigurationProvider { bool containsKey(String key) { return configs.containsKey(key); } + + @override + void set(String key, String? value) { + configs[key] = value; + } + + @override + void remove(String key) { + configs.remove(key); + } + + @override + void removeAll() { + configs.clear(); + } + + @override + void setAll(Map configs) { + configs.addAll(configs); + } } diff --git a/lib/system_config.dart b/lib/system_config.dart index f9b2a23..0d7a934 100644 --- a/lib/system_config.dart +++ b/lib/system_config.dart @@ -1,19 +1,25 @@ import 'package:configurable/configuration_provider.dart'; +import 'package:configurable/mutable_configurable_provider.dart'; import 'package:configurable/simple_configuration_provider.dart'; +/// System Configuration Static Functions +/// +/// @author sombochea +/// @since 1.0.0 class SystemConfig { - static ConfigurationProvider? provider; + static ConfigurationProvider? _provider; + /// Set configuration provider static void setProvider(ConfigurationProvider provider) { - SystemConfig.provider = provider; + _provider = provider; } static ConfigurationProvider getProvider() { - if (provider == null) { + if (_provider == null) { setProvider(SimpleConfigurationProvider()); } - return provider!; + return _provider!; } static String get(String key, {String? defaultValue}) { @@ -25,4 +31,22 @@ class SystemConfig { } static bool containsKey(String key) => getProvider().containsKey(key); + + /// Convert to mutable configuration provider + static MutableConfigurationProvider _getMutableConfigurationProvider() { + if (_provider is MutableConfigurationProvider) { + return (_provider as MutableConfigurationProvider); + } else { + throw Exception( + 'Error cannot use mutable functions without mutable configuration provider!'); + } + } + + static void set(String key, String? value) { + _getMutableConfigurationProvider().set(key, value); + } + + static void setAll(Map values) { + _getMutableConfigurationProvider().setAll(values); + } } diff --git a/pubspec.yaml b/pubspec.yaml index b504b75..6c143f1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: configurable description: System Configuration and Dotenv Environment for Dart and Flutter -version: 1.0.2 +version: 1.0.3 homepage: https://github.com/CUBETIQ/system-config-dart repository: https://github.com/CUBETIQ/system-config-dart.git diff --git a/test/config_test.dart b/test/config_test.dart index b79cff1..6b066b7 100644 --- a/test/config_test.dart +++ b/test/config_test.dart @@ -1,5 +1,5 @@ import 'package:configurable/configurable.dart' show getConfigOrNull, hasConfigkey; -import 'package:configurable/dotenv_configuration_provider.dart'; +import 'package:configurable/simple_configuration_provider.dart'; import 'package:configurable/system_config.dart'; import 'package:test/test.dart'; @@ -7,29 +7,23 @@ void main() { test('get system config by key', () { var key = 'app.name'; var value = 'CUBETIQ'; + + // set simple provider + SystemConfig.setProvider(SimpleConfigurationProvider()); + var result = SystemConfig.getOrNull(key, defaultValue: value); expect(value, equals(result)); expect(value, equals(SystemConfig.getOrNull(key))); }); - test('get system config by key with dotenv provider', () { - var key = 'app.name'; - var value = 'CUBETIQ Solution'; - - // set dotenv provider - SystemConfig.setProvider(DotenvConfigurationProvider()); - - var result = SystemConfig.getOrNull(key); - - expect(value, equals(result)); - expect(value, equals(SystemConfig.getOrNull(key))); - }); - test('get config by key with function', () { var key = 'app.name'; var value = 'CUBETIQ Solution'; + // set simple provider + SystemConfig.setProvider(SimpleConfigurationProvider()); + var result = getConfigOrNull(key, defaultValue: value); expect(value, equals(result)); diff --git a/test/dotenv_test.dart b/test/dotenv_test.dart index 2f359b8..afc06c1 100644 --- a/test/dotenv_test.dart +++ b/test/dotenv_test.dart @@ -5,7 +5,7 @@ void main() { test('just call env', () { load(); - var result = env['app.name']; + var result = env['app.title']; expect('CUBETIQ Solution', equals(result)); }); diff --git a/test/system_dotenv_test.dart b/test/system_dotenv_test.dart new file mode 100644 index 0000000..80fa328 --- /dev/null +++ b/test/system_dotenv_test.dart @@ -0,0 +1,18 @@ +import 'package:configurable/dotenv_configuration_provider.dart'; +import 'package:configurable/system_config.dart'; +import 'package:test/test.dart'; + +void main() { + test('get system config by key with dotenv provider', () { + var key = 'app.title'; + var value = 'CUBETIQ Solution'; + + // set dotenv provider + SystemConfig.setProvider(DotenvConfigurationProvider()); + + var result = SystemConfig.getOrNull(key); + + expect(value, equals(result)); + expect(value, equals(SystemConfig.getOrNull(key))); + }); +}