Archive for the 'tools' Category

CF Groovy 1.0RC (With Hibernate!)

I've just released 1.0RC of CF Groovy, including Hibernate support.  You can download it, or view the demo app.  The download includes both the demo and the runtime engine.

The big new feature is Hibernate support, of course.  Here are a couple snippets from the demo app.  First, the entity class:

package com.barneyb

import javax.persistence.*

@Entity
class User extends AbstractEntity {

    String firstName
    String lastName
    String email

    @Column(unique = true)
    String username
}

Then the engine setup (in code here, ColdSpring would be better):

<cfset application.cfhibernate = createObject("component", "HibernatePlugin").init() />
<cfset application.cfhibernate.setEntityPath(
    getDirectoryFromPath(getCurrentTemplatePath()) & "entities"
) />
<cfset application.cfhibernate.setColdFusionDsn("cfgroovydemo") />
<cfset application.cfhibernate.setAnnotatedClasses(listToArray("com.barneyb.User")) />
<cfset application.cfgroovy = createObject("component", "cfgroovy").init() />
<cfset application.cfgroovy.addPlugin(application.cfhibernate) />

Finally, doing some persistence operations:

<cfset request.sess = application.cfhibernate.getSessionForRequest() />
<g:script>
import com.barneyb.*

variables.user = new User([
    firstName: attributes.firstName,
    lastName: attributes.lastName,
    username: attributes.username,
    email: attributes.email
])
request.sess.save(variables.user)
request.sess.flush()
</g:script>
<cfset userList = request.sess.createQuery(
    "from User order by lastName, firstName, username"
).list() />

As you can see, very minimal fuss to do some pretty powerful things.  This only scratches the surface of what Hibernate can do; the full power of the tool is available (custom annotations, the full XML mapping capabilities, caching, etc.).

Another significant change is that all non-inline Groovy is precompiled into Java classes and loaded dynamically.  This gives you compiler checks on your Groovy code and engine startup, more helpful message for code errors, and some performance improvements.

Finally, as illustrated above, the <g:setPath> tag has been superceded by an explicitly managed CF Groovy runtime.  This gives you complete control over the lifecycle of the engine, which lets you target performance in production and development speed in development.  You can still use <g:setPath> instead of managing the runtime yourself, but that should be considered a deprecated practice.

Junction, from Sysinternals

I discovered Junction today, while looking for a way to easily test my Groovy/Hibernate integration on all three major CFML runtimes.  It's part of Sysinternals, which is a suite of tools aimed at making Windows not suck so much.  It's got all the low-level utilities you need to efficiently manage and monitor your Windows machines that Microsoft figured people would be happy to write and compile some C++ to use.

Junction, as you might guess, is a utility for managing NTFS junction nodes, which are the equivalent of *NIX symlinks.  With symlinks, I can have a single working copy "live" inside three different web roots, one for Railo, one for CF, and one for OBD.  Any changes made to that working copy are instantly available in all three places without any need to sync anything.

As you can imagine, this makes testing a breeze: just save and go refresh the tab for the runtime in question, and when it's set, go refresh the other tabs to ensure it still works everywhere else.  Until this point, I'd always used separate VMs, but that's way overkill for a standalone CFML app.  If you've got database and web server integration and all that, VMs are a lifesaver, but I just wanted to run the same 30 files in three web contexts and Junction is perfect.

CF Groovy Redux

I just got through significantly revamping CF Groovy so that it's CFC based, instead of all in the custom tags.  The custom tags remain, but now you can create and manage a CF Groovy runtime instance manually, rather than letting the tags do it for you.  This will greatly assist in production performance as the tag-based version recreated everything each request.  It's also a prerequisite for Hibernate support, as Hibernate spinup is non-trivial and needs to be avoided unless needed, even in development.

These changes are still in the Hibernate branch in Subversion, not the main trunk.  I don't plan on moving the new code back to the trunk until the Hibernate integration is complete.

Query/Reporting DSL Bug Fix

Rob Pilic found a couple local variables that I (gasp!) forgot to var scope in the DSL implementations while he was troubleshooting some concurrency issues.  There was one in each file, though totally unrelated.  I've applied his patch to Subversion and updated the demo install (though it won't matter, because it doesn't share instances).

Scriptlets in CF Anyone?

My last post about Comparators via CF Groovy was simplistic in nature, but the underlying concept is incredibly powerful.  Here's a similar snippet (from the demo app), this time using a Comparator class:

<g:script>
Collections.sort(variables.a, new ReverseDateKeyComparator())
</g:script>

So what do we have here?  Why it's a snippet of Java embedded directly in your CFML page, and it gets dynamically compiled and executed just like the CFML.  Let me say that again: it's a snippet of Java embedded directly in your CFML page.  It's even better than that, though, because it's Groovy, which is Java plus a whole lot more.

In addition to the scriptlet functionality, CF Groovy provides a classloading framework for Groovy scripts and classes, in addition to the ones provided by the CFML runtime and the JVM.  You can define an extra Groovy classpath for your scriptlets like this:

<g:setPath path="#expandPath('/groovy')#,#expandPath('/packages')#" />

There's no way to tell how the ReverseDateKeyComparator class in the first example is implemented, but it happens to be a Groovy class that I've added to the classpath with the <g:setPath> tag.  That class, just like the scriptlet, is dynamically compiled and executed just like the CFML.  This is also very powerful, because it eliminates the compile/build/deploy cycle that is usually required for Java code in a JEE environment.

Combining these two, you can use <g:script> to execute arbitrary scripts from the Groovy classpath, instead of inlining them:

<g:script name="myScript.groovy" />

This lets you reuse scripts across CFML pages, as well as reusing classes between scripts.

I've added CF Groovy to my Projects page, so that's the place for the latest info, as well as some documentation and the all-important Subversion information.

FB3 Lite Updates

After 6 months of use in the wild, I decided to consolidate and republish a couple mods I've made to the framework. The core functionality is unchanged, so for background, check my original post from last year. The enhancements, in no particular order, are as follows:

  • Changed the config at the top of index.cfm to use CFPARAM instead, so the framework can be parameterized by calling code.
  • Most significant is the addition of an 'appSearchPath' control variable, which means index.cfm (the core file) and fbx_Switch.cfm no longer have to be in the same directory. This allows you to put index.cfm in some arbitrary directory (probably with svn:externals) and use it from there. It defaults to ".,.." (this directory and the parent directory), but can be set to whatever is needed.
  • Added the context path and script name to the front of the 'self' variable. I realize this breaks the core on non-JEE CFML implementations, but it fixes some weird redirect issues with IE, so I feel it's worth it. If you don't want it, just remove everything before the question mark.
  • Removed defaulting of the empty fuseaction. Now only a missing fuseaction will be set to the configured default.
  • Added CFABORT to the end of the location() UDF. I went back and forth on this one for quite a while (like measured in days) when I was originally writing the framework, and finally decided to leave it out. After using the framework for a while, I've decided that was the wrong decision, so I've put it in there in this version.
  • Added ability to use multiple circuits via the new 'allowMultipleCircuits' control variable. It defaults to true, which means any slashes in your fuseactions will be treated as path segments, potentially breaking existing apps. If you need to use slashes in your fuseactions, set it to false. When true, a fuseaction of "circuit/fusection" will be converted into an include of "circuit/fbx_Switch.cfm" with attributes.currentFuseaction set to "fuseaction". I've intentionally NOT used the dot separator because they are not circuit aliases as in real Fusebox, but are simply path segments. The include() UDF has always had this ability do to it's strictly path-based nature, though it was undocumented.
  • Added some comments to index.cfm.

There is a project page for the framework, as well as Subversion access available. Current utilization (that I'm aware of) is about 15 distinct applications, most of which I either own or am a contributor to.

Checkbox Range Selection Update

Just a little update to my checkbox range selection jQuery plugin to allow chaining.  I'd forgotten to return 'this' at the end of the function.  Here's the full source, including the mod:

(function($) {
  $.fn.enableCheckboxRangeSelection = function() {
    var lastCheckbox = null;
    var $spec = this;
    $spec.bind("click", function(e) {
      if (lastCheckbox != null && e.shiftKey) {
        $spec.slice(
          Math.min($spec.index(lastCheckbox), $spec.index(e.target)),
          Math.max($spec.index(lastCheckbox), $spec.index(e.target)) + 1
        ).attr({checked: e.target.checked ? "checked" : ""});
      }
      lastCheckbox = e.target;
    });
    return $spec;
  };
})(jQuery);

You can check the project page as well, for full history and updates.

Prototype's Array.any/all with jQuery

I needed to convert a couple Array.any() and Array.all() calls (from Prototype) to jQuery syntax. Since jQuery doesn't extend the built-in objects with nice functionality like this, you have to fake it. Here's what I came up with. Old version ('images' is an array):

images.all(function(o){return o.status == "ready";})

and the new version:

jQuery.inArray(false,
  jQuery.map(images, function(o){return o.status == "ready";})
) < 0

Array.any()'s equivalent is the reverse:

jQuery.inArray(true,
  jQuery.map(images, function(o){return o.status == "ready";})
) >= 0

Might have to rip the Enumerable and Array extensions out of Prototype for standalone use as well.

Prototype and jQuery

Since I discovered it a few years ago, I've been a big Prototype fan.  It's simple, and gets the job done with a minimum of fuss.  It's not without warts, of course.  I still occasionally forget to put 'new' in front of Ajax.Request, and some of the Ruby-like methods share their lineage's arcane naming.  When it was new, it was the best thing around, and while it now has competitors, it's certainly not lagging behind.

At work, however, jQuery has been adopted as the standard (and I've no power to change it).  The lack of the $() function is annoying; several times I've debated adding this function (or one of various similar ones) to our library:

function $(id) {
  return jQuery("#" + id)[0];
}

I haven't, of course, as it's not the jQuery way.  jQuery also lacks any sort of class assistance, so we still use the Prototype class framework for our class-based JS.  That seems to work fairly well, except for the fact that we have to use two frameworks where one could suffice.

jQuery is not without it's benefits, of course.  The plugin architecture is a nice aspect that Prototype didn't really offer an equivalent of.  It means the core stays lighter (good), but if you want additional functionality you're stuck managing files from a bunch of different projects (annoying).  Event handling is a bit more straightforward, in some ways.  "Magically" acting on collections of elements with a single call (i.e. no .each(function(o){…}) garbage) definitely makes for more readable code as well.

Because of this shift at work, I've been porting some of my personal apps over to jQuery as well.  I've actually been using a couple jQuery plugins (both self-written and external) for specific tasks for a while now, but not the core framework.  What I've found, however, is that jQuery can be prone to slow code.  To avoid a huge amount of extra work on the part of the JS interpreter, using temporary variables for jQuery objects is essential.  If you do strictly id-based queries, the degradation isn't huge, but if you do CSS-based queries, it can be significant.  With Prototype's focus on id-based queries (at least until $$() came about in 1.5), that was less of an issue.

This need to query a minimum number of times can provide a fair amount of complexity when you have more than a handful of closures hanging about and/or a dynamic DOM.  You end up doing a lot of state management work because you're, in effect, caching DOM lookups and have to ensure you never have stale cache.

Other than that issue and the lack of an equivalent to document.viewport, porting has been relatively painless.  Still very id heavy, so not leveraging jQuery as much as could be, but most of what I'm doing wouldn't benefit from other selectors.

Which one is better?  Hard to say.  jQuery seems to make you work harder to type less code, while Prototype seems to cost you a few more characters for a bit less density.  With the exception of Prototype's class support, their feature sets are fairly equivalent, especially with jQuery UI now available to "compete" with Scriptaculous.  For the moment, I'm choosing to use jQuery on new stuff, but wishing for Prototype every few minutes.  Until I come up against some sort of significant wall, it'll probably stay that way, just to stick with the same tooling professionally and personally.  And over time it'll probably get better as the Prototype-ness fades from apps.

IndentXml CF UDF

I had a need to fix indentation of some XML today, and a quick Googling didn't turn up much help. So I wrote a little UDF that will take an XML string and return it with all the tags nicely indented:

<cffunction name="indentXml" output="false" returntype="string">
  <cfargument name="xml" type="string" required="true" />
  <cfargument name="indent" type="string" default="  "
    hint="The string to use for indenting (default is two spaces)." />
  <cfset var lines = "" />
  <cfset var depth = "" />
  <cfset var line = "" />
  <cfset var isCDATAStart = "" />
  <cfset var isCDATAEnd = "" />
  <cfset var isEndTag = "" />
  <cfset var isSelfClose = "" />
  <cfset xml = trim(REReplace(xml, "(^|>)\s*(<|$)", "\1#chr(10)#\2", "all")) />
  <cfset lines = listToArray(xml, chr(10)) />
  <cfset depth = 0 />
  <cfloop from="1" to="#arrayLen(lines)#" index="i">
    <cfset line = trim(lines[i]) />
    <cfset isCDATAStart = left(line, 9) EQ "<![CDATA[" />
    <cfset isCDATAEnd = right(line, 3) EQ "]]>" />
    <cfif NOT isCDATAStart AND NOT isCDATAEnd AND left(line, 1) EQ "<" AND right(line, 1) EQ ">">
      <cfset isEndTag = left(line, 2) EQ "</" />
      <cfset isSelfClose = right(line, 2) EQ "/>" />
      <cfif isEndTag>
        <!--- use max for safety against multi-line open tags --->
        <cfset depth = max(0, depth - 1) />
      </cfif>
      <cfset lines[i] = repeatString(indent, depth) & line />
      <cfif NOT isEndTag AND NOT isSelfClose>
        <cfset depth = depth + 1 />
      </cfif>
    <cfelseif isCDATAStart>
      <!---
      we don't indent CDATA ends, because that would change the
      content of the CDATA, which isn't desirable
      --->
      <cfset lines[i] = repeatString(indent, depth) & line />
    </cfif>
  </cfloop>
  <cfreturn arrayToList(lines, chr(10)) />
</cffunction>

There's nothing XML-ish about the implementation, as you can see, so you can happily feed non-XML tag based markup, as long as it uses '<' and '>' as tag delimiters in the XML fashion. Just don't expect to get good formatting if you don't have tags that follow the XML spec (e.g. CFELSE).  Also, it doesn't account for open tags (or closing tags) that are split across multiple lines. That wasn't a case I cared about, and I don't know that you can solve it correctly without actually parsing at least CDATA blocks out of the XML.