The Anatomy of a CouchApp

posted by Gilbert on 2010.07.14, under CouchDB, Programming
14:

With the development of users and update validations, CouchDB and CouchApp is finally maturing to a viable option to build web applications with. Since my next personal project will be heavily data oriented, CouchApp seems like a natural fit.

However, in my opinion the documentation on CouchApp is light. I have spent a day tracing the code flow so that I can understand where everything goes, and though it was daunting at first, I have to say I am very fond of the data-oriented directory structure approach CouchApp takes.

Generate a fresh CouchApp app

Enough talk. Before we start, be sure you have CouchDB 0.11 or 1.0 installed, as well as CouchApp. Let’s begin by generating a new CouchApp and instantly seeing it in action:

$ couchapp generate myapp
$ cd myapp
$ couchapp push http://localhost:5984/myappdb
... ... ... [INFO] Visit your CouchApp here:
http://localhost:5984/myappdb/_design/myapp/index.html

Copy/paste the url into your browser, and you should see a fresh, fully functional, auto-generated CouchApp. Pretty slick.

Introduction to Evently

Now let’s figure out where this is all coming from. I recommend using Textmate or something similar, since navigating the directory tree will be very important. Here’s a screenshot of my editor’s tree, highlighting what we’ll be focusing on:

A fresh CouchApp directory tree

_attachments is essentially your public folder[1] where you put your main html, javascript, and stylesheets. Open up index.html. This is the layout of the entire page. Notice how clean it is? That’s because all data and html is populated on page load. Checkout the javascript at the bottom:

21
22
23
24
25
26
27
28
<script type="text/javascript" charset="utf-8">
  $.couch.app(function(app) {
    $("#account").evently("account", app);
    $("#profile").evently("profile", app);
    $.evently.connect("#account","#profile", ["loggedIn","loggedOut"]);
    $("#items").evently("items", app);
  });
</script>

This is where all the magic happens. Much like Ruby on Rails views, CouchApp’s jquery.evently.js plugin takes your directory structure and reads for html, event handlers, and callbacks.

Look at index.html:23 (highlighted). This says evently will use the dom element "#account" to output whatever it needs to when running the event code located inside the "account" folder. This is where it gets interesting.

CouchApp’s Directory Tree Structure

Where is the "account" folder located? It just so happens that account is part of the default package that comes with CouchApp. This account folder is located at vendor/couchapp/evently/account (you can also see it in the directory tree screenshot I showed you earlier). The important part to take away from this is that it’s in the vendor/couchapp/evently folder. This is equivalent to moving that account folder into the more shallow evently folder.

What does this mean? Basically, it means evently looks for events inside the two evently folders in your project; first yours, then the one in vendor/couchapp. If you make your own "account" folder inside your own evently folder, any files in there will override and be used instead of the ones with the same names inside the vendor/couchapp/evently folder.

Back to index.html:23. The first thing that runs[2] is vendor/couchapp/evently/account/_init.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function() {
  var elem = $(this);
  $.couch.session({
    success : function(r) {
      var userCtx = r.userCtx;
      if (userCtx.name) {
        elem.trigger("loggedIn", [r]);
      } else if (userCtx.roles.indexOf("_admin") != -1) {
        elem.trigger("adminParty");
      } else {
        elem.trigger("loggedOut");
      };
    }
  });
}

We’ll follow _init.js:11, since that’s what happens first if you’re not logged in (and assuming you’re not having an admin party). This line essentially “calls” the vendor/couchapp/evently/account/loggedOut folder. It takes whatever is in loggedOut/mustache.html and replaces the "#account" div’s contents with it.

After that, it reads from loggedOut/selectors.json:

{
  "a[href=#signup]" : {"click" : ["signupForm"]},
   "a[href=#login]" : {"click" : ["loginForm"]}
}

Using this object literal, evently uses jquery to query the keys and adds triggers based on the values, very much like index.html:23. In this case, clicking the "#signup" anchor will trigger the "signupForm" event, and clicking the "#login" anchor will trigger the "loginForm" event.

Let’s look at the "signupForm" event folder. Wow, it looks very similar to "loggedOut" now, doesn’t it? To reiterate, each of these folders contains the anatomy of an event. Here’s what I’ve gathered so far, in the order of CouchApp execution:

  • map.js: A CouchDB map function. You write this in the exact same way you would when writing for a CouchDB view.
  • query.js: A CouchDB query in json format. You can also mix in evently parameters in here, but I’m not sure how that works.
  • data.js: a function that handles the data being passed when the event is triggered.
  • selectors.json: An object whose keys are dom queries and whose values are an object with the key:value format “eventname”:["myEvent1", "myEvent2", ...]
  • selectors (folder):
    • A folder that replaces selectors.json, where each file inside has the dom query as the name (such as #some.selector.js), and contains the events in file itself.
    • If the selectors folder contains another folder (such as “form”), then the name of the folder is treated as a selector, and the files inside it are treated as nested selectors.
    • Note that dom events can be handled too. For example, evently/profile/selectors/form/submit.js handles the submit.
  • mustache.html: The mustache content that will replace the entire dom element that evently was called on (in our case, "#account").
  • after.js: a function with no parameters that gets called after everything else on this list.

Documentation is light at the time of writing, so if there are any more, or if I’ve gotten something wrong, please let me know and I’ll fix the issue.

I’ll write another post sometime that explains each one in detail, as well as gives code examples for each one.

Hopefully this helps people understand CouchApp’s codeflow a bit more. Have fun developing CouchApps!


[1] NOTE: ALL your files in the project are accesible to read, so don’t put anything you wouldn’t want others to see.

[2] _init is actually an event, so it can also be a folder with mustache.html, selectors, etc.

  • http://twitter.com/andygold99 andygold99

    This has been very useful and is the best guide I have seen so far on how to use evently with couchapps. When will you be able to add some more?

  • http://twitter.com/jchris J Chris Anderson

    There is an instructional screencast (67 minutes long) here.

    http://www.youtube.com/watch?v=Xk5gaUURdJI

    I dive into the anatomy of an evently widget and then do some hacking to add file uploads to a simple application.

  • Sublim21

    A nice tutorial. Looking forward to reading more on your understanding.

  • http://www.victusspiritus.com/ Mark Essel

    Thanks Gilbert, excellent example.

    I'm hoping automated continuous replication after restart gets handled soon. Is there a place where I can read up on how that's planned. Maybe I can work on it.

  • https://launchpad.net/~felixhummel Felix Hummel

    For evently parameters in query.js try:
    function(e, params) {
    alert(params.myparam);
    }

    I use this with pathbinder and a path like “/some/path/:myparam”.

    Small typo: mustaShe.html vs. mustaChe.html

  • Pulkit Singhal

    Very well written, for those still looking for some more details after finishing this, you can head over to:
    http://couchapp.org/docs/_design/docs/index.html#/topic/account
    which explains the account widget and explains most (though not all) of the key pieces.

  • http://romangeber.com/2011/11/18/couchdb-roundup/ CouchDB roundup · romangeber.com

    [...] The anatomy of a CouchApp [...]

blog comments powered by Disqus

TrackBack URL :

pagetop