This blog is now obsolete. Go to scott.arbeitman.id.au for all new content.

1

ColdFusion and YAML

| Tuesday, April 24, 2007
One interseting thing in Ruby on Rails that's sorely missing in ColdFusion is YAML. For those who don't know, YAML is similar to XML or JSON, and is a way to represent complex data or objects. YAML is extrememly lightweight and less verbose than XML.

After a little server configuration, it's easy to turn YAML text in a file into a ColdFusion structure.

I'm using JYaml as my YAML parsing library, but other options are available. Unfortunately, JYaml requires Java 5, which is not the standard ColdFusion operating environment. To remedy this, download a Java 5 JRE and follow Ben Forta's concise directions. Don't forget to put the JYaml jar file in your classpath and restart your ColdFusion server.

Assuming your .cfm file and .yaml file are in the same directory, creating a ColdFusion structure out of it is a easy as:


<cfscript>
Yaml = CreateObject("java", "org.ho.yaml.Yaml");

configFile = CreateObject("java", "java.io.File").init(ExpandPath('.')&"/settings.yaml");

settings = Yaml.load(configFile);
</cfscript>


Dumping the "settings" variable reveals exactly the structure you were expecting.

One caveat: you may experience unexpected behavior when trying to access your struct's properties using dot notation. Instead, use the "associative array" with square brackets option.

So do this:

value = settings['credentials']['username']

not this:

value = settings.credentials.username
2

Site Layouts and Application.cfc

| Sunday, April 22, 2007
A common problem for any site developer is maintaining a consistent layout across pages. Many frameworks have been designed to simplify this. Model-Glue, for example, can handle this by having the site template as the last view included in the response cycle. This a heavy approach if all you want to do is view decoration.

Let's take a look at solving this using the onRequest method in Application.cfc to capture our outputted content, and then placing it within a template. Here's a skelteton onRequest method:


<cffunction name="onRequest">
<cfargument name="thePage" type="string" required="true" />
<!--- Magic will happen here --->
</cffunction>


For those of you familiar with onRequest, you'll know that if we don't manually included the requested page, ColdFusion won't send any content to the browser. This might strike some people as odd, but it opens a world of possibilities. For example, see my previous post on using Application.cfc as the application controller in an MVC framework.

In this case, instead of invoking a method within the Application.cfc, we'll store what ColdFusion would have outputted into a variable using the cfsavecontent tag. We'll then include another .cfm page, which will have access to all the variables in the onRequest function, including the just-saved content of the included page. Thus, the onRequest magic looks like this:


<cfsavecontent variable="contentForLayout">

<cfinclude template="#ARGUMENTS.thePage" />

</cfsavecontent>

<cfinclude template="Layout.cfm" />


Now, the code within Layout.cfm has access to the contentForLayout variable which contains the dynamic region of your page. A simple layout could simply output the content within the HTMl body:


<html>

<body>

<cfoutput>#contentForLayout#</cfoutput>

</body>

</html>

Naturally, you've have a much more complex layout, where you'd include Javascript, stylesheets, a header, footer, etc.

Another very important consequence of this is that your Layout.cfm has access to all the variables available within your Application.cfc so there is no reason which your layout cannot content dynamic content.
1

Using Application.cfc as your application controller

| Thursday, April 19, 2007
A few months ago, I was experimenting with Model-Glue for an application. One thing that struck me as odd was that certain events which are normally handled by the Application.cfc (onRequestStart, onRequestEnd) were handled by Model-Glue controllers.

Rather than moving custom application event handling logic in a seperate controller, why not handle them with the Application.cfc? After all, the Application.cfc framework is a well-documented, frequently used ColdFusion features which already handles many application-level events.

To that end, I've written yet another ColdFusion MVC framework I call "Boca", which is Rails-esque insofar as mapping event names to controller methods (handlers) to view pages without configuration. But what makes Boca unique (and uniquely ColdFusion) is that Application.cfc serves as the controller for all application events. A sample Application.cfc would like this:


<cfcomponent displayname="My Great Application" extends="BaseApplicationController">

<cffunction name="onApplicationStart" ...

<cffunction name="onSessionStart" ...

<!--- Custom Event Handlers --->

<cffunction name="addToCart" access="public" returntype="void">

<cfargument name="productId" type="numeric" required="true">
<!--- Do application logic here --->

</cffunction>

<cffunction name="signIn" access="public" returntype="void">

<cfargument name="username" type="string" required="true">
<cfargument name="password" type="string" required="true">
<!--- Do application logic here --->

</cffunction>


</cfcomponent>


While I cannot make the source code public, I can offer insight on how to accomplish this in your application. The secret is using the onRequest function in Application.cfc to intercept calls, call the appropriate function within the Application.cfc, and include the corrent view page(s). This can all be done in less than 100 lines of code. To reuse this filter across Application.cfc files within your site, simply extend the base component which implements the onRequest method, and don't override that method.

If, like in other MVC frameworks, the event contains the union of FORM and URL scopes, I suggest invoking the event handlers using the event as the argument collection of the handler method like so:


<cfset event = StructNew() />
<cfset StructAppend(event, URL) />
<cfset StructAppend(event, FORM) />
<cfinvoke method="#action#" argumentcollection="#event#" />


You the get better event documentation by enumerating the arguments and types for your event handler (although this is optional, since ColdFusion supports dynamic arguments, and will also preserve the name-value pairs passed in). See the sample component above for an example.

A consequence of this approach is that variables set (and not var-scoped) within the event handlers are available in the view pages included within the onRequest method (this is a well-documented Application.cfc behaviour), saving Model-Glue like usage of event and view-state components across controllers and views.