Adding HoganJS to Brunch

Mustache

As we described in an ealier post, Brunch is really just a simple way to build and develop any HTML5 application. Part of that simplicity is rooted in how it handles compilation of files with plugins.

Brunch doesn’t care

When you build on top of Brunch, a plugin actually handles the compilation and minification of each file. Brunch is responsible for watching for changes and making sure the appropriate plugin is notified of any changes.

For example, when Brunch comes across a file with a .coffee extension, it sends the contents of that file to the Coffeescript plugin for compilation and includes the result in the final application.

Lets take a look at the Coffeescript plugin code:

coffeescript = require 'coffee-script'
module.exports = class CoffeeScriptCompiler
  brunchPlugin: yes
  type: 'javascript'
  extension: 'coffee' # Here's where we determine what files to handle
  compile: (data, path, callback) ->
    try
      result = coffeescript.compile data
    catch err
      error = err
    finally
      callback error, result

Now when we include any .coffee file in our project, the final result will be compiled automatically every time it changes. But this is a trivial example, lets try something a little more complex.

You’ve got some compilation in your mustache

While mustache is our preferred templating language, we also wanted to precompile the templates to Javascript, a capability found in both Handlebars and HoganJS.

Of course it’s a little more involved then that, because while they get precompiled and served as JS, they still rely on the HoganJS/Handlebars library to render in the browser.

Brunch has this handled as well, and we were able to quickly write a HoganJS plugin by adding an ‘include’ method to the module.

hogan = require 'hogan.js'
sysPath = require 'path'
module.exports = class HoganCompiler
  brunchPlugin: yes
  type: 'template'
  extension: 'mustache'

  # Returns a precompiled template with a 'render' function
  # Usage Example:
  # @$el.html(template({name: "mdp", city: "SF"}))
  compile: (data, path, callback) ->
    try
      content = hogan.compile data, asString: yes
      result = "module.exports = new Hogan.Template(#{content});"
    catch err
      error = err
    finally
      callback error, result

  # Add '../node_modules/hogan.js/web/builds/2.0.0/template-2.0.0.js'
  # to vendor files.
  include: ->
    [(sysPath.join __dirname, '..', 'node_modules', 'hogan.js', 'web', 'builds', '2.0.0', 'template-2.0.0.js')]

Now when we use HoganCompiler in a project, Brunch will ensure that the file ‘template-2.0.0.js’ is automatically included in the output JS, and all the templates will be precompiled by the Hogan.js node library. All in less than 30 lines of code.

Final thoughts

The Brunch docs give the best overview of how to build your own plugins, but like many thing looking at some actual example plugins helped immensely. Here’s a quick list of some important ones you’ll probably want to use at some point:

Javascript Compilers

CSS Compilers

Minifiers

Photo courtesy of cote

Published: Sunday, May 6 2012
Author: Mark Percival

Death of a hashbang

Meh

A lot has been written about the hashbang, so instead of starting up the debate again, I’m instead going to simply show how we killed it, and why we think it never mattered in the first place.

Background

The hashbang is no longer just something for web developers to argue about; it’s been implemented in a number of frameworks (most notably Backbone.js), and a number of large sites (Twitter being the best example). The idea is simple, in browsers that don’t support HTML pushstate, we revert to using an anchor (the hashbang - #!) in the URL to hold state.

So when you click on a link in Safari, you may see this in your URL bar:

http://mysite.com/posts/123

But inside IE, your URL would display:

http://mysite.com/#!posts/123

The hashbang in this case prevents the page from reloading, and allows the Backbone router to handle running the appropriate action. But it’s a hack, and as you’ll see, it’s not entirely necessary.

Do we even need it?

While the hashbang does allow for quicker and smoother page transitions for older browsers, you still must handle someone directly hitting the URL. For example, if I wind up on /posts/12345, I could expect to hit refresh and have the page correctly reload the content and view.

So do I even need hashbangs for IE, or can I just let them load the link as they normally would?

It’s all about the expectations

The downside of this is obvious, the page is going to completely re-render for an older browser. But this was always the case before pushstate came into existance. Most of your assets will be cached at this point, so at worst it’ll take the time to 304 and run the Backbone.js applicaiton. And most importantly, for a user on an older browser, this reload is already expected. You’re not making their experience worse, but you are improving the experience for other users.

The nitty gritty

First, we should prevent the Hashbang from ever occuring by overriding Backbone’s ‘loadUrl’ method (Hat tip to Michael Franzkowiak):

Backbone.History.prototype.loadUrl = (fragmentOverride) ->
  fragment = this.fragment = this.getFragment(fragmentOverride, 
                                              this._wantsPushState &&
                                              !this._wantsHashChange)
  matched = _.any this.handlers, (handler) ->
    if handler.route.test(fragment)
      handler.callback(fragment)
      return true
  matched

Then we need to find a way to make certain links pushstate capable.

  $(document).delegate "a.pushState", "click", (evt) ->
    # Get the anchor href and protocol
    href = $(this).attr("href")
    protocol = this.protocol + "//"

    # Ensure the protocol is not part of URL, meaning its relative.
    # Stop the event bubbling to ensure the link will not cause a page refresh.
    if href.substring(0, protocol.length) != protocol

      # Leading /'s break Backbone router nav
      if href.charAt(0) == '/'
        href = href.substr(1, href.length)
      if Backbone.history._hasPushState
        evt.preventDefault()
        app.router.navigate href, trigger: true
      else
        #Do Nothing

Now when an IE9 browser clicks on a link that has the class ‘pushState’ we simply allow the click to happen and the browser to load the new page. And likewise, when a pushState capable browser clicks the link, we prevent it from following it, and instead use Backbone’s router to navigate to the new view.

Should you implement it?

There’s no magic bullet here, and this isn’t going to end the debate on hashbangs. We’re deciding today to support the latest pushstate navigation at the expense of a slower (but expected) browsing experience for our IE users. And unlike starting with support for hashbangs, we can always add them back later, but we can never take them away.

Photo Credit Rick Harris

Published: Tuesday, April 3 2012
Author: Mark Percival

Sadly, A brunch that doesn't involve bacon

Brunch!!!

How it starts

When Snip.it first launched, we had a fairly typical Rails stack, with the majority of view logic taking place on the server side. And like many web applications, as the application grew, we came to increasingly rely on Javascript for user interactions.

Whether it’s a simple ajaxy button or a complicated piece of form validation, if you’re not careful, your oh-so-DRY code on the Rails side soon becomes filled with duplicated client side functionality.

A move to Backbone.js

For us, the solution seemed to be the easy choice of moving to a more structured JS framework like Backbone.js. Backbone would allow us to move all the view rendering to the client side, and allow Rails to handle formatting and controlling the data coming from the database. But when the time came to get started on the implementation we soon found ourselves having to make even more decisions about directory structure, compilation, minification and deployment.

If you’re familiar with Backbone.js, then you’re probably also familiar with the amazing Addy Osmani and his book Backbone Fundementals. The advanced section has a great breakdown on ways to structure your app, as well as how to go about setting up tools like Require.js. And while this is a a great guide to handling some of the finer points of Backbone.js development, we weren’t in the position to make these decisions this early on in our development.

Brunch to the rescue

At it’s core, Brunch is just a Node.js based build tool for developing and deploying HTML5 apps. But like Rails, when you create a new Brunch project, you get a pretty awesome set of defaults for building your application.

Brunch handles our four main areas of concern. Compiling server side code (eg. Coffeescript/Roy), wrapping multiple files as common.js modules, building/minifying the final output, and allowing team members to quickly get started developing.

Rolling this out to the team is as simple as having each member ‘npm install’ the latest version of brunch, and then executing the brunch CLI tool. And since it’s based on npm, you can even explicitly list out your dependencies in package.json much as you would in a Gemfile.

What you get is a single compiled JS file which includes all the libraries you depend on, your backbone application code, and the common.js wrappers. And while you’re developing, Brunch will watch for changes and automatically recompile the code.

Where do we go from here

This is a big topic, and we’ve decided to publish a series of blog posts about Brunch and our development process. In the next few weeks, we’re going to touch on the following issues.

  • Death of a hashbang
  • Building on top of the Brunch API
  • Migrating from Rails to Rails/Backbone.js
  • Playing with alternate setups like Chaplin

Resources for your perusal

Photo courtesy of Martin Cathrae

Published: Friday, March 30 2012
Author: Mark Percival