If You Build or Maintain Client Side Libraries or Widgets I Want To Talk To You

The ColdFusion community is full of bright people who have built really neat libraries and widgets to make better features and functions for applications. One of my favorites, CFUniform (a rich forms library) lets you build consistent, feature rich forms with very minimal code. I use this library all the time to make my applications snazzier and more maintainable. There are plenty of cool projects I've not yet used and while working on some Model-Glue features this weekend, I had an idea.

See, I've been working on the scaffolding feature in Model-Glue. This feature is a great way to get a jump start on a data-centric application. Simply configure Model-Glue and your ORM of choice and Model-Glue will generate everything needed to Administrate your data. Forms, Lists, delete screens, Bam, it'll generate the whole thing for you in seconds. Of course, the generated code is easily customized to fit the need of the application, but because it is generated from the database, the functionality is generic and the look and feel is somewhat limited.

What if we had a real easy way to plug in some of these rich libraries into Model-Glue? Forms libraries, Ajax widgets, Grids, Rich tables, all these could be as simple as adding a few characters into an XML file. Sounds fun, right?

What I want is to talk to a few of the folks behind some of the coolest libraries to explore how I can provide the best integrations. If you are the author or maintainer of some library or widget, or you use something that you are really fond of, let me know by leaving a comment. We might be able to work together to provide some really neat, useful functionality for the community.

Give Model-Glue A Piece Of Your Mind

We on the Model-Glue team have put together a simple survey and want to hear from you. Even if you aren't using Model-Glue or Frameworks or OO at all, your views are important to us.

How long will it take?

We've all taken long and boring surveys with endless questions requiring 1-10 rankings on sundry topics like "How much do you prefer crunchy peanut butter over creamy peanut butter?". Model-Glue is all about making things easy for developers and this survey is no exception. This survey was tested with several monkeys from the North Carolina Zoo. Each monkey was able to complete the survey and give quality answers, all within a 20 minute period. The way we see it, a reasonably intelligent ColdFusion user like yourself should be able to complete the survey in 10 minutes.

Would you kindly give us 10 minutes of your time for the Model-Glue Survey? We promise to read all answers and use the answers for the good of the framework.



No monkey's were harmed in the making of this Blog Post.

Welcome to the Model-Glue Cookbook

Model-Glue is a very powerful and robust ColdFusion Framework. When solving application problems with Model-Glue there are recurring solutions that work well for particular problems. Up until now, one would have to troll the various blogs hoping the right keywords unlocked google to reveal a proper solution. While effective, this method is also time consuming.

Todd Sharp has developed and released the Model-Glue cookbook. Following the common cookbook format, this timely and relevant application will hold collective wisdom on how to use the Model-Glue framework for fun and profit.

If you want to learn more about Model-Glue, please check the Model-Glue cookbook for interesting tips and solutions for everyday programming. It even has an RSS feed for you!

If you are a Model-Glue expert, or you have found some interesting solutions with Model-Glue, share them with the simple Entry Form. Adding an entry to the Model-Glue Cookbook is easier than blogging and you get full credit.

I admire Todd Sharp. He is an effective community leader, generous with his time and efforts. I mean, look at what he has done in just the last 3 months:

  • CFEmmys
  • Snip-a-thon contest
  • PPTUtils Project
  • cfsnippets.org
  • Model-Glue Cookbook

Todd gets my vote for Most Deserving of Adobe Community Expert Status

Frameworks, XML and duplication

Duplicating code is Bad! When designing software, take great pains to avoid duplication of code. After all, if an application has a lot of duplicated logic, or cut/pasted processes, it is very easy for parts of the system to get out of wack. A small tweak here, a short change there and next thing you know there are bugs and inconsistencies galore.

As software engineers, we are trained to spot and avoid this duplication. There are whole mantras dedicated toward this end (DRY Don't Repeat Yourself). There are whole books dedicated towards refactoring out commonalities for the goal of improving software quality. This is a very important topic and duplication is one of the easier Code Smells to uncover.

Sometimes the same warning flags will pop into mind when working with an XML driven framework. "Should I really duplicate this line of XML repeatedly in my application?" and "Am I designing my application effectively?" are thoughts the reasonable programmer has. After all, if duplicating code is such a poor idea, why is duplicating XML configuration any different?

The reason it is different is what the underlying XML represents. It represents Code! Code that has been written and tested and debugged Code that performs vital purpose in your application. We aren't duplicating this code, we are duplicating a reference to the code. A type of shorthand, if you will.

If, for example, you have functionality for checking for an authorized user, and such code is wrapped into an XML snippet like this:

<broadcasts>
<message name="verifyUserLogin" />
</broadcasts>

You would be perfectly fine replicating this line of XML wherever you needed User Login Verification. The line of XML is very simple, with a clean syntax and very easy to implement without error. When the rules defining User Login Verification change, simply update the single section of code referred to by the XML snippet.

Properly designed XML configurations represent complex processes in a syntactically simple manner. So feel good about duplicating your lines of XML. Happy Coding!

So you want to use Transfer ORM in your ModelGlue:Unity application (Part 1)

Previously, we connected Transfer ORM into our ContactOMatic application. Now, we will use it to get some things done! The completed files for this exercise are included at the bottom of the post, just click on the 'Download' link.

Using Transfer with MG:U is quite different than hand coding all the data access instructions so I separated out the new Transfer enabled code from the old hand coded bits to help compare similar operations between the two styles. This means the ContactOMatic is not an example of Architectural Purity! Shock! Horror! Gasp!

To make the separation, I added another ModelGlue configuration file, named ContactAction.xml, to the main ModelGlue.xml file through the use of the include tag as follows:

[More]

Installing Transfer in ModelGlue:Unity

Today we will install the Transfer ORM inside our Contact-O-Matic application. To complete this tutorial, you should have the Contact-O-Matic installed and running. If you have not completed this step, please create the database described at the bottom of Series 6 and install the files at Series 10 download link. Test the application by manually adding several ContactTypes to the ContactType table in your database (I chose Co-Worker, Enemy, Friend). Then use the Contact Form in the ContactOMatic application to enter a few contacts.

[More]

So you want more tutorials?

I am going to continue the 'So You Wanna Create a Model Glue Application series. (Thanks Lola ;) For those just tuning in, the Contact-O-Matic is a simple example of a mini-application using ModelGlue and ColdSpring. The tutorials on the Contact-O-Matic go through the code line by line showing how to perform such tasks as:

  1. Set up the frameworks
  2. Build a View
  3. Place your CSS and JS files
  4. Create a form
  5. Validate a submitted form and persist the data
  6. Return success/error messages
  7. Resolve CFC dependancies with ColdSpring
  8. Create Instance Objects with factories
  9. Refactor, as needed

The Contact-O-Matic is not an example of a Best Practices- Enterprise application. This is due to the simple nature of the program. We simply have a list of contacts, a mechanism to add and edit contacts, and a way to remove contacts. As Object Models grow complex in complex applications, it is important to note that there is no perfect Object Model, only the least annoying set of tradeoffs. I want the code in the Contact-O-Matic to remain simple, and so it shall.

Our next move is to integrate Transfer ORM into our application. The next set of posts will cover installation and testing of the Transfer ORM Framework, the inclusion of another 'Contact-O-Feature' in our application as well as some Architectural Techniques.

So you want to create a ModelGlue:Unity application? ( Part 10 )

Sean Corfield pointed out that using ColdSpring to make instance (throwaway) components is quite a heavyweight approach for a simple form bean. He is, of course, correct. Our ContactFormBean is currently created by ColdSpring. In this series, we will add a factory object that will make our instance beans for us. The factory itself will be configured through ColdSpring as well as the configuration for our objects.

This is a very simple factory. It has three methods, GetConfig, SetConfig and getBean. In turn:

  1. getConfig() returns a struct with keys = objectnames, and values = paths
  2. setConfig() provides an interface for ColdSpring to set the configuration struct.
  3. getBean() gets the path for a specific object from the configuration struct and returns a created object.
Note: the getBean() method runs an init() method on the newly created object. This is considered a convention in CFCs.

<cfcomponent>

   <!---    Author: dwilson Date: 3/14/2007 Usage: I return an instance object    --->
   <cffunction name="getBean" output="false" access="public" returntype="any" hint="I return an instance object">
      <cfargument name="ObjectName" type="string" required="true"/>
      <cftry>

          <cfreturn createObject( "component", structFind(getConfig(), arguments.ObjectName ) ).init() />
      
         <cfcatch type="any">
            
            <cfthrow message="BAD_OBJECT_CONFIG_IN_INSTANCEFACTORY" detail="You provided [ #arguments.ObjectName # ] and I can't create it. Go check the config." />
            
         </cfcatch>
      
      </cftry>

   </cffunction>

   <!--- Usage: GetConfig / SetConfig methods for Config value --->
   <cffunction name="getConfig" access="public" output="false" returntype="any">
      <cfreturn variables.instance.Config />
   </cffunction>
   
   <cffunction name="setConfig" access="public" output="false" returntype="void">
      <cfargument name="Config" type="any" required="true" />
      <cfset variables.instance.Config = arguments.Config />
   </cffunction>

</cfcomponent>

To use this instance factory, add the following ColdSpring xml snippet to the ColdSpring.xml file.

<bean id="instanceFactory" class="ContactManagerMG.model.InstanceFactory">
    <property name="Config">
    <map>
    <entry key="ContactFormBean"><value>ContactManagerMG.model.ContactFormBean</value></entry>
    </map>
   </property>
</bean>

Now remove the original ContactFormBean configuration.

Next, open the Controller.cfc file and change each instance (getContactForm, handleContactForm and removeContact) of getModelGlue().getBean("ContactFormBean") to getModelGlue().getBean("InstanceFactory").getBean("ContactFormBean") .

Finally, reinitialize your application and click on the ContactForm tab. You should see your form as before.

A factory might seem like overkill right now, since this is a simple application. However, Factory objects are a good pattern to learn. Our motivation for the factory in this case was to reduce the amount of unnecessary processing incurred by using ColdSpring to make a simple form bean. We could however, expand our factory to take additional parameters and create all sorts of dynamic objects for us.

In a later series, we'll do just that.

So you want to create a ModelGlue:Unity application? ( Part 9 )

In our last segment we built the functionality to store our contacts to the database. Now we need a way to edit the information because after all, friends can become enemies, and enemies can become co-workers. We have a good structure in place and these changes will be simple.

[More]

So you want to create a ModelGlue:Unity application? ( Part 8 )

In our last series, we moved the ContactTypes from a ColdSpring configured struct, to a database table. This set the stage to move the rest of our persisted data to the database.

As we begin this series our main goal is to have our contacts and lists of contacts stored in our database. Originally, we used in-memory storage as it allowed us a functional application without a database.

In this series, we will introduce two new files, ContactDAO and ContactGW (GW = Gateway). Both components will reside in the *ContactManagerMG.model directory. The ContactDAO will handle the DB work for Creating, Reading, Updating and Deleting our Contacts. Incidentally, this group of functionality is commonly referred to as C.R.U.D. The ContactGW will handle the database work for pulling queries of Contacts. We'll have a function called getContactQuery().

Here is a look at the ContactDAO. Note: we reference the AppConfig inside our ContactDAO so we can use the Application Configuration defined in ColdSpring. While we mostly care about the DSN for now, the flexibility to pull other configuration parameters will come in handy later.

<cfcomponent>

   <cffunction name="Create" access="public" output="false">
      <cfargument name="Contact" required="yes" />
      <cfset var CreateQuery = "" />
   
         <cfquery name="CreateQuery" datasource="#getAppConfig().getConfig().dsn#">
   
            INSERT INTO Contact
            ( ContactName, ContactTypeID )
            VALUES
            (
               <cfqueryparam value="#arguments.Contact.getContactName()#" cfsqltype="cf_sql_varchar">,
               <cfqueryparam value="#val( arguments.Contact.getContactTypeID() )#" cfsqltype="cf_sql_numeric">               
            )         
   
         </cfquery>
      
   </cffunction>

   <cffunction name="Read" access="public" output="false">
      <cfargument name="Contact" required="yes" />
      <cfset var ReadQuery = "" />
      
      <cfquery name="ReadQuery" datasource="#getAppConfig().getConfig().dsn#">
   
         SELECT ContactID, ContactName, ContactTypeID
         FROM Contact
         WHERE ContactID = <cfqueryparam value="#val( arguments.Contact.getContactID() )#" cfsqltype="cf_sql_numeric">
   
      </cfquery>

      <cfset arguments.Contact.setContactID( ReadQuery.ContactID ) />
      <cfset arguments.Contact.setContactName( ReadQuery.ContactName ) />
      <cfset arguments.Contact.setContactTypeID( ReadQuery.ContactTypeID ) />

   </cffunction>

   <cffunction name="Update" access="public" output="false">
      <cfargument name="Contact" required="yes" />
      <cfset var UpdateQuery = "" />
      
      <cfquery name="UpdateQuery" datasource="#getAppConfig().getConfig().dsn#">
   
         UPDATE Contact
            SET
               ContactName = <cfqueryparam value="#arguments.Contact.getContactName()#" cfsqltype="cf_sql_varchar">,
               ContactTypeID = <cfqueryparam value="#arguments.Contact.getContactTypeID()#" cfsqltype="cf_sql_numeric">
         WHERE ContactID = <cfqueryparam value="#arguments.Contact.getContactID()#" cfsqltype="cf_sql_numeric">
            
      </cfquery>      
   
   </cffunction>

   <cffunction name="Delete" access="public" output="false">
      <cfargument name="Contact" required="yes" />
      <cfset var DeleteQuery = "" />
      
      <cfquery name="DeleteQuery" datasource="#getAppConfig().getConfig().dsn#">
   
         DELETE FROM Contact
         WHERE contactID = <cfqueryparam value="#arguments.Contact.getContactID()#" cfsqltype="cf_sql_numeric">
   
      </cfquery>
   
   </cffunction>


   <cffunction name="getAppConfig" access="public" output="false" returntype="any">
      <cfreturn variables.instance.AppConfig />
   </cffunction>
   
   <cffunction name="setAppConfig" access="public" output="false" returntype="void">
      <cfargument name="AppConfig" type="any" required="true" />
      <cfset variables.instance.AppConfig = arguments.AppConfig />
   </cffunction>
   
   
   

</cfcomponent>

Here is a look at the ContactGW. Once again we reference the AppConfig.

<cfcomponent>


<cffunction name="getContactQuery" access="public" output="false" returntype="query">
<cfset var ContactQuery = "" />

<cfquery name="ContactQuery" datasource="#getAppConfig().getConfig().dsn#">
SELECT C.ContactID, c.ContactName, c.ContactTypeID, ct.ContactType
FROM Contact c LEFT JOIN ContactType ct ON c.ContactTypeID = ct.ContactTypeID
</cfquery>

<cfreturn ContactQuery />

</cffunction>

<cffunction name="getAppConfig" access="public" output="false" returntype="any">
<cfreturn variables.instance.AppConfig />
</cffunction>

<cffunction name="setAppConfig" access="public" output="false" returntype="void">
<cfargument name="AppConfig" type="any" required="true" />
<cfset variables.instance.AppConfig = arguments.AppConfig />
</cffunction>

</cfcomponent>

Now we add each component to our ColdSpring configuration. Each component takes one Property, the AppConfig object. Add both beans as new property tags to the existing ContactService bean definition. We will also remove the references to the old Array based ContactList. Let's do a little housecleaning on our ColdSpring file shall we?

Add the definitions for the components

<bean id="ContactDAO" class="ContactManagerMG.model.ContactDAO">
<property name="AppConfig"><ref bean="AppConfig" /></property>
</bean>

<bean id="ContactGW" class="ContactManagerMG.model.ContactGW">
<property name="AppConfig"><ref bean="AppConfig" /></property>
</bean>

Remove the following lines from the existing ContactService definition:

<property name="ContactList">
   <list></list>
</property>

Add in the property tags for our new components.

<bean id="ContactService" class="ContactManagerMG.model.ContactService">
      <property name="ContactTypeGW"><ref bean="ContactTypeGW" /></property>
      <property name="ContactDAO"><ref bean="ContactDAO" /></property>
      <property name="ContactGW"><ref bean="ContactGW" /></property>      
   </bean>

Once complete, ColdSpring will run setContactDAO() and setContactGW() on the ContactService component and stuff the proper object in our service for us. We need to change the service a little now to account for this new functionality. Open up the ContactService and make the following changes:

Remove the getContactList() and setContactList() functions. These functions used to manage our Array based contact list from ColdSpring, with our new Database driven Contact-O-Matic, we won't need them any longer.

Remove:

<cffunction name="setContactList" access="public" returntype="void" output="false">
      <cfargument name="ContactList" type="array" required="true" />
      <cfset variables.instance.ContactList = arguments.ContactList />
   </cffunction>
   <cffunction name="getContactList" access="public" returntype="array" output="false">
      <cfreturn variables.instance.ContactList />
   </cffunction>

Now add in the get/set functions for each component. While we are in here, also add in a new function to get our ContactList from our new ContactGW. The function on the ContactGW is called getContactQuery(). When complete, you'll have removed two functions and added five. Examples of the new functions are below:

<cffunction name="getContactList" output="false" access="public" returntype="query" hint="I return a list of contacts">
      <cfreturn getContactGW().getContactQuery() />
   </cffunction>
   
   <cffunction name="getContactDAO" access="public" output="false" returntype="any">
      <cfreturn variables.instance.ContactDAO />
   </cffunction>
   
   <cffunction name="setContactDAO" access="public" output="false" returntype="void">
      <cfargument name="ContactDAO" type="any" required="true" />
      <cfset variables.instance.ContactDAO = arguments.ContactDAO />
   </cffunction>

   <cffunction name="getContactGW" access="public" output="false" returntype="any">
      <cfreturn variables.instance.ContactGW />
   </cffunction>
   
   <cffunction name="setContactGW" access="public" output="false" returntype="void">
      <cfargument name="ContactGW" type="any" required="true" />
      <cfset variables.instance.ContactGW = arguments.ContactGW />
   </cffunction>

Now our service has references to the ContactDAO and the ContactGW, in turn both the ContactDAO and ContactGW have references to our AppConfig. All the plumbing for the database interaction is complete.

Previously, we stored the ContactType in our ContactFormBean. This was acceptable when referential integrity was not on our list. Now, we need to modify the Contact Form and the Contact Form Bean to refer to the ContactTypeID instead of simply the ContactType. In the form itself, all we need to do is switch the reference. Inside the Bean there is a tiny bit more to it. We will start with the form.

The select tag block should be changed as follows

Type: <select name="ContactTypeID">
   <cfloop query="ContactTypes">
<option value="#ContactTypeID#" <cfif ContactFormBean.getContactTypeID() IS ContactTypeID>selected</cfif>>#ContactTypes.ContactType#</option>
</cfloop>
   </select>

Now open the ContactFormBean and change all the ContactType references to ContactTypeID. A find and replace would be nice here. A simply Find and Replace should make 11 changes...

There are two references in the Init() function, one in the validate function and also a get/set block at the bottom. Each of these should be changed to ContactTypeID.

The completed ContactFormBean is here:

<cfcomponent
   displayname="ContactFormBean"
   output="false"
   hint="A bean which models the ContactFormBean form.">



   <!---
   PROPERTIES
   --->

   <cfset variables.instance = StructNew() />

   <!---
   INITIALIZATION / CONFIGURATION
   --->

   <cffunction name="init" access="public" returntype="ContactManagerMG.model.ContactFormBean" output="false">
      <cfargument name="ContactID" type="string" required="false" default="" />
      <cfargument name="ContactName" type="string" required="false" default="" />
      <cfargument name="ContactTypeID" type="string" required="false" default="" />

      <!--- run setters --->
      <cfset setContactID(arguments.ContactID) />
      <cfset setContactName(arguments.ContactName) />
      <cfset setContactTypeID(arguments.ContactTypeID) />

      <cfreturn this />
   </cffunction>

   <!---
   PUBLIC FUNCTIONS
   --->
   
   <cffunction name="getMemento" access="public"returntype="struct" output="false" >
      <cfreturn variables.instance />
   </cffunction>

   <cffunction name="validate" access="public" returntype="boolean" output="false">
      <cfargument name="Errors" type="struct" required="true" />
      <cfset tempErrors = structNew() />

      <cfif NOT len( trim( getContactName() ) ) >
         <cfset tempErrors['ContactName'] = "Please enter a name for your contact" />
      </cfif>
      <cfif NOT len( trim( getContactTypeID() ) )>
         <cfset tempErrors['ContactTypeID'] = "Please enter a contact type for your contact" />
      </cfif>      
      
      <cfif structCount( tempErrors ) >
         <cfset structAppend( arguments.Errors, tempErrors ) />
         <cfreturn false />
      <cfelse>
         <cfreturn true />      
      </cfif>
      
   </cffunction>

   <!---
   ACCESSORS
   --->

   <cffunction name="setContactID" access="public" returntype="void" output="false">
      <cfargument name="ContactID" type="string" required="true" />
      <cfset variables.instance.ContactID = arguments.ContactID />
   </cffunction>
   <cffunction name="getContactID" access="public" returntype="string" output="false">
      <cfreturn variables.instance.ContactID />
   </cffunction>

   <cffunction name="setContactName" access="public" returntype="void" output="false">
      <cfargument name="ContactName" type="string" required="true" />
      <cfset variables.instance.ContactName = arguments.ContactName />
   </cffunction>
   <cffunction name="getContactName" access="public" returntype="string" output="false">
      <cfreturn variables.instance.ContactName />
   </cffunction>

   <cffunction name="setContactTypeID" access="public" returntype="void" output="false">
      <cfargument name="ContactTypeID" type="string" required="true" />
      <cfset variables.instance.ContactTypeID = arguments.ContactTypeID />
   </cffunction>
   <cffunction name="getContactTypeID" access="public" returntype="string" output="false">
      <cfreturn variables.instance.ContactTypeID />
   </cffunction>

</cfcomponent>

Finally, we need to change how the contacts are saved. In the beginning, we simply appended the ContactFormBean object into an Array of ContactFormBean object and iterated over the array for our ContactList. Now, we need to change the controller function to call the service. We will let the service decide how to handle the save. In the controller located at *ContactManagerMG.controller modify the isvalid branch to call a ContactService method called saveContact()

<cfif isValid >
       <cfset ContactService.saveContact( ContactFormBean ) />
       <cfset arguments.event.addResult("Success") />
    <cfelse>
       <cfset arguments.event.setValue("ErrorStruct", ErrorStruct) />
       <cfset arguments.event.addResult("Failure") />
    </cfif>

Now add the saveContact() function into your ContactService. This function will accept a Contact object, being our ContactFormBean, and check to see if the ContactID has been assigned. If so, we will update the record, if not, we will create the record. Your saveContact() function should look like this:

<cffunction name="saveContact" access="public" returntype="void" output="false">
      <cfargument name="Contact" type="any" required="true"/>
      
      <cfif val( Contact.getContactID() ) GT 0 ><!--- Update --->
         <cfset getContactDAO().update( arguments.Contact ) />
      <cfelse><!--- Insert --->
         <cfset getContactDAO().create( arguments.Contact ) />
      </cfif>

</cffunction>

After changing the save contact flow, we have closed the circle for adding / updating the individual contacts. Remaining, is the ContactList page.

We will now be returning a query and ourcontact list page used to operate on an array. Thus, we must adjust.

Change your dspContactList.cfm page to use a query. Your completed list page will look as follows:

<cfset myself = viewstate.getValue("myself") />
<cfset ContactList = viewstate.getValue("ContactList") />

<cfoutput>
   <cfif ContactList.recordcount IS 0 >
      -No Saved Contacts-<br />
   <cfelse>
      <table>
         <tr>
            <th>Name</th>
            <th>Type</th>
         </tr>   
      <cfloop query="ContactList">
         <tr>
            <td>#ContactName#</td>
            <td>#ContactType#</td>
         </tr>
      </cfloop>
      </table>   
   </cfif>
</cfoutput>

Reset your application and run your new Database Powered Contact-O-Matic. If you've followed all the steps, (and I haven't left any out) you now have a proper application.

We touched a good chunk of the application and I trust this has been a good refresher into the inner workings of our Contact-O-Matic. In our next series, we will include the mechanics to delete contacts from the database.

More Entries

BlogCFC was created by Raymond Camden. This blog is running version 5.9.001. Contact Blog Owner