I was talking to Sandy Clark back at CFUNITED about how I still use a stripped-down FB3 for a lot of projects. She said I should publicize it, so here goes. At it's heart, it's a toolset for designing something that resembles a single-circuit FB3 application, but with some of the niceties (like DO) of FB4/5. I don't have a sample application to show at the moment, but I thought I'd share the framework itself (all 61 lines of it). It's inline at the end of the post, along with a sample switch file.
To use it, you just need to save the code as 'index.cfm' (or whatever) create a 'fbx_Switch.cfm' file in the same directory that has a CFSWITCH block that switches on 'attributes.currentFuseaction'. There are two mandatory CFCASEs it must contain: onRequestStart and onRequestEnd. The do exactly what you'd expect. Finally, the default fuseaction is named "__default_fuseaction__" (two underscores on the ends), so you'll want to define a CFCASE with that value as well, probably multi-aliased to something a little more friendly (like "home"). That's enough to get code executing.
The "tooling" that the framework exposes is quite simple, and I'll sum it up in a few bullet points:
- a 'self' variable, which is set to "?#fuseactionVariable#=" and can be used for links (e.g. <a href="#self##xfa.logout#">)
- an 'attributes.originalFuseaction' variable storing the requested fuseaction (or the default if none was specified)
- an 'attributes.currentFuseaction' variable storing the currently executing fuseaction
- a 'do' UDF that accepts a fuseaction to execute, and an optional variable name to put the execution output into
- an 'include' UDF that accepts a template root to execute, and an optional variable name to put the execution output into
- a 'location' UDF that accepts a destination URL and does a redirect via a HTTP Location header
Content variables for both the 'do' and the 'include' UDFs are append-only, so if you want to overwrite, you have to manually do it with a CFSET in your switch. Also as you'd expect, this is single-directory centric; index.cfm and fbx_Switch.cfm must be in the same directory, and includes must be pathed relative to that directory.
Why did I build such a framework? Mostly because neither FB3 nor FB5 fits the bill for small applications. Both are a bit on the heavy side, with FB5 being the heavier of the two. But at the same time, they both provide some really nice features (again, FB5 providing a larger number). So I wanted the best of both worlds, and I think I got it. My biggest gripe is that the single-directory nature makes it hard to clearly delineate the app from the framework, but that's a relatively minor issue I've found.
While I think any smaller application could use the framework without too many complaints, it does lend itself to apps where the total number of fuseactions is fairly small (duh), you don't want to do MVC inside the front controller (i.e. you want to use a separate backend), you want the dynamic assembly of FB5 (content variables, DOs and dynamic DOs, etc.), and you prefer to do your controller coding in CFML.
Without further ado, here's the code. I'll admit it seems rather dense, particularly on the web without syntax highlighting. But it's really pretty straightforward, and you probably won't ever need to open the file after you save it if you're using it.
<cfsetting enablecfoutputonly="true" />
<cfset fuseactionVariable = "do" />
<cfset defaultFuseaction = "__default_fuseaction__" />
<cfset self = "?#fuseactionVariable#=" />
<cfif NOT isDefined("attributes") OR NOT isStruct(attributes)>
<cfset attributes = structNew() />
</cfif>
<cfset structAppend(attributes, url) />
<cfset structAppend(attributes, form) />
<cffunction name="do">
<cfargument name="fuseaction" type="string" required="true" />
<cfargument name="contentVariable" type="string" required="false" />
<cfset var oldFuseaction = attributes.currentFuseaction />
<cfset attributes.currentFuseaction = fuseaction />
<cfif structKeyExists(arguments, "contentVariable")>
<cfparam name="#contentVariable#" default="" />
<cfsavecontent variable="#contentVariable#"><cfoutput>#variables[contentVariable]#</cfoutput>
<cfinclude template="fbx_Switch.cfm" />
</cfsavecontent>
<cfelse>
<cfinclude template="fbx_Switch.cfm" />
</cfif>
<cfset attributes.currentFuseaction = oldFuseaction />
</cffunction>
<cffunction name="include">
<cfargument name="template" type="string" required="true" />
<cfargument name="contentVariable" type="string" required="false" />
<cfif listFind("cfm,cfml,htm,html", listLast(template, ".")) EQ 0>
<cfset template = template & ".cfm" />
</cfif>
<cfif structKeyExists(arguments, "contentVariable")>
<cfparam name="#contentVariable#" default="" />
<cfsavecontent variable="#contentVariable#"><cfoutput>#variables[contentVariable]#</cfoutput>
<cfinclude template="#template#" />
</cfsavecontent>
<cfelse>
<cfinclude template="#template#" />
</cfif>
</cffunction>
<cffunction name="location" output="false">
<cfargument name="destUrl" type="string" required="true" />
<cfheader statuscode="302" statustext="Object Temporarily Moved" />
<cfheader name="Location" value="#destUrl#" />
</cffunction>
<cfparam name="attributes.#fuseactionVariable#" default="" />
<cfset attributes.fuseaction = attributes[fuseactionVariable] />
<cfif len(trim(attributes.fuseaction)) EQ 0>
<cfset attributes.fuseaction = defaultFuseaction />
</cfif>
<cfset attributes.originalFuseaction = attributes.fuseaction />
<cfset attributes.currentFuseaction = attributes.fuseaction />
<cfset do("onRequestStart") />
<cfset do(attributes.currentFuseaction) />
<cfset do("onRequestEnd") />
And the sample fbx_Switch.cfm:
<cfswitch expression="#attributes.currentFuseaction#">
<!--- lifecycle fuseactions --->
<cfcase value="onRequestStart">
</cfcase>
<cfcase value="onRequestEnd">
</cfcase>
<!--- normal fuseactions --->
<cfcase value="home,__default_fuseaction__">
<cfset include("dsp_home", "bodyContent") />
<cfset do("lay_default") />
</cfcase>
<!--- private fuseactions --->
<cfcase value="lay_default">
<cfset xfa.home = "home" />
<cfset xfa.login = "loginForm" />
<cfset include("lay_default") />
</cfcase>
<cfdefaultcase>
<cfthrow type="IllegalFuseactionException"
message="The specified fuseaction (#attributes.currentFuseaction#) is unknown." />
</cfdefaultcase>
</cfswitch>
Edit: Here's a link to the project page.