Tuesday, January 13, 2009

ColdFusion mixins within singleton objects

Sometimes it's nice to be able to use ColdFusion mixins to include dynamic data at runtime inside CFCs. In the following example, I'm using a CFC to generate a database-driven form, with each form potentially containing multiple templates.


<cfcomponent output="false">

<cffunction name="displayForm" access="public" output="false" returntype="string">
<cfargument name="formID" required="true" type="numeric" />

<cfset var local = StructNew() />

<!--- get the templates associated with the form --->
<cfquery name="local.getTemplates" datasource="#application.datasource#">
select file_path
from form_templates
where form_id = <cfqueryparam value="#arguments.formID#" cfsqltype="cf_sql_integer" />
order by sort_order asc
</cfquery>

<!--- capture the generated html --->
<cfsavecontent variable="local.html">

<!--- loop over the templates and display (include) each template --->
<cfoutput query="local.getTemplates">

<cfinclude template="#local.getTemplates.file_path#" />

</cfoutput>

</cfsavecontent>

<cfreturn local.html />

</cffunction>

</cfcomponent>


Hopefully that's straightforward enough. This works just fine at first, until you store the component in the application scope as a singleton and a developer doesn't properly scope a variable inside one of the included templates, thereby creating a public variable that could bleed into other requests.

The solution is pretty simple: rather than using the cfinclude tag to include the mixin, use a helper function that mimics the functionality but traps the variables.

Doing this requires creating an include helper and an include proxy. The helper is created as a singleton and its purpose is to create instances of the proxy, which is in charge of including the original file.


<cfcomponent displayname="Include Helper" output="false">

<cffunction name="template" access="public" output="true" returntype="any">
<cfargument name="template" required="true" type="string" />

<cfreturn createObject("component","includeProxy").init(arguments.template) />

</cffunction>

</cfcomponent>



<cfcomponent displayname="Include Proxy" output="false">

<cffunction name="init" access="public" output="true" returntype="void">
<cfargument name="template" required="true" type="string" />

<cfinclude template="#arguments.template#" />

</cffunction>

</cfcomponent>


With the include components creating, I can now replace <cfinclude template="#local.getTemplates.file_path#" /> with #helpers.include.template(local.getTemplates.file_path)# and the template variables are now properly hidden from my component.

Monday, January 12, 2009

A few things about onMissingMethod()

ColdFusion 8 includes the onMissingMethod event handler, a very simple yet powerful feature that can be used in a variety of ways, from implicit getters and setters to method interceptors and more.

I've stumbled upon 2 things worth mentioning that didn't seem apparent at first.

1. onMissingMethod only works for public method calls. If you try to access a function remotely, such as http://mysite.com/components/foo.cfc?method=bar, and the bar() method doesn't exist, onMissingMethod won't catch it.

2. If you're using memcached to cache your objects, onMissingMethod won't fire at all. This seems to make sense, since ColdFusion appears to fire onMissingMethod at runtime.

Sunday, January 11, 2009

AOP, Caching, and memcached

Last week I started playing around with Aspect-Oriented Programming (AOP) in ColdSpring and was pretty amazed at how useful it could be. To be able to alter the functionality of your application without touching your core application code is an extremely powerful concept, but I wasn't sure where to begin.

The example on the ColdSpring site shows how AOP can be applied to handle logging across an application, but that doesn't seem all that useful (or sexy) to me. I wanted a better scenario.

After reviewing some code in a project I've been working on, I noticed a prime example of where I could use AOP: caching.

There was code inside multiple function calls that would perform caching based on the component, method, and arguments being passed into the method. The code was using non-deterministic caching, meaning if the data didn't exist in the cache, the system would fetch the data then put it into the cache. The caching code looks similar to this:


<cfset key = application.cache.getKey("reportDAO","getReportByID",arguments) />

<cfif application.cache.hasData(key)>

<cfset report = application.cache.getData(key) />

<cfelse>

... build the report ...

<cfset application.cache.setData(key,report) />

</cfif>


The code worked fine, but I had to add those same 4 lines of code to each method I wanted to cache. Plus, it led to a low level of cohesion; the method needed to know how to build a report as well as how to store and retrieve itself in memory.

With AOP, I could now configure ColdSpring to intercept each method call I wanted to cache without having to flood my code with caching logic.

As a nice side bonus, the non-deterministic key/value caching strategy I was using turned out to be similar to how memcached, a high-performance, distributed memory object caching system, caches its data. Knowing this, I was able to modify the caching advice component I created and plugin memcached in a matter of minutes.

I'm currently working on an XML based caching component that will handle bean/method caching in the request scope, session scope, and memcached. I'll post more details when I have something to show.

Sunday, January 4, 2009

Label Tags (or Lack Thereof)

One of my biggest pet peeves is when sites don't use label tags, especially around radio buttons. How much does this bother me? I actually emailed my bank's support email address about adding label tags to their "Make a Payment" page. So far, nothing has changed.



Why force a user to click the small circle when they could click the text instead? It's such a quick and simple thing to do and yet can make a page so much easier to use. There's really no reason to not use label tags other than developer laziness.