Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
A module is nothing more than a folder that contains a ModuleConfig.cfc
file. The only requirement for that CFC is that it contains a method called configure()
. Modules can also contain models, interceptors, commands, and pretty much anything else you want to stick in them. As long as they contain a box.json
in their root, they are also a package, which means they are self-describing, can install dependencies, and can be installed via the install
command.
CommandBox has two default modules
directories.
~/.CommandBox/cfml/system/modules
~/.CommandBox/cfml/modules
The first is for system modules so you shouldn't need to touch it. All the built-in commands are in those modules, so feel free to check out how they work. All user-installed modules are in the second folder. The cfml
folder is the "root" of the CommandBox installation, so there will also be a box.json
created in that folder once you start installing modules via the install
command.
The first way to create a module is to manually create a folder in the ~/.CommandBox/cfml/modules
directory and place your ModuleConfig.cfc
inside of it. This is the process described in the guide on creating your first module.
If you have a package of type commandbox-modules
locally, in a Git repo, or in Forgebox, you can install from any working directory. When CommandBox sees the package type, it will change the installation directory to be the user modules folder.
CommandBox is written in CFML, which puts any CFML developer in a position to easily build upon it for their own needs. CommandBox is not only a CLI tool and collection of pre-built commands, but also an extensible framework for building your own CFML-based automations. The core is modular such that anyone can extend it themselves or via the help of community modules hosted on ForgeBox, or any other installation endpoint.
The unit of re-use in CommandBox is the module. A module is nothing more than a folder containing some settings and code that follows a convention. Modules mean that you can develop your own additions and customizations to the tool without needing to modify the core code.
Read about Developing Modules here.
CommandBox's expressive CLI gets its power from commands. You can create your own commands by packaging simple CFCs inside of modules. This is a powerful way to create custom, reusable CFML scripts that interact with the user, automate hard tasks, and can be shared with other developers. Commands are better than stand alone CFML files because they have very well-defined parameters, validation, and WireBox DI.
Read about Developing Commands here.
Customizing the internals of CommandBox is achieved via an event model known as interceptors. What this means is that at pre-defined points in the lifecycle of the shell, command execution, or web server starting, the CLI broadcasts events that you can listen to. This lets you provide custom error handling, special server handling, or modify command output. Interceptors are packaged up in modules and can be combined with custom commands and config settings for fully-configurable shell add-ons.
Read about Developing Interceptors here.
The developerMode
setting reloads shell before each command to help testing changes to CommandBox core or modules.
It will prevent you from needing to use the reload
command, but it will cause a delay before each command. Don't forget to turn this back off when you're done.
The configure()
method will be run before a module is loaded.
The following variables will be created for you in the variables scope of the ModuleConfig.cfc
.
shell
- The shell object (kind of the core CommandBox controller)
moduleMapping
- The component mapping to the module
modulePath
- The physical path to the module
logBox
- The LogBox instance
log
- A named logger, ready to log!
wirebox
- Your friendly neighborhood DI engine
binder
- The WireBox binder, handy for mapping models manually
The configure()
method does not accept or return any data. Instead, the config CFC itself represents the data for configuring the module with data structures in the variables scope. It's the job of the configure method to ensure that these data structures are created and populated.
There is no required data, but here is the list of optional data you can set:
settings
- A struct of custom module settings. These defaults can be overridden when the module is loaded with the CommandBox user config.
interceptors
- An array of declared interceptor structures that should be loaded in the entire application. Each interceptor structure contains class
, and optional properties
.
interceptorSettings
- A structure of settings for interceptor interactivity which includes the sub-key customInterceptionPoints
, a list of custom interception points to add to the application wide interceptor service
Here is an example configure()
method. Note these data structures are placed in the variables scope.
The unit of re-use in CommandBox is the module. A module is a folder containing some settings and code that follows a convention. Modules allow you you to develop your own additions and customizations to the tool without needing to modify the core code. If you have worked with the ColdBox MVC framework, CommandBox modules are very similar to ColdBox modules. If not, don't worry, they're very simple to learn.
A module is a folder that minimally contains a ModuleConfig.cfc
file inside it. User modules are stored in the ~/.CommandBox/cfml/modules/
directory on your hard drive. Let's navigate to that folder and create a new sub directory called test
. Now inside of our new folder, create a new file called ModuleConfig.cfc
and place the following in it.
~/.CommandBox/cfml/modules/test/ModuleConfig.cfc
This module runs the upgrade
command every time the CLI starts up to check and see if you have the latest version. Pretty cool, huh?
Let's take it a step further. Since this upgrade check could get annoying, let's only run it if we're starting the shell in interactive mode (with the blinking cursor awaiting input). That way, one-off commands from the OS shell that exit right away won't trigger the update check. Our onCLIStart()
method gets an argument called intercept
data that has a flag for this.
One final improvement. Create a module setting called checkForUpdates
which defaults to true
. Users can set it to false
to disable the update check. Here is the final version of the ModuleConfig.cfc
:
Now run the following command to override our module's default setting and turn off the update check.
Reload the shell and you'll see the update check is gone.
Now that you've written your first module, you can share it with the world. The following commands run from the root folder of your module will turn it into a package:
Drop the contents of your test
folder in a Github repo and now anyone can install it in their own CommandBox installation with the install
command using the Git endpoint.
The defaults for a module is stored in a settings
struct in the ModuleConfig.cfc
for that module. However, users can override those settings without needing to touch the core module code. This is made possible by special namespace in the CommandBox config settings called modules
.
Consider the following module settings:
/modules/TestModule/ModuleConfig.cfc
The following config set
commands will create config settings that override those. The pattern is modules.moduleName.settingName
.
When a module is loaded, the config settings (that exist) for that module are also loaded and overwrite the defaults from ModuleConfig.cfc
. You can easily see what settings are set for our TestModule
like so:
Every module follows a sequence of steps when it is loaded and unloaded. Modules are automatically loaded for you when CommandBox starts up, but here are some ways for a module to affect how it loads.
There are two life-cycle callback events you can declare in your ModuleConfig.cfc
:
onLoad()
- Called when the module is loaded and activated
onUnLoad()
- Called when the module is unloaded from memory
This gives you great hooks for you to do bootup and shutdown procedures for this specific module.
The ModuleConfig.cfc
object itself is an interceptor so you can declare all of the CLI's interception points in the configuration object and they will be registered as interceptors.
The configuration for a module is contained within in the ModuleConfig.cfc
that lives in the root folder. Here's an overview of the options for configuring your module.
CommandBox allows you to link a module directly into the CommandBox core so that you can use the module as though it's installed but without having two copies of the code. To do this, cd
to the root of the module you want to link into CommandBox.
This will create a symlink in the core CommandBox modules directory to your package and reload the shell for you so you can immediately test it out. Any further code changes you make can be tested by running the reload
command again.
When you are done testing, you can remove the link by running the package unlink
command from the root of the module you've been developing on.
This will remove the symlink for you.
What makes modules so easy to use is the convention-over-configuration approach that lets you use expected file and folder names instead of manually configuring things. Here are some of the high-level conventions that modules use.
The settings for a module is stored in the ModuleConfig.cfc
file in the root of the module. This CFC is automatically created and its settings extracted when the module is loaded. It has lifecycle methods like onLoad()
and onUnLoad()
that will be called automatically if they exist. This CFC will also be loaded up as an interceptor, so any method names that match interception points will be registered. .
CommandBox ships with WireBox, which is the glue that holds everything together. Your custom modules can contain as many models as they like. Place your CFCs (beans, services, etc) in the models
folder and WireBox will automatically map them as modelName@moduleName
so they can easily be injected into any other model, interceptor, or command CFC.
One of the primary purposes of modules is to serve as a delivery mechanism for custom commands. All command CFCs should be placed in the commands
folder inside your module. Any CFCs there will automatically be registered when the module is loaded, and will be available in the help
command, as well as tab-completion, etc. To create namespace commands, create extra sub folders inside the commands
folder that match the name of the namespace.
Wait, what-- modules can have their own modules?? Yep, we call that "module inception" and it's one of the most powerful features of modular development. If your module depends on another CommandBox module, then specify that dependency in your module's box.json
and it will automatically be installed for you inside your modules
folder.
You can control how CommandBox loads your module by setting optional settings in the this
scope.
this.autoMapModels
- Will automatically map all model objects under the models folder in WireBox using @modulename
as part of the alias.
this.modelNamespace
- The name of the namespace to use when registering models in WireBox. Defaults to name of the module.
this.cfmapping
- The CF mapping that will be registered for you that points to the root of the module. Defaults to name of the module.
this.disabled
- You can manually disable a module from loading and registering.
this.dependencies
- An array of dependent module names. All dependencies will be registered and activated FIRST before the module declaring them
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
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
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:
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:
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.
Commands also have a variables.wirebox
variable as well as their own getInstance()
method which proxies to WireBox to get objects.
A command can go by more than one name. For instance, the dir
command can also be called as ls
, ll
, or directory
. Set a comma-delimited list of aliases in your component declaration like so:
Command aliases don't even have to be in the same namespace. For instance, the server start
command has an alias of just start
. This can be very powerful as you can install a command that "overwrites" a built-in command by aliasing itself as that command.
With power comes responsibility though. Make sure you don't distribute a command that accidentally stomps all over another command. Unless your command name is decidedly unique, it's probably best for most custom commands to use a namespace to avoid conflicts.
Any interceptor CFCs packaged inside a module should go in an interceptors
folder. Interceptors are not loaded automatically though. You'll need to reference the component path in your ModuleConfig.cfc
. The path that points to the interceptors folder can be resolved in your config file as #moduleMapping#.interceptors.MyInterceptorName
.