So at this point I’ve messed around with Sammy.js and Director.js. I looked at Backbone.js but think it does a lot more than what I think I need right now.
Back to Director.js: I left off yesterday playing with asynchronous routes and route recursion. Let’s take a closer look at both of these mechanisms.
Suppose we have a URL: http://blahblah.org/#/this/is/a/local/route
We can register a ‘route’ with director.js for the path ‘/this/is/a/local/route’ to receive a callback when document.location is set to this URL.
We can also separately register:
… and enable director.js’s ‘route recursion’ in order to receive a series of route callbacks instead of a single callback in response to navigating to http://blahblah.org/#/this/is/a/local/route. Director.js supports both forward and backwards ‘recursion’. If set to ‘forward recursion’ director.js will dispatch callbacks to the function registered for ‘/this’, then ‘/this/is’ … and so ending finally with the call to the function registered for the ‘/this/is/a/local/route’ path. When set to ‘reverse recursion’ the order of the callbacks is simply reversed.
To understand how this facility might be used consider a slightly different URL: http://blahblah.org/#/appcache/localstorage/appboot/apprun
- An ‘appcache’ object: monitors asynchronous events from browser appcache on page load
- A ‘localstorage’ object: responsible for initializing the local storage store, version, etc on page load.
- An ‘appboot’ object: responsible for creating a session, setting up the data model, view, bindings…
- An ‘apprun’ object: responsible for managing the runtime state of the application
Each object in the list has a dependency on the objects that appear above it. i.e. these objects must be constructed in the order shown.
Now consider that a user navigates to http://blahblah.org/#/appcache/localstorage/appboot/apprun
We could register a simple function callback for each portion of the route path, enable forward route recursion, and upon callback conditionally construct an object if it doesn’t already exist. In this way, we’re ensured that by the time that the registered callback function for ‘/appcache/localstorage/appboot/apprun‘ is dispatched that the app has been booted (i.e. the appboot object is constructed and initialized w/out err0r).
Building on the ‘/appcache/localstorage/appboot/apprun‘ example above, consider the ‘appcache’ object to be constructed by the registered callback for the ‘/appcache’ route. When application cache is used, page load checks the application manifest on the server, and may affect an update of the local application cache if the manifest has been changed. Until the application cache reaches a specified state indicated by an event callback, the application is not cached.
So we have to wait for the browser’s application cache to be audited / potentially updated before proceeding.
By default Director.js uses ‘synchronous routing’. In the recursive examples above this means that it will immediate dispatch the entire series of calls corresponding to registered subpaths of the route. This isn’t exactly what we need however: we do not want the ‘/appcache/localstorage’ callback to execute until after ‘/appcache’ callback has determined that the application cache is ready for use.
Enabling director.js ‘asynchronous routing’ is just a fancy way of saying that instead of unilaterally dispatching the entire series of callbacks, director.js will instead pass the next route in the series to it’s parent route callback, and the parent route can invoke the subroute when it’s ready.
So ‘/appcache’ would ‘continue’ the route by calling the registered route handler for ‘/appcache/localstorage’ passed to it by director.
Do we want/need an asynchronous router with recursion?
I’m beginning to think the answer is NO.
After messing around with this I think I’m going to bolt director.js into a Coffeescript class that simply handles the ‘notfound’ route synchronously and take entire job of parsing the local path into the main application logic.
This is probably a bit more work than taking a bigger dependency on the router. However, it’s ultimately more flexible.
One of the first systems I’m going to design using the Schema app is a router because I don’t think the world needs routing libraries at all. Routers are essentially just parsers whose input comes from the host browser and whose output is typically conveyed via callbacks to app logic. Parsers are just state machines. State machines can be specified and modeled in SCDL. SCDL can be transformed directly to highly optimized JS to implement your custom router. No router library.