On February 7th, I’ll be demoing an all-new version of my application at a conference. The problem? I’m starting today.
Months ago, I committed to a rewrite of my 4-year old app in Model-Glue with Transfer and ColdSpring. While my home-built framework had served me well over the prior years, adding new functionality was becoming more and more difficult and I wanted to have a solid base for the next 4 years. I rewrote the API immediately and was promptly dragged into PCI DSS compliance which sucked up 3.5 months. With the same February 7th deadline, I’m going to try and stuff 400 hours into the next 35 days to refactor the front-end of my site into Model-Glue and add some critical new features.
This is my first Model-Glue site. My API was already in extracted CFCs so converting that to Transfer/Coldspring and into a complete service layer was… tedious but relatively straightforward. There is a ton of info out there on starting from scratch but very little on converting an existing application. I’m jotting down some notes (as well as questions) here as an exercise in learning. I’m hoping this will serve like a travelogue for explorers who come after me.
Findings Today
- Model-glue.xml – Break it up! For any application of decent size, your model-glue.xml file will be unwieldy almost from square one. I split mine up into just two sub-files to get started:
<modelglue>
<config>
<setting name="reload" value="true" />
<setting name="dsn" value="mydsn" />
</config><include template="config/eventhandlers.xml" />
<include template="config/controllers.xml" />
</modelglue>
I am finding I’m working in the controllers file adding things and then I work with the eventhandlers next as I figure out the right flow from event to event. - Application variables – I use application.config.* for things like DSN which I store in an external file that is separately managed via source control. At the moment, I’m bringing those values back into my application with a SimpleConfig in the coldspring.xml file. Here’s a snippet:
<bean id="GlobalConfig" class="ModelGlue.Bean.CommonBeans.SimpleConfig">
<property name="config">
<map>
<entry key="WEB_DOMAIN"><value>MotorsportReg.com</value></entry>
<entry key="WEB_HOST"><value>localhost:8302</value></entry>
<entry key="VERSION"><value>4.0-alpha</value></entry>
<entry key="DOWNTIME">
<value>
<list>
<map>
<entry key="start"><value>2007-07-25 20:00:00</value></entry>
<entry key="end"><value>2007-07-25 21:00:00</value></entry>
<entry key="desc"><value>This is some downtime, yo!</value></entry>
</map>
<map>
<entry key="start"><value>2008-07-25 20:00:00</value></entry>
<entry key="end"><value>2008-07-25 21:00:00</value></entry>
<entry key="desc"><value>This is some downtime!</value></entry>
</map>
</list>
</value>
</entry>
</map>
</property>
</bean>
You can replicate arrays and structs with the list and map tags respectively. Then I made this bean an argument to some of my service model:
<constructor-arg name="GlobalConfig"><ref bean="GlobalConfig" /></constructor-arg>
I’ve already split off a few of the config variables into a separate EmailConfig. I’m going to break these settings out into more specifics – like a “BaseConfig”, “ThemeConfig”, and so forth to the level of granularity that eliminates overlap. - Theming – I have a session-based theme component that tracks which site the person is viewing and modifies the UI based on the URL and other conditions. I created a SessionService which is effectively a facade to the SESSION scope and then a ThemeController which talks to the SessionService for working with the theme (managed by a ThemeService). The ThemeController fetches the global config (which includes some default theme data), sets a theme in OnSessionStart and listens for a refresh key in OnRequestStart in case we need to flush the theme and start over. Then I basically stuff my theme object (generated by Transfer) and the GlobalConfig into the event object in OnRequestStart:
<cfset arguments.event.setValue("globalConfig", getThemeService().getConfig()) />
...
<cfset arguments.event.setValue("theme", getSessionService().getTheme()) />
Then my view picks up these settings:
<cfset config = viewState.getValue("globalConfig") />
And I use them to drive stuff like the page name, the CSS location and so forth:
< a href="http://#keys.config.getConfigSetting("WEB_HOST")#/index.cfm?event=logout">Logout
...
#theme.getStyleSheet()#
...
#config.getConfigSetting("WEB_DOMAIN")# - Using getValue() defaults – By passing a second parameter to the event object’s getValue() method, you can have a built-in default. This is pretty cool, although it makes working with boolean values painful. I’ve resorted to this syntax:
<cfif arguments.event.getValue("someBoolean", 0)>
...
</cfif>
The only problem with this is if someone were to modify the URL to change someBoolean from a 1 to, say, “foobar”, then it would throw an exception because foobar can’t be converted to a boolean. I hate to write a check for isBoolean() though as the point of short-circuit boolean evaluation is to make it fast to write. Adding a second check kind of defeats that… for now, I don’t know if I’m going to do anything about that. I believe another option is to use:
<cfif yesNoFormat(arguments.event.getValue("someBoolean", 0))>
And that would be slightly more resilient to “true”, “false”, “0″, “1″ and anything else failing to false.
Questions Today
- Taglibs - I’m converting from a lot of imported taglibs that give me things like <ui:datepicker> for creating a date drop down box. I’m not sure how, or if, I should convert these to MG views. I have a bunch of toSelectFoo() methods that previously created <select> boxes that I was recommended to convert to simple views. Should I do the same for these other “widgets” or keep them as taglibs that expect queries and parameters?
- Model-Glue Arguments vs. Values – arguments are set in the XML for controllers and values are set in the controllers for the views. Is that right?
- Passing defaults to a view – I have pages where I have something like a cfparam that defaults some value to true/false but based on a click, the user might change that value (and it might persist). I’m not 100% sure on how to get/set that value in the view.
- Event naming – I’m not really sure how to name my events. Today, in my directory-based system, I have basically two applications. The root directory and a /pap directory that all use the same look and feel and security and a /pmp directory that uses a different look and feel and security. I’m thinking I’m going to mirror this by naming my events pap.* and pmp.* but I might deviate to something like public.*, pap.* and pmp.* or something altogether different.
Resources Today
- Doug Boude’s Demystified Views
- Jared Rypka-Hauer’s Event naming post
- I’m checking out Validat and EventValidator for validation and ResinFox for access controls.
- Dan Wilson’s MG Tutorial Series
Comments are closed.