Showing posts with label coldfusion. Show all posts
Showing posts with label coldfusion. Show all posts

Thursday, April 28, 2011

Advanced INI Parsing in ColdFusion

At work, I've been using the Zend Framework for PHP a lot. One of the best features in my opinion is how you're able to manage your configuration settings between different environments within a project.

I've always been a fan of using INI files for storing configuration settings, but the Zend_Config_Ini class that Zend Framework comes with takes INI files to a whole new level by adding section inheritance and nested properties.

After a couple hours, I was able to take the same powerful functionality that Zend Framework provides and port it to ColdFusion, mainly for the purpose of adding it to my ColdMVC framework.

Why do I think this is cool? Let's look at a sample INI file that you might see on a project:


; Settings for the production environment
[production]
autoReload = false
development = false
reminderService.sendReminders = true
facebook.api.username = prod@mycompany.com
facebook.api.password = 90ujlc890$f

; Settings for the staging environment
[staging : production]
reminderService.sendReminders = false
facebook.api.username = staging@mycompany.com
facebook.api.password = 879kjasdf!

; Basic settings for the development environment.
; Each developer should create their own environment that extends this one.
[development : staging]
development = true
autoReload = true

; Settings for Tony's development environment
[development-tony : development]
emailService.options.forceTo = [ "tony@mycompany.com", "tony@gmail.com" ]
facebook.api.username = tony@mycompany.com
facebook.api.password = w1nn1ng

; Settings for Ryan's development environment
[development-ryan : development]
emailService.options.forceTo = ryan@mycompany.com
facebook.api.username = ryan@mycompany.com
facebook.api.password = govikes

; Settings for Joe's development environment
[development-joe : development]
emailService.options.forceTo = joe@mycompany.com
facebook.api.username = joe@mycompany.com
facebook.api.password = welcome


Pretty straightforward settings file. Each environment has its own section, but now it comes with a twist.

See the colons in the section names? That's inheritance in action. If you look at the development-tony environment, you'll see that it extends the development environment, which extends the staging environment, which extends the production environment.

Also, notice the periods in the property names. Those are nested properties, which will be automatically converted to ColdFusion structs at runtime.

So how does this work? Pretty simple:


var ini = new Ini("/path/to/config.ini");
var config = ini.getSection("development-tnelson");


If I were to now dump the config variable that was returned, I'd see the following output:



Pretty sweet if you ask me.

You can find all of the code on GitHub at https://github.com/tonynelson19/ini/, which also includes 26 green unit tests.

Also, I've included the new INI parser in the latest version of ColdMVC, which you should definitely check out if you haven't yet.

Monday, February 7, 2011

Don't Mix Tags and Script in the Same Component

Every now and then, I'll see some code in an open source ColdFusion project that mixes tag syntax and script syntax in the same component. Personally, I find it really annoying when people jump in and out of cfscript blocks. If the component isn't written entirely in script, then don't use any script - stick with tags.

Some people think that since writing in script is cleaner, it's cleaner to write part of the function in script than write it using tags. However, if you're declaring your component and functions in tags, switching to script inside the function actually decreases your code readability in my opinion. Take the following hypothetical example, which is sadly all-too-common in certain open source projects:


<cffunction name="setFoo" access="public" output="false" returntype="void">
<cfargument name="foo" required="true" type="string" />

<cfscript>
variables.foo = arguments.foo;
</cfscript>


</cffunction>

<cffunction name="getFoo" access="public" output="false" returntype="string">

<cfscript>
return variables.foo;
</cfscript>

</cffunction>


I find this style of coding ridiculous.

Saturday, January 8, 2011

Using Markdown in ColdFusion

Markdown is "a text-to-HTML conversion tool for web writers. Markdown allows you to write using an easy-to-read, easy-to-write plain text format, then convert it to structurally valid XHTML (or HTML)."

Here's a quick post describing how you can use Markdown in ColdFusion.

First, download MarkdownJ, a Java port of the Markdown conversion utility originally written in Perl. Here's a direct link to the download site: http://code.google.com/p/markdownj/.

Next, either put the markdownj.jar file in the ColdFusion classpath or download JavaLoader. I prefer using JavaLoader, so that's what my example will show.

Finally, some code:


<cfset paths = [ expandPath("markdownj-1.0.2b4-0.3.0.jar") ] />

<cfset javaLoader = new javaloader.JavaLoader(paths, true) />

<cfset markdownProcessor = javaLoader.create("com.petebevin.markdown.MarkdownProcessor").init() />

<cfset html = markdownProcessor.markdown("This is a *simple* example") />

<cfoutput>
#html#
</cfoutput>


The above code will produce the following HTML output:


<p>This is a <em>simple</em> example</p>


OK. So if that's the simple example, what about a more complex example?


# This is an h1

## This is an h2

This is a normal paragraph. Lorem ipsum dolor sit amet,
consectetur adipisicing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua.

* This is a unordered list. Hanging indents allow
you to wrap a list item onto multiple lines
* List item 2
* List item 3

---
These are horizontal rules
***

This is how to *italicize* something. This is _another way_ to italicize something.

This is how to **bold** something. This is __another way__ to bold something.

1. This is an ordered list
2. List item 2
3. List item 3

This is [an example](http://example.com/ "Title") inline link.

[This link](http://example.net/) has no title attribute.

> This is a block quote. Lorem ipsum dolor sit amet,
> consectetur adipisicing elit, sed do eiusmod tempor
> incididunt ut labore et dolore magna aliqua.

This is a code block.
To produce a code block in Markdown, simply
indent every line of the block by at least
4 spaces or 1 tab



And that's the basics of Markdown. Here's a full set of syntax rules: http://daringfireball.net/projects/markdown/syntax. Pretty sweet if you ask me.

Monday, August 16, 2010

Thoughts on Property Getters/Setters in ColdFusion

In ColdFusion 9, you can have ColdFusion automatically generate getters and setters for your properties by adding @accessors true to the component metadata. For example, the following two code snippets are practically identical.


component {
public string function getFirstName() {
return variables.firstName;
}

public void function setFirstName(required string firstName) {
variables.firstName = arguments.firstName;
}
}



/**
* @accessors true
*/
component {
property firstName;
}


Nice. That's a lot less code. Now what happens if you have a business rule where you always need the first name to be capitalized. I know it's not the best real world example, but it's straightforward. Simple enough, just override the generated getter by defining your own getFirstName method.


/**
* @accessors true
*/
component {
property firstName;

public string function getFirstName() {
return ucase(variables.firstName);
}
}


Done. While that works and is the correct way of overriding the getter, it doesn't quite feel cohesive enough to me since the getter isn't visually tied directly to the property. I think it would be better if we were able to define the getters and setters as part of the property, similar to how other languages do it.


/**
* @accessors true
*/
component {

property firstName {
get: function() {
return ucase(variables.firstName);
}
}

}


Obviously this is just hypothetical syntax, but I think it reads a lot better.

Wednesday, June 16, 2010

ColdFusion 9 Bug #82955

Awhile ago I submitted a bug for ColdFusion 9. Here's the description from the bug tracker: Error when calling a function with an implicit struct argument containing a complex value inside a loop. It's a little hard to explain exactly what's going on in words, but here's some sample code that can reproduce the error.

<!--- To test, remove the comments from around each scenario --->

<!--- create a simple array of users --->
<cfset users = [] />
<cfset users[1] = { name = "Tony" } />
<cfset users[2] = { name = "Joe" } />

<cfoutput>
<!---
Scenario 1: Pass in the user. This should work. <br />
<cfloop array="#users#" index="user">
#sayHelloSimple(user)# <br />
</cfloop>
--->

<!---
Scenario 2: Pass in a struct containing the user. This should work. <br />
<cfloop array="#users#" index="user">
<cfset parameters = { user = user } />
#sayHelloComplex(parameters)# <br />
</cfloop>
--->

<!---
Scenario 3: Pass in an implicit struct containing the user. This should work, but doesn't. <br />
<cfloop array="#users#" index="user">
#sayHelloComplex({ user = user })# <br />
</cfloop>
--->

<!---
Scenario 4: Same as Scenario 4, but wrapped inside a try/catch. This should print "fail". <br />
<cfloop array="#users#" index="user">
<cftry>
#sayHelloComplex({ user = user })# <br />
<cfcatch type="any">
fail <br />
</cfcatch>
</cftry>
</cfloop>
--->

<!---
Scenario 5: Pass in a single implicit struct containing the user without looping. This should work. <br />
#sayHelloComplex({ user = users[1] })# <br />
--->

<!---
Scenario 6: Pass in an implicit struct containing the user inside an include. This should work. <br />
<cfloop array="#users#" index="user">
<cfinclude template="user.cfm" />
</cfloop>
--->

<!---
content of user.cfm
<cfoutput>
#sayHelloComplex({ user = user })# <br />
</cfoutput>
--->

</cfoutput>

<cffunction name="sayHelloSimple" returntype="string">
<cfargument name="user" type="any" />

<cfreturn "Hello, #arguments.user.name#" />

</cffunction>

<cffunction name="sayHelloComplex" returntype="string">
<cfargument name="collection" type="any" />

<cfreturn "Hello, #arguments.collection.user.name#" />

</cffunction>


I should note that the complex value doesn't need to be a struct, but it can be an object as well. If you've got a free second, do me a favor and vote it up! http://cfbugs.adobe.com/cfbugreport/flexbugui/cfbugtracker/main.html#bugId=82955.

Also, ColdMVC is now on RIAForge, so check that out too if you haven't yet: http://coldmvc.riaforge.org/.

Monday, March 1, 2010

ColdFusion 9 40% Faster? I doubt it...

Adobe recently released a performance brief claiming ColdFusion 9 is 40% faster than ColdFusion 8. While that number looks really good at first glance, Marc Ackermann was kind enough to point out that they were running ColdFusion 8 using the 1.6.0_04 version of Java, which has a known class loader bug.

While I don't doubt that ColdFusion 9 is faster than ColdFusion 8, I don't trust any of the numbers from the performance brief, especially a 700% improvement in CFC object creation.

Saturday, February 20, 2010

Another ColdFusion Framework?

Over the past couple weeks, I've spent some free time creating a simple MVC Front Controller framework for ColdFusion 9. What? Another framework for ColdFusion? Why would you want to do such a thing? While there are plenty of solid frameworks out there to choose from, none of them really showcase the power and elegance of ColdFusion 9 (and specifically ORM). Besides, it was fun to write.

While the framework is loosely based on Grails and Ruby on Rails, I've borrowed inspiration and concepts from other ColdFusion (ColdBox, ColdFusion on Wheels, Framework One, Mach-II, Model-Glue, QuickSilver) and non-ColdFusion (Spring, jQuery, Swiz) frameworks, as well as incorporated some of my own tips and tricks, too. Best of all, it's powered by ColdFusion 9, Hibernate, and ColdSpring.

Without going into too many specifics, here are some key concepts in the framework:
* convention over configuration
* MVC design pattern
* automatic creation of controller beans
* implicit invocation of controller actions
* centralized event dispatching
* metadata-driven AOP
* dynamic finders
* global helpers available using $
* implicit rendering of views and layouts
* helper tags and method plugins for views
* form data binding
* params and flash scopes

Finally, here's a sample application I created called UserDirectory, which performs your basic user CRUD.



Application.cfc

/**
* @extends coldmvc.Application
*/
component {

}


config/settings.ini

[default]
controller=users

[development]
development=true


app/controllers/UserController.cfc

/**
* @action list
* @extends coldmvc.Controller
*/
component {

function list() {

var paging = $.paging.options();

var options = {
sort = "firstName",
order = "asc",
max = paging.max,
offset = paging.offset
};

var search = $.params.get("search");

if (search != "") {
params.users = _User.findAllByFirstNameLikeOrLastNameLike(search, search, options);
params.count = _User.countByFirstNameLikeOrLastNameLike(search, search);
}
else {
params.users = _User.list(options);
params.count = _User.count();
}

}

function edit() {

var userID = $.params.get("userID");
params.user = _User.get(userID);

}

function save() {

var user = _User.get(params.user.id);
user.populate(params.user);
user.save();

flash.message = "User saved successfully";
redirect("edit", "userID=#user.id()#");

}

function delete() {

var user = _User.get(params.userID);
user.delete();

flash.message = "User deleted successfully";
redirect("list");

}

}


app/model/User.cfc

/**
* @extends coldmvc.Model
* @persistent true
*/
component {

property id;
property firstName;
property lastName;
property email;

}


app/views/users/list.cfm

<cfoutput>
<form>
Search: <input name="search" value="#search#" wrapper="false" />
</form>

<table label="Users">
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
<th>Edit</th>
<th>Delete</th>
</tr>
<each in="#users#" value="user" index="i">
<tr index="#i#">
<td>#user.firstName()#</td>
<td>#user.lastName()#</td>
<td>#user.email()#</td>
<td><a href="#linkTo('edit','userID=#user.id()#')#">Edit</a></td>
<td><a href="#linkTo('delete','userID=#user.id()#')#">Delete</a></td>
</tr>
</each>
<cfif users.size() eq 0>
<tr>
<td colspan="3">No users have been added yet</td>
</tr>
</cfif>
</table>
<paging records="#count#" />
<a href="#linkTo('edit')#">Add a User</a>
</cfoutput>


app/views/users/edit.cfm

<cfoutput>
<fieldset label="User Information">
<form action="save" bind="user">
<hidden name="id" />
<input name="firstName" />
<input name="lastName" />
<input name="email" />
<submit />
<cfif user.exists()>
<a href="#linkTo('delete','userID=#user.id()#')#">Delete</a>
</cfif>
<a href="#linkTo('list')#">Back to List</a>
</form>
</fieldset>
</cfoutput>


app/layouts/users.cfm

<cfoutput>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="en-us" xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>User Directory</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
#renderCSS("reset.css")#
#renderCSS("style.css")#
#renderJS("jquery.1.4.2.js")#
</head>
<body>
<cfif structKeyExists(params, "message")>
<div class="flash">
#params.message#
</div>
</cfif>
<div class="content">
#render()#
</div>
</body>
</html>
</cfoutput>


If you couldn't tell from the code, I've named the framework ColdMVC. Pretty boring, right? If someone has a better idea, I'm open for suggestions. I've written a handful of small sample applications with the framework already and, to be honest, it's made programming fun again. Plus, writing everything in cfscript syntax makes ColdFusion finally feel like a real scripting language.

Eventually I'd like to release the framework as open source, but I haven't had the time to make it happen yet. Plus there are a couple more features I'd like to finalize first, like validation and more robust route mapping. In the meantime, if anyone is interested in learning more or seeing some more code, shoot me an email or feel free to track me down at cf.Objective() in a couple weeks.

Saturday, February 13, 2010

Using Custom Tags to Render HTML Elements

I'm a big fan of using custom tags to render form fields. This isn't a particularly new idea, as Grails, Ruby on Rails, CFWheels, and Mach-II (to name a few) all have similar features. However, each has their own slightly different syntax for how the tags are called. Depending on the language/framework, you could use <g:textField />, <%= text_field %>, text_field(), textField(), or <form:input />.

So which would I choose? Personally I prefer keeping the tags as tags, so text_field() and textField() are out. And since we're using ColdFusion, we'll need to throw out the Rails syntax too. That leaves us with <g:textField /> and <form:input />. I don't know about you, but I like to write as little code as possible. So if given the choice, I would probably combine the two and use <g:input />.

While the g prefix works well for Grails, it doesn't really apply to ColdFusion. So naturally I tried using a cf prefix.


<cfimport prefix="cf" taglib="tags/" />

<cf:input type="text" name="firstName" value="Tony" />


ColdFusion didn't like this very much. Here's the ever-so-friendly error message: A tag starting with 'CF' has been detected. This tag is not supported by this version of ColdFusion. Please verify your typo and try again. Unknown tag: cfmodule.

So I decided to look up the valid naming rules for the cfimport tag. While doing so, I stumbled upon this gem: If you import a CFML custom tag directory and specify an empty value, "", for this attribute, you can call the custom tags without using a prefix. Intrigued, I tested it out.


<cfimport prefix="" taglib="tags/" />

<input type="text" name="firstName" value="Tony" />


Sure enough, ColdFusion sent the call to /tags/input.cfm, which looks like this:


<cfoutput>
<cfif thisTag.executionMode eq "start">
<cfset html = [] />
<cfset html.add("input") />
<cfloop collection="#attributes#" item="attribute">
<cfset html.add('#lcase(attribute)#="#attributes[attribute]#"') />
</cfloop>
<cfelse>
<#arrayToList(html, " ")# />
</cfif>
</cfoutput>


I'll admit this isn't the most intuitive solution, since it appears as if you're rendering a normal HTML element. However, if used correctly, this could be extremely powerful. For starters, you could automatically wrap all dynamic values in htmlEditFormat(). You could also set the "id" attribute to the "name" attribute if it wasn't specified, set a default "title" attribute, wrap all the fields with an appropriate label, or even add in your own custom attributes. Better yet, you could create your own "HTML" elements with their own custom behavior. Take the following hypothetical form for example:


<form controller="users" action="update" bind="user">
<hidden name="id" />
<input name="firstName" />
<input name="lastName" />
<email name="email" />
<date name="birthDate" />
<phone name="phoneNumber" />
<address name="homeAddress" />
<radio name="gender" options="Male,Female" />
<checkbox name="subscribeToNewsletter" />
<submit label="Update Account" />
</form>


Yeah it might take some time getting used to, but I think the power might outweigh the initial learning curve in the long run.

Friday, February 12, 2010

Underlying Java Methods for ColdFusion Data Types

Every now and then in blog posts I'll see people using Java methods rather than ColdFusion functions in their code examples. For instance, they might use <cfset users.add("Tony") /> rather than the typical <cfset arrayAppend(users, "Tony") />. I never gave it much thought before, since I didn't know what all was available. Today I decided to spend a little time playing around with the underlying Java methods available for different ColdFusion data types. I came up with the following script that shows all the available methods.


<cfset dataTypes = {} />
<cfset dataTypes["array"] = [] />
<cfset dataTypes["boolean"] = true />
<cfset dataTypes["date"] = now() />
<cfset dataTypes["integer"] = 1 />
<cfset dataTypes["numeric"] = 1.5 />
<cfset dataTypes["query"] = queryNew("id") />
<cfset dataTypes["string"] = "" />
<cfset dataTypes["struct"] = {} />

<cfset metaData = {} />

<cfloop collection="#dataTypes#" item="dataType">

<cfset metaData[dataType] = {} />
<cfset metaData[dataType].class = dataTypes[dataType].getClass().toString() />
<cfset metaData[dataType].methods = {} />

<cfset classMethods = dataTypes[dataType].getClass().getMethods() />

<cfloop array="#classMethods#" index="classMethod">

<cfset method = {} />
<cfset method.string = classMethod.toString() />
<cfset method.name = listLast(listFirst(method.string, "("), ".")/>

<cfset method.parameters = listToArray(listFirst(listLast(method.string, "("), ")")) />

<cfloop from="1" to="#arrayLen(method.parameters)#" index="i">
<cfset method.parameters[i] = listLast(method.parameters[i], ".") />
</cfloop>

<cfset method.returnType = listToArray(listFirst(method.string, "("), " ") />
<cfset method.returnType = listLast(method.returnType[arrayLen(method.returnType)-1], ".") />

<cfset metaData[dataType].methods[method.name & "(" & arrayToList(method.parameters) & ")"] = method />

</cfloop>

</cfloop>


If you were to <cfdump var="#metaData#" />, you'd get something that looks like this:



I haven't spent too much time playing around with all methods, but let's take a look at a couple examples:

Check to see if a start date is before an end date:

<cfset startDate = now() />
<cfset endDate = dateAdd("d", 1, startDate) />

<cfif dateCompare(startDate, endDate) eq -1>
The start date is before the end date.
<cfelse>
The start date is not before the end date.
</cfif>


Same thing, but using Java:

<cfset startDate = now() />
<cfset endDate = dateAdd("d", 1, startDate) />

<cfif startDate.before(endDate)>
The start date is before the end date.
<cfelse>
The start date is not before the end date.
</cfif>


Check to see if a name ends with "Nelson":

<cfset name = "Tony Nelson" />

<cfif right(name, 6) eq "Nelson">
The name ends with "Nelson".
<cfelse>
The name does not end with "Nelson".
</cfif>


Same thing, but using Java:

<cfset name = "Tony Nelson" />

<cfif name.endsWith("Nelson")>
The name ends with "Nelson".
<cfelse>
The name does not end with "Nelson".
</cfif>


Get the number of elements in a struct:

<cfset states = {} />
<cfset states["MN"] = "Minnesota" />
<cfset states["ND"] = "North Dakota" />

<cfoutput>
#structCount(states)#
</cfoutput>


Same thing, but using Java:

<cfset states = {} />
<cfset states["MN"] = "Minnesota" />
<cfset states["ND"] = "North Dakota" />

<cfoutput>
#states.size()#
</cfoutput>


Append the items of one array onto another array:

<cfset states = [] />
<cfset states[1] = "Minnesota" />
<cfset states[2] = "North Dakota" />

<cfset newStates = [] />
<cfset newStates[1] = "South Dakota" />

<cfloop array="#newStates#" index="state">
<cfset arrayAppend(states, state) />
</cfloop>


Same thing, but using Java:

<cfset states = [] />
<cfset states[1] = "Minnesota" />
<cfset states[2] = "North Dakota" />

<cfset newStates = [] />
<cfset newStates[1] = "South Dakota" />

<cfset states.addAll(newStates) />


Granted the differences are all pretty minor, yet the Java examples all read better to me.

Tuesday, January 12, 2010

Consistent Looping in ColdFusion

I've said this before, but I hate how inconsistent looping is in ColdFusion. And to make matters worse, ColdFusion 9 script syntax doesn't support looping over arrays using for...in statements. Why not?

I decided to try to ease my pain by creating a custom tag to loop over data in a consistent way. Here's what I came up with.

Usage

<cfimport prefix="for" taglib="/com/tags" />

<for:each key="key" in="#data#" value="value" index="i">
... stuff ...
</for:each>


Examples

<cfimport prefix="for" taglib="/com/tags" />

<cfoutput>
Array

<cfset people = [] />
<cfset people[1] = "LeBron James" />
<cfset people[2] = "Dwyane Wade" />
<cfset people[3] = "Kobe Bryant" />

<for:each in="#people#">
Hello, my name is #it#
</for:each>

Struct

<cfset people = {} />
<cfset people["LeBronJames"] = "LeBron James" />
<cfset people["DwyaneWade"] = "Dwyane Wade" />
<cfset people["KobeBryant"] = "Kobe Bryant" />

<for:each in="#people#" value="name">
Hello, my name is #name#
</for:each>

List

<cfset people = "LeBron James,Dwyane Wade,Kobe Bryant" />

<for:each in="#people#">
Hello, my name is #it#
</for:each>

Query

<cfset people = queryNew("firstName,lastName") />

<cfset queryAddRow(people) />
<cfset querySetCell(people, "firstName", "LeBron") />
<cfset querySetCell(people, "lastName", "James") />

<cfset queryAddRow(people) />
<cfset querySetCell(people, "firstName", "Dwyane") />
<cfset querySetCell(people, "lastName", "Wade") />

<cfset queryAddRow(people) />
<cfset querySetCell(people, "firstName", "Kobe") />
<cfset querySetCell(people, "lastName", "Bryant") />

<for:each in="#people#" value="person">
Hello, my name is #person.firstName# #person.lastName#
</for:each>

Nested Array

<cfset people = [] />
<cfset people[1] = {firstName="LeBron", lastName="James"} />
<cfset people[2] = {firstName="Dwyane", lastName="Wade"} />
<cfset people[3] = {firstName="Kobe", lastName="Bryant"} />

<for:each in="#people#" value="person">
Hello, my name is #person.firstName# #person.lastName#
</for:each>

Nested Struct

<cfset people = {} />

<cfset people["LeBronJames"] = {firstName="LeBron", lastName="James"} />
<cfset people["DwyaneWade"] = {firstName="Dwyane", lastName="Wade"} />
<cfset people["KobeBryant"] = {firstName="Kobe", lastName="Bryant"} />

<for:each in="#people#" value="person">
Hello, my name is #person.firstName# #person.lastName#
</for:each>
</cfoutput>


And here's the code
/com/tags/for/each.cfm

<cfif thisTag.executionMode eq "start">

<cfparam name="attributes.value" default="it" />
<cfparam name="attributes.in" default="" />
<cfparam name="attributes.start" default="1" />
<cfparam name="attributes.delimeter" default="," />

<cfset attributes.type = getType(attributes.in) />
<cfset attributes.length = getLength(attributes.in, attributes.type, attributes.delimeter) />

<cfif not structKeyExists(attributes, "end")>
<cfset attributes.end = attributes.length />
</cfif>

<cfif attributes.length>
<cfset processLoop(attributes) />
<cfelse>
<cfexit method="exittag" />
</cfif>

<cfset content = [] />

<cfelse>

<cfset arrayAppend(content, thisTag.generatedContent) />

<cfset thisTag.generatedContent = "" />

<cfset attributes.start++ />

<cfif attributes.start lte attributes.end>
<cfset processLoop(attributes) />
<cfexit method="loop" />
</cfif>

<cfoutput>
#arrayToList(content, "")#
</cfoutput>

</cfif>

<cffunction name="processLoop" access="private" output="false" returntype="void">
<cfargument name="attributes" required="true" type="struct" />

<cfif structKeyExists(attributes, "index")>
<cfset caller[attributes.index] = attributes.start />
</cfif>

<cfif structKeyExists(attributes, "key")>
<cfset caller[attributes.key] = getKey(attributes.in, attributes.type, attributes.delimeter, attributes.start) />
</cfif>

<cfif structKeyExists(attributes, "value")>
<cfset caller[attributes.value] = getValue(attributes.in, attributes.type, attributes.delimeter, attributes.start) />
</cfif>

</cffunction>

<cffunction name="getType" access="private" output="false" returntype="string">
<cfargument name="data" required="true" type="any" />

<cfif isArray(arguments.data)>
<cfreturn "array" />
<cfelseif isStruct(arguments.data)>
<cfreturn "struct" />
<cfelseif isQuery(arguments.data)>
<cfreturn "query" />
<cfelse>
<cfreturn "string" />
</cfif>

</cffunction>

<cffunction name="getLength" access="private" output="false" returntype="numeric">
<cfargument name="data" required="true" type="any" />
<cfargument name="type" required="true" type="string" />
<cfargument name="delimeter" required="true" type="string" />

<cfswitch expression="#arguments.type#">

<cfcase value="array">
<cfreturn arrayLen(arguments.data) />
</cfcase>

<cfcase value="struct">
<cfreturn structCount(arguments.data) />
</cfcase>

<cfcase value="query">
<cfreturn arguments.data.recordCount />
</cfcase>

<cfcase value="string">
<cfreturn listLen(arguments.data, arguments.delimeter) />
</cfcase>

</cfswitch>

</cffunction>

<cffunction name="getKey" access="private" output="false" returntype="string">
<cfargument name="data" required="true" type="any" />
<cfargument name="type" required="true" type="string" />
<cfargument name="delimeter" required="true" type="string" />
<cfargument name="index" required="true" type="numeric" />

<cfset var result = "" />

<cfset var i = "" />

<cfswitch expression="#arguments.type#">

<cfcase value="array">
<cfset result = arguments.index />
</cfcase>

<cfcase value="struct">
<cfset result = listGetAt(listSort(structKeyList(arguments.data), "text"), arguments.index) />
</cfcase>

<cfcase value="query">
<cfset result = arguments.index />
</cfcase>

<cfcase value="string">
<cfset result = listGetAt(arguments.data, arguments.index, arguments.delimeter) />
</cfcase>

</cfswitch>

<cfreturn result />

</cffunction>

<cffunction name="getValue" access="private" output="false" returntype="any">
<cfargument name="data" required="true" type="any" />
<cfargument name="type" required="true" type="string" />
<cfargument name="delimeter" required="true" type="string" />
<cfargument name="index" required="true" type="numeric" />

<cfset var result = "" />
<cfset var i = "" />

<cfswitch expression="#arguments.type#">

<cfcase value="array">
<cfset result = arguments.data[arguments.index] />
</cfcase>

<cfcase value="struct">
<cfset result = arguments.data[listGetAt(listSort(structKeyList(arguments.data), "text"), arguments.index)] />
</cfcase>

<cfcase value="query">
<cfset result = {} />
<cfloop list="#arguments.data.columnList#" index="i">
<cfset result[i] = arguments.data[i][arguments.index] />
</cfloop>
</cfcase>

<cfcase value="string">
<cfset result = listGetAt(arguments.data, arguments.index, arguments.delimeter) />
</cfcase>

</cfswitch>

<cfreturn result />

</cffunction>


It's been quite a while since I've used custom tags, so the code could probably be better, but it seems to do the job.

Thursday, January 7, 2010

Using a ColdSpring Post Processor to add "Static" Domain Classes

This is a quick follow-up to an idea I had created by one of Dan Vega's latest posts.

If your application has Products, you might have a ProductController that looks something like this:


component {

property productService;

public array function list() {
return getProductService().listProducts();
}

}


Then you might have a ProductService that might look like:


component {

property dao;

public array function listProducts() {
return getDAO().list("Product");
}

}


And finally you might have a generic DAO that looks like this:


component {

public array function list(string entityName) {
return entityLoad(entityName);
}

}


And now you've got proper encapsulation and separation of concerns with loosely couple components, which is good. However, you also have several components that merely delegate to other components.

In Grails and Rails, you can access your data through static methods on your domain classes. So rather than having the full stack of components, you could simply call Product.list() inside your controller.

I wanted to do something similar in ColdFusion.

First, I added a DomainClassInjector factory post processor to ColdSpring. It accepts an array of suffixes as well as the path to a generic domain class.


<bean id="domainClassInjector" class="com.utils.DomainClassInjector" factory-post-processor="true">
<property name="suffixes">
<list>
<value>Controller</value>
</list>
</property>
<property name="classPath">
<value>com.utils.DomainClass</value>
</property>
</bean>


Here's the DomainClassInjector.cfc

component accessors="true" {

property suffixes;
property classPath;

public any function init() {

variables.domainClasses = {};
variables.entityNames = ormGetSessionFactory().getAllClassMetaData();

return this;

}

public void function postProcessBeanFactory(required any beanFactory) {

var i = "";

local.beanDefinitions = arguments.beanFactory.getBeanDefinitionList();

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

local.suffix = variables.suffixes[i];

local.length = len(local.suffix);

for (local.beanName in local.beanDefinitions) {

if (right(local.beanName, local.length) == local.suffix) {

local.bean = arguments.beanFactory.getBean(local.beanName);

for (local.entityName in variables.entityNames) {

if (structKeyExists(local.bean, "set#local.entityName#")) {

if (structKeyExists(variables.domainClasses, local.entityName)) {
local.domainClass = variables.domainClasses[local.entityName];
}
else {
local.domainClass = createObject("component", variables.classPath);
local.domainClass.setEntityName(local.entityName);
variables.domainClasses[local.entityName] = local.domainClass;
}

evaluate("local.bean.set#local.entityName#(local.domainClass)");

}

}

}

}

}

}

}


And here's the DomainClass.cfc

component accessors="true" {

property entityName;

public any function new() {
return entityNew(getEntityName());
}

public any function load(required string id) {
return entityLoadByPK(getEntityName(), arguments.id);
}

public array function list() {
return entityLoad(getEntityName());
}

public any function onMissingMethod(required string missingMethodName, required struct missingMethodArguments) {
// logic for dynamic finders...
}

}


Now I can inject any domain classes I want into my controllers by adding a property that matches the name of my domain class. Here's what my ProductController looks like now:


component accessors="true" {

property Product;

public array function list() {
return Product.list();
}

}


I'm not sure if I'm ready to abandon the Controller/Service/DAO stack yet, but it's nice to know I have options. If I were to fully embrace this approach, I would probably have my entities all extend the DomainClass.cfc, then have the DomainClassInjector inject new instances of my entities into my controllers.

Saturday, January 2, 2010

Fun with ColdSpring Post Processors

One really powerful feature of ColdSpring that I love is the ability to define factory post processors. I've blogged about how I've used post processors to simulate the "beans" scope in my applications, which is essentially annotation-based autowiring.

While autowiring helps keep my ColdSpring.xml definition file to a minimum, things tends to get a little redundant. Here's what my XML starts to look like:

ColdSpring.xml

<beans default-autowire="no">

<bean id="productController" class="com.app.controllers.ProductController" />
<bean id="productService" class="com.app.services.ProductService" />
<bean id="productGateway" class="com.app.model.product.ProductGateway" />

<bean id="securityController" class="com.app.controllers.SecurityController" />
<bean id="securityService" class="com.app.services.SecurityService" />
<bean id="securityGateway" class="com.app.model.security.SecurityGateway" />

<bean id="userController" class="com.app.controllers.UserController" />
<bean id="userService" class="com.app.services.UserService" />
<bean id="userGateway" class="com.app.model.user.UserGateway" />

<bean id="fooController" class="com.app.controllers.FooController" />
<bean id="fooService" class="com.app.services.FooService" />
<bean id="fooGateway" class="com.app.model.foo.FooGateway" />

<bean id="barController" class="com.app.controllers.BarController" />
<bean id="barService" class="com.app.services.BarService" />
<bean id="barGateway" class="com.app.model.bar.BarGateway" />

<!-- etc... -->

<bean id="beansScopeInjector" class="com.util.BeansScopePostProcessor" factory-post-processor="true" />

<bean id="helpers" class="com.util.HelpersScopePostProcessor" factory-post-processor="true">
<property name="directories">
<list>
<value>/com/app/helpers/</value>
</list>
</property>
</bean>

</beans>


Basically each area of concern (products, security, users, etc...) has a corresponding controller, service, and gateway that all follow similar naming conventions and folder structures. Wouldn't it be nice if I didn't have to explicitly define each bean, but instead my application just knew how to create its own bean definitions based on conventions? I thought it would, so I created BeanDetector.cfc.

BeanDetector

<bean id="beanDetector" class="com.util.BeanDetector" factory-post-processor="true">
<property name="directories">
<list>
<value>/com/app/controllers/</value>
<value>/com/app/model/</value>
<value>/com/app/services/</value>
</list>
</property>
<property name="patterns">
<list>
<value>[\w]+Controller</value>
<value>[\w]+Gateway</value>
<value>[\w]+Service</value>
</list>
</property>
</bean>


Inside your ColdSpring.xml, you define the BeanDetector and tell it what directories to scan for CFCs. It will scan recursively deep, so in my case I don't need to worry about nested folders in my /model directory. You can also pass in an optional array of regular expressions to match the file name against to exlude non-singleton beans. Here's the code:

BeanDetector.cfc

component accessors="true" {

property directories;
property patterns;
property autowire;

public any function init() {

variables.directories = [];
variables.patterns = [];
variables.autowire = "no";

}

public void function postProcessBeanFactory(required any beanFactory) {

var i = "";
var j = "";
var k = "";

var beans = {};

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

var directory = expandPath(variables.directories[i]);

var classPath = convertDirectoryToClassPath(variables.directories[i]);

var components = directoryList(directory, true, "query", "*.cfc");

for (j=1; j <= components.recordCount; j++) {

var bean = {};
bean.id = listFirst(components.name[j], ".");

var folder = replaceNoCase(components.directory[j] & "\", directory, "");

folder = convertDirectoryToClassPath(folder);

if (folder == '') {
bean.class = classPath & "." & bean.id;
}
else {
bean.class = classPath & "." & folder & "." & bean.id;
}

if (!structKeyExists(beans, bean.id) && !beanFactory.containsBean(bean.id)) {

if (arrayIsEmpty(variables.patterns)) {
beans[bean.id] = bean;
}
else {

for (k=1; k <= arrayLen(variables.patterns); k++) {

if (reFindNoCase(variables.patterns[k], bean.id)) {
beans[bean.id] = bean;
break;
}

}

}

}

}

}

for (i in beans) {

arguments.beanFactory.createBeanDefinition(
beanID=beans[i].id,
beanClass=beans[i].class,
children=[],
isSingleton=true,
isInnerBean=false,
autowire=variables.autowire);

}

}

private string function convertDirectoryToClassPath(required string directory) {

arguments.directory = replace(arguments.directory, "\", "/", "all");

return arrayToList(listToArray(arguments.directory, "/"), ".");

}

}


Now I can remove all those bean definitions and everything will work great, right? Not exactly. Here's what my updated ColdSpring.xml looks like:

ColdSpring.xml

<beans default-autowire="no">

<bean id="beanDetector" class="com.util.BeanDetector" factory-post-processor="true">
<property name="directories">
<list>
<value>/com/app/controllers/</value>
<value>/com/app/model/</value>
<value>/com/app/services/</value>
</list>
</property>
<property name="patterns">
<list>
<value>[\w]+Controller</value>
<value>[\w]+Gateway</value>
<value>[\w]+Service</value>
</list>
</property>
</bean>

<bean id="beansScopeInjector" class="com.util.BeansScopePostProcessor" factory-post-processor="true" />

<bean id="helpers" class="com.util.HelpersScopePostProcessor" factory-post-processor="true">
<property name="directories">
<list>
<value>/com/app/helpers/</value>
</list>
</property>
</bean>

</beans>


You'll notice I'm using multiple factory post processors. It's OK to use multiple post processors, but in my case the post processors need to execute in a certain order: the BeanDetector needs to create all my additional bean definitions before my other post processors can add the "beans" and "helpers" scopes. However, ColdSpring doesn't care about the order of your bean definitions - it stores everything inside a struct. To get around this, I created an OrderedPostProcessor.cfc to manage the order in which my post processors are executed. One post processor to rule them all.

OrderedPostProcessor

<bean id="orderedPostProcessor" class="com.util.OrderedPostProcessor" factory-post-processor="true">
<property name="postProcessors">
<list>
<ref bean="beanDetector" />
<ref bean="beansScopeInjector" />
<ref bean="helpers" />
</list>
</property>
</bean>


Since the OrderedPostProcessor handles calling postProcessBeanFactory() on my other post processors, I no longer need to define those beans as post processors inside my ColdSpring.xml, leaving me with the following:

ColdSpring.xml

<beans default-autowire="no">

<bean id="beanDetector" class="com.util.BeanDetector">
<property name="directories">
<list>
<value>/com/app/controllers/</value>
<value>/com/app/model/</value>
<value>/com/app/services/</value>
</list>
</property>
<property name="patterns">
<list>
<value>[\w]+Controller</value>
<value>[\w]+Gateway</value>
<value>[\w]+Service</value>
</list>
</property>
</bean>

<bean id="beansScopeInjector" class="com.util.BeansScopePostProcessor" />

<bean id="helpers" class="com.util.HelpersScopePostProcessor">
<property name="directories">
<list>
<value>/com/app/helpers/</value>
</list>
</property>
</bean>

<bean id="orderedPostProcessor" class="com.util.OrderedPostProcessor" factory-post-processor="true">
<property name="postProcessors">
<list>
<ref bean="beanDetector" />
<ref bean="beansScopeInjector" />
<ref bean="helpers" />
</list>
</property>
</bean>

</beans>


And finally, here's the code:

OrderedPostProcessor.cfc

component accessors="true" {

property postProcessors;

public void function postProcessBeanFactory(required any beanFactory) {

var i = "";

for (i=1; i <= arrayLen(variables.postProcessors); i++) {
variables.postProcessors[i].postProcessBeanFactory(arguments.beanFactory);
}

}

}


Sorry for such a long post. If you took the time to read the whole thing, hopefully it was worth it.

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.

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?

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...");
}