Scripted Noob: Queries (and Issues)

OK, I'm not a noob. Not even with cfscript. In fact, I love cfscript, and prefer to script as much as I can. ColdFusion 9 created a much greater degree of parity between cftags and cfscript. Unfortunately, the events of the past year and a half have left me with few opportunities to work on ColdFusion 9, so I'm playing catch-up on some of these great new enhancements. I did script the application.cfc back in October of 2009, but aside from that I was buried in writing a book, new job responsibilities, and more. I'm working to write all future ColdFusion examples in as much script as possible, but I still hit the occassional hurdle and ask for help.

So, while writing my examples for my last post, I kept hitting a snag while scripting a query. After banging my head on the wall for a while, I finally pinged the ColdFusionJedi himself for assistance. Ray probably though I was off my nutter, having never scripted a query, but we did run into something worth talking about.

First, I've gotten into a habit of scoping querynames. Why? If you didn't (in ColdFusion 8 or earlier) they were part of the VARIABLES scope. This can give you unintentional results, if you aren't careful, so I'd gotten into scoping querynames.

view plain print about
1<cffunction name="getSiteId" output="false" access="public" returntype="struct">
2     <cfargument name="cgiScope" required="true" type="struct" />
3 <cfset var LOCAL = StructNew() />
4 <cfset LOCAL.retVal = StructNew() />
5 <cfset LOCAL.retVal['success'] = true />
6 <cftry>
7     <cfquery name="LOCAL.qSiteId" datasource="#VARIABLES.instance.dsn#">
8     SELECT    siteId
9 FROM    sites
10 WHERE    urlAddress = <cfqueryparam cfsqltype="cf_sql_varchar" value="#ARGUMENTS.cgiScope.server_name#" />
11 </cfquery>
12 <cfif LOCAL.qSiteId.recordCount>
13     <cfset LOCAL.retVal['result'] = LOCAL.qSiteId />
14 <cfelse>
15     <cfthrow type="My_Custom" errorcode="001" message="No siteId was found for this domain." />
16 </cfif>
17 <cfcatch type="any">
18     <cfset LOCAL.retVal['success'] = false />
19 <cfset LOCAL.retVal['message'] = CFCATCH.message />
20 <!--- Any other error handling --->
21 </cfcatch>
22 </cftry>
23 <cfreturn LOCAL.retVal />
24 </cffunction>

We'll come back to that in just a minute. I also like to pass argumentCollections into functions. Maybe it's just me, but it's something I do. So, I created a collection to pass into the constructor of a new Query.

view plain print about
1// THIS DIDN'T WORK!!!
2
3 /**
4 * FUNCTION getSiteIdByUrl
5 * @access public
6 * @returnType struct
7 * @output false
8 */

9 function getSiteIdByUrl(required struct cgiScope) {
10 LOCAL.retVal = {};
11 LOCAL.retVal['success'] = true;
12 LOCAL.qPrms = {};
13 LOCAL.qPrms.name = "LOCAL.qSiteId";
14 LOCAL.qPrms.datasource = VARIABLES.instance.dsn;
15 LOCAL.qPrms.sql = "SELECT siteId
16 FROM sites
17 WHERE UrlAddress = :urlAddress";
18 LOCAL.q = new Query(argumentCollection = LOCAL.qPrms);
19 LOCAL.q.addParam(name = "urlAddress", value = ARGUMENTS.cgiScope.urlAddress,
20 cfsqltype = "cf_sql_varchar");
21         try {
22     LOCAL.retVal['queryResult'] = LOCAL.q.execute();
23 if (!LOCAL.retVal.queryResult.recordCount) {
24     throw (type = "My_Custom",errorcode = "001",message = "No siteId was found for this domain.");
25 }
26 } catch (any excpt) {
27     LOCAL.retVal['success'] = false;
28 LOCAL.retVal['message'] = excpt.message;
29 // other error handling here
30 }
31 return LOCAL.retVal;
32 }

Alright, it seems to look OK, right? So why is it erroring? Well, Ray first told me to break it down some. Make it simple, remove the param, stuff like that. No dice. Then he said use getResult() after the execute statement. Uh huh. Then I decided to take the LOCAL scope out (it's local anyway, right?) Still no dice. Finally Ray said "Don't use argumentCollection." Use the set methods instead. Bam! It worked! I thanked Ray for the assist, and went back to recreating the full function.

Whoops! Not working again. Could not find retVal.qSiteId in LOCAL (or something like that). Now what? But then I saw I had put the LOCAL scope back on the query name. Took it off, and it worked like a charm.

view plain print about
1//    THIS WORKS GREAT!
2
3 /**
4 * FUNCTION getSiteIdByUrl
5 * @access public
6 * @returnType struct
7 * @output false
8 */

9 function getSiteIdByUrl(required struct cgiScope) {
10 var retVal = {};
11 retVal['success'] = true;
12 var sql = "";
13 var q = new Query();
14 q.setName("qSiteId");
15 q.setDatasource(VARIABLES.instance.dsn);
16 sql = "SELECT siteId
17 FROM sites
18 WHERE UrlAddress = :urlAddress";
19 q.setSQL(sql);
20 q.addParam(name = "urlAddress", value = ARGUMENTS.cgiScope.urlAddress,
21 cfsqltype = "cf_sql_varchar");
22         try {
23     retVal['queryResult'] = q.execute().getResult();
24 if (!retVal.queryResult.recordCount) {
25     throw(type = "My_Custom",errorcode = "001",message = "No siteId was found for this domain.");
26 }
27 } catch (any excpt) {
28     retVal['success'] = false;
29 retVal['message'] = excpt.message;
30 // other error handling here
31 }
32 return retVal;
33 }

Now, I should go back and test argumentCollection without the LOCAL scope on the query name, but for now I'm just happy that it works. I really do love scripting, and see where this really brings ColdFusion (once again) to another level as a language choice.

MSOC Part 3: Setting Up Your Applications

In part two we setup our web server to handle trafficing multiple domains to our single codebase, but now we have to get our applications setup. This is where we have to start thinking about separation. Since each domain is driven off the same set of physical files, we now have to consider how we separate one domain's sessions/actions/resources from another. You don't want one user's session to be cross domain in any way, and it wouldn't be good to have the media assets of one site displaying on another. Where do we begin? With the code.

[More]

New Custom Tag for Google Maps: CFGMap

Two years back I had to write a new mapping implementation for my former employer, who wanted to move away from MapQuest. We chose Google Maps, for a number of reasons. I wrote the implementation using Scott Mebberson's implementation, the Google Maps Tag. I was quick and easy, and ultimately we had to ditch it at the last minute. Why? Google's licensing at the time was too restrictive for our use case. Running almost 2,000 sites of of one codebase, we would have had to get a separate license key for each site, or get an enterprise license through Google. We were going down that path originally, but the cost at the time was almost $40k, and required some work on their part that they couldn't make our deadline (during the holidays), so when the autorenewal kicked in on (cheaper) MapQuest we just rolled with it.

Last month my former employer once again wanted to get rid of MapQuest. First we looked at our implementation, realizing that the same hurdles were in place. Next we looked at ColdFusion 9's new cfmap tags. That implementation works the same way, requiring the API key per domain. Luckily, I remembered seeing a tweet from someone about changes to Google's Maps API. A change that wouldn't require an API key anymore. So, I went to check it out.

The latest version of the Google Maps Javascript API is very nice, and has one very significant change.

The JavaScript Maps API V3 is a free service, available for any web site that is free to consumers.

This was perfect, as all of our sites were free to consumers. The first thing I did was try to make some adjustments to my initial implementation from Scott's tags. This didn't work, as there were some major differences in Google's new implementation. I ended up rewriting the entire implementation to work with the new API, creating my own custom tag. In forking Scott's code, I had to keep the license the same, which allows me to put it back out to the community at large (and with the approval of my old boss).

CFGMap is now available on RIAForge for download. My simple example source code is included with the download, and all code is heavily documented. My example uses JQuery for the basic DOM manipulation involved, but JQuery is not required to use the tag itself. You'll want to pay special attention to the testmap.js file, which shows how you can access your map object to plot directions and stuff. The tag puts a map on the page, and plots the points you've fed to it. It will even trigger a callback method, that you define, for passing lat/lng info that's been goelocated back to your database, reducing the geolocation hits on subsequent map visits.

It's only the first go-around with the updated library, and I'm sure that changes will need to be made at some point. I welcome any and all feedback, questions, and suggestions.

MSOC Part 2 - Web Server Configuration

When you're writing an application to run on multiple domains, but using a single copy of code, the first thing you have to work out is your Web Server Configuration. Basically there will be a DNS entry for each domain pointed to your server's IP Address. Then your web server will have to be configured to handle those domains. Rarely, in an MSOC environment, will you need a separate site entry for each site you're serving, requiring one entry to serve all sites. If you're working with IIS on Windows you could just use the Default Web Site entry in your IIS manager. Apache is a little different.

[More]

Many Sites, One Codebase

The ultimate of reusable code: using one codebase to serve many sites. Not copies of that codebase mind you, but one actual set of code templates. Update a template to fix a bug or add a feature and...? Presto! You've just updated X number of sites at once. Genius! Or is it?

[More]

Out With the Old, In With the New: Welcome 2011

2010 brought a lot of changes to the Blades household, the biggest of which being a promotion into management at my job. I took over managing both the Systems and the Development Team, at a time when there wasn't much team left. This began a long, arduous task of recruiting and hiring, and by the end of the year I had hired three ColdFusion developers, one User Interface/User Experience specialist, one SQL developer, one desktop administrator, and one systems administrator. Along with this we took on the completion of four major projects with very short deadlines. These had begun prior to me taking the new position, and were out the door fairly close to schedule, but with the fun that accompanies projects that are poorly planned ahead of time. One of them had a necessary last minute technology change on client-side rendering, which happened very quickly but dragged our testing and deployment schedule immensely. I even pushed through a switch on our mapping application at the last minute, to use Google's JavaScript v3 Maps API, which I actually got to write myself (you don't write much code in management).

2010 also brought my new book, Learning Ext JS 3.2. Due to some scheduling issues with one of the other authors, the book was released way behind schedule, finally arriving just weeks prior to the Sencha Conference. ExtLLC became Sencha, who released their new Sencha Touch framework for mobile development. During the Sencha Conference they also showed some sneaks of the upcoming Ext JS 4; a rewrite of most of the existing framework making it leaner, meaner, faster, better. Hey look! New book material ;)

Adobe had some killer announcements as well. After Apple had banned new apps to the AppStore from cross compilers, Adobe turned around and focused heavily on the Android platform, releasing some very nice tool enhancements for creating mobile content, as well as getting the Flash Player into the Android Marketplace. Later in the year, Apple reversed their cross-compiler position, and Adobe immediately began to work again on tools for creating mobile content on the iOS platform. Unfortunately for me, I was so heavily entrenched at the office that I didn't get to spend near enough time playing with these new developments (Adobe or Sencha). That's about to change though...

In my new role, as a manager, I had hoped to have the ability to drive technical change in a positive direction. Unfortunately, events that long preceded my taking the position worked hard to prevent that momentum, and many of the decisions were no longer mine to make. This, coupled with the drain on my time, my family, and my ability to do the things that I enjoy, were taking a heavy toll on my personal well being. Time had finally come for a change.

December was a very interesting month. First I recieved an offer for a new opportunity, once again doing full time development, but this time as a telecommuter with flex schedule. I wasn't looking at the time, but it kind of came to me, and appeared a perfect fit. I resigned my management position, with the customary two week notice. This was bittersweet, as I had devoted a lot of time and effort to the company over the past five years, but this new opp was too good to pass up, and works with a vertical that's very near and dear to my heart. On a Friday I said goodbye, and the following Monday went to work with the new gig for a week before taking 10 days of pre-planned family fun for my daughter's birthday and the Christmas holiday.

Now that my time is settling back into some semblence of a groove, I once again have some time to re-devote to the different development communities I enjoy being a part of. Jay Garcia recently asked me to convert his book's Ext JS examples over to ColdFusion for some of his readers. Ext JS 4 is due for public beta very soon. And Adobe has many things on tap for this coming year, for which I am on pins and needles. 2011 will be a new start, and is already shaping up to be a great year.

Rookie Mistakes: cfparam

All of us make rookie mistakes. I've always said it's best to learn from the successes and failures of others. One of my guys came to me earlier this week with one I had made myself a few years back. Something so simple, yet easily overlooked. Now, this guy has been writing ColdFusion for years, yet breezed right over it, assuming things acted one way when they acted completely another.

view plain print about
1<cfparam name="REQUEST.base" default="" type="string" />

So, fairly simple, right? My guy expected REQUEST.base to be an empty string. But it wasn't. Why? Well, in an earlier part of the process was another cfparam tag:

view plain print about
1<cfparam name="REQUEST.base" default="#CGI.query_string#" type="string" />

The code in the first block was in a template that, in this case, was nested within another template (via cfinclude) that contained the second code block in it's head. So CGI.query_string was already assigned to REQUEST.base. What one has to remember is that, with cfparam, the default is only applied to the variable if the variable does not already exist. If the variable already exists, then the cfparam tag has no effect. Basically this is just like writing something like this:

view plain print about
1<cfparam name="REQUEST.base" default="" type="string" />
2<!--- The same as --->
3<cfif not StructKeyExists(REQUEST,"base")>
4    <cfset REQUEST.base = "" />
5</cfif>

Personally, I prefer the cfparam ;) (less code) Now, my guy was expecting an empty string, and was baffled as to why some other code wasn't processing properly. Once we sat down to dig in further, he saw that the value was not an empty string, and went about trying to figure out why it had a value. His confusion and frustration turned into anger over such a rookie hit, but everybody does it at some point.

New Book: Learning Ext JS 3.2

I've been pretty busy this year, starting with my new position at work. And, having worked on major side projects the last three years running, I also took my after work time to spend some overdue quality time with my family. But, I did make time to work with Shea, Colin, and new author Nigel White, to work on the second edition of our Ext JS book, now titled Learning Ext JS 3.2. Released last Monday by Packt Publishing, our latest book brings Ext JS developers up to date in working with the 3.x framework, updating the content to cover many changes to the library as well as introducing several new chapters on key bits about Menus and Buttons, Plugins, Charting, and Ext.Direct.

Sencha (formerly Ext LLC) released Ext JS 3.3 on the same day that Learning Ext JS 3.2 shipped from Packt. There are several new and exciting features added in 3.3, but the core content of the book still aligns with the core of the framework itself, giving developers the tools and information they need to get off the ground running. There were several important changes to the framework between the last book (finalized just before the release of 2.2) and this one, and it was important to get that information out to those ready to learn. In the new chapter about Ext.Direct, I dissect the ColdFusion Server-side Stack, written by Sencha's Aaron Conran, to give the bare bones info needed for writing one's own server-side data marshalling services, going through the pieces step-by-step. Changes to the Data package were just one of the reasons to write this book. I know that Colin, Nigel, Shea, and myself, hope that everyone enjoys our latest work.

Introducing Sencha

Great things are coming. Great things are here!

On June 14th, Ext JS LLC rebranded as part of their announced partnership with the principles of the JQTouch and Raphael projects, creating Sencha. The Ext JS library is still one of their major offerings, but they have also created Sencha Labs as a repository of various Open Source Projects under the MIT License (Like JQTouch, Raphael, and Ext Core). Great things were on the way!

Having David Kaneda (JQTouch) and Dmitry Baranovskiy (Raphael) join forces with the Ext JS crew is huge, and really plays well in understanding a series of recent blog posts around HTML5, CSS3, and what HTML5 means to developers today. But, it gets better.

This morning, Sencha launched their first joint product in public beta, Sencha Touch. Sencha Touch is a cross-platform mobile application framework built to leverage HTML5, CSS3, and JavaScript. It gives you the same sort of consistent API that you've come to expect from the Ext JS team, with a familiar syntax, great documentation, user forums for support, and many samples included with the download to help you learn. I've had the opportunity to preview this code for a while, and it is outstanding work. There will be some interesting apps to come out of this.

The future looks bright for Sencha, and I can't wait to see what they do next. Judging from their post on the rebranding, my prediction are changes to ExtDesigner (possibly to become SenchaDesigner), that would allow a developer to build both Ext JS and Sencha Touch interfaces from the same tool. My guess. (Man, that would be really cool.)

My CF + ExtJs Preso for cf.Objective() 2010

ColdFusion + ExtJsAttached to this is my slide deck and sample code from my ColdFusion + ExtJs presentation here at cf.Objective() 2010. Overall it seemed to go really well, despite the typical technical difficulties, and though Ray said I needed to be a little more introductory (Thanks Ray. I appreciate the feedback.) I heavily commented the JavaScript in my source code, so hopefully that will help to fill in the gaps for people. If anyone has any questions, feel free to use the contact link at the bottom of the page.

I want to shout out to Aaron Conran of ExtJs, for providing me with a license for their new ExtDesigner to giveaway in my presentation. I pinged him last minute on this, and he really came through (Hope you like it Lance. Drop me your info to give back to Aaron.) For those who haven't checked it out yet, it's a fantastic tool, really well done, and more than worth the small price tag on it.

On a side note, I'm using a "work-in-progress" version of CFQueryReader in this sample. I'm in the process of refactoring to support some advanced features of Ext.Direct, and the new version will only be compatible with 3.2 and above. When I put it into SVN I'll add some notes on which revision is the cutoff for previous versions of ExtJs.

Update: I've added notes to the readme.txt file of the sample download with instructions on how to make the examples work in ColdFusion 8 as well.

Previous Entries / More Entries