CommandBox is extensible via CFML by creating modules that contain command CFCs. This is a very easy and powerful way to create custom, reusable CFML scripts that interact with the user, automate hard tasks, and can be shared with other developers. Let's create a "Hello World" command.
To create our first command, we'll need a new module. A module can contain as many commands as you like. You can create a module by placing a folder in ~/.CommandBox/cfml/modules/
that contains a ModuleConfig.cfc
file. The minimum contents of your module config is:
modules/test/ModuleConfig.cfc
component {
function configure(){}
}
Now, create a commands
folder inside your module for your command to live in. Each CFC in this folder will be registered as a command. The only requirement for a command CFC is that is has a run()
method.
modules/test/commands/Hello.cfc
component {
function run(){
return 'Hello World!';
}
}
That's it! After creating your module, run the reload
command from the shell, and then the name of the new command is the same as the name of the CFC. In this case, you would run the command above like so:
hello
It would output Hello World!
to the console. Anything after hello
will be passed to your run()
function as parameters.
To create a two-part command like say hello
create CFCs that are nested in subfolders, for example: ~/.CommandBox/cfml/modules/test/commands/say/Hello.cfc
The contents of the Hello.cfc
would not change, but the namespace will match the folder name by convention. The namespaced command would be called like so:
say hello
There is no limit to how deeply you can nest your namespace folders. CommandBox's built in help and tab-completion will always work via conventions.
Note: The box.json file can have a key called "ignore" that allows you stipulate folders or files which should not be installed but can be kept in reserve for testing or private purposes. Any folder in your namespace that matches that pattern will not be copied during installation.
Commands are created and stored once for the duration that CommandBox is running. CFProperty injections are also aggressively cached on disk across restarts. If testing changes to a command in the interactive shell, use the reload
command (aliased as r
) to reload the shell. Your changes will immediately be available. Using the up arrow to access the shell's history can also be useful here.
All CFCs including commands are created and wired via WireBox, so dependency injection and AOP are available to them. This can be handy for commands to wrap services provided by models, or to access utilities and services inside CommandBox.
This command would inject CommandBox's ArtifactService to list out all the packages being stored.
component {
property name='artifactService' inject='artifactService';
function run(){
var results = artifactService.listArtifacts();
for( var package in results ) {
print.boldCyanLine( package );
}
}
}
Commands also have a variables.wirebox
variable as well as their own getInstance()
method which proxies to WireBox to get objects.
var results = getInstance( 'artifactService' ).listArtifacts();