Batman.js is Shopify’s new open source CoffeeScript framework, and I’m absolutely elated to introduce it to the world after spending so much time on it. Find Batman on GitHub here.
Batman emerges into a world populated with extraordinary frameworks being used to great effect. With the incredible stuff being pushed out in projects like Sproutcore 2.0 and Backbone.js, how is a developer to know what to use when? There’s only so much time to play with cool new stuff, so I’d like to give a quick tour of what makes Batman different and why you might want to use it instead of the other amazing frameworks available today.
Batman makes building apps easy
Batman is a framework for building single page applications. It’s not a progressive enhancement or a single purpose DOM or AJAX library. It’s built from the ground up to make building awesome single page apps by implementing all the lame parts of development like cross browser compatibility, data transport, validation, custom events, and a whole lot more. We provide handy helpers for development to generate and serve code, a recommended app structure for helping you organize code and call it when necessary, a full MVC stack, and a bunch of extras, all while remaining less than 18k when gzipped. Batman doesn’t provide the basics, or the whole kitchen sink, but a fluid API that allows you to write the important code for your app and none of the boilerplate.
A super duper runtime
At the heart of Batman is a runtime layer used for manipulating data from objects and subscribing to events objects may emit. Batman’s runtime is used similarly to SproutCore’s or Backbone’s in that all property access and assignment on Batman objects must be done through
- transparently access “deep” properties which may be simple data or computed by a function,
- inherit said computed properties from objects in the prototype chain,
- subscribe to events like
readyon other objects at “deep” keypaths,
- and most importantly, dependencies can be tracked between said properties, so chained observers can be fired and computations can be cached while guaranteed to be up-to-date.
1 2 3 4
This kind of stuff is available in Backbone and SproutCore both, however we’ve tried to bring something we missed in those frameworks to Batman: “deep” keypaths. In Batman, any keypath you supply can traverse a chain of objects by separating the keys by a
. (dot). For example:
1 2 3 4 5 6 7 8 9 10
This works for observation too:
1 2 3
The craziest part of the whole thing is that these observers will always fire with the value of whatever is at that keypath, even if intermediate parts of the keypath change.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
Notice what happened? Even though the middle segment of the keypath changed (a whole new
crimeReport object was introduced), the observer fires with the new deep value. This works with arbitrary length keypaths as well as intermingled
The second neat part of the runtime is that because all access is done through
set, we can track dependencies between object properties which need to be computed. Batman calls these functions
accessors, and using the CoffeeScript executable class bodies they are really easy to define:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Importantly, the observers you may attach to these computed properties will fire as soon as you update their dependencies:
1 2 3
You can also define the default accessors which the runtime will fall back on if an object doesn’t already have an accessor defined for the key being
1 2 3 4
This feature is useful when you want to present a standard interface to an object, but work with the data in nontrivial ways underneath. For example,
What’s it useful for?
The core of Batman as explained above makes it possible to know when data changes as soon as it happens. This is ideal for something like client side views. They’re no longer static bundles of HTML that get cobbled together as a long string and sent to the client, they are long lived representations of data which need to change as the data does. Batman comes bundled with a view system which leverages the abilities of the property system.
A simplified version of the view for Alfred, Batman’s todo manager example application, lies below:
1 2 3 4 5 6 7 8 9 10 11 12 13
We chose to use bindings because we a) don’t want to have to manually check for changes to our data, and b) don’t want to have to re-render a whole template every time one piece of data changes. With mustache or
jQuery.tmpl and company, I end up doing both those things surprisingly often. It seems wasteful to re-render every element in a loop and pay the penalty for appending all those nodes, when only one key on one element changes, and we could just update that one node. SproutCore’s ‘SC.TemplateView’ with Yehuda Katz' Handlebars.js do a good job of mitigating this, but we still didn’t want to do all the string ops in the browser, and so we opted for the surgical precision of binding all the data in the view to exactly the properties we want.
What you end up with is a fast render with no initial loading screen, at the expense of the usual level of complex logic in your views. Batman’s view engine provides conditional branching, looping, context, and simple transforms, but thats about it. It forces you to write any complex interaction code in a packaged and reusable
Batman.View subclass, and leave the HTML rendering to the thing that does it the best: the browser.
Batman does more than this fancy deep keypath stuff and these weird HTML views-but-not-templates. We have a routing system for linking from quasi-page to quasi-page, complete with named segments and GET variables. We have a
Batman.Model layer for retrieving and sending data to and from a server which works out of the box with storage backends like Rails and
localStorage. We have other handy mixins for use in your own objects like
Batman.EventEmitter. And, we have a lot more on the ay. I strongly encourage you to check out the project website, the source on GitHub, or visit us in #batmanjs on freenode. Any questions, feedback, or patches will be super welcome, and we’re always open to suggestions on how we can make Batman better for you.
Until next time….
This article has been crossposted to the Shopify Technology blog here.