Coldspring injection

Published October 11, 2006 on adamfortuna

    Coldspring injection is one of the coolest things I've seen in a long while. It's changed my whole view of how I design CFCs (for the better) and in doing so encouraged me to be a better programmer (well, hard not to following the standards).

    To begin with, Coldspring can be used organize your CFC and relationships between them. One of the most obvious and huge implications of this is that by having your relationships handled by CS, your CFC don't need know about each and every object they deal with. Instead they'll just magically have them available. This means that your CFCs will be more reusable, have less code and be easier to maintain.

    So here's an example of all this. Say you're creating a user object for someone browsing the page (that will later go in the session scope). This would be the same using Coldspring or not in my example here…

    <cfcomponent name="user">
      <cffunction name="init" returntyoe="any">
        <cfset variables.login_time="now()">
        <cfreturn this="this">
      </cfreturn></cfset></cffunction>
    </cfcomponent>
    

    At this point you can create a mapping to this object in your coldspring.xml file that looks something like…

    <bean id="User" class="path.to.User"></bean>
    

    Which is extremely straightforward. User it what you'll request in your code, and in response it'll return the object at path.to.User. Assuming you have coldspring set in your application scope, you'd usually do something like this to return the user object.

    <cfset user='application.cs.getBean("user")'>
    

    Beauty of it at this point is that you no longer need to know the path to the CFC. The only place you'll have to refine the paths is in the Coldspring file, keeling the code itself a lot cleaner too.

    But still we haven't really done anything useful using this. What if the User object also had a Locale object used to store all settings for that user? Using the non-Coldspring way, we could change the cfc to look like this.

    <cfcomponent name="user">
      <cffunction name="init" returntyoe="any">
        <cfset variables.login_time="now()">
        <cfset variables.localeservice='createobject("component","path.to.LocaleService")'>
        <cfreturn this="this">
      </cfreturn></cfset></cfset></cffunction></cfcomponent>
    
    <cffunction name="getLocaleService" returntype="any">
        <cfreturn variables.localeservice="variables.LocaleService">
      </cfreturn></cffunction>
    
    <cffunction name="setLocaleService" returntype="void">
        <cfargument name="LocaleService" required="true" type="any">
        <cfreturn variables.localeservice="arguments.LocaleService">
      </cfreturn></cfargument></cffunction>  
    

    Note: you see how I've used getLocaleService instead of getLocale? Well that's because getLocale and setLocale are CF functions, which annoys me sometimes. If you have a better variable name for this situation, I'd love to hear it.

    That's all nice and good at this point. We have a user object, it has a locale object (which presumable must set itself to some locale, because we haven't set it). There are a lot of loose ends to this method. What if we want this LocaleService to default to some set language so the user can see some errors? Time to head back to coldspring and define it.

    <bean id="LocaleService" class="path.to.LocaleService">
      <constructor-arg name="Language"><value>en</value>
      <constructor-arg name="Country"><value>US</value>
    </constructor-arg></constructor-arg></bean>
    
    <cfset variables.localeservice='application.cs.getBean("LocaleService")'>
    

    Changing the line in the earlier User object to this cfset call, returns the same LocaleService object (at path.to.LocaleService), but also does a little magic on it. When the LocaleService object is created, it'll in effect be doing the call:

    <cfset localeservice='createobject("component","path.to.LocaleService").init(Language="en",Country="US")'>
    

    in only

    <cfset localeservice='application.cs.getBean("LocaleService")'>
    

    Note: This is what is called constructor injection.

    Taking this to the final step (which you can probably already guess) you can use Coldspring to pass that LocaleService object right into the User object. This is done by referencing it in the Users bean config

    <bean id="User" class="path.to.User"></bean>
    
    <property name="LocaleService">
      <ref bean="LocaleService"></ref>
    </property>[/XML]
    

    (Remove this…)

    <cfset variables.localeservice='createobject("component","path.to.LocaleService")'>
    

    Now what happens when you create a user is a little more complicated. It will create the LocaleService object, initialized with the language and country, then create the User object. If you take a look at the <property> tag in the XML doc, it has a name parameter. Coldspring will attempt to call a function by that name passing in whatever follows (in this case the LocaleService. In effect what's happening is User.setLocaleService(application.cs.getBean(”LocaleService”)), after it's been initialized.

    And all in a single call!

    <cfset user='application.cs.getBean("user")'>