Viewing by month: September 2006

Sep 29 2006

Model-Glue Bloating, Mach-II Dying, Frameworks Stinking...

Ah, geez. It's been a few months since CFUnited, and everyone seems to be forgetting that us ColdFusion folks are people who get along really well when we're in person (and the bar hasn't run dry). Suddenly, frameworks suck, Model-Glue:Unity is a bloated kitchen-sink framework, and Mach-II is obtuse and maybe it "should be allowed to die a dignified death."

I guess that one of the responsibilities that I've gotten from Model-Glue is to speak up about it as well as frameworks in general when something is said that I feel is wrong.

Mom, please excuse the occaisional profanity. It's been a long day, I'm across the country from Dale and the dog, and I have to fly through O'Hare tomorrow.

So, here's my no-crap $0.02:

Framework's dont suck...

..and for some, they're not a half-bad way to learn things. For others, it's probably not a good idea. I've had some people tell them Model-Glue helped OO "click" because it got them thinking in terms of loose coupling and interfaces. It's also befuddled the hell out of some others.

Do what suits you, but recognize that others may not learn in the same way. Don't evangelize the way you learned as the way everyone else should, and recognize that others may learn in ways that you can't.

I'm one of the kids who couldn't learn to swim until I just jumped into the deep end. Some would drown. I probably never would've gotten over my fear of water, though, if I didn't just jump.

Mach-II isn't a dead duck...

...it's just different from Model-Glue (in MVC comparison). I've moved apps back and forth between the two for fun.

To be frank, if you're used to how J2EE does things, you'll probably like Mach-II more. If you're from ColdFusion or Flash, Model-Glue will probably make more sense. Either way, the differences aren't all that huge.

MVC Framework: not big deal.

Business model it connects to: big deal.

Model-Glue:Unity and "Kitchen Sinks"

Calling Model-Glue:Unity a kitchen sink framework or comparing it to an electric lawnmower with silly attachments is, well, just as silly as an electric lawnmower with a set of silly non-lawn-mowing attachments that doesn't have a beer holder. I mean, come on, if we're going to make crazy metaphors Dr. Phil would be be proud of, don't forget the beer holder.

Seriosuly, though, when Peter's post implied that I'm not giving enough considering to what goes "in" the framework, I took a bit of offense. I doubt it was intentional, and the picture in the CFUnited Flickr pool of us choking each other is a joke.

I've consistently turned down requests to add many features that would be useful to a subset of developers in solving edge-cases more elegantly but not to the Model-Glue community at large. I've had people get stinking mad at me over it, but it's something I've had to repeatedly stand up for. It's not easy to tell people that some code they've written is great for solving their problem but that it just doesn't belong in the framework. I've chosen to have friends get stinking mad at me rather than go against my judgement. It tweaks me that anyone thinks I've taken it likely.

The decision to provide scaffolding and ORM capabilities in Model-Glue:Unity was carefully considered. In the end, the pros simply outweighed the cons. I wrote and tossed three ways of doing it before I found a way that let me do three things:

  1. Remove the tedium from creating basic data layers and master / detail lists
  2. Make sure the generated data layer can be extended and refactored while allowing nondestructive regeneration.
  3. Allow anyone who's used scaffolding/ORM to completely toss the Model-Glue based HTML interface without losing any data or business logic
Even more carefully planned was the architecture behind adding the capabilities, and it's important to understand that scaffolding and ORM were added in a way that introduces no dependencies unless you wish to use them. Which leads us to:

Model-Glue extension vs. Mach-II extension

Peter says that the direction Mach-II is going is "extension through architecture instead of providing everything but the kitchen sink."

It probably wasn't intentional, but this underestimates me as a software designer, and it expresses ignorance of Model-Glue:Unity. The framework does, in fact, provide "extension through architecture."

It's simply doing it better than Mach-II.

Mach-II extension uses a plug-in architecture, requiring developers to use its framework-specific plug-in API.

Model-Glue:Unity has a rewritten core, dependent on an Inversion of Control container implementing the ColdSpring BeanFactory interface as an architectural paradigm for extension.

In other words, Mach-II has a framework-specific extension architecture you've got to learn, while Model-Glue has an extension architecture (ColdSpring) that doesn't even know Model-Glue exists, is an already widely used tool in OO ColdFusion, and is re-usable to provide the extension's service outside of Model-Glue.

In a market where we're going to need to provide the same services to HTML, Flex, and other clients, which do you think is advantageous?

Model-Glue:Unity and ColdSpring Dependencies

The framework creates no ColdSpring dependencies within your application. To claim otherwise is a statement of ignorance concerning ColdSpring: anyone who actually knows ColdSpring knows that no ColdSpring application is, in fact, dependent on ColdSpring. It's a beautifully clever thing when grokked.

If you'd like to get down to brass tacks about the lack of dependency, you could implement your own IoC implementation, meet a few interface requirements, toss ColdSpring out the window, and still run Unity.

In the real world, though, the extent of the ColdSpring requirement is that its files must be on your hard drive. Your applications don't need to know it's even there. Hell, no one on your team but the one who downloads Model-Glue needs to know it exists.

Big dependency, that ColdSpring.

Model-Glue:Unity and Reactor Dependencies

You don't have to even to download Reactor run Unity. It's not needed at all.

If you'd like to do scaffolding and automagic database functions, yes, you'll probably need Reactor.

I say "probably" because all of the Model-Glue code that knows anything about Reactor is isolated into a few specific places, defined via interfaces, and it's "plugged in" via that whole "extension through architecture" bit. It took Sean all of a few hours to re-implement and plug in the two components necessary to get Transfer mostly working (I'm not sure he's gotten around to scaffolding/committing hasMany relationships, but it shouldn't be too rough).

Big dependency, that Reactor.

Your application and its dependency on Model-Glue:Unity

If you create an HTML-based ColdFusion application, and you use a MVC framework, are you now dependent on the framework for that application?

Of course not: the point of the MVC frameworks is that just the HTML-based "client" for the Model/application is dependent on the framework.

Unity doesn't change that at all. You could use scaffolding to create a Widget editor, delete everything but the Model directory, and then use the same data tier in Mach-II.

Create a service wrapping the functionality, expose it as remote, and you've got a Flex application.

Big dependency, that Model-Glue.

13 comments - Posted by Joe Rinehart at 12:54 AM - Categories: Model-Glue | Reactor | Causing Trouble

Sep 20 2006

Video: Ajax Scaffolds in Model-Glue

As development on Model-Glue:Unity calms down and I enter testing/documentation, I'm spending less time working on the framework and more time testing the "extension" features the new architecture provides.

As I travelled over the past week, I thought it'd be fun to write my own custom set of scaffolds that used simple "div replacement" Ajax to avoid unnecessary page reloads. The bits behind it aren't public yet (should be available in a test form late next week), but I wanted to show a bit of the results.

I'm pretty pleased: change the a config setting, state that you want the new Ajax-based events (commit and delete) to be default scaffolds, include some JS, and away you go.

Here's a video of the customization in action.

6 comments - Posted by Joe Rinehart at 4:30 PM - Categories: Model-Glue

Sep 13 2006

The Seven P's

I'm travelling (again) this week, so I'll be doing some Model-Glue work in the evenings. It's currently 5:55AM and I'm sitting in Raleigh-Durham Int'l, waiting for a 7:15AM flight.

Why am I so early?

It's because I follow a simple motto for a lot of things I do, something I picked up from a book entitled Bravo Two Zero, which documents the experience of a British SAS sergeant in the first Gulf War. The SAS (and, I imagine, loads of other military organizations) follow a principle called "The Seven P's":

Proper Planning and Preparation Prevent Piss Poor Performance

So, why am I so early?

First, the known: I live in a brand-new suburb that doesn't show up on maps, and I rely on a taxi to get here. I go through the same company, but drivers vary.

The unknown: as I just found out, implementations of the new TSA "no liquid" rules may vary. After I demonstrated that my glasses are, in fact, prescription and explained that I spend 8-10 hours a day staring at a computer, and I demonstrated use, they let me keep my ever-present eyedrops. That was a bit of a delay.

WTF does this have to do with software?

Have you ever dived in to a simple project, coding up a storm, just to later find out that one or two overlooked details are showstoppers? I have to plead guilty - with tools like MG:U and RoR, it's sometimes so much fun to code that I don't want to stop, open up Word, and document what I'm about to do.

However, it's also not good to go overboard with planning: an overblown discovery and planning phase can turn the requirements for a "simple four-week addon to our existing product" into a sixteen-volume tome that does nothing but firmly anchor a bookshelf to a forgotten corner of a product manager's office.

So how can we, as software developers, effectively apply the 7P's?

Recently, I've found that the most effective tools are Use Case documentation/diagrams, paper prototyping (you know, pencil + paper = GUI prototype), and Unit Testing. (TDD is my new favorite thing.)

Coming back to the airport, though, I'm really here so @#$23ing early because the cab company I use follows the 7P's as well. If you live in Raleigh/Durham, and need to get to the airport, I really recommend Rite Choice Transportation - the first time I used them, the driver gave himself an additional hour just to find my house, ended up needing and, and still picked me up exactly on time. I tipped him well, and all I have to do now is say my development and street name and he knows who it is and where I live.

3 comments - Posted by Joe Rinehart at 11:11 PM - Categories: Best Practices

Sep 8 2006

Dynamic sorting with modelglue.GenericList

Yesterday on the Model-Glue list, someone asked if it's possible to dynamically sort the resultset returned by the GenericList functionality in the Model-Glue:Unity beta. Yes, it is, but you have to write a wee bit of code to do it.

A bit of background:

The "GenericList" is what's known as a Generic Database Message. These are pre-defined messages within Unity that allow you to List, Edit, Commit, and Delete instances without writing a dedicated controller or listener function. It's possible to "configure" the behavior of each using message arguments.

For example, the default use of GenericList to list records from the WidgetType table looks like this:

<message name="modelglue.GenericList">
<argument name="object" value="WidgetType" />
</message>

That'll create a query named "WidgetTypeQuery" in the viewstate listing all records from the WidgetType table.

If you wanted to only list records where WidgetType.UserID was equal to the UserID in the viewstate (say, URL.UserID) and you wanted to list them in order of WidgetType.Name, and you wanted the resulting query to be named "UserWidgetTypeQuery," you'd use the following:

<message name="modelglue.GenericList">
<argument name="object" value="WidgetType" />
<argument name="criteria" value="UserID" />
<argument name="orderColumn" value="Name" />
<argument name="queryName" value="UserWidgetTypeQuery" />
</message>

Ok, that's fine, but it's still pretty static. What if the query needed to do two subqueries, a join, and some aggregate functions?

Well, at that point, you're beyond the scope of what Reactor will automatically perform. You'd open up your WidgetTypeGateway.cfc, write some custom SQL in a method named something like "listWithComplexStuff", and use the "GatewayMethod" argument of GenericList to instruct it to use that method instead of the generic getByQuery() functionality that Reactor provides. It'd look like this:

<message name="modelglue.GenericList">
<argument name="object" value="WidgetType" />
<argument name="criteria" value="UserID" />
<argument name="orderColumn" value="Name" />
<argument name="queryName" value="UserWidgetTypeQuery" />
<argument name="gatewayMethod" value="listWithComplexStuff" />
</message>

That's OK, but it's now going to ignore the "orderColumn" argument because you're using your own custom SQL.

What's worth noting, though, is that any value listed in the criteria argument is passed to your custom method as a named argument. In other words, you'll have an argument named "arguments.UserID" available withing listWithComplexStuff.

Knowing this, we can revisit our original problem: how do we sort a GenericList based on something like a URL variable?

Now that we know about how criteria work with custom gateway methods, the answer's pretty simple: write a custom gateway method that has arguments like "orderBy" and "ascending" and uses Reactor's sort mechanism to perform the sorting.

If you want to cheat, here's the code (it should work in any Gateway, like widgetTypeGateway.cfc):

<cffunction name="listSorted" returntype="query" output="false">
   <cfargument name="orderBy" type="string" required="true" />
   <cfargument name="ascending" type="boolean" required="false" default="true" />

   <cfset var query = createQuery() />
   <cfset var order = query.getOrder() />
   <cfset var where = query.getWhere() />
   <cfset var field = "" />

   <cftry>   
      <cfloop collection="#arguments#" item="field">
            <cfset where.isEqual(_getAlias(), field, arguments[field]) />
      </cfloop>
      <cfcatch type="reactor.getField.FieldDoesNotExist"></cfcatch>
   </cftry>

   <cfif arguments.ascending>
      <cfset order.setAsc(_getAlias(), arguments.orderBy) />
   <cfelse>
      <cfset order.setDesc(_getAlias(), arguments.orderBy) />
   </cfif>
   
   <cfreturn getByQuery(query) />
</cffunction>

Note that this'll not just allow you to enter url variables like "orderBy" and "ascending," but also pass along additional criteria. With it, we could use the following message broadcast XML:

<message name="ModelGlue.genericList">
   <argument name="criteria" value="orderBy,ascending,userId" />
   <argument name="object" value="widgetType" />
   <argument name="queryName" value="widgetTypeQuery" />
   <argument name="gatewayMethod" value="listSorted" />
</message>

Now, hitting a URL like http://localhost/widget/?event=widgetType.list&orderBy=name&ascending=false&userId=1 will give us a list of WidgetType records where WidgetType.UserID = 1 sorted by WidgetType.Name in a descending manner.

6 comments - Posted by Joe Rinehart at 8:07 AM - Categories: Model-Glue