D language utility library and modular application framework
The main package contains only two modules;
dxx.packageVersion
which is generated by the build and not stored in the
repo, and dxx.constants
which contains globally defined
platform variables.
The rest of the library is broken into subpackages.
The packages may be built using dale:
dub run dale -- build
Run the Example:
dub run dale -- runexample
Run the unittest:
dub run dale -- test
Once dale
is built, you can run it directly
(unless dale.d has been updated):
.dub/build/shi_sha/dale.exe upgrade
Or, just run the project:
dub run --config=shi_sha -- test
This builds on the utility library (see below) to provide a structured application framework.
Application access to the configuration variables
defined in the core
package.
The values are of type Variant
are taken from:
LocalConfig
properties.LocalInjector
properties- finally, a global array of default values.
This allows for types and parameters to be auto-registered with the injection container.
The mixin template registerComponent
is used
to register a type that inherits from
RuntimeComponents
.
The RuntimeComponents
class takes a variadic
parameter list. This list is parsed by the Injection
wrapper, and classes and parameters registered in
the container.
The principle is that applications, rather than register classes directly with the injector, instead provide a method in the runtime component that provides an instance of the type. Then, another application can extend the component type, and override the method to provide a different implementation of the registered type.
This is the default runtime component for generic applications.
The interface PlatformComponents
describes the
types that are available in this component.
The class PlatformRuntime
extends the default
runtime component class, and takes the same parameter
list.
Classes in the client may extend PlatformRuntime
to override the defaults, or to add their own
types.
A PlatformJob
interface and PlatformJobBase
abstract class are provided by the platform module.
Important types registered by this component are:
WorkflowRunner
PluginLoader
A notifying job runner with status events.
Notifying workflow model and job type. Clients create workflow elements, and these are instantiated and added to a workflow object.
The workflow object may be loaded from a definitions at compile time, or at runtime.
Workflow elements take configuration settings and would use the injector to access local parameters.
The workflow can then be attached to a WorkflowJob
wich is created by the WorkflowRunner
.
Plugins are dynamic modules with a class activator.
A plugin application may create a
class extending PluginDefault
, and override
the activation methods.
Plugins may be loaded into an application using
the class PluginLoader
.
The plugin broadcasts lifecyle notifications, as does the plugin loader.
Stores key / value pairs defined during compilation as runtime constants, so that we can compare versions at runtime, in a dynamic library for example.
There are two classes; Constants
which contains the
enums and aliases which define each constant, and
RTConstants
which contains constant variable definitions
for each constant.
The local application configuration is available at runtime in a static __gshared
array of Variant[string]
.
The values are loaded from the file dxx.ini
file at compilation time, and are assumed to remain constant
during the application lifecycle.
The ini data is defined in the module as DXXConfig
and an application can override this, or merge
additional values.
This array is used to configure the
application components, and is exposed via the class
LocalConfig
.
The injection tooling is a wrapper around aedi.
The library provides an application local injection container may be created by a client. This is initialised using template parameters.
The values passed in to the template are either component classes, which are scanned by the injection container, or property tuples, which are used to declare properties accessible to the application.
The property values are loaded into the injector when the container is initialised.
The property values can come from local configuration files, environment variables, and command line parameters.
TODO we want to reload the injection container transparently without breaking things. Or, more breaking them. Breaking them morely. More so.
A simple notification framework. Notifications may be synchronous or asynchronous.
Listeners implement the NotificationListener
interface.
A user-defined struct may be passed as an argument to a notification event, which is passed on to all listeners.
Clients may extend the NotificationListenerBase
which handles re-casting the notification parameter.
A simple string externalisation feature, allowing key-based translation files. The language is specified at compile time.
A notifying logger that also uses the injector.
We can hot-load dynamic libraries from files, using the reloaded library.
Our module loader takes care of version-checking of the dxx library at runtime.
This is because we want to build in a notification callback system for our application plugin framework.
The callbacks rely on matching structs defined in the core library, and these may vary between library versions.