WireBox allows you to create your own DSL object builders and then register them via your configuration binder. This allows you to create a namespace or override an internal namespace with your own object builder. By now we have seen our injection DSL and noticed that we have internal namespaces. With this feature we can alter it or create new ones so our annotations and injection DSLs can be customized to satisfaction. This is easily done in the following process
Create a CFC that implements wirebox.system.ioc.dsl.IDSLBuilder
Register your custom DSL builder in your configuration binder
Registering a Custom DSL
To register a custom namespace in WireBox, place the following configuration in the wirebox struct defined within the configure() method of your WireBox binder CFC. in a ColdBox app, this is /config/WireBox.cfc. Alternatively, you can use the mapDSL() call in the configure() method.
If you want to register a custom DSL namespace from a module, you can make the same call via the binder reference that is provided to your ModuleConfig.cfc.
Now I can use the ortus DSL Namespace in my mappings DSL and even my annotations, isn't that cool!
Dynamic Custom DSL Registration
Injectors allow you to register custom DSLs at runtime by using the registerDSL() method on any injector.
The scope interface can be found here: coldbox.system.ioc.dsl.IDSLBuilder.
Please note that you DO NOT need to add the implements to your code. We actually highly suggest you don't. There are many issues with interfaces yet in multiple CFML engines. So we do runtime checks for it, instead at compile time.
/** * Copyright Since 2005 ColdBox Framework by Luis Majano and Ortus Solutions, Corp * www.ortussolutions.com * --- * The main interface to produce WireBox namespace DSL Builders **/interface {/** * Configure the DSL Builder for operation and returns itself * * @injector The linked WireBox Injector * @injector.doc_generic coldbox.system.ioc.Injector * * @return coldbox.system.ioc.dsl.IDSLBuilder */functioninit( required injector );/** * Process an incoming DSL definition and produce an object with it * * @definition The injection dsl definition structure to process. Keys: name, dsl * @targetObject The target object we are building the DSL dependency for. If empty, means we are just requesting building
* @targetID The target ID we are building this dependency for * * @return coldbox.system.ioc.dsl.IDSLBuilder */functionprocess( required definition, targetObject, targetID );}
Your DSL Builder
Here is a sample DSL builder:
/** * Copyright Since 2005 ColdBox Framework by Luis Majano and Ortus Solutions, Corp * www.ortussolutions.com * --- * Process DSL functions via LogBox **/component accessors="true" {/** * Injector Reference */ property name="injector";/** * LogBox Reference */ property name="logBox";/** * Log Reference */ property name="log";/** * Configure the DSL Builder for operation and returns itself * * @injector The linked WireBox Injector * @injector.doc_generic coldbox.system.ioc.Injector * * @return coldbox.system.ioc.dsl.IDSLBuilder */functioninit( required injector ){variables.injector = arguments.injector;variables.logBox =variables.injector.getLogBox();variables.log =variables.injector.getLogBox().getLogger( this );returnthis; }/** * Process an incoming DSL definition and produce an object with it * * @definition The injection dsl definition structure to process. Keys: name, dsl * @targetObject The target object we are building the DSL dependency for. If empty, means we are just requesting building
* @targetID The target ID we are building this dependency for * * @return coldbox.system.ioc.dsl.IDSLBuilder */functionprocess( required definition, targetObject, targetID ){var thisType = arguments.definition.dsl;var thisTypeLen =listLen( thisType,":" );// DSL stagesswitch ( thisTypeLen ) {// logboxcase1: {returnvariables.logBox; }// logbox:root and logbox:loggercase2: {var thisLocationKey =getToken( thisType,2,":" );switch ( thisLocationKey ) {case"root": {returnvariables.logbox.getRootLogger(); }case"logger": {returnvariables.logbox.getLogger( arguments.definition.name ); } }break; }// Named Loggerscase3: {var thisLocationType =getToken( thisType,2,":" );var thisLocationKey =getToken( thisType,3,":" );// DSL Level 2 Stage Typesswitch ( thisLocationType ) {// Get a named Loggercase"logger": {// Check for {this} and targetobject existsif ( thisLocationKey eq "{this}"ANDstructKeyExists( arguments,"targetObject" ) ) {returnvariables.logBox.getLogger( arguments.targetObject ); }// Normal Logger injectionreturnvariables.logBox.getLogger( thisLocationKey );break; } }break; }// end level 3 main DSL } }}
/** * Copyright Since 2005 ColdBox Framework by Luis Majano and Ortus Solutions, Corp * www.ortussolutions.com * --- * The ORM WireBox DSL */component accessors="true" { property name="injector"; property name="log";/** * Constructor as per interface */ public any functioninit( required any injector ){variables.injector = arguments.injector;variables.log =arguments.injector.getLogBox().getLogger( this );returnthis; }/** * Process an incoming DSL definition and produce an object with it * * @definition The injection dsl definition structure to process. Keys: name, dsl * @targetObject The target object we are building the DSL dependency for. If empty, means we are just requesting building
* @targetID The target ID we are building this dependency for * * @return coldbox.system.ioc.dsl.IDSLBuilder */functionprocess( required definition, targetObject, targetID ){var DSLNamespace =listFirst( arguments.definition.dsl,":" );switch ( DSLNamespace ) {case"entityService": {returngetEntityServiceDSL( argumentCollection = arguments ); } } }/** * Get an EntityService Dependency */functiongetEntityServiceDSL( required definition, targetObject ){var entityName =getToken( arguments.definition.dsl,2,":" );// Do we have an entity name? If we do create virtual entity serviceif ( len( entityName ) ) {returnnewcborm.models.VirtualEntityService( entityName ); }// else return Base ORM Servicereturnnewcborm.models.BaseORMService(); }}
Registration
In your configuration binder you can then register the DSL component you created
This will register a new injection DSL namespace called ortus that maps to that instantiation component path.model.dsl.OrtusBuilder.
As you can see from the sample, creating your own DSL builder is fairly easy. The benefits of a custom DSL builder is that you can very easily create and extend the injection DSL language to your own benefit and if you are funky enough, override the behavior of the internal DSL Namespaces.