How to start with Backbone.js: A simple skeleton app

Translations

TL;DR

It took me some time to get an optimal code/directory layout for Backbone.js apps.

Because I think this is a major pain for beginners, I prepared a well commented sample skeleton app.

Get it from Github while it’s hot, pull requests/feedback are welcome.

Prerequisites

You need a solid knowledge of JavaScript, familiarity with Backbone, Ruby, HAML, SASS and CoffeeScript to find this writeup useful.

Also, I’m developing on a Mac and have not tested this on other platforms. Although, I do not see any reason why it shouldn’t work.

1. Philosophy

Part of the success of Rails was the conventions and its predefined directory tree. While looking overwhelming and maybe annoying to a beginner at first, it soon becomes liberating. With experience things fall into place, and soon you feel feel like every tiny bit of code has it’s dedicated home.

Backbone, being the nimble, does not prescribe any particular code or directory structure. Until I read enough material and settled on this particular layout, I was feeling very confused and disoriented.

This skeleton app was extracted from a production app and then extensively annotated, to explain certain decisions and choices.

2. Backstory

When I first started to play with Backbone I was already heavily entrenched in the Ruby and Rails world. So naturally I thought, yeah, MVC, I know that. It turned out to be a bit farther from the truth than I wanted or cared to admit.

Disclaimer: I rarely developed pure client-side software, though I was using JavaScript extensively to make things faster and more responsive.

Thing is that MVC on the server-side is quite a bit different from MVC on the client. It has something to do with wiping the state clean each time you reload the page. Statelessness.

The client on the other hand is stateful and thus keeps all your bad practices in memory until they start to slow things down and eventually stop working.

This was the biggest client-side project for me so far, building out the Dubjoy editor for dubbing online video.

The hardest part of learning to develop MVC on the client and using Backbone was seeing the big picture. Seeing where all the little parts fit in and how this all works together in the grand scale.

So for the most part, my journey with Backbone consisted of finding out best practices for file and code organization, setting up the environment and directory structures.

Using backbone.js as a library was “easy”. (Not really, but this isn’t what this article’s about.)

One of the biggest mistakes I was making when starting out was trying to use Backbone constructs for everything.

Backbone is intentionally kept simple, because it’s supposed to be a complement to your own JavaScript. So just create your own App class, and populate it with the stuff and initialization your app really needs.

3. Tools

So a good workflow needs good tools. Here I’ll describe the tools that I found indispensable when developing in Backbone.

CoffeeScript, HAML and SASS

Because I resent cruft and redundancy, I’m a big fan of abstraction languages. Whenever I can, I opt for HAML, SASS and CoffeeScript.

The brevity they bring is paramount to me.

HAML Coffee

In Backbone, you usually need a template engine. Templates provide the markup for views. There’s a lot of solutions for this, but because I like to be consistent, the best choice was to use HAML.

Fortunately, there’s a library for this: haml-coffee, which enables you to use HAML intertwined with snippets of CoffeeScript.

Guard

To be able to use these languages seamlessly, you need some sort of a on-demand compiler. Turns out a Ruby gem called Guard does exactly this.

Guard is extremely flexible. It watches for file system changes and then doing something to files that changed.

Jammit

Jammit is an asset packaging library. It concatenates and compresses CSS and Javascript. It’s easy to use, but needs a configuration file, that defines which files to work on.

Sinatra with Isolate

Backbone apps are static files and you can run them directly off your hard drive. But to do proper paths and even maybe some API, we need a server.

Sinatra, a mini Ruby web framework, forms the base of the server. This enables some quick server-side magic as well as making an API for persistence.

To make this part as easy as possible to use, I packaged the server with Isolate, a small Ruby library for sand-boxing, which is like a mini-Bundler. When launching the server with rake server for the first time, it will check and auto-install it’s dependencies. It just works.

4. Using the skeleton

Getting started with a new app using my skeleton is trivial. It uses Ruby in several critical places, so be sure you have a working installation of Ruby, preferably of the 1.9 kind.

All of the files, directories and their meaning is described with more detail in the README file of the skeleton.

A working example of this app is available online. This way you can check if the console output is the same on your local setup and here.

Start by cloning my backbone-skeleton repo.

$ git clone https://github.com/mihar/backbone-skeleton.git my-new-backbone-app

Then use the bundle command that comes with Ruby Bundler to install the necessary dependencies for guard. Guard will compile our HAML, SASS and CoffeeScript to their native counterparts.

$ cd my-new-backbone-app
$ bundle

Once Bundler completes the installation, we can try starting Guard, to immediately start watching files for changes.

$ bundle exec guard

While leaving guard running, go to another terminal and let’s fire up a simple, bundled Ruby web server, that we’ll use for development. The server will install all of it’s dependencies by itself.

$ rake server

[1/1] Isolating sinatra (>= 0).
Fetching: rack-1.4.1.gem (100%)
Fetching: rack-protection-1.2.0.gem (100%)
Fetching: tilt-1.3.3.gem (100%)
Fetching: sinatra-1.3.3.gem (100%)
[2012-12-05 18:17:05] INFO  WEBrick 1.3.1
[2012-12-05 18:17:05] INFO  ruby 1.9.3 (2012-02-16) [x86_64-darwin11.3.0]
[2012-12-05 18:17:05] INFO  WEBrick::HTTPServer#start: pid=39675 port=9292

Now our server is listening on http://localhost:9292, so go ahead, and open that.

If you see “Skeleton closet”, everything is go.

Go check out the JavaScript console for more information.

5. Resources

The missing CDN

They host all the libraries, including Backbone and underscore.

Backbone Peepcode tutorials

Peepcode has been my friend since my Ruby and Rails days. They produce high-quality screencasts on a variety of topics.

They have a series of 3 videos on Backbone, going from the basics to some pretty advanced stuff.

Be prepared to shell out $12 per video, though.

Derick Bailey backbone posts

I’ve learned so much about the correct ways to do things on Derick’s blog. He’s a seasoned Backbone developer that has overcome many problems and written up on the progress. Wealth of resources.

Derick Bailey’s 4 part screencast

Haven’t seen this one yet, but if I’m judging by his blog, this should be very worth the money. 4 videos, $12 a pop.

Backbone Patterns

Documented patterns extracted from building many Backbone apps.

Organizing Backbone apps with modules

Article exploring similar problems of code/directory structure and organizations.

Backbone Boilerplate

A much bigger project with a similar goal as mine.

Backbone for absolute beginners

How to directly upload files to Amazon S3 from your client side web app

Why you need this?

You don’t want your heavy-weight data to travel 2 legs from Client to Server to S3, incurring the cost of IO and clogging the pipe 2 times.

Instead, you want to ask your server to give your client one-time permission to upload your data directly to S3. The process is still 2 legged, but heawy-weight data travels only on 1 leg.

On Amazon S3 this is implemented with CORS (Cross Origin Resource Sharing)

We use this at Dubjoy, where customers upload their huge video files to S3 for translation and voice-over, and we don’t want our Heroku server to have anything to do with heavy-weight video files.

Steps to implement this

  1. Set up Amazon S3 bucket CORS configuration
  2. Implement client-side JavaScript (CoffeScript, JavaScript)
  3. Implement server-side upload request signing (Ruby/Sinatra, trivial to do in any other language)

1. Amazon S3 bucket CORS configuration

Set this in AWS S3 management console. Right-click on the desired bucket and select Properties. Below, on the permissions tab, click Edit CORS configuration, paste the XML below and click Save.

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>*</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>Authorization</AllowedHeader>
    </CORSRule>
    <CORSRule>
        <AllowedOrigin>*</AllowedOrigin>
        <AllowedMethod>PUT</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>Content-Type</AllowedHeader>
        <AllowedHeader>x-amz-acl</AllowedHeader>
        <AllowedHeader>origin</AllowedHeader>
    </CORSRule>
</CORSConfiguration>

2. Your web app

Head to GitHub repo with CoffeScript and JavaScript Class files to include. In your app, do the following:

HAML

%input#file{ :type => 'file', :name => 'files[]'}

or HTML for the chevron-lovers

<input type='file' name='files[]' />

CoffeScript

s3upload = s3upload ? new S3Upload
    file_dom_selector: '#files'
  s3_sign_put_url: '/signS3put'
    onProgress: (percent, message) ->
        console.log 'Upload progress: ', percent, message # Use this for live upload progress bars
    onFinishS3Put: (public_url) ->
        console.log 'Upload finished: ', public_url # Get the URL of the uploaded file
  onError: (status) ->
    console.log 'Upload error: ', status

or JavaScript for the brace-lovers

var s3upload = s3upload != null ? s3upload : new S3Upload({
  file_dom_selector: '#files',
  s3_sign_put_url: '/signS3put',
  onProgress: function(percent, message) { // Use this for live upload progress bars
    console.log('Upload progress: ', percent, message);
  },
  onFinishS3Put: function(public_url) { // Get the URL of the uploaded file
    console.log('Upload finished: ', public_url);
  },
  onError: function(status) {
    console.log('Upload error: ', status);
  }
});

Be sure to set the right DOM selector name file_dom_selector for file input tag, #files in our case. s3_sign_put_url is an end-point on your server where you will be signing S3 PUT requests.

3. Server-side request signing

Be sure to set S3_BUCKET_NAME,S3_SECRET_KEY,S3_ACCESS_KEY. Create a bucket and get the keys under Security Credentials menu in AWS management console.

S3_BUCKET_NAME = 'CREATE_A_BUCKET_AND_SET_THE_NAME_HERE'
S3_SECRET_KEY = 'GET_THIS_IN_AWS_CONSOLE'
S3_ACCESS_KEY = 'GET_THIS_IN_AWS_CONSOLE'

get '/signS3put' do
  objectName = params[:s3_object_name]
  mimeType = params['s3_object_type']
  expires = Time.now.to_i + 100 # PUT request to S3 must start within 100 seconds

  amzHeaders = "x-amz-acl:public-read" # set the public read permission on the uploaded file
  stringToSign = "PUT\n\n#{mimeType}\n#{expires}\n#{amzHeaders}\n/#{S3_BUCKET_NAME}/#{objectName}";
  sig = CGI::escape(Base64.strict_encode64(OpenSSL::HMAC.digest('sha1', S3_SECRET_KEY, stringToSign)))

  {
    signed_request: CGI::escape("#{S3_URL}#{S3_BUCKET_NAME}/#{objectName}?AWSAccessKeyId=#{S3_ACCESS_KEY}&Expires=#{expires}&Signature=#{sig}"),
    url: "http://s3.amazonaws.com/#{S3_BUCKET_NAME}/#{objectName}"
  }.to_json
end

Resources

The code is a based on these resources, but has been put in to an easy to use CoffeeScript/JavaScript Class

You can learn more about CORS here

Rok Krulec / @tantadruj

How to record audio in Chrome with native HTML5 APIs

Two weeks ago a new version of Chrome was released. Google switched from the default Adobe’s Flash Player to an in-house developed version called “Pepper Flash”. Unfortunately Pepper Flash has a problem with audio recording, resulting in distorted audio on almost all Macs.

This happened right in the middle of our efforts to build the Dubjoy Editor, a browser-based, easy to use tool for translating (dubbing) online videos. Relying on Flash for audio recording was our first choice, but when confronted with this devastating issue, we started looking into other options. Using native HTML5 APIs seemed like a viable solution.

We started researching the space and checked a lot of sample code out there, but had limited success.

From what you can find on html5rocks, capturing audio seems to be well supported. We started with the sample code for capturing video and modified it for our audio recording test:

<html>
  <body>
    <audio controls autoplay></audio>

    <input onclick="startRecording()" type="button" value="start recording" />
    <input onclick="stopRecording()" type="button" value="stop recording and play" />

    <script>
      var onFail = function(e) {
        console.log('Rejected!', e);
      };

      var onSuccess = function(s) {
        stream = s;
      }

      window.URL = window.URL || window.webkitURL;
      navigator.getUserMedia  = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;

      var stream;
      var audio = document.querySelector('audio');

      function startRecording() {
        if (navigator.getUserMedia) {
          navigator.getUserMedia({audio: true}, onSuccess, onFail);
        } else {
          console.log('navigator.getUserMedia not present');
        }
      }

      function stopRecording() {
        audio.src = window.URL.createObjectURL(stream);
      }
    </script>
  </body>
</html>

Everything seems easy and pretty straightforward, right? Wrong!

When clicking the “start recording” button, a permission request to use the microphone appears. After allowing access the recording should start, but clicking on the “stop recording and play” button does absolutely nothing.

Looks like the problem lies in assigning the recorded stream to the native audio source as it’s done in the video sample on html5rocks:

audio.src = window.URL.createObjectURL(stream);

After playing around and searching the web for hours, we found countless posts of people asking on forums why it isn’t working. The answer is that the current implementation of Chrome returns raw audio samples. These are not playable by the native <audio> control.

What you need to do is to create an audio context and a media stream source:

var context = new webkitAudioContext();
var mediaStreamSource = context.createMediaStreamSource(s);

These can be used for creating an audio loop that enables you to hear your own voice:

mediaStreamSource.connect(context.destination);

To implement the recording functionality you need to buffer the returned raw audio samples until the recording is done. If you want to play it back, you need to convert the buffered samples to a format that can be played natively by the <audio> control. This can be quite cumbersome and not something you want to spend your time with.

Luckily there are libraries available that handle this for you. Recorderjs is the one we used and did the trick.

The media stream source created above, can be passed as a parameter to the recorder object for buffering raw audio samples:

recorder = new Recorder(mediaStreamSource);
recorder.record();

When done, the recorder object can promptly convert the buffered audio to a natively playable WAV file:

recorder.stop();
recorder.exportWAV(function(s) {
  audio.src = window.URL.createObjectURL(s);
});

Which is exactly what we were looking for.

Here’s the full version of the HTML5 native audio recorder complete with playback functionality:

<html>
  <body>
    <audio controls autoplay></audio>
    <script type="text/javascript" src="recorder.js"> </script>

    <input onclick="startRecording()" type="button" value="start recording" />
    <input onclick="stopRecording()" type="button" value="stop recording and play" />

    <script>
      var onFail = function(e) {
        console.log('Rejected!', e);
      };

      var onSuccess = function(s) {
        var context = new webkitAudioContext();
        var mediaStreamSource = context.createMediaStreamSource(s);
        recorder = new Recorder(mediaStreamSource);
        recorder.record();

        // audio loopback
        // mediaStreamSource.connect(context.destination);
      }

      window.URL = window.URL || window.webkitURL;
      navigator.getUserMedia  = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;

      var recorder;
      var audio = document.querySelector('audio');

      function startRecording() {
        if (navigator.getUserMedia) {
          navigator.getUserMedia({audio: true}, onSuccess, onFail);
        } else {
          console.log('navigator.getUserMedia not present');
        }
      }

      function stopRecording() {
        recorder.stop();
        recorder.exportWAV(function(s) {
          audio.src = window.URL.createObjectURL(s);
        });
      }
    </script>
  </body>
</html>

Unfortunately there’s a caveat. In the current stable version of Chrome, the support for native HTML5 audio playback is not enabled by default. For the code above to work you need to enable “Web Audio Input” in chrome://flags, which is a huge deal breaker for us.

Before starting the development of the Dubjoy Editor, we decided to support Chrome as our only browser, because of its wide adoption on both Macs and Windows, auto-updates and the built-in Flash Player.

After trying hard to find a workaround, we’re still waiting for the Chrome team to fix the “Pepper Flash Bug" (that in the meantime has spread to millions of users around the world) or to enable "Web Audio Input" by default in their stable version of Chrome.

HTML5 is very promising and when browsers will support it widely, a lot of the problems we face today will disappear. But we’re not quite there yet.

You can download sample code from Github.