Using Wirebox in Tasks

Hi,

According to the documentation, “All CFCs including tasks are created and wired via WireBox”. I have installed the “cbmongodb” module in the same directory the main task resides (via “box install cbmongodb” command). The module got installed in “/www/mytasks/modules/cbmongodb/”). However, seems like Wirebox is scanning only the main CommandBox folder ({local user’s dir}/commandbox/system in my case). When trying to inject one of the module models: FileEntity in the task cfc (via either property name=‘FileEntity’ inject=‘FileEntity@cbmongodb’ or wirebox.getInstance(“FileEntity@cbmongodb”), one of the following errors is thrown:

The DSL Definition […] did not produce any resulting dependency

The instance could not be located in any declared scan location(s) (/commandbox/system) or full path location

Is there any additional configuration required for Wirebox to scan and resolve dependencies located in directories outside the main CommandBox installation folder?

Since cbmongodb is a Coldbox module, it would have to be activated as a module by the framework. The DSL mapping and the Wirebox scanning takes place upon module activation.

If you want to use ActiveEntity/FileEntity inheritance, you’ll also need to add a mapping in Application.cfc for /cbmongodb

HTH,

Jon

Hi Jon,

Thank you for your reply. So, if I get it correctly, in order to use cbmongodb, I will also need to install Coldbox? I’m trying to use it within a CommandBox task:

box task run mytask

mytask.cfc is located in /www/mytasks/

In my tests, I’ve also created a simple cfc under /www/mytasks/models/test.cfc and tried to inject it in the task property name=“test” inject=“models.test”; - still no luck. Only using the full path “www.mytasks.models.test” makes the Wirebox injection possible.
I’ve also added Application.cfc with this.mappings["/models"]="/www/mytasks/models/"; - no luck either, still receiving error in the console:

The DSL Definition {REF={null}, REQUIRED={true}, ARGNAME={}, DSL={id:models.test}, JAVACAST={null}, NAME={test}, TYPE={any}, VALUE={null}, SCOPE={variables}} did not produce any resulting dependency

I’m guessing I need to somehow tell Wirebox to include the current folder in its scanned locations, just not sure how should it be accomplished.

Thanks!

Yes, correct. It is supported only as a ColdBox module, which means it would need the framework. You could certainly duplicate the functionality in the ModuleConfig.cfc file and add the modules/cbmongodb/models directory to wirebox as a scan location with a custom DSL suffix ( @cbmongodb ). It’s not tested or planned to be, though, outside of ColdBox.

Thanks again for your response. Seems to me that task runners do not invoke Application.cfc, so my guess is that I won’t be able to use Coldbox in tasks.
Is there a way to use Wirebox for local sub-folders in task runners? I can always use CreateObject(), but wouldn’t it be cool to be able to use Wirebox? :wink:

Hi guys, hopefully I can help clear up some of the confusion here. I think you two may have been on different pages in regards to what was being asked and what’s possible.

So firstly, yes CommandBox does use WireBox and your tasks have access to use WireBox.
However, it’s important to note that your task runners run inside the CommandBox CLI which is a separate process from any servers you may have running. Tasks (or the entirely of CommandBox) don’t really pay any attention to any of the other files in the directory, nor do they load up any modules in the current directory, nor are any frameworks loaded, nor do any Application.cfc files of your own get executed.
The CLI process is its own thing that starts its own WireBox instance. So when you run a task, the CFC is created in a (relative) vacuum.

Now, that said, CommandBox is module-aware. It has its own module service and it can load generic Box modules that have a ModuleConfig.cfc and it uses the same conventions for interceptors, and models as well as some additional conventions for commands. CommandBox has its own modules folder that it loads modules out of and it’s located inside your user home where CommandBox installs to. There are many modules that CommandBox can load and use. To install a module into CommandBox, use the –system flag.

install semanticVersion --system

You can even set an external folder of modules for CommandBox to load. CommandBox’s moduleService also supports the same basic conventions of ColdBox that allow you to override a module’s settings in your MVC or CLI configuration.

So, this means you can install the mongodb module INTO CommandBox and it will try to load it. But here’s the catch-- this particular module won’t work in CommandBox and the reason is it references some ColdBox specific things in its onLoad() function which aren’t going going to exist in CommandBox. (Namely the ColdBox “controller”) So if you were to install the module using the --system flag, you’d get an error message that the module failed to load.

So, the good news is this can be fixed, but Jon would need to refactor some of his code to make it more generic and not depend on ColdBox.

  1. Change how ModuleService is accessed. Right now ColdBox and CommandBox have slightly different injection DSLs for the module service, so a check might be required. Though honestly, I think the usage of the module service in this case is questionable anyway. Javaloader should always be loaded regardless because it’s listed in this.dependencies.
  2. Stop manually overriding settings. CommandBox and ColdBox both allow a built in way to override a modules settings that doesn’t require the module to do anything manually. In fact, all that parse parent settings stuff could go away and move the module’s settings into the proper “settings” struct. That parse parent settings was a bad habit we developed back before ColdBox had conventions in place to override settings from the config automatically. (CommandBox has done this since the first release that supported modules)
  3. That’s actually pretty much it…
    So in the mean time, there’s not a great deal you can do to use the ColdBox module. It’s not just a matter of adding scan locations into WireBox (which is possible inside CommandBox BTW). It’s important that the actual module receive a proper loading since it creates other things like CF mappings so super classes like cbmongodb.models.BaseDocumentService still resolve. This is actually the second time this has come up this week so I think it’s a great time for us to start refactoring our modules to be a bit more generic. My personal take is that any module not specifically doing something MVC (with handlers and views, etc) should, in theory, be able to run in CommandBox as well. We just need to ensure all the plumbing is in place for modules to get what they need without having to “reach into” the framework at all.

Thanks!

~Brad

ColdBox/CommandBox Developer Advocate
Ortus Solutions, Corp

E-mail: brad@coldbox.org
ColdBox Platform: http://www.coldbox.org
Blog: http://www.codersrevolution.com

Hi Brad,

Thank you very much for your in-depth reply. The main takeaways for me are the following:

  1. Coldbox-dependent modules are incompatible with CommandBox
  2. CommandBox is module-aware, but uses its own Wirebox instance which requires specific DSL (slightly different from the Coldbox one)
  3. I still have no idea if there is any MongoDB CF wrapper available for CommandBox :slight_smile:

Regarding the 2nd point: do all modules need to be installed in the CommandBox main installation directory (using the --system flag), or can a module installed in a local folder (where -say- tasks reside) can be used too? If so, is there any additional configuration required in order to force Wirebox to scan an external location?

Regarding point No. 3, I’ll start a new thread.

Thanks!

> Coldbox-dependent modules are incompatible with CommandBox

That is correct. Any Box module which references CFCs or WireBox DLSs that are unique to the ColdBox framework will throw errors if they tried to load up inside of the CommandBox “framework”.

> CommandBox is module-aware, but uses its own Wirebox instance which requires specific DSL (slightly different from the Coldbox one)

Mostly-- WireBox still works the same way (and is actually a complete standalone DI solution outside of anything else). When WireBox is used inside of ColdBox, there are additional injection namespaces registered to allow you to request instances of services from within Coldbox. And the same is true of CommandBox. It also registers its own custom set of injection namespaces with WireBox to allow you to get instances of CommandBox services easily. The trick is that both ColdBox and CommandBox have a “ModuleService” but their injection DSLs differ so it’s not super easy to write a module that can work across both if that module needs to get access to the ModuleService.

In ColdBox, you inject the ModuleService like so:
property name=“ModuleService” inject=“coldbox:moduleService”;

And in CommandBox, it looks like this:
property name=“ModuleService” inject=“moduleService”;

Now that doesn’t mean that every injection DSL is different, just a few of them that overlap. I’ve also considered adding aliases into CommandBox’s WireBox namespaces so DLSs like “coldbox:moduleservice” resolve to the same thing as a sort of compatibility layer.

> I still have no idea if there is any MongoDB CF wrapper available for CommandBox :slight_smile:

No. The answer is “no”. :slight_smile: At least not anything specifically made to work with CommandBox. That said, any library that was nothing more than a stand-alone CFC should work just fine. Also, like we discussed, the existing module could be made to work, it would just need massaged a bit. To be honest, you’re on the cutting edge here as people are really just now starting to pick up things like CommandBox tasks for heavy duty CFML-from-the-CLI operations. Don’t expect there to be a lot already out there, but please do be a part of creating it!

> Regarding the 2nd point: do all modules need to be installed in the CommandBox main installation directory (using the --system flag), or can a module installed in a local folder (where -say- tasks reside) can be used too?

CommandBox can literally load modules from anywhere. Well, more specifically, the ModuleService can do that. I think I already mentioned earlier the external modules location which is an arbitrary folder on your hard drive you can specify of global modules to load. In fact, CommandBox actually has THREE modules folders is loads from out of the box. There’s /cfml/modules, /cfml/system/modules, and /cfml/system/modules_app.

Now, regarding your question of loading arbitrary modules from the current folder, yes that is possible but I haven’t written anything to do it out of the box yet, I think this would be very cool, but there’s a lot of questions we’d need to answer first. Luis and I have actually been discussing this very thing. Here is a ticket we recently put in:
https://ortussolutions.atlassian.net/browse/COMMANDBOX-669

In the meantime, you should be able to do exactly what you want with just a few lines of code. Let’s say you have a module sitting in a "local’ folder to the task that you’re in, here’s a quick of example of how you could load arbitrary modules into CommandBox at run time. Please note however, that these modules wouldn’t “unload” by default and would stay in memory if the CLI stays open as part of the interactive shell. This would be fairly trivial to make a built-in part of tasks, but we just have to think through all the implications and decide if it’s what we want.

For this test, I created a simple task, whose code is below, and in the same folder as the task is a module_app folder that has a folder called “brad” inside of it which contains a simple module containing a “bradService.cfc” in the models folder (which will be automatically loaded when the module is registered.

component {
property name=‘moduleService’ inject=‘ModuleService’;

function run() {
// Generate a CF mapping that points to the module’s folder
var relativeModulePath = fileSystemUtil.makePathRelative( getCWD() & ‘modules_app’ );
moduleService.registerAndActivateModule( ‘brad’, relativeModulePath );

// Now use the module
print.line( getInstance( ‘bradService@brad’ ).doSomething() );

}

}

So in this way, you can arbitrarily load any module you want at run time inside of your task and everything from the module will instantly be available including WireBox mappings, module settings, interceptors, and custom CommandBox commands. The only two tricks really just creating a relative path and then asking the module service to load up the module. My thought is that it would be pretty easy for me to wrap up those two lines in a helper like this:

require( “modules_app/brad” );

Just brainstorming, but it would allow people to load arbitrary stuff that’s specific to a single project. The only real downside is that if you had a “brad” module in 5 different projects, and then you ran tasks from all 5 projects from inside the CommandBox interactive shell, you’d be loading 5 different “brad” modules which could start polluting the CommandBox framework.

Thanks!

~Brad

ColdBox/CommandBox Developer Advocate
Ortus Solutions, Corp

E-mail: brad@coldbox.org
ColdBox Platform: http://www.coldbox.org
Blog: http://www.codersrevolution.com