The Model-Glue Framework has a really nice feature called ConfigBeans, powered by a little tool I wrote called ChiliBeans. By using ConfigBeans instead of inserting configuration values, like Datasource names, into your ModelGlue.xml file, you can seperate your configuration from your application. This post gives a quick overview of creating a new ConfigBean and using it in your application. Once you've seen how to create one from scratch, you should have no problem using the default ConfigBeans provided with the framework.
Note: I'm typing this in between running some huge queries, so it may be a bit choppy in flow.
Download the example code for this tutorial at http://clearsoftware.net/configbeantutorial.zip.
ConfigBeans: Why bother?
Why use a ConfigBean instead of putting a new tag into your ModelGlue.xml file? Here's a few reasons:
- ConfigBeans keep your application code (which includes ModelGlue.xml) seperate from yor configuration
- Configuration properties managed through a ConfigBean can be complex values (arrays or structs), so that you can keep your configuration in a much neater package
- It's a lot easier to instantiate a ConfigBean and pass it along to your viewstate than it is to pass a huge number of individual strings. For example, you can pass a bean for a datasource, rather than a DatasourceName, DatasourcePassword, and DatasourceUsername settings
- It's a good design principle to favor passing objects (interfaces) over simple values (implementations).
Example: Basic internationalization
In our example, we'll create a ConfigBean that manages basic internationalization of time and date formats. It'll manage two settings:
- 1. Timeformat will define a TimeFormat() mask that we'll use
- 2. Dateformat will be a struct, with Dateformat.long looking something like "January 1, 2005," and Dateformat.short looking something like "1/1/2005."
Then, we'll show how to use our bean to customize how users see dates and times.
Step 1: Create a "bean"-style CFC to hold these settings
First, we make our "bean" itself. It's a basic little CFC that has an Init() method, and Getter/Setter methods for each of our configuration properties. Naming is important here: when we get to the bean XML, it's going to look for methods named Get[NAME] and Set[NAME]!.
The directory that you create the bean in is important - it must be one of the mappings listed in your ModelGlue.xml's beanMappings setting. In the code that accompanies this tutorial, you'll find this bean at /config/beans/DateTimeFormatBean.cfc .
Without further ado, here's the bean:
<cfcomponent output="false">
<cffunction name="Init" output="false">
<cfset variables.timeFormat = "" />
<cfset variables.dateFormat = structNew() />
<cfset variables.dateFormat.short = "" />
<cfset variables.dateFormat.long = "" />
<cfreturn this />
</cffunction>
<cffunction name="SetTimeFormat" output="false">
<cfargument name="value" type="string" required="true" />
<cfset variables.timeFormat = arguments.value />
</cffunction>
<cffunction name="GetTimeFormat" output="false">
<cfreturn variables.timeFormat />
</cffunction>
<cffunction name="SetDateFormat" output="false">
<cfargument name="value" type="struct" required="true" />
<cfset variables.dateFormat = arguments.value />
</cffunction>
<cffunction name="GetDateFormat" output="false">
<cfreturn variables.dateFormat />
</cffunction>
</cfcomponent>
Pretty simple little nugget of information, no?
Step 2: Create an XML file containing your configuration properties
Ok, now we need a place to actually store your application-specific configuration information. We store this in an XML file that maps to the CFC we just created - we'll put it in /config/beans/DateTimeFormat.xml. Then, in your Model-Glue application, when you ask for a bean from this XML file, you receive an instance of the CFC whose properties are populated with the information in your XML file. It may sounds confusing, but it's really pretty simple once you see it in action.
Here's our XML: it shows how to define properties that are simple (timeformat) and struct (dateFormat). (Aside: If you look in /ModelGlue/Bean/CommonBeans/ExampleBean.xml, you'll see a full example that shows how to create all the complex data types).
<bean class="ConfigBeanTutorial.config.beans.DateTimeFormatBean" singleton="true">
<!-- Time Format -->
<property name="TimeFormat">
<value>H:MM TT</value>
</property>
<!-- Date Format -->
<property name="DateFormat">
<struct>
<element key="Long">
<value>mmm d, yyyy</value>
</element>
<element key="Short">
<value>m/d/yy</value>
</element>
</struct>
</property>
</bean>
Let's look at that line by line:
<bean class="ConfigBeanTutorial.config.beans.DateTimeFormatBean" singleton="true">
This line states that when you ask ModelGlue for a bean from "DateTimeFormat.xml", you'll receive an instance of the CFC at ConfigBeanTutorial.config.beans.DateTimeFormatBean. "Singleton" means that Model-Glue will save this instance in the application scope, and any further requests for the DateTimeFormat.xml bean will receive a reference to the same instance.
<!-- Time Format -->
<property name="TimeFormat">
<value>H:MM TT</value>
</property>
This defines the value of the TimeFormat property. When ChiliBeans hits this block, it'll look for a function in DateTimeFormatBean.cfc called "TimeFormat", and try to set it to the value in the blocks.
<!-- Date Format -->
<property name="DateFormat">
<struct>
<element key="Long">
<value>mmm d, yyyy</value>
</element>
<element key="Short">
<value>m/d/yy</value>
</element>
</struct>
</property>
This portion is much like the Timeformat block, but as you can see, we have a fairly easy to remember XML format for defining a structure and its members. This will set the DateFormat property.
Step 3: Use your bean!
Now, in our application, we need to actually ask ModelGlue for the bean represented by DateTimeFormat.xml. To do that, in our controller, we first use the GetModelGlue() function to ask for a reference to the framework (you can do a lot with this, like adding new controllers at runtime, but some of it is dangerous!). Next, you use the GetConfigBean() method, passing it a filename to read. In the sample application, we do this in our controller's constructor, and place the bean into the variables scope. That effective "caches" the bean at the applicaiton level, and makes it available to any other methods that need it. It looks like this:
<cffunction name="Init" access="Public" returnType="Controller" output="false">
<cfargument name="ModelGlue">
<cfset super.Init(arguments.ModelGlue) />
<cfset variables.dateTimeFormat = GetModelGlue().GetConfigBean("DateTimeFormat.xml") />
<cfreturn this />
</cffunction>
Next, we need to actually use the settings from the bean. In our OnRequestStart handler, we'll place formatted versions of the current datetime into the data bus. Remember, variables.dateTimeFormat now contains our ConfigBean, and we can use its getTimeFormat() and getDateFormat() methods to get our formatting masks.
<cffunction name="OnRequestStart" access="Public" returnType="ModelGlue.Core.Event" output="false" hint="I am an event handler.">
<cfargument name="event" type="ModelGlue.Core.Event" required="true">
<cfset var currentTime = timeFormat(now(), variables.dateTimeFormat.getTimeFormat()) />
<cfset var currentDateLong = dateFormat(now(), variables.dateTimeFormat.getDateFormat().long) />
<cfset var currentDateShort = dateFormat(now(), variables.dateTimeFormat.getDateFormat().short) />
<cfset arguments.event.setValue("currentTime", currentTime) />
<cfset arguments.event.setValue("currentDateLong", currentDateLong) />
<cfset arguments.event.setValue("currentDateShort", currentDateShort) />
<cfreturn arguments.event />
</cffunction>
Finally, we add a little code to our view to show the values:
<cfoutput>
Time: #viewState.getValue("currentTime")#<br />
Long Date: #viewState.getValue("currentDateLong")#<br />
Short Date: #viewState.getValue("currentDateShort")#<br />
</cfoutput>
Conclusion
Ok, not too shabby! We've created a ConfigBean from scratch, and shown what we could do with it. We've completely separated the configuration from our application.
What next?
Imagine you have a bunch of DAOs. In your configuration, they just need a datasource name passed to their constructor. However, a client's implementation requires a datasource name, username, and password. Oops. Time to edit some code and add more arguments...
Wait! That's a great place to use ConfigBeans. Instead of passing a datasource to the constructor, use the ModelGlue.Bean.CommonBeans.Datasource bean (or your own implementation). That way, you can pass whatever you need, shrinking or growing what the bean contains, but not having to change the interface to your existing components.
5 comments - Posted by Joe Rinehart at 10:54 AM - Categories: ColdFusion MX | Model-Glue