In the first part of this tutorial we looked at reading a document from CouchDB and keeping our markup separate from our code using Haml templates. We arrived at the point where we clicked on a link, but nothing happened. Today we are going to put that right with some very simple routing.
The URL Module
The basis of all routing is the URL. Fortunately, Node has some basic functions which handle reading the URL so we can then decide what the user is actually trying to do.
First, we want to make sure we have included the URL module.
The aim of this tutorial is to create a Node powered web app using a CouchDB database and Haml based templates. I wanted something relatively simple that I could do in a couple of sittings, but complex enough to be of some actual use. In the end I opted for a “top 10 list” type application. The functionality being as follows:
To list a number of items
To remove an item from the list
To add an item to the list
Obviously, before getting cracking with this tutorial I had to think up a name for this “killer app” of the Node world.
So I give you ErdNodeFlips! In honour of erdnussflips – the strangely addictive Wotsit type things that taste of peanut butter and stick to the top of your mouth. Yum! (OMG – they even have their own Facebook page!)
A project we have going on here at ibrow towers involves quite a bit of jQuery Validation. Whilst the Validation plugin is great with the default functions it offers, what makes it truly excellent is the fact that it is easily extendable.
For example, one thing that has been bugging me is to validate a URL the user must type in the initial http://. But why? Can the validation not just assume that if, for example, www.robsearles.com is typed in, that the user actually meant http://www.robsearles.com?
Well, after a quick read of the documentation and a hack around this little irritant is now gone. It’s all down to addMethod() which lets you add a custom validation method. Lets create one called complete_url().
jQuery.validator.addMethod("complete_url",function(val, elem){// will contain our validation code},'You must enter a valid URL');
A quick word about return values. If the method returns true, everything is considered OK. However, if false is returned then validation is assumed to have failed.
Our new method needs to do three things:
If no url, don’t do anything
Check that the user had entered http:// in their URL, if not, add it in for them.
Check that the URL is now valid. For this I just used the source for the original url validation method.
Lets translate this into some code:
jQuery.validator.addMethod("complete_url",function(val, elem){// if no url, don't do anythingif(val.length== 0){returntrue; }// if user has not entered http:// https:// or ftp:// assume they mean http://if(!/^(https?|ftp):\/\//i.test(val)) {
val ='http://'+val; // set both the value
$(elem).val(val); // also update the form element}// now check if valid url// http://docs.jquery.com/Plugins/Validation/Methods/url// contributed by Scott Gonzalez: http://projects.scottsplayground.com/iri/return/^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(val);
});
Validating the form with this new method is very straight forward:
As many of you know, I use Sup for my emailing needs, and I think it is pretty great. But today, disaster (or at least I initially thought so)!
Instead of opening up and downloading as normal it instead told me it was locked by another process. This in itself is not unusual, sometimes I close the terminal window it is running in by mistake, or shutdown without first closing sup. Hoever, what was different was that the user, host and PID were all blank. When I confirmed it should kill the old process it, well, crashed.
Error: the index is locked by another process! User '' on
host '' is running with pid .
The process was alive as of at least 14 seconds ago.
Should I ask that process to kill itself (y/n)? y
Ok, trying to kill process...
Terminated
It’s pretty simple at the moment, probably very buggy and needs lots done on it, but it is a start. I am making heavy use of Rob Righter’s node-xml module which has saved me hours, possibly days of time!
What you have to do is edit the /usr/share/dbus-1/services/org.gnome.keyring.service file, comment out where it says Name=org.gnome.keyring and add the line Name=org.freedesktop.secrets
For example, my /usr/share/dbus-1/services/org.gnome.keyring.service file looks like this:
As I’ve now been using Emacs for quite a long time, I have picked up a few tips along the way. One thing I’ve been doing more and more is using the shell within Emacs. (For more details see here and here.)
Some of my most used shell commands are using SVN, such as add, revert etc. However, I couldn’t find anywhere to just add/revert the current file I am working on. Fortunately there is Stack Overflow! Someone with a very similar issue to me has had a couple of good answers, which I have “borrowed” and now adding files to my SVN repository is really easy.
I have uploaded some tweaks to the user interface and back end functionality, plus also started work on an API. I even added a blog to keep you all up-to-date.