Fusebox 4.1 Lexicons

I was the presenter at the Portland, OR CFUG last week, and the
topic was Fusebox 4.1.  One of the big points of interest was
custom lexicons, so I thought I'd share a couple examples here as well.

Fusebox
4.x is based around an XML grammar (composed of individual verbs, much
like CFML is composed of CF tags) for describing your application,
separate from the actual implementation of your application.  So
the XML documents the structure and program flow, but none of the
code.  Very elegant, and very helpful, particularly when you're
designing and managing large apps.  Custom lexicons allow you to
extend that XML grammar with application-specific functionality. 
By and large this isn't needed, but there are some places where the
built-in grammar is simply insufficient, and a custom lexicon can make
your life enormously easier.

The simplest example of a custom
verb is a 'write' verb that take a 'text' attribute and simply writes
it to the output buffer.  To many people, the inability to
natively do this in FB4 is surprising, but it quickly becomes apparent
that there is almost never a need to do it, except when you're hashing
out an application structure.  First, here's how the verb will be
used:

<beb.write text="hello, world!" />

The 'beb'
part indicates the lexicon the verb is in, and is specified when you
register your lexicon in fusebox.xml.  The 'write' part specifies
the specific verb to invoke, and then the 'text' attribute is just an
attribute passed to the verb, much like a custom tag.  Here's the
implementation:

<cfset fb_appendLine('<cfoutput>#fb_.verbInfo.attributes.text#</cfoutput>') />

So
what do we have?  We've got this 'fb_appendLine' function, we've
got this 'fb_.verbInfo' structure, and we've got some raw CFML encased
in a string.  But even if you don't understand the nitty gritty, the jist of what's happening should be pretty obvious.

A little
description of how FB4 works under the hood is in order.  The core
files take your XML documents and use them to generate execution plans
(parse files) in native CFML.  Then those execution plans are
actually used to service requests.  Because that translation
happens once (and not every request), FB4 is very efficient.  On
the flip side, integrating a custom verb is confusing, because your
verb is not running as part of a request, it's running as part of the
XML to CFML translation process.  And note that this is all
exactly the same for the PHP version too, just it's XML to PHP.

So
what does that mean to our custom verb?  It means we can't
actually output anything while we're being processed, instead we have
to add some CFML to the execution plan for later execution as part of
handline a request.  Oof.  However, the core files make it
really easy, just use the fb_appendLine function, and pass it a string
containing the CFML you want to add to the plan.

That takes care
of the first and last items from the example, but what about the
fb_.verbInfo structure?  That structure is a dedicated "scope" for
your verb instance to do it's thing in.  If you've written a
plugin for FB4, it's similar to the dedicated plugin scope within the
myFusebox structure.  When your verb is processed, all the
attributes for the verb will be stored in that structure, along with
the lexicon and verb names that are currently executing (should those
be of value).  So that's how you get your attributes, but there is
another piece of the puzzle.

Because custom verbs are not run as custom tags, they don't have their own private 'variables' scope, so you MUST
place any local variables in that same fb_.verbInfo structure to avoid
them "leaking" out and potentially interfering with other parts of the
application.

Here's a slightly more complex example that illustrates this point:

<beb.dump var="#attributes#" />

Basically it's a CFDUMP tag, but usable within FB4.  However, there is some
trickery we can do because of this whole "building an execution plan,
not actually executing" paradigm.  Here's the code:

<cfset fb_.verbInfo.v = fb_.verbInfo.attributes["var"] />
<cfif structKeyExists(fb_.verbInfo.attributes, "label")>
    <cfset fb_.verbInfo.l = fb_.verbInfo.attributes.label />
<cfelse>
    <cfset fb_.verbInfo.l = replace(fb_.verbInfo.v, "##", "####", "all") />
</cfif>
<cfset fb_appendLine('<cfdump var="#fb_.verbInfo.v#" label="#fb_.verbInfo.l#" />') />

This
time we're using a couple local variables ('v', and 'l', for var and
label, respectively), and carefully constraining them to the
fb_.verbInfo structure.  But if you look carefully, we're actually
using the textual value of the 'var' attribute as the default for the
'label' attribute.  Because the 'var' attribute isn't being
evaluated until the execution plan runs, the verb can actually read out
the textual value of the attribute, something that's impossible with a
CFML custom tag.  If you've ever done something like this:

<cfdump var="#attributes#" label="attributes" />

then you'll quickly see the value of letting the custom verb do take care of the 'label' attribute for you automatically.

So
that's a very brief primer on custom lexicons in FB4.1.  I have to
mention that custom lexicons are a 'preview' feature in FB4.1, not an
official part of the framework.  They definitely aren't going
away, but it's possible they will change.  In particular, real XML
namespaces will be used at some point, rather than the dot notation
currently in use.  That was a stopgap meature, because CF6.x
didn't have namespace support, but CF7 does.  But that's
definitely not a reason to avoid them; they're far to useful to wait.

One response to “Fusebox 4.1 Lexicons”

  1. johnb