Monday, August 23, 2010

Creating a LESS CSS Plugin for ColdMVC

One of the coolest things I've seen in awhile is LESS CSS, which "extends CSS with variables, mixins, operations, and nested rules". Barney Boisvert has blogged about using LESS with ColdFusion already, but I wanted to show an even simpler integration using my ColdMVC framework with a little help from around the web.

Assuming you already have ColdMVC up and running, your next steps will be to download JavaLoader and the LESS jar file. Next, we'll create a new ColdFusion project called ColdCSS, which contains the LESS jar file and a single component, ColdCSS.cfc.



Next, open your application's /config/plugins.cfm template and register the ColdCSS plugin. The path to your plugin might be different, but here's what it looks like if the ColdCSS project is in the same directory as your application.


<cfset add("coldcss", "../../coldcss/") />


Here's the content of the new component, ColdCSS.cfc:


/**
* @accessors true
* @singleton
*/
component {

property pluginManager;

/**
* @events applicationStart
*/
public void function generateFiles() {

var jars = [ getDirectoryFromPath(getCurrentTemplatePath()) & "lesscss-engine-1.0.22.jar" ];
var javaLoader = new javaloader.JavaLoader(jars, true);
var lessEngine = javaLoader.create("com.asual.lesscss.LessEngine").init();
var directories = pluginManager.getPluginPaths();
var i = "";

arrayAppend(directories, expandPath("/public/css/"));

for (var directory in directories) {

var files = directoryList(directory, true, "query", "*.less");

for (i = 1; i <= files.recordCount; i++) {

var source = files.directory[i] & "/" & files.name[i];
var destination = files.directory[i] & "/" & replaceNoCase(files.name[i], ".less", ".css");
var content = fileRead(source);

fileWrite(destination, lessEngine.compile(content));

}

}

}

}


Without going over the component line by line, here's how it works. When your application starts, ColdMVC will execute ColdCSS.generateFiles(), which will scan your application and all other registered plugins and find any files ending with a .less file extension, compile them to CSS using the LESS engine, and write them back to disk in the same folder as the original .less file, all in less than 50 lines of code.

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.