Viewing by month: May 2005

May 31 2005

Model-Glue 0.9 en route

Sorry for the delay - had the whole marriage, honeymoon, new dog, fried power supply on main workstation thing to deal with. M-G 0.9 is on its way, a few of the changes include:

* Revised logging within the EventRequest. The debugging output will no longer reset when an exception is thrown.

* A few additional "Oops!" exceptions being thrown from Model-Glue.cfc, in the event of things like duplicately named tags.

* Multiple-application support via application keys. You'll be able to literally have multiple instances of Model-Glue.cfc in your application scope, all of which listen to the current request. It's a bit of an advanced thing that probably shouldn't be messed with unless you understand it and the occaisional need for using it instead of sharing controllers amongst multiple apps :).

If there's anything anyone else would like to see in the framework, please let me know!

2 comments - Posted by Joe Rinehart at 2:39 PM - Categories: ColdFusion MX | Model-Glue

May 27 2005

Eat Your Beans: ConfigBeans in Model-Glue

The Model-Glue Framework has a really nice feature called ConfigBeans, powered by a little tool I wrote called ChiliBeans. By using ConfigBeans instead of inserting configuration values, like Datasource names, into your ModelGlue.xml file, you can seperate your configuration from your application. This post gives a quick overview of creating a new ConfigBean and using it in your application. Once you've seen how to create one from scratch, you should have no problem using the default ConfigBeans provided with the framework.

Note: I'm typing this in between running some huge queries, so it may be a bit choppy in flow.

Download the example code for this tutorial at http://clearsoftware.net/configbeantutorial.zip.

ConfigBeans: Why bother?

Why use a ConfigBean instead of putting a new tag into your ModelGlue.xml file? Here's a few reasons:

  1. ConfigBeans keep your application code (which includes ModelGlue.xml) seperate from yor configuration
  2. Configuration properties managed through a ConfigBean can be complex values (arrays or structs), so that you can keep your configuration in a much neater package
  3. It's a lot easier to instantiate a ConfigBean and pass it along to your viewstate than it is to pass a huge number of individual strings. For example, you can pass a bean for a datasource, rather than a DatasourceName, DatasourcePassword, and DatasourceUsername settings
  4. It's a good design principle to favor passing objects (interfaces) over simple values (implementations).

Example: Basic internationalization

In our example, we'll create a ConfigBean that manages basic internationalization of time and date formats. It'll manage two settings:

  1. 1. Timeformat will define a TimeFormat() mask that we'll use
  2. 2. Dateformat will be a struct, with Dateformat.long looking something like "January 1, 2005," and Dateformat.short looking something like "1/1/2005."

Then, we'll show how to use our bean to customize how users see dates and times.

Step 1: Create a "bean"-style CFC to hold these settings

First, we make our "bean" itself. It's a basic little CFC that has an Init() method, and Getter/Setter methods for each of our configuration properties. Naming is important here: when we get to the bean XML, it's going to look for methods named Get[NAME] and Set[NAME]!.

The directory that you create the bean in is important - it must be one of the mappings listed in your ModelGlue.xml's beanMappings setting. In the code that accompanies this tutorial, you'll find this bean at /config/beans/DateTimeFormatBean.cfc .

Without further ado, here's the bean:

<cfcomponent output="false">

<cffunction name="Init" output="false">
<cfset variables.timeFormat = "" />
<cfset variables.dateFormat = structNew() />
<cfset variables.dateFormat.short = "" />
<cfset variables.dateFormat.long = "" />
<cfreturn this />
</cffunction>

<cffunction name="SetTimeFormat" output="false">
<cfargument name="value" type="string" required="true" />
<cfset variables.timeFormat = arguments.value />
</cffunction>

<cffunction name="GetTimeFormat" output="false">
<cfreturn variables.timeFormat />
</cffunction>

<cffunction name="SetDateFormat" output="false">
<cfargument name="value" type="struct" required="true" />
<cfset variables.dateFormat = arguments.value />
</cffunction>

<cffunction name="GetDateFormat" output="false">
<cfreturn variables.dateFormat />
</cffunction>

</cfcomponent>

Pretty simple little nugget of information, no?

Step 2: Create an XML file containing your configuration properties

Ok, now we need a place to actually store your application-specific configuration information. We store this in an XML file that maps to the CFC we just created - we'll put it in /config/beans/DateTimeFormat.xml. Then, in your Model-Glue application, when you ask for a bean from this XML file, you receive an instance of the CFC whose properties are populated with the information in your XML file. It may sounds confusing, but it's really pretty simple once you see it in action.

Here's our XML: it shows how to define properties that are simple (timeformat) and struct (dateFormat). (Aside: If you look in /ModelGlue/Bean/CommonBeans/ExampleBean.xml, you'll see a full example that shows how to create all the complex data types).

<bean class="ConfigBeanTutorial.config.beans.DateTimeFormatBean" singleton="true">
<!-- Time Format -->
<property name="TimeFormat">
   <value>H:MM TT</value>
</property>

<!-- Date Format -->
<property name="DateFormat">
   <struct>
    <element key="Long">
    <value>mmm d, yyyy</value>
    </element>
    <element key="Short">
    <value>m/d/yy</value>
    </element>
   </struct>
</property>
</bean>

Let's look at that line by line:

<bean class="ConfigBeanTutorial.config.beans.DateTimeFormatBean" singleton="true">

This line states that when you ask ModelGlue for a bean from "DateTimeFormat.xml", you'll receive an instance of the CFC at ConfigBeanTutorial.config.beans.DateTimeFormatBean. "Singleton" means that Model-Glue will save this instance in the application scope, and any further requests for the DateTimeFormat.xml bean will receive a reference to the same instance.

<!-- Time Format -->
<property name="TimeFormat">
   <value>H:MM TT</value>
</property>

This defines the value of the TimeFormat property. When ChiliBeans hits this block, it'll look for a function in DateTimeFormatBean.cfc called "TimeFormat", and try to set it to the value in the blocks.

<!-- Date Format -->
<property name="DateFormat">
   <struct>
    <element key="Long">
    <value>mmm d, yyyy</value>
    </element>
    <element key="Short">
    <value>m/d/yy</value>
    </element>
   </struct>
</property>

This portion is much like the Timeformat block, but as you can see, we have a fairly easy to remember XML format for defining a structure and its members. This will set the DateFormat property.

Step 3: Use your bean!

Now, in our application, we need to actually ask ModelGlue for the bean represented by DateTimeFormat.xml. To do that, in our controller, we first use the GetModelGlue() function to ask for a reference to the framework (you can do a lot with this, like adding new controllers at runtime, but some of it is dangerous!). Next, you use the GetConfigBean() method, passing it a filename to read. In the sample application, we do this in our controller's constructor, and place the bean into the variables scope. That effective "caches" the bean at the applicaiton level, and makes it available to any other methods that need it. It looks like this:

<cffunction name="Init" access="Public" returnType="Controller" output="false">
<cfargument name="ModelGlue">

<cfset super.Init(arguments.ModelGlue) />

<cfset variables.dateTimeFormat = GetModelGlue().GetConfigBean("DateTimeFormat.xml") />

<cfreturn this />
</cffunction>

Next, we need to actually use the settings from the bean. In our OnRequestStart handler, we'll place formatted versions of the current datetime into the data bus. Remember, variables.dateTimeFormat now contains our ConfigBean, and we can use its getTimeFormat() and getDateFormat() methods to get our formatting masks.

<cffunction name="OnRequestStart" access="Public" returnType="ModelGlue.Core.Event" output="false" hint="I am an event handler.">
<cfargument name="event" type="ModelGlue.Core.Event" required="true">

   <cfset var currentTime = timeFormat(now(), variables.dateTimeFormat.getTimeFormat()) />
   <cfset var currentDateLong = dateFormat(now(), variables.dateTimeFormat.getDateFormat().long) />
   <cfset var currentDateShort = dateFormat(now(), variables.dateTimeFormat.getDateFormat().short) />

   <cfset arguments.event.setValue("currentTime", currentTime) />
   <cfset arguments.event.setValue("currentDateLong", currentDateLong) />
   <cfset arguments.event.setValue("currentDateShort", currentDateShort) />

<cfreturn arguments.event />
</cffunction>

Finally, we add a little code to our view to show the values:

<cfoutput>
Time: #viewState.getValue("currentTime")#<br />
Long Date: #viewState.getValue("currentDateLong")#<br />
Short Date: #viewState.getValue("currentDateShort")#<br />
</cfoutput>

Conclusion

Ok, not too shabby! We've created a ConfigBean from scratch, and shown what we could do with it. We've completely separated the configuration from our application.

What next?

Imagine you have a bunch of DAOs. In your configuration, they just need a datasource name passed to their constructor. However, a client's implementation requires a datasource name, username, and password. Oops. Time to edit some code and add more arguments...

Wait! That's a great place to use ConfigBeans. Instead of passing a datasource to the constructor, use the ModelGlue.Bean.CommonBeans.Datasource bean (or your own implementation). That way, you can pass whatever you need, shrinking or growing what the bean contains, but not having to change the interface to your existing components.

5 comments - Posted by Joe Rinehart at 10:54 AM - Categories: ColdFusion MX | Model-Glue

May 27 2005

OO is too much code? Bah.

I frequently hear people say "that's a lot of code to do something simple!" when they start looking at OO code, especially in cf.

Why does it take a seperate file, with all of those and tags, just to do the four basic queries needed for CRUDding an entity?

It takes more code because you're not _just_ writing the code for those four queries. You're defining a reusable, encapsulated component that can be relied upon to always handle CRUDding the entity - and that definition is what the extra code consists of.

The amount of extra code is predictable, increasing on a linear scale. For the Delete part of the crud, we go from four lines:

<cfquery datasource="#dsn#">
DELETE FROM BeerBottles
WHERE BeerBottleId = <cfqueryparam cfsqltype="cf_sql_varchar" value="#beerBottleId#" />
</cfquery>

To seven for the definition:

<cffunction name="deleteBeerBottle">
<cfargument name="beerBottleId">
<cfquery datasource="#dsn#">
DELETE FROM BeerBottles
WHERE BeerBottleId = <cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.beerBottleId#" />
</cfquery>
</cffunction>

And one more for the invocation:

<cfset BeerBottleDAO.deleteBeerBottle(#beerBottleId#) />

That's a net increase of five lines. It's a linear increase: you pay the price one.

We're all about reusing code, right?

The next time we need to delete a bottle of beer (ok, drink...), we just invoke our component:

<cfset BeerBottleDAO.deleteBeerBottle(#beerBottleId#) />

That's one line of code, versus four:

<cfquery datasource="#dsn#">
DELETE FROM BeerBottles
WHERE BeerBottleId = <cfqueryparam cfsqltype="cf_sql_varchar" value="#beerBottleId#" />
</cfquery>

That's a savings of three lines. Reuse it twice, and you've saved six. Three times, and that's nine. That's still a linear progression, but even there, you're saving effort and making maintainance and updates oodles easier through encapsulating functionality.

When you start *combining* objects, you'll see an even more interesting cost savings when you reuse code. Encapsulating multiple-object functionality via composition and/or facades, you'll start to see exponential savings in effort.

Imagine a system where all functionality is encapsulated into a single service facade: to do any function, you're likely to use hundreds of lines of code that facade encapsulate. Each page (or even client application) that uses that facade is therefore saving itself those hundreds of lines of code.

w00t.

3 comments - Posted by Joe Rinehart at 10:22 AM - Categories: ColdFusion MX

May 26 2005

Pete F. on CF Arrays vs. Structs

Pete Freitag takes a great look at the underbellies ColdFusion arrays and structures. The comparison covers topics like performance and memory allocation, and is definitely with a read!

0 comments - Posted by Joe Rinehart at 10:13 AM - Categories: ColdFusion MX

May 26 2005

Sell a friend for an iPod? Only a rat would.

Wow, iPods are everywhere now! They've even invaded comic-strip culture: http://www.comics.com/comics/pearls/.

1 comments - Posted by Joe Rinehart at 9:26 AM - Categories: Off Topic

May 24 2005

Gamma (GoF) on Design Pattern Use

Erich Gamma gives a great interview over at Artima Developer on learning and using Design Patterns. The full article is at http://www.artima.com/lejava/articles/gammadp.html, but here are some of my favorite quotes:

"The GoF patterns provide you with little tools that help you with these problems. They do so not by giving a pat solution but by explaining trade-offs."

"... since patterns provide you with names for design building blocks they provide you with a vocabulary..."

"You have to feel the pain of a design which has some problem. I guess you only appreciate a pattern once you have felt this design pain."

"I really like to use patterns after the fact, refactoring to patterns."

"Trying to use all the patterns is a bad thing, because you will end up with synthetic designs."

"Because we were fluent in patterns, our conversation was going really fast, enabling a high-velocity design."

"...it became apparent that a mature framework contains recurring design structures that give you properties like extensibility, decoupling, and last but not least, elegance."

0 comments - Posted by Joe Rinehart at 7:49 AM - Categories: ColdFusion MX

May 23 2005

O'Reilly Firefox "Switcher" book

O'Reilly (in its ever-prudent manner) has released "Don't Click on the Blue 'E'," a book designed for users looking to switch to Firefox. While I imagine most people reading my blog are way ahead of actually needing a book, it couldn't hurt to leave a few copies around the office, or convince your IE-loving management staff to give it a read...

The book: http://www.oreilly.com/catalog/bluee/index.html

1 comments - Posted by Joe Rinehart at 12:09 PM - Categories: Off Topic