Skip to content

Commit

Permalink
#162 CBWIRE is unable to load wires located in an external module loc…
Browse files Browse the repository at this point in the history
…ation
  • Loading branch information
grantcopley committed Aug 21, 2024
1 parent f047d9a commit 4a09564
Show file tree
Hide file tree
Showing 10 changed files with 254 additions and 15 deletions.
39 changes: 36 additions & 3 deletions models/CBWIREController.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,8 @@ component singleton {

try {
// Attempt to create an instance of the component
return variables.wirebox.getInstance(local.fullComponentPath);
return variables.wirebox.getInstance(local.fullComponentPath)
._withPath( arguments.name );
} catch( Injector.InstanceNotFoundException e ) {
local.singleFileComponent = variables.singleFileComponentBuilder
.setInitialRender( true )
Expand All @@ -205,23 +206,55 @@ component singleton {
}
}

/**
* Returns the path to the modules folder.
*
* @module string | The name of the module.
*
* @return string
*/
function getModuleRootPath( module ) {
var moduleRegistry = moduleService.getModuleRegistry();

if ( moduleRegistry.keyExists( module ) ) {
return moduleRegistry[ module ].invocationPath & "." & module;
}

throw( type="ModuleNotFound", message = "CBWIRE cannot locate the module '" & arguments.module & "'.")
}

/**
* Returns the full dot notation path to a modules component.
*
* @path String | Name of the cbwire component.
* @module String | Name of the module to look for wire in.
*/
private function getModuleComponentPath( path, module ) {
function getModuleComponentPath( path, module ) {
var moduleConfig = moduleService.getModuleConfigCache();
if ( !moduleConfig.keyExists( module ) ) {
throw( type="ModuleNotFound", message = "CBWIRE cannot locate the module '" & arguments.module & "'.")
}

// if there is a dot in the path, then we are referencing a folder within a module otherwise use the default wire location.
var moduleRegistry = moduleService.getModuleRegistry();
return arguments.path contains "." ?

var result = arguments.path contains "." ?
moduleRegistry[ module ].invocationPath & "." & module & "." & arguments.path :
moduleRegistry[ module ].invocationPath & "." & module & "." & getWiresLocation() & "." & arguments.path;

return result;
}

/**
* Returns the path to the wires folder within a module path.
*
* @module string | The name of the module.
*
* @return string
*/
function getModuleWiresPath( module ) {
local.moduleRegistry = moduleService.getModuleRegistry();
return arguments
}

/**
Expand Down
55 changes: 46 additions & 9 deletions models/Component.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ component output="true" {
property name="_redirect";
property name="_redirectUsingNavigate";
property name="_isolate";
property name="_path";

/**
* Constructor
Expand Down Expand Up @@ -312,11 +313,12 @@ component output="true" {
}
// Instaniate this child component as a new component
local.instance = variables._CBWIREController.createInstance(argumentCollection=arguments)
._withParent( this )
._withEvent( variables._event )
._withParams( arguments.params, arguments.lazy )
._withKey( arguments.key )
._withLazy( arguments.lazy );
._withPath( arguments.name )
._withParent( this )
._withEvent( variables._event )
._withParams( arguments.params, arguments.lazy )
._withKey( arguments.key )
._withLazy( arguments.lazy );

// Check if lazy loading is enabled
if ( arguments.lazy ) {
Expand Down Expand Up @@ -574,10 +576,21 @@ component output="true" {
}

/**
* Passes the current event into our component.
* Passes the path of the component.
*
* @path string | The path of the component.
*
* @return Component
*/
function _withPath( path ) {
variables._path = arguments.path;
return this;
}

/**
* Passes the current event into our component.
*
* @return Component
*/
function _withEvent( event ) {
variables._event = arguments.event;
Expand Down Expand Up @@ -832,7 +845,7 @@ component output="true" {
local.normalizedPath &= ".cfm";
}
// Ensure the path starts with "/wires/" without duplicating it
if (left(local.normalizedPath, 6) != "wires/") {
if (!isModulePath() && left(local.normalizedPath, 6) != "wires/") {
local.normalizedPath = "wires/" & local.normalizedPath;
}
// Prepend a leading slash if not present
Expand Down Expand Up @@ -1398,7 +1411,21 @@ component output="true" {
* Returns the path to the view template file.
*/
function _getViewPath(){
return "wires." & _getComponentName();
if ( isModulePath() ) {
var moduleRoot = variables._CBWIREController.getModuleRootPath( _getModuleName() );
return moduleRoot & ".wires." & _getComponentName();
}

return "wires." & variables._path;
}

/**
* Returns the module name.
*
* @return string
*/
function _getModuleName() {
return variables._path contains "@" ? variables._path.listLast( "@" ) : "";
}

/**
Expand Down Expand Up @@ -1450,7 +1477,8 @@ component output="true" {
if ( variables._metaData.name contains "cbwire.models.tmp." ) {
return variables._metaData.name.replaceNoCase( "cbwire.models.tmp.", "", "one" );
}
return variables._metaData.name.replaceNoCase( "wires.", "", "one" );
// only returns the last part of the name seprate by dots
return variables._metaData.name.listLast( "." );
}

/**
Expand Down Expand Up @@ -1549,6 +1577,15 @@ component output="true" {
return local.outerElement.trim();
}

/**
* Returns true if the path contains a module.
*
* @return boolean
*/
function isModulePath() {
return variables._path contains "@";
}

/**
* Returns true if the cbvalidation module is installed.
*
Expand Down
4 changes: 3 additions & 1 deletion test-harness/config/Coldbox.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ component{
//Development Settings
reinitPassword = "",
handlersIndexAutoReload = true,
modulesExternalLocation = [],
modulesExternalLocation = [
"/modules_external"
],

//Implicit Events
defaultEvent = "main.index",
Expand Down
101 changes: 101 additions & 0 deletions test-harness/modules_external/ExternalModule/ModuleConfig.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/**
* Module Directives as public properties
*
* this.title = "Title of the module";
* this.author = "Author of the module";
* this.webURL = "Web URL for docs purposes";
* this.description = "Module description";
* this.version = "Module Version";
* this.viewParentLookup = (true) [boolean] (Optional) // If true, checks for views in the parent first, then it the module.If false, then modules first, then parent.
* this.layoutParentLookup = (true) [boolean] (Optional) // If true, checks for layouts in the parent first, then it the module.If false, then modules first, then parent.
* this.entryPoint = "" (Optional) // If set, this is the default event (ex:forgebox:manager.index) or default route (/forgebox) the framework will use to create an entry link to the module. Similar to a default event.
* this.cfmapping = "The CF mapping to create";
* this.modelNamespace = "The namespace to use for registered models, if blank it uses the name of the module."
* this.dependencies = "The array of dependencies for this module"
*
* structures to create for configuration
* - parentSettings : struct (will append and override parent)
* - settings : struct
* - interceptorSettings : struct of the following keys ATM
* - customInterceptionPoints : string list of custom interception points
* - interceptors : array
* - layoutSettings : struct (will allow to define a defaultLayout for the module)
* - wirebox : The wirebox DSL to load and use
*
* Available objects in variable scope
* - controller
* - appMapping (application mapping)
* - moduleMapping (include,cf path)
* - modulePath (absolute path)
* - log (A pre-configured logBox logger object for this object)
* - binder (The wirebox configuration binder)
* - wirebox (The wirebox injector)
*
* Required Methods
* - configure() : The method ColdBox calls to configure the module.
*
* Optional Methods
* - onLoad() : If found, it is fired once the module is fully loaded
* - onUnload() : If found, it is fired once the module is unloaded
**/
component {

// Module Properties
this.title = "ExternalModule";
this.author = "";
this.webURL = "";
this.description = "";
this.version = "1.0.0";
// If true, looks for views in the parent first, if not found, then in the module. Else vice-versa
this.viewParentLookup = true;
// If true, looks for layouts in the parent first, if not found, then in module. Else vice-versa
this.layoutParentLookup = true;
// Module Entry Point
this.entryPoint = "ExternalModule";
// Inherit Entry Point
this.inheritEntryPoint = false;
// Model Namespace
this.modelNamespace = "ExternalModule";
// CF Mapping
this.cfmapping = "";
// Auto-map models
this.autoMapModels = true;
// Module Dependencies
this.dependencies = [];

/**
* Configure the module
*/
function configure(){
// parent settings
parentSettings = {};

// module settings - stored in modules.name.settings
settings = {};

// Layout Settings
layoutSettings = { defaultLayout : "" };

// Custom Declared Points
interceptorSettings = { customInterceptionPoints : [] };

// Custom Declared Interceptors
interceptors = [];

// Binder Mappings
// binder.map("Alias").to("#moduleMapping#.models.MyService");
}

/**
* Fired when the module is registered and activated.
*/
function onLoad(){
}

/**
* Fired when the module is unregistered and unloaded
*/
function onUnload(){
}

}
13 changes: 13 additions & 0 deletions test-harness/modules_external/ExternalModule/config/Router.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* Module Router
* https://coldbox.ortusbooks.com/the-basics/routing/routing-dsl
*/
component{

function configure(){

route( "/", "Home.index" );

}

}
21 changes: 21 additions & 0 deletions test-harness/modules_external/ExternalModule/config/Scheduler.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* Module Task Scheduler
* https://coldbox.ortusbooks.com/digging-deeper/scheduled-tasks
*/
component {

function configure(){

/* task( "photoNumbers" )
.call( () => {
var random = getInstance( "PhotoService" ).getRandom();
writeDump( var="xxxxxxx> Photo numbers: #random#", output="console" );
return random;
} )
.every( 15, "seconds" )
.delay( 60, "seconds" )
.onEnvironment( "development" ); */

}

}
13 changes: 13 additions & 0 deletions test-harness/modules_external/ExternalModule/handlers/Home.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* The main module handler
*/
component{

/**
* Module EntryPoint
*/
function index( event, rc, prc ){
event.setView( "home/index" );
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<cfoutput>
<h1>Welcome to my cool module page!</h1>
</cfoutput>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<cfoutput>
<div>
<h1>External Module Loaded</h1>
</div>
</cfoutput>

<cfscript>
// @startWire
// @endWire
</cfscript>
9 changes: 7 additions & 2 deletions test-harness/tests/specs/CBWIRESpec.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ component extends="coldbox.system.testing.BaseTestCase" {
// and prepareMock() is a custom method to mock any dependencies, if necessary.
setup();
testComponent = getInstance("wires.TestComponent");
testComponent._withEvent( getRequestContext( ) );
testComponent
._withEvent( getRequestContext( ) )
._withPath( "wires.TestComponent" );
CBWIREController = getInstance( "CBWIREController@cbwire" );
prepareMock( testComponent );
});
Expand Down Expand Up @@ -1116,7 +1118,10 @@ component extends="coldbox.system.testing.BaseTestCase" {
expect( result ).toContain( "Nested module component using default wires location" );
} );


it( "can load components from an external modules folder", function() {
var result = cbwireController.wire( "should_load_external_modules@ExternalModule" );
expect( result ).toInclude( "External Module Loaded" );
} );
});

describe( "Preprocessors", function() {
Expand Down

0 comments on commit 4a09564

Please sign in to comment.