Getting Started With Code Generators - Part 2
In the last post in the Project Learn series we installed the Illudium PU-36 Code Generator. This post will go into a little more detail and we will actually get into generating some code.
Before I get started I should give a quick mention that the actual application that we will be building in this series will be an extremely simple Content Management System (CMS). Essentially the system will have a users table, a pages table and a few other miscellaneous tables (tables for links, link targets, etc). I figured this would be the easiest way to dig into the code generators and framework while still being something that is actually a (hopefully) usable end product.
If you'd like to build along with this series you can download the attached scripts by clicking on 'Download' below. There are two scripts in the zip - one for creating the table structure in MSSQL 2000 and another for 2005. I will not be adding support for any other dbms since I really do not use any other dbms. After you have the zips, create a database called CMS and point a CF datasource to that db. Run the table creation scripts and you're database is ready to go.
So now we have our db, our CF DSN and the code generator. Open the code generator and you should notice that the new DSN 'CMS' should be available in the DSN drop down. I believe appending ?reload=1 to the url will reinit the DSNs if yours is not shown (I'm sure Brian will confirm if that is true). The next step is to chose a table and a template and enter the dot notation path to where your components will eventually reside. In my case my path will be 'cms.com.user'. Note - in looking ahead at the Mach-ii documentation this path should/may/will probably change in the future, but for now I'm just evaluating the generator - not customizing any of the code so I will come back later and regenerate the code with the proper path if I choose to ultimately use the resulting code.
Before I go further let me jump back to the Template option in the generator. For this let me quote Brian's documentation:
The generator allows you to choose the set of templates that you would like to generate against, allowing you the ability to add custom templates without modifying the core code. The custom stylesheets should be placed in a subfolder of /xsl/projects/. You will find a copy of the core files in there under the prototype folder that you can copy or rename. There is also an included set of templates for using Mark Mandel's Transfer ORM project.
I chose the 'default' template. In conjunction with the other options defined above you are now ready to make some code - just click on the 'Generate' button and several tabs of components are created. So far pretty simple I must say.
At this point we now have 6 components generated for us - bean, DAO, gateway, service, to and coldspring. Being new to full blown OO I am a little confused at this point. I'm OK with the bean, DAOand gateway. Beans are your individual data elements at the lowest level - essentially each column in your table represents a bean. Bean components have what are called 'assecors' - essentially your get and set methods. Here is an example of the userId getter and setter:
<cfargument name="userId" type="string" required="true" />
<cfset variables.instance.userId = arguments.userId />
</cffunction>
<cffunction name="getuserId" access="public" returntype="string" output="false">
<cfreturn variables.instance.userId />
</cffunction>
So what I'm picturing is a form in the application that will be posted with the individual form fields being used to 'set' the beans. Later I'm assuming that the DAO will 'get' the bean to do simple CRUD operations (note to self: in what scope will the beans ultimately be held?).
Before moving on to the DAO, let me add that Brian has a super cool feature in the bean component - a validate() method. Here's a snippet of the portion of the validate() function as it relates to the userId field from the database:
<cfset var thisError = structNew() />
<!--- userId --->
<cfif (NOT len(trim(getuserId())))>
<cfset thisError.field = "userId" />
<cfset thisError.type = "required" />
<cfset thisError.message = "userId is required" />
<cfset arrayAppend(errors,duplicate(thisError)) />
</cfif>
<cfif (len(trim(getuserId())) AND NOT IsSimpleValue(trim(getuserId())))>
<cfset thisError.field = "userId" />
<cfset thisError.type = "invalidType" />
<cfset thisError.message = "userId is not a string" />
<cfset arrayAppend(errors,duplicate(thisError)) />
</cfif>
Here Brian has built in validation based on the dbms metadata. He's determined that our userId is both required (does not allow null inserts) and nvarchar (isSimpleValue) and has pre-built the validation for us to return an array of structs containing all failed validation checks for all of the beans in this method. I'm sure I'd clean up the messages a little bit to make them a little more user friendly, but the fact that the code is in place seems to be a huge timesaver. I'm seeing this being tied into the form submission later on when we are 'setting' our beans.
Another cool feature is the dump method:
<cfargument name="abort" type="boolean" default="false" />
<cfdump var="#variables.instance#" />
<cfif arguments.abort>
<cfabort />
</cfif>
</cffunction>
The DAO is basically your general CRUD methods. I won't go into too much detail - everything generated looks very usable with minimal (if any) alteration. The one nice method I see (which I assume is probably pretty standard OO practice) is the save() method.
<cfargument name="user" type="cms.com.user" required="true" />
<cfset var success = false />
<cfif exists(arguments.user)>
<cfset success = update(arguments.user) />
<cfelse>
<cfset success = create(arguments.user) />
</cfif>
<cfreturn success />
</cffunction>
This method accepts the bean its only argument. It checks whether or not the primary key (userId) exists in the table and returns boolean. If it exists then the update() method is called. If not then the create() method is called. (note to self: would the userId be created then in a hidden form field?).
The next component to review is the gateway. The gateway is used to return an entire record set (or records by value) for your table. This method I can see needing to be customized for certain elements. For example when generating the 'page' elements for our CMS we'll certainly want to return a page with the associated 'link' elements, so the gateway will likely need to be customized as such.
So that covers the bean, DAO and gateway creation using the Illudium PU-36 Code Generator. The service component I'm sure will eventually act as a service layer with Mach-ii. A service layer is what the framework will talk to instead of directly talking to the component. This allows are model to be easily plugged into another framework in the future if we so chose to.
Overall I have to say that I am impressed with Brian's tool. The only complaint that I have so far is that I can not find the 'Save To File' button! The generated code seems clean, concise and appears to be ready to roll with little modification.
In my next post I'll take a look at cfcPowerTools to generate some more code. Overall I'm feeling a little confused, a little enlightened about OO. I'm sure as I dig my way through this many of these things will start to make sense for me.
Update: After talking with Brian it seems that the version that I obtained from Google Code was old - the new and most recent version is available at RIAForge. You can find it here: http://cfcgenerator.riaforge.org/



I just wanted to refer you to some articles that really helped me "get" the principles behind OO, from Robert Martin.
http://butunclebob.com/ArticleS.UncleBob.Principle...
In particular, the first 5 that relate to class design helped me the most (though, I will always remain a student on this, I think).
When you pick a table for code generation, can you specify only some of the table's fields? I often have tables with 30-40 fields (I did not design these), but I only need the CFC to work with 5-10 of the fields.
Cheers for a great wee tool Brian.
If this isn't the case here, let me know.
BTW, have you looked at this page in Safari or Firefox? Its' unreadable. On a humourous note, I initially was going to put in a comment to that effect before reading the article, and the captcha I got was "F U". So I didn't post :-)