This is a simple little program that I wrote mostly to practice using Coffeescript, Node, and Promises. I have a lot of little web projects going on all the time on my PC, and keeping track of them all is a sometimes difficult task. I wanted to make that task easier.

The basics of the task are simple: get a list of all the ports where I usually drop off a web-oriented project, try to get the home page, and if it’s there, try to get the title of out the HTML.

For this project, I used the excellent node-promise library, mostly because its behavior most closely matched that of jQuery’s Deferreds library. I also used the scraper library for screen-scraping the HTML; Scraper actually returns a jQuery object suitable for manipulating on the client.

Although Node is famous for being asynchronous, let’s face it: there is an order in which some things must be done. In this case, you must get all the ports, then visit every port to get the title, and then print the results. Because I’m going to use Haml, I must also get the template; this can happen in parallel with, well, just about everything else. But we can not display the results until we have all the titles and the template.

Literate Program

As is my usual practice, this article was written with the Literate Programming toolkit Noweb. Where you see something that looks like <this>, it’s a placeholder for code described elsewhere in the document. Placeholders with an equal sign at the end of them indicate the place where that code is defined. The link (U->) indicates that the code you’re seeing is used later in the document, and (<-U) indicates it was used earlier but is being defined here.

The Program

The first step is to get all the ports. Netstat is the cheapest way to do that, and spawning processes and reading from them is something Node does very well.

The only thing of note here is the promise. This object returns a promise that, when resolved, returns the data.

<get ports>= (U->)
get_ports = () ->
    promise = new deferred.Promise()
    data = ''

    accrue = (d) ->
        data += d

    netstat = spawn 'netstat', ['-anp', '-t', 'tcp']
    netstat.stdout.on 'data', accrue

    netstat.on 'exit', () ->
        promise.resolve(data)

    promise

Once we have the ports, we want to de-dupe them, as netstat sometimes returns duplicates. The de-dupe is trivial in coffeescript:

<de-duplicate an array>= (U->)
dedupe = (arr) ->
    obj={}
    for i in arr
        obj[i] = 0
    for i of obj
        i

For each port, we want to get the title. We want to use the promise so the program will block until done.

<get titles>= (U->)
get_title = (port) ->
    promise = new deferred.Promise()
    scraper 'http://localhost:' + port, (err, jQuery) ->
        if err
            promise.resolve [port, err.message]
            return
        promise.resolve [port, jQuery('title').text()]
    promise

We want to get the titles from all of the the ports, and then spew out the results. As this is a CGI program, we want a simple header.

It’s that double deferred.when() that makes the difference. when() takes a promise as an argument. deferred.all() takes a bunch of promises and returns a single promise that resolves when all of the promises passed in finish. So here, we’re saying all of the get_title() operation, and the get_template operation, must complete before we go on to render the results.  Notice how the data that gets returned is the array from the inner deferral and the template.

<display ports>= (U->)
display_ports = (data) ->
    <return matched ports>

    <get template file>

    matches =  dedupe(matcher(i) for i in data.split(/\n/) when matcher(i))
    promises = (get_title(i) for i in matches)
    deferred.when deferred.all(deferred.all(promises), getTemplate()), (data) ->
        [data, template] = data
        console.log("Content-type: text/html\r\n\r\n")
        handler = haml(template)
        console.log handler({data: data})

The matcher is just a regular expression check:

<return matched ports>= (<-U)
matcher = (i) ->
    r = (/^.{20}.*?\:(\d+)/).exec(i)
    if not r
        return null
    r = parseInt(r[1])
    if (r >= 3000 and r < 3099) or (r >= 8000 and r < 8300) or (r == 80) or (r == 81)
        return r
    null

And the template get is equally trivial. dReadFile is an asynchronous read function from node-promise that returns a promise, the resolution of which is the contents of the file.

<get template file>= (<-U)
getTemplate = () ->
    dReadFile('layout.haml', 'utf8')

The whole of the program becomes:

<counter.cgi>=
#!/usr/bin/coffee

deferred = require('promise')
dReadFile = require('fs-promise').readFile
spawn = require('child_process').spawn
scraper = require('scraper')
haml = require('haml')

<get ports>

<get titles>

<de-duplicate an array>

<display ports>

deferred.when get_ports(), display_ports

And that is pretty much it. The last line launches the script, and guarantees the process runs in the right order.

Cakefile

<Cakefile>=
exec = require('child_process').exec

task 'build', 'Build the main program out of Noweb', ->
    exec 'notangle -c -Rcounter.cgi counter.nw > counter.cgi', (err) ->
        console.log err if err

xelatex_cmd = ('xelatex counter.tex; ' +
    'while grep -s "Rerun to get cross-references right" counter.log; ' +
    'do xelatex counter.tex;\n done')

task 'docs', 'Build the PDF of this document', ->
    exec 'noweave -x -delay counter.nw > counter.tex', (err, stdout) ->
        if err
            console.log err
            return
        exec xelatex_cmd, (err) ->
            console.log err if err

task 'html', 'Build the PDF of this document', ->
    exec 'noweave -filter l2h -delay -index -autodefs c -html counter.nw > counter_doc.html', (err) ->
        if err
            console.log err

Index

Warning

Okay, so this is a fairly simple program. It also requires a ton of stuff be installed in a directory where the CGI is going to be run from, so it exposes a lot of stuff you might not want to expose. Like I said, this was an experiment.

Source Code

The source code is available from GitHub at PortProject. Yeah, it’s a boring name. Also, at this moment there is a bug in jsdom (Fix ReferenceError in the scanForImportRules helper function) that causes this script to spew warnings about CSS parsing. Those can safely be ignored (really!).

TL;DR version: Down at the bottom is a simple naive RSS proxy written in Coffeescript for Node.js, written as an example.  I explain how to install Node and Coffeescript, and how the proxy works.

There’s a lovely blog out there, Elegant Code, which has a series entitled “Baby Steps with Node.JS.”   There are a few problems with the site, not the least of which is that the version of node covered is so old the API doesn’t jibe with Node 0.4 (the current stable release as of this writing), some of the examples are buggy, and finally they’re all written in Javascript.  Now, I’ve written a lot of articles with Coffeescript in them, but they were huge articles.  I understand if someone got lost.

What is Node.js?  It’s a virtual machine for the server that runs Javascript, using Google’s V8 engine (the same one running in Google Chrome), but with two extra features: a very tight event loop, and a new language feature, require(), that lets you import libraries.  Node.js both is and is not another programming language in the spirit of Python, Ruby, or PHP: It is in the sense that it can do all of the things those languages to, if and when people get around to writing bindings for things like GUIs and such.

But it’s not meant for that.  It doesn’t do math as fast as compiled code, it’s not elegant the way Python or Ruby can be.  What it’s very good at is events, and the web is all about events: requests/responses/waitings-on.  Where another program blocks an HTTP request and idles while it waits for a response from a database, and needs another thread (or process!) to handle another request, Node just puts those transactions into a queue and goes on to the next, round-robin, very very fast.  Plus it’s written in Javascript, a language most of us already know, which is a benefit.

Node.js runs on all modern Unix clones, including Apple OS.   Node.js 0.5, the “unstable” build, also runs on Windows.

Once you’ve installed Node.js, you need the Node Package Manager.  It will let you install common Node packages easily:

curl http://npmjs.org/install.sh | sh

If you know Javascript well, you might have also encountered Coffeescript.  Coffeescript is a little language that compiles to Javascript.  There’s a one-to-one correspondence between Coffeescript structures and Javascript structures, which makes Coffeescript easy to learn.  I think Coffeescript is easier to learn and more clear to write, but then I’ve been writing Python for a very long time.  The Coffeescript compiler is written in Coffeescript and runs in all the major server-side Javascript implementations.  Since we’re doing Node, you may as well use that.

Once you’ve installed NPM, Coffeescript is as simple as:

npm install coffeescript

Okay, enough talk.  We’re going to build an RSS proxy– you’ll be getting HTTP output formatted for RSS.  This will work in all browsers but it’s best in Firefox because Firefox still makes a heroic effort to parse RSS correctly.

http = require 'http'
url = require 'url'

handle = (server_response) ->
    (response) ->
        if response.statusCode != 200
            console.log(response)
            return
        responseBody = ''
        response.on 'data', (chunk) ->
            responseBody += chunk
        response.on 'end', () ->
            server_response.write(responseBody)
            server_response.end()

myserver = http.createServer (request, response) ->
    feedUrl = 'http://feeds.delicious.com/v2/rss/tag/javascript'
    parsedUrl = url.parse(feedUrl)
    client = http.createClient(80, parsedUrl.hostname)
    client_request = client.request(parsedUrl.pathname, {'host': parsedUrl.hostname})
    client_request.on('response', handle(response))
    response.writeHead(200, {'Content-Type': 'text/html'})
    client_request.end()

myserver.listen(8124)

Coffeescript is a whitespace-delimited language, with some vaguely Rubyesque parsing behaviors.  Many people say if you know Ruby you’ll love Coffeescript, but the Python partisan in me loves it too.  Coffeescript’s language is so regular that most often you can skip using parentheses.  The example above is inconsistent– sometimes I use them when they’re not necessary, sometimes I don’t– but if you examine the two uses of .on() (in the handler, and in the server declaration), you can see the differences.  The syntax ‘(argument, argument) ->‘ is a function declaration and returns an anonymous function. If you understand that, you understand 75% of all Coffeescript, and all you need to know now.

At the top, we use the Node-specific keyword require() to import two libraries we’re going to use.  Both of these libraries come with Node, so we’re not going to use NPM for anything.  Our target is the “most recent Javascript-tagged stuff on delicious.”

Start with the createServer entry.   It takes a function that takes two objects, the request and the response.   The very last line there starts the server, listening at a specified port.  Whenever a browser hits that specified port, the function is called and passed a request object and a response object.

In my example, the function then creates its own HTTP client, makes a call to a specified URL, and sets a handler for the response.

The handler is that first defined function above.  It return a function with the server’s response object enclosed within it.  This handler processes the response coming back from the client (in this case, by just storing it), and then, when the client’s response is ended, forwards that response through the server’s response object and ends the connection.

The magic here is all of the .on() calls.  They handle the events that can happen: when a request gets a response, when the response gets data, when the response ends, and so forth.  What’s magical about them is that while they’re waiting for those events Node.js can handle any other events for other request/response cycles that are pending.  It’s this switchboard power, combined with the familiarity of Javascript, that makes Node.js so nifty.

To run this, just copy the code into a file server.coffee, and run:

coffee server.coffee

Point your browser to http://localhost:8124 and see the magic.

17Sep

Why careful system design matters!

Posted by Elf Sternberg as Design

I read today about a recall of birth control pills. The detail that caught my attention was this:

Blister packs of the birth control pills were rotated 180 degrees, which reverses the weekly tablet distribution. This packaging error could cause women to take pills in the incorrect order and could lead to unintended pregnancy.

I suspect this means someone at the factory put the empty blister packs into the packaging machine upside down.

Buddha wept, this is Industrial Design 101. If you have an integration process where orientation is important, you avoid this kind of disaster by enforcing orientation through asymmetry. You can’t plug a USB cable in upside down because the plug is asymmetrical along the critical axis. You shouldn’t be able to load unlabeled blister packs into a machine upside down, nor should it be possible to fill and label those blister packs. All it would take is a notch in one corner to ensure orientation.

I’m sure press people would assure me “it’s more complicated than that.” No, it’s not. I could be wrong about the mechanical particulars, but issues like this represent a systems design mistake of a real-world artifact: somewhere along the way a physical template, die, guide, measure, or clamp that could have been designed to prevent this from happening was not.

16Sep

Lifestreams and Gamification

Posted by Elf Sternberg as Design, programming

I was having a discussion the other day with a PHP developer, helping him architect a gamification layer for a website he owned.  He had this ridiculously broad table of everything the user could earn, and I winced and said, “What if you want to add a new badge?”  He hemmed and hawed and allowed that might cause some difficulty.  I then proceeded to walk him through the logic of using a meta-table for currencies– the things you earn when gamifying.  For example, in Stack Overflow, which I consider an examplary example of gamification, users earn reputation for certain acts, and they also earn badges for certain specific events– your first post with 10 likes, or your first post you deleted even though other people liked it because you considered the answer wrong.  Each of these is a currency: “a post,” “a post with 10 likes,” “an answer with 10 likes,” “a deleted accepted answer.”

I showed how one table of currencies, and another of user/currency/current_amount, was all he needed to track what a user was earning.  He accepted that and said, “How do we track when a currency awarded?”  We talked about the trade off of doing event-driven aspect-oriented programming versus interval analysis.  Currency awards are metadata derived from database events; you could do the full analysis from database logs with every event, but that would be expensive.  We also talked about when badges and privileges were awarded, and how watching the user/currency/current_amount table was how that happened.

We hit a complication.  It’s apparently not unusual with PHP to store SQL, PHP, and DSLs in the database.  He wanted to store the rules for awarding as a DSL.  After a while, we agreed that this was a difficult issue.  The badge awards were fairly easy: “when X > 10 award Y”.   It was the rules for awarding currency in the first place that he had trouble with.

Today, I hit upon the answer.  First, you have to ask the right questions: “When is currency awarded to a user?”  There are three answers: (1) When the user does something; (2) When the user does something for someone else; (3) When someone else does something to an object owned by the user.   You get currency when you post; you get currency when you answer another question; you get currency when someone else likes your answer.   It occurred to me the second I wrote those down that they looked very familiar.

They should: they’re lifestream events.  They’re at the core of every “What your friends are doing!” display on every social network worth your attention.

Gamification is metadata about what you and your friends are doing on a social networking site.  It uses exactly the same data and processing, and exactly the same analysis.   All it does is add another layer, showing you and your friends the extent to which you participate on the site.  A layer that might be computationally demanding.  A layer this absolutely difficult to get right from a design point of view.  But from the programmatic layer, is trivial for a modern application server such as Django, Rails, or Express.

Introduction To Version 2.0, Modern Edition.

When I wrote the original Backbone Store, I didn’t have a clear understanding of how Backbone worked.  I wrote it to teach myself Backbone, after all.  Since then, I’ve come to understand that there is some Javascript about which I didn’t have the world’s clearest understanding.  This version of the store clears up those misconceptions and illustrates a clear separation between the data layer and the presentation layer.  Because of this clarity, it looks much less like the original JSON store tutorial for Sammy.js.

This particular document shows my current work kit.  I have provided a version of The Backbone Store 2.0 for main-line coders who still use Javascript, HTML and CSS with Backbone, but after doing so I decided to re-write all of my code to show how I really work, with Coffeescript, HAML, Stylus, and Backbone.  This is the result.

CoffeeScript is a lovely little languange that compiles into Javascript. It provides a class-based architecture (that is compatible with Backbone), has an elegant structure for defining functions and methods, and strips out as much extraneous punctuation as possible. Some people find the whitespace-as-semantics a’la Python offputting, but most disciplined developers already indent appropriately and find using Coffeescript a breeze.

HAML is a languange that compiles into HTML. Like CoffeeScript, it uses whitespace for semantics: indentation levels correspond to HTML containerizations. It allows you to use rich scripting while preventing heirarchy misplacement mistakes. Its shorthand also makes writing HTML much faster.

Stylus is languange that compiles into CSS. Like CoffeeScript and HAML, it uses whitespace for semantics.  (If you’re detecting a theme here, you should know by now that I’m a Python partisan.)  It also provides mixins and functions that allow you to define visual styles such as borders and gradients, and mix them into specific selectors in the CSS rather than having to write them into the HTML.

Backbone.js is a popular Model-View-Controller (MVC) library that provides a framework for creating data-rich, single-page web applications. It provides (1) a two-layer scheme for separating data from presentation, (2) a means of automatically synchronizing data with a server in a RESTful manner, and (3) a mechanism for making some views bookmarkable and navigable.

There are a number of other good tutorials for Backbone (See: Meta Cloud, &Yet’s Tutorial, Backbone Mobile (which is written in Coffee), and Backbone and Django. However, a couple of months ago I was attempting to learn Sammy.js, a library very similar to Backbone, and they had a nifty tutorial called The JsonStore.

In the spirit of The JSON Store, I present The Backbone Store, Modern Edition.

Literate Program

A note: this article was written with the Literate Programming toolkit Noweb. Where you see something that looks like <<this>>, it’s a placeholder for code described elsewhere in the document. Placeholders with an equal sign at the end of them indicate the place where that code is defined. The link (U->) indicates that the code you’re seeing is used later in the document, and (<-U) indicates it was used earlier but is being defined here.

Revision

This is version 2.0 of The Backbone Store. It includes changes to the store based upon a better understanding of what Backbone.js can do. This version uses jQuery 1.6.2 and Backbone 0.5.2.

The Store

To demonstrate the basics of Backbone, I’m going to create a simple one-page application, a store for record albums, with two unique views: a list of all products and a product detail view. I will also put a shopping cart widget on the page that shows the user how many products he or she has dropped into the cart. I’ll use jQuery’s fadeIn() and fadeOut() features to transition between the catalog and the product detail pages.

Models, Collections, and Controllers

Backbone’s data layer provides two classes, Model and Collection. To use the Model, you inherit from it, modify the subclasss as needed, and then create new objects from the subclass by constructing the model with a JSON object. You modify the object by calling get() or set() on named attributes, rather than on the Model object directly; this allows Model to notify other interested objects that the object has been changed. And Model comes with fetch() and save() methods that will automatically pull or push a JSON representatino of the model to a server, if the Model has url as one of its attributes.

Collections are just that: lists of objects of a specific model. You extend the Collection class in a child class, and as you do you inform the Collection of what Model it represents, what URL you use to push/pull the full list of objects, and on what field the list should be sorted by default. If you attempt to add a raw JSON object to a collection, it constructs a corresponding Model object out of the JSON and manipulates that.

I will be getting the data from a simplified JSON file that comes in the download; it contains six record albums that the store sells. (Unlike the JSON store, these albums do not exist; the covers were generated during a round of The Album Cover Game, a meme one popular with graphic designers.)

For our purposes, then, we have a Product and a ProductCollection. A popular convention in Backbone is to use concrete names for models, and NameCollection for the collection.

Models are duck-typed by default; they do not care what you put into them. So all I need to say is that a Product is-a Model. The Collection is straightforward as well; I tell it what model it represents, override the initialize() method (which is empty in the Backbone default) to inform this Collection that it has a url, and create the comparator function for default sorting.

Note that Coffeescript uses ‘@’ to represent this, and always returns the last lvalue generated by every function and method. So the last line of initialize below compiles to return this.

<product models>= (U->)
class Product extends Backbone.Model

class ProductCollection extends Backbone.Collection
    model: Product

    initialize: (models, options) ->
        @url = options.url
        @

    comparator: (item) ->
        item.get('title')

For the shopping cart, our cart will hold Items, and the cart itself will be an ItemCollection. Shoppings carts are a little odd; the convention is that an Item is not a single instance of a product, but a reference to the products and a quantity.

One thing we will be doing is changing the quantity, so I have provided a convenience function for the Item that allows you to do that. Now, no client classes such as Views need to know how the quantity is updated.

Also, it would be nice to know the total price of the Item.

<cart models>= (U->) [D->]
class Item extends Backbone.Model
    update: (amount) ->
        if amount == @get('quantity')
            return
        @set {quantity: amount}, {silent: true}
        @collection.trigger('change', this)

    price: () ->
        @get('product').get('price') * @get('quantity')

The ItemCollection is a little trickier. It is entirely client-side; it has no synchronization with the backend at all. But it does have a model.

The ItemCollection must be able to find an Item in the cart to update when a view needs it. If the Item is not in the Collection, it must create one. The method getOrCreateItemForProduct does this. It uses the detect() method, a method Collection inherits from Backbone’s one dependency, Underscore.js; detect() returns the first Item in the ItemCollection for which the function returns true. Also, when I have to create a new Item, I want to add it to the collection, and I pass the parameter silent, which prevents the Collection from notifying event subscribers that the collection has changed. Since this is an Item with zero objects in it, this is not a change to what the collection represents, and I don’t want Views to react without having to.

Finally, I add two methods that return the total count of objects in the collection (not Items, but actual Products), and the total cost of those items in the cart. The Underscore method reduce() does this by taking a function for adding progressive items, and a starting value.

<cart models>+= (U->) [<-D]
class ItemCollection extends Backbone.Collection
    model: Item

    getOrCreateItemForProduct: (product) ->
        pid = product.get('id')
        i = this.detect (obj) -> (obj.get('product').get('id') == pid)
        if (i)
            return i
        i = new Item
            product: product
            quantity: 0
        @add i, {silent: true}
        i

    getTotalCount: () ->
        addup = (memo, obj) -> obj.get('quantity') + memo
        @reduce addup, 0

    getTotalCost: () ->
        addup = (memo, obj) ->obj.price() + memo
        @reduce(addup, 0);

Views

Backbone Views are simple policy objects. They have a root DOM element, the contents of which they manipulate and to which they listen for events, and a model or collection they represent within that element. Views are not rigid; it’s just Javascript and the DOM, and you can hook external events as needed.

More importantly, a View is sensitive to events within its model or collection, and can respond to changes automatically. This way, if you have a rich data ecosystem, when changes to one data item results in a cascade of changes throughout your datasets, the views will receive “change” events and can update themselves accordingly.

I will show how this works with the shopping cart widget.

To achieve the fadeIn/fadeOut animations and enforce consistency, I’m going to do some basic object-oriented programming. I’m going to create a base class that contains knowledge about the main area into which all views are rendered, and that manages these transitions.

With this technique, you can do lots of navigation-related tricks: you can highlight where the user is in breadcrumb-style navigation; you can change the class and highlight an entry on a nav bar; you can add and remove tabs from the top of the viewport as needed.

<base view>= (U->) [D->]
class _BaseView extends Backbone.View
    parent: $('#main')
    className: 'viewport'

The above says that I am creating a class called BaseView and defining two fields. The first, ‘parent’, will be used by all child views to identify into which DOM object the View root element will be rendered. The second defines a common class we will use for the purpose of identifying these views to jQuery. Backbone automatically creates a new DIV object with the class ‘viewport’ when a view constructor is called. It will be our job to attach that DIV to the DOM. In the HTML, you will see the DIV\#main object where most of the work will be rendered.

<base view>+= (U->) [<-D->]
    initialize: () ->
        @el = $(@el)
        @el.hide()
        @parent.append(@el)
        @


The method above ensures that the element is rendered, but not visible, and contained within the DIV\#main. Note also that the element is not a sacrosanct object; the Backbone.View is more a collection of standards than a mechanism of enforcement, and so defining it from a raw DOM object to a jQuery object will not break anything.

Next, we will define the hide and show functions.

Note that in coffeescript, the => operator completely replaces the _.bind() function provided by underscore.

<base view>+= (U->) [<-D]
    hide: () ->
        if not @el.is(':visible')
            return null
        promise = $.Deferred (dfd) => @el.fadeOut('fast', dfd.resolve)
        promise.promise()

    show: () ->
        if @el.is(':visible')
            return

        promise = $.Deferred (dfd) => @el.fadeIn('fast', dfd.resolve)
        promise.promise()

Deferred is a new feature of jQuery. It is a different mechanism for invoking callbacks by attaching attributes and behavior to the callback function. By using this, we can say thing like When everything is hidden (when every deferred returned from hide has been resolved), then show the appropriate viewport.” Deferreds are incredibly powerful, and this is a small taste of what can be done with them.

Before we move on, let’s take a look at the HAML we’re going to use for our one-page application. The code below compiles beautifully into the same HTML seen in the original Backbone Store.

<index.haml>=
!!! 5
%html{:xmlns => "http://www.w3.org/1999/xhtml"}
  %head
    %title The Backbone Store
    %link{:charset => "utf-8", :href => "jsonstore.css", :rel => "stylesheet", :type => "text/css"}/
    <product list template>
    <product detail template>
    <cart template>
    </head>
  %body
    #container
      #header
        %h1
          The Backbone Store
        .cart-info
      #main
    %script{:src => "jquery-1.6.2.min.js", :type => "text/javascript"}
    %script{:src => "underscore.js", :type => "text/javascript"}
    %script{:src => "backbone.js", :type => "text/javascript"}
    %script{:src => "store.js", :type => "text/javascript"}

It’s not much to look at, but already you can see where that DIV\#main goes, as well as where we are putting our templates. The DIV\#main will host a number of viewports, only one of which will be visible at any given time.

Our first view is going to be the product list view, named, well, guess. Or just look down a few lines.

This gives us a chance to discuss one of the big confusions new Backbone users frequently have: What is render() for?. Render is not there to show or hide the view. Render() is there to change the view when the underlying data changes. It renders the data into a view. In our functionality, we use the parent class’s show() and hide() methods to actually show the view.

That call to \_super\_ is a Backbone idiom for calling a method on the parent object. It is, as far as anyone knows, the only way to invoke a superclass method if it has been redefined in a subclass. It is rather ugly, but useful.

<product list view>= (U->)
class ProductListView extends _BaseView
    id: 'productlistview'
    template: $("#store_index_template").html()

    initialize: (options) ->
        @constructor.__super__.initialize.apply @, [options]
        @collection.bind 'reset', _.bind(@render, @)

    render: () ->
        @el.html(_.template(@template, {'products': @collection.toJSON()}))
        @


That _.template() method is provided by undescore.js, and is a full-featured, javascript-based templating method. It’s not the fastest or the most feature-complete, but it is more than adequate for our purposes and it means we don’t have to import another library. It vaguely resembles ERB from Rails, so if you are familiar with that, you should understand this fairly easily.

And here is the HAML:

<product list template>= (<-U)
%script#store_index_template(type="text/x-underscore-tmplate")
  %h1 Product Catalog
  %ul
    <% for(i=0,l=products.length;i<l;++i) { p = products[i]; %>
    %li.item
      .item-image
        %a{:href => "#item/<%= p.id %>"}
          %img{:src => "<%= p.image %>", :alt => "<%= p.title %>"}/
      .item-artist <%= p.artist %>
      .item-title <%= p.title %>
      .item-price $<%= p.price %>
    <% } %>


One of the most complicated objects in our ecosystem is the product view. It actually does something! The prefix ought to be familiar, but note that we are again using \#main as our target; we will be showing and hiding the various DIV objects in \#main again and again.

The only trickiness here is twofold: the (rather hideous) means by which one calls the method of a parnt class from a child class via Backbone’s class heirarchy (this is most definitely not Javascript standard), and keeping track of the itemcollection object, so we can add and change items as needed.

<product detail view>= (U->) [D->]
class ProductView extends _BaseView
    id: 'productitemview'
    template: $("#store_item_template").html()
    initialize: (options) ->
            @constructor.__super__.initialize.apply @, [options]
            @itemcollection = options.itemcollection
            @item = @itemcollection.getOrCreateItemForProduct @model
            @

There are certain events in which we’re interested: keypresses and clicks on the update button and the quantity form. (Okay, “UQ” isn’t the best for “update quantity”. I admit that.) Note the peculiar syntax of “EVENT SELECTOR”: “methodByName” for each event.

Backbone tells us that the only events it can track by itself are those that jQuery’s “delegate” understands. As of 1.5, that seems to be just about all of them.

<product detail view>+= (U->) [<-D->]
    events:
        "keypress .uqf" : "updateOnEnter"
        "click .uq"     : "update"

And now we will deal with the update. This code ought to be fairly readable: the only specialness is that it’s receiving an event, and we’re “silencing” the call to cart.add(), which means that the cart collection will not publish any events. There are only events when the item has more than zero, and that gets called on cart_item.update().

In the original tutorial, this code had a lot of responsibility for managing the shopping cart, looking into it and seeing if it had an item for this product, and there was lots of accessing the model to get its id and so forth. All of that has been put into the shopping cart model, which is where it belongs: knowledge about items and each item’s relationship to its collection belongs in the collection.

Look closely at the update() method. The reference @\$ is a special Backbone object that limits selectors to objects inside the element of the view. Without it, jQuery would have found the first input field of class ‘uqf’ in the DOM, not the one for this specific view. @\$('.uqf') is shorthand for $('uqf', @el), and helps clarify what it is you’re looking for.

<product detail view>+= (U->) [<-D->]
    update: (e) ->
        e.preventDefault()
        @item.update parseInt(@$('.uqf').val())

    updateOnEnter: (e) ->
        if (e.keyCode == 13)
            @update e


The render is straightforward:

<product detail view>+= (U->) [<-D]
    render: () ->
        @el.html(_.template(@template, @model.toJSON()))
        @

The product detail template is fairly straightforward. There is no underscore magic because there are no loops.

<product detail template>= (<-U)
%script#store_item_template(type= "text/x-underscore-template")
  .item-detail
    .item-image
      %img(src="<%= large_image %>" alt="<%= title %>")/
    .item-info
      .item-artist <%= artist %>
      .item-title <%= title %>
      .item-price $<%= price %>
      .item-form
      %form(action="#/cart" method="post")
        %p
          %label Quantity:
          %input(type="text" size="2" name="quantity" value="1" class="uqf")/
        %p
          %input(type="submit" value="Add to Cart" class="uq")/

      .item-link
        %a(href="<%= url %>") Buy this item on Amazon
      .back-link
        %a(href="#") &laquo; Back to Items

So, let’s talk about that shopping cart thing. We’ve been making the point that when it changes, when you call item.update within the product detail view, any corresponding subscribing views sholud automatically update.

<cart widget>= (U->) [D->]
class CartWidget extends Backbone.View
    el: $('.cart-info')
    template: $('#store_cart_template').html()

    initialize: () ->
        @collection.bind('change', _.bind(@render, @));


And there is the major magic. CartWidget will be initialized with the ItemCollection; when there is any change in the collection, the widget will receive the ‘change’ event, which will automatically trigger the call to the widget’s render() method.

The render method will refill that widget’s HTML with a re-rendered template with the new count and cost, and then wiggle it a little to show that it did changed:

<cart widget>+= (U->) [<-D]
    render: () ->
        tel = @el.html _.template @template,
            'count': @collection.getTotalCount()
            'cost': @collection.getTotalCost()
        tel.animate({paddingTop: '30px'}).animate({paddingTop: '10px'})
        @

And the HTML for the template is dead simple:

<cart template>= (<-U)
%script#store_cart_template(type="text/x-underscore-template")
  %p Items: <%= count %> ($<%= cost %>)


Lastly, there is the Router. In Backbone, the Router is a specialized View for invoking other views. It listens for one specific event: when the window.location.hash object, the part of the URL after the hash symbol, changes. When the hash changes, the Router invokes an event handler. The Router, since its purpose is to control the major components of the one-page display, is also a good place to keep all the major components of the sytem. We’ll keep track of the Views, the ProductCollection, and the ItemCollection.

<router>= (U->) [D->]
class BackboneStore extends Backbone.Router
    views: {}
    products: null
    cart: null

There are two events we care about: view the list, and view a detail. They are routed like this:

<router>+= (U->) [<-D->]
    routes:
        "": "index"
        "item/:id": "product"

Like most Backbone objects, the Router has an initialization feature. I create a new, empty shopping cart and corresponding cart widget, which doesn’t render because it’s empty. I then create a new ProductCollection and and corresponding ProductListView. These are all processes that happen immediately.

What does not happen immediately is the fetch() of data from the back-end server. For that, I use the jQuery deferred again, because fetch() ultimately returns the results of sync(), which returns the result of an ajax() call, which is a deferred.

<router>+= (U->) [<-D->]
    initialize: (data) ->
        @cart = new ItemCollection()
        new CartWidget
            collection: @cart

        @products = new ProductCollection [],
            url: 'data/items.json'
        @views =
            '_index': new ProductListView
                collection: @products
        $.when(@products.fetch({reset: true}))
            .then(() -> window.location.hash = '')
        @


There are two things to route to, but we must also route from. Remember that our two major views, the product list and the product detail, inherited from \_BaseView, which has the hide() and show() methods. We want to hide all the views, then show the one invoked. First, let’s hide every view we know about. hide() returns either a deferred (if the object is being hidden) or null. The _.select() call at the end means that this method returns only an array of deferreds.

<router>+= (U->) [<-D->]
    hideAllViews: () ->
        _.select(_.map(@views, (v) -> return v.hide()),
            (t) -> t != null)

Showing the product list view is basically hiding everything, then showing the index:

<router>+= (U->) [<-D->]
    index: () ->
        view = @views['_index']
        $.when(@hideAllViews()).then(() -> view.show())


On the other hand, showing the product detail page is a bit trickier. In order to avoid re-rendering all the time, I am going to create a view for every product in which the user shows interest, and keep it around, showing it a second time if the user wants to see it a second time.

Not that we pass it the ItemCollection instance. It uses this to create a new item, which (if you recall from our discussion of getOrCreateItemForProduct()) is automagically put into the collection as needed. Which means all we need to do is update this item and the item collection changes, which in turn causes the CartWidget to update automagically as well.

<router>+= (U->) [<-D]
    product: (id) ->
        product = @products.detect (p) -> p.get('id') == (id)
        view = (@views['item.' + id] ||= new ProductView(
            model: product,
            itemcollection: @cart
        ).render())
        $.when(@hideAllViews()).then(
            () -> view.show())


Finally, we need to start the program

<initialization>= (U->)
$ ->
    new BackboneStore()
    Backbone.history.start()

 

The Program

Here’s the entirety of the program. Coffeescript provides its own namespace wrapper:

<store.coffee>=
<product models>

<cart models>

<base view>

<product list view>

<product detail view>

<cart widget>

<router>

<initialization>

Compiling

None of these formats are native to the browser, so getting them into the browser requires a compilation step. Here are the basic compilation steps for these three pieces of code:

haml --unix-newlines --no-escape-attrs --double-quote-attribute index.haml > index.html
coffee -compile store.coffee
stylus jsonstore.styl

I hope in the future to provide a Cakefile (a makefile alternative written in Cake) for your amusement.

A Little Stylus

Stylus is a beautiful little language that compiles down to CSS. The original version of The Backbone Store used the same CSS provided from the original Sammy tutorial, but I wanted to show you this one extra tool because it’s an essential part of my kit.

If you want rounded borders, you know that writing all that code, for older browsers as well as modern ones, and providing it to all the different objects you want styled that way, can be time consuming. Stylus allows you to define a function that can be called from within any style, thus allowing you to define the style here, and attach a set style to a semantic value in your HTML:

<jsonstore.styl>= [D->]
rounded(radius)
  -moz-border-radius-topleft: radius
  -moz-border-radius-topright: radius
  -moz-border-radius-bottomleft: radius
  -moz-border-radius-bottomright: radius
  -webkit-border-bottom-right-radius: radius
  -webkit-border-top-left-radius: radius
  -webkit-border-top-right-radius: radius
  -webkit-border-bottom-left-radius: radius
  border-bottom-right-radius: radius
  border-top-left-radius: radius
  border-top-right-radius: radius
  border-bottom-left-radius: radius

background_gradient(base)
  background: base
  background: -webkit-gradient(linear, left top, left bottom, from(lighten(base, 20%)), to(darken(base, 20%)))
  background: -moz-linear-gradient(top,  lighten(base, 20%), darken(base, 20%))

And if you look down below you’ll see the rounded() function called for the list items, which have borders.

One of the real beauties of Stylus is that you can contains some style definitions within others. You can see below that the header contains an H1, and the H1 definitions will be compiled to only apply within the context of the header. Stylus allows you to write CSS the way you write HTML!

<jsonstore.styl>+= [<-D]
body
  font-family: "Lucida Grande", Lucida, Helvetica, Arial, sans-serif
  background: #FFF
  color: #333
  margin: 0px
  padding: 0px

#header
  background_gradient(#999)
  margin: 0px
  padding: 20px
  border-bottom: 1px solid #ccc

  h1
    font-family: Inconsolata, Monaco, Courier, mono
    color: #FFF
    margin: 0px

  .cart-info
    position: absolute
    top: 0px
    right: 0px
    text-align: right
    padding: 10px
    background_gradient(#555)
    color: #FFF
    font-size: 12px
    font-weight: bold

img
  border: 0

#productlistview
  ul
    list-style: none

.item
  float:left
  width: 250px
  margin-right: 10px
  margin-bottom: 10px
  padding: 5px
  rounded(5px)
  border: 1px solid #ccc
  text-align:center
  font-size: 12px

.item-title
  font-weight: bold

.item-artist
  font-weight: bold
  font-size: 14px

.item-detail
  margin: 10px 0 0 10px

  .item-image
    float:left
    margin-right: 10px

  .item-info
    padding: 100px 10px 0px 10px

And that’s it. Put it all together, and you’ve got yourself a working Backbone Store.

This code is available at my github at The Backbone Store, modern branch.  This branch includes the current Makefile, as well as the native noweb document.

Introduction To Version 2.0

When I wrote the original Backbone Store, I didn’t have a clear understanding of how Backbone worked.  I wrote it to teach myself Backbone, after all.  Since then, I’ve come to understand that there is some Javascript about which I didn’t have the world’s clearest understanding.  This version of the store clears up those misconceptions and illustrates a clear separation between the data layer and the presentation layer.  Because of this clarity, it looks much less like the original JSON store tutorial for Sammy.js.

Backbone.js is a popular Model-View-Controller (MVC) library that provides a framework for creating data-rich, single-page web applications. It provides (1) a two-layer scheme for separating data from presentation, (2) a means of automatically synchronizing data with a server in a RESTful manner, and (3) a mechanism for making some views bookmarkable and navigable.

There are a number of other good tutorials for Backbone (See: Meta Cloud, &Yet’s Tutorial, Backbone Mobile (which is written in Coffee), and Backbone and Django. However, a couple of months ago I was attempting to learn Sammy.js, a library very similar to Backbone, and they had a nifty tutorial called The JsonStore.

In the spirit of The JSON Store, I present The Backbone Store.

Literate Program

A note: this article was written with the Literate Programming toolkit Noweb. Where you see something that looks like <<this>>, it’s a placeholder for code described elsewhere in the document. Placeholders with an equal sign at the end of them indicate the place where that code is defined. The link (U->) indicates that the code you’re seeing is used later in the document, and (<-U) indicates it was used earlier but is being defined here.

Revision

This is version 2.0 of The Backbone Store. It includes changes to the store based upon a better understanding of what Backbone.js can do. This version uses jQuery 1.6.2 and Backbone 0.5.2.

The Store

To demonstrate the basics of Backbone, I’m going to create a simple one-page application, a store for record albums, with two unique views: a list of all products and a product detail view. I will also put a shopping cart widget on the page that shows the user how many products he or she has dropped into the cart. I’ll use jQuery’s fadeIn() and fadeOut() features to transition between the catalog and the product detail pages.

Models, Collections, and Controllers

Backbone’s data layer provides two classes, Model and Collection. To use the Model, you inherit from it, modify the subclasss as needed, and then create new objects from the subclass by constructing the model with a JSON object. You modify the object by calling get() or set() on named attributes, rather than on the Model object directly; this allows Model to notify other interested objects that the object has been changed. And Model comes with fetch() and save() methods that will automatically pull or push a JSON representatino of the model to a server, if the Model has url as one of its attributes.

Collections are just that: lists of objects of a specific model. You extend the Collection class in a child class, and as you do you inform the Collection of what Model it represents, what URL you use to push/pull the full list of objects, and on what field the list should be sorted by default. If you attempt to add a raw JSON object to a collection, it constructs a corresponding Model object out of the JSON and manipulates that.

I will be getting the data from a simplified JSON file that comes in the download; it contains six record albums that the store sells. (Unlike the JSON store, these albums do not exist; the covers were generated during a round of The Album Cover Game, a meme one popular with graphic designers.)

For our purposes, then, we have a Product and a ProductCollection. A popular convention in Backbone is to use concrete names for models, and NameCollection for the collection.

Models are duck-typed by default; they do not care what you put into them. So all I need to say is that a Product is-a Model. The Collection is straightforward as well; I tell it what model it represents, override the initialize() method (which is empty in the Backbone default) to inform this Collection that it has a url, and create the comparator function for default sorting.

<product models>= (U->)
    var Product = Backbone.Model.extend({})

    var ProductCollection = Backbone.Collection.extend({
        model: Product,

        initialize: function(models, options) {
            this.url = options.url;
        },

        comparator: function(item) {
            return item.get('title');
        }
    });

For the shopping cart, our cart will hold Items, and the cart itself will be an ItemCollection. Shoppings carts are a little odd; the convention is that an Item is not a single instance of a product, but a reference to the products and a quantity.

One thing we will be doing is changing the quantity, so I have provided a convenience function for the Item that allows you to do that. Now, no client classes such as Views need to know how the quantity is updated.

Also, it would be nice to know the total price of the Item.

<cart models>= (U->) [D->]
    var Item = Backbone.Model.extend({
        update: function(amount) {
            this.set({'quantity': amount}, {silent: true});
            this.collection.trigger('change', this);
        },
        price: function() {
            console.log(this.get('product').get('title'), this.get('quantity'));
            return this.get('product').get('price') * this.get('quantity');
        }
    });

The ItemCollection is a little trickier. It is entirely client-side; it has no synchronization with the backend at all. But it does have a model.

The ItemCollection must be able to find an Item in the cart to update when a view needs it. If the Item is not in the Collection, it must create one. The method getOrCreateItemForProduct does this. It uses the detect() method, a method Collection inherits from Backbone’s one dependency, Underscore.js; detect() returns the first Item in the ItemCollection for which the function returns true. Also, when I have to create a new Item, I want to add it to the collection, and I pass the parameter silent, which prevents the Collection from notifying event subscribers that the collection has changed. Since this is an Item with zero objects in it, this is not a change to what the collection represents, and I don’t want Views to react without having to.

Finally, I add two methods that return the total count of objects in the collection (not Items, but actual Products), and the total cost of those items in the cart. The Underscore method reduce() does this by taking a function for adding progressive items, and a starting value.

<cart models>+= (U->) [<-D]
    var ItemCollection = Backbone.Collection.extend({
        model: Item,

        getOrCreateItemForProduct: function(product) {
            var i,
            pid = product.get('id'),
            o = this.detect(function(obj) {
                return (obj.get('product').get('id') == pid);
            });
            if (o) {
                return o;
            }
            i = new Item({'product': product, 'quantity': 0})
            this.add(i, {silent: true})
            return i;
        },

        getTotalCount: function() {
            return this.reduce(function(memo, obj) {
                return obj.get('quantity') + memo; }, 0);
        },

        getTotalCost: function() {
            return this.reduce(function(memo, obj) {
                return obj.price() + memo; }, 0);
        }
    });

Views

Backbone Views are simple policy objects. They have a root DOM element, the contents of which they manipulate and to which they listen for events, and a model or collection they represent within that element. Views are not rigid; it’s just Javascript and the DOM, and you can hook external events as needed.

More importantly, a View is sensitive to events within its model or collection, and can respond to changes automatically. This way, if you have a rich data ecosystem, when changes to one data item results in a cascade of changes throughout your datasets, the views will receive “change” events and can update themselves accordingly.

I will show how this works with the shopping cart widget.

To achieve the fadeIn/fadeOut animations and enforce consistency, I’m going to do some basic object-oriented programming. I’m going to create a base class that contains knowledge about the main area into which all views are rendered, and that manages these transitions.

With this technique, you can do lots of navigation-related tricks: you can highlight where the user is in breadcrumb-style navigation; you can change the class and highlight an entry on a nav bar; you can add and remove tabs from the top of the viewport as needed.

<base view>= (U->) [D->]
    var _BaseView = Backbone.View.extend({
        parent: $('#main'),
        className: 'viewport',

The above says that I am creating a class called BaseView and defining two fields. The first, ‘parent’, will be used by all child views to identify into which DOM object the View’s root element will be rendered. The second defines a common class we will use for the purpose of identifying these views to jQuery. Backbone automatically creates a new DIV object with the class ‘viewport’ when a view constructor is called. It will be our job to attach that DIV to the DOM. In the HTML, you will see the DIV\#main object where most of the work will be rendered.

<base view>+= (U->) [<-D->]
        initialize: function() {
            this.el = $(this.el);
            this.el.hide();
            this.parent.append(this.el);
            return this;
        },


The method above ensures that the element is rendered, but not visible, and contained within the DIV\#main. Note also that the element is not a sacrosanct object; the Backbone.View is more a collection of standards than a mechanism of enforcement, and so defining it from a raw DOM object to a jQuery object will not break anything.

Next, we will define the hide and show functions:

<base view>+= (U->) [<-D]
        hide: function() {
            if (this.el.is(":visible") === false) {
                return null;
            }
            promise = $.Deferred(_.bind(function(dfd) {
                this.el.fadeOut('fast', dfd.resolve)}, this));
            return promise.promise();
        },

        show: function() {
            if (this.el.is(':visible')) {
                return;
            }
            promise = $.Deferred(_.bind(function(dfd) {
                this.el.fadeIn('fast', dfd.resolve) }, this))
            return promise.promise();
        }
    });

Deferred is a new feature of jQuery. It is a different mechanism for invoking callbacks by attaching attributes and behavior to the callback function. By using this, we can say thing like “When everything is hidden (when every deferred returned from hide has been resolved), then show the appropriate viewport.” Deferreds are incredibly powerful, and this is a small taste of what can be done with them.

Before we move on, let’s take a look at the HTML we’re going to use for our one-page application:

<index.html>=
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title>
            The Backbone Store
        </title>
        <link rel="stylesheet" href="jsonstore.css" type="text/css">
        <product list template>
        <product detail template>
        <cart template>
    </head>
    <body>
        <div id="container">
            <div id="header">
                <h1>
                    The Backbone Store
                </h1>

                <div class="cart-info">
                </div>
            </div>

            <div id="main"> </div>
        </div>
        <script src="jquery-1.6.2.min.js" type="text/javascript"></script>
        <script src="underscore.js" type="text/javascript"></script>
        <script src="backbone.js" type="text/javascript"></script>
        <script src="store.js" type="text/javascript"></script>
    </body>
</html>

It’s not much to look at, but already you can see where that DIV\#main goes, as well as where we are putting our templates. The DIV\#main will host a number of viewports, only one of which will be visible at any given time.

Our first view is going to be the product list view, named, well, guess. Or just look down a few lines.

This gives us a chance to discuss one of the big confusions new Backbone users frequently have: What is render() for?. Render is not there to show or hide the view. Render() is there to change the view when the underlying data changes. It renders the data into a view. In our functionality, we use the parent class’s show() and hide() methods to actually show the view.

That call to \_super\_ is a Backbone idiom for calling a method on the parent object. It is, as far as anyone knows, the only way to invoke a superclass method if it has been redefined in a subclass. It is rather ugly, but useful.

<product list view>= (U->)
    var ProductListView = _BaseView.extend({
        id: 'productlistview',
        template: $("#store_index_template").html(),

        initialize: function(options) {
            this.constructor.__super__.initialize.apply(this, [options])
            this.collection.bind('reset', _.bind(this.render, this));
        },

        render: function() {
            this.el.html(_.template(this.template,
                                    {'products': this.collection.toJSON()}))
            return this;
        }
    });


That _.template() method is provided by undescore.js, and is a full-featured, javascript-based templating method. It’s not the fastest or the most feature-complete, but it is more than adequate for our purposes and it means we don’t have to import another library. It vaguely resembles ERB from Rails, so if you are familiar with that, you should understand this fairly easily.

And here is the HTML:

<product list template>= (<-U)
<script id="store_index_template" type="text/x-underscore-tmplate">
  <h1>Product Catalog</h1>
  <ul>
    <% for(i=0,l=products.length;i<l;++i) { p = products[i]; %>
    <li class="item">
      <div class="item-image">
        <a href="#item/<%= p.id %>">
          <img alt="<%= p.title %>" src="<%= p.image %>" />
        </a>
      </div>
      <div class="item-artist"><%= p.artist %></div>
      <div class="item-title"><%= p.title %></div>
      <div class="item-price">$<%= p.price %></div>
    </li>
    <% } %>
  </ul>
</script>


One of the most complicated objects in our ecosystem is the product view. It actually does something! The prefix ought to be familiar, but note that we are again using \#main as our target; we will be showing and hiding the various DIV objects in \#main again and again.

The only trickiness here is twofold: the (rather hideous) means by which one calls the method of a parnt class from a child class via Backbone’s class heirarchy (this is most definitely not Javascript standard), and keeping track of the itemcollection object, so we can add and change items as needed.

<product detail view>= (U->) [D->]
    var ProductView = _BaseView.extend({
        id: 'productitemview',
        template: $("#store_item_template").html(),
        initialize: function(options) {
            this.constructor.__super__.initialize.apply(this, [options])
            this.itemcollection = options.itemcollection;
            this.item = this.itemcollection.getOrCreateItemForProduct(this.model);
            return this;
        },


There are certain events in which we’re interested: keypresses and clicks on the update button and the quantity form. (Okay, “UQ” isn’t the best for “update quantity”. I admit that.) Note the peculiar syntax of “EVENT SELECTOR”: “methodByName” for each event.

Backbone tells us that the only events it can track by itself are those that jQuery’s “delegate” understands. As of 1.5, that seems to be just about all of them.

<product detail view>+= (U->) [<-D->]
        events: {
            "keypress .uqf" : "updateOnEnter",
            "click .uq"     : "update",
        },

And now we will deal with the update. This code ought to be fairly readable: the only specialness is that it’s receiving an event, and we’re “silencing” the call to cart.add(), which means that the cart collection will not publish any events. There are only events when the item has more than zero, and that gets called on cart_item.update().

In the original tutorial, this code had a lot of responsibility for managing the shopping cart, looking into it and seeing if it had an item for this product, and there was lots of accessing the model to get its id and so forth. All of that has been put into the shopping cart model, which is where it belongs: knowledge about items and each item’s relationship to its collection belongs in the collection.

Look closely at the update() method. The reference this.\$ is a special Backbone object that limits selectors to objects inside the element of the view. Without it, jQuery would have found the first input field of class ‘uqf’ in the DOM, not the one for this specific view. this.\$('.uqf') is shorthand for $('uqf', this.el), and helps clarify what it is you’re looking for.

 

<product detail view>+= (U->) [<-D->]
        update: function(e) {
            e.preventDefault();
            this.item.update(parseInt(this.$('.uqf').val()));
        },

        updateOnEnter: function(e) {
            if (e.keyCode == 13) {
                return this.update(e);
            }
        },


The render is straightforward:

<product detail view>+= (U->) [<-D]
        render: function() {
            this.el.html(_.template(this.template, this.model.toJSON()));
            return this;
        }
    });

The product detail template is fairly straightforward. There is no underscore magic because there are no loops.

<product detail template>= (<-U)
<script id="store_item_template" type="text/x-underscore-template">
  <div class="item-detail">
    <div class="item-image">
      <img alt="<%= title %>" src="<%= large_image %>" />
    </div>
  </div>
  <div class="item-info">
    <div class="item-artist"><%= artist %></div>
    <div class="item-title"><%= title %></div>
    <div class="item-price">$<%= price %></div>
    <form action="#/cart" method="post">
      <p>
        <label>Quantity:</label>
        <input class="uqf" name="quantity" size="2" type="text" value="1" />
      </p>
      <p>
        <input class="uq" type="submit" value="Add to Cart" />
      </p>
    </form>
    <div class="item-link">
      <a href="<%= url %>">Buy this item on Amazon</a>
    </div>
    <div class="back-link">
      <a href="#">&laquo; Back to Items</a>
    </div>
  </div>
</script>

So, let’s talk about that shopping cart thing. We’ve been making the point that when it changes, when you call item.update within the product detail view, any corresponding subscribing views sholud automatically update.

<cart widget>= (U->) [D->]
    var CartWidget = Backbone.View.extend({
        el: $('.cart-info'),
        template: $('#store_cart_template').html(),

        initialize: function() {
            this.collection.bind('change', _.bind(this.render, this));
        },


And there is the major magic. CartWidget will be initialized with the ItemCollection; when there is any change in the collection, the widget will receive the ‘change’ event, which will automatically trigger the call to the widget’s render() method.

The render method will refill that widget’s HTML with a re-rendered template with the new count and cost, and then wiggle it a little to show that it did changed:

<cart widget>+= (U->) [<-D]
        render: function() {
            this.el.html(
                _.template(this.template, {
                    'count': this.collection.getTotalCount(),
                    'cost': this.collection.getTotalCost()
                })).animate({paddingTop: '30px'})
                .animate({paddingTop: '10px'});
        }
    });

And the HTML for the template is dead simple:

<cart template>= (<-U)
<script id="store_cart_template" type="text/x-underscore-template">
  <p>Items: <%= count %> ($<%= cost %>)</p>
</script>


Lastly, there is the Router. In Backbone, the Router is a specialized View for invoking other views. It listens for one specific event: when the window.location.hash object, the part of the URL after the hash symbol, changes. When the hash changes, the Router invokes an event handler. The Router, since its purpose is to control the major components of the one-page display, is also a good place to keep all the major components of the sytem. We’ll keep track of the Views, the ProductCollection, and the ItemCollection.

<router>= (U->) [D->]
    var BackboneStore = Backbone.Router.extend({
        views: {},
        products: null,
        cart: null,

There are two events we care about: view the list, and view a detail. They are routed like this:

<router>+= (U->) [<-D->]
        routes: {
            "": "index",
            "item/:id": "product",
        },

Like most Backbone objects, the Router has an initialization feature. I create a new, empty shopping cart and corresponding cart widget, which doesn’t render because it’s empty. I then create a new ProductCollection and and corresponding ProductListView. These are all processes that happen immediately.

What does not happen immediately is the fetch() of data from the back-end server. For that, I use the jQuery deferred again, because fetch() ultimately returns the results of sync(), which returns the result of an ajax() call, which is a deferred.

<router>+= (U->) [<-D->]
        initialize: function(data) {
            this.cart = new ItemCollection();
            new CartWidget({collection: this.cart});

            this.products = new ProductCollection([], {
                url: 'data/items.json'});
            this.views = {
                '_index': new ProductListView({
                    collection: this.products
                })
            };
            $.when(this.products.fetch({reset: true}))
                .then(function() { window.location.hash = ''; });
            return this;
        },


There are two things to route to, but we must also route from. Remember that our two major views, the product list and the product detail, inherited from \_BaseView, which has the hide() and show() methods. We want to hide all the views, then show the one invoked. First, let’s hide every view we know about. hide() returns either a deferred (if the object is being hidden) or null. The _.select() call at the end means that this method returns only an array of deferreds.

<router>+= (U->) [<-D->]
        hideAllViews: function () {
            return _.select(
                _.map(this.views, function(v) { return v.hide(); }),
                function (t) { return t != null });
        },

Showing the product list view is basically hiding everything, then showing the index:

<router>+= (U->) [<-D->]
        index: function() {
            var view = this.views['_index'];
            $.when(this.hideAllViews()).then(
                function() { return view.show(); });
        },


On the other hand, showing the product detail page is a bit trickier. In order to avoid re-rendering all the time, I am going to create a view for every product in which the user shows interest, and keep it around, showing it a second time if the user wants to see it a second time.

Not that we pass it the ItemCollection instance. It uses this to create a new item, which (if you recall from our discussion of getOrCreateItemForProduct()) is automagically put into the collection as needed. Which means all we need to do is update this item and the item collection changes, which in turn causes the CartWidget to update automagically as well.

<router>+= (U->) [<-D]
        product: function(id) {
            var product, v, view;
            product = this.products.detect(function(p) { return p.get('id') == (id); })
            view = ((v = this.views)['item.' + id]) || (v['item.' + id] = (
                new ProductView({model: product,
                                 itemcollection: this.cart}).render()));
            $.when(this.hideAllViews()).then(
                function() { view.show(); });
        }
    });


Finally, we need to start the program

<initialization>= (U->)
    $(document).ready(function() {
        new BackboneStore();
        Backbone.history.start();
    });

 

The Program

Here’s the entirety of the program:

<store.js>=
(function() {

<product models>

<cart models>

<base view>

<product list view>

<product detail view>

<cart widget>

<router>

<initialization>

}).call(this);

And that’s it. Put it all together, and you’ve got yourself a working Backbone Store.

This code is available at my github at The Backbone Store.

One of the things I like to use with CSS is percentages. Often, this goes a bit over the top; right now, I have a two column layout that looks something like this:

.leftcolumn {
    float: left;
    width: 22.340425%;
    margin-right: 1.06382%;
    overflow: hidden;
}

.rightcolumn {
    float: left;
    width: 76.59574%;
    margin-right: 0;
}

Which is about as “relative” as you can imagine. Combined with max-width and some judicious (and very sparse) HTML nesting, however, you can do some pretty nice resizing things before you have to go to the media-query and up–  or downsize your entire layout.

Unfortunately, if you want to use jQuery UI’s drag-and-drop feature, percentages suck. jQuery will take them literally, and apply them to the helper in accordance with the first common relatively positioned parent, which can be disastrous; I was ending up with draggables so wide they didn’t fit inside the drop zone.

Fortunately, there is a fix. Draggables have a list of options, one of which is “helper.” The most commonplace arguments to helper are “clone” and “original.” The first makes a copy of the dragged object; the second takes the dragged object out of its original flow and makes it mobile. Taking an object out of its flow, or making a copy and attaching it to a parent object, can have the aforementioned disastrous side effects. Helper can, however, take as an argument a function that returns a suitable object for dragging. By creating a copy of the original object, and assigning to it a hard width derived from the calculated width of the original, you can ensure that the helper’s rectangle matches your eye’s expectations. Like so:

$(el).draggable({
    'helper': function() { return $(this).clone().css('width', this.offsetWidth)[0]; }
    ... /* other options */
});

In this case, this refers to the object to be copied, obviously. jQuery passes it as context. This works for jQuery 1.5 and up, and jQuery UI 1.8.2 and up.

Frequently, when dealing with remote APIs, there’s a progression of states with a definitive order and an endpoint.  Often, when comparing the existing state to the proposed state, I want to make sure state is progressing in the correct direction.  The stupid simple version of this is something like:

ORDER_STATES = (
    ('PROCESSING', _("Processing")),
    ('APPROVED',   _("Approved")),
    ('PROCESSED',  _("Processed")),
    ('SHIPPED',    _("Shipped")),
    ('CANCELLED',  _("Cancelled")),
    ('DECLINED',   _("Card Declined")),
)
class StateIndexManager(object):
    def __init__(self, states):
        self.states = states
    def __call__(self, value):
        cp = [i[0] for i in self.states]
        return cp.index(value)
state_index = StateIndexManager(ORDER_STATES)

This is obviously for a shopping cart, my arch nemesis. (Actually, every programmer’s arch nemesis. Shopping carts are teh evulzh).  With something like this, you can create code in a Django model that reads:

    def _get_status(self):
        return self.order_status
    def _set_status(self, value):
        if state_index(value) > state_index(self.order_status):
            self.order_status = value
    status = property(_get_status, _set_status)

Feel free to add a raise() in there if the state is going in the wrong direction.

I just find myself doing this often enough that having a “personal snippet” location seemed like a good idea.

I’m sorry about this, but I’ve deleted the JFP.  Almost nobody used it, the number of frameworks out there proliferated into madness while others sank beneath the waves (about the only reason prototype/scriptaculous holds on is because too many rails heads still need it), and it had become filled with hacks and spam.  I didn’t need that SEO headache.  So away it went.

One of the things I dislike most about some web frameworks is how they try to hide the machinery from your prying eyes.  In some cases, this can be downright necessary– the mind that invented Django’s ORM is clearly a twisted evil genius from Mars who knows more about metaprogramming than I do, although I have learned a lot from reading his code.  But the ORM isn’t hidden, it’s right there in the /db folder.

Kanso is a toolkit for rapidly building out Javascript applications that run on the CouchDB server.   I like the basic idea of it, and it comes with a primitive administrative interface that’s something of a cross between Django’s admin and a Rails scaffold– it gives you ready access to a document and its properties, as long as you’ve defined those types clearly The Kanso Way™.  I’m not sure I entirely like “The Kanso Way”; I’m still trying to figure out how to integrate deep, complex documents with the Kanso Type manager, and how that relates to client-side utilities like Backbone or Scriptcore.

But if you want to really understand what a design doc looks like, the perfect tool is Couch-Docs, a tool (written in Ruby, alas) that can dump your design docs, and then re-upload them, all in one nice, compete package.  It makes laying out a couchdb application an utter breeze.  Handholding is sometimes indistinguishable restraint, and that’s how I feel about Kanso and CouchApps in general.  Couch-Docs is CouchApp development for grownups.

Recent Comments