Sunday, December 16, 2012

Using JQuery and CouchDb to build a simple AJAX web application


I am Sagar S. Vasule initially wrote this post a couple of months ago, while trying out writing a blog on wordpress. That blog didn't go anywhere, but I thought this tutorial on writing an AJAX web application with JQuery and CouchDb might still be of interest to developers playing with CouchDb. Certainly I couldn't find anything similar at the time
I assume that you have CouchDb 0.10 and CouchApp 0.5 installed. Later versions may work, but no guarantees.
First, go to http://127.0.0.1:5984/_utils and create a database called "addressbook". Because we'll be making the functionality to display data before the functionality to create or update it, it's a good idea to use the built-in CouchDb web interface to add a couple of documents. The initial version of our webapp will let us store a mobile number for each person, so add the following 2 documents:
  1. {"type""address""name""Fred""mobile""555-0001"}  
  2. {"type""address""name""Barney""mobile""555-0002"}  
CouchDb uses design documents to expose the database to the world, but editing design documents directly gets a little painful. Couchapp eases this pain, by breaking design documents out into a set of directories and files on the local filesystem representing shows, views, and so on, which can be pushed to the CouchDb server at will. We need to create a couchapp design document, and also a view to retrieve the phone numbers, which we can do using couchapp at the command-line:
couchapp generate app addressbook
couchapp generate view addressbook phonenumbers
The first line creates a directory called addressbook with a skeleton design document set up, and the second creates a view in the design document.
We can push this to the server using:
couchapp push addressbook http://127.0.0.1:5984/addressbook
If you look at the database using the web interface now, you should see the design document "_design/addressbook" in addition to the documents representing the two phone numbers.
The view we will need simply gives each name and mobile number, with name as key, so the view is sorted by name. No reduce function is required, so delete addressbook/views/phonenumbers/reduce.js, and put the following in addressbook/views/phonenumbers/map.js
  1. function(doc) {  
  2.   if (doc.type && doc.type == "address" && doc.name && doc.mobile) {  
  3.     emit(doc.name, doc.mobile);  
  4.   }  
  5. }  
Push the couchapp to the server using
couchapp push addressbook http://127.0.0.1:5984/addressbook
and check that you can see the view at http://127.0.0.1:5984/addressbook/_design/addressbook/_view/phonenumbers
Couchapp has set up an initial index.html for us, which we can edit at addressbook/_attachments/index.html, and view at http://127.0.0.1:5984/addressbook/_design/addressbook/index.html. Make sure that you can view the file ok. You should then alter index.html to contain:

  1. <!DOCTYPE html>  
  2. <html>  
  3.   <head>  
  4.     <title>Simple address book</title>  
  5.     <link rel="stylesheet" href="style/main.css" type="text/css">  
  6. <span style="white-space: pre;"> </span><script src="/_utils/script/json2.js"></script>  
  7. <span style="white-space: pre;"> </span><script src="/_utils/script/jquery.js?1.3.1"></script>  
  8. <span style="white-space: pre;"> </span><script src="/_utils/script/jquery.couch.js?0.9.0"></script>  
  9. <span style="white-space: pre;"> </span><script type="text/javascript">  
  10. <span style="white-space: pre;"> </span></script>  
  11.   </head>  
  12.   <body>  
  13.     <h1>Simple address book</h1>  
  14. <span style="white-space: pre;"> </span><div id="add"><button type="button" id="add">Add</button></div>  
  15.     <div id="addressbook"></div>  
  16.   </body>  
  17. </html>  

The head contains a title, a link to a stylesheet, imports jQuery and jQuery Couch. These are used by the CouchDb web interface, and included with CouchDb by default. It then has a space for our own javascript to go - more on this later. The body contains an add button within a div, and an empty div called addressbook. The app will display address book entries in the addressbook div.
We use javascript to access data in the CouchDb, and to process events from the browser, such as buttons and links being clicked. Conceptually, we can think of CouchDb as the Model, the DOM representing the HTML shown to the user as being the view, and our javascript as being the Controller, albeit with relatively little separation in this example.
Javascript typically uses a continuation-style to process events. For example, we call asynchronous functions, for example to request data from CouchDb, and pass them a function to call when they return, eg to update the view with the data we receive. A simple example:
  1. $.couch.db("addressbook").saveDoc(  
  2.   {type: "address", name: "Wilma", mobile: "555-003"},  
  3.   {success: function() { alert("Saved ok."); }}  
  4. );  
This example saves a new document to the Couch database, and if successful, pops up an alert.
Another example is jQuery, which calls functions when links or buttons are clicked. The following example will trigger an alert whenever an "a" element is clicked (ie a hyperlink):
  1. $("a").click(function() { alert("Click"); })  
jQuery uses selectors to control what elements of an HTML document will trigger functions. In the above example, the selector applied to all "a" elements. We can use "a#blah" to select all "a" elements with an id attribute of "blah", or "a.blah" to select all "a" elements with a class of blah. Selectors form a mini-language and the jQuery documentation gives more details.
The first javascript we add is called when the base document has finished loading. It retrieves the JSON object of the phonenumbers view from CouchDb. When the JSON object is retrieved a continuation iterates over each row in the view, adding a div for each row containing the name, phonenumber and links to edit and delete it:
  1.   $db = $.couch.db("addressbook");  
  2.   
  3.   function refreshAddressbook() {  
  4.    $("div#addressbook").empty();  
  5.    $db.view("addressbook/phonenumbers", {  
  6.     success: function(data) {  
  7.      for (i in data.rows) {  
  8.       id = data.rows[i].id;  
  9.       name = data.rows[i].key;  
  10.       phonenumber = data.rows[i].value;  
  11.       html = '<div class="address">' +  
  12.        '<span class="name">' + name + '</span> ' +  
  13.        '<span class="phonenumber">' + phonenumber + '</span> ' +  
  14.        '<a href="#" class="edit">edit</a> '+  
  15.        '<a href="#" class="delete">delete</a> '+  
  16.        '</div>';  
  17.       $("div#addressbook").append(html);  
  18.      }  
  19.    }});  
  20.   }  
  21.   
  22.   $(document).ready(function() {   
  23.    refreshAddressbook();  
  24.   });  
A few things to note:
  • We have created a variable $db to access the CouchDb database. If we change the name of the database, this means we only have to change one place.
  • The function called on success(ful retrieval of data from CouchDb) takes a parameter, data, which is the data returned fromt the view.
  • We call the function from a continuation passed to $(document).ready(). This ensures that we won't request data until the HTML document is fully loaded.
  • The links have an id, which is the id of the document representing the address book entry.
If we push to the server now, using
couchapp push addressbook http://127.0.0.1:5984/addressbook
and refresh in the browser, we should see the page load and then populate with phone numbers from the database. The add button and the edit and delete links will be inactive, but at least the R part of our CRUD app works.
The next thing to focus on is deleting entries. A nice way for this to work is for the delete link, when clicked, to pop up further links to confirm or cancel deletion. We can use jQuery to insert these links in the div for the address book entry when the delete links are created.
But this poses a problem - jQuery binds functions to the DOM elements that exist at the time. The links we are proposing adding do not exist when the $(document).ready continuation is called, so we cannot bind functions to them until they are created. Fortunately, there is a trick we can use. Events such as clicks propogate up the DOM tree if they are not processed - so a click will first be processed by the a element, if a handler exists, else it will be processed by the containing div, for example, if a handler exists, all the way up to the document itself. The continuation takes a parameter, which can be used to determine the exact element that was clicked.
In our example, we can bind a function to the click event of div#addressbook, and then check whether the click came from an edit link, a delete link, or the confirm or delete links we will create. Add the following to the $(document).ready continuation:

  1. $("div#addressbook").click(function(event) {  
  2.   var $tgt = $(event.target);  
  3.   if ($tgt.is('a')) {  
  4.      id = $tgt.attr("id");  
  5.      if ($tgt.hasClass("edit")) {  
  6.       // TODO: implement edit functionality     
  7.      }  
  8.      if ($tgt.hasClass("delete")) {  
  9.       html = '<span class="deleteconfirm">Sure? <a href="#" class="dodelete">Yes</a> <a href="#" class="canceldelete">No</a></span>';  
  10.       $tgt.parent().append(html);  
  11.      }  
  12.      if ($tgt.hasClass("dodelete")) {  
  13.       $db.openDoc(id, { success: function(doc) {  
  14.        $db.removeDoc(doc, { success: function() {  
  15.         $tgt.parents("div.address").remove();       
  16.        }})  
  17.       }});        
  18.      }  
  19.      if ($tgt.hasClass("canceldelete")) {  
  20.       $tgt.parents("span.deleteconfirm").remove();  
  21.      }  
  22.     }  
  23.    });  

This code runs when any element in the addressbook div is clicked. It gets the clicked element, the id of that target element (which we set above to be the relevant CouchDb document's id), and then runs different code depending on the class of the clicked element. If the link with class "delete" is clicked, we append two new links - "Yes" with class "dodelete", and "No" with class "canceldelete" - within a span with class deleteconfirm. Then, if a link with class "canceldelete" is clicked, we remove the relevent "confirmdelete" span from the DOM to delete the confirm message. On the other hand, if a link with class "dodelete" is clicked, we call $db.openDoc to retrieve the document to delete from CouchDb, and pass it, using a success continuation to $db.removeDoc. We need to retrieve the document prior to deletion in CouchDb, to avoid deleting from an older version than the most current in the database. If this is successful, we remove the parent address div from the DOM, to match the database. Take a deep breath, and read that again.
If you push this design document to CouchDb, you should be able to delete entries from the address book. However, you will have to use the CouchDb built-in interface to re-add them at this point.
We adopt a similar approach for editing or creating new entries - clicking the button or link exposes a form to enter new details. CouchDb allows us to save a document with an id to indicate an update, or without an id to indicate that one should be generated, and a new document created. Consequently, we can use the same form for creating and editing if we are careful.

Add the following function between the refreshAddressbook function and the $(document).ready
  1. function addUpdateForm(target, existingDoc) {  
  2.   html = '<form name="update" id="update" action=""><table>' +  
  3.     '<tr><td>Name</td><td>Number</td></tr>' +  
  4.     '<tr>' +  
  5.       '<td><input type="text" name="name" id="name" value="' +  
  6. <span style="white-space: pre;"> </span>    (existingDoc ? existingDoc.name : "") + '"/></td>' +  
  7.       '<td><input type="text" name="mobile" id="mobile"/ value="' +   
  8. <span style="white-space: pre;"> </span>    (existingDoc ? existingDoc.mobile : "") + '"></td>' +   
  9.       '<td><input type="submit" name="submit" class="update" value="' +   
  10. <span style="white-space: pre;"> </span>    (existingDoc?"Update":"Add") + '"/></td>' +   
  11.       '<td><input type="submit" name="cancel" class="cancel" value="Cancel"/></td>' +   
  12.     '</tr>' +  
  13.   '</table></form>';  
  14.   target.append(html);  
  15.   target.children("form#update").data("existingDoc", existingDoc);  
  16. }  

Apologies for the inelegant table. I would love to hear a better way for laying out forms. In the meantime, this does the job. And the job is that it appends a form to the target element. If existingDoc is provided, its name and mobile values are used to populate the form, else blanks are used. Further, the existing document is attached to the form, so it can be updated with new values provided by the user to save data later.
To display the form when an edit link is clicked, add the following in place of the edit TODO above:
  1.      if ($tgt.hasClass("edit")) {  
  2.       $("button#add").show();  
  3.       $("form#update").remove();  
  4.       $db.openDoc(id, { success: function(doc) {  
  5.         addUpdateForm($tgt.parent(), doc);  
  6.       }});        
  7.     }  
 And to display the form when the add button is clicked, add a continuation to the bottom of the $(document).ready continuation:
  1.    $("button#add").click(function(event) {     
  2.     $("form#update").remove();  
  3.     $("button#add").hide();  
  4.     addUpdateForm($("div#add"));  
  5.    });   
Note that we hide the add button when it is clicked, and show it otherwise. We also remove any existing forms, so only one form shows at any time.
Finally, we need to process the events when the update(/add) button is hit, or the cancel button. We could adopt the same approach as above, of binding to an element up the hierarchy, but I've chosen to demonstrate a different approach. The .live method can be used to bind to a selector, and to any future elements that selector applies to. That is, as new DOM elements are created, the continuation will apply to them also. Here is an example to bind the click events, of input elements of class "cancel", which hides the form and shows the add button when the cancel button is pressed:
  1.     $("input.cancel").live('click', function(event) {  
  2.     $("button#add").show();  
  3.     $("form#update").remove();  
  4.     return false;  
  5.    });    
 You should add this to the end of the $(document).ready continuation.
Finally, we need to process the update/add event. The key here is to pick up the existing CouchDb document, if it exists, from the form DOM element we attached it to. This ensures we are updating a CouchDb document with the appropriate id and revision. If there is no existing document, we can populate an empty JSON object, and CouchDb will save it as a new document with a uniquely generated id. Here is the continuation and binding to add to $(document).ready:
  1.    $("input.update").live('click', function(event) {  
  2.     var $tgt = $(event.target);  
  3.     var $form = $tgt.parents("form#update");  
  4.     var $doc = $form.data('existingDoc') || {};  
  5.     $doc.type = "address";  
  6.     $doc.name = $form.find("input#name").val();  
  7.     $doc.mobile = $form.find("input#mobile").val();  
  8.     $db.saveDoc(  
  9.      $doc,  
  10.      {success: function() {  
  11.       $("button#add").show();  
  12.       $("form#update").remove();  
  13.       refreshAddressbook();  
  14.     }});  
  15.     return false;  
  16.    });  
Note that we remove the form and show the button when the document has been saved.
Ok, that's it. Push this to CouchDb, and reload the page. You should find that you have a perfectly working, albeit minimal, CRUD webapp.
There are a few other things that you would want to do in production code, which I have left out here for clarity and brevity. Verification functions should be added to the design document, to avoid badly formed data being passed to CouchDb. Errors should be handled, as well as successes. Also, you might want slightly more from an address book webapp. Nonetheless, I hope this is a useful example of using CouchDb and jQuery to make an AJAX webapp.

Tuesday, December 11, 2012

DropBox as a GIT Central Repository.


If you've been reading my blog, it should be clear by now that I'm a bit of a fan of the Git version control system.
I've been hosting some projects on Unfuddle for a while now, but have not been very happy with fiddling with SSH keys and setting up the central repositories through the Git Extensions for Windows. It works, but that's all I can say.
Another issue I was not looking forward to tackling was setting up TeamCity to pull from Unfuddle. I haven't tried, and for all I know it might work great, but if there's a perfectly good shortcut that I can take, I'll take it!
A few days ago I saw some tweets from people mentioning Git and DropBox as a combo. Unfortunately, I can't find the messages any more as Twitter's retention policy seems to be very aggressive. So I decided to try doing it myself.
The advantages of having a Git repository in DropBox are that:
  • it does not rely on a service that (for all I know) might go out of business one day;
  • it prevents me from having to create SSH keys as credentials;
  • it makes it easier to point TeamCity to a central git repository (which is in fact on the local disk);
  • the central repository is always synced to all of my dev machines and to the DropBox server, so there is multiple backups in case of catastrophic failure;
  • you will always have the ability to push changes to a central repository, even if you have no (or intermittent) internet connectivity;
  • if you have to work without internet connectivity, but with network connectivity, you could just set-up a network share and push and pull to each of the repositories that way.
I'm wildly enthusiastic about the distributed nature of the Git version control system, it really does allow me to work from anywhere and in any way that I want.

I am assuming that you have DropBox installed and that you are using the Git Extensions that I've mentioned in earlier blog posts on Git.
I've created a folder somewhere in my DropBox folders that will hold my Git repositories. In there, I've created a folder called "Example.git".
Next, I created a new Git repository using the Git Extensions. When you start Git Extensions, choose "Initialize new repository" from the "Commands" menu.
I created a "Central repository, no working dir" in the "Example.git" folder.
gitdropbox01
Then, in the folder that holds my Example project, I created a personal repository, as I would do normally when putting you project under version control (again through "Commands", "Initialize new repository").
gitdropbox02
This will open up the repository in Git Extensions. The first thing I like to do is hit the "Edit .gitignore" button, add the default ignores and commit that as the initial commit.
gitdropbox03
With that inital commit out of the way, I will add a remote from the "Remotes" menu.
I've name the new remote "DropBoxExample" and instead of entering a URL, I point to the "Examples.git" folder that I created earlier.
gitdropbox04
After clicking the "Save" button, Git Extensions will ask if I want to automatically configure the default push and pull behavior, I've skipped this step because the folder on the disk is not actually a server that understands commands. Then I closed the "Remote repositories" window and start pushing the master branch to the remote.
gitdropbox05
And got a confirmation message, it worked!
gitdropbox06
Now I am able to actually set the pull defaults, so I go back to managing the remote, go to the "Default pull behavior" tab to choose the defaults from the dropdown lists and clicked "Save".
gitdropbox07
All done!
I went to my other machine and created the same "D:\Dev\Example" folder.
I opened up Git extensions and initalized the folder as a personal repository. Then I was able to add the remote repository in exactly the same way, instead of entering a URL, I pointed to the "Example.git" folder in DropBox, saved the remote and pulled from it.
To my delight, I saw the .gitignore file appear immediately and noticed that I had received the commit history from the other machine. Success!
Of course I then proceeded to add a Visual Studio project, pushed it to the central repository, pulled it in on the other machine, and again, everything appeared as I hoped it would. Success!

How to use Dropbox as an SVN repository


Recently, the popular Cloud based startup Dropbox is getting lot of attention on Internet. It is a free file synchronization/backup service where you can backup, save and share files. Dropbox gives 2 GB of free space to every user where one can save / backup their files.
One of the interesting usage of Dropbox can be to use it as Subversion Repository to store your projects. We can create a local SVN repository on Dropbox which can be synchronized on Cloud by Dropbox. You can checkout this repository and add your projects / files into it. This way you can access your project anywhere. Also it is possible to share your repository with other users if you want to. Simply by sharing Dropbox folder where you have stored subversion repository can enables you to share it with other users.
Here is an example of hosting a local SVN repository on Dropbox.

Step 1: Create Dropbox repository

cd ~Dropbox
mkdir SVNRepository
cd SVNRepository
If you are using subversion command line utilities in Windows or using Unix box, we can create a new subversion repository by using svnadmin.
svnadmin create .
If you are using TortoiseSVN client then you may want to create repository using following:

Right-click on the newly created folder SVNRepository and select TortoiseSVN → Create Repository here….
We just created a Subversion repository on top of Dropbox folder. Don’t edit those files yourself!!. If you get any errors make sure that the folder is empty and not write protected.

Step 2: Checkout Dropbox repository

Our Dropbox repository is ready and we can do checkin / checkout in this now. Before we add any project / files in this repository, we need to make sure we add proper directory structure in this SVN repository. It is a good practice to divide an SVN repositories in three directory: trunk, branches and tags.
First create a working folder where you want to checkout your SVN repository.
mkdir ~MyWorkspace
cd MyWorkspace
svn checkout file:///~Dropbox/SVNRepository
This will checkout the newly created repository in folder MyWorkspace.
Now create 3 directories in MyWorkspace: trunk, branches and tags and commit the change back to repository.
mkdir trunk
mkdir branches
mkdir tags
svn commit -m "Initial setup (committing trunk, branches, tags)"

Step 3: Checkout Trunk of Dropbox SVN repository

We are done with the setup.. Just checkout the Trunk of this newly created SVN repository and start committing your changes.
svn checkout file:///~Dropbox/SVNRepository/trunk
That’s it. Now you can checkout the SVN repository “file:///~Dropbox/SVNRepository/trunk” anywhere and commit your changes to it. Ofcourse this is now hosted on Dropbox so you can easily synchronize your changes between different computers. You can also share this repository with other Dropbox users letting them SVN your repository access.

How to use Git and Dropbox


How to use Git and Dropbox together:
This is how I created the enENCOMMAS Git Repo directory: -you must
have Dropbox on yourmachine installed. For a Mac, this puts a
directory called /Users/$HOME/Dropbox in your home root directory
(here italics mean what you type at the command line):
cd ~/Dropbox
mkdir -p repos/enNCOMMAS.git
cd !$
git --bare init
This creates a bare directory we can create a master repository into.
We now have to create a cleaned version of the enNCOMMAS directory,
then create ourlocal git database. First cd into the enNCOMMAS
directory. We canrecursivelyremove all of the .svn (or CVS)
directories using a quick command inside the enNCOMMAS directory:
find . -name '.svn' -type d -print | xargs rm -rf {}
or
find . -name 'CVS' -type d -print | xargs rm -rf {}
Then, we create the local git repository for enNCOMMAS:
git init
git add .
git commit
You now have create a git repository for enNCOMMAS. We can now push
this up to the Dropbox repos by:
cd enNCOMMAS
git remote add dropbox file://$HOME/Dropbox/repos/enNCOMMAS.git
git push dropbox master
Inside the git directory, if you want `git pull` and `git push` to use
the dropbox repo by default, you can type:
git config branch.master.remote dropbox
git config branch.master.merge master
Now you can use git just like SVN, e.g., say you have a new file you
want to add:
git add new_file.f90
git commit new_file.f90
and the usual commit interface message comes up. For changes to an
existing file,
git commit existing_file.f90
Now just because you have created a local commit –you have not yet
“pushed” the changes up to the master. To do this now, simply:
git push
On another machine, you can simply clone from the dropbox account:
git clone -o dropbox file://$HOME/Dropbox/repos/enNCOMMAS.git
and if you already have the enNCOMMAS repos on your machine, you can
pull the changes from the master:
git pull
In this way, you have the entire history of the enNCOMMAS on your
machineand a master branch at Dropbox. One can then (at a finer grain
level) control what is stored locally in a repo, and what is available
to everyone –you have the entire history stored on you machine (like a
laptop) and can branch/fork locally to test stuff, then merge back.
Some of this information was obtained from Bradley Wright’sweb page:
http://tumblr.intranation.com/post/766290743/using-dropbox-git-repository
Additional Techniques
Since we have local Git servers and you want to push code to a remote
machine that may be outside the firewall –this is possible via the
followingprocedure. Similar to the Dropbox procedure –you will
create a bare local repository first, push your repository (or branches
from it) to that remote repo, and then pull the code from the remote
repository onto the remote machine. So on the remote machine, do the
following commands
mkdir -p repos/enNCOMMAS.git
cd !$
git --bare init
You now have a bare repository. Now go back onto your local machine
where the git repo sits, and type in:
git push ssh://remote_machine/…/…/repos/enNCOMMAS.git master
This pushes the code over to that machine. Now inside the
enNCOMMAS.git directory,there is a repo (HEAD, config, etc.). This
has all the changes you have on the local machine (which, BTW, does not
have to match was is stored in the community dropbox).  To get the code
for the remote machine out of the repo,
cd repo
git pull
and wonderkind!  In ~/repo/enNCOMMASis your code. If then you change
anything on the remote machine, commit it there and then you can PULL
it to your local machine via:
git pull ssh://remote_machine/…/…/repos/enNCOMMAS
Note:  you are pulling from the working copy, not the repo!You can
still push changes from the local copy in the same way you did the
initialpush.
If you get a message when trying to push the repo from your local
machine to the remote machine like:
error: refusing to update checked out branch: refs/heads/master
error: By default, updating the current branch in a non-bare repository
error: is denied, because it will make the index and work tree inconsistent
error: with what you pushed, and will require 'git reset --hard' to match
error: the work tree to HEAD.
error:
error: You can set 'receive.denyCurrentBranch' configuration variable to
error: 'ignore' or 'warn' in the remote repository to allow pushing into
error: its current branch; however, this is not recommended unless you
error: arranged to update its work tree to match what you pushed in some
error: other way.
error:
Then go to the remote machine and type in the enNCOMMAS.git repo,
git config receive.denyCurrentBranch ignore
and afterwards, you maynot see your files, and type
git checkout –f
But as far as I can tell, you should not need to do this.
Handy Stuff
To commit all new changes (adding, deleting, etc), you can just type:
git commit –a
which is a big improvement over SVN