NO MORE CAREER
POLITICIANS!
Get Out Of Our House: Replacing congress with TRUE citizens!
Contact Doug!
Learn About Doug!
View Doug Boude's online resume
updated 11/18/2009

View Doug Boude's profile on LinkedIn
Link to me!

Follow Doug Boude on Twitter
Follow me!

Be Doug's friend on Facebook
Befriend me!
(I promise not to follow you home)
OO Lexicon
Chat with Doug!
Recent Entries
You may also be interested in...
Web Hosting

best web hosting - top web hosting sites, thetop10bestwebhosting.com

Czech your Page Rank!
Check Page Rank of any web site pages instantly:
This free page rank checking tool is powered by Page Rank Checker service
Surf's Up!
Visit Egosurf.org and massage YOUR web ego!
My Score: 9,001
Doug's Books

Read (and recommend)

  • Men are from Mars, Women are from Venus
  • The Wisdom of Crowds: Why the Many Are Smarter Than the Few and How Collective Wisdom Shapes Business, Economies, Societies and Nations
  • Blink: The Power of Thinking Without Thinking
  • Head First Design Patterns
  • Transact-SQL Programming
  • What's So Amazing About Grace?
  • Just So Stories (Rudyard Kipling collection)

Reading

  • Prayer: Does it Make Any Difference?
  • Data Mining (Practical Machine Learning Tools and Techniques)
<< September, 2010 >>
SMTWTFS
1234
567891011
12131415161718
19202122232425
2627282930
Search Blog

Recent Comments
Re: Using Google as your CF Mail Server (by Mike at 9/07 4:02 PM)
Re: Viewing Option Text (in IE7) that's Wider than the Select List (by Nithin Chacko Ninan at 9/07 1:34 AM)
Re: Viewing Option Text (in IE7) that's Wider than the Select List (by Nithin Chacko Ninan at 9/07 1:33 AM)
Re: Configuring Apache To Use Multiple Versions of ColdFusion (by Lola LB at 9/06 6:28 AM)
Re: Configuring Apache To Use Multiple Versions of ColdFusion (by ComboFusion at 9/06 5:17 AM)
Re: Railo 3.1 on Windows Server 2008 and IIS7 - Part 3 of 3 (by Jon at 8/27 2:04 PM)
Re: Hosts File Changes Not Acknowledged on Vista 64 (by Spacy at 8/24 3:46 PM)
Re: THE DAY CFUNITED DIED (by ComboFusion at 8/23 10:50 AM)
Re: My Grandpa (by Tasha at 8/10 4:29 PM)
Re: Just What IS a 'Service Layer', Anyway? (by dougboude at 8/02 10:10 AM)
Categories
Archives
Photo Albums
Funnies (5)
Family (3)
RSS

Powered by
BlogCFM v1.11

29 July 2008
How to Think about Controller objects and Model objects
with Coldspring in the mix!

Even though MVC has been "all the rage" now for a good long time and several frameworks have been lovingly and painstakingly developed and shared by the CF community to help facilitate the migration to a more standardized OO approach to development, I do believe that there will always be a need to regularly return to the foundational principles of our craft. For some, an affirmation or further perfecting of their core thought processes; for others, the first exposure to a new way of thinking about and viewing the tools they wield as a problem solver and programmer. After having immersed oneself so deeply in MVC and OO for a substantial length of time and embracing those concepts so completely, these foundational principles can become so much a part of us that we hardly even recognize them as such, and tend rather to simply see them as a part of who we are and how we think. Sometimes, though, the privileged occasion arises when we get to once again see them as the wondrous, shiny ideas that they are, when a person who has had the tenaciousness and fortitude to embark upon that OO journey asks us to share our own insight in order to facilitate the evolution of their own assimilation of these concepts.

Alright, perhaps a bit too poetic a prelude, but I was recently asked to share my own understanding of the relationship between controller objects and model objects and the mechanics there. I couldn't help but get a little excited as I took the time to examine my own understanding of the subject and attempt to relate it to this person and realized just how much a part of my thinking it has become. Of course, it goes without saying that I know for a fact I still have a lot of room for growth in this area, but I do feel at least a sense of satisfaction that the couple of years I've invested so far in this has grown me more technically than I believe I had in the five years prior doing procedural coding.

 Okay, enough with the nostalgia already! Here's the question I was asked:

[10:09 PM]  hey Doug-
[10:10 PM]  thanks so much for all the tutorials and learning materials, can't thank you enough
[10:11 PM]  i've got a basic model glue site up and running with one exception, total MVC with not much in the model, totally bloated controllers. Still trying to get my head around calling and using the code from my model via the controller, any blog entries or anecodotal stories to suggest that would guide me?

My Response:

Hey, you're welcome for the tutorials and such. I definitely don't do it for the fame, but rather because I KNOW how painful it can be to traverse a new learning curve, espcially when it comes to frameworks, OO, and the like. If I can spare someone even ten minutes of hair pulling then I've accomplished what I set out to do! :)
 
Using your model code in your controllers (which is exactly what a person should be doing :) )...this is nothing more or less than giving your controller cfc an instance of your model cfc so it can manipulate it, call its methods, etc. For instance, imagine for a moment that we are attempting to log a person in to our app (framework doesn't matter, all we know is it's one that supports MVC). The user has just filled in their username and password and clicked the submit button, and those two pieces of info are now handed off to our controller via whatever mechanism the framework we're using has provided us. We have created for ourselves a Security.cfc that has a method called "Login".

 
<cfcomponent displayname="Security">
   <cffunction name="login">
       <cfargument name="username" />
       <cfargument name="password" />
    ...
   </cffunction>
</cfcomponent>


 
Now, Security is our controller, so by virtue of that fact the general rule of thumb is that the controller's job is to be nothing more than a liaison between the View (our forms and results page) and our model (where the real work gets done!), our controller cfc should have very little code in it and in fact should be using our model object to get the real work done. In order for it to do that, it needs an instance of it's Model security counterpart. In our scenario, we have a CFC living in the Model directory also called Security which performs the actual act of attempting authentication. Here's what our Model/Security.cfc might look like:
 

<cfcomponent displayname="Security">
   <cffunction name="login">
       <cfargument name="username" />
       <cfargument name="password" />
       <cfset var results = false /><!--- set the value of our return variable. by default we're going to return a result of false, meaning the authentication failed --->
       <cfquery name="qryLogin">
          select username from user_table where username = <cfqueryparam value="#arguments.username#" cfsqltype="cf_sql_varchar" /> and password = <cfqueryparam value="#arguments.password#" cfsqltype="cf_sql_varchar" />
       </cfquery>
       <cfif qryLogin.recordcount eq 1><!--- we authenticated! --->
            <cfset results = true />
       </cfif>
       <cfreturn results />
   </cffunction>
</cfcomponent>

 
Makes sense to you, I'm sure...same kind of code we've been using for decades to authenticate a person, right? Okay, so now we need for our controller to actually make the login call. Let's give our Security controller access to this functionality. Here's our controller Security CFC again...
 

<cfcomponent displayname="Security">
   <cffunction name="login">
       <cfargument name="username" />
       <cfargument name="password" />
       <cfset var objSecurity = createobject("component","Model.Security") />
       <cfset var results = false />
       <cfset results = objSecurity.login(username=arguments.username,password=arguments.password) />
       <cfreturn results />
   </cffunction>
</cfcomponent>

 
So, within our controller, we have created an instance of our Model security CFC and called its login method. I know this is a very simplified example, but it is precisely the way to think about the relationship between your model and your controller objects! We can get a little fancier if we like and put our object into the variables scope within our CFC so that we only create one instance of it and then let every method in the controller access it as needed, like this:
 

<cfcomponent displayname="Security">
 <cffunction name="init">
  <cfset variables._objSecurity = createobject("component","Model.security") />
 </cffunction>
 <cffunction name="getSecurity">
  <cfreturn variables._objSecurity />
 </cffunction>
 <cffunction name="login">
    <cfargument name="username" />
    <cfargument name="password" />
    <cfset var results = false />
    <cfset results = getSecurity().login(username=arguments.username,password=arguments.password) />
    <cfreturn results />
 </cffunction>
</cfcomponent>


 
Now any method in my controller that needs to use the model security object need only do a "getSecurity()" call! This is assuming that your Init method is getting called when your controller is instantiated by your framework; it would need to in order for the security object to be present and available.
 
Now, for many reasons, it is not the best idea to be hard coding object instantiation into your controllers. I mean, what if you did that and then somewhere down the road you had to make a change to your Model/security.cfc that now required the init method to be called upon instantiation? You would have to hunt down every place in your app where the security object was being instantiated and change the createobject line to include the init method call. In this scenario probably not a huge deal to do so; but potentially this could create a LOT of extra maintenance. This specific thing is why I have come to love Coldspring so much. It allows me to "inject" an instance of my model/security.cfc into my controller/security.cfc in one handy place so that I need never (probably) touch my controller when a change is made to my model as I described. Here is my controller when I inject the model/security object using Coldspring:
 

<cfcomponent displayname="Security">
 <!--- Coldspring Autowire methods --->
 <cffunction name="setSecurity" access="public" returntype="void" output="false">
  <cfargument name="Security" required="true" type="any" />
  <cfset variables._Security = arguments.Security />
 </cffunction>
 <cffunction name="getSecurity" access="private" returntype="any" output="false">
  <cfreturn variables._Security />
 </cffunction>
 
 <cffunction name="login">
    <cfargument name="username" />
    <cfargument name="password" />
    <cfset var results = false />
    <cfset results = getSecurity().login(username=arguments.username,password=arguments.password) />
    <cfreturn results />
 </cffunction>
</cfcomponent>

 
By adding Coldspring to the mix, when it instantiates your controllers and finds corresponding SET and GET methods like that, it will look to see if it has a definition for the object the set and get references (in this case, the set and get are for 'Security'...getSECURITY, setSECURITY...so it will be looking for a bean definition named SECURITY). If it does, it will go ahead and create that object and inject it into the controller for you so that it is "present and accounted for" from that moment on. So, I could theoretically define a whole bunch of different objects (aka 'beans') for Coldspring, and then when I need one of those objects in my controller, all i gotta do is create a SET and GET method that matches the object name. Let's say our Security controller ALSO needs to be able to send emails, and our app has an Emailservice CFC in the model. Well heck, let's just inject that bad boy and use it! :
 

<cfcomponent displayname="Security">
 <!--- Coldspring Autowire methods --->
 <cffunction name="setSecurity" access="public" returntype="void" output="false">
  <cfargument name="Security" required="true" type="any" />
  <cfset variables._Security = arguments.Security />
 </cffunction>
 <cffunction name="getSecurity" access="private" returntype="any" output="false">
  <cfreturn variables._Security />
 </cffunction>
 
 <cffunction name="setEmailService" access="public" returntype="void" output="false">
  <cfargument name="EmailService" required="true" type="any" />
  <cfset variables._EmailService = arguments.EmailService />
 </cffunction>
 <cffunction name="getEmailService" access="private" returntype="any" output="false">
  <cfreturn variables._EmailService />
 </cffunction>
 
 <cffunction name="login">
    <cfargument name="username" />
    <cfargument name="password" />
    <cfset var results = false />
    <cfset results = getSecurity().login(username=arguments.username,password=arguments.password) />
    <cfif not results>
      <cfset getEmailService().sendAdminEmail(subject="failed login") />
    </cfif>
    <cfreturn results />
 </cffunction>
</cfcomponent>

 
Coldspring just makes object injection SO simple, don't you think? Setters and getters are one way of injecting objects using Coldspring; it also can do it another way, which I won't get into right now (no need to muddy the waters at this juncture). Now, you should also know that there are times when individual Model objects ALSO need to use instances of other model objects (you may have encountered this already at some point). For instance, the example of an Emailservice object...by virtue of the very defnition of a service object (which I do have a blog post about), it is likely manipulating the methods of two or more OTHER objects. Just like our controller, for our model object to use another object, it has to have an instance injected into it somehow. Either we created that instance manually using a Createobject statement, OR we leveraged Coldspring to auto-inject those instances for us. The manner in which it is done is exactly the same as it is for controllers, no significant differences at all.
 
Okay, I know I've kinda gone on and on about this, but (if you can't tell) I do tend to have a passion for Coldspring and the beauty it brings to an OO application! But you know, even if you opt not to leverage Coldspring to perform the object injection and choose rather to do it manually (which I, for one, would not frown upon you for doing :) ), the concept is exactly the same: If one object needs to manipulate another object,  you must create an instance of the needed object(s) within the controlling object, whether that happens to live in the model or controller. Having said that, there are a few hard and fast rules of thumb when writing these apps that I always follow and which you may want to consider for yourself.
 
1. A controller's job is to be liaison between view and model, only. Keep controller methods as light as possible, doing as little work as possible.
2. A controller should NEVER talk to the database
3. A controller is the ONLY one who has the privilege of talking to Session or Application scopes (unless you have yourself a session facade object, which I personally have never seen the need for. If you DO have a session facade object, then IT is the only object in the app who has the privilege of talking to session. in that case, the session facade will have a corresponding controller and model object).
4. Model objects should be doing as much of the app's work as possible. In a nutshell, build your model objects as if tomorrow you would be told that you had to change frameworks and your model was the only thing you could take with you.
5. NEVER even consider injecting a controller object into a model object. BLAH! YUK! PTOOEY! In fact, never allow yourself the sacrelige of injecting one CONTROLLER into another. If you're seeing the possibility of a need to do that, then probably you need to refactor some logic and move some code around.
 
That's about it. Have I helped fill in any gaps? Have I opened any more? Did I address what it is you were needing? Let me know, I don't mind taking the time to share (and learn)!
 
Doug  :0)

 

 

 

Posted by dougboude at 9:31 AM | PRINT THIS POST! | Link | 2 comments



24 July 2008
Using Special Characters within Coldspring.XML

Just because a person uses Model Glue and has learned enough about XML to know how to create basic settings doesn't necessarily mean that they have a strong command of XML, right? Well, I needed to put a funky, "special character filled" value into my global config object for use later, and through some experimentation found that using XML's 'CDATA' allowed me to do this so I thought I'd share it since I hadn't seen it used in Coldspring.xml before. Allow the picture to speak volumes:

  <bean id="GlobalConfig" class="ModelGlue.Bean.CommonBeans.SimpleConfig">
    <property name="Config">
      <map>
        <entry key="DNS"><value>myDNS</value></entry>
  <entry key="FusionMapDir"><value>/FusionMaps</value></entry>
  <entry key="FusionJS"><value>/js/FusionMaps.js</value></entry>
  <entry key="FusionChartDir"><value>/FusionCharts</value></entry>
  <entry key="FusionChartJS"><value>/js/FusionCharts.js</value></entry>
  <entry key="PDFRoot"><value>/images/PDFMaps</value></entry>
  <entry key="adminEvents"><value>page.admin,page.datamgmt,page.entityadmin,page.dataadmin</value></entry>
       <entry key="chartSubCaption"><value><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Asia Matters for America\nwww.asiamattersforamerica.org\n]]></value></entry>
   </map>     
    </property>
  </bean>

I am using a third party Flash-based charting tool and needed to apply a global sub caption to all charts produced, so decided to store it in my Coldspring.xml as part of my global config bean.

Now, I will say that I first attempted to pass in that subcaption value within my modelglue.xml file as an argument, like so:

 

 <event-handler name="getChart">
   <broadcasts>
    <message name="getChartData">
     <argument name="qryName" value="qryChartData" />
     <argument name="entityTypeID" value="3" />
    </message>
    <message name="getChartXML">
     <argument name="subcaption" value="&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Asia Matters for America\nwww.asiamattersforamerica.org\n" />
    </message>
   </broadcasts>
   <results />
   <views>
    <include name="returnChart" template="dspBigChart.cfm" />
   </views>
  </event-handler>

Of course, that turned my xml invalid. So I tried doing it like this:

 

  <event-handler name="getChart">
   <broadcasts>
    <message name="getChartData">
     <argument name="qryName" value="qryChartData" />
     <argument name="entityTypeID" value="3" />
    </message>
    <message name="getChartXML">
     <argument name="subcaption" value="<![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Asia Matters for America\nwww.asiamattersforamerica.org\n]]>" />
    </message>
   </broadcasts>
   <results />
   <views>
    <include name="returnChart" template="dspBigChart.cfm" />
   </views>
  </event-handler>

Still invalid, so i just gave up and went with the coldspring.xml approach for time's sake. Does anybody know the correct way (if there is one) to utilize CDATA within an XML attribute value like I tried to do? I probably won't stray from the way I'm doing it now in my global config, but I am curious.

Posted by dougboude at 12:09 AM | PRINT THIS POST! | Link | 0 comments
29 June 2008
My FB 5.5+ Content Pulled a David Blaine!
FB 5.5+ for Model-Gluers
For any of you Model-gluers out there who are dabbling in Fusebox 5.5+ (which I happen to be for a current client project), I came across a hair-pulling situation that I wanted to share with you in the hopes that you'll not have to endure the same.

The symptom: you wrote a perfectly good controller method that is being called and, according to the debugging, is rendering all of your content just fine. But, your final page returns completely empty! It's just gone, not there, not even a single line in your page's source. Poof! It pulled a David Blaine. After spending more time on this than I care to admit, following are the details of the issue and the solution.

The Cause and Solution
You know how in your MG controllers and model CFCs you're in the habit of typing out all of your attributes and their values when declaring a new CFFUNCTION (such as, "<cffunction name="getEntityDetails" output="false" access="public">")? And, you know how in MG, 99.9% of the time, your methods don't need to render any output and so you set the output attribute to "false" just out of habit? Well, if you're using FB 5.5+ (at least in the 'no xml' mode, which is what I'm doing), you have to be mindful NOT to set the output attribute to false if your method is going to render any page output (which is the way to do it in FB 5.5+ in the 'no-xml' mode). You may be saying "duh, if it's rendering output of COURSE you wouldn't set output="false"! But, typically if a method is going to render output, it's going to do so with either cfoutput tags or the like, which would trigger your programmer mind to remind you to change the output attribute. In the approach that FB 5.5+ takes to do this, however, the natural mechanism that would trigger your mind to make the output value change isn't tripped, because we're just not used to seeing it done that way (eg; <cfset myFusebox.do( action="layout.main_template" ) />). At least it didn't trip mine.

Here's an example of a non-working and a working FB 5.5+ method that performs my login functionality and then renders the appropriate display page:

Non-Functioning method:
<cffunction name="login" returntype="void" output="false">
   <cfargument name="myFusebox" />
   <cfargument name="event" />
   [code to perform login logic here]
   <cfset myFusebox.do( action="display.dspHome",contentvariable="body" ) />
   <cfset myFusebox.do( action="layout.main_template" ) />
</cffunction>


Functioning method:
<cffunction name="login">
   <cfargument name="myFusebox" />
   <cfargument name="event" />
   [code to perform login logic here]
   <cfset myFusebox.do( action="display.dspHome",contentvariable="body" ) />
   <cfset myFusebox.do( action="layout.main_template" ) />
</cffunction>


So, even though FB 5.5+ can be used in an MVC manner, and even though many of the ways of thinking you've grown accustomed to with Model Glue do translate directly over to FB 5.5+, SOME DO NOT. Just remain mindful of this fact as you take the FB 5.5+ journey and you should be able to avoid hitting dead ends such as this one.

Doug out.
Posted by dougboude at 11:28 AM | PRINT THIS POST! | Link | 0 comments
27 May 2008
Model-Glue 'Generic Read' Time Tests

It's in the wee hours of the night and I'm trying to knock out one more item from my client's laundry list: speed up the load time of a specific template in his Model Glue app.

This particular template has several different select lists, each being populated with the results of a Model-Glue generic List broadcast. It also relies upon the return of a pre-populated object that is fairly "heavy" as far as Reactor-defined objects go (many hasOne/hasMany relationships), it being retrieved via a MG generic Read. (if you're not familiar with Model-Glue's generic database messages, check out the MG Docs). So, the first thing I thought to do was find out if a generic message was the most efficient way to interact with Reactor.

I created a simple method in my controller that, for all intents and purposes, duplicated the functionality of the generic Read:

<cffunction name="getRecordObject" access="public" output="false" returntype="void">
 <cfargument name="event" type="any" required="yes" />
 <cfset var reportID = arguments.event.getValue("reportID",0) />
 <cfset var retVal = "" />
 <cfset retVal = getModelGlue().getORMService().createRecord("Report").load(ReportID=reportID) />
 <cfset arguments.event.setValue("ReportRecord",retVal) />
</cffunction>

I then modified my modelglue.xml, commenting out the generic Read and replacing it with a message to my replacement method:

<!--

<message name="ModelGlue.genericRead">
 <argument name="recordName" value="ReportRecord" />
 <argument name="criteria" value="ReportID" />
 <argument name="object" value="Report" />
</message>

-->


<message name="getRecordObject" />

Now for some time tests based on Model-Glue's debugging output!

 

 

The results showed that, after the application's initialization ("init=true"), the generic Read was an average of THREE TIMES FASTER than the manual record creation using the ORM Service (99ms VS 307ms) . During initialization, the manual record creation was an average of 26 TIMES FASTER (422ms VS 11,070ms)! But, how often do you really need to reinitialize your app? I'll be sticking with the generic Read.

I didn't dig too deeply into why the performance differences might be there, but I did notice that the Model Glue's implementation of the generic Read is utilizing the ORMAdapter and the 'read/new' methods, whereas the manual call examples I found to create a Record object use the ORMService and the 'createRecord' method. Not sure what the difference there is, but there must be one.

Anyway, just thought I'd toss that out there as some rough empirical evidence to support the use of MG's generic messages (if your app uses an ORM  ).

P.S. If you're interested in the raw data(er), well, it was just too bulky and boring to take up blog space with it. If you really want to see the actual times, though, here they are.

Posted by dougboude at 2:05 AM | PRINT THIS POST! | Link | 1 comment
01 May 2008
Why I Hate ORMs (a solicited rant)
A necessary disclaimer...
Everything in the following post that appears to be an opinion most likely is and should be taken as such. My personal view on a topic does not (necessarily) invalidate any other opposing view. Readers should ascribe whatever value they so choose to the information that follows and either adopt it or reject it at their own discretion.

In the comment thread to a post I did on custom validation in Model Glue, I shared the fact that ORMs aren't my friends. In so doing, it prompted another commenter to ask me to share more details about my decision to be anti-ORM, as they too are weighing out the pros and cons of incorporating it:

"  Doug,
Can you elaborate on why you've abandoned ORM? I browsed thru your blog but didn't see any references to this decision. I'm wrestling with its costs vs benefits as well and would be interested in hearing from someone further down that path.

Thanks,
Dave  "


What follows are those details.

Why I am Anti-ORM


The Beginning

Just over a year ago, my team and I embarked on a journey to learn the ways of the object oriented programmer. We evaluated some of the popular Coldfusion MVC frameworks and decided to go with Model Glue. At the time, the trend was also to utilize other frameworks alongside Model Glue, so we too jumped on the bandwagon, reasoning that if we were going to adopt the tried and true standards of the OO world, we were going to embrace it completely. So, we architected our first MVC application, opting to utilize MG as our MVC framework, Coldspring as our IOC framework, and [brand X] as our ORM. For those of you who may not know what each of these frameworks are for, ModelGlue is the framework that separates, organizes, and "glues" together your views, your CFCs, and the controller CFCs that act as liaison between the other two. Coldspring is the IOC (Inversion of Control) framework whose job it is to manage the relationships between your CFCs. For instance, you might have a User.cfc that needs to perform a function located within another CFC such as Security.cfc. Using Coldspring, you can define that relationship ahead of time and, rather than hard coding the instantiation of your Security.cfc within your User.cfc, Coldspring will do that for you automatically whenever your app asks for the User.cfc. Ah, and now for [brand X]. {brand X] is an ORM framework whose job it is (and here's where my opinion and personal understanding heavily apply) to add a layer between your app and your database. The reason for this, you might ask? The reasons that my team and I saw for doing so were:

1) To allow us to easily change backend database platforms later down the road, should the need occur (not likely, right?)

2) To allow us to take advantage of some of the nifty "helper" features, such as auto-validation and scaffolding (automatically building Create, Update, View, and Delete forms for a given table)

3) To allow us to pre-define relationships between data so that we could "drill down" into child data by calling auto-generated methods, without having to query for it

4) Because using [brand X] was what every CFer "in the know" was doing, so it must be the right thing to do!

Sound like decent reasons, right?

I'll summarize, from my experience, the reasons that ORMs gave me to despise them. I'll try my best not to rant (actually I already did a lot of that in this other post).


The Reasons

The first reason my ORM gave me to hate it was that it forced me to have to synchronize case between field names and elements within different XML files. Coldfusion has LONG been a case-insensitive language for the most part, but suddenly now, as I am embarking on a journey through the foreignness of ORMs, I have to ALSO be mindful that everytime I type in a field name, I had just BETTER make sure I type it exactly the same way every time! Of course, I learned about this the hard way, through many hours of troubleshooting, following nearly useless error messages, and piecing together scraps and tidbits from here and there on the net building myself a "Franken-Solution".

Second reason: the Coldfusion ORM frameworks are a work in progress, and thus subject to undiscovered anomolies coded into their core. While my team and I were burning the midnight oil trying to learn and use our ORM, it kept changing on us. We'd update and replace core files only to find that some behaviors had suddenly changed. The one thing that DIDN'T change were the nearly useless error messages, though...that kept it "interesting" for us.

Hey, this ever happen to you? You're building an application and suddenly realize you need another field in a certain table, so you...go ahead and add it? Sheesh, it happens ten times a day sometimes! A very common occurrence, a very expected occurrence, right? Which leads me to the third reason my ORM gave me to hate it: our ORM didn't take kindly to change. It took us FAR too long to try and figure out how to cause it to see and propagate database changes throughout the app without spilling its groceries into a useless error message. The final solution, that took us weeks of evolving to: nuke it. Just delete the whole cotton-picking cache of files that our ORM had automagically generated for us and FORCE it to recreate them all. This approach defies common sense, and thus it was the end of our evolution and not the beginning. Why's an ORM gotta defy common sense?

The fourth reason: gluttony. As mentioned in the list of reasons why we chose to use an ORM, we wanted to leverage the beauty of pre-defining data relationships and be able to drill down into child data with ease. In theory, this is a beautiful thing. In practice, it's a disgusting hag. The bloated, inefficient objects within objects within objects that get created in this process can cause an app to literally CRAWL. What would take less than a second to perform the traditional way can take many hundreds of times that amount of time when using these auto-generated object nests. When you do take the time to define all of these relationships as they truly are (and good luck getting that right the first five tries), the resulting objects are ginormous and slow as molasses on a cold January morning. Painfully slow. Not acceptable, ORM. Not acceptable.

And ah, the grandest reason of them all that I pretty much despise ORMs: losing my beloved sql. Now, I wouldn't have a problem with losing sql as long as my ORM provides me with a suitable substitute. But it does not. The task of translating a simple sql statement into "ORM-speak" is far, far from simple, my friend. I and others I know have quite literally spent an entire DAY trying to figure out how to write the code, leveraging our ORM, to execute a simple join. Don't get me wrong, it IS do-able. But from the perspective of someone who knows how to query a database, doing the same thing using only ORM objects is convoluted, WAY over-complicated, gluttonous from an efficiency point of view, time consuming, and in the end....what the heck good is it anyway? What did I just do to help myself by spending an entire DAY figuring out how to define object relationships in a way that my ORM likes, defining events that have every parameter present and properly cased, making sure that the database changes I make along the way are actually recognized by my ORM and my application has been properly reinitialized, and then synchronizing the whole thing and crossing my fingers that it'll actually work and not throw me back some useless error? Writing and executing what was and is simple SQL is ridiculously convoluted by an ORM, eats up a huge amount of precious development hours in figuring out how to do it, and gives me only negative return on that investment by creating code that increases my bottom line in IO and makes it so that only another person who has drunk the ORM kool-aid can possibly ever understand. Oh yeah, and when you DO finally figure out how to write a simple join, you then realize that there are actually probably four or five different ways you could have done it using the ORM objects; which one is most efficient? who the heck knows, and you'll only know if you invest the hours to write it with each approach and time it yourself. And even your ORM's BEST and most efficient approach will STILL NOT beat the time you would get by executing straight SQL...there's no way.


Conclusion

What this and any decision comes down to is really just a list of reasons TO do something versus reasons NOT to do it. In this case, based on what I consider to be thorough personal experience combined with that of my peers, adopting an ORM has an extremely sparse list of "Pros" which in no way even come CLOSE to outweighing the list of Cons that come with it.

I realize I didn't do very well at filtering out my ranting. I also realize that, if you happen to be a person who has already gone to ORM prison and now you're used to the lifestyle of having it dwell within your apps, you're going to have a totally different take on it. But I'd be willing to bet money that even you die-hard ORMians felt the same way I do at one point in your J-Curve, only instead of ceasing to knock your head against the wall, you banged a little longer and finally broke through it. Now it's second nature to you to  know where NOT to step in order to avoid the landmines, so you can play soccer freely in the minefield having only lost a few virtual limbs in the process.  HOWEVER, if you are the person who has yet to step into that tempting and beckoning ORM minefield, if you're still on this side of the kool-aid, you should know that forcing yourself to use and learn an ORM is a bit like forcing yourself to learn to smoke. Yeah, given enough choking, hacking, and puffing, you CAN eventually learn to love the feeling of hot, acrid, killer gases in your lungs; but is the miniscule physical pleasure and social "coolness" worth the initial pain, suffering, and certain long term problems that go with it? I say no way, and after having hacked and puffed on my chosen ORM for a solid six months, I say no way to that as well.

I am of course committing a social faux pas by pre-judging all ORMs based on my experience with one, much the same way as it is commonly said that you can't judge all women by the way one woman treated you. But you know what? Even the slickest ORM is STILL only going to provide me with 2 or 3 pros, tops, and will STILL of necessity be adding overhead to my application's efficiency and learning curve to my timeline. From that perspective then, yes, I AM pre judging and have every intention of remaining "ORM Celibate" from here on out. I've worked on several applications since the one I mentioned earlier, all completely without an ORM involved, and have been utterly delighted with their performance. Good riddance, ORMs, I'm not missing you at all and will probably never recommend you.

Doug out.
Posted by dougboude at 11:54 AM | PRINT THIS POST! | Link | 20 comments
09 March 2008
Client-Side Drilldowns Made Easy
Last September I shared a post on an alternative to Ajax for client-side interactivity leveraging Coldfusion's WDDX. I'd like to take it a step further now and share an approach (and corresponding code) I often use in my Model-Glue apps when needing to create tiered or drilldown-type select lists withOUT having to make numerous calls to the server. The gist of this methodology is the same as in my previous post:
  1. Retrieve all needed data sets for populating the drilldowns;
  2. Convert those CF queries to a form that Javascript can manipulate;
  3. Write the necessary functions to populate the dropdowns based on previous options selected.

Here is the working sample. Go ahead, play with it!





The main difference in this example is the fact that I'm not utilizing WDDX, but rather JSON to make the data Javascript friendly.

Let me show you what the queries look like for each tier of this particular drilldown:
Tier 1Tier 2Tier 3

An important item of note here is that the data set for a given tier must include the parent ID of the previous tier in order to perform the filtering operations you'll see soon.

Okay, so how to get the queries into a format Javascript can manipulate. The route I chose to go was to convert the queries to JSON via one of two methods, depending on what version of CF you're using. Since not everyone is using version 8 yet, I made this example compatible with anything 6 or higher (versions previous to 8 are dependent on outside conversion; I chose to use JSON.CFC. Version 8 can utilize the built in function "serializeJSON"). Here's the statement where the queries are transformed (using the commonly known custom tag "QuerySim" to create the data for this example):

<cf_querysim>
    level_1
    level_1_id,name
    1|Colors
    2|Shapes
    3|Foods
</cf_querysim>
<cf_querysim>
    level_2
    level_2_id,level_1_id,name
    1|1|Red
    2|1|Blue
    3|1|Yellow
    4|2|Triangle
    5|2|Square
    6|2|Circle
    7|3|Bread
    8|3|Meat
    9|3|Fruit
</cf_querysim>
<cf_querysim>
    level_3
    level_3_id,level_2_id,name
    1|1|Fuschia
    2|1|Brick Red
    3|1|InfraRed
    4|2|Teal
    5|2|Cyan
    6|2|Navy Blue
    7|3|Light Yellow
    8|3|Dark Yellow
    9|4|Isosceles
    10|4|Equilateral
    11|4|Right Triangle
    12|5|Rectangle
    13|5|Parallelogram
    14|6|Ellipse
    15|6|Oval
    16|7|Matzah
    17|7|Hot Cross Buns
    18|7|Brioche
    19|8|Steak
    20|8|Fajitas
    21|8|Hamburger
    22|9|Kiwi
    23|9|Grapes
    24|9|Oranges
</cf_querysim>

<!--- convert our query data to JSON strings...mind the second parameter to the serializeJSON function... --->

<cfif listfirst(SERVER.ColdFusion.ProductVersion) gt 7>
    <cfset level_2_json = serializeJSON(level_2, true) />
    <cfset level_3_json = serializeJSON(level_3, true) />
<cfelse><!--- we're on less than version 8. use json.cfc --->
    <cfset objJSON = createobject("component","json") />
    <cfset level_2_json = replace(objJSON.encode(level_2),"data","DATA","all") />
    <cfset level_3_json = replace(objJSON.encode(level_3),"data","DATA","all") />
</cfif>

<script>
    //set our json data into Javascript objects
    var lev2data = <cfoutput>#level_2_json#</cfoutput>;
    var lev3data = <cfoutput>#level_3_json#</cfoutput>;
</script>

You'll notice in the section where I rely on JSON.CFC, I am doing a replace on the lower case word "data" to make it upper case. This is to make the JSON string produced consistent with the one produced utilizing serializeJSON. Since Javascript is case sensitive, case consistency in the JSON is required if you wish to utilize only one javascript function to perform the select list population. You'll also notice the use of the optional secondary parameter in the serializeJSON function call. This is needed in order to produce a JSON string that can be accessed by Javascript exactly the same as the JSON.CFC string.

MG NOTE: Regarding the fact that I utilize this approach in Model-Glue apps...I have my controller return an already formatted JSON string to my view rather than return a query and then perform the transformation there. Many of my controller methods have an optional "returnJSON" argument that I use when I need a JSON string back rather than a query.

Okay, data sets are available to Javascript. Now to write a few Javascript functions that can spin through that data and populate the appropriate select list. Here are the functions needed to perform the necessary tasks:

<script>
    //function to repopulate targeted select list
    function repopulate(targetObjID,targetDataSet,selectedIDVal, idColName, valColName, optionValColName){
        /*
         parameters:
         targetObjID - the ID of the select list we want to populate;
         targetDataSet - the actual Javascript data object we created previously;
         selectedIDVal - the name of the column in this data set that contains the parent record ID
         idColName - the name of the column that contains THIS tier's own record ID
         valColName - the name of the column that contains the data we want to display as the option text in the dropdown;
         optionValColName - the name of the column that contains the value we want to use as the new options's VALUE value;
        */
       
        //loop over the data object. for every object with a keyname of idval, add it to the dropdown
        var objTarget = document.getElementById(targetObjID);
        ResetObject(objTarget);
        for(i=0;i<targetDataSet.DATA[idColName].length;i++){
            if(targetDataSet.DATA[idColName][i] == selectedIDVal){
                objTarget[objTarget.options.length] = new Option(targetDataSet.DATA[valColName][i].substring(0,45),targetDataSet.DATA[optionValColName][i],false,false);
            }
        }
    }
    //function to clear a dropdown
    function ResetObject(objTarget){
            objTarget.options.length=0;
            objTarget.options[0] = new Option("---------------","",false,false);
            return;
    }

function resetAll(objIDList){//empty out all of the dropdowns specified in objIDList
        var idlist = objIDList.split(",");
        for (i=0;i<idlist.length;i++){
                ResetObject(document.getElementById(idlist[i]));
        }
        return;
}
    
</script>


Of note is the fact that we are working with three select lists. The first select list is always populated at load time and needs no Javascript intervention at all. Select lists "level_2" and "level_3", however, are being completely manipulated by the JS calls by one generically written function.

Last but not least, the HTML with the Javascript calls embedded in the select lists' onChange event:

<body>
    <h2>Client-Side Drilldown Example</h2>
    Level 1: <select name="level_1" id="level_1" onChange="resetAll('level_2,level_3');repopulate('level_2',lev2data,this.options[this.selectedIndex].value,'level_1_id','name','level_2_id');">
        <option value=""><cfoutput>#repeatstring("-",15)#</cfoutput></option>
        <cfoutput query="level_1">
            <option value="#level_1_id#">#name#</option>
        </cfoutput>
    </select>
    <hr width="25%" align="left">
    Level 2:
    <select name="level_2" id="level_2" onChange="resetAll('level_3');repopulate('level_3',lev3data,this.options[this.selectedIndex].value,'level_2_id','name','level_3_id');">
        <option value=""><cfoutput>#repeatstring("-",15)#</cfoutput></option>
    </select>
    <input type="button" value="show ID" onClick="alert(document.getElementById('level_2').options[document.getElementById('level_2').selectedIndex].value);">
    <hr width="25%" align="left">
    Level 3:
    <select name="level_3" id="level_3" >
        <option value=""><cfoutput>#repeatstring("-",15)#</cfoutput></option>
    </select>
    <input type="button" value="show ID" onClick="alert(document.getElementById('level_3').options[document.getElementById('level_3').selectedIndex].value);">
</body>

Voila! That's it! instant, client-side drilldown with only a single call to the server!

Hope it saves someone a little time. ;)

Doug out.
Posted by dougboude at 10:46 PM | PRINT THIS POST! | Link | 0 comments
21 February 2008
Finding 'Fat Finger' errors in Modelglue.xml
Ever felt so confident in your skills and ability to code an entire model glue event from the xml to the controller, model, and view that you decided to go ahead and do two or three entire events before you actually attempted to run your code, only to find out that SOMEWHERE along the lines you fat fingered your xml, and for the LIFE of you can't see it? All Model-Glue is going to tell you is that your XML is invalid, but it's up to you to find the change in case, missing slash, misspelling, or mis-nesting. ONCE IN A GREAT WHILE this happens to me (  ) , so I thought I'd share a handy little tool I use to save myself a lot of time and grief.

http://www.w3schools.com/dom/dom_validate.asp

Just paste your modelglue xml content into the little window and hit validate. In a jiffy your type-o will be staring you in the face!
Posted by dougboude at 12:31 PM | PRINT THIS POST! | Link | 2 comments
12 December 2007
Model-Glue Ambiguous Error Matrix
As any user of Model-Glue can attest to, there are often times when you get the ol' "OOPS!" message and it doesn't give you much of a clue as to what the REAL issue is. Well, in order to help myself save time during future development and decipher these rather vague and even misleading types of errors, I've begun keeping track of them. I capture the error itself, what I found the TRUE cause to be, and what I did to rectify it. I hope to one day turn the matrix into more of a Model-Glue knowledgebase of sorts, but for now here it is in its basic state, in hopes that it may help someone else save a little hair pulling.

Bear in mind that since the errors I encounter are generated by my own code, some of the details will be reflective of that code. Just look past code specifics and into the heart of the matter in order to find what, within your OWN application, spawned your non-informative Model-Glue error.

May I also encourage all of you Model-Glue developers to do a similar thing as you encounter such ambiguous and frustrating errors. If you'll forward those to me (dougboude att geemale dote calm) along with a cause and fix, I'll gladly add them for the benefit of the community!

Without further adieux, my "Ambiguous Model-Glue Error Matrix" as it currently exists:


Error TextCauseFix

Oops!

Message An error occured while Searching an XML document.

Detail Empty expression!

Extended Info Tag Context C:\ColdFusion8\wwwroot\reactor\core\objectTranslator.cfc (144) C:\ColdFusion8\wwwroot\reactor\core\objectTranslator.cfc (159) C:\ColdFusion8\wwwroot\reactor\core\objectTranslator.cfc (159) C:\ColdFusion8\wwwroot\reactor\core\objectTranslator.cfc (99) C:\ColdFusion8\wwwroot\reactor\core\objectFactory.cfc (214) C:\ColdFusion8\wwwroot\reactor\reactorFactory.cfc (80) C:\ColdFusion8\wwwroot\ModelGlue\unity\orm\ReactorAdapter.cfc (57) C:\ColdFusion8\wwwroot\ModelGlue\unity\controller\GenericORMController.cfc (113) C:\ColdFusion8\wwwroot\ModelGlue\unity\listener\Listener.cfc (26) C:\ColdFusion8\wwwroot\ModelGlue\unity\eventrequest\MessageBroadcaster.cfc (32) C:\ColdFusion8\wwwroot\ModelGlue\unity\framework\ModelGlue.cfc (359) C:\ColdFusion8\wwwroot\ModelGlue\unity\framework\ModelGlue.cfc (332) C:\ColdFusion8\wwwroot\ModelGlue\unity\framework\ModelGlue.cfc (306) C:\ColdFusion8\wwwroot\ModelGlue\unity\framework\ModelGlue.cfc (264) C:\ColdFusion8\wwwroot\ModelGlue\unity\ModelGlue.cfm (104) C:\ColdFusion8\wwwroot\Housefacks\index.cfm (50)

Case sensitivity issue when using Reactor with or without a Modelglue Generic Event. The generic commit lists the value as "ImageRecord"; the actual object exists as "imageRecord".

Portion of the event calling the generic commit: 
<message name=""ModelGlue.genericCommit"">
    <argument name=""recordName"" value=""ImageRecord"" />
    <argument name=""criteria"" value=""imageID"" />
    <argument name=""object"" value=""image"" />
</message>

reactor.xml corresponding entry:
<object name="hf_image" alias="image">
    <hasOne name="Report">
        <relate from="ReportID" to="ReportID" />
    </hasOne> 
</object>

Make sure that case matches on every front. Generic event's recordname value; generic event's object value; alias name in reactor.xml for that object; actual name of record object generated by Reactor. Correct all case values, put Reactor into Development mode (within coldspring.xml), then re-execute the event that broadcasts the generic message, ensuring that "&init=true" is appended to the url. This will regenerate the object with the proper case and everything will be in sync again.
The web site you are accessing has experienced an unexpected error. Please contact the website administrator. The following information is meant for the website developer for debugging purposes.
Error Occurred While Processing Request Model-Glue XML Problem: Bad tag. Every must have a NAME attribute.
The Event definition was malformed in the modelglue.xml file (or included xml file). Here is the offending event:
<event-handler name=""inspector.saveprefs"" access=""public"">
 
<broadcasts>
  <message name=""ModelGlue.genericCommit"">
   <argument name=""recordName"" value=""ClientRecord"" />
   <argument name=""criteria"" value=""ClientID"" />
   <argument name=""object"" value=""Client"" />
   <argument name=""validationName"" value=""PreferenceValidation"" />
  </message>
  <results>
   <result name=""commit"" do=""Inspector.ClientDetails"" redirect=""true"" append="""" preserveState=""false"" />
   <result name=""validationError"" do=""Inspector.ClientDetails"" redirect=""false"" append="""" preserveState=""true"" />
  </results>
  <views />
 </broadcasts>
</event-handler>

Ensure that the event definition is well formed. Even though from an XML standpoint it may be valid, it must also be valid from a Modelglue standpoint as well.

Oops!

Message The cause of this output exception was that: coldfusion.tagext.sql.QueryParamTag$InvalidDataException: Invalid data value [invite to parrty] exceeds maxlength setting [50]..

Detail

Extended Info

Tag Context C:\ColdFusion8\wwwroot\reactor\project\housefacks\Dao\ClientDao.cfc (694) C:\ColdFusion8\wwwroot\reactor\project\housefacks\Dao\ClientDao.cfc (13) C:\ColdFusion8\wwwroot\reactor\base\abstractRecord.cfc (148) C:\ColdFusion8\wwwroot\reactor\base\abstractRecord.cfc (139) C:\ColdFusion8\wwwroot\reactor\base\abstractRecord.cfc (129) C:\ColdFusion8\wwwroot\ModelGlue\unity\orm\ReactorAdapter.cfc (526) C:\ColdFusion8\wwwroot\ModelGlue\unity\controller\GenericORMController.cfc (139) C:\ColdFusion8\wwwroot\ModelGlue\unity\listener\Listener.cfc (26) C:\ColdFusion8\wwwroot\ModelGlue\unity\eventrequest\MessageBroadcaster.cfc (32) C:\ColdFusion8\wwwroot\ModelGlue\unity\framework\ModelGlue.cfc (359) C:\ColdFusion8\wwwroot\ModelGlue\unity\framework\ModelGlue.cfc (332) C:\ColdFusion8\wwwroot\ModelGlue\unity\framework\ModelGlue.cfc (306) C:\ColdFusion8\wwwroot\ModelGlue\unity\framework\ModelGlue.cfc (264) C:\ColdFusion8\wwwroot\ModelGlue\unity\ModelGlue.cfm (104) C:\ColdFusion8\wwwroot\Housefacks\index.cfm (50)

Occurs during attempted insert/update/commit, when the value of submitted form field contains more characters than the table field allows. This entry present because it CAN occur even when invoking Reactor's auto-validation. The reason this error could be seen WITH validation in place is due to the finicky nature of Reactor and how and when it regenerates its base objects. If the base object was not regenerated after the table had been modified, then no validation exists for any new fields that were added and we get this mostly untrapped error. Solution:
  • make sure reactor is in Development mode.
  • reinit the app while still in dev mode, walk through the portion of your app that performs validation
  • switch back to production mode for reactor
  • reinit the app

Oops!

Message Routines cannot be declared more than once.

Detail The routine setState has been declared twice in the same file. The CFML compiler was processing: A cffunction tag beginning on line 395, column 10. Extended Info

Tag Context C:\ColdFusion8\wwwroot\reactor\project\housefacks\Record\UserRecord.cfc (395) C:\ColdFusion8\wwwroot\reactor\core\objectFactory.cfc (145) C:\ColdFusion8\wwwroot\reactor\reactorFactory.cfc (26) C:\ColdFusion8\wwwroot\Housefacks\model\Authentication.cfc (62) C:\ColdFusion8\wwwroot\Housefacks\controller\AuthenticationController.cfc (25) C:\ColdFusion8\wwwroot\ModelGlue\unity\listener\Listener.cfc (26) C:\ColdFusion8\wwwroot\ModelGlue\unity\eventrequest\MessageBroadcaster.cfc (32) C:\ColdFusion8\wwwroot\ModelGlue\unity\framework\ModelGlue.cfc (359) C:\ColdFusion8\wwwroot\ModelGlue\unity\framework\ModelGlue.cfc (332) C:\ColdFusion8\wwwroot\ModelGlue\unity\framework\ModelGlue.cfc (306) C:\ColdFusion8\wwwroot\ModelGlue\unity\framework\ModelGlue.cfc (264) C:\ColdFusion8\wwwroot\ModelGlue\unity\ModelGlue.cfm (104) C:\ColdFusion8\wwwroot\Housefacks\index.cfm (50)

Added a relationship to Reactor.xml. Attempted to relate a User's state ID value to a record in the States table in order to be able to retrieve the state abbreviation as opposed to the state ID.
<object name="hf_User" alias="User">
    <hasMany name="Role">
        <link name="UserRole" />
    </hasMany>  
    <hasMany name="Report">
        <relate from="UserID" to="InspectorID" />
    </hasMany>
    <hasMany name="Client" >
        <relate from="UserID" to="UserID" />
    </hasMany>
    <hasMany name="Realtor">
        <relate from=""UserID"" to="UserID" />
    </hasMany>
    <hasOne name="membership">
        <relate from="membershipID" to="membershipID" />
    </hasOne>
    <hasOne name="State">
        <relate from="state" to="stateID" />
    </hasOne>
</object>

The PROBLEM is that User already has a field named "State", and since the States table is also called State, Reactor is attempting to create two methods with the identical name...one that would retrieve the value of the State FIELD, and one that would retrieve the State OBJECT. This violates the rules.
SOLUTION: Use the attribute "alias" within the hasOne or hasMany tag to alias the method that will be generated. Like so:
<hasOne name="State" alias="ST">
    <relate from="state" to="stateID" />
</hasOne>

An error occured while Parsing an XML document.

The element type "value" must be terminated by the matching end-tag "".

The error occurred in C:\ColdFusion8\wwwroot\coldspring\beans\DefaultXmlBeanFactory.cfc: line 87 Called from C:\ColdFusion8\wwwroot\coldspring\beans\DefaultXmlBeanFactory.cfc: line 63 Called from C:\ColdFusion8\wwwroot\ModelGlue\unity\ModelGlue.cfm: line 71 Called from C:\ColdFusion8\wwwroot\Housefacks\index.cfm: line 50 85 : 86:00:00 87 :

88 : 89:00:00
ID10T error. Accidentally erased part of a tag in Coldspring.xml
look at the change that was just made and correct the malformed tag. In this case, it was a property tag:
 
<property name="mode"><value>production/value></property>
 

Model-Glue: C:\ColdFusion8\wwwroot\catschedule\scheduler\config\security.xml isn't valid XML!

The error occurred in C:\ColdFusion8\wwwroot\ModelGlue\unity\loader\XmlConfigurationLoader.cfc: line 76 Called from C:\ColdFusion8\wwwroot\ModelGlue\unity\loader\XmlConfigurationLoader.cfc: line 99 Called from C:\ColdFusion8\wwwroot\ModelGlue\unity\loader\FrameworkLoader.cfc: line 98 Called from C:\ColdFusion8\wwwroot\ModelGlue\unity\ModelGlue.cfm: line 116 Called from C:\ColdFusion8\wwwroot\catschedule\scheduler\index.cfm: line 72

 
           74 : </p>
            <cfset cfg = xmlParse(cfg).xmlRoot />    
            75 :            <cfcatch>    
            76 :                    <cfthrow message="Model-Glue: #configurationPath# isn't valid XML!" />    
            77 :            </cfcatch>    
            78 :    </cftry>    
Mal-formed modelglue.xml (or included configuration xml file). If you've looked it over carefully and your tags are all balanced, you might try also verifying that you're using the same CASE for all tag names. XML is case sensitive, so "value" is not the same as "Value". It is recommended that you not make TOO many changes to your modelglue configuration file before testing it, otherwise tracking down the offending line may be a very frustrating and time consuming task.
Oops!   
   
Message    An error occured while Parsing an XML document.
Detail    The content of elements must consist of well-formed character data or markup.
Extended Info   
Tag Context    D:\web\housefacks.com\www\reactor\core\objectTranslator.cfc (91)
    D:\web\housefacks.com\www\reactor\core\objectFactory.cfc (214)
    D:\web\housefacks.com\www\reactor\reactorFactory.cfc (80)
    D:\web\ModelGlue\unity\orm\ReactorAdapter.cfc (271)
    D:\web\ModelGlue\unity\orm\ReactorAdapter.cfc (145)
    D:\web\ModelGlue\unity\orm\ReactorAdapter.cfc (250)
    D:\web\ModelGlue\unity\controller\GenericORMController.cfc (73)
    D:\web\ModelGlue\unity\listener\Listener.cfc (26)
    D:\web\ModelGlue\unity\eventrequest\MessageBroadcaster.cfc (32)
    D:\web\ModelGlue\unity\framework\ModelGlue.cfc (359)
    D:\web\ModelGlue\unity\framework\ModelGlue.cfc (332)
    D:\web\ModelGlue\unity\framework\ModelGlue.cfc (306)
    D:\web\ModelGlue\unity\framework\ModelGlue.cfc (264)
    D:\web\ModelGlue\unity\ModelGlue.cfm (104)
    D:\web\housefacks.com\www\index.cfm (50)
This error was thrown by Reactor when it attempted to XMLParse a dictionary file for a given table. What has probably happened is you are using SVN in your development cycle, and when you performed an update to the destination code, SVN did a kind of "merge" on one of your dictionary files and flubbed it all up for you, thinking it was helping. Because the merged file is now not valid XML, Reactor barfed on it. Replace the target/faulty dictionary file with what's in the repository rather than attempt to merge it.
Bean creation exception in model.client

Cannot declare local variable homehistory twice.:Local variables cannot have the same names as parameters or other local variables.:

Line: -1   The error occurred in C:\ColdFusion8\wwwroot\MyApp\coldspring\beans\BeanDefinition.cfc: line 400
Called from C:\ColdFusion8\wwwroot\MyApp\coldspring\beans\BeanDefinition.cfc: line 215
Called from C:\ColdFusion8\wwwroot\MyApp\coldspring\beans\DefaultXmlBeanFactory.cfc: line 566
Called from C:\ColdFusion8\wwwroot\MyApp\coldspring\beans\DefaultXmlBeanFactory.cfc: line 495
Called from C:\ColdFusion8\wwwroot\MyApp\ModelGlue\unity\loader\XmlConfigurationLoader.cfc: line 293
Called from C:\ColdFusion8\wwwroot\MyApp\ModelGlue\unity\loader\XmlConfigurationLoader.cfc: line 210
Called from C:\ColdFusion8\wwwroot\MyApp\ModelGlue\unity\loader\XmlConfigurationLoader.cfc: line 58
Called from C:\ColdFusion8\wwwroot\MyApp\ModelGlue\unity\loader\XmlConfigurationLoader.cfc: line 74
Called from C:\ColdFusion8\wwwroot\MyApp\ModelGlue\unity\loader\FrameworkLoader.cfc: line 73
Called from C:\ColdFusion8\wwwroot\MyApp\ModelGlue\unity\ModelGlue.cfm: line 94
Called from C:\ColdFusion8\wwwroot\MyApp\index.cfm: line 50

398 :                           <cfthrow type="coldspring.beanCreationException"
399 :                                   message="Bean creation exception in #getBeanClass()#"
400 :                                   detail="#cfcatch.message#:#cfcatch.detail#:#additionalInfo#">
401 :                   </cfcatch>
402 :           </cftry>
The model.client CFC had a local variable named the same as one of the arguments

<cfargument name="myVar"... />
<cfset var myVar = "foo" />
Rename either the local variable or the argument.
Posted by dougboude at 11:20 AM | PRINT THIS POST! | Link | 4 comments
04 October 2007
Yet Another Model-Glue Quickstart...
Jim Pickering, the manager of the Kansas City CF User group, recently started a group project to rebuild their UG's web site. The project has the secondary agenda of promoting team development and learning Model-Glue, so he invited me to participate by setting up a skeleton app with basic security and then giving his volunteers a "Model-Glue Quickstart".  The entire presentation is just over an hour long, and includes:

  • Jim giving an overview of setting up the local development environment (minutes 0 through 2),
  • a Model-Glue overview and dissection of the basic security functionality (minutes 2 through 34),
  • and the adding of a new piece of functionality to the app (minutes 34 through the end).

There are places where the visuals and the dialog got a bit out of whack (minutes 15 or so and onward), but it levels out enough to not be irritating around minute 25.

So if you're interested in yet another high speed "Model-Glue for The Rest of Us" style preso, here it be!

https://admin.adobe.acrobat.com/_a200985228/p70824254/
Posted by dougboude at 12:51 PM | PRINT THIS POST! | Link | 8 comments
03 October 2007
Model-Glue XML Isn't Valid!!! Short Reminder
Don't you hate it when you're focused on adding functionality and/or troubleshooting issues in your Model-Glue app and when you save your modelglue.xml file to see if it all works right, you get the error:

Model-Glue: C:\ColdFusion8\wwwroot\yourAppName\config\modelglue.xml isn't valid XML!

I hate it. A lot.

See if you can spot the earth-shattering error in the following snippet of trouble-causing xml:


<event-handler name="view.register">
    <broadcasts />
    <results>
        <result do="view.template" />
    </results>
    <views>
        <include name="body" template="dsp_register.cfm" />
    </Views>
</event-handler>


This has been my day to be mentally challenged for some reason, so perhaps all of you found it quickly. But fifteen minutes, several experiments to isolate the offending code block, and a visit to an online XML validator later, I was reminded that XML is case sensitive. The way my font settings are in my IDE make a lower case and capital "V" look VERY much alike, so I kept looking right over it.

Just thought I'd share this in case it helps someone else save a few minutes. ;)

Posted by dougboude at 4:36 PM | PRINT THIS POST! | Link | 2 comments
27 September 2007
Global Configuration in Model-Glue: Using Your Own Config CFC
In a post I did a while back, I shared what I had learned about using Model-Glue's built in "SimpleConfig" cfc. Well, I just got a question from a fellow developer who was trying to leverage the information in that post to implement his own home grown configuration settings cfc, and what I had shared there didn't quite apply. I tried to email him back, but it was returned as undeliverable, so I thought I'd just post my response to him here for the sake of anybody else who is endeavoring to do the same thing.

His email:

Hi Doug,

I love your post (http://www.dougboude.com/blog/1/2007/06/Global-Configuration-Settings-in-ModelGlueUnity.cfm)

I was actually finding it hard to implement though. I basically have written a settings.cfc with a medium  sized struct containing all my site settings. I have functions like getProperty([name]) which returns the required value.

Now, I am just getting into MG and i  am finding it hard to simply put this in the global scope available to my Models, Views and Controllers.

I read your article but am still confused.

Can you help us out with this?

Thanks



My response to...we'll call him Johnny:


Sure can, Johnny!  Since you have created your OWN settings CFC, which I have also done in other MG apps, here's what I completely recommend doing: use Coldspring. 

What we're going to do is let Coldspring handle the "injection" of our settings object into those other objects that need it. Let's say for an example that we have a USER.cfc in our model directory that will need the DSN in order to perform a query. The DSN value is located in our SETTINGS.cfc. So, in the Coldspring.XML file we're going to have to do two things:

1. tell Coldspring about our settings.cfc
2. tell Coldspring about our user.cfc, at the same time telling it to put 'settings' inside of 'user' 

Now, two questions become immediately apparent:

1. What is the proper syntax for telling Coldspring the above two items?
2. What do I have to do special inside of my USER.cfc in order to help Coldspring do what I told it to do?

Telling Coldspring About Your Objects

Look at the Coldspring.XML snippet below. It is accomplishing the first two items we said we needed to tell Coldspring about (our settings cfc and our user cfc):

<bean id="Settings" class="model.settings" />

<bean id="User" class="model.user" >
    <property name="Settings">
       <ref bean="Settings"/>
    </property>
</bean>


Accommodating Coldspring Within Our CFCs

Now, what do we do special inside our User.CFC in order to help Coldspring accomplish what we told it to do? We create two additional methods, one called SetSettings and one called GetSettings.

Here's what those two methods should look like:

<CFFUNCTION name="SetSettings" access="public" returntype="void" output="false">
       <CFARGUMENT name="Settings" type="model.settings" required="true" />
       <CFSET variables._Settings = arguments.Settings />
</CFFUNCTION>

<CFFUNCTION name="GetSettings" access="public"
returntype="model.settings" output="false">
       <CFRETURN variables._Settings />
</CFFUNCTION>


By us including those two methods (notice the correlation between the names of the methods...get[bean id here] and set[bean id here] to the ID value we chose in the Coldspring.XML snippet), Coldspring will inject an instance of your settings object. Then, within any method inside of the User.cfc, we can access our settings
with "GetSettings().getProperty([name])".

For clarification, here is a sample USER.cfc to illustrate what I mean:
<CFCOMPONENT DISPLAYNAME="user object" >
    <CFFUNCTION name="SetSettings" access="public" returntype="void" output="false">
           <CFARGUMENT name="Settings" type="model.settings" required="true" />
           <CFSET variables._Settings = arguments.Settings />
    </CFFUNCTION>
   
    <CFFUNCTION name="GetSettings" access="public" returntype="model.settings" output="false">
           <CFRETURN variables._Settings />
    </CFFUNCTION>
   
    <CFFUNCTION NAME="login" ACCESS="public" RETURNTYPE="string">
        <CFARGUMENT NAME="username" TYPE="string" REQUIRED="yes" />
        <CFARGUMENT NAME="password" TYPE="string" REQUIRED="yes" />
       
        <cfset var retval = "" />
       
        <CFQUERY NAME="LogMeIn" datasource="#GetSettings().getProperty("DSN")#">
            select * from users where
            username = <cfqueryparam value="#arguments.username#">
            AND
            password = <cfqueryparam value="#arguments.password#">
        </CFQUERY>
       
        <cfif LogMeIn.recordcount eq 1>
            <cfset retval = "success" />
        <cfelse>
            <cfset retval = "failure" />
        </cfif>
       
        <CFRETURN retval />
    </CFFUNCTION>
</CFCOMPONENT>
This sample assumes that your homegrown settings cfc uses a method called 'getProperty' to retrieve values


That's it!

For every CFC that requires an instance of your Settings.cfc, just make sure that you define it in Coldspring.XML AND include the Set and Get methods named appropriately inside of the recipient CFC.


A SPECIAL CASE

IF you are trying to inject your Settings.cfc into a Modelglue CONTROLLER cfc, then there's one less step involved for you. The controller CFC still needs the GET and SET methods present inside itself, BUT you do NOT need to define the controller bean within Coldspring.XML. Allow me to repeat myself: As long as the Settings bean is defined in Coldspring.XML, all you have to do to have that injected into a CONTROLLER cfc is to create a SET and GET method identical to the examples above within the recipient controller cfc.


In Closing

I'll close by saying that there are other ways, using Coldspring, to get a Settings object into other objects, but in my opinion those approaches are no better or worse than the one I described above, so let's just keep it simple and stick to this one.

Hope this helps, Johnny, and let me know if you have any more questions; I'm always glad to share what I've learned.

Doug  :0)
Posted by dougboude at 3:21 PM | PRINT THIS POST! | Link | 0 comments
25 September 2007
My Approach to Basic Security in a Model-Glue Application
By now almost all of us have "rolled our own" when it comes to giving a Coldfusion app some semblance of security, so the concept is nothing new. It can be, however, somewhat daunting when attempting to take what we know procedurally and apply it to an MVC styled application.

The Kansas City Coldfusion User Group invited me to assist them with a group project using Model-Glue, specifically asking me to show them how to implement security. What follows is my way of thinking about and approaching the implementation of basic security in a Model-Glue application. At the end of this post is a link to download the tutorial app I put together for them as well since I know "a line of code replaces a thousand lines of abstract explanations".

GOALS OF SECURITY

What we’re wanting to accomplish is this:
  1. User arrives at our app.
  2. We evaluate their current login status, setting the appropriate values in the appropriate places so that those portions of our app who DO care if the user is logged in or not can make the right choices.
  3. Those values follow the user along their tour of our app.
     

PRE-REQUEST ACTIONS

Putting this scenario under the MG microscope then, and following along the path of the MG event lifecycle, here’s what I want to make happen:

     
  1. User arrives, an event is called (either the default event if none is specified or the one explicitly called);
  2. Before that event is processed, I ensure that I have in session for this person:
    • a “loggedin” variable with either a value of true or false;
    • a user object, whether it’s empty or populated.
  3. Make those session values available to the rest of my app by placing them in the Event bucket
     
If I do accomplish the above checklist, then any template or process that cares about authentication will need only access the Event object or the Viewstate object (depending on whether it’s a view template as opposed to a controller or model object) in order to find out this user's status.

Okay, so how do I accomplish the above laundry list? Model-Glue comes pre-configured with a generic controller that has methods and listeners already set up to perform whatever action you want at the following times:
  • onRequestStart
  • onRequestEnd
  • onQueueComplete
When you first unpack your Model-Glue sample app, you'll see something similar to the following already present in Modelglue.XML:
<controllers>
    <controller name="GlobalController" type="controller.Controller">
        <message-listener message="OnRequestStart" function="OnRequestStart" />
        <message-listener message="OnQueueComplete" function="OnQueueComplete" />
        <message-listener message="OnRequestEnd" function="OnRequestEnd" />
    </controller>
</controllers>

This is the generic controller I was referring to, and the OnRequestStart listener and method is the one we'll be leveraging. Since OnRequestStart is called before the event itself is executed, it is the perfect place to do ensure that our default values are present and available to the rest of the app at all times.

Here is the onRequestStart method after I modified it to accommodate security:

<cffunction name="onRequestStart" access="public" returnType="void" output="false">
    <cfargument name="event" type="any">
    <cfif not structkeyexists(session,"user")>
        <cfset session.user = getUser().init() />
    </cfif>      
    <cfif not structkeyexists(session,"loggedin")>
        <cfset session.loggedin = "false" />
    </cfif>
    <cfset arguments.event.setValue("loggedin",session.loggedin) />
    <cfset arguments.event.setValue("user",session.user) />
</cffunction>

Here's what's happening.:

Before any event is executed, I am first checking for the existence of a "user" key in session. If it’s not there, I’m creating an empty instance of the user.cfc in my model (which was auto-wired in to this controller…you can see how that's done by checking out the sample app at the end of this post) and putting it into session. I’m then checking for the existence of a "loggedin" key in session, and if not there, creating it with a value of false. After this, I put both session values into the Event bucket and let the event that was called finish its execution.

THE LOGIN PROCESS

Okay, so now we have appropriate values present and available throughout the app. How about the actual logging in process? Let’s walk through it and see how the different parts are arranged. It’s pretty simple (and mostly familiar), you’ll see.

The Process:
  • User is presented with a login form whose action calls the “login” event;
  • The login event makes a broadcast to the authentication controller to perform it’s “login” method, username and password being present in the Event bucket since they were submitted via the login form;
  • The Authentication controller creates an empty instance of the user object, then calls the user object’s ‘login’ method, passing in the username and password arguments;
  • Internally to the User object, IF the user was found based on the credentials passed in, it populates its internal variables (firstname, lastname, id, etc.);
  • The authentication controller checks to see if the User object’s ID value is anything other than the default value of zero. If it IS, then authentication was successful. If it’s zero, authentication failed.
  • If authentication was successful, we put that instance of the user object into session, set session.loggedin equal to true, put both the user object and the loggedin value  into the Event bucket, and add a Result value of “success” so that our event will know what action to take.
  • If authentication failed, we put a message into the Event bucket indicating this (“login failed! Try again!”), and add a Result value of “failure”, again so that our event will know where to redirect to.
Here is my Authentication Controller's Login method:

<cffunction access="public" name="login" output="false" returntype="void" hint="I make the call to log a user in">
    <CFARGUMENT name="event" type="any">
    <cfset var objUser = getUser().init() />
    <cfset objUser.login(username = arguments.event.getValue("username"), password = arguments.event.getValue("password")) />
    <cfif objUser.getID() IS NOT 0>
        <cfset session.loggedin = true />
        <cfset session.user = objUser />
        <cfset arguments.event.setvalue("loggedin",true) />
        <cfset arguments.event.addResult("success") />
    <cfelse>
        <cfset arguments.event.setvalue("loggedin",false) />
        <cfset arguments.event.setvalue("message","Login failed! Try again.") />
        <cfset arguments.event.addResult("failure") />
    </cfif>
</cffunction>


SUMMARY

Those are the items of interest to understand and think about when doing basic authentication. Now, since I know that reading about a thing abstractly is not NEARLY the same as chewing on actual code, I zipped up the whole security skeleton app so you can download it and poke through its innards. It has nothing in it except for the basic security outlined above, but it DOES function (I'm using Querysim in my model).

Also, just for disclaimer purposes, I didn't invest a lot of time making it overly pretty, nor did I comment it as heavily as I normally would. I do believe, however, that it's a very good jumping off point for someone who has been wondering "how to think" about security in an MVC app.

Hope you find it useful!

Doug out.

NOTE 8/24/2009 - I realized long after I did this post that I actually coded in something in a wrong way: the way a new user bean is retrieved. I erroneously auto-wired in the user object to my controller, which causes multiple users to see each others information! What should happen is that the user bean must be gotten directly from the model. If you are using Coldspring (which I highly recommend), then the way you should get a new instance of the user object is by calling "getModelGlue.getConfigBean('user')". In your Coldspring.xml file, you will also need to make sure that you have the Singleton flag set to true, as in "<bean id="user" class="model.user" singleton="true" />". So, instead of this line:

getUser().init()

you would use this line:

getModelGlue.getConfigBean('user')

Sorry for the misinformation! :)



DOWNLOAD SECURITY SKELETON APP ZIP

Posted by dougboude at 3:54 AM | PRINT THIS POST! | Link | 13 comments
21 September 2007
ModelGlue addResult/setValue Order Matters!
Small But Time Consuming Modelglue Gotcha
Attempting a save action, evaluating the outcome, and adding a subsequent "success" or "Check your Work!" message, along with a Modelglue Result value (in order to redirect appropriately) are common things to do when building an app, right? Well, I burned about fifteen minutes this morning trying to do this simple task, tracing events and double checking code, trying to figure out WHY the message value I added to the Event object wasn't present in the final viewstate.

Let me show you two versions of a snippet from my controller. The first snippet resulted in NO "message" value being present, the second one DID:


<cfif retValresult is "success">
    <cfset arguments.event.addResult("success") />
    <cfset arguments.event.setValue("message","Your Dream has been Saved!") />
<cfelse>
    <cfset arguments.event.addResult("failure") />   
    <cfset arguments.event.setValue("message","There's a problem with your Dream...") />
</cfif>
the value "message" was NOT present in my viewstate!


<cfif retVal is "success">
    <cfset arguments.event.setValue("message","Your Dream has been Saved!") />
    <cfset arguments.event.addResult("success") />
<cfelse>
    <cfset arguments.event.setValue("message","There's a problem with your Dream...") />
    <cfset arguments.event.addResult("failure") />
</cfif>
the value "message" WAS present in my viewstate!

Apparently, once Modelglue sees that a Result value has been added to the event object, "that's all she wrote, time to evaluate actions and ignore whatever code follows!"

Just thought I'd share this in case it saves someone else fifteen minutes.
Posted by dougboude at 6:52 AM | PRINT THIS POST! | Link | 2 comments
12 September 2007
Snippet: Outputting Reactor Validation Structures
Reactor's built-in validation produces a validationError structure when it encounters something amiss, and passes it back to your viewstate. That structure is actually a structure of arrays.

I DO use Reactor's validation, and found myself copying and pasting the same snippet for outputting the validation error results a time or two this morning... figured it might come in handy for other folks, too.

code from my view template...
<cfset validation = viewstate.getValue("[name of error structure here]", structnew()) />

<cfif structcount(validation) gt 0>
    <cfoutput>
        <cfloop collection="#validation#" item="v">
            <cfloop from="1" to="#arraylen(validation[v])#" index="i">
                #validation[v][i]#<br>
            </cfloop>
        </cfloop>
    </cfoutput>
</cfif>


A couple of things to remember:

1. The "[name of error structure here]" value is determined by the value you gave it in your event

sample generic Commit showing where the name of the validation error structure is designated...
<message name="ModelGlue.genericCommit">
    <argument name="recordName" value="ClientRecord" />
    <argument name="criteria" value="ClientID" />
    <argument name="object" value="Client" />
    <argument name="validationName" value="PreferenceValidation" />
</message>


2. When calling the viewstate.getValue() method, you can specify a default value to use if the item isn't found. In my snippet, I specified that "if the validation structure isn't there, just give me back an empty structure so my code won't break".

That's it!
Posted by dougboude at 1:23 PM | PRINT THIS POST! | Link | 0 comments
Reactor: A LOT Like a Wendys Drive Through
Okay, I've been up since 3 A.M (went to bed waaay too early last night) working on a Modelglue project, and now I'm feeling the need to rant a little bit. Not complain, per se, because I truly do appreciate the blood, sweat, and tears that must have gone into creating the frameworks that comprise Unity; But out of the 4 hours I "worked", a full third of it was spent on issues related to trying to get my app to "see" certain kinds of changes, even WITH my cheat sheet (which itself was born out of a lot of time spent pulling my hair and bumping around in the dark). That's just a wee bit much for something that's supposed to help me spend my time more efficiently, wouldn't you agree? In particular, this morning my beef is with Reactor.

Reactor has a Development mode and a Production mode. In theory, I should put this baby in Dev gear and just leave it there until I'm ready for production. But, Dev mode is just plain slow as molasses in January, so I opt rather to work with Reactor in production mode until and unless I make a change directly related to the database. This morning happened to be one where I had added some fields to a table. So I make my table changes, change Reactor to Development mode, and reinit the app. I then walk through my app as a user would to the point where I KNOW this particular table's record object is needed. It HAS to do be done this way, you know. If I didn't, Reactor would not have re-created my object for me, DESPITE the fact that I re-init'd the app, because it is a WHOLE lot like a Wendy's drive-through: neither one creates it until you actually order it. Okay, so now I am able to successfully edit and insert records containing values for these new fields, so I must have ordered my burger right (pun intended). As usual, I now switch back to production mode for efficiency's sake. More testing, and I realize that something's amiss with one of my fields...I'm getting a sql error when trying to do an update after having edited a value. BUT HEY NOW! WAIT JUST A MINUTE! HOW can I be getting a SQL error when I'm using Reactor validation to check values before attempting to insert them? This cannot be! And yet, it is. After fiddle farting around with double checking syntax, making sure quotes were correct, field names and form field names correlated...it finally occurred to me to open up the Reactor validator object for this table, JUST TO MAKE SURE it looked right. Surely it would be right. After all, I KNOW I did what Reactor required of me to regenerate that table's objects. Sure enough, though, no validators existed for the new fields. DANG IT! Apparenly Reactor only regenerated the SPECIFIC Reactor objects I needed for the functions I performed before switching back to production mode. Back to development mode, re initialize the app, walk through as a user to the point where I INVOKED VALIDATION, then all was well.

When I finally DID get everything working well and regenerated in my local environment, I then committed my changes via SVN to the repository and ran the update for our testing environment. We aren't including Reactor's base project CFCs in our repository, so now it was time to switch to dev mode in Test and regenerate. Ay, here I go again, having to login in as a user and physically "touch" the app in a lot of places to force Wendy...er, Reactor, to make my burger. You can imagine how much time it can potentially take when you have to go into the app and USE it in order to make changes happen on the backend like that. And what if you don't know the app from the front end? What if you're job only entails backend work? Then you're either forced to learn the app, or wait for your testers to go in and tell you if it worked or not. Either way, it's not efficient use of time.

My rant is just this: why's it gotta be so painful to use Reactor? Why do I NEED an initialization cheat sheet when developing with it? If I've jumped through the fiery hoop to get Reactor to regenerate my table's record object, why can't it just regenerate ALL of the objects for that particular table in one fell swoop??? WHY am I FORCED to know the app from the FRONTend in order to manifest a change on the BACKend (Reactor's whole, "I won't make it for ya till you order it!" philosophy)? What if all I was hired to do was back end coding and don't know the app well enough from the user's perspective to properly navigate my way through to where my code change lives?

Perhaps I'm just using this awesome tool the wrong way; I don't think so, though.  I've worked with several other people together on MG projects, I've read just about everything out there that has to do with MG:Unity, I've got a LOT of hours logged getting my hands dirty with this framework, and I haven't seen anybody else do it any different than I. If I had a magic wand, I would wave it and miraculously "init=true" really WOULD reinitialize my app completely!  As it stands now though, it's kind of a pain.

Okay, I'm done venting. Thanks for listening. ModelGlue:Unity, I still love you.

Doug out.
Posted by dougboude at 12:29 PM | PRINT THIS POST! | Link | 4 comments
07 September 2007
ModelGlue Development Cheat Sheet
When my team and I first began building our Model-Glue:Unity application, we experienced a GREAT deal of head scratching and frustration during the development process. We would alter a line here or there, refresh our browser, and NOT see the change. It took us quite some time to finally figure out what kind of action was warranted for what kind of change in order to make it manifest, so we documented it for ourselves in our local Wiki.

Following are the guidelines and shortcuts we came up with to make our development a wee bit less frustrating. I don't claim to have the market cornered on understanding all of the nuances and functionality of MG:U, so please, feel free to let me know about any changes or additions you feel would benefit those who read it.


ModelGlue:Unity - Modes of Operation

(Note: ALL of the settings required to perform the following functions reside within the Coldspring.XML file)

There are essentially two modes of operation in ModelGlue: Development and Production. What each of these modes entails is actually user-definable since MG provides several different options that can be associated with each. For instance, you can be running your application in "production" mode and yet still opt to display Model-Glue debugging information. For the purposes of this post, "Production" will imply that all options associated with development have been disabled and the application is configured to run at its maximum speed.


How to Toggle Modes


In order to toggle between development and production modes:
  • change the "reload" property in Coldspring.xml to "true" for development or "false" for production;
  • edit the "debug" property, setting the value to "true" for development or "false" for production;
  • ensure that the "Mode" property in the Reactor bean definition is set to the value "Production" for production, or "Development" for development;
<property name="reload"><value>true</value></property>
<property name="debug"><value>true</value></property>

<bean class="reactor.config.config" id="reactorConfiguration">
    ...
    <property name="mode"><value>development</value></property>
</bean>
settings for development mode

<property name="reload"><value>false</value></property>
<property name="debug"><value>false</value></property>

<bean class="reactor.config.config" id="reactorConfiguration">
    ...
    <property name="mode"><value>production</value></property>
</bean>
settings for production mode

Refreshing During Development

Because ModelGlue:Unity maintains some objects, values, and settings in scopes that aren't typically refreshed, certain types of modifications require certain actions in order to make them manifest. The following matrix covers the typical mods that require specific reset actions.
ModificationReset Action
Change to ModelGlue.xml (or any XML file included in Modelglue.xml)
  • Make sure the "reload" property in Coldspring.xml has a value of "true";
  • re-initialize the app by going to the url [app url]/?init=true (http://www.myMGapp.com/index.cfm?init=true)
Changes to Coldspring.XML
  • Make sure the "reload" property in Coldspring.xml has a value of "true";
  • re-initialize the app by going to the url [app url]/?init=true (http://www.myMGapp.com/index.cfm?init=true)
Change to a controller cfc re-initialize the app by going to the url [app url]/?init=true (http://www.myMGapp.com/index.cfm?init=true)
Change to a model cfc all changes should be visible real time...just refresh your page!
(note: IF the cfc you edited ALSO happens to be injected into a controller, then you'll have to follow the steps for "changes to a controller cfc")
Change to the database Reactor needs to be reset. This is accomplished by:
  • In the Colsdspring.XML Reactor Configuration Bean, make sure "Mode" is "development";
  • Make sure the "reload" property in Coldspring.xml has a value of "true".
  • If Scaffolding is being used, ensure that Scaffold is set to "true".
  • Once the settings are verified, reinitialize the application by going to the url [app url]/?init=true (http://www.myMGapp.com/index.cfm?init=true).
After the application finishes reinitializing, you will very likely wish to DISABLE scaffolding and set your reactor mode back to development, then RE-INITIALIZE again for those changes to take effect.

CRITICAL NOTE! Reactor is a LOT like Wendy's...it doesn't make till you order it. What this means is that, even though you may have reinitialized the app AND Reactor is in Development mode, that one little record object you needed to be recreated WON'T be recreated UNTIL you actually call the portion of your code that asks ModelGlue for it. SO, be sure you leave Reactor in Development mode UNTIL you get to the portion of your code that actually USES the object you were trying to refresh. Ya feel me?

relevant portion of Coldspring.xml file
<bean id="reactorConfiguration" class="reactor.config.config">
    ...
    <property name="mode"><value>development</value></property>
</bean>

<property name="rescaffold"><value>true</value></property>
Change to a View All changes should be visible real time...just refresh your page!
Change to Reactor Dictionary file (model/data/reactor/
Dictionary/[table name].xml)
All changes should be visible real time...just call your event again!

Again, any additions or corrections that need to be made to this matrix, please shoot me an email (via my funky resume) or leave a comment (if you can pass the Turing Test).

Doug out.
Posted by dougboude at 12:14 PM | PRINT THIS POST! | Link | 3 comments
29 August 2007
The Model-Glue Event Lifecycle in Layman's Terms
Okay, actually there's no way to COMPLETELY distill this topic down to the point where you don't have to have a decent understanding of web development to grasp it. BUT, I do believe that I've been able to demystify the subject sufficient to allow even the beginner OOP/Model-Glue developer to "get it", which is what I wish I would have had a year ago when I left the comforts of procedural and embarked on my OO journey. That being said, this post is not one of the shorter ones I've done; necessarily so, as there's a lot of ground that I felt couldn't afford to be skimmed over. By taking the time to read through it, however, I do believe that a LOT of the "gray areas" that we OO newbies face, architecturally speaking, will be cleared up enough to at least give us a clue which direction to go. That being said, what follows is my PERSONAL understanding (which has worked well for me) of the Model-Glue Event Lifecycle. Enjoy.

UNDERSTANDING THE MODEL-GLUE EVENT LIFECYCLE

The Model-Glue framework can present an intimidating learning curve when coming directly out of the procedural world to that of Object Oriented Programming. One of the things that will help the Model-Glue student immensely is to have a solid understanding of how the Model-Glue event lifecycle works: being able to visualize the invisible. With this knowledge in pocket, many of the architectural questions that will inevitably arise will be more easily addressed. So, let’s explore what we mean by the term ‘lifecycle’, what the definition of ‘event’ is in a Model-Glue context, and what Event’s role is in a Model-Glue application!


 A Lifecycle You Already Understand

 Let’s look at a lifecycle you’re already familiar with: http request. Pictures are worth a thousand words, so allow me to illustrate this:


Looks familiar, doesn’t it? The request begins life when the client initiates it with an http call to our web server for a specific template. The server receives the request, grabs the template requested, and hands it over to the Coldfusion server for processing. The Coldfusion server renders the CFM template into pure HTML (since that’s all a browser understands), hands the HTML back to the web server, and the web server returns the HTML to the client. End of the request’s lifecycle!

 
When a client makes a request of a Model-Glue application, a similar process takes place. The focus of this post will begin (and end) at the point where index.cfm is requested and passed a parameter called “event” (eg; http://www.myMGapp.com/index.cfm?event=home). The receipt of this parameter is the first breath our Model-Glue application draws, and begins the internal process which we will now follow through to its conclusion.

 

What Exactly IS A Model-Glue Event?

‘Event’ is two distinct things:

  • ‘Event’ is a simple value: a name, such as ‘home’, or ‘do.login’ (yes, compound event names are acceptable and even advantageous for organization’s sake!);
  • ‘Event’ is a bucket.

 
 “A bucket?”, you may be asking yourself. Yes, a bucket! Buckets carry things around, and at a certain point in our event lifecycle study we will see how the event changes from being simply a name to an actual value-carrying OBJECT that gets passed around within the application! It’s a beautiful metamorphosis, as you will see.

 The Event Lifecycle in a Nutshell

 

Let’s begin with an overview of what takes place in the lifecycle of a Model-Glue event (We’ll be exploring each step in detail later):

  1. A request is made, notifying model-glue to execute a specific event (eg; http://www.myMGapp.com/index.cfm?event=do.login). At this point, Model-Glue generates an empty Event Object (aka: bucket).
  2. Model-Glue looks in its modelglue.xml file to find out what messages to broadcast to listening controllers ( <broadcast><message name="login" /></broadcast> ) for the 'do.login' event.
  3. Controllers listening for the "login" message execute their corresponding functions ( <controller name="AuthenticationController" type="controller.AuthenticationController"><message-listener message="login" function="AuthenticateUser" /></controller> ).
  4. Model-Glue executes any relevant result actions (behaves as an 'if' statement), if present.
  5. Model-Glue renders any views that are defined for this event. HTML is delivered and the event lifecycle is over.

 
Event Lifecycle, Step One: Create the Event Object (aka: bucket)

 
The whole event lifecycle depends upon values being passed around within the application, so the first thing Model-Glue does after being notified which event to execute is to create an Event Object. Just visualize it as a bucket at this point.

 After the Event object is created, any incoming form fields and URL parameters are immediately stuffed into it for safekeeping.

 
Event Lifecycle, Step Two: Reading the Event Definition

 Now that Model-Glue has the Event Object all populated with incoming values, it’s time to discover exactly what should be done with them for the event name that was called. The instructions, or event definitions, that Model-Glue refers to when an event name is received all live in an XML document called “Modelglue.xml”. Here is what an event definition looks like:
 

<event-handler name="do.login">

            <broadcasts>

                        <message name="login" />

            </broadcasts>

            <results>

                        <result name="success" do="home" redirect="true" />

                        <result name="failure" do="login" redirect="false" />

            </results>

            <views />

</event-handler>


Summary of steps 1 and 2

The <event-handler> tag has three children: <broadcasts>, <views>, and <results>, and they are evaluated in that order. Let’s look at each one more closely, following our Event Object through the process.

 

Event Lifecycle, Step Three: Giving Shout-Outs (aka: making broadcasts)

 The first thing Model-Glue does for a given event is to make any defined broadcast. A broadcast is simply a shout out that triggers certain CFCs to execute specific methods. In our example, the broadcast “login” is made. Model-Glue looks in another section of its Modelglue.XML file to find out what CFCs are “listening” for that particular message, then executes the relevant method. In this scenario, this is what Model-Glue found in it’s XML file:

 <controller name="AuthenticationController" type="controller.AuthenticationController">

            <message-listener message="Login" function="Login" />

            <message-listener message="Logout" function="Logout" />

</controller>

 
Ah, so Model-Glue is going to invoke the “Login” method of the AuthenticationController.cfc (which lives in the “controller” directory). Here is an important thing to know at this point: for every method that Model-Glue calls, the Event Object that was initially created and populated will be passed in as an argument, like so:

 <cffunction name="Login" access="public" returnType="void" output="false">

            <cfargument name="event" type="ModelGlue.Core.Event" required="true">

            ….

</cffunction>

 
Why? So that our methods can read values out of it (such as the username and password that were passed in via our login form) AND so our methods can put values INTO the bucket as well! In our example, the Login method is going to attempt to authenticate a user. If it succeeds, it’s going to add a ‘Success’ result to the Event bucket; if authentication fails, it’s going to add a ‘Failure’ result.


Summary of Step 3

Okay, the “Login” method finished, Model-Glue has the Event Object in hand, and there are no more broadcasts defined for this event, so let’s go on to the next step in the Lifecycle.

 

Event Lifecycle, Step Four: Decide What Results to Execute

 Take a look at the <results> section of the event definition. We see two named results defined (so called because each of them has been given a specific name): success and failure. Recall how our Login method added one of two possible results to the event bucket, either “Success” or “Failure”. At this point, Model-Glue looks for any results in the Event bucket and executes whichever one is appropriate. Executing a result really just means “hey Model-Glue, you found a result named “Success”, eh? Then I want you to jump to the defined event named “home” and execute that now.” If a result named “Failure” had been found, then the “login” event would have been called, presenting the user with the login form again. When Model-Glue executes a named result and jumps to another event, the programmer has two choices at this juncture. To start the new event fresh with an empty Event object, or to carry the existing, populated Object over to the next event. This is controlled with the <result> attribute “redirect”. If set to ‘True’, a new, empty Event object is created; if set to “False’, the original Event Object is passed along.

Summary of Step 4

At this point, all of our messages have been broadcast and relevant methods executed; if we needed to redirect to another event, that was also already accomplished. We’ve now arrived at the final step in the event lifecycle: Render our HTML and send it back!

 

Event Lifecycle, Step Five: Render The Views!

(aka: create the HTML that will be passed back)

 Since our “do.login” event was all about actions and not views, let’s look at the “home” event that a successful login attempt redirects us to:

 <event-handler name="home">

            <broadcasts />

            <results />

            <views>

                        <include name="body" template="dspMainContent.cfm" />

                        <include name="final" template="dspLayout.cfm" />

            </views>

</event-handler>

 This particular event doesn’t ask Model-Glue to make any broadcasts nor does it ask Model-Glue to look for and process any results. What it DOES do, however, is ask Model-Glue to render two different CFM templates, dspMainContent.cfm and dspLayout.cfm. There are several important things to note here.

 First, that we can tell Model-Glue to render any number of individual CFM templates. Second, notice how each template is given a “name” attribute…the rendered HTML will later be referred to by this name. Third, the order we list them in is irrelevant with one WHOPPING exception: the last template rendered is the ONLY one that Model-Glue will return to the browser. So what happens to the other Views that were rendered and placed onto the Viewstack (notice how I just increased your Model-Glue vocabulary ;) )? All other rendered templates are “included” in the final template, in nearly EXACTLY the same manner as using a CFINCLUDE, only slightly different. See a previous post I did on Views called “Model-Glue Views Demystified” for more detailed information on this topic. Now, let’s find out where our Event Object/Bucket is relative to the views that are being rendered, shall we?

 Model-Glue makes the contents of the Event Object available to your CFM view templates in an object referred to as “ViewState”. It, too, is an object, very similar to the Event Object, and it contains all the values that your template could possibly need. You may be asking yourself, “why do we need the ViewState Object if we already have a perfectly good Event Object?”. Valid question, and the answer lies within the philosophy of object oriented programming and the two cardinal virtues that a framework like Model-Glue seeks to uphold: Encapsulation and Autonomy. Suffice it to say that in order not to violate basic MVC principles, copying the contents of the Event Object into the ViewState Object was the better thing to do. Okay, take a look at the following two lines of code taken from within a view template:

 <CFSET firstname = ViewState.getValue("firstname") />

 <CFOUTPUT>Welcome, #firstname#!</CFOUTPUT>

 Any CFM template created for use within a Model-Glue application can absolutely count on the presence of “ViewState”. The value “firstname” that we’re retrieving from ViewState is a value that was placed in the Event Bucket at some point previously in the event lifecycle, likely by one of our broadcasts. Are you seeing the beauty of the Event Object yet? From the beginning of our event call, that bucket was passed around here and there, values being put into it, redirections being made as needed, until finally we arrive at our views where all of the previously saved values are made available for use within your templates. Strings, Arrays, Structures, Queries, and even other Objects can be stuffed into the Event Bucket and made available via ViewState. No limitations in that arena!

Slightly off topic from this article, let’s quench some curiosity and sneak a quick peek at how the final view being rendered includes content previously rendered in the viewstack, in this case, how “body” is output within “final”. Take a gander at the following few lines of code:

 <cfset body = ViewCollection.getView("body") />

 <div class="contentbody">

<cfoutput>#body#</cfoutput>

</div>

Yes, it’s really that simple. All rendered views are placed onto the viewstack, referenced by the name we gave them in the event definition, and are available within our template by accessing the “ViewCollection”.  Again, for more detail on Model-Glue views, see the “Model-Glue Views Demystified” post.

Views all rendered and the final HTML complete, Model-Glue returns it to the web server, which in turn returns it to the browser that requested this particular event in the first place.


Summary of Step 5, and the end of our Event Lifecycle

This is the end of our Event’s life; it is disposed of properly. RIP.

 
One Final Bit of Trivia

If you’re curious about what the Event Object really looks like, here’s a CFDUMP showing it and its methods:


CFDUMP of the Event Object

Conclusion

 As its name implies, Model-Glue is a framework that allows a developer to create code that is truly self-contained and reusable by acting as the glue that binds all the pieces together. It accomplishes this by managing the one common denominator that the Model, View, and Controller all have in common: The Event Object. From the client’s initial call to the final delivery of the requested HTML to the browser, it is the Event Object that the developer must understand and interact with in order to create a working application. Only a mid-level understanding of the event lifecycle is required to meet the majority of architectural needs. How to incorporate security, how to ensure the display of appropriate navigation, how to easily add in new functionality: these are all common challenges that understanding the Model-Glue event lifecycle will enable any developer to overcome.

Posted by dougboude at 1:04 AM | PRINT THIS POST! | Link | 3 comments
27 July 2007
Model Glue Views Demystified
At first, getting a grasp on how to "think" about rendered views in Model Glue may appear to be challenging; but I tell you, that you already know exactly how to think about them. If you have EVER included a template using <CFINCLUDE>, then you have a two minute learning curve to working with views in Model Glue.

Breaking your page up into individual pieces, and then including those pieces later on as needed is not a new thing for most of us. That is precisely what Model Glue allows us to do as well. Is it ever required that we divide our code into individual templates? Nay, and neither does Model Glue require it. It is, however, a very good idea in most instances, and MG gives us a very easy way to do this.

Picture if you will, a template.

We have dsp_main.cfm that will act as our layout, containing only DIV tags with appropriate IDs. Dsp_main.cfm also includes our external style sheet to provide appropriate positioning and look and feel to whatever content we put into those DIVs. Before Model Glue, we'd do it like this:

<html>
<head>
    <title>My Composite Template</title>
    <link rel="stylesheet" type="text/css" href="css/styles.css" media="screen" />
</head>

<body>

<div id="header"><cfinclude template="dsp_header.cfm"></div>
<div id="nav"><cfinclude template="dsp_nav.cfm"></div>
<div id="body"><cfinclude template="dsp_body.cfm"></div>
<div id="footer"><cfinclude template="dsp_footer.cfm"></div>

</body>
</html>


Nothing new there, right? With Model Glue, the same dsp_main.cfm looks like this:

<!--- grab my rendered views out of the viewcollection... --->
<cfset header = viewCollection.getView("header") />
<cfset footer = viewCollection.getView("footer") />
<cfset body = viewCollection.getView("body") />
<cfset nav = viewCollection.getView("nav") />

<html>
<head>
    <title>MG View Demo</title>
    <link rel="stylesheet" type="text/css" href="css/styles.css" media="screen" />
</head>

<body>

<div id="header"><cfoutput>#header#</cfoutput></div>
<div id="nav"><cfoutput>#nav#</cfoutput></div>
<div id="body"><cfoutput>#body#</cfoutput></div>
<div id="footer"><cfoutput>#footer#</cfoutput></div>

</body>
</html>


With Model Glue, rather than include those templates within the page itself, the templates were pre-rendered (almost exactly like using a CFSAVECONTENT), the rendered html stuffed into what's called the ViewCollection, and then we simply grab that rendered HTML out of the ViewCollection and output it in a very familiar fashion.

Now, how were those individual templates included? We told Model Glue to do it within the Modelglue.XML file that defines our individual events. For instance, let's say the event we called was 'main.landing' (http://www.mysite.com/index.cfm?event=main.landing). The relevant XML looks like this:

<event-handler name="main.landing">
    <broadcasts />
    <results />
    <views>
        <include name="body" template="dsp_content.cfm" /><!-- available in the viewcollection as 'body' -->
        <include name="nav" template="dsp_nav.cfm" /><!-- available in the viewcollection as 'nav' -->
        <include name="footer" template="dsp_footer.cfm" /><!-- available in the viewcollection as 'footer' -->
        <include name="header" template="dsp_header.cfm" /><!-- available in the viewcollection as 'header' -->
        <include name="main" template="dsp_main.cfm" /><!-- container template to layout individual parts -->
    </views>
</event-handler>


Using the 'include' tag within the 'views' tag, we told Modelglue which templates to render. Of note is the fact that the order in which they are rendered is completely irrelevant, WITH ONE EXCEPTION: the template that acts as the layout container MUST BE RENDERED LAST.

Also of note is the fact that each of your view templates should act independently of one another. For example, let's say that a Model Glue variable is needed in the dsp_nav.cfm and that same variable is also used somewhere in the dsp_content.cfm template. It would be bad practice for you to retrieve that variable within dsp_nav.cfm and then attempt to access the retrieved instance from dsp_content.cfm; each template should be retrieving it for themselves.

That's it, boys and girls. Nothing to it, and very little difference from the way you've been using <CFINCLUDE> all along!
Posted by dougboude at 12:01 PM | PRINT THIS POST! | Link | 4 comments
02 July 2007
Basic Event Security in Model-Glue Applications
To anyone who has not yet breached the subject of model-glue event security, it can potentially be confusing at first, so I thought I'd share my approach to it in case it helps save someone a little time.

Understanding and being able to visualize the life-cycle of a Model-Glue event is a prerequisite to really grasping event security, so let me share my take on what a brief overview of that life-cycle is.

  1. A request is made, notifying model-glue to execute a specific event (eg; http://www.somesite.com/?event=fireinthehole )
  2. MG looks in its modelglue.xml file to find out what messages to broadcast to listening controllers ( <broadcast><message name="blowitup" /></broadcast> ) for the 'fireinthehole' event
  3. controllers listening for the "blowitup" message execute their corresponding functions ( <controller name="bombController" type="controller.eodGuy"><message-listener message="blowitup" function="BlowInPlace" /></controller> )
  4. MG executes any relevant result actions (acts as an 'if' statement almost) if they exist
  5. MG renders any views that are defined for this event
  6. The event lifecycle is over.

So, now that we know the flow of a named event within Model-Glue, it's time to add in a security check to make sure the current user has permission to execute that event. In my scenarios typically I have private and public events (those that can be executed without being logged in (such as the 'login' event itself), and those that require previous authentication (such as 'manageAccount')) and events requiring a specific role (such as viewing billing reports).
What we will effectively do is slide in some functionality between the event request and the execution of the event itself by leveraging OnRequestStart. This functionality will either allow the named event to pass on through OR redirect the user to the event we want them to arrive at. For instance, if they attempt to access an event that requires login and we intercept that event, we'll redirect them to the login page.

Here's the process for putting the named event check functionality in place:

1.Create a function that checks the current event name against a given list of event names;
2.Register that function to be called at 'onRequestStart';
3.In the Modelglue.xml file, create an event called "modelglue.OnRequestStart";
4.Within the modelglue.OnRequestStart event, register named results and provide appropriate redirection values;

What's going to happen then during the event life-cycle is that, before the actual named event executes, any controllers listening for the 'onRequestStart' message will execute their functions. One of those functions will have the sole job of verifying that the event being requested is allowed to be called in the current session state (logged in, not logged in, is an admin, etc.) If the event is good to go, the function is finished. If the event should NOT be allowed to execute, the function will set a model-glue named result. Next, since we defined an event called modelglue.OnRequestStart, that event is evaluated before any named events. The only thing we have defined for it to do is to look for specific named results and if one of them is found, perform the appropriate redirection. If none of the results being watched for are present, the named event executes normally.

I know at this point there must be a lot of questions on how to actually implement what I've been describing at a high level, so here are the same steps with snippets you can use:

1.Create a function that checks the current event name against a given list of event names;
In our scenario, let's assume two things: that we have events we want to require security, and events that we want to require that the person logged in also be an administrator.
<CFFUNCTION name="checkEvent" access="public" returntype="void" output="false">
    <CFARGUMENT name="event" type="any">
    <CFSET var eventname = arguments.event.getValue(arguments.event.getValue("eventValue")) />
    <CFSET var user = arguments.event.getValue("currentUserObject") />
    <CFIF not user.getUserID() and not listFindNoCase("login,signup,forgotpassword,sendpassword", eventname)>
        <!--- if we aren't logged in AND the event we're calling is NOT a public event... --->
        <CFSET arguments.event.addResult("LoginNeeded") />
    <cfelse><!--- we are logged in. If this event is in our list of events requiring admin login, check to see our user is an admin. if not, redirect them to home. --->
        <cfif listFindNoCase("admin.home,admin.billing,admin.creditAccount", eventname)
                and not user.getRole("Admin")>
                <CFSET arguments.event.addResult("AdminNeeded") />
        </cfif>
    </CFIF>
</CFFUNCTION>


2.Register that function to be called at 'onRequestStart';
<controller name="MyController" type="controller.Controller">
    <message-listener message="OnRequestStart" function="checkEvent" />
</controller>

3.In the Modelglue.xml file, create an event called "modelglue.OnRequestStart";
and
4.Within the modelglue.OnRequestStart event, register named results and provide appropriate redirection values;
<event-handler name="modelglue.OnRequestStart">
    <results>
        <result name="LoginNeeded" do="login" redirect="true" />
        <result name="AdminNeeded" do="home" redirect="true" />
    </results>
</event-handler>

As with anything Model-Glue or OO, there's always greater levels of detail to be expounded upon, but these snippets are more for illustrative purposes than actual 'out of the box' code, so I'm leaving a lot of the details to you. Such as where you should really store your lists of named events, or how your user object is always present, empty or populated, regardless of whether or not the user is authenticated, etc.

Anyway, hope this helps get somebody over the hump!
Posted by dougboude at 3:27 PM | PRINT THIS POST! | Link | 2 comments
10 June 2007
Global Configuration Settings in Model-Glue:Unity
Some Suggestions
When making the transition from procedural CF to OO, sometimes what was SO simple before can become a challenge (at least the first time). For instance, the way we used to pass around our app's DSN value was probably something along the lines of setting an application variable in our application.cfm or cfc, thus making it accessible throughout our app while consolidating the value to a single location for ease of change later. But now that we've decided to force ourselves to learn to do things in an OO fashion, where on earth is the "correct" place, or what is the "correct" way to perform this same action?

In our procedural apps, common sense told us to "find a place within the execution path that always gets called, no matter what, and check for and set your global value there...". Let's use that same line of thinking for our OO app, too, and find an appropriate spot in which to set our global variable.

Even in an OO approach, CF still respects application.cfc/cfm, so setting the value there could be an option. However, one of the chief tenets of OO is encapsulation, meaning that we try to make each aspect of the application as non-dependent as possible on the rest of the application. Setting our DSN in application.cfc/cfm then means that likely our urge would be to stuff it into the application scope:

<cfset application.dsn = "Horton" />

If I want to use that value within any of my model CFCs then, SOMEWHERE I'd have to write a line of code that talks to the application scope; not a best practice, and indeed immediately makes my CFC dependent on the existence of some value outside of itself. No bueno.

How 'bout instead we utilize a configuration bean...a tiny object whose sole purpose in life is to pack around global values I might need here and there, and to make itself available anywhere within my app? Now that makes my common sense tingle with delight. Utilizing Model-Glue:Unity, let me share a couple of ways I have done this very thing.

At this juncture, if you aren't familiar with the member of the Model-Glue: Unity trinity called Coldspring, it's time you two met. Coldspring is an expert bean handler, and is quite a versatile fellow when it comes to creating beans and making them available wherever you'll be needing them. Coldspring does all of what it does based on the content of its XML configuration file (Coldspring.XML), so it is within that file where we will 'instruct' it regarding our configuration values.

At this point you're probably thinking to yourself, "okay self, all I need to do then is create a CFC that handles configuration values for my app. I'll make a getter and setter method for my DSN, one for my images directory path, one for....". Hey, that is definitely a good way of thinking. HOWEVER, Coldspring has a surprise for you, O Best Beloved. Coldspring can automagically create that configuration bean for you, and all you gotta do is tell it what you want it to contain via the Coldspring.XML file! Allow me to
elucidate.

In order to auto-create one of these configuration beans, let's define it within Coldspring.XML, like so:
  <bean id="AppConfiguration" class="ModelGlue.Bean.CommonBeans.SimpleConfig">
    <property name="Config">
      <map>
    <!-- this is the DSN used throughout the app... -->
        <entry key="DSN"><value>Horton</value></entry>
    <!--  path the CSS directory -->
        <entry key="CSSPath"><value>/appCSS/</value></entry>
      </map>     
    </property>
  </bean>
(Note: Notice the class of our configuration bean...we're leveraging a built-in class of model-glue)

Now that we've defined it, from this moment onward we'll maintain our app settings RIGHT THERE. If the DSN changes, I'll change the value of the DSN entry key.

Making this configuration bean available throughout the app is a purposeful endeavor, meaning that you have to explicity tell Coldspring to make it available to a specific CFC. We do this by "injecting" it into other CFCs that we've told Coldspring about. Looky here:

  <bean id="EmailService" class="model.EmailService">
    <constructor-arg name="AppConfiguration">
      <ref bean="AppConfiguration" />
    </constructor-arg>
  </bean>

I've told Coldspring "I have an EmailService.CFC, and it is expecting an argument value named 'AppConfiguration', so when you instantiate EmailService for me, please go ahead and pass in an instance of my AppConfiguration bean as that value. Thanks man."

Okay, the only other thing you have to do in order for this to work smoothly is to ensure that the object you're wanting to inject the configuration bean into is prepared to receive it. With the approach we're taking, the proper way to do this is to make sure your CFC has an init method, and within that init method create a required argument named exactly the same as your constructor-arg name. Like so:

<cffunction name="init" returntype="EmailService" output="false" hint="Constructor">
    <cfargument name="AppConfiguration" required="true" />
    <cfset variables._config = arguments.AppConfiguration />
    <cfreturn this />
</cffunction>

Okay, so now we have our Coldspring simple config object injected. Pay attention, this is important: for every value we defined for this config object, you will access it via the following method: getConfigSetting([value name]). For instance, if within our EmailService object we have a method that needs the DSN value, we'll grab it with
<cfset myDSN = variables._config.getConfigSetting("DSN") />

Alternatively (and this is what I did), you can create a generic "GetConfigSetting" method within your recipient object, like so:

<cffunction name="GetConfigSetting" access="private" returntype="string" output="false">
    <cfargument name="name" required="true" type="string" />
    <cfreturn variables._config.getConfigSetting(arguments.name) />
</cffunction>

With that method present in your recipient object, you would access the DSN like this:

<cfset myDSN = GetConfigSetting("DSN") />


It's a little cleaner, I suppose.

Now, you could be thinking that since your config object contains values that may also be needed within a view, such as a CSS path, or an images directory path, that you'll need a way to make it available there, too. Let me toss out a suggestion based on the way I have done it.

Everytime a MG:U event is called, you can have listeners that execute beforehand based on the fact that they're listening for a call to "OnRequestStart", as in this snippet from my modelglue.XML file:


<controller name="MyController" type="controller.Controller">
   <message-listener message="OnRequestStart" function="loadConfiguration" />
</controller>

You can, within the method being referenced in onRequestStart, stuff the configuration bean into the event object, thus making it available to every other object referencing that event, INCLUDING your views! Let's look at the controller object mentioned in this example:

<cfcomponent displayname="Controller" extends="ModelGlue.unity.controller.Controller" output="false">
    <!--- Autowire / private getter --->
    <cffunction name="setAppConfiguration" access="public" returntype="void" output="false">
        <cfargument name="AppConfiguration" required="true" type="any" />
        <cfset variables._config = arguments.AppConfiguration />
    </cffunction>
    <cffunction name="getAppConfiguration" access="private" returntype="any" output="false">
        <cfreturn variables._config />
    </cffunction>


    <!--- Place the configuration bean into the viewstate --->
    <cffunction name="loadConfiguration" access="public" returnType="void" output="false">
      <cfargument name="event" type="any">
      <cfset arguments.event.setValue("appConfiguration", getAppConfiguration()) />
    </cffunction>
</cfcomponent>

A few things to point out here:
1. Because this object is a controller object, Coldspring gives us the privilege of autowiring in beans that it knows about by simply providing it with a "get" and "set" method named according to the bean we want to inject. In this instance, Coldspring already had created for us a bean called AppConfiguration, so by virtue of the fact that Coldspring saw our controller object had a method named "getAppConfiguration" and "setAppConfiguration", it assumed we wanted it to inject an instance of the AppConfiguration bean. Coool.
2. On request start, Model-Glue is calling our "loadConfiguration" method, which is taking the injected configuration bean and placing it into the Event bucket (some like to call it the event 'bus', but thinking of it as a bucket makes more sense to me). This automatically means that the configuration bean will be available in our viewstate as well, and from within any view can be referenced like


<cfset appConfig = viewstate.getValue("appConfiguration") />

<link rel="stylesheet" type="text/css" href="<cfoutput>#appConfig.getConfigSetting("CSSPath")#</cfoutput>styles.css" media="screen" />

Now, having shared all of that, I will tell you that if you're using Reactor within your app, you were already defining the DSN within the Coldspring Reactor Bean, and may not want to define the DSN twice, but rather would like to use Reactor's DSN value throughout your app. This can be done just as easily, only you'll be injecting not your appConfiguration bean into the CFCs that need the DSN value, but the "reactorConfiguration" bean instead (already exists within your Coldspring.xml file). Once injected, you'll get at your DSN value with a method called "getDSN()".

Here's something else to chew on that may be helpful to you (as it was for me): Besides just a global application config bean, you can also define configuration beans for different services that your app has. For instance, my app performs image manipulation, so within my Coldspring.xml file I defined a configuration bean called "ImageServiceConfig" that contains settings for thumbnail dimensions, large image dimensions, naming conventions, pathing information, etc., then I inject that bean into my ImageService.cfc. I do the same thing for my EmailService.cfc. This allows me to truly consolidate all of my application settings within my Coldspring.xml, simplifying maintenance in that regard.

That, Boys and Girls, is injection at its finest, and it allows me to maintain my app's global values in one place, just like I used to do with application.cfc/cfm.

Doug out.
Posted by dougboude at 9:58 AM | PRINT THIS POST! | Link | 8 comments
25 May 2007
Custom Validation with Generic Commit: a Model-Glue Case Study
Someone recently asked about how to specifically ensure that a submitted email address is unique when using a model-glue generic commit, so I thought I'd share an example since I recently had to do that very thing.

I'm assuming for the remainder of this post that the reader is already familiar with Model-Glue in a practical sense, and at least knows of the existence of Reactor's automagic validation. Still, I'll try not to leave out too many relevant details.

Okay, the scenario:

I have a secured app, and I want to give new users the opportunity to sign up for an account. I'm using email address as the user name since in theory it should always be unique to an individual. (note: I opted NOT to set up my user table so that the email address field has a unique index on it) So, the user clicks "Sign me up!", I present them with a form to fill out, one field being their email address. They submit the form, and here's where we dive down under the waves to see what's happening...


The form submits to the event "inspector.create", which in the modelglue.xml file reads as follows:
<event-handler name="inspector.create">
    <broadcasts>
        <message name="ModelGlue.genericCommit">
            <argument name="recordName" value="UserRecord" />
            <argument name="criteria" value="" />
            <argument name="object" value="User" />
            <argument name="validationName" value="UserValidation" />
        </message>
    </broadcasts>
    <views></views>
    <results>
        <result name="commit" do="inspector.newuser" redirect="true" append="email" preserveState="false" />
        <result name="validationError" do="inspector.signup" redirect="false" append="" preserveState="true" />
    </results>
</event-handler>

Notice we're using a generic commit to handle this, which works because all of the needed data resides within form fields that are named exactly as their database field counterparts (eg; in my table there's a field called 'email', in my form there's a form field named 'email', etc.).
Now, the fact that we have specified an argument named "validationName" in our generic commit means that before the form data is committed to the database, Reactor is going to invoke the aid of one of the CFCs it auto-generated for us in order to "validate" the info submitted. By default, validation consists of checks that were created based on your table's metadata (unique indexes, datatypes, null not allowed, etc.), but Reactor was kind enough to provide us a convenient place to extend and customize that default validation if we so desire. In my scenario, since I did NOT choose to put a rule in place specifying that my email field should be unique, I added a custom method to perform that check.

To locate the CFC for adding custom validation, look in \model\data\reactor\Validator\, and find the cfc named after your target table. In my case, it's UserValidator.cfc

By default, the guts of the Validator CFCs you'll be working with look similar to the following:

<cfcomponent hint="I am the validator object for the Section object.  I am generated, but not overwritten if I exist.  You are safe to edit me."
    extends="reactor.project.myprojectname.Validator.SectionValidator">
    <!--- Place custom code here, it will not be overwritten --->
</cfcomponent>

I decided to add two more methods of validation to my user object:
  1. make sure they typed their password in twice the same way,
  2. and make sure their email isn't already used in the system
Ah, but when you look at the content of my CFC, you'll see there are THREE methods present, and NOT just two. Take a gander:

<cfcomponent hint="I am the validator object for the User object.  I am generated, but not overwritten if I exist.  You are safe to edit me."
    extends="reactor.project.myprojectname.Validator.UserValidator">

    <CFFUNCTION name="validate" access="public" hint="I validate an  record" output="false" returntype="any" _returntype="reactor.util.ErrorCollection">
        <cfargument name="UserRecord" hint="I am the Record to validate." required="no" type="any" _type="reactor.project.housefacks.Record.UserRecord" />
        <cfargument name="ErrorCollection" hint="I am the error collection to populate. If not provided a new collection is created." required="no" type="any" _type="reactor.util.ErrorCollection" default="#createErrorCollection(arguments.UserRecord._getDictionary())#" />
        <CFSET validatePasswordVals(arguments.UserRecord, arguments.ErrorCollection) />
        <CFSET validateEmail(arguments.UserRecord, arguments.ErrorCollection) />
        <CFSET super.validate(arguments.UserRecord, arguments.ErrorCollection) />
        <CFRETURN arguments.ErrorCollection />
    </CFFUNCTION>
   
    <CFFUNCTION name="validatePasswordVals" access="public"  output="false" returntype="reactor.util.ErrorCollection">
        <CFARGUMENT name="UserRecord" hint="I am the Record to validate." required="no" type="reactor.project.myprojectname.Record.UserRecord" />
        <CFARGUMENT name="ErrorCollection" hint="I am the error collection to populate. If not provided a new collection is created." required="no" type="reactor.util.ErrorCollection" default="#createErrorCollection(arguments.UserRecord._getDictionary())#" />
        <!--Blue is only allowed as a selection for Sky for people  whose weathercode = 5-->
        <CFIF  arguments.UserRecord.getPassword() is not arguments.UserRecord.getConfirmPassword() >
            <CFSET arguments.ErrorCollection.addError("user.password.notconfirmed") />
        </CFIF>
        <CFRETURN arguments.ErrorCollection />
    </CFFUNCTION>
   
    <CFFUNCTION name="validateEmail" access="public"  output="false" returntype="reactor.util.ErrorCollection">
        <CFARGUMENT name="UserRecord" hint="I am the Record to validate." required="no" type="reactor.project.myprojectname.Record.UserRecord" />
        <CFARGUMENT name="ErrorCollection" hint="I am the error collection to populate. If not provided a new collection is created." required="no" type="reactor.util.ErrorCollection" default="#createErrorCollection(arguments.UserRecord._getDictionary())#" />
        <cfset var userGateway = "" />
        <CFIF  arguments.UserRecord.getEmail() is not "" >
            <cfset userGateway = reactorfactory.createGateway("user").getByFields(email=arguments.UserRecord.getEmail()) />
            <cfif userGateway.recordcount IS NOT 0>
                <CFSET arguments.ErrorCollection.addError("user.email.alreadyexists") />
            </cfif>
        </CFIF>
        <CFRETURN arguments.ErrorCollection />
    </CFFUNCTION>
</cfcomponent>


The method present here that you may not have anticipated is called "validate", and is the exact same name as the method you would find in Reactor's core user validation object. So, what have we in effect done, boys and girls? That's right! We have (choose your favorite word, they both mean the same thing) overloaded/overriden the main "validate" method, in order to ensure that not only the original, auto-generated validation methods get called, but also the two new ones we added after the fact.

Let's take a closer look at our version of the "validate" method (this will only take a second, there are a couple of important things to note in there).

First off, you'll note that every validation method requires two arguments: an incoming record whose values are being validated, and the errorcollection where we (dang, this makes too much sense!) collect our errors.
Second, notice that we are FIRST executing our custom validation methods, then afterwards executing the auto-generated "validate" method by calling the object we extended, directly, via a call to "SUPER". Very cool, eh? Even though we initially overloaded our validate method in order to ensure that it got called rather than the core version of it, we were STILL able to call the original version as well. (By the way, that is a little trick I learned from Doug Sims' blog www.evenamonkey.com).

Alright then, we have submitted our form, used generic commit to perform validation, that validation called our extended object, executed the local custom methods first, then the system validate method. If any errors were encountered, our generic commit would have added a result named "ValidationError" to the event bucket (see the modelglue.xml snippet above), thus redirecting us back to the original page (where we have code in place looking for the presence of the error collection). If no errors were encountered, we're directed forward to the next event in the chain, and all is well.


One Mo Thang

Ah, one last thing that is of great importance to be aware of regarding Reactor validation: The Dictionary. The dictionary is an xml file that is specific to a validation object. In our example, since we have a userValidator object, there also exists a \model\data\reactor\Dictionary\userdictionary.xml file. This file is used to look up and translate any errors encountered so that the user is presented with readable text rather than a cryptic message. When you add custom validation methods, you also need to add dictionary entries. Consider the following snippet from my userdictionary.xml file:
<?xml version="1.0" encoding="UTF-8"?>
    <User>
        <email>
            <label>email</label>
            <comment/>
            <maxlength>100</maxlength>
            <scale>0</scale>
            <invalidType>The email field does not contain valid data.  This field must be a string value.</invalidType>
            <invalidLength>The email field is too long.  This field must be no more than 100 bytes long.</invalidLength>
            <notProvided>The email field is required but was not provided.</notProvided>
            <alreadyexists>That email address is already being used. Please select another email address. If you have forgotten your password, return to the main login screen and select "Forgot Password"</alreadyexists>
        </email>
        <password>
            <label>password</label>
            <comment/>
            <maxlength>50</maxlength>
            <scale>0</scale>
            <invalidType>The password field does not contain valid data.  This field must be a string value.</invalidType>
            <invalidLength>The password field is too long.  This field must be no more than 50 bytes long.</invalidLength>
            <notProvided>The password field is required but was not provided.</notProvided>
            <notconfirmed>The password you typed in is not the same as the "confirm password" value.</notconfirmed>
        </password>
    </User>

Look back at the custom method "validateEmail" we added earlier, and notice that if our validation fails, we're adding an error that looks like

<CFSET arguments.ErrorCollection.addError("user.email.alreadyexists") />


The syntax of that message is no coincidence...it's the same syntax you would use to access an item in an XML file. Fancy that! 'user' denotes the user dictionary; 'email' denotes the particular table field; and 'alreadyexists' is a term I just made up, and indicates that Reactor should look for a tag called 'alreadyexists' in order to find the correct translation for this error. Thus, you'll notice the tag

<alreadyexists>That email address is already being used. Please select another email address. If you have forgotten your password, return to the main login screen and select "Forgot Password"</alreadyexists>

in the <email> section of our dictionary file.


To Sum it all up!

Okay, so in a nutshell, if you have a form being submitted and you want to ensure that the email address is unique (AND you haven't put a rule in place within the database itself so specifying this):

  1. add a method to your customizable validator CFC for the target table;
  2. add a 'validate' method to the same CFC in order to overload the system version of the same;
  3. within your custom 'validate' method, execute your custom method first
  4. within the same, execute the system version of validate using "SUPER.Validate()"
  5. edit your dictionary file to add a translation for your new custom error

That's it!

Doug out.
Posted by dougboude at 1:30 AM | PRINT THIS POST! | Link | 9 comments
19 March 2007
Using Ajax with Model-Glue - it's really quite simple
At first glance, the topic can seem somewhat overwhelming. The key, however, is to understand each of them separately and then using them together won't seem overwhelming in the least. I'll touch briefly on each of them independently, but will assume for the bulk of this post that you have at least an elementary understanding of what each is and generally how they work.

Ajax refers to the ability we have to make Javascript run back to our web server and retrieve information for us, without reloading the entire page. It is doing nothing more than making an http call to a web page, just like we would do everytime we click a link on a page. The Javascript, however, makes the call in the background, unseen by the user, receives whatever content results from that web page call, and then does something with it inside of our current page.


Model-Glue is a popular Coldfusion application framework that is becoming the "procedural man's tour guide to object oriented programming", breaking "the rest of us" into the world of OO. If you haven't played with it or invested the time to get familiar with it, you have a lot of your peers who HIGHLY recommend it. I recently had the privilege of doing
a presentation for the Kansas City ColdFusion User's Group on Model-Glue that was saved as a Breeze preso if you need or want some kind of intro.

Okay, from this point forward I can safely assume that you have at least a working understanding of Ajax and Model-Glue as separate tools, and will now share with you how the two integrate easily (from the 1,000 foot view). I will NOT be focusing on any one Ajax library, as there are several good ones out there, and as the specific Ajax library being used is (or should be, if the Ajax library is intuitive enough) COMPLETELY IRRELEVANT to understanding how they integrate with one another.

First thing, consider a basic Model-Glue event lifecycle:

1. A Model-Glue event is called via navigation to a URL (index.cfm?event=app.home).
2. The Model-Glue framework looks up the event (in the modelglue.xml file), determines what broadcasts to make, what results to evaluate, and what views to render.
3. Rendered views are returned to the browser.
4. Browser displayes the rendered view.

Hmm, nothing complex here, eh?

Now consider a basic Ajax call to a Model-Glue event:
1. A Model-Glue event is called via Javascript library (var newcontent = ajax.remotecall('index.cfm?event=ajax.getInfo');).
2. The Model-Glue framework looks up the event (in the modelglue.xml file), determines what broadcasts to make, what results to evaluate, and what views to render.
3. Rendered views are returned to the browser.
4. Javascript receives the rendered view and does whatever it was told to do with it (document.getElementById('ajaxDiv').innerHTML = newcontent;)!!

So what is different when incorporating Ajax into the mix? Very, very little. The only REAL difference that I've found I need to keep in mind is in regard to what my view contains when the event I'm calling was specifically created for an Ajax call. For instance, in a "normal" event call, i'm typically going to be returning an entire html page, complete with body tags and the works. In an Ajax call, it will be either a chunk of html or some kind of Javascript-ready data (XML, JSON data, etc.) I have found that it's simpler, whenever possible, to return a chunk of pre-rendered HTML and have my client-side javascript simply place the returned html into the target div. Working with returned XML is where Ajax will become more complex, but even then, the complexity is STILL on the client side JS coding and not anything to do with Model-Glue at all.

I don't believe that there's anything else a person needs to know in order to be able to "think about" and visualize the interaction between Model-Glue and Ajax...it really is straightforward stuff! Not sure why just a few months ago it sounded like such a difficult concept to me....

Here's an illustration showing how Ajax would work with a Model-Glue page:



The actual Javascript that would handle the call could be as simple as the following example:
<!--- First, we load up the ajax library... --->
<script src="/js/AjaxLibrary.js" type="text/javascript"></script>

<!--- Now define the function we'll be using when the user clicks our "Ajaxified" link... --->
<script>         
function gotClicked(){
    /*first, retrieve the new content ... */
    var newcontent = ajax.remotecall('index.cfm?event=ajax.getInfo');
    /*now stuff the newly retrieved content into our ajaxDiv... */
    document.getElementById('ajaxDiv').innerHTML = newcontent;
}
</script>

That's it, really! Nothing more to it. After grasping this very basic understanding, the only thing left to do now is to select an Ajax library (personally, I take a shine to Scriptaculous...) and start learning how to use it!

Doug out.

Posted by dougboude at 11:34 PM | PRINT THIS POST! | Link | 6 comments
05 March 2007
Coldspring Automagic Lesson
Okay, I spent the greater part of today at work wrestling with a situation involving the injection of objects into other objects using Coldspring. After far too many hours of experimentation, furrowed brow, taking up my teammate's time, and frustration, I came to some conclusions. I can't explain the "WHY's" of the conclusions, but i can definately validate the conclusions themselves by my results.

Here's the scenario:

I have object B which extends object A. Object A has an INIT method, object B does not. Object B is being injected into object C. In my Coldspring.xml, I define a bean for object A, B, and C. Object A has a property value ("thisURL") being set via a <property> and <value> tag; object C has object B injected via a <property> and <ref> tag.  (see illustration and coldspring.xml snippet)

The Challenge (there are no problems, only challenges): When all objects are instantiated, I am calling method Y on object C. Method Y itself refers to a method X in the injected object B. Method X in injected object B refers to the property "thisURL" that was set via Coldspring within object A. WE GET AN ERROR, HOWEVER, INDICATING THAT THE VALUE IN OBJECT A WAS NEVER SET!

The Experimentation:
Hmmm, let's put a CFABORT tag in the "setThisURL" method in object A (the method needed by Coldspring in order to 'auto-inject'), with a 'ShowError' attribute. The abort is never executed, meaning that Coldspring never called the 'setThisURL' method on object A, despite the fact that we configured it to do so.

Okay, let's move everything we need into object B, remove the "Extends" attribute from the cfcomponent tag, re-do our Coldspring.XML to inject the 'thisURL' property directly into object B. Hey, it works!

Let's move it all back again, and try adding an init method to object B that performs a "Super.init()" to see if that fixes it. NOPE.

In my mind, in the mind of my peers, configuring Object A to have a property value injected should have resulted in Coldspring executing the "setThisURL" method. In fact, I NEED it to do that, because I plan on utilzing this base class (object A) by extending it with other objects as well, and the constant 'thisURL' needs to be present and the same in all cases. However, it did not behave in the expected manner.

As I stated at the beginning, I have NO idea why it did not behave as I expected. But, I thought I'd share the results of my headache in case it helps someone else avoid one later.

Conclusion:
The only way to make this work was to have Coldspring set the 'thisURL' property directly in object B if I wanted to be able to access it after having extended object A.

Illustration and Coldspring.xml snippet:


Snippet:
<bean id="objectA" class="model.objectA" >
    <property name="thisURL">
          <value>http://my.testserver.com/myWS.cfc?wsdl</value>
    </property>
</bean>

<bean id="
objectB" class="model.objectB" />

<bean id="
objectC" class="model.objectC" >
     <property name="objectB">
         <ref bean="objectB" />
    </property>   
</bean>

(back to top)
Posted by dougboude at 12:00 AM | PRINT THIS POST! | Link | 11 comments
02 March 2007
Dynamic Data Stores: A Model-Glue/OO Case Study
The scenario is that my team and I are building an app that must be able to seamlessly communicate between multiple datastores; one local, and the remainder remote and accessible via web service calls. In order to accommodate this, I've created a DataStoreAdapter component that has an instance of each of the specific data store interface components injected into it by Coldspring.

The challenge here was to find a way to dynamically call the appropriate methods on the appropriate data store object. After several different attempts, following is what I came up with that works (you may want to first sneak a peek at the visual I added to this post):


1. My user object (nestled within my user service layer) calls a generic method (named appropriately "callMethod") on the DataStore Adapter:

<cfset stResults = getUserDataStoreAdapter().callMethod(argumentCollection=arguments) />

(note: one of the arguments being passed in within the argumentcollection is 'methodname' with a value of 'getUserData')

2. The 'callMethod' method in the datastore adapter dynamically invokes the appropriate generic method:
    <cffunction ACCESS="public" name="callMethod" OUTPUT="false" RETURNTYPE="any" DESCRIPTION="I am the generic method that calls the appropriate backend system.">
        <cfargument name="backendSystem" TYPE="string" REQUIRED="yes" HINT="I am the name of the targeted backend system" />
        <cfargument name="methodName" TYPE="string" REQUIRED="yes" HINT="I am the name of the method to call within the targeted backend system" />
        <cfset var local = structnew() />
       
        <cfinvoke argumentcollection = "#arguments#" method = "#arguments.methodName#" returnvariable = "local.myResults" />
       
        <cfreturn local.myResults />
    </cffunction>

(arguments.method name in this case = "getUserData")

3. The generic method that was called invokes the appropriate specific method to return the autowired object needed, then the appropriate method is called from the specific backend object returned by the invoke:
    <cffunction access="private" name="getUserData" OUTPUT="false" returntype="any" DESCRIPTION="I am the generic getUserData function" >
        <!--- incoming argument collection is implied, no need to specify individual arguments. --->
        <cfset var local = structnew() />
       
        <!--- step 1: Make the call to the appropriate backend system... --->
        <cfinvoke  method = "get#arguments.backendSystem#User" returnvariable = "local.myObject" />

        <!--- we're invoking the backend-specific method from the appropriate object in the following line... --->
        <cfreturn local.myObject.getUserData(argumentcollection=arguments) />
    </cffunction>

(notice this specific method is private...can only be invoked by the "callMethod" method...)

4. The returned data is passed back up the calling chain to the original calling component.

Here's a diagram that might help to visualize it better.

Back to Top
All data massaging is encapsulated in the lowest level backend system components, so that each of them will return a consistent data set regardless of what they themselves are receiving from the backend system. And, as our upper management dictates that we interface with yet ANOTHER wack backend system, all we'll need to do is create the low level component and wire it in to our datastore adapter service!

Anyway, I was pretty pleased with how we addressed this situation so thought I'd share it.

Doug out.
Posted by dougboude at 4:35 PM | PRINT THIS POST! | Link | 1 comment
28 February 2007
Lost my public speaking virginity!
Yesterday evening (Tuesday February 27th) I gave my first live presentation! The Kansas City Coldfusion User's group (www.kcdevcore.org) invited me to come and speak about Model-Glue since I've been using it on my day job for the past six months or so and have learned it and OO coming from a strictly procedural background. I wasn't nervous until Jim Pickering (the UG manager) and I drove up to the building where they meet. The nervousness finally subsided about five minutes after I got into the presentation and then I felt like it flowed pretty smoothly.

My approach to this presentation was a little different than what most probably expected. I opted NOT to do a demo app at all since there's already a plethora of sample apps and tutorials out there already that are really great. Instead, I decided to take the time to share all of the things needed in order to know how to "think about" OO and model-glue...all of the things that I spent the most time researching and trying to comprehend so that I COULD take advantage of Model-Glue's simplicity.

So, for what it's worth, here is a link to my first presentation!

https://admin.adobe.acrobat.com/_a200985228/p45002941/

Actual PPT and accompanying Word doc for my presentation (caution: the PPT is nearly 5mb, so only click it if you really want it): MMG.PPT   MGPreso.DOC

Disclaimer: Everything I shared in this presentation is as I understand it to be, and was not painstakingly researched to validate it's accuracy. Whether it's accurate or not, complete or not, the understandings I share in the presentation work for me in my real-world scenarios and should be considered as "this guy's view"  and not as gospel fact.
Posted by dougboude at 5:14 PM | PRINT THIS POST! | Link | 2 comments
21 February 2007
Just What IS a 'Service Layer', Anyway?

(Consider hiring Doug to instruct you and your team in the ways of OO!)

I've been hearing the term 'Service Layer' everywhere for at least the past year and a half, as I'm sure most of my peers have as well. Whether it's seen in blog posts, heard during casual conversation at conferences, found in articles, or woven throughout almost any discussion at all concerning OO and Coldfusion...the term is quite prevalent and common these days. But I had a problem: the term held absolutely ZERO connotation in my mind. No picture was ever conjured up when I came across it, no corresponding 'Doug Boude' translation was found in my internal lexicon; I felt quite out of the loop. Ah, but recently that has all changed, as the phrase itself has not only come to life for me, but found an absolute and permanent place in all that I do, technically speaking; an epiphany, if you will.  I just figured there were others out there who, like myself, may still be kinda grasping for a way to think about the term, so what follows is my personal definition/explanation of what a Service Layer really is. Please feel free to append, addend, flip, and twist it to your heart's content until it's as whole as it ever can be.

SERVICE LAYER

 

 

This is not NEARLY as gray and ambiguous a term as you might think. Picture if you will, a man sitting comfortably on his sofa. In one hand is the remote for his very large plasma TV; in the other hand is a remote for his home theater system. The two remotes and the man are all objects, and all three come pre-built with things they can do. In the kitchen is the man’s wife; let’s think of her as the calling application. She barks out the order to the husband object, “START THE MOVIE, YOU IMBECILE!”. The husband object just happens to have a startTheMovie method, and begins to execute it. First, he manipulates the objTVRemote object, calling its “tvOn” method. Then, he manipulates the objDVDRemote object, calling it’s “dvdOn” and “dvdPlay” methods. Now he manipulates the objTVRemote again, calling the “inputSRC” method and switching the tv to receive the dvd input. Tada! Movie is playing now!

Pretty clear scenario, eh? Well, in this illustration, the MAN is acting as the SERVICE LAYER. Although he has a “startTheMovie” method, all he’s really doing is coordinating efforts between other lower level objects that actually do the work. His wife doesn’t care about the remotes or how they work, and her life is then simplified because she need only make her one call to her SERVICE LAYER OBJECT and he handles the nitty gritty details. Service Layer…not such a deep, complicated mystery after all, is it?

Doug out.

Posted by dougboude at 6:15 AM | PRINT THIS POST! | Link | 13 comments
29 June 2006
CFUNITED - MVC Unraveled
I spent the first hour of my morning listening (and gladly so) to Joe Reinhart preaching the need for us all to focus on just comprehending exactly what MVC is. Personally I felt like he started the presentation a few feet over most of the audience's head, using terms that were quite comfortable for him but new to many of us. Not his fault, and to his credit he did take the time to define many of the terms for us. Despite the elevated theme, in our short one hour together he accomplished his task in a two-fold manner: by increasing the audience's vocabulary, and by creating a sample app that used the MVC pattern.

That's right, MVC (Model View Controller) IS an application design pattern used across the board with any object oriented language. It also happens to popularly be known as the king of patterns, itself being an efficient symbiotic relationship between three other patterns of lesser scope and with more focused purpose. The Strategy pattern, Observer pattern, and the Composition pattern are what allow MVC to do what it does so well: keep our application segregated (loosely coupled) while allowing each portion of the app to remain highly specialized and autonomous (encapsulated).

The M in MVC is the Model. This is the portion of the application where nearly (if not all) of the business logic and database access will live. Business rules, queries, special validation...basically, if it has nothing to do with the user interface, then more than likely it will live in the Model. Physically the model will be a collection of CFCs that contain the functionality mentioned previously.

The View is that portion of your app responsible for displaying and gathering data. It knows nothing about the Model, the database, the backend system...it only knows that it will display the data given to it, and it will deliver the data it collects. Physically the View will consist of CFM templates containing primarily just layout information, such as forms, tables, and CSS.

lastly we have the Controller. It's job within the MVC design pattern is nothing more than passing data back and forth between the Model and the View; that's it.

The last 10 minutes (that's right, 10 minutes) of the presentation were spent building an entire basic blog application from scratch. Using the Model-Glue Unity framework and some Eclipse Ant scripts, he quickly was able to add in all of the CRUD (Create, Read, Update, Delete) functionality for blog entries and comments. How was this possible? By leveraging the many hours of hard work that have gone into producing and uniting three frameworks that have been all the buzz lately: Coldspring, Reactor, and Model-Glue. By strategically editing XML configuration files and using such shortcuts as "scaffolding", it is possible to auto-generate what would otherwise take a person days to code by hand.

Witnessing the creation of an entire application in just a few minutes is both exciting and disconcerting. On the one hand, Model-Glue Unity provides the developer with shortcuts that are nothing short of amazing, allowing productivity levels that I believe are unachievable otherwise. On the other hand, I could very well empathisize with the framework opposition camp, seeing how it is they could say that the usage of advanced frameworks such as Model-Glue will lead to the de-evolution of the CF community, removing many of the "needs" that often motivate developers to learn and grow. My own opinion, however, is that although frameworks such as this will allow us developers to become more productive, the convenience of it all will never quench the insatiable curiosity that has driven us up to this point, and we will always be "peeking under the hood" regardless of how much automation we adopt.
Posted by dougboude at 5:58 PM | PRINT THIS POST! | Link | 11 comments
CFUNITED - The Frameworks Debate Continues
Most of you may be aware of the debate over frameworks that has been raging off and on since at least CFObjective back in March of this year. In the one corner, representing all that is opposed to the preaching of frameworks to the masses is Simon Horwith. In the other corner, representing framework evangelists and followers from all over the globe is Hal Helms.

I first became aware of this schism at CFObjective when I attended a seminar by Simon Horwith entitled 'Object Think'. It would more appropriately have been entitled 'Friends don't let Friends use Frameworks', as he openly bashed everything else that conference was about and made himself the public prosecutor of the majority of the CF expert community's opinion on frameworks. Last night was no different, as I sat in on a session here at CFUNITED entitled 'Celebrity Death Match'.

It was an open and formal debate between Simon Horwith and Hal Helms, who himself participated remotely via Breeze. The conference room was filled to the hilt as newbie and seasoned alike gathered to be judge and jury for the topic at hand. Let me summarize for you the two opposing arguments, and then tell you what the verdict was.

The Framework Opposition says that frameworks are a crutch that essentially stifle and hinder a developer's professional and technical growth. This camp says that if a developer begins his or her career using an open-source comunity developed framework, they will remain handicapped and incapable of ever being able to truly build an application from the ground up on their own, resulting in a pool of 'talentless talent'.

The Framework Supporters maintain that using a framework, though it is indeed a greater initial investment of time and resources, is a ONE time cost that is greatly offset by the long term benefits afforded at every level. The use of frameworks creates a community-accepted standard that takes an already robust common vocabulary to an exponential level, not only enhancing our ability to transfer and share knowledge on a large scale, but also to easily exchange entire applications and code without the need for significant investments of time to utilize. They also counter that in NO way does the usage of frameworks stifle a developer's technical growth, citing the fact that none of their own growth was stunted when they started using Model-Glue and the like.

Near the end of this mock trial, Simon made a point that set me free of the conflict of the debate. He related an account of a project he had worked on in England some time in the past in which he had 100 developers under him. He shared how it was that this large team had tried using Mach II, tried using Fusebox, and in both instances found those frameworks wanting. Then he showed them his own 'methodology', which they readily adopted and used to successfully complete the project. Simon's point was that he and his team had accomplished a large task, and didn't need to use a framework in the process. But between Simon's words lay the whole truth of this matter, the truth that finally freed me from the points and counterpoints that were tugging my intellect in both directions. This truth, folks, is this: Any application that actually works uses a framework. Simon likes to call his framework his 'methodology', resisting the urge to give it any kind of formal name or documentation, and thus disguising it obscurity and vagueness. Nevertheless, it is indeed a framework. So folks, this whole debate is bogus, the opposition's stance is vapor, and in reality is a one sided debate with the answer to the question of the validity of frameworks already woven into the very fabric of both sides of the argument. The answer, my fellow CF coders, to the question of whether or not using frameworks is a good thing, is an unequivocable "YES". You need frameworks, and whether you roll your own or adopt a community standard, there is no way to avoid them...logic will not permit it.

After being set free by this epiphany, I then had to wonder at what it is that truly motivated Simon Horwith to take such a public stance. What moved him to place himself in the limelight and accept the label of 'framework-hater'? Is he plain and simply a rebel, one who will swim against the current on principle alone despite the direction it takes him? Or, more than likely, is it that he is simply a self-appointed sentinal of the free thinking world, fearing the loss of innovation and open-mindedness and the onset of apathy and weakness in the CF community's skill set? Finding nobody else who could see what he saw, did he then, in our defense, take up the cause to protect what it is he cherishes and believed to be utterly at stake, giving his all to "keep balance in the Force"? We are the jury here....

Back in the conference room the smell of popcorn and cotton candy filled the air as the debate was brought to an end. The judges called for final comments and then a show of hands from the "jury" as to who had won this debate. Almost unanimously, the jury sided with Hal.

Bottom line folks: ignore this debate and give the hard work of your more advanced and experienced peers the attention it is most worthy of. Must you choose a religion (framework)? No. Choose them all, choose one, choose none and roll your own; it doesn't matter. It's all good, it's all good. what IS important is that you keep striving to learn, always push yourself to new levels of understanding, and then you, like ALL those in both corners who are so zealous for their CF causes here, are right.
Posted by dougboude at 12:23 PM | PRINT THIS POST! | Link | 8 comments
16 June 2006
Personal OO Lexicon
Learning OO can be somewhat intimidating at first, especially coming from a procedural CF background.  One of the first things needed in order to be able to take advantage of the different posts, presentations, and articles so graciously shared by your more advanced ColdFusion brethren is to have the OO vocabulary ready at hand. In order to assist with this, I am sharing with you my own personal lexicon of the terms and phrases I find myself most often exposed to in pursuit of mastering OO, especially with regards to the Model-Glue framework.

I'll be updating it periodically as my own understanding grows, so check back every now and then for a fresh copy.


Doug Boude's Lexicon
Posted by dougboude at 12:00 AM | PRINT THIS POST! | Link | 3 comments
ColdFusion Component Variable Scopes: This, Variables, and Var
Scope within a CFC is incredibly important, and even moreso when you start using Object Oriented Frameworks for your application. It is essential to understand it in order to save yourself a lot of mind numbing troubleshooting, especially when writing recursive functions.

The following information is also contained in my OO Lexicon, but I felt like it was sufficiently encapsulated (that's an OO term ) to go ahead and re-post it here.

THIS, VARIABLES, and VAR Scopes

These are three of the variable scopes that are found within the world of a ColdFusion object or component, and three scopes that can make you pull the rest of your hair out when you don’t know how to think about them. In a nutshell, you’re looking at three increasing levels of variable privacy, from most liberal to most private.

The “THIS” scope holds items that can be directly accessed from anywhere inside OR outside of the object itself. Consider the following example of an instantiation of the myTest.cfc that has a variable called THIS.GLOBALVAL within it’s INIT method:

The component code...

<cfcomponent>
       <cfset this.GLOBALVAL = "Whatchoo lookin at Willis?" >
       <cffunction name="init" output="false" returntype="myTest">
          <cfreturn THIS>
       </cffunction>
    ...
</cfcomponent>

The call from the outside template...

 

 

<cfscript>

            myTestObj = CreateObject(“component”,”myTest”).Init();

            myTestObj.GlobalVal = “I set you from outside of the object!”;

</cfscript>

 

In this example, because the variable GlobalVal was put into the THIS scope within our object, our application could directly access it as a property. Cool, if that’s what you intended to happen. Not cool if it wasn’t.

 

 

 

The VARIABLES scope within a component object is a scope that can be accessed by any method within the object at any time, in real time. In other words, if our component had set up a variable called Variables.LimitedVal, all methods will be sharing that one instance of the variable. If method one sets it to “5”, and later the app calls method two which reads that variable, it will see the value “5”. Any attempt, however, from outside the object itself to manipulate that value will result in an error. The following would FAIL:

The component code...

<cfcomponent>
       <cfset variables.GLOBALVAL = "Whatchoo lookin at Willis?" >
       <cffunction name="init" output="false" returntype="myTest">
          <cfreturn THIS>
       </cffunction>
    ...
</cfcomponent>

The call from the outside template...

 

 

<cfscript>

            myTestObj = CreateObject(“component”,”myTest”).Init();

            myTestObj.GLOBALVAL = “I set you from outside of the object!”;

</cfscript>

 

 

 

 

And finally, the VAR scope. This scope is one which can be seen only from inside of the actual method itself. For example, I can have three methods, each that use a variable with the same name that was initialized within the VAR scope, and no method will ever see the variable used by the other methods. It is a VITAL thing that you initialize your private variables in the VAR scope inside of your methods, because by default they are set up in the VARIABLES scope, and who knows WHAT havoc will occur if you have methods sharing variables that were intended to be private. Consider the following sample of initializing a variable in the VAR scope:

 

<cffunction access="public" name="sampleMethod" output="false" returntype="void">

                        <cfargument name="headlines" type="array" required="yes" >

                        <cfargument name="sourceID" type="numeric" required="yes">

                       

                        <cfset var iterations = 0>

                        <cfset var urlitems = "">

                        <cfset var newInsertItems = arraynew(1)>

</cffunction>

 

Only the ‘sampleMethod’ method will be able to see and manipulate those variables set using the ‘var’ scope. VAR is so private, that every other method within our component could initialize variables with the same name within their own var scope and no overlap would occur.

Posted by dougboude at 12:00 AM | PRINT THIS POST! | Link | 29 comments