Sammy 0.5: Something for Everyone
I’m pleased to announce the next major release of Sammy.js, 0.5.0. I’ll go over what I think are the most import changes, but you can check out the HISTORY or just cut to the chase and download it.
jQuery 1.4.1
One of the big events I was waiting for in order to release this new version was the release of jQuery 1.4.1. Besides being a good leap and jump faster then previous versions, the latest jQuery added support for cross-browser bubbling of the submit
event. This might not sound like a big deal, but Sammy handles post/put/delete
routes by binding to forms and their submit
events. In previous versions of Sammy, if a form was dynamically added to the page (i.e. by a partial
or get
route), you (or the app) would fire a changed
event, which would tell the app to search for all newly added forms and bind to their submit
events. Using the power of jQuery 1.4.1, Sammy can now just listen for submit events that will bubble up to the applications element and from their dispatch the route. In general this is a lot cleaner and will make it easier for beginners as it just works.
Chain of fools
From user requests, and to fulfill my own desires, I’ve tried make Sammy more adaptable to different styles of coding/app creation. The first thing I realized why not make Sammy app definitions chain-able? It’s a style of development that jQuery developers are already used to, and it looks pretty swanky, too. With 0.5, all the app modifying methods are now chain-able:
// classic style:
$.sammy(function() {
this.use(Sammy.Mustache);
this.use(Sammy.Session);
this.before(function() {
// …
});
this.get('#/', function() {
// …
});
this.bind('run', function() {
// …
});
})
// New chain style:
$.sammy()
.use(Sammy.Mustache)
.use(Sammy.Session)
.before(function() {
// .
})
.get('#/', function() {
// …
})
.bind('run', function() {
// …
});
Sammy()
Once just a namespace, Sammy()
is now a function (and its aliased to $.sammy). The Sammy()
method provides access to the new Sammy.apps object and hence allows you to access the different Sammy applications you’ve added to the page without having to define additional global vars. Finding and extending is all based on @element_selector@s so those should be unique per-application. It also provides a quicker syntax for defining an app bound to an element_selector
.
// Old way
// we're forced to make app a global so we can access it
// outside of this closure
app = $.sammy(function() {
this.element_selector = '#myapp';
//…
})
// equivalent to
app = new Sammy.Application(function() {
this.element_selector = '#myapp';
//…
});
// NEW WAY
$.sammy('#myapp', function() {
//…
});
// equivalent to Sammy('#myapp', function() {})
// No need to assign it, the app can be looked up by element selector
$.sammy('#myapp') //=> Sammy.Application
There are some cool use-cases for this, including being able to iterate over all the Sammy apps in play and extend them.
any
way you want it
Sammy 0.5 adds the new any
pseudo-verb which allows you to easily add a route that will match against any verb:
$.sammy(function() {
this.any('#/', function() {
this.log("I'm up for anything");
});
// also equivalent
this.route('#/', function() {});
});
This was actually the first step of another way to define Sammy applications. The new mapRoutes()
method allows you to define any number of routes as 2D Array.
$.sammy(function() {
this.runIndex = function() {
this.partial('index.template');
};
this.mapRoutes([
// each element in the array represents the arguments sent to route()
['get', '#/user', function() {
this.partial('user.template');
}],
// leaving out the verb assumes the verb is 'any'
[/\/alert$/, function() {
alert(this.params['alert']);
}],
// passing a string instead of a callback assigns the method with that
// name from the application.
['get', '#/', 'runIndex']
]);
});
I personally like the method syntax better, but there are some cool uses for this – You could even load your routes from multiple files and add them to the application using JSON.
Filters
before
filters are very useful for extracting common functionality out of your routes. However, I’ve found myself writing this pretty often:
$.sammy(function() {
this.before(function() {
// only run if the path is not the index
if (this.path != '#/') {
//… do something
}
})
});
Taking some hints from Rails, I’ve added options to before
filters. The above example could be rewritten:
$.sammy(function() {
this.before({except: '#/'}, function() {
//… do something
})
});
The filters can match against path
(as a string or RegExp) and verb
and you can define them in the positive (only
) or negative (except
). The method that does the matching is exposed as contextMatchesOptions
so you can use it outside of before()
I’ve also found myself doing a lot of wrapping entire route callbacks in an asynchronous check for something (usually a login). The new around
filters make this easy. around
takes a single argument which is a neatly packed function that contains the full execution path of the route being accessed. This includes additional filters and the route callback itself. This also means you can entirely halt the execution by never running the passed callback.
$.sammy(function() {
this.around(function(callback) {
// `this` here is the Sammy.EventContext
// just like before, etc
$.get('/session', function() {
callback();
});
});
})
Lots more
Theres actually a fair amount more to cover in Sammy 0.5, but I think I’ll be doing more short ‘Whats new’ posts soon. Check the HISTORY for a full rundown of changes.
Thanks
Special thanks goes out to everyone who helped with ideas, commits and testing for this release including Lena Herrmann, Jinzhu, dpree, Jeff O’Connell, Todd Willey, Tim Caswell, and Frank PrΓΆΓdorf (endor).
Really interesting… I’m gonna try it. Thanks!
P.S. The download link, “http://github.com/quirkey/sammy/tarball/v0.5.0”, is broken. I downloaded from “http://github.com/quirkey/sammy/tarball/0.5.0” (no ‘v’ before “0.5.0”). Bye!
Thanks for the catch! that was my mistake. I’ve re-pushed the tag as v0.5.0.
I love this library!
One suggestion for the next tutorial: how to do a set of tabs, such that each tab page is a Sammy route so tab changes get into the history – and can jQuery tabs be used for this?