comment

Use Ant to Maintain Custom Framework Builds

2008 May 18
No Comments
I'm pretty new to Ant, so I'm thankful that a lot of other developers have examples and sample build files that I can use as a guide to learning Ant. After writing a few small build files, I wanted to tackle something a little more ambitious. I like to keep all the frameworks that I use up to date from their respective repositories, and, when deploying a new site or project, I will typically include the framework dependencies as a minimal include at the project root. This requires a bit of work, exporting and stripping out documentation, examples and tests from my local checkout of the framework files. As a learning objective, I decided to write an ant build file to maintain my own "nightly build" of each framework customized to carry only the core files.

read more...

cf.Objective() 2008 Reflections

2008 May 05
No Comments
I really picked a winner for my first Coldfusion conference. Unfortunately, I wasn't able to attend every session that I wanted, so I guess I'll have to come back next year! It's really hard to pick a favorite, but the Agile, Coldspring and Coldbox workshops all stand out. Over and above the excellent content and presenters, I got to meet and hang out with some really great folks. I'd start name dropping y'all, but you know who you are... The evening meals had a lot of variety. First there was the fabulous Brazilian churrascaria steakhouse Fogo de Chao, then two classics White Castle and Mickey's Diner, and finally the wonderful Sakura topping it off. Big thanks to Jared, Steven and the entire steering committee for putting on a great conference!

Transfer ORM Quickstart

2008 April 25
No Comments
Transfer ORM is, as the name implies, an object relational mapper for Coldfusion. Transfer has a myriad of advanced features which can be intimidating for newcomers. Fortunately, Transfer can be installed and used very easily without learning some of the more advanced features. What follows are instructions you can use to install, configure and use transfer in a few simple steps.

Installation

UPDATE: Transfer 1.0 is now available. The last official release of transfer is 0.6.3. This version is now quite old and it is recommended that you use the bleeding edge release (BER) from subversion. If you have any misgivings about using the BER, rest assured that many production systems are running the transfer BER. It's stable. To install the transfer BER, point your subversion client to http://svn.riaforge.org/transfer and checkout or export the source from /trunk/transfer. You can either set a mapping for "/transfer" in the cf administrator, or drop the source transfer folder in your web root.

Configuration

To use transfer in an application you'll need to setup two configuration files. One for the datasource that transfer will use and another to describe the objects that transfer maps from your relational database. These files are typically named Datasource.xml and Transfer.xml, but you can use any name you like and/or append a .cfm extension to prevent the files from being visible in a web browser. Datasource.xml The transfer datasource.xml config specifies the values for the following: - a datasource name that currently exists in the cf administrator - a username (if required - leave blank otherwise) - a password (if required - leave blank otherwise) <?xml version="1.0" encoding="UTF-8"?>
<datasource xsi:noNamespaceSchemaLocation="../xsd/datasource.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<name>dsn_name</name>
<username></username>
<password></password>
</datasource>
Transfer.xml The transfer.xml file is used to define the properties and relationships of transfer objects that are mapped to the relational tables in your database. Objects can have onetomany, manytoone, or manytomany relationships. The following xml is the object definitions from the tBlog sample app. <?xml version="1.0" encoding="UTF-8"?>
<transfer xsi:noNamespaceSchemaLocation="../xsd/transfer.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<InvalidTagDefinitions>
<package name="user">
<!-- User details -->
<InvalidTag name="User" table="tbl_User">
<id name="IDUser" type="numeric"/>
<property name="Name" type="string" column="user_Name"/>
<property name="Email" type="string" column="user_Email"/>
</object>
</package>
<package name="system">
<!-- Different categories for Blog Posts -->
<InvalidTag name="Category" table="tbl_Category">
<id name="IDCategory" type="numeric"/>
<property name="Name" type="string" column="category_Name"/>
<property name="OrderIndex" type="numeric" column="category_OrderIndex"/>
</object>
</package>
<package name="post">
<!-- A Blog Post, with a Decorator of type com.Post -->
<InvalidTag name="Post" table="tbl_Post" decorator="tblog.com.Post">
<id name="IDPost" type="numeric"/>
<property name="Title" type="string" column="post_Title"/>
<property name="Body" type="string" column="post_Body"/>
<property name="DateTime" type="date" column="post_DateTime"/>
<!-- Link between a Post and the User it who wrote it -->
<manytoone name="User">
<link to="user.User" column="lnkIDUser"/>
</manytoone>
<!--
Link between a post and it's array of Comments
-->

<onetomany name="Comment">
<link to="post.Comment" column="lnkIDPost"/>
<collection type="array">
<order property="DateTime" order="asc"/>
</collection>
</onetomany>
<manytomany name="Category" table="lnk_PostCategory">
<link to="post.Post" column="lnkIDPost"/>
<link to="system.Category" column="lnkIDCategory"/>
<collection type="array">
<order property="OrderIndex" order="asc"/>
</collection>
</manytomany>
</object>
<!-- A comment for a blog post -->
<InvalidTag name="Comment" table="tbl_Comment">
<id name="IDComment" type="numeric"/>
<property name="Name" type="string" column="comment_Name"/>
<property name="Value" type="string" column="comment_Value"/>
<property name="DateTime" type="date" column="comment_DateTime"/>
</object>
</package>
</objectDefinitions>
</transfer>

Integration

Using Transfer with Coldspring <bean id="transferFactory" class="transfer.TransferFactory">
<constructor-arg name="datasourcePath"><value>/config/transfer/Datasource.xml</value></constructor-arg>
<constructor-arg name="configPath"><value>/config/transfer/Transfer.xml</value></constructor-arg>
<constructor-arg name="definitionPath"><value>/path/to/definitions</value></constructor-arg>
</bean>

<bean id="datasource" factory-bean="transferFactory" factory-method="getDatasource" />

<bean id="transfer" factory-bean="transferFactory" factory-method="getTransfer" />
Using transfer as an application scope "singleton". <cfset transferConfig = StructNew() />
<cfset transferConfig["datasourcePath"] = "/config/transfer/Datasource.xml" />
<cfset transferConfig["configPath"] = "/config/transfer/Transfer.xml" />
<cfset transferConfig["definitionPath"] = "/path/to/definitions" />
<cfset application.transferFactory = CreateObject("component","transfer.TransferFactory").init(argumentCollection=transferConfig) />
<cfset application.transfer = application.transferFactory.getTransfer() />

Usage

To grasp the full breadth of the transfer API, you really need to review the Transfer docs. I'll cover the four methods you will likely work with the most in the beginning. They are, get(), new(), save() and delete(). Using the tBlog object definitions above, to retrieve a post object use: <cfset application.transfer.get("post.Post",1) /> To have transfer return a new instance: <cfset post application.transfer.new("post.Post") /> To modify and save a new post: <!--- create a new post --->
<cfset post = application.transfer.new("post.Post") />
<!--- retrieve category with id 1 --->
<cfset category = application.transfer.get("system.Category",1) />
<!--- set properties (not shown) --->
<!--- set category for post --->
<cfset post.setCategory(category) />
<!--- save the post --->
<cfset application.transfer.save(post) />
A small, but significant, benefit of using Transfer ORM, is that when you save an new object the object's state reflects it's new persisted state. So you can find out the new id by simply calling. <cfset newid = post.getID() /> Lastly, transfer delete() method works like save(), you pass the transfer object to the method. <cfset application.transfer.delete(post) />

Much, much, more

This quick start is intended to help impatient types (like myself) get a taste for how simple it is to start using Transfer ORM. As you become comfortable with the Transfer API, you will definitely want to explore features like Transfer Object Decorators, the Transfer Event Model and Transfer Query Language (TQL). Mark Mandel has put a great deal of effort to into Transfer since it turned POSS. If you are not comfortable using the bleeding edge source code from subversion, keep an eye out for a 1.0 Release Candidate.

My cf.Objective() Schedule - Focus on Frameworks and Automation

2008 April 17
No Comments
I've finally decided to stick with a theme for my first CF conference. The goal is to increase my frameworks acumen and better my development methodology. So with that in mind, here's my cf.objective() schedule. Let's hope it sticks.

Fancy Bread Blog Turns One Today!

2008 March 29
No Comments
Thanks to all the folks who have read and commented on this blog over the past year. I've met a lot great people and learned more than I imagined. A quick review of my stats for the year shows 44 posts with an average of 865 views and just under 2 comments per post. The top three categories are ColdFusion, ColdSpring and Transfer. I started blogging to share my learning experiences and ideas. Year two will be a lot more of the same. :)

Taming the MXUnit Ecplise Plugin with Apache Alias

2008 March 09
No Comments
If you are into Unit Testing and haven't yet heard of MXUnit, I definitely recommend giving it a whirl. Some things that stand out for me, thus far in my evaluation, are the simple, but extensible, assertions, the ecplise plugin, and the debug output. One gotcha that has come up for people when using the eclipse plugin, is that is was designed assuming that the package path to your components resolves directly from your web root. For people who run separate server instances, virtual machines, or develop locally with Apache virtual hosts, this is a show stopper. Luckily, for folks, like myself, who prefer to develop locally with Apache, getting the MXUnit eclipse plugin to work is simply a matter of adding a couple of Alias directives to your vhost.

read more...

ColdFusion 8 Application Mappings and Sub-folders

2008 February 27
No Comments
Now that I'm on a CF8 hosting plan, I've started leveraging some of that goodness in my applications. The first thing I have implemented is application level mappings to keep my configuration files and components out of the web root. Here's how my current project layout rolls.
/project
  /config <-- Coldspring and Transfer config files
  /model <-- model components
  /root <-- web root
    /admin  <-- secure admin screens
Basically, I want to create mappings to config and model. I did a little googlin' and found a nice snippet on Sean Corfield's blog on looping over a list to build the mappings. I aped that code with a slight mod for my setup. <cfif (ListLast(CGI.HTTP_HOST,".") eq "local")>
<cfset mapPath = "../">
<cfelse>
<cfset mapPath = "../" & this.name & "/">
</cfif>

<cfloop index="p" list="config,model">
<cfset this.mappings["/" & p] = expandPath(mapPath) & p />
</cfloop>
I dev on Mac and deploy on Windows and my local folder structure is slightly different from my host. The first part of the code is just a test of whether the site is being served up locally. When on the production server, the mapped folders are in a folder with the same name as the app. This is a great alternative to using Apache Alias or Virtual Directories in IIS for mappings. That is, until I tried using the mapping from within the admin folder. Suddenly the path was not recognized. I initially thought this was a transfer issue, but that was not the case. I tried several variations and workarounds. Eventually, I figured out that the mappings were fine for files in the web root where Application.cfc resides. So why was the path not available to files in a folder below the web root? The answer was pretty simple. The admin folder had no Application.cfc, thus no mappings. Remembering another gem by Mr. Corfield regarding extending Application.cfc via a Proxy, I created an Application.cfc in the admin folder and the required proxy in the web root. Instead of repeating the code for creating the mappings with only the path prefix changed, I wrapped the creation of mappings up in a method. <cffunction name="createMappings" access="private" output="false" returntype="void">
   <cfargument name="pathPrefix" type="string" required="false" default="../">
   <cfif (ListLast(CGI.HTTP_HOST,".") eq "local")>
      <cfset variables.mapPath = arguments.pathPrefix>
   <cfelse>
      <cfset variables.mapPath = arguments.pathPrefix & this.name & "/">
   </cfif>
   <cfloop index="p" list="config,model">
<cfset this.mappings["/" & p] = expandPath(variables.mapPath) & p />
   </cfloop>
</cffunction>
And call it from the Application.cfc in the admin folder using the appropriate prefix. <cfcomponent displayname="Application" extends="ApplicationProxy">

<!--- set mappings in this directory --->
<cfset createMappings(pathPrefix="../../")>

</cfcomponent>
Application level mappings are handy, but the per directory limitation does not make for a tad more code. Overall, I think it's a small price to pay for the benefit gained.

UPDATE:

After testing this on my Windows machine, it appears that using "this.mappings" inside a method does not properly set the mappings. I've changed things around so that the createMappings() method returns a struct and I set it as a pseudo-constructor. <cfset this.mappings = createMappings(pathPrefix="../") >

Of Views and Models and Working With Transfer ORM

2008 February 05
No Comments
Yesterday, I asked Mark Mandel for some advice on tuning my object model for performance. Mark, being an ever-so-helpful (P)OSS author, gave me a few suggestions. Turns out that someone asked basically the same question on the transfer list today. Jaime Metcher made a couple of excellent points on choosing between queries and object composition. Paraphrasing, Jaime, when working with large data sets (the type of thing you might be tempted to use arrays of objects for), you will get better performance using a query. If you want to display a single instance of some object and the view is truly a composite, then your model should reflect that composition. Understanding this helps with choosing between onetomany or manytoone relationships for your object compositions.

read more...

My Journey into Test Driven Development

2008 February 02
No Comments
If you wonder about Unit Testing and Test Driven Development, but are afraid of commitment, do yourself a favour, read Terrance Ryan's thoughts on "How to convince yourself to Unit Test". A couple of months ago, I made a commitment to start unit testing. I'll admit that it took a little while to wrap my head around the terminology and process. A good practice point for me was to run some of my first CFCs though Seth Petry-Johnson's var scope checker. When I discovered a plethora of un-scoped vars, I used the opportunity to write tests against the methods I was going to change. After only a few tests, I started to get this warm feeling of security. By the time my updates and tests were complete, I had a nice little suite that I could return to any time a change was required.

read more...

Services, DAOs and Functional Organization

2008 January 29
No Comments
While cleaning out the office recently, I ran across a couple of textbooks I kept from my business school courses. I didn't care much for business school, but I did learn some valuable concepts. A few of which came from a course on Organizational Behaviour (OB). OB is a "management science" which can be briefly described as the study of groups in organizations. A goal of OB is to determine the best strategy to use within an organization to maximize employee performance (productivity) and, as a result, profitability. If you think of an organization with managers and employees, where each employee performs specific duties, you might find it strange to have a manager for every employee. You rarely see this in the real world, because it is grossly inefficient.

read more...