The onTap Framework is Ugly!

The onTap framework is ugly. Yep. You heard me, I said it. The onTap framework is ugly, and I don't mind admitting that...

As a programmer I spend a lot of time thinking about the "most elegant" way to solve a problem. Although in reality it's not really the most elegant way, it's just what I feel is the most elegant way that I can think of and that's currently available to me with my resources. It's always possible that someone else might have thought of a more elegant solution, or that there might be a more elegant solution that's simply not available to me, often due to financial constraints. The release of recent versions of ColdFusion and the addition of application-specific mappings actually resolved a number of ongoing issues I had personally with code I felt was "ugly" or "inelegant". And that's not the only time a ColdFusion upgrade has helped me to clean up something I had always struggled with. The addition of onMissingMethod made possible a long time dream for me of having a lazy-loading function library that could load utility functions on-demand. And don't think I'm being hyperbolic when I say "long time dream" -- I was trying to accomplish that with ColdFusion 5, immediately after CFSCRIPT was introduced and made custom functions possible in the first place. Yet with all the advancements to the core language, I still routinely struggle internally with this notion of "elegance".

Part of the problem is the way that people think. Scientists used to believe that humans followed a "path to action" like this: think -> do -> feel. So in this model, you would think about what you're going to do, do it and then afterward you would decide how you felt about that action. Was it good or bad? Should you do it again? This is a very logical way of handling the world, however, it turns out to be the opposite of the way we actually behave. Our actual paths to action (and this includes us programmers) looks like this: feel -> do -> think. This path is not rational, but it is very, very efficient, which is why our brains evolved this way. It's also the cause of what I've called opinion driven development (ODD). Andre Marquis explains how this works in this video here. In this model we have an emotional desire to do something like eat or play a game, we do it, and then afterward we rationalize that decision. Usually we create a "logical" explanation for our actions which is incorrect, because it assumes our actions were inspired by reason instead of our emotions. Even the belief that we behave rationally is inspired by our emotions -- it's uncomfortable to us to think that we might behave irrationally, regardless of how strong the empirical evidence is. And it's that discomfort, that very emotional discomfort, that makes it difficult for us to admit to irrational behavior. Although we can develop ways of thinking about things that allow us to entertain these ideas without that discomfort, specifically by developing a "growth mindset".

[More]

IoC and Lazy Libraries

Okay, so I'm finally going to post something actually related to the framework again. :)

Here's the deal. I've been aware for a while that folks have been unhappy with some of the features in the onTap framework, in particular the fact that there are a number of places where code is executed outside of CFCs. This is done in the framework for very specific reasons, and I know for a fact that it's actually more flexible than using CFCs (think "Fusebox lexicon" and you should start to get an idea why). I also believe (although I don't know) that it's not significantly more "dangerous" (see my articles about duck-typing and about descriptive variable names).

Still it's difficult for people (myself included) to see past their knee-jerk responses to something that looks unusual or seems like a bad practice to actually understand the problems it solves.

So in recent weeks, largely because I was already migrating the ORM components out to DataFaucet, I started making some pretty radical changes to the onTap framework. This is good news on several fronts. First if you're already using it, you don't need to worry, because the system was so well encapsulated already (believe it or not, yes, yes it was), that your code shouldn't change by more than a handful of lines. In spite of the fact that I'm taking a few weeks to make these changes, *your* changes shouldn't take more than a few minutes. Secondly the new system is adding CFCs in some critical places and in at least one place this will make the system much more tweakable. It's certainly going to improve on its already very effective encapsulation.

First, the _appsettings.cfm that has confused people for a long long time as the initial method of configuring the framework is going away. It's being replaced by a config.cfc in the application root directory that extends tap.cfc which loads the framework core. This leads to some simplifications of some previous features like application-specific mappings. Want another mapping? Simple.

<cfcomponent displayname="config" extends="tap">

<cffunction name="configure" access="private" output="false">

<cfset addMapping("datafaucet","../datafaucet") />

</cffunction>
</cfcomponent>

Is that a relative path?! In a mapping? Yes it is... and it works. the addMapping() method allows you to specify mappings either absolute or relative to the framework root directory. How does it know? If it finds a directory on the relative path, then it's relative, otherwise it's absolute. Also the addMapping() function automatically prepends the required "/" at the beginning of the mapping string, which has been the source of LOTS of confusion about how precisely to create mappings in ColdFusion 8. Which means this method of adding mappings is much simpler and more straightforward than Application.cfc.

The code for path-settings is also going to become CFCs and there will be a similar, new _mappings directory for the addition of 3rd-party mappings (which should be used sparingly, but will be an important enhancement to the framework's plugin manager).

Now on to the 2 features I mentioned in the article title. I've already added lazy-libraries. In the past the framework has had a bit of a heavy initial load because it loaded its entire set of function libraries on application start. Going all the way back to the ColdFusion 5 days there had been this notion of having a library of functions (DLL wouldn't be an entirely bad analogy), which each understood their own dependencies and could then load those dependencies when they themselves loaded.

Sweet! At least in theory. In practice however loading them always turned out to be a pain. When / where do you load them? Which ones need to load on app start? Which ones can be deferred? Have I loaded x on a given page request? Should I load it on request start? ::sigh:: leading me ultimately to make the framework load them all by default, which was ugly and had some of its own problems aside from the initial load time.

A while back I had this notion of creating a "lazy loading" library out of them, where all those functions before had been in a simple structure, they would become part of a CFC and then could be loaded dynamically as-needed using the new onMissingMethod() feature in ColdFusion 8. I had actually even started working on that change a while ago and ended up giving up and rolling the change back in SVN because I had too many problems implementing it at the time. Well now I've gone back and revisited the idea and I've managed to overcome all the challenges I found before. :)

Sweet! So now you never have to worry about whether or not a particular function is loaded because the library will load it for you when it needs it. And there's an extra bonus that the framework loads faster! I've also added internal references in the libraries themselves so that instead of calling request.tapi.OtherMethod() the internal method calls become lib.OtherMethod() -- using the internal pointer. Granted that if you're calling a method in the same library, it can just use this.OtherMethod() -- lib.OtherMethod() just allows you to traverse the library from the root (similar to the way the default / mapping in ColdFusion lets you traverse files from the web root).

The core onTap.cfc also got a new getLib() method for returning the framework's core library, so that library calls from CFCs can be "composed" rather than referencing the request scope directly.

The only down side to this change is that due to a limitation in the getCurrentTemplatePath() method caused by inconsistency in its behavior (which the CF team last I knew refused to acknowledge), the file methods had to be moved outside of the main library to a new request.fs. structure. Those functions can't be stored in a CFC -- no way, no how. There simply is no workaround that allows them to exist in a CFC. Unfortunately... I believe I've also added getFS() to the ontap.cfc to fetch the file library in the same way that the core library is "composed". This way if at some point in the future it becomes possible to move that library back into the lazy library, then getFS() can just return the getLib() pointer and the getFS() method can be deprecated. So that should be good forward-looking.

If you're using the file.cfc to manage your files, then you don't have anything to worry about with this change. You might need to do a little extra leg-work if you were calling a lot of request.tapi.fileRead() or the like -- you'll just have to change those references, which shouldn't be too difficult.

Lastly I haven't started working on it yet, but I'm planning an IOC-MANAGER for the next release. I say "manager" because although it will have a default IOC package, the system is going to be designed specifically for the purpose of managing multiple IOC packages from different applications / plugins. So where for example ColdBox has just an IOC-type (coldspring/lightwire) to integrate a single IOC configuration, the onTap framework will instead have an addIOC() method for attaching arbitrary IOC packages, and a facade for allowing you to write your own IOC-wrappers (although I plan to include one for ColdSpring and one for LightWire out of the box). Your application code can then either getIOCManager().getBean("name","DataFaucet") for example or it can getIOCManager().getIOCPackage("DataFaucet").getBean("name"). Although I would encurage not doing those all in the same function and instead compose separate getIOCManager(), getIOCPackage() and getIOCBean() functions in your own components. That will minimize the amount of code you need to write later if anything in the core application needs to change - OR - if you later decide to migrate to a different framework.

This Looks Like a Job for EventDuck!

So if you've worked with some other frameworks and you're curious about the onTap framework, it occurs to me that there's a reasonably simple way to convert your views to use the onTap framework without removing any of the references they make to the "event" object, even though the onTap framework doesn't have one and generally uses the "attributes" scope like older versions of Fusebox.

Just download the enclosure in this blog and extract it to the root of your onTap framework application (apologies to anyone who tried to download it before, I didn't realize you wouldn't be able to download a .cfc as an enclosure)

The .cfm in the archive will execute prior to your "event-handler" code (which is placed in /_local/) for each event and instantiate the duck, creating a substitute for the event object of your favorite framework.

I think this should work for views ported from ColdBox, Fusebox or Mach-II. Model-Glue might be a bit more involved with the viewState object, but the principal should be the same. The only reason this might break in any of your views is if either I didn't include an event method you use frequently in your views* or you've got a view template that calls methods on another CFC and those methods are strict-typed, expecting the "event" object to be of type "ColdBox.system.event" or the like instead of having an argument type of "any". But then in theory there shouldn't be a lot of method calls in your view templates anyway. ;) Still, it's another good reason to duck type.

* do people use param in their views? add a comment or drop me a note and I'll add any methods you use if I can simulate them

"AND" / "OR" Keyword Search

I know that verity includes a lot of advanced search features, but I honestly just haven't worked with it much. So I'm not sure if verity offers an "and/or" keyword feature, though I wouldn't be surprised. Then again verity requires you to create and maintain collections and that means double-maintenance if the data is stored in your database and you don't always need the other features verity has. So a while back I decided I wanted the ability to include and/or keywords in searches, (similar to google I suppose) and rather than copying and pasting that code every time, the framework's SQL abstraction layer gave me the ability to simplify the process of creating those and/or filters. The framework code looks something like this:

<cfparam name="attributes.search" type="string" default="" />
<cfif len(trim(atributes.search))>
<cfset search = Datasource.getStatement("select").init("tbl_Content") />
<cfset search.andOrFilter("title,keywords,contentText",attributes.search) />
<cfdump var="#search.execute()#" />
</cfif>

This code then takes whatever you've entered in the search form, breaks it apart into phrases separated by the words "and" and "or"1 and spreads those phrases across the title, keywords and contentText columns in the tbl_content table. Being able to do this with just one line of code is awfully convenient. The resultant query is something like this:

searched for "fish and chicken"

select * from tbl_Content
where (
(title like '%fish%'
or keywords like '%fish%'
or contentText like '%fish%')
and
(title like '%chicken%'
or keywords like '%chicken%'
or contentText like '%chicken%')
)

So with this sql output, you can see that the query will return any record that contains both words, but it also returns results where the words are in different portions of the content, for example the word "fish" in the title and "chicken" in the contentText, so if there's an article titled "let's go shark fishing" with the phrase "are you chicken?" in the contentText, this search will return that record. What makes this really nice however is that as the number of columns increase and/or the complexity of the search string increases, the SQL statement becomes very large and very complicated rather quickly, however, the andOrFilter facade which handles the thankless task of creating the SQL syntax never becomes any more complicated than the one simple line above2.

<cfset search.andOrFilter("title,keywords,contentText",attributes.search) />

Today I realized that the facade was limited to only accessing columns in a single table, so although it worked it was still not as flexible as I wanted it to be. I was working on a contact manager and needing to include two columns from joined tables in the and/or column list. So I updated the facade to allow that and now you can also do this:

<cfset search.join("author a") />
<cfset search.andOrFilter(
"title,keywords,contentText,a.firstname,a.lastname"
,attributes.search) />

It was a really good thing for me, because the query I was working on was massive... Check this out: 3

Aside from the fact that the sql abstraction layer escapes all the column names for me and ensures case insensitivity (even with a case-sensitive database collation), creating this query required me to write a remarkably small amount of actual code, much of which is even reused in other queries.

So anyway, I uploaded a new archive with the enhancement for multi-table and/or filters4.

  1. Actually it's a bit more sophisticated than that even because it uses localized strings for the and/or keywords for international use, so for example, german users can use the keywords "und" and "oder" in their search.
  2. And really, without a facade like this, and/or keyword searching is one of those features that just never gets implemented because, as nice as it may be for the users, it's tedious and never makes it into the list of priorities we can spend time working on.
  3. Thanks to search.getSyntax() for the sql syntax - this lets you output the syntax instead of executing the query for use in generating scripts to be executed elsewhere, as in my case it's easier to debug a query sometimes by copying and pasting the SQL syntax into an IDE provided by the database vendor like Query Analyzer. This way I don't have to sit through the tedium of copying all the query-param values, sifting through the query and replacing question-marks, because getSyntax() performs the merge for me.
  4. I also discovered that when using filterGroups (they allow you to wrap parenthesis around groups of filters like in the query above) if you misspelled the name of a table alias, it was producing an unusual error -- it's supposed to throw a custom "column not found" error, which it wasn't doing, so I fixed that also.

What's On the Menu?

The office where I'm working currently, we have a lot of this

<div>
[&nbsp;<a ...>main</a>
<cfif xyz>&nbsp;|&nbsp;<a ...>users</a></cfif>
&nbsp;|&nbsp;<a ...>documents</a>&nbsp;]
</div>

I imagine a lot of you are already familiar with this sort of thing... Probably most of you don't think much of it, but this sort of thing can be problematic. Primarily what I'm talking about here is the brackets [ ] surrounding the menu and the pipes in between the menu items. This style of display has been pretty popular at a lot of the places I've worked, but then I've also seen a lot of cases where an item in the menu isn't displayed for one reason or another and you get || in the menu, i.e.

main |  | documents ]

Instead of

main | documents ]

Or sometimes an extra pipe on either end like this

main | documents  | ]

And I know a lot of people think "so what?" ... but the point here isn't that it ever happens. No amount of experience or education prevents us from making mistakes. The most brilliant programmers still create typos and contribute code containing seemingly simple mistakes, because whatever we might want to think about our abilities, we're still human. That's why I always cringe when I hear managers berate people for "not testing your code" -- but every manager I've ever met does it. People make mistakes and sometimes that means code going to production with a bug - PERIOD. Their being human doesn't require they be made to feel like crap. If the software going to production is too error prone it's almost always because the company doesn't have a good Quality Assurance (QA) team and/or continue to demand that changes be made at break-neck speeds and refuse to set reasonable deadlines. (In my experience most companies have *NO* QA team, and demand break-neck deadlines in addition, which is a whole other rant.)

No my point about this in particular is that display bugs of this variety are pretty common when the code is formed the way you see it up above. It's the same reason why I wouldn't want a hot-key for "delete" right next to a hot-key for "save". It's about taking simple steps to make some of these common mistakes less common. This is why the framework offers the features to support this:

<cf_html>
<div tap:open="[" tap:close="]"
tap:childdelimiter="|"
style="white-space:nowrap;">

<a ...>main</a>
<cfif xyz>
<a ...>users</a>
</cfif>
<a ...>documents</a>
</div>
</cf_html>

Now this code will produce basically the same menu above, however, it will also ensure that you don't accidentally leave any pipes in or out of conditional statements to produce that display bug up above. It also has some other advantages, for example it involves fewer keystrokes and no duplication (you only enter the pipe character once). And then there are the extended advantages that you can use a combination of the framework's native XSLT features and/or the native internationalization features to easily replace the pipes and brackets for special needs or for branding for clients who would prefer a different look and feel (because we're a way's off from being able to control that text with CSS across the common browsers).

The other thing I'll point out here is that I removed all the &nbsp; character entities. This is of course not a framework issue, but I think its important for people to know. The style attribute white-space:nowrap; takes care of this for us by eliminating the need to use non-breaking spaces to prevent wrapping, which has a number of advantages, not the least of which being that you don't have to type all those entities... although ultimately I wouldn't prefer to place that in an inline style attribute, I would move it out to an external style sheet.

So long story short, the above code is cleaner and less error prone.

Enjoy!

BlogCFC was created by Raymond Camden. This blog is running version 5.5.006. | Protected by Akismet | Blog with WordPress