Tuesday, December 29, 2009

jQuery Autocomplete and ColdFusion

While migrating my applications from Prototype to jQuery, I needed to replace the Ajax.Autocompleter that comes with script.aculo.us, the effects library written for Prototype.

After a quick Google search, I found a pretty good plugin that looked like it would do what I wanted. However, there was a pretty big difference in how the two plugins work. Ajax.Autocompleter must return an unordered list (<ul>), with each suggestion rendered inside its own list item (<li>). Piece of cake.

However, the jQuery Autocompleter expects a string, with each suggestion rendered on its own line. This threw me for a loop at first, since ColdFusion isn't always the best at dealing with whitespace. After a little thought, here's what I came up with.

index.cfm

<link rel="stylesheet" type="text/css" href="jquery.autocomplete.css" />
<script src="jquery-1.3.2.js"></script>
<script src="jquery.autocomplete.js"></script>

<script type="text/javascript">
$().ready(function() {
$('#search').autocomplete('UserService.cfc?method=autocomplete&_cf_nodebug=true', {
multiple: true,
formatItem: function(row) {
var user = JSON.parse(row.toString());
return user.name;
},
formatResult: function(row) {
var user = JSON.parse(row.toString());
return user.email;
}
});

$('#search').result(function(event, data, formatted) {
var user = JSON.parse(data.toString());
alert('You selected user '+user.id);
});

});
</script>

<input type="text" id="search" name="search" size="60" />


UserService.cfc

component {

remote void function autocomplete(required string q) {

// get the users
var users = searchUsers(arguments.q);
var result = [];
var i = "";

for (i=1; i <= arrayLen(users); i++) {

// build the user record
var user = {};

// maintain lowercase keys
user["id"] = users[i].id;
user["name"] = users[i].name & " (" & users[i].email & ")";
user["email"] = users[i].email;

// serialize the user record and append it to the result
arrayAppend(result, serializeJSON(user));

}

// convert the result from an array to a list, with each user on its own line
var html = arrayToList(result, chr(10));

// output the JSON result
writeOutput(html);

}

public array function searchUsers(required string search) {

// normally this would query the db,
// but for this demo I'll just create a static array of users
var users = [
{id="1", name="Adam", email="adam@email.com"},
{id="2", name="Bob", email="bob@email.com"},
{id="3", name="Brady", email="brady@email.com"},
{id="4", name="John", email="john@email.com"},
{id="5", name="Kaitlyn", email="kaitlyn@email.com"},
{id="6", name="Leanne", email="leanne@email.com"},
{id="7", name="Lisa", email="lisa@email.com"},
{id="8", name="Mike", email="mike@email.com"},
{id="9", name="Nate", email="nate@email.com"},
{id="10", name="Ryan", email="ryan@email.com"},
{id="11", name="Sean", email="sean@email.com"},
{id="12", name="Tony", email="tony@email.com"},
{id="13", name="Tyler", email="tyler@email.com"}
];

return users;

}

}


On a side note, JSON.parse() is a built-in function only available to newer browsers, like Firefox 3.5 and IE 8.0. Does anybody know of a good plugin to accommodate older browsers? Something similar to Prototype's String.evalJSON would be preferred.

Friday, December 18, 2009

Using onMissingMethod to Treat Properties More Like... Properties

Since I didn't come from a Java background, I'm relatively new to OOP best practices. One thing that I've grown to loathe in my short time working with objects in Hibernate is having to go through getters and setters to access my properties:


<cfset user = new User() />
<cfset user.setFirstName("Tony") />
<cfset user.setLastName("Nelson") />

<cfoutput>
My name is #user.getFirstName()# #user.getLastName()#
</cfoutput>


I'd much rather prefer true implicit getters and setters a la Groovy, ActionScript, C#, etc...


<cfset user = new User() />
<cfset user.firstName = "Tony" />
<cfset user.lastName = "Nelson" />

<cfoutput>
My name is #user.firstName# #user.lastName#
</cfoutput>


After working with jQuery a little more, I really like the convention used on their event helpers, like change() and change(fn). If you pass in an argument, it binds a new observer. If you don't pass in an argument, it triggers the event.

I was able to apply similar logic to my entities in ColdFusion with the help of onMissingMethod() inside a base class that my entities extend.


component {

public void function set(required string property, required any value) {

if (structKeyExists(this,"set#arguments.property#")) {

if (!structKeyExists(arguments,"value") || isNull(arguments.value) || (isSimpleValue(arguments.value) && arguments.value eq "")) {
evaluate("set#arguments.property#(javacast('NULL',''))");
}
else {
evaluate("set#arguments.property#(arguments.value)");
}

}

}

public any function get(required string property) {

if (structKeyExists(this,"get#arguments.property#")) {
local.value = evaluate("get#arguments.property#()");
}

if (!structKeyExists(local,"value")) {
local.value = "";
}

return local.value;

}

public any function onMissingMethod(required string missingMethodName, required struct missingMethodArguments) {

if (structIsEmpty(arguments.missingMethodArguments)) {
return get(arguments.missingMethodName);
}

set(arguments.missingMethodName,arguments.missingMethodArguments[1]);

return this;

}

}



Now if I pass in an argument, it sets the property. If I don't pass an in argument, it gets the property, leaving me with the following code:


<cfset user = new User() />
<cfset user.firstName("Tony") />
<cfset user.lastName("Nelson") />

<cfoutput>
My name is #user.firstName()# #user.lastName()#
</cfoutput>


And in case you didn't read the code, also included in the base class are generic get() and set() methods that relay calls to the actual property getters and setters in order to maintain proper encapsulation.

Wednesday, December 16, 2009

Migrating to jQuery: My First Plugin

A couple posts ago I wrote about how I couldn't justify switching from Prototype to jQuery. Well I take it back (sort of).

After spending more time with jQuery's event handling, I wanted more out of Prototype and I couldn't justify trying to port what I wanted over from jQuery and expect it to work across all browsers. I'd rather let jQuery and its large, active community help me out with testing. So I've begun the process of converting a good portion of my applications over to jQuery.

While updating the code is more tedious than anything, I found myself missing the convenience of Prototype's direct access to DOM elements. For example, I no longer have the following:


if ( $('checkboxA').checked ) {
$('checkboxB').checked = false;
}


Granted with jQuery there are multiple ways to accomplish the same thing but, while they're not too much longer, they're still longer:


if ( $('#checkboxA').attr('checked') ) {
$('#checkboxB').attr('checked',false);
}

if ( $('#checkboxA').is(':checked') ) {
$('#checkboxB').removeAttr('checked');
}

if ( $('#checkbox:checked').length ) {
$('#checkboxB').attr('checked',false);
}


Since I like to write as little code as possible and everybody else seems to really like jQuery's plugin architecture, I figured I'd try write my own plugin. And since it's my first plugin, please don't make too much fun of it.


jQuery.each(('checked,disabled').split(','), function(i, name) {
jQuery.fn[name] = function() {
return arguments ? this.attr(name, arguments[0]) : this.attr(name);
};
});


It works similar to the built-in jQuery event helpers, such as $(selector).change() and $(selector).click(fn). In my plugin, if you pass an an argument, it sets the attribute's value. If you don't pass in an argument, it returns the attribute's value.

Here's how I can write my sample code now:


if ( $('#checkbox').checked() ) {
$('#checkboxB').checked(false);
}


I'll admit it's not much shorter, but it reads a lot better to me. Plus, I don't have to directly call .attr() anymore, which is basically just a generic getter/setter. And if you didn't notice, I also added the same logic for .disabled() for good measure.

I haven't decided if I want to fully embrace a plugin that accomplishes something so trivial, but for some reason I like it.

Monday, December 14, 2009

Handling HTTP Request Parameters in ColdFusion

One thing that always kind of annoyed me in ColdFusion is the separation of HTTP request parameters, meaning parameters for GET requests are put into the url scope and POST parameters are put into the form scope. While this makes sense technically, I find it kind of annoying. For the most part, I don't care where the parameters come from.

Most ColdFusion frameworks alleviate this problem by combining the scopes for you. For example, Model-Glue and Mach-II create an event object that exposes parameters via event.getValue(key) or event.getArg(key). FW/1 combines the variables into request.context, which is then aliased as rc inside your controllers and views. ColdBox does a little bit of both, by having event.getValue(key) inside your controllers and rc inside your views.

While this makes working with request parameters easier, it still feels a little cumbersome having to go through getters and setters or cryptically-named structs (rc? really?). I'd rather have all the parameters combined into a single scope from the get-go.

One of the many things I like about Ruby on Rails and Grails is the params scope, which contains all GET and POST request parameters. Not surprisingly, CFWheels also has the params scope.

However, what happens if you're not using CFWheels, or even a framework at all? I want the same functionality, but without the overhead of adding a framework. With a little inspiration from Ben Nadel, here's my solution:

Application.cfc

component {

function onRequestStart() {

var params = {};
structAppend(params,url);
structAppend(params,form);
getPageContext().getFusionContext().hiddenScope.params = params;

}

}


Now all url and form variables are combined into a single params struct at the beginning of each request, which is then made globally accessible throughout my application, just like a built-in scope. Short and simple, no framework necessary.

On a final note, while this does work, I don't know if all the ColdFusion "experts" would consider it a best practice to add a new "scope" to the language. But in my opinion, it's isolated and helps solve a problem, so why not?

Thursday, December 10, 2009

Firing Native JavaScript Events using Prototype

I've been using Prototype as my JavaScript library of choice for a couple years now. While I'll admit I would probably choose jQuery if I were to rewrite all of my code from scratch, that's simply not an option at this point. I can't justify rewriting perfectly good code for the sake of rewriting it -- if it ain't broke, don't fix it.

For the most part, Prototype has everything I need: Ajax, CSS selectors, DOM manipulation, event handling, and a slew of convenience methods for working with objects and classes. However, I've always been a little bothered by the inability to fire native JavaScript events, meaning there's no way to fire an element's change event. While Prototype allows you to fire custom events using Element.fire, all event names must be namespaced using a colon, which rules out $(id).fire('change').

So today, after some quick Googling, I wrote my own Prototype-based version of jQuery's change() method in order to fire native events. Here's what I came up with:


(function() {
var methods = {};
var events = 'blur,change,click,dblclick,error,'+
'focus,keydown,keypress,keyup,load,'+
'mousedown,mouseenter,mouseleave,'+
'mousemove,mouseout,mouseover,mouseup,'+
'resize,scroll,select,submit,unload';
events.split(',').each(function(event) {
methods[event] = function(element, fn) {
if (fn == undefined) {
if(document.createEvent) {
var evt = document.createEvent('HTMLEvents');
evt.initEvent(event,true,true);
return !element.dispatchEvent(evt);
}
else {
var evt = document.createEventObject();
return element.fireEvent('on'+event,evt)
}
}
else {
element.observe(event,fn);
}
}
});
Element.addMethods(methods);
})();


With this, you can simply call $(id).change() to fire the element's change event or $(id).change(function(){...}) to add an observer to the element's change event.

I did a quick test and it seems to work in both IE8 and Firefox, but I can't guarantee anything about older browsers, so use at your own risk.

On a final note, I don't know why Prototype seems to be overlooked by so many people in favor of jQuery. I actually prefer the Prototype syntax in many cases, but that's probably because I've been working with it longer. Maybe it's because developers who weren't too familiar with JavaScript stumbled upon jQuery one day and decided they didn't have to look at any other libraries out there, which seems really wrong to me. That would be the same as choosing Mach-II without looking at Model-Glue or ColdBox. Or choosing PHP without looking at ColdFusion or Ruby. You really can't know if you've made the right decision unless you've evaluated all your options.

Wednesday, December 9, 2009

7 reasons I switched back to ColdFusion...

First, read this article: 7 reasons I switched back to PHP after 2 years on Rails.

Now, read it again, only this time replace the word "PHP" with "ColdFusion".

Languages are like frameworks: it doesn't really matter which one you use, they all pretty much do the same thing. There is no Holy Grail. In the end, it's all just a bunch of 0's and 1's that can help you get things done.

Monday, December 7, 2009

Check if a Value Exists in a Query

Sometimes you'll need to know if a certain value exists in a query. For example, let's say I have a query of colors.



For demonstration purposes, let's manually build the query:

<cfset colors = queryNew("color") />

<cfloop list="blue,red,green,yellow,blue,black,red,orange" index="color">
<cfset queryAddRow(colors) />
<cfset querySetCell(colors,"color",color) />
</cfloop>

Now let's say I wanted to know if the color "green" exists in my query. There are quite a few ways to do this.

One way to do this would be to create a list of all the colors, then check to see if "green" exists in the list.

<cfset colorList = valueList(colors.color) />

<cfif listFindNoCase(colorList,"green")>
green exists
<cfelse>
green does not exist
</cfif>

However, converting the values to a list might cause problems if one of the values has a comma in it. Granted that's not the case in this scenario, but it's worth mentioning.

Another problem might come up if the column name is dynamic. For example, the following code won't work.

<cfparam name="url.column" default="color" />

<cfset colorList = valueList(colors[url.column]) />

<cfif listFindNoCase(colorList,"green")>
green exists
<cfelse>
green does not exist
</cfif>

I'm not sure how often this situation comes up, but here's a trick to be able to use dynamic column names: use arrayToList() rather than valueList().

<cfparam name="url.column" default="color" />

<cfset colorList = arrayToList(colors[url.column]) />

<cfif listFindNoCase(colorList,"green")>
green exists
<cfelse>
green does not exist
</cfif>

Another way to check if "green" exists in the query would be to use a query of a query.

<cfquery name="getGreen" dbtype="query">
select *
from colors
where color = 'green'
</cfquery>

<cfif getGreen.recordCount gt 0>
green exists
<cfelse>
green does not exist
</cfif>

However, queries of queries can potentially hurt your application's performance if called multiple times. Plus, they're case-sensitive.

In order to increase performance, you could loop over the colors query and insert the values into a struct, then check to see if the key exists in the struct.

<cfset colorStruct = {} />
<cfloop query="colors">
<cfset colorStruct[color] = true />
</cfloop>

<cfif structKeyExists(colorStruct,"green")>
green exists
<cfelse>
green does not exist
</cfif>

While this should help your application's performance, there's still the issue of case-sensitivity. Fortunately, ColdFusion 9 helps solve this problem with the addition of arrayFindNoCase.

Once again, let's treat the query as an array. Normally accessing query["column"] is handled the same as query["column"][1] and would only return the value from the first record in the query, but apparently it references the entire array if used inside a function call.

<cfif arrayFindNoCase(colors["color"],"green")>
green exists
<cfelse>
green does not exist
</cfif>

Not only is this approach case-insensitive, but it's also the least amount of code. Win-win.

Tuesday, December 1, 2009

ColdFusion Reference Wikis

I recently stumbled upon a couple ColdFusion wiki sites that I thought were worth mentioning.

Both sites were created by Kevan Stannard, a ColdFusion developer from Australia. While the sites appear to be a work in progress, there's still quite a bit of useful, thorough, and user-friendly content to read, as well as links to other resources on the web. His personal development blog has some good posts that are worth checking out too.

I don't know Kevan and honestly I hadn't heard of him until running across his sites, which I find a little surprising considering the quality of the content. It was pretty refreshing to see some common programming techniques applied in ColdFusion and explained with a human touch. I wish I had found these sites when I was first starting out.

Anyways, I just thought he should get a quick shout out for his efforts.

Wednesday, November 11, 2009

ORM Event Handling in ColdFusion 9

I've been playing around with ORM event handling in ColdFusion 9. For my application, I want to be able to fire events during various points in the lifecyle of my persistent entities. However, I don't want to have to clutter up my business logic with tasks such as logging, synchronizing integration data, or pushing changes out to Flex. I want to take a more AOP-style approach.

To do this, I created an XML-driven EventManager to use as my application's global event handler. With my EventManager, I'm able to configure listeners to respond to events fired by my entities using the built in ORM event handlers. The events can be registered using regular expressions and all follow the pattern {EntityName}:{EventHandler}. For example, I might have User:postUpdate or Product:postDelete.

In my sample application, I've configured my EventManager to send an email before and after any entity is loaded, inserted, updated, or deleted. In other words, basically anytime something happens to an entity. I'm not sure how useful this would be, but it's just an example.

Here's the directory structure:



Application.cfc

component {

this.name = "Sample";
this.datasource = "Sample";
this.mappings["/sample"] = getDirectoryFromPath(getCurrentTemplatePath());

this.ormEnabled = true;
this.ormSettings.dbcreate = "update";
this.ormSettings.eventHandling = true;
this.ormSettings.eventHandler = "sample.com.EventHandler";

public void function onApplicationStart() {

application.beanFactory = createObject("component","coldspring.beans.DefaultXmlBeanFactory").init();

application.beanFactory.loadBeans("/sample/config/coldspring.xml");

}

}


EventHandler.cfc

component implements="cfide.orm.IEventHandler" {

public void function preLoad(any entity) {
handleEvent(entity,"preLoad");
}

public void function postLoad(any entity) {
handleEvent(entity,"postLoad");
}

public void function preInsert(any entity) {
handleEvent(entity,"preInsert");
}

public void function postInsert(any entity) {
handleEvent(entity,"postInsert");
}

public void function preUpdate(any entity, struct oldData) {
handleEvent(entity,"preUpdate");
}

public void function postUpdate(any entity) {
handleEvent(entity,"postUpdate");
}

public void function preDelete(any entity) {
handleEvent(entity,"preDelete");
}

public void function postDelete(any entity) {
handleEvent(entity,"postDelete");
}

private void function handleEvent(any entity, string handler) {

var collection = {};
collection.entity = ormGetSession().getEntityName(entity);
collection.id = entity.getID();
collection.handler = handler;

var eventManager = application.beanFactory.getBean("eventManager");
eventManager.dispatchEvent("#collection.entity#:#collection.handler#",collection);

}

}


EventManager.cfc

component accessors="true" {

property configPath;

public any function init() {
variables.events = {};
variables.loaded = false;
}

public void function setConfigPath(required string configPath) {

if(fileExists(configPath)) {
variables.configPath = configPath;
}
else {
variables.configPath = expandPath(configPath);
}

}

public void function dispatchEvent(required string event, struct data) {

if(!structKeyExists(arguments,"data")) {
arguments.data = {};
}

if(!variables.loaded) {
loadConfig();
variables.loaded = true;
}

local.listeners = getListeners(event);

for (var i=1;i <= arrayLen(local.listeners);i++) {

local.bean = application.beanFactory.getBean(local.listeners[i].bean);

evaluate("local.bean.#local.listeners[i].method#(argumentCollection=data)");
}

}

private array function getListeners(required string event) {

if(!structKeyExists(variables.events,event)) {

local.used = {};

local.listeners = [];

for (var i=1;i <= arrayLen(variables.config);i++) {

if(reFindNoCase(variables.config[i].name,event)) {

for (var j=1;j <= arrayLen(variables.config[i].listeners);j++) {

local.listener = variables.config[i].listeners[j];

if(!structKeyExists(local.used,local.listener.id)) {

arrayAppend(local.listeners,local.listener);
local.used[local.listener.id] = true;

}

}

}

}

variables.events[event] = local.listeners;

}

return variables.events[event];

}

private void function loadConfig() {

variables.config = [];
local.xml = xmlParse(fileRead(getConfigPath()));

for (var i=1;i <= arrayLen(local.xml.events.xmlChildren);i++) {

local.event = {};
local.event.name = local.xml.events.xmlChildren[i].xmlAttributes.name;
local.event.listeners = [];

for (var j=1;j <= arrayLen(local.xml.events.xmlChildren[i].xmlChildren);j++) {

local.listener = {};
local.listener.bean = local.xml.events.xmlChildren[i].xmlChildren[j].xmlAttributes.bean;
local.listener.method = local.xml.events.xmlChildren[i].xmlChildren[j].xmlAttributes.method;
local.listener.id = local.listener.bean & "." & local.listener.method;

arrayAppend(local.event.listeners,local.listener);
}

arrayAppend(variables.config,local.event);

}

}

}


NotficationService.cfc

component {

public void function sendNotification() {

savecontent variable="local.body" {
writeDump(arguments)
}

var notification = new Mail();
notification.setTo("joe@example.com");
notification.setFrom("joe@example.com");
notification.setSubject("Notification");
notification.setType("html");
notification.send(body=local.body);

}

}


coldspring.xml

<beans>

<bean id="eventManager" class="sample.com.EventManager">
<property name="configPath">
<value>/sample/config/events.xml</value>
</property>
</bean>

<bean id="notificationService" class="sample.com.NotificationService" />

</beans>


events.xml

<events>
<event name="[\w]+:(pre|post)(Load|Insert|Update|Delete)">
<listener bean="notificationService" method="sendNotification" />
</event>
</events>


I'm not sure if this is the best approach or how well this would scale, but it seems to work pretty well for now.

PS - note the use of savecontent inside script. And they said it was pointless... :)

Tuesday, November 10, 2009

ColdFusion 9 Mail in cfscript

I ran into an issue today trying to send an email using script syntax. My code was pretty simple:


var email = new Mail();
email.setTo("joe@example.com");
email.setFrom("joe@example.com");
email.setSubject("Test Email");
email.setType("html");
email.send(body="Hello, world");


However, when I tried running the code, I got the following error:

Could not find the ColdFusion component or interface Mail.

While it's pretty obvious now what the problem is now, at first I was pretty confused. I figured since all the tags were converted to handle script syntax, everything should just work. I checked the ColdFusion 9 documentation and everything looked fine. After about 10 minutes I finally figured it out.

When installing ColdFusion, one of the first things I do is clean up ColdFusion Administrator by removing the default datasources and custom tag paths. However, when Adobe added script support for a couple tags (ftp, http, mail, pdf, query, storedproc), they chose to implement the functions as objects using CFCs. When I deleted the default custom tag path, ColdFusion was no longer able to find the Mail.cfc, since it was relying on the custom tag path.

If you go to cf_root\servers\cfusion\cfusion-ear\cfusion-war\WEB-INF\cfusion\CustomTags\com\adobe\coldfusion\, you should see all the tags implemented as CFCs. Kinda interesting.

On a random note, although it was said that <cfsavecontent /> would not be implemented in script syntax, apparently it was. You can see it in action if you view the examples on the documentation for using mail in script.


savecontent variable="mailBody"{
WriteOutput("This message was sent by...");
}

Thursday, October 29, 2009

ColdFusion + Spring?

After the recent additions of Hibernate, Ehcache and Solr (Lucene) to ColdFusion 9, it's starting to become really clear what ColdFusion does best: provide a clean and easy interface to powerful, open-source Java libraries.

With that being said, I would vote for Spring to be included next. While ColdSpring is great, why not go straight to the source? Not only would it allow for a tighter, seemless integration between ColdFusion and Java objects, but it would also leverage the power and performance of Java for our IoC and AOP needs. On top of that, Spring provides other services, such as integration with BlazeDS for Flex powered applications.

Just think, you could configure Spring inside your Application.cfc using this.iocEnabled and this.iocSettings.iocConfig. And if you didn't want to use XML, you could define your beans using annotations in your components (component beanid="userService" scope="singleton") and wire in your dependencies through properties (property name="userService" autowire="true";). It could look pretty similar to the way Hibernate was implemented.

Granted trying to integrate Spring into ColdFusion is probably easier said than done, but if they got it to work with Hibernate, I'm sure it could be done with Spring too.

Thursday, October 22, 2009

ColdFusion 9 ORM Custom Naming Strategy

I'm pretty particular when it comes to naming conventions. If I'm working with code and objects, I prefer properties to be in written in camel case. However, I like database fields to be written in proper case, with all spaces replaced with underscores. For example, I would have user.id and user.firstName in my code, but User.ID and User.First_Name in my database.

Hibernate is able to automatically generate your database schema for you based on your *.hbmxml files, which can be automatically generated for you by ColdFusion after creating your Entity CFCs. While this can be a really handy feature for development, I was a little annoyed when Hibernate generated all of my columns in camel case to match my properties.

Then I found a really nice post by an Adobe engineer that talked about using a custom naming strategy, which is a component that Hibernate can use to generate your database table and column names.

In your Application.cfc, you can set this.ormSettings.namingStrategy to a CFC that implements the "cfide.orm.INamingStrategy" interface.

cfide.orm.INamingStrategy

component {

public string function getTableName(string tableName) {
}

public string function getColumnName(string columnName) {
}

}


With that in mind, here's my UncamelizeStrategy.cfc

component implements="cfide.orm.INamingStrategy" {

public string function getTableName(string tableName) {
return uncamelize(tableName);
}

public string function getColumnName(string columnName) {
return uncamelize(columnName);
}

private string function uncamelize(string name) {

var array = [];

for (var i=1;i <= len(name);i++) {
var char = mid(name,i,1);

if(i == 1) {
arrayAppend(array,ucase(char));
}
else {
if(reFind("[A-Z]",char)) {
arrayAppend(array,"_" & char);
}
else {
arrayAppend(array,lcase(char));
}
}
}

var newName = arrayToList(array,"");

if(newName == "Id") {
newName = "ID";
}
else if(right(newName,3) == "_Id") {
newName = left(newName,len(newName)-3) & "_ID";
}

return newName;

}

}


I'm sure there's probably an easier way to write this, perhaps using a regular expression, but I chose the brute-force approach of looping over each character. Short and simple.

Tuesday, October 20, 2009

Hibernate Data Filters

In a lot of the applications I write, I prefer to use soft deletes rather than hard deletes to ensure that data can easily and quickly be restored if (and when) a user accidentally deletes something. However, trying to implement soft deletes is typically easier said than done. For example, if you wanted a query of all the non-deleted products, your query might look like:


<cfquery name="getProducts">
select *
from product
where isDeleted = 0
</cfquery>


To convert this to ColdFusion 9 ORM, you could write an HQL statement that looks like:


<cfset products = ORMExecuteQuery("from Product where isDeleted = 0") />


Obviously it isn't ideal replacing a simple entityLoad("Product") with HQL, but it becomes even worse when you consider having to recreate that logic across relationships. Yuck.

Introducing Hibernate Filters

"Hibernate3 has the ability to pre-define filter criteria and attach those filters at both a class level and a collection level. A filter criteria allows you to define a restriction clause similar to the existing "where" attribute available on the class and various collection elements. These filter conditions, however, can be parameterized. The application can then decide at runtime whether certain filters should be enabled and what their parameter values should be." Read more.

Long story short, you can define a filter that applies the isDeleted logic to your entities without actually touching your entities.

To accomplish this, I had my Product entity extend a base Entity in order to perform the save() and delete() methods, which are responsible for handling the soft deletes. After that, I updated my Product.hbmxml file to include the filter definition and add it to my Product entity. Finally, I updated Application.onRequestStart() to enable the softDeletes filter, which is disabled by default. Here's the sample code:

Application.cfc

component {

this.name = "sample";
this.datasource = "sample";
this.ormEnabled = true;
this.ormSettings.dbcreate = "update";
this.ormSettings.saveMapping = true;

public void function onRequestStart() {
if(structKeyExists(url,"init")) {
ormReload();
}
ORMGetSession().enableFilter("softDeletes");
}

}


Product.cfc

component persistent="true" extends="Entity" {

property id;
property name;

}


Entity.cfc

component accessors="true" {

property isDeleted;

public void function save() {
setIsDeleted(0);
entitySave(this);
}

public void function delete() {
setIsDeleted(1);
entitySave(this);
}

}


index.cfm

<cfset products = EntityLoad("Product") />

<cfdump var="#products#" />


Product.hbmxml

<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>

<class entity-name="Product" lazy="true" name="cfc:sample.Product" table="Product">
<id name="id" type="int">
<column length="10" name="id"/>
<generator class="native"/>
</id>
<property name="name" type="string">
<column name="name"/>
</property>
<property name="isDeleted" type="boolean">
<column name="isDeleted"/>
</property>
<filter name="softDeletes" condition="isDeleted = 0" />
</class>

<filter-def name="softDeletes" />

</hibernate-mapping>


The really nice part about this approach is that I've got soft deletes working without my Product entity being aware, since that logic is hidden in the base class, a config file, and a global template.

Monday, October 19, 2009

ColdFusion 9 Oddities

I ran into a couple interesting things when playing around with ColdFusion 9 today.

Oddity #1:
I was trying to create a really simple User entity using ORM.

User.cfc

<cfcomponent persistent="true">

<cfproperty name="id" fieldtype="id" generator="native" />
<cfproperty name="firstName" />
<cfproperty name="lastName" />
<cfproperty name="isDeleted" type="boolean" />
<cfproperty name="createdOn" type="date" />

</cfcomponent>


Here's what a dump of the User looks like:



I realized that I typically put isDeleted and createdOn on all of my entities, so I decided I would put them on a base class, Entity.cfc, that my User entity could extend. Here's the updated components:

User.cfc

<cfcomponent persistent="true" extends="Entity">

<cfproperty name="id" fieldtype="id" generator="native" />
<cfproperty name="firstName" />
<cfproperty name="lastName" />

</cfcomponent>


Entity.cfc

<cfcomponent>

<cfproperty name="isDeleted" type="boolean" />
<cfproperty name="createdOn" type="date" />

</cfcomponent>


However, when I went to look at my User, I noticed the extended properties were missing.



After a little while, I tried adding accessors="true" to my Entity base class. It seems slightly odd to need to add that since the parent object is persistent, but it gave me my getters and setters back.

Entity.cfc

<cfcomponent accessors="true">

<cfproperty name="isDeleted" type="boolean" />
<cfproperty name="createdOn" type="date" />

</cfcomponent>


And here's the result:



All good, right? Unfortunately not. I tried executing the following code:

index.cfm

<cfset user = EntityNew("User") />

<cfset user.setFirstName("Tony") />
<cfset user.setLastName("Nelson") />
<cfset user.setIsDeleted(0) />
<cfset user.setCreatedOn(now()) />

<cfset EntitySave(user) />


While this appeared to work in the interface. when I took a peak at the database, it didn't save my isDeleted or createdOn values. In fact, it didn't even create the columns in the database.

I decided to check out what Hibernate was doing by setting this.ormSettings.saveMapping = true in my Application.cfc. Here's the auto-generated User.hbmxml:

User.hbmxml

<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class entity-name="User" lazy="true" name="cfc:cat.User" table="`User`">
<id name="id" type="int">
<column name="id"/>
<generator class="native"/>
</id>
<property name="firstName" type="string">
<column name="firstName"/>
</property>
<property name="lastName" type="string">
<column name="lastName"/>
</property>
</class>
</hibernate-mapping>


Apparently Hibernate wasn't able to pick up any properties defined in my base Entity.cfc. To solve this, I simply manually added the properties to the hibernate mapping file like such:

User.hbmxml

<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class entity-name="User" lazy="true" name="cfc:cat.User" table="`User`">
<id name="id" type="int">
<column length="10" name="id"/>
<generator class="native"/>
</id>
<property name="firstName" type="string">
<column length="255" name="firstName"/>
</property>
<property name="lastName" type="string">
<column length="255" name="lastName"/>
</property>
<property name="isDeleted" type="boolean">
<column name="isDeleted"/>
</property>
<property name="createdOn" type="date">
<column name="createdOn"/>
</property>
</class>
</hibernate-mapping>


This seemed to do the trick and everything was happy once again. I guess I'll be using the mapping files more than I had originally planned.

Oddity #2:
It appears that using ColdFusion mixins inside CFCs no longer works in CF9. Take the following really basic example:

User.cfc

<cfcomponent>

<cfinclude template="mixin.cfm" />

</cfcomponent>


mixin.cfm

<cffunction name="sayHello">

<cfreturn "Hello, world" />

</cffunction>


I could then create a new User like so:

index.cfm

<cfset user = createObject("component","User") />

<cfdump var="#user#" />


If I were to run this code in CF8, I would get a User that looked like:



However in CF9, I get a completely empty User object:



Odd. Yeah it's not a very common use-case, but it's still something that can come in handy every now and then.

UPDATE: Apparently mixins still work, they just don't appear when you dump the component.

Wednesday, October 7, 2009

Generic DAO Factory for ColdFusion 9

Joe Rinehart has a nice post on creating a generic DAO wrapper for Hibernate in ColdFusion 9. For example, if you wanted to create a UserDAO using the GenericDAO, you could set this up in ColdSpring like:


<beans>

<bean id="userDAO" class="GenericDAO" />

<bean id="userService" class="UserService">
<property name="dao">
<ref bean="userDAO" />
</property>
</bean>

</beans>


Then to get a User record from your UserDAO inside your UserService, your code might look like:


component {
public any function get(string id) {
return getDAO().get("User",id);
}
}


While this works, it seems a little weird having to pass in the name of the entity to the DAO. Ideally the code would simply be:


component {
public any function get(string id) {
return getDAO().get(id);
}
}


I decided to play around a little bith with the code and was able to accomplish this with the help of ColdSpring. Rather than having my userDAO point directly to the GenericDAO class, I've created a DAOFactory instead to generate an instance of a DAO. Here's what my config looks like:


<beans>

<bean id="userDAO" factory-bean="daoFactory" factory-method="getUserDAO" />

<bean id="daoFactory" class="DAOFactory">
<constructor-arg name="class">
<value>GenericDAO</value>
</constructor-arg>
</bean>

<bean id="userService" class="UserService">
<property name="dao">
<ref bean="userDAO" />
</property>
</bean>

</beans>


Here's what my DAOFactory.cfc looks like:


component extends="SingletonFactory" {

public any function init(string class) {
return super.init("dao",class);
}

public any function onMissingMethod(string missingMethodName, struct missingMethodArguments) {

var object = getObject();

object.entityName = getEntityName(missingMethodName);

return object;

}

}


Here's what SingletonFactory.cfc looks like, which the DAOFactory extends:


component {

public any function init(string type, string class) {
variables.type = type;
variables.class = class;

return this;
}

private any function getObject() {
return createObject("component",variables.class);
}

private string function getEntityName(string methodName) {
methodName = replaceNoCase(methodName,"get","");

return left(methodName,len(methodName)-len(variables.type));
}

public any function onMissingMethod(string missingMethodName, struct missingMethodArguments) {
return getObject();
}

}


Finally, here's what my GenericDAO.cfc looks like:


component {

this.entityName = "";

public array function list(string criteria, array params) {

var hql = "from " & this.entityName;

if (structKeyExists(arguments,"criteria")) {
hql = hql & " where " & criteria;
}

if (structKeyExists(arguments,"params")) {
return ormExecuteQuery(hql,params);
}

return ormExecuteQuery(hql);
}

public any function get(string id) {
return entityLoad(this.entityName, id);
}

public void function save(any entity) {
entitySave(entity);
}

public void function delete(any entity) {
entityDelete(entity);
}

}


Taking this one step further, you could use the same idea and create really simple Services using a ServiceFactory.

Here's what your coldspring.xml might look like:


<beans>

<bean id="userDAO" factory-bean="daoFactory" factory-method="getUserDAO" />

<bean id="daoFactory" class="DAOFactory">
<constructor-arg name="class">
<value>GenericDAO</value>
</constructor-arg>
</bean>

<bean id="userService" factory-bean="serviceFactory" factory-method="getUserService">
<property name="dao">
<ref bean="userDAO" />
</property>
</bean>

<bean id="serviceFactory" class="ServiceFactory">
<constructor-arg name="class">
<value>GenericService</value>
</constructor-arg>
</bean>

</beans>


Here's what the ServiceFactory.cfc looks like:


component extends="SingletonFactory" {

public any function init(string class) {
return super.init("service",class);
}

}


And here's GenericService.cfc:


component {

public void function setDAO(any dao) {
variables.dao = arguments.dao;
}

private string function getDAO() {
return variables.dao;
}

public array function list(string criteria, array params) {
if(!StructKeyExists(arguments,"criteria")){
arguments.criteria = "";
}
if(!StructKeyExists(arguments,"params")){
arguments.params = [];
}

return getDAO().list(criteria,params);
}

public any function get(string id) {
return getDAO().get(id);
}

public any function save(any entity) {
return getDAO().save(entity);
}

public any function delete(any entity) {
return getDAO().delete(entity);
}

}


I took most of this code from Joe's example, but just shrunk it down a little bit for the sake of the post. Also, I haven't hooked this up to a database to test it, so there might be some syntax errors. Finally, I wrote this during a rather boring session at MAX, so I'll admit there's a lot of rambling going on. Hopefully you can kind of follow the logic.

Sunday, October 4, 2009

Adobe MAX 2009

Heading to LA today to attend Adobe MAX for the 2nd time. Hopefully it's as inspiring for me as it was last year.

UPDATE: There's been some speculation that Adobe will release ColdFusion 9 at MAX. I guess there's no more need to speculate... http://www.adobe.com/products/coldfusion/buy/

Thursday, October 1, 2009

Implicit Getters and Setters

While the addition of implicit getters and setters for properties in ColdFusion is really nice, I'm still a little jealous of the how they're handled in Groovy.

Take the typical User object.


<cfcomponent name="User">

<cfproperty name="firstName" />
<cfproperty name="lastName" />

</cfcomponent>


In ColdFusion, you would interact with this object like such:


<cfset user = new User() />
<cfset user.setFirstName("Tony") />
<cfset user.setLastName("Nelson") />

<cfoutput>
Hello, my name is #user.getFirstName()# #user.getLastName()#
</cfoutput>


Pretty basic. Now how would that look if ColdFusion had implicit getters and setters like Groovy:


<cfset user = new User() />
<cfset user.firstName = "Tony" />
<cfset user.lastName = "Nelson" />

<cfoutput>
Hello, my name is #user.firstName# #user.lastName#
</cfoutput>


Much cleaner. Now before you say it's just accessing the properties directly (as if they were stored in the this scope), there's more to it than that. When you're accessing the property, the call is still being routed through the implicit getters and setters. This means you can still add your own custom logic to the properties by overriding their methods.

To demonstrate, let's add a 3rd property to the User component, fullName, and add our own getter.


<cfcomponent name="User">

<cfproperty name="firstName" />
<cfproperty name="lastName" />
<cfproperty name="fullName" />

<cffunction name="getFullName">
<cfreturn getFirstName() & " " & getLastName() />
</cffunction>

</cfcomponent>


Now let's update our code:


<cfset user = new User() />
<cfset user.firstName = "Tony" />
<cfset user.lastName = "Nelson" />

<cfoutput>
Hello, my name is #user.fullName#
</cfoutput>


Even though it looks like we're accessing the fullName property directly, it will still be routed through getFullName(), which should output "Tony Nelson".

I've gone back and forth on whether or not this is a good thing. On one hand, you're calling a method even though it doesn't look like it. On the other, it's a lot cleaner. Some people may argue that access to properties should remain hidden behind Accessors (getters) and Mutators (setters), but in truth they still are.

Besides, if our goal is to have rich business objects that have both properties and behavior, shouldn't we be able to treat the properties like they were just simply properties and nothing more? To me, having a getter implies there's some additional behavior required in order to retrieve a property, which in most cases there isn't. What are we really gaining by masking the call to a property behind a method?

Monday, September 21, 2009

ColdFusion and Frameworks

If you haven't read it yet, I would suggest reading this post by Matt Woodward. Very good points.

Honestly, if ColdFusion has been around for almost 15 years, why don't we have anything that comes even remotely close to Ruby on Rails or Grails? You could make the case that ColdFusion on Wheels and ColdBox are similar in their implementations, but then there are people that swear by Model-Glue, Mach-II, and Fusebox... and then there's Edmund, FW/1, onTap, etc... and those are just the front controller frameworks. If you want the total package, you'll have to wire in ColdSpring, LightWire, Transfer, Reactor, MXUnit, cfcUnit, cfSpec, etc...

Unfortunately, the ColdFusion framework community is so fragmented that I don't think there will ever be a widely-accepted, well-designed, dominant framework - a framework that just works - a framework that some developers might actually confuse with a language, like what happens with Rails(Ruby) and Grails(Groovy). Right now there are just too many smart ColdFusion developers all working on their own versions of the same thing.

Tuesday, September 1, 2009

Annotation-based Dependency Injection using ColdSpring

ColdSpring is by far the most essential tool I need for building ColdFusion applications. However, I'm not a huge fan of writing a ton of XML and I'm certainly not a fan of writing a lot of getters and setters inside my components.

To help ease that pain, I borrowed a feature from Model-Glue 3 and extended ColdSpring to create the "beans" scope.

Little did I realize ColdSpring can already handle a lot of what I wanted to do without having to modify ColdSpring itself simply by creating a factory post processor. To automatically inject the "beans" scope into my ColdSpring-managed beans, I created the following BeansScopeFactoryPostProcessor.cfc:


<cfcomponent>

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

<cfset var local = {} />

<cfset local.beanDefs = getConcreteBeanClasses() />

<cfloop collection="#local.beanDefs#" item="local.beanName">

<cfset local.beanList = getBeanScopeList(local.beanDefs[local.beanName]) />

<cfif local.beanList neq "">

<cfset local.bean = getBeanFactory().getBean(local.beanName) />

<cfset injectBeansScope(local.bean,local.beanList) />

</cfif>

</cfloop>

</cffunction>

<cffunction name="getConcreteBeanClasses" access="private" returntype="struct">

<cfset var local = {} />

<cfset local.classes = {} />

<cfset local.beanDefs = getBeanFactory().getBeanDefinitionList() />

<cfloop collection="#local.beanDefs#" item="local.beanName">

<cfif not local.beanDefs[local.beanName].isAbstract()>
<cfset local.classes[local.beanName] = local.beanDefs[local.beanName].getBeanClass() />
</cfif>

</cfloop>

<cfreturn local.classes />

</cffunction>

<cffunction name="injectBeansScope" access="private" returntype="void">
<cfargument name="bean" required="true" />
<cfargument name="beanList" required="true" />

<cfset var local = {} />

<cfif isCFC(arguments.bean)>

<cfset local.beans = {} />

<cfloop list="#arguments.beanList#" index="local.beanName">
<cfset local.beans[local.beanName] = getBeanFactory().getBean(local.beanName) />
</cfloop>

<cfset arguments.bean.__setVariable = variables.__setVariable />

<cfset arguments.bean.__setVariable("beans",local.beans) />

<cfset StructDelete(arguments.bean,"__setVariable") />

</cfif>

</cffunction>

<cffunction name="getBeanScopeList" access="private" returntype="string">
<cfargument name="bean" required="true" />

<cfset var local = {} />

<cfset local.metaData = getComponentMetaData(arguments.bean) />

<cfset local.beanList = "" />

<cfif StructKeyExists(local.metaData,"beans")>

<cfloop list="#local.metaData.beans#" index="local.beanName">
<cfset local.beanList = ListAppend(local.beanList,local.beanName) />
</cfloop>

</cfif>

<cfset local.extendedMetaData = local.metaData />

<cfloop condition="StructKeyExists(local.extendedMetaData,'extends')">

<cfif StructKeyExists(local.extendedMetaData,"beans")>

<cfloop list="#local.extendedMetaData.beans#" index="local.beanName">

<cfif not listFindNoCase(local.beanList,local.beanName)>
<cfset local.beanList = ListAppend(local.beanList,local.beanName) />
</cfif>

</cfloop>

</cfif>

<cfset local.extendedMetaData = local.extendedMetaData.extends />

</cfloop>

<cfreturn local.beanList />

</cffunction>

<cffunction name="setBeanFactory" access="public" returntype="void">
<cfargument name="beanFactory" required="true" type="coldspring.beans.BeanFactory" />

<cfset variables.beanFactory = arguments.beanFactory />

</cffunction>

<cffunction name="getBeanFactory" access="private" returntype="any">

<cfreturn variables.beanFactory />

</cffunction>

<cffunction name="__setVariable" access="public" returntype="void">
<cfargument name="key" required="true" />
<cfargument name="value" required="true" />

<cfset variables[arguments.key] = arguments.value />

</cffunction>

<cffunction name="isCFC" access="private" returntype="boolean">
<cfargument name="object" required="true" />

<cfset var metaData = getMetaData(arguments.object) />

<cfreturn isObject(arguments.object) and structKeyExists(metaData,"type") and metaData.type eq "component" />

</cffunction>

</cfcomponent>


Long story short, it looks at all the beans defined in ColdSpring, checks their metadata for a "beans" attribute inside the cfcomponent tag, and automatically injects the requested beans into the component inside a variables.beans struct.

To get ColdSpring to process my beans, I define the factory post processor as such:


<bean id="beanInjector" class="test.BeansScopeFactoryPostProcessor" factory-post-processor="true" />


For those unaware of how factory post-processors work, ColdSpring will look for any beans where factory-post-processor="true" and automatically call postProcessBeanFactory() on those beans once the bean factory has been initialized.

To keep the code relatively small in this post, I removed any comments. Hopefully it's still somewhat straight-forward and easy to follow.

Friday, August 28, 2009

CF9 Front Controller Framework

I want to start writing a very simple front controller framework for CF9 that combines convention simplicity from ColdBox with configuration flexibility from Model-Glue, while borrowing a couple things from Rails/Grails/SpringMVC. Here's what a sample controller might look like:


component extends="Controller" {

public void function listUsers() {

var users = list("User");

render("users/list.cfm",{users=users});

}

public void function showUser() {

var user = get("User",params.userID);

render("users/show.cfm",{user=user});

}

public void function editUser() {

if(exists(flash,"user")) {
var user = flash.user;
}
else {
var user = get("User",params.userID);
}

render("users/edit.cfm",{user=user});

}

public void function saveUser() {

var user = get("User",params.userID);

populate(user,params);

var errors = validate(user);

if(!has(errors)) {

save(user);

flash.message = "User updated successfully";

redirect("showUser",{userID=user.getID()});

}
else {

flash.user = user;

flash.message = errors;

redirect("editUser",{userID=user.getID()});
}

}

public void function deleteUser() {

delete("User",params.userID);

flash.message = "User deleted successfully";

redirect("listUsers");

}

}

Saturday, August 8, 2009

How smart should an object be?

Since I'm relatively new to OOP, one thing I've been struggling with lately is how smart should I make my domain objects. Should an object know how to save itself? Take the following code for example:


user = new User();
user.setFirstName("Tony");
user.setLastName("Nelson");
user.save();


This type of code follows the Active Record design pattern, where domain objects contain your CRUD operations.

Hibernate, which acts as a Data Access Object, takes a slightly different approach. Rather than having the object save itself, Hibernate performs the CRUD.


user = entityNew("User");
user.setFirstName("Tony");
user.setLastName("Nelson");
entitySave(user);


Since Hibernate has become the ORM of choice in ColdFusion, I've decided that an object should not know how to save itself. Now with that in mind, should an object know how to populate itself? Consider the following code:


user = new User();
user.setFirstName(arguments.firstName);
user.setLastName(arguments.lastName);
user.setEmail(arguments.email);
user.setPassword(arguments.password);
user.setBirthDate(arguments.birthDate);
user.setGender(arguments.gender);
user.setHeight(arguments.height);
etc...


Pretty sure we've all seen code like this, where you're just taking all the arguments and calling their corresponding setters by adding "set" in from the argument name. Wouldn't it be a lot simple to just do:


user = entityNew("User");
user.populate(arguments);
entitySave(user);


But again, since a user shouldn't know how to save itself, it probably shouldn't know how to populate itself either, which gives us:


user = entityNew("User");
entityPopulate(user,arguments);
entitySave(user);


Mmmm... entityPopulate() would be a handy little tool. Now what about validation? Same theory applies...


user = entityNew("User");
entityPopulate(user,arguments);
errors = entityValidate(user);
if(ArrayLen(errors) == 0) {
entitySave(user);
}

Well it's short, although I'm not sure it's entirely clear what we're saving since it's so generic. But again, maybe that's a good thing. Ideally, you could get rid of all the "entity" stuff and make a really generic Service that looked like this:


user = new("User");
populate(user,arguments);
errors = validate(user);
if(ArrayLen(errors) == 0) {
save(user);
}


That almost looks as good as pseudo-code, with the only thing that's a little code-y being ArrayLen(errors). Speaking of which, there should be a generic length() function that can accept an array, query, struct, or string and return its length, regardless of data type. Man, that would be pretty sweet. Then the code would look even shorter.


user = new("User");
populate(user,arguments);
errors = validate(user);
if(length(errors) == 0) {
save(user);
}


Seems pretty clean. Just for fun, how would that look using smarter objects and a little method chaining?


user = new("User").populate(arguments);
errors = user.validate();
if(length(errors) == 0) {
user.save();
}


Not too shabby. And now you see my struggles.

Wednesday, August 5, 2009

ColdFusion 9 Wishlist

After playing around with ColdFusion 9 for a little bit, here's my wishlist for future enhancements:

• If you run entityLoadByPK("User",1) and there isn't a User with an ID of 1, it would return an empty User. Either that or add a new method, entityGet("User",1), that would accomplish the same result.

• Add cacheExists(key) that would check to see if a certain key exists in the cache. You can currently use cacheGetAllIds() to return an array of all the keys, then use arrayFind(), but that's a little tedious.

• Allow the ORM event handlers preLoad(), preInsert(), preUpdate(), and preDelete() to return a value or prevent the actual event (load/insert/update/delete) from firing. This would allow you to fetch from the cache before hitting the database a lot easier.

• Add syntax support for dynamic method invocation, similar to user["get#property#"](). You can get around this by using <cfinvoke component="#user#" method="get#property#" />, but it's so verbose.

• Add an implicit "instance" scope to components to keep instance data separate from methods, then update the implicit getters/setters to reference the instance scope. That way, if you wanted the component's instance data (aka memento), you could simply return the instance scope.

• Add support for <cfloop query="#users#" index="user"> where user would be a struct containing the current row's data.

• Add support for <cfloop collection="#users#" index="user"> rather than having to use item="user".

• When looping over a struct, it should loop the keys in alphabetical order. Yeah I know if you want a collection to maintain an order, you should probably use an array, but looping alphabetically is better than looping arbitrarily. This would come in handy when working with API's that use the OAuth protocol.

• Allow you to set the default output value to "false" for all components and methods. Not sure why the default value is "true". Doesn't make much sense.

• Allow you to select a setting to automatically trim all form/url variables. I hate trailing whitespace...

• Maintain the case of keys in structs. Kinda lame how user.name turns into user.NAME but user["name"] stays like it should.

That's all I can think of for now. And although it might appear like I'm complaining a lot, I'm actually really excited about CF9. Lots of good stuff ahead.

Tuesday, August 4, 2009

Handling JavaScript Cookies with Prototype

When dealing with cookies, it's important to be aware of certain browser limitations that might affect your user's experience. For example, Internet Explorer can only handle up to 20 cookies per domain at a time. If your site relies heavily on cookies, this could become a problem if the browser reaches its max and starts tossing cookies unexpectedly. In ColdFusion, this could mean losing your CFID and CFTOKEN cookies and getting logged out for no apparent reason.

To address this, I created a simple Cookie manager with the help of the Prototype library. The basic idea is to store multiple cookies in a single cookie using a JSON key-value pair associative array.

The methods available are:


<script type="text/javascript">
Cookie.get(key, default);
Cookie.set(key, value);
Cookie.clear(key);
Cookie.exists(key);
</script>


For example, I might have the following code to toggle the visibility of a menu:


<script type="text/javascript">
toggleMenu = function(set) {
var visible = Cookie.get('menu', true);
if (set) {
visible = !visible;
Cookie.set('menu', visible);
}
visible ? $('menu').show() : $('menu').hide();
}

Event.observe(window,'load',function(){
toggleMenu(false);
});
</script>


If I were to output the cookie, I would see it's being stored as:


{"menu": true}


If I wanted to add similar functionality to handle the visibility of a sidebar, my code might look like following:


<script type="text/javascript">
toggleItem = function(item, set) {
var visible = Cookie.get(item, true);
if (set) {
visible = !visible;
Cookie.set(item, visible);
}
visible ? $(item).show() : $(item).hide();
}

Event.observe(window, 'load', function(){
toggleItem('menu', false);
toggleItem('sidebar', false);
});
</script>


And my cookies would be stored as:


{"menu": true, "sidebar": false}


Not only is it storing multiple key-value pairs in a single cookie, but we have the added bonus of a really clean API for managing cookies. If you're curious, here's the full Cookie class.


<script type="text/javascript">
var Cookie = {

key: 'cookies',

set: function(key, value) {
var cookies = this.getCookies();
cookies[key] = value;
var src = Object.toJSON(cookies).toString();
this.setCookie(this.key, src);
},

get: function(key){
if (this.exists(key)) {
var cookies = this.getCookies();
return cookies[key];
}
if (arguments.length == 2) {
return arguments[1];
}
return;
},

exists: function(key){
return key in this.getCookies();
},

clear: function(key){
var cookies = this.getCookies();
delete cookies[key];
var src = Object.toJSON(cookies).toString();
this.setCookie(this.key, src);
},

getCookies: function() {
return this.hasCookie(this.key) ? this.getCookie(this.key).evalJSON() : {};
},

hasCookie: function(key) {
return this.getCookie(key) != null;
},

setCookie: function(key,value) {
var expires = new Date();
expires.setTime(expires.getTime()+1000*60*60*24*365)
document.cookie = key+'='+escape(value)+'; expires='+expires+'; path=/';
},

getCookie: function(key) {
var cookie = key+'=';
var array = document.cookie.split(';');
for (var i = 0; i < array.length; i++) {
var c = array[i];
while (c.charAt(0) == ' '){
c = c.substring(1, c.length);
}
if (c.indexOf(cookie) == 0) {
var result = c.substring(cookie.length, c.length);
return unescape(result);
};
}
return null;
}
}
</script>


Enjoy!

Monday, August 3, 2009

ColdFusion 9 ORM Event Handlers

Included with Hibernate is a set of event handlers that can be invoked when loading, inserting, updating, and deleting records from Hibernate. My first thought was to use these event handlers as interceptors to pull data from the cache rather than hitting the database. After further review, it appears as if this isn't possible since all of the event handlers return void. Bummer. On the plus side, you can still use the event handlers to put data into the cache, just not pull it out.

Sunday, August 2, 2009

Hibernate, ColdFusion 9, and DAOs

It's been a long weekend, so this post might seem a little scattered and more-or-less random thoughts. Bear with me.

Since Hibernate gives us a consistent interface for data access, most of the Data Access Objects (DAOs) that we write in CF9 will/should look pretty similar. With that in mind, I've been playing around with creating a generic Data Access Object using simple code generation that could be used and decorated by all persistent objects in the application.

The general idea is that all DAOs are generated by a DAO Factory using a skeleton CFC as a template and basic keyword replacement. The factory would then write the DAOs to disk, where the newly created components would be instantiated and returned to ColdSpring as part of a factory-method.

Here's what my ColdSpring definition looks like:


<beans>

<bean id="userService" class="app.services.UserService" />

<bean id="userDAO" factory-bean="daoFactory" factory-method="getUserDAO" />

<bean id="daoFactory" class="utils.orm.DAOFactory">
<constructor-arg name="relativePath">
<value>/app/proxy/dao</value>
</constructor-arg>
</bean>

</beans>


You can even extend the generated DAO by defining the bean like this:


<bean id="userDAO" factory-bean="daoFactory" factory-method="getUserDAO">
<constructor-arg name="extends">
<value>app.model.user.UserDAO</value>
</constructor-arg>
</bean>



The factory would use the onMissingMethod event handler to determine the correct entity to load by stripping "get" and "DAO" from the factory-method being called. In this case, the entity would be "User". The factory would then do really simple text replacement in the DAO template CFC to generic the UserDAO.

Here's what my DAO template looks like:


<cfcomponent>

<cffunction name="get" access="public" output="false" returntype="any">
<cfargument name="id" required="true" />

<cfreturn EntityLoadByPK("${Entity}",arguments.id) />

</cffunction>

<cffunction name="save" access="public" output="false" returntype="void">
<cfargument name="${entity}" required="true" />

<cfset EntitySave(arguments.${entity}) />

</cffunction>

<cffunction name="delete" access="public" output="false" returntype="void">
<cfargument name="${entity}" required="true" />

<cfset EntityDelete(arguments.${entity}) />

</cffunction>

</cfcomponent>


And here's what the final UserDAO looks like:


<cfcomponent>

<cffunction name="get" access="public" output="false" returntype="any">
<cfargument name="id" required="true" />

<cfreturn EntityLoadByPK("User",arguments.id) />

</cffunction>

<cffunction name="save" access="public" output="false" returntype="void">
<cfargument name="user" required="true" />

<cfset EntitySave(arguments.user) />

</cffunction>

<cffunction name="delete" access="public" output="false" returntype="void">
<cfargument name="user" required="true" />

<cfset EntityDelete(arguments.user) />

</cffunction>

</cfcomponent>


Hopefully that makes some sense. I haven't fully tested everything yet, but the theory is there. The next step would be to update the DAO template to add some more complex logic, like adding validation and leveraging CF9's new caching enhancements. Granted you could always use ColdSpring's AOP to add caching, but that's a different topic.

If anyone is interested in seeing more of the code, let me know.

Thursday, July 23, 2009

Looping in ColdFusion

I really wish you could loop an array, query and struct in a consistent manner in ColdFusion. In truth, you're just iterating over a collection of data. Why does it need to be so different? For example, here's looping over a collection of Users and retrieving a single User record.


<cfloop array="#users#" index="user">

</cfloop>



<cfloop collection="#users#" item="i">
<cfset user = users[i] />
</cfloop>



<cfloop query="users">
<!--- convert the row into a struct in order to access all the user's data in a single collection --->
<cfset user = {} />
<cfloop list="#users.columnList#" index="i">
<cfset user[i] = users[i][currentRow] />
</cfloop>
</cfloop>


I really wish I could do...


<cfloop collection="#users#" index="user">

</cfloop>



<cfloop query="#users#" index="user">

</cfloop>


... but that might make too much sense.

Seriously, someone just needs to take all the nice parts of ColdFusion and re-write the damn language. It would make me so much happier. Also, don't get me started on the lack of #'s in cfloop query.

Thursday, June 18, 2009

ColdFusion Inconsistencies

One of my biggest pet peeves in programming is inconsistency. I absolutely hate it when things aren't consistent, especially when it's something really, really simple.

ColdFusion 9 is going to include some new functionality for dealing with spreadsheets. While this should be a huge win for ColdFusion, I'm a little disappointed in the implementation. Why? Let's take a look at the tags:


<cfspreadsheet action="read" src="c:\Sales.xls" query="sales" />


Hmmm... that looks pretty similar to...


<cffile action="read" file="c:\Sales.xls" variable="sales" />


But they changed "file" to "src" and "variable" to "query". Odd. That also looks pretty similar to...


<cffile action="write" file="c:\Sales.xls" output="#sales#" />


Well at least that's a little consistent. And the spreadsheet equivalent...


<cfspreadsheet action="write" filename="c:\Sales.xls" name="sales" />


What? Now it's "filename"? And now they're using a "name" attribute instead of the "variable" attribute? Huh? Why?

Just for fun, let's check out one last cffile action...


<cffile action="copy" source="Sales.xls" destination="Sales2.xls" />


Now they're using "source" again, but this time spelled out.

Honestly, am I the only person that's bothered by this?