config_manager 0.4.2 copy "config_manager: ^0.4.2" to clipboard
config_manager: ^0.4.2 copied to clipboard

A JSON-based configuration manager with path-based access to nested values, singleton pattern, and mixin support.

Config Manager #

A Dart configuration manager package that loads JSON configuration files and provides path-based access to configuration values.

Features #

  • Load configuration from JSON files, raw JSON strings, or in-memory maps
  • Access nested values via an explicit key path (List<String>), e.g. ['database', 'host'] — a single top-level key is a one-element list, e.g. ['appName']; an empty list [] addresses the whole configuration map
  • List support with index access (e.g. ['features', '0'])
  • Sector-based configuration, so multiple independent configs can coexist
  • Check whether a sector is loaded (isLoaded) or whether a set of key paths is present (containsKeys)
  • Skip reloading an already-loaded sector with force: false
  • set rejects writing a non-null value for a key whose type is already known and differs from it, throwing ConfigTypeMismatchException
  • Optional type schema (setSchema/typeOf): declare expected types for a sector by hand, or let set and every load call build them up on their own from the very first write/load of a key — every subsequent set/loadFromJson/loadFromString/loadFromMap call is validated against it; a dynamic schema leaf opts a key out of checking entirely
  • Singleton-based configuration access
  • Mixin-based configuration extension for classes
  • getOrDefault<T>/getDurationSeconds fall back to a default configuration tree when a key isn't present in what's loaded
  • lookupPath/deepMergeMaps top-level utilities for path-walking and deep-merging plain Map<String, dynamic> trees

Usage #

import 'package:config_manager/config_manager.dart';

void main() {
  final config = ConfigManagerSingleton();
  config.loadFromJson('config.json');

  print(config.get(['appName']));
  print(config.get(['database', 'host']));
  print(config[['database', 'credentials', 'user']]);

  // get<T> is generic: T defaults to dynamic, but can be specified to get
  // a typed value back without an explicit cast at the call site. Since
  // JSON carries no type information, a mismatched T throws a TypeError
  // at call time rather than being caught at compile time.
  final appName = config.get<String>(['appName']);
  final port = config.get<int>(['database', 'port']);

  // set and every load call check a non-null value against whatever type
  // is already known for that key, and record it if not: loading
  // config.json above already taught the schema that database.port is an
  // int, so this succeeds...
  config.set(['database', 'port'], 5433); // ok: int -> int
  // ...and this would throw, without ever calling setSchema by hand:
  // config.set(['database', 'port'], 'not a port');

  // Optional: setSchema declares types up front, e.g. for a key you want
  // checked before it's ever written or loaded.
  config.setSchema({
    'database': {'host': String},
  });
  print(config.typeOf(['database', 'port'])); // int, learned from the load
  // config.loadFromJson('bad_config.json'); // throws if a type disagrees

  // A schema leaf of `dynamic` opts a key out of checking entirely, and
  // stays that way even after future writes/loads.
  config.setSchema({'appName': dynamic});
  config.set(['appName'], 42); // fine, no longer locked to String

  print(config.isLoaded());
  print(config.containsKeys([
    ['appName'],
    ['database', 'host'],
  ]));

  // force: false is a no-op when the sector is already loaded.
  config.loadFromJson('config.json', force: false);

  // A key path is always an explicit list of segments, so a literal "."
  // inside a key name is never ambiguous with nesting.
  config.set(['database.host'], 'literal-value');
  print(config.get(['database.host']));

  // getOrDefault falls back to a default tree for a partially loaded
  // config, e.g. one loaded via loadFromString/loadFromMap that omits a
  // key. getDurationSeconds does the same, reading the value as seconds
  // and converting it to a Duration.
  const defaults = {
    'timeouts': {'connect': 5},
  };
  final connectTimeout =
      config.getDurationSeconds(['timeouts', 'connect'], defaults);
  print(connectTimeout); // 0:00:05.000000, even if "timeouts" was never loaded

  // lookupPath/deepMergeMaps are the plain-Map building blocks behind
  // getOrDefault/getDurationSeconds; useful directly when working with
  // configuration trees outside of a loaded sector.
  print(lookupPath(defaults, ['timeouts', 'connect'])); // 5
  print(deepMergeMaps(defaults, {'timeouts': {'connect': 10}}));
}

Examples #

The example/ folder has one focused file per topic, each runnable on its own (e.g. dart run example/sectors_example.dart). example/main.dart runs all of them in sequence.

  • basic_usage.dart — loading a JSON file, reading values
  • lists_and_paths.dart — lists, casting, index access
  • writes_and_edge_cases.dart — writing nested values, missing keys
  • sectors_example.dart — independent per-sector configuration
  • is_loaded_and_contains_keys.dartisLoaded and containsKeys
  • force_reload.dartforce: false no-op reload
  • nested_vs_flat_keys.dart — nested paths vs. a literal dotted key
  • mixin_extension.dartConfigExtension mixin
  • custom_extension.dart — a custom IConfigExtension implementation

License #

MIT

0
likes
140
points
348
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A JSON-based configuration manager with path-based access to nested values, singleton pattern, and mixin support.

Repository (GitHub)

License

MIT (license)

More

Packages that depend on config_manager