Extending the FB5 framework with Custom Lexicons

In Fusebox 4.1, there are two ways to extend the framework: custom lexicons and plugins. The former method has undergone significant changes in Fusebox 5, the latter method is almost completely unchanged. This page covers the differences between Fusebox 4.1 and Fusebox 5 and the new features in lexicons offered by Fusebox 5.

Fusebox 5 supports per-circuit custom lexicons using XML namespace syntax. You declare a custom lexicon using the xmlns attribute in the <circuit> tag:

<circuit xmlns:prefix="path/to/verbs/">

As with Fusebox 4.1, the path is relative to the lexicon/ directory in your application root. Fusebox 5.1 allows the path to be absolute (or mapped) so that lexicons can be shared across multiple applications.

You invoke custom verbs using XML namespace syntax as follows:

<prefix:verb />

Custom verbs can have nested child verbs:

<prefix:verb>
<set name="foo" value="42"/>
</prefix:verb>

What's happening here?

At this point, its worth remembering what goes on when Fusebox 5 parses your circuit.xml file. Once the parser has understood the verb Fusebox has to validate the parameters and then add code to the parsed file that implements the verb concerned. The same is true with a custom verb.

Custom verbs now execute twice - just like custom tags in ColdFusion when you specify a closing slash (/) or an end tag - with an execution mode of "start" at the opening tag and "end" at the closing tag. A Fusebox 5 custom verb implementation file therefore typically looks like this:

<cfscript>
if (fb_.verbInfo.executionMode is "start") {
// validate fb_.verbInfo.attributes contents
// compile start tag CFML code
} else {
// compile end tag CFML code
}
</cfscript>

You add CFML code to the parsed file primarily by calling fb_appendLine() as follows:

fb_appendLine("<cfset foo = 42>");

This adds the CFML line to the parsed file, to be executed at runtime. It's important to remember that custom verbs execute at compile time, i.e., when a circuit.xml file is loaded and parsed, but the code that the verbs generate is executed at runtime, i.e., on all requests.

You can also specify custom attributes on <fuseaction> tags (and <circuit> tags) using the XML namespace notation:

<circuit xmlns:prefix="path/to/verbs/" prefix:security="enabled">
...
<fuseaction name="main" prefix:admin="no">

A plugin (or custom verb) can then call getCustomAttributes("prefix") on the fuseaction or circuit object to retrieve a struct containing any matching attributes. The assumption is that you will generally have custom verbs associated with the prefix / path. Using custom attributes like this is the recommended way to extend the grammar in Fusebox 5 and ensures that you will not conflict with any future built-in attributes.

You can also specify custom attributes on <class> declarations in fusebox.xml, and the <fusebox> tag itself, if you declare the XML namespace on the <fusebox> tag. Again, getCustomAttributes("prefix") can be called on the class object or Fusebox application object to retrieve any matching attributes.

Example verbs

In order to show how easy it is to create custom verbs here are a couple of simple but useful examples of verbs that implement standard ColdFusion Features that are not present in standard Fusebox 5.

<cf:abort>

<cfscript>
if (fb_.verbInfo.executionMode is "start") {
//
// no attributes so nothing to validate
// start mode:
fb_appendLine('<cfabort>');
} else {
//
// end mode - do nothing
}
</cfscript>

<cf:dump>

<cfscript>
if (fb_.verbInfo.executionMode is "start") {
//
// validate attributes:
// var is required:
if (not structKeyExists(fb_.verbInfo.attributes,"var")) {
fb_throw("fusebox.badGrammar.requiredAttributeMissing",
"Required attribute is missing",
"The attribute 'var' is required, for a 'dump' verb
in fuseaction #fb_.verbInfo.circuit#.#fb_.verbInfo.fuseaction#.");
}
// label is optional, create the text to generate - note the quoting technique:
if (structKeyExists(fb_.verbInfo.attributes,"label")) {
fb_.label = 'label="#fb_.verbInfo.attributes.label#"';
} else {
fb_.label = '';
}
//
// start mode:
fb_appendLine('<cfdump #fb_.label# var="#fb_.verbInfo.attributes.var#">');
} else {
//
// end mode - do nothing
}
</cfscript>

Writing Custom Verbs

Fusebox 5 verbs behave somewhat like custom tags. They have an execution mode (start, inactive, end) accessible through the fb_.verbInfo structure. That structure has the following members:

  • lexicon - the prefix for the namespace (in Fusebox 4.1 this was the lexicon name)
  • lexiconVerb - the actual verb being invoked (same as Fusebox 4.1)
  • attributes - the struct containing attributes provided in the verb invocation (same as Fusebox 4.1)
  • circuit - the alias of the circuit in which this verb is being invoked
  • fuseaction - the name of the fuseaction in which this verb is being invoked
  • action - the object that represents the current fuseaction (see fuseboxAction below)
  • hasChildren - true if there is are other verbs nested within this invocation
  • skipBody - if the verb sets this to true in start mode, no nested (child) verbs are executed (but this verb will still execute in end mode)
  • executionMode - "start" / "end" / "inactive"
  • parent - present if this verb is nested inside another verb (parent is a reference to the verbInfo structure of that enclosing verb, therefore parent.executionMode will be "inactive")

As indicated above, custom verbs are typically written in CFscript and always follow this form:

<cfscript>
if (fb_.verbInfo.executionMode is "start") {
// validate fb_.verbInfo.attributes contents
// compile start tag CFML code
} else {
// compile end tag CFML code
}
</cfscript>

A custom verb has access to a locally declared struct called fb_ but in reality the only useful content is in fb_.verbInfo. However, a custom verb can use the fb_ struct to store temporary local variables that exist for the execution of the verb (including its children). This allows a custom verb to set up some data in start mode that is accessible in end mode, directly in the fb_ structure. That data is not accessible to any children of that verb. A custom verb may also store additional data inside the fb_.verbInfo structure, which is accessible to any children of that verb, as the fb_.verbInfo.parent structure. In this way, custom verbs have both a private data area and a public data area, allowing quite a bit of flexibility.

In addition, custom verbs have access to the following methods for writing to the parsed file:

fb_appendLine(lineContent) - write the specified line to the parsed file, followed by a newline (the lineContent should not include a newline).

  • fb_appendSegment(segmentContent) - write the specified text to the parsed file (without a newline).
  • fb_appendNewline() - write just a newline to the parsed file.
  • fb_throw(type,message,detail) - throw the specified exception.

There are other fb_*() methods provided primarily for backward compatibility that deal with indentation but those are not officially supported in Fusebox 5 and may be deprecated in a future release.

To access the current circuit's object within a verb, use fb_.verbInfo.action.getCircuit(). To access the application object (and, hence, any fusebox.xml parameter values), use fb_.verbInfo.action.getCircuit().getApplication().

Built-In Verbs

It is worth noting that built-in verbs are also implemented as a custom lexicon. Built-in verbs exist in a reserved namespace called $fusebox. They are invoked without a prefix. In all other respects, they are just like user-defined custom lexicon verbs. Note that the verbs <do> and <fuseaction> are handled inside the framework and do not exist as external verbs (because they require framework 'magic' to accomplish their tasks: they are really compiler directives, rather than actual verbs).

For more information, see Built-In Verbs.

Extending Fusebox Verbs

A collection of Fusebox lexicons is made available within Fusebox.org's Essentials DownloadFusebox.org's Essentials Download. This contains custom Lexicons for Coldfusion, ColdSpring, and Reactor.

For more information, see Lexicon Essentials.

External Links