Jun 01 2012

Shipyard 0.2

The 0.2 version of Shipyard is out and about. Besides the usual minor bug clean-up, I wanted to share all the brand new toys it’s got.

  1. ObservableArrays
  2. CollectionView and SelectView
  3. Model Relationships
  4. Logging

If you’re like, “Show me naow!”, you can play over on jsFiddle. Or maybe you like changelogs.


The major players in Shipyard (Models, Views, and Controllers) are all Observables, which is what makes live-data-binding to Views available. However, binding to data wouldn’t work well if that piece of data was an array (like an array of Models). The ObservableArray mimics an Array, but extends from Observable. So, you can observe any index on the array, or the length.

The most useful part is that you can observe the array property of the ObservableArray. This property event will always emit the index of the change, an array of the removed elements, and an array of the added elements.

var arr = new ObservableArray('Foo', 'Bar');
arr.observe('array', function(index, removed, added) {
    console.log(index, removed, added);

arr.shift(); // log: 0, ['Foo'], []
arr.push('Baz', 'Quux'); // log: 1, [], ['Baz', 'Quux']

CollectionView and SelectView

With the use of the ObservableArray, CollectionViews are a whole lot better. You no longer need to manually manage them by with addItem. If you create an ObservableArray, and pass it as content to the CollectionView, it will update it’s childViews whenever you modify the ObservableArray instance. You can also pass a regular array, and later on .get('content').push(myModel), and the CollectionView will update automatically.

var arr = new ObservableArray('Foo', 'Quux');
var list = new ListView({ content: arr });

arr.splice(1, 0, 'Bar', 'Baz');
// list view now has child views in order of
// 'Foo', 'Bar', 'Baz', 'Quux'

SelectView is a new CollectionView available, with an additional computed property, selected, to observe which value on the content array is selected.

Model Relationships

Two new Fields are available, ForeignKey and ManyToManyField, that allow you define relationships between models. ManyToManyFields also take advantage of ObservableArrays, letting you know when a Model’s list of related models change.

var tags = task.get('tags');
var tagsView = new TagList({ 
    content: tags,
    // TagList is fictional, so is onTagSelect
    onTagSelect: function(tag) {
        var index = tags.indexOf(tag);
        if (index >= 0) {
            tags.splice(index, 1);
            // Task.tags has this tag removed
            // and also automatically updates this ListView


The tiny logging utility has morphed into a serious logging module very similar to Python’s own logging module. It’s still very easy to use quickly:

require('shipyard/logging').info('an info message');

However, it also comes ready to customize completely. You can specify different formats, different log levels, and different handlers for various loggers. Specifically, you might only care about shipyard internal logging that is of level WARN and above, but want DEBUG and above from your own app. In addition, you might want to pass any and all ERROR logs to your server, so you can see whenever your JavaScript is causing errors on other computers.

It would all be something like this:

var config = require('shipyard/logging/config');
    handlers: {
        'console': {
            'class': ConsoleHandler
        'xhr': {
            'class': XHRHandler,
            'level': 'ERROR',
            'url': '/api/log'
    loggers: {
        'shipyard': {
            'handlers': ['console', 'xhr'],
            'level': 'WARN',
            'propagate': false
        'myapp': {
            'handlers': ['console', 'xhr'],
            'level': 'DEBUG',
            'propagate': false

Get it

It’s on GitHub, as always. It’s also available from npm. And it’s in the dropdown on jsFiddle. So yea.

  • #shipyard
  • #javascript
  • #programming
  • #planet
  • #mvc