We've been hard at work developing some cool new features for CFML's CLI, Package Manager, REPL, and Embedded Server, CommandBox. Since there are so many major features, we're targeting this as a major release of the product, so welcome version 3.0.0. We want you to kick the tires and try out the newest features before our final release. Please put in tickets for any issues you run across, or post to our mailing list.
Installation
First things first. To install the latest 3.0.0 beta version of CommandBox, download the appropriate binary for your OS here:
http://integration.stg.ortussolutions.com/artifacts/ortussolutions/commandbox/3.0.0/
The first time you run box, it will install and replace any previous installation. If you want to go back to the previous version later, you'll need to remove the .CommandBox folder in your user home first.
Documentation
The GitBook-based docs have had major updates made to them. Prior to the final release of CommandBox 3.0.0, you can preview the bleeding edge of our docs here:
http://commandbox.ortusbooks.com/content/v/development/
Also, the bleeding edge API docs are available here as well:
http://integration.stg.ortussolutions.com/artifacts/ortussolutions/commandbox/be-apidocs/
Major Features
Here's a run down of the major features in CommandBox 3.0.0. To see the full list of ticket in this release, check out the preliminary release notes in JIRA.
Config Settings
CommandBox now has a JSON file of settings that can be used to configure any kind of behavior we want. We're still working on implementing features that actually use the settings, but the first will be the ability to set up a proxy server for your corporate network. Config settings give us a place to store ad-hoc settings like this as well as an API for retrieving them. What's great is the settings JSON file can store ANY information, including complex structs and arrays so feel free to use it for your own purposes as well. You can interact with config settings like so:
config set name=mySetting config set modules.myModule.mySetting=foo config set myArraySetting[1]="value" config set setting1=value1 setting2=value2 setting3=value3 config show settingName
Modules
This is perhaps the most radical thing we've done in CommandBox to date and it is huge. We've introduced modules (just like ColdBox) into the actual CLI itself. A module is a unit of code re-use that allows you to take a folder of code that follows a few simple conventions and drop it into a module-aware application for instant extension. This means that we've broken out all the internal commands into system modules for organization. What's more, you can write your own CommandBox modules that hook into the internal workings with interceptors, register their own custom commands, or help manage settings or servers. Modules can be placed on ForgeBox and installed by your friends in seconds to extend the core of CommandBox. The benefits here can't be understated. Check out the docs and go through the quick, easy steps to create your first CommandBox module.
Not only can modules have settings, but you can also override a module's default settings easily with config settings that follow a simple convention. For example, if a module named "foo" has a setting named "bar", you don't need to edit the module's code to change the setting. Simply run this command:
config set modules.foo.bar=value
Interceptors
CommandBox interceptors, like modules, work the same way that ColdBox interceptors do. They give you hooks that you can register to listen to events broadcast by the CommandBox core, or custom events of your own design that you announce. These are very powerful for being able to extend and modify how the core CLI works to build upon it. Interceptors are bundled inside modules so they install quickly and easily. Distribute them on ForgeBox as well. I've already created a simple example module that uses the onCLIStart interceptor to modify the ASCII art banner that appears when you start CommandBox.
Here's some of the core interception points:
- onCLIStart
- onCLIExit
- preCommand
- postCommand
- onServerStart
- onServerStop
- onException
- preInstall
- postInstall
Now you can write modules that check for upgrades on CommandBox startup, manipulate the output of commands, log exceptions, customize server startup, or audit what package you install the most!
Standardized Command Packaging
We've had the ability for custom commands for a while, but they were limited and didn't easily allow you to include additional CFC files with your commands. Now with the addition of modules, your custom commands can be package in a module right alongside settings, interceptors, or services. We also simplified the creation of custom commands so things like extending our BaseCommand class is optional thanks to WireBox's virtual inheritance. We hate boilerplate as much as you do!
There are already some cool custom commands popping up on ForgeBox. Check out this community addition for making http calls from the command line similar to curl.
install commandbox-http-command
Server.json
This feature has been a long-time coming. There are a lot of options you can set when starting a server, and portability has been hard for people wanting to distribute an app that needs to start with custom JVM args, rewrites, or a specific port. Now all server startup options can be set in your web root in a server.json file which will be used automatically the next time you run "start". You interact with these settings the same as package or config settings.
server set web.http.port=8080 server show web.http.port server start
Shortcut for Native OS Binaries
We've had the "run" command for a while now that allows you to run native binaries from the interactive shell, or from a CommandBox recipe. The output is returned which allows you to create mashups that pipe the output of OS commands directly into CommandBox commands. We took this a step further and borrowed from other CLIs out there so now the parser allows you to call native OS binaries by simply prefixing an exclamation mark (!) in front of the binary name. Now only are OS commands run in the current working directory, they are also executed via the shell for that machine which makes non-binaries and aliases like "ll" function.
!myApp.exe !git pull !dir
Shortcut for CFML Functions via REPL
This is a really neat feature that allows you to actually run CFML functions straight from the CommandBox CLI as commands. Just prefix the function name with a hash sign (#) and then type the function name with no parenthesis. Any parameters to the function can be passed (or piped) into the command like normal named or positional CLI command parameters.
#now > {ts '2016-01-19 16:14:23'} #hash mypass > A029D0DF84EB5549C641E04A9EF389E5 #reverse abc > cba
This really gets cool when you start piping the output of commands together to string together mixtures of CommandBox commands and CFML functions for fancy one-liners. Here's some string manipulation. The first one does some list manipulation. The second one outputs the lowercase package name.
#listGetAt www.foo.com 2 . | #ucase | #reverse > OOF package show name | #lcase > my package
But wait, there's more! You can even use struct and array functions. Their output is returned as JSON and automatically deserialized as input to the next command. Keep in mind that piped data gets passed in as the FIRST parameter to the next command. This outputs a nice list of all the top-level dependencies in your package.
package list --JSON | #structFind dependencies | #structKeyList > coldbox,docbox,testbox,cbmessagebox
Expressions in Command Parameters
Parameter values passed into a CommandBox command don't have to be static. Any part of the parameter which is enclosed in backticks (`) will be evaluated as a CommandBox expression. That means that the enclosed text will be first executed as though it were a separate command and the output will be substituted in its place.
echo "Your CommandBox version is `ver` and this app is called '`package show name`'!!" > Your CommandBox version is 3.0.0 and this app is called 'Brad's cool app'!
You can really go crazy with these mashups by combining CFML functions too. This example sets a property in a package's box.json that's equal to a nicely formatted date:
package set createdDate=`#now | #dateformat mm/dd/yyyy` > Set createdDate = 1/19/2016
Command DSL
We've really focused on doing CommandBox development now with the possibilities opened up with the addition of modules. One pain point of extending CommandBox was calling other commands since parameters needed to be escaped. We created a nice method-chaining DSL to help execute any other command from inside of your custom commands.
command( 'cp' ) .params( path='/my/path', newPath='/my/new/path' ) .run();
You can even nest the DSL to pipe output between commands:
command( "echo" ) .params( "hello#chr( 10 )#world" ) .pipe( command( "grep" ) .params( "lo" ) ) .run();
New WireBox Injection DSLs
In line with the previous item, we've made it yet easier to write custom modules that extend the functionality of CommandBox by adding new WireBox injection DSLs. Everything inside of CommandBox is created and autowired by WireBox. You can now ask WireBox to inject core services, module settings, or config settings.
property name='shell' inject='commandbox'; property name='ModuleService' inject='ModuleService'; property name='MyService' inject='MyService@MyModule'; property name='mySetting' inject='commandbox:moduleSettings:moduleName:mySetting'; property name='myConfigSetting' inject='commandbox:ConfigSettings:myConfigSetting'
Much Much More!
There's many more small improvements and bug fixes in our 3.0 release. Everything has been documented so please dig into our preliminary CommandBox 3.0.0 release notes if you want to see everything.
Add Your Comment