Doug's Resume
OO Lexicon
Chat with Doug!
Recent Entries
You may also be interested in...

heaters
hotels boeken in 7 sec
Engagement Rings
Online Dating Australia




SURF'S UP!
You:
Your Web Site:
<< September, 2007 >>
SMTWTFS
1
2345678
9101112131415
16171819202122
23242526272829
30
Search Blog

ColdFusion Jobs
Recent Comments
Re: Promoting Family Unity: Lowering Your Utility Bills! (by Fernando Lopez at 5/07 10:12 PM)
Re: Why I Hate ORMs (a solicited rant) (by Richard at 5/06 10:56 AM)
Re: Why I Hate ORMs (a solicited rant) (by dougboude at 5/06 10:27 AM)
Re: Why I Hate ORMs (a solicited rant) (by Richard at 5/06 6:50 AM)
Re: Why I Hate ORMs (a solicited rant) (by Sean Corfield at 5/06 1:40 AM)
Re: Why I Hate ORMs (a solicited rant) (by Steve Bryant at 5/05 5:07 PM)
Re: Why I Hate ORMs (a solicited rant) (by dougboude at 5/05 4:36 PM)
Re: Why I Hate ORMs (a solicited rant) (by Mark Mandel at 5/05 3:52 PM)
Re: Why I Hate ORMs (a solicited rant) (by dougboude at 5/05 3:42 PM)
Re: Why I Hate ORMs (a solicited rant) (by Brian Rinaldi at 5/05 3:14 PM)
Categories
Archives
Photo Albums
Funnies (5)
Family (3)
RSS
Reciprocal Links

Powered by
BlogCFM v1.11

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.

DOWNLOAD SECURITY SKELETON APP ZIP
Posted by dougboude at 3:54 AM | PRINT THIS POST! | Link | 7 comments
22 September 2007
Promoting Family Unity - The Power of Forgiveness
The other day a very close friend of mine IM’d me, quite distraught and looking for input on a situation he had at home between he and his two young daughters. The situation was that his wife’s father was terminally ill and so she was out of town, spending as much time with her father and family until he passed away. So my friend, we’ll call him John, was left to tend to all of his usual burdens as provider and dad as well as the responsibilities as primary homemaker and caregiver that his wife normally fulfilled. John is a young guy, full of energy, charismatic…every time I picture him I see him smiling. This particular day, though, even though our conversation was purely textual, John wasn’t smiling at all and was carrying a massive burden that he didn’t know how to move.

The burden began the morning prior, when he had instructed his girls to clean up the room they shared. After some time when they hadn’t reported completion of their task, he went to inspect and found that they hadn’t even touched it. Wrong morning, wrong side of the bed, missing his wife, burnt toast…all of the little things that he had been working hard to deal with had accumulated and his daughter’s lack of effort or obedience became the final straw. Emotions got the better of him, and the correction he dealt to the girls, especially the older, more responsible one, was completely (this is his assessment, not mine) out of line and unproductive. In fact, it was downright mean and ugly…picture a grouchy father bear roaring at his cubs. He felt guilty, but knew his daughters were in the wrong, so he maintained his position as “Dad” and stuck to his guns, withdrawing none of the harshness he had dealt earlier. The girls got on the stick and cleaned their room, but bearing the stinging mental wounds of a harsh, emotionally charged correction.

The next morning was Monday and the girls had school. The majority of yesterday’s scene had distilled down to just the parts that were still relevant: the guilt he felt for his lack of control and judgment. This in itself added to the normal burden he was carrying, and thus made him more sensitive to any disobedience on the girls’ part. As John describes it, he had asked his older daughter several times the day before about her homework…was it complete, did she have it, each of which time she assured him all was well. As they approached her school that morning, he asked her one more time just to make certain, and the response she gave was NOT what he wanted to hear. She had forgotten the homework at grandma’s house, and in fact, hadn’t done it at all. Rightly so, he judged her to have lied to him and the scene became once again one that I know afterwards broke John’s heart to even recall.

It was later that day that he contacted me, elucidated on the details of it all, and asked me for advice. Having literally spent over half of my life so far raising my own seven children, and being part of that “Village” with many of my close friends and their children, I have stood in John’s shoes more times than I care to recollect. I’ve always put a lot of energy into making it a point to learn from my mistakes, as much as my ability to do so is, and so shared with him what I believed would help. Knowing that such a situation is not nor has ever been unique to only he and I, and knowing that a lot of fathers (and mothers) out there have and will deal with the same burden of having made the wrong choice when correcting their child, I now share my advice with you as well. Take from it what you will, leave of it what you will, let it work in you as you will. The first thing I did was to remind John of some very basic, yet extremely important truths; truths that are so simple that in the tunnel vision we have in the midst of such a situation, we absolutely cannot see them at all.

Foolishness is bound in the heart of a child. The Creator Himself tells us this, that children are not created wise, informed, or with good judgment, but rather are almost completely void of anything we adults would call common sense. It’s not their fault, they aren’t accountable for these lackings, and they were given parents to help weed out the foolishness and impart the wisdom of living. It’s when we forget this and hold them accountable for things they cannot possibly fulfill that we set ourselves up for such a fall as John took. Of course it goes without saying, that as children age and learn they slowly become more and more accountable for their actions, but careful attention to who they are as people and using our best judgment in individual scenarios will help us to decide where that accountability lies.

Children are people. Yes, they wear a size one shoe whereas we wear a ten; they eat half a bowl of oatmeal whereas we eat a whole one; but miniature though they be, they are as whole and complete a human being as you or I. Their little hearts can be broken or be filled with joy; their little minds can be captivated or bored; they have their own little individual personalities, quirks, and habits; they can be provoked to anger and grudges; they can feel sorrow and repentance, humility and pride, stubbornness and irrationality. Even when our child first comes into the world, did we not marvel at their every little detail and comment out loud and to ourselves continuously how incredible they were and possessed everything that we did? “Look at his little fingers!”, everybody exclaims, as they can’t help but see that this tiny, tiny, swaddling bundle is EXACTLY like us. Children are people, minus judgment and wisdom, and when we deal with them we should deal with them as we would have ourselves to be dealt with. Children are taught to honor their parents…but a parent should first behave honorably toward them, with the same respect you would give any other adult. Not leaving off the fact that you are the authority, but rather letting the fact that they ARE completely in your hands be the thing that keeps you walking uprightly toward them.

YOU are a person, a human being, who, although you may have managed thus far to take tens of trips around the sun, are and always will be in a constant state of growth and learning. As such, my friend, you will make errors in judgment and you have to accept that about yourself. As I told John and as was once shared with me by a dear mentor and friend of mine, “everybody falls; just make sure that you fall forward”. Just as holding on to unrealistic expectations with your children can only result in a negative outcome, so too will holding unrealistic expectations for YOURSELF keep you from ever being able to “fix it when you break it”. Don’t get me wrong, I believe that everybody should set their standards and expectations just out of their own reach in order to excel, but don’t make the mistake of somehow believing that you won’t make a mistake. I reminded my friend John of this. I had to, because the man was beating himself up in the wrong way and not giving his own self the opportunity to do what I know is the next most important step of all: repent.

After reminding John of these basic things, I then told him what he had to do to fix the situation, both with the girls and with himself so that everybody could gain from the situation. He had to go to them, either individually or together, and the man had to ask their forgiveness. Not just an “I’m sorry”, but a genuine humbling of himself before them, an admission of his guilt and lack of judgment in how he dealt with the situation, and an assurance that he would do his best to learn from this and not repeat it again. Oh the incredible and almost miraculous healing that takes place within every heart involved when a child’s parent, those whom they rely on and love with all their being, comes to them and, figuratively speaking, bows before them in an act of such contrition! Oh the incredible lifelong lessons that are taught in the example of that one single act! And lessons, not only for the child, but for the parent, too. Asking for your child’s forgiveness is absolutely the only right and productive thing that can be done when you or I or John have left love, patience, and judgment by the wayside in correcting our baby.

Asking your child to forgive you is far from merely an act of contrition: it is a lesson that is given without ever  directly giving it. Your sincere apology will not only be giving them what will help them in their own struggle to forgive you, but you will be SHOWING THEM how an adult behaves when they have wronged another human being, and you will teach them how it is that they will be able to perform the same healing process on themselves when they too one day become a parent and make the same age-old mistake. Humbling yourself before another person, especially your own children, is in every single way the best example you could ever set for them.

So, this is what I shared with John, facts and lessons that I absolutely know to be the unequivocable truth because I have stood in his shoes more times than a man should, in my opinion. I still make mistakes; I still sometimes feel like I’m unsuccessfully grasping in the dark for those glimmers of truth that would help me to deal with a situation with my children properly. In more ways than I care to admit most times, I am far from being as mature as I believe a man my age should be. But I keep on trying, and when I wrong my children or any other person, most of the time I find the good judgment to go to them and ask their forgiveness for it. That same beloved mentor I mentioned earlier also once told me this regarding the summation of what repentance should be: “Admit it, Quit it, and Forgit it”. You will make mistakes, accept that, and when you do, admit it, try harder not to do it again, and then take the most important step of all, my friend, and forgive yourself.
Posted by dougboude at 7:36 AM | PRINT THIS POST! | Link | 2 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
Dynamically Outputting Query Data 'X' Columns Across
Dynamically outputting query data in "X number of columns across" is no new thing, but since I found myself having to do that very thing this morning, I thought I'd share this sweet little snippet for the benefit of everyone (but mostly for my own snippet collection since I seem to tend to "forget what I once new"... lol).

In this example I'm going to output a series of short questions using the following query result:
coldfusion query result set

WIth the final html and result looking like this:
html table final result of outputting x rows across

<table width="100%">
    <tr>
        <td>Dead loved ones present</td>
        <td>Animal(s) as prominent player(s)</td>
        <td>"Significant Other" present</td>
    </tr>
    <tr>
        <td>Familiar Setting</td>
        <td>Unfamiliar Setting</td>
        <td>Awoke Emotional</td>
    </tr>
    <tr>
        <td>Dreamed you were Awake</td>
        <td>&nbsp;</td>
        <td>&nbsp;</td>
    </tr>
</table>
generated html

Without further adieux, your snippet:

<cfset numColumns = 3 /><!--- set the number of columns to output --->

<!--- determine number of total rows to output --->
<cfif qryQuestions.recordcount mod numColumns eq 0>
    <cfset numrows = qryQuestions.recordcount/numColumns>
<cfelse>
    <cfset numrows = int(qryQuestions.recordcount/numColumns) + 1>
</cfif>

<!--- output the table! --->
<table width="100%">
    <cfloop from="1" to="#numrows#" index="i">
        <tr>
            <!--- create columns for this row 'i' --->
            <cfloop from="1" to="#numColumns#" index="j">
                <!--- calculate which record we're outputting --->
                <cfset qryRow = ((i-1)*numColumns)+j >
                <!--- ...as long as wer're not trying to access a query record beyond our 'recordcount' value... --->
                <cfif qryRow lte qryQuestions.recordcount>
                    <td><cfoutput>#qryQuestions["question"][qryRow]#</cfoutput></td>
                <cfelse><!--- output an empty 'filler' td... --->
                    <td>&nbsp;</td>
                </cfif>
            </cfloop>
        </tr>
    </cfloop>
</table>
Posted by dougboude at 5:34 AM | PRINT THIS POST! | Link | 0 comments
20 September 2007
Adobe User Groups Everywhere: I Have A Dream

I haven't been a member of very many Coldfusion/Adobe-centered User groups, but the few I have participated in seem to have a common, perpetual and prevalent concern floating in the air: membership and attendance.

I have very recently had the privilege of being part of the resurrection of San Antonio’s own Coldfusion Users Group. It is being born out of the existing, more general, Alamo Area Multimedia Users Group due to the sudden influx of hardcore Coldfusion users. Our first meeting was two weeks ago. Nine of us sat in a library and talked about what we wanted to gain by participating in the group, who the group Manager would be, when we would meet, where we would host our site, etc. It was fast paced, and everybody in the room was SO zealous, it was contagious. All levels of skill were represented, and you could just feel the hunger for knowledge that the junior among us harbored. Being a brand new group, however, did not exempt us from the same question of attendance that even well established groups entertain: “How would we increase our membership”?

Here, O Best Beloved, is where the true heart of this post begins, as I found myself engaging in a session of combinatory play while looking at this scenario from a whole other perspective. My first inclination was to think of all the companies in town that I knew used Coldfusion, and then come up with ways to solicit their developers. Then came the epiphany that I must say seemed at the moment (and still does) to be absolutely brilliant:

If we want more Coldfusion developers, then why on earth don’t we just MAKE them ourselves?

We who are intimate with Coldfusion KNOW the deep level of satisfaction and sustenance that it can provide. We know that learning it is not so very difficult, we know the true power that it puts into the hands of a developer, and we know that if more people who already have a propensity for technology and problem solving were exposed to those basic truths, they would very likely adopt it in a heartbeat as we have we.

I raised my hand (keeping things orderly, you know) and when called on shared my idea with the group. The sudden silence and ambiguous faces made me feel as though I had just suggested that the emperor had no clothes, my idea appeared to be so foreign to them. In a moment, though, as they mentally extrapolated what investing the time to teach Coldfusion basics to newbies could mean, they heartily seconded the motion to do so (hammering out the details of who, where, and what will be fodder for the next meeting).

Now, I am not vain enough to think that no other User Group has ever come up with this idea. But again, in all the group’s I’ve had the privilege of sharing time with, and in the midst of all the apparent “worry” over attendance, I never once have heard anybody step outside the mindset of “bring in the Coldfusion developers!” and say “hey, why don’t we grow our own”? Isn’t it a marvelous idea, though? If I were a User Group Manager, or an Adobe corporate brain, and my prime directive was to increase User Group participation, I would be organizing and marketing free “Intro to Coldfusion” classes within my target area. I would put an ad in the Thrifty Nickel; I would post it on Craigslist; I would have all my existing members put flyers up on the corkboards at the office. Shoot, I would bring my kids! (we all know that even a child can learn Coldfusion…) I would follow the true spirit of Evangelism and I would preach Coldfusion to the ignorant and uninformed, not just those already converted. I’d gather as many people as would come and meet in a donated conference room with a few cookies and cokes in the back. I’d fire up my laptop and I would INSPIRE these people! The beauty of Coldfusion’s simplicity and elegant folding together with HTML would be revealed to them; the mystery of the request lifecycle would be elucidated to their previously darkened minds; the POWER of loops and conditionals would be handed over to them; and the secrets of interfacing with amorphous backend data would be made secret no more. When they would leave that one hour revival, those people would walk away filled with the spirit of Coldfusion, empowered and enlivened enough so that they would not soon forget what they had learned. That thin foundation would be the catalyst that would bring them back to the NEXT free session to add yet another layer, until they were brought to the point (as I was so long ago) where they could begin to add onto that foundation for themselves. The end result (in my optimist’s vision, anyway) would be a thriving, zealous San Antonio Coldfusion User Group whose collective affection for their craft would inspire all who approached us.

Wow. I think I’ve just cheerleaded myself to the point where I need to go sleep it off. Lol. But, my point is this, Coldfusion (and other Adobe-oriented) User Group Managers AND MEMBERS: If your membership and attendance leaves something to be desired, don’t be afraid to deviate from “the way we’ve always done it”; get creative, think outside the bun, and why on earth not make the time to grow a few of your own from scratch?  The process of introducing someone to Coldfusion and imparting your knowledge of it to them is SO rewarding and SO educational, most especially to those who are teaching. Everybody profits, the Group grows strong, and Adobe Coldfusion (and other products) lives long and prospers.

Doug out.

Posted by dougboude at 3:44 AM | PRINT THIS POST! | Link | 6 comments
17 September 2007
Just what IS an Object, Anyway?
Making a distinction between the definition of an Object versus a Class is probably not a vital factor in one's OOP abilities. I believe, however, that if we're going to have and use terms, we should do so accurately in order to effectively communicate. I absolutely hate it when people misuse words in ignorance, such as when someone refers to a Cicada as a Locust (a locust is a grasshopper! a Cicada is a Cicada!), or a bat as a rodent (they're both mammals, but that's as close as it gets). For clarification's sake, then, following is what I have discovered to be the definition of an Object in a Coldfusion context.

When asked the question "what IS an object?", I nearly always respond with a description that includes the phrase "living and breathing". Here's why:

According to science, the basic checklist to use when determining if something is alive or not is:

  • Can it Reproduce?
  • Can it Obtain and use energy?
  • Can it Grow, develop, and die?
  • Can it Respond to the environment?

Once you issue the command

<CFSET objFrankenObject = CreateObject("component","model.Frankenstein") />

Coldfusion takes what is a lifeless blueprint (the Frankenstein.cfc file) and breathes life into it, creating something that meets every one of these criteria!
  • Can this Creation reproduce? It certainly does have the potential, depending on how it was designed.
  • Can it grow, develop, and die? Abso-frickin-lutely! It occupies a certain amount of ram, and I guarantee you that as soon as I call one of its setters and stuff a large structure into it that it will grow and then occupy MORE ram! If I <CFSET objFrankenObject = "", it'll be dead and gone.
  • Can it respond to its environment? Of course; that is what it was created to do in the first place.
  • Finally, can it use and obtain energy? Without spending too much effort in attempting to stretch the analogy into that realm...let's just say 'yes' since our server is plugged into an active outlet.
By all accounts, this Object is a living, breathing, programmatical entity. This way of thinking about objects in contrast to the flat, lifeless CFC/Class (that so often is referred to as an object, erroneously in my opinion) makes the distinction between them crystal clear.

In a nutshell then:

Object = living breathing instance of a CFC (Class)


Again, thinking of objects in this way may not make one a better or worse OO programmer, but it definitely doesn't hurt to have a finer understanding of what the terms truly represent, right?


If you missed the post "Just What IS a Class", it's the complement of this one and might make good prerequisite reading since it defines what an object IS NOT.

(this definition along with all the others I've collected over the past year can be found in my personal OO Lexicon)
Posted by dougboude at 12:34 PM | PRINT THIS POST! | Link | 0 comments
14 September 2007
Just What IS a CLASS Anyway?
OO Terms in a Coldfusion Context
Which phrasing is correct:

1."Hey boss, take a look at this object I just wrote!"
2."Hey boss, take a look at this class I just wrote!"

The answer is number 2. After reading the following definition, hopefully the phrasing in choice 1 will sound strange to you.


The term "CLASS" tends to be misused, or even UNDERused in OO conversations with regard to Coldfusion, and is very often omitted in favor of the term Object. There is, however, an important distinction between the two. Let's illustrate this by asking a simple question that I KNOW you know the answer to.

Can you open a CFC in notepad?

The answer is "Heck yeah!" Why is this true? Because in reality, a CFC is JUST A TEXT FILE with a '.cfc' extension. It's the content, however, of this text file that makes it special, because what it contains are the blueprints that Coldfusion uses to create living breathing objects. So, when you tell Coldfusion something like


<cfset objUser = createobject("component","model.user")>


 you're actually saying "CF, go find the user.cfc file, open it up, read the contents, and give me back a living breathing OBJECT that is built according to the blueprints."

In a very small nutshell then,

A Class is the definition used to create an object.

The class tells Coldfusion what methods to create, what variables are available, what items are returned...gosh, it tells CF every little detail about how the finished product (object) should behave and respond!

Class = CFC = Blueprint
 
For contextual illustration, a Java ".class" file is also just a text file containing the blueprints that Java needs in order to construct a Java object. A CFC is a Coldfusion class containing the blueprints CF needs in order to construct a Coldfusion object.


 The plan...the blueprint...the CFC... it's what the rest of the OO world refers to as a CLASS, and so should you!

(this definition along with all the others I've collected over the past year can be found in my personal OO Lexicon)
Posted by dougboude at 6:12 PM | PRINT THIS POST! | Link | 1 comment
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
11 September 2007
last time I promise...Coldfusion is Dead!
Sys-con and some of the other brilliant prognosticators out there were my source of inspiration this morning on the drive in to to work...here, have a look-see into my unique mind....

CF is dead!
Posted by dougboude at 4:49 PM | PRINT THIS POST! | Link | 0 comments
My Blog is Now MUCH More Print Friendly
Just an FYI for those who might be interested...

At the suggestion of a recent reader of my blog, I have made it MUCH easier now to print out any posts of interest. At the bottom of each post is a link which will render it for you in a new window (and in a format I'm sure you'll recognize).

If you do give this a whirl and find any posts that cause the process to "not work so good", please let me know about it!

Thank ye.

The Management
Posted by dougboude at 4:00 AM | PRINT THIS POST! | Link | 0 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
05 September 2007
To Every Man A Penny

“There’s one more waffle if anybody wants it!”, called mom from the kitchen. All around the dining room table the kids and dad sat looking quite full, with only syrup and crumbs on their plates. “I think everybody’s full, Sweety”, dad called back, the children’s nodding heads confirming his observation. Mom appeared a minute later with her glass of orange juice and sat down between Amber and Meghan. “So what shall we do on such a fine Saturday?”, dad asked everyone. “What do you think, Honey?”. “Well”, said mom with a smile on her face, “I was thinking that if everyone gets their chores done and then helps give the van a bath that maybe…just maybe we could go to the movies”. “Hoorahs!” and “woohoos!” came from all around the large, wooden table. “That’s an awesome idea!”, said the two younger boys Ethan and Andy, almost in unison. “Alright then, kiddos, you guys clear your places while your mom and I decide what chores need done”. Ethan, Andy, Jeremy, Meghan, and Amber quickly scooted back their chairs and began to carry their plates and silverware to the kitchen. “Here, let me carry your plate for you, Amber”, Meghan said. Amber was only six and still had a little trouble when trying to carry too many things at once. With the kids all down the hall, dad rubbed his full tummy and said “Okay mom, what do we need to accomplish before we go out today?”. “Well”, replied mom, “I have to go pick Chrissy up from her friend’s house later. Besides that, let’s get three of the kids to do the dishes and the other two on the bathrooms, then they can all work on the van together”. “Sounds good to me”, said dad.

 After the chores were finished, the kids found dad outside with the garage door open. He had a large orange plastic bucket, two small buckets, a long handled brush, and several cloth gardening gloves. “What are the gloves for, dad?”, asked Andy. “Those are for you to wash the van with”, dad said smiling. “They’re left over from our gardening earlier this year so I figured you could use them instead of rags. Just dip your hand into the bucket then start rubbing”. “Sounds like fun”, said Meghan, and dad tossed everybody a glove. “Andy”, said dad with a wink, “you’re in charge of the hose, but no squirting your brothers and sisters, okay?”. “You can count on me”, Andy said, winking back. The kids went to work and had already surrounded the van, soppy white gloves rubbing the soapy vehicle all over when mom appeared with her purse and car keys. “I’ll be right back”, she told dad as she kissed him goodbye, “I’m going to get Chrissy now”. The children were too busy washing the van and ‘not getting wet’ to notice when mom left in the other car.

 By the time mom got back, everybody was gathered around the computer looking over dad’s shoulder at movies and times and deciding which they wanted to see. Chrissy walked by on the way to her room to put her overnight bag away. “Hey dad”, Andy asked a bit distraught, “is SHE going to the movies?”. “Who, Chrissy?”, dad inquired, “I don’t know; I think we should see if she wants to”.  Andy’s face suddenly changed from his usual cute, slightly pudgy smile, to a dark scowl, reflecting the twelve year old’s displeasure at the very idea of Chrissy going with them. “But that’s not fair!”, Andy exclaimed.  The other children, some of whom were already being affected by Andy’s sentiment, were listening intently. Meghan spoke up next: “She didn’t help us clean OR wash the van!”.  It was obvious now that all of the children were questioning to one degree or another whether or not Chrissy should be allowed to go the movies with them. “Alright now”, dad said sternly while shutting down the computer, “that’s enough of that talk. I want you all to go wait in the living room for your mother and I”. The children did as they were told, grumbling all the while about how unfair it would be for Chrissy to get to go when she hadn’t helped with a single chore.

 “Momma, we’ve got a small rebellion on our hands”, dad said to mom, stealing up behind her in the kitchen. “Seems some of the kids think it wouldn’t be fair for their older sister to go to the movies with us because she didn’t help with the chores. I’m thinking they need a quick reminder about personal accountability”. Mom and dad had been raising kids for a long time and made a good team, so very little pre-discussion was needed. They walked into the living room together, sitting side by side on the loveseat, facing the children. “Nobody else speak unless you raise your hand and I call on you”, dad began. “Okay, now Andy, explain why it is that you’re upset”. Andy looked around at the others, then said “Well, you and mom said that if we did our chores and washed the van that you would take us to the movies, and Chrissy didn’t help at all so she shouldn’t be able to go!”. A soft murmering of agreement came from the other children in the form of head nodding and whispers. “Jeremey, do you agree?”, mom asked. “Well, she didn’t help”, he shyly answered, not seeming to be sure of his position on the matter. “Meghan, what was our agreement?”, dad asked. Like a lawyer who knew their case inside and out she thoroughly and concisely replied, “That if we did our chores and washed the van, you and mom would take us to the movies”. “and ARE we taking you to the movies?”, dad asked the children as a group. “Yes”, came the unanimous answer. A moment of silence ensued, mom and dad allowing the children time to consider things up to this point, then mom shifted slightly in her seat and said in her firmest mom-voice: “Every one of you are being rewarded because you fulfilled your part of an agreement with us, but I think that you are all also forgetting that the privilege and right to give and withhold rewards belong to your father and I, to use as we see fit. Now, does anybody disagree with that?” All heads shook in the negative. “So then”, dad interjected, “if your mom and I take into consideration all of the times that Chrissy does the dishes without being asked, or folds a load of you boys’ laundry, or never complains when we ask her to check the mail or walk Amber to her bus stop, are we wrong for deciding to reward her by inviting her to go along with us to the movies today?”.  The look of animosity that had been on nearly every child’s face had now turned to one more so resembling shame, as they all shook their heads in unison; they understood exactly what mom and dad were saying. Seeing that they were obviously feeling repentant, mom spoke up to help them work with it,  saying “It’s alright, kids. Feeling that things are unfair at times is perfectly natural. But, we have to learn that we should look to ourselves when it comes to getting rewards…ask yourself if you did what you were supposed to, did you do a good job, are you worthy of a reward...and don’t worry so much about what other people do or do not get”.  The children sat silent, taking it all in and feeling ashamed at how they had thought about their sister. “Hey, did you guys know that 2,000 years ago someone else was angry about the exact same thing you were?”, dad said. He knew that the subject had been sufficiently covered, but just for good measure he thought he would end the lecture with a quick lesson from the Bible. At hearing his words, the children looked puzzled, trying to figure out who in the world could have been upset about their sister going to the movies 2,000 years ago. “Yep”, said dad, “in the book of Matthew Jesus told the story of a man who agreed to work all day in another man’s field for a penny. As the day went on, the farmer kept hiring more and more people to work in the field, and at the end of the day everybody showed up to get paid. Every man was paid exactly what he and the farmer had agreed on, but when the first man who was hired went to collect his pay, he expected that he should get more money than all the rest because he worked longer. How many of you think the farmer gave him more money because he worked longer?”, dad asked the children. Three hands went up. “How many of you think the man got only a penny?”. Two more hands went up. “Well, I tell you…that man got exactly what he had agreed to, a single penny, and it didn’t matter how much or how little anybody else had worked. The whole point was, it was the FARMER who had the right to decide what to do with his money“. Smiling faces could be seen amidst the children now as they considered the fact that this their Saturday moral dilemma had already been a discussion held so long ago. “Okay now”, asked mom, “who wants to be the one to invite Chrissy to go to the movies with us?”. All hands immediately rose enthusiastically. “You can all go ask her”, dad said smiling. Dad pulled mom closer and hugged her, while the sound of urgent footsteps hurrying to Chrissy’s room boomed up the stairs. “We have good kids, momma”, Dad said. “Yeah”, replied Mom, “we sure do”.

Posted by dougboude at 12:06 AM | PRINT THIS POST! | Link | 0 comments