Full Stack Web Development with Backbone.js (2014)
Chapter 10. Automated Workflows
As your application grows and you start working as a team with multiple developers, questions on workflow, coding style, and testing become important. Tools to organize workflows help to apply conventions and bring applications to the next level.
In this chapter, we enter the world of Yeoman. Based on the ideas of build automation, this chapter shows how a Yeoman generator can provide you with standard steps to develop Backbone application modules.
Bundled with Yeoman generators, there often comes a setup that favors the RequireJS module format over CommonJS modules. While CommonJS and Browserify are interesting when the JavaScript application must be loaded up front, RequireJS has its benefits if you are building a web application where parts of the experience should be loaded asynchronously as needed.
At the end of this chapter, our goal is to have the movie application running on the Node toolchains. This includes:
§ How to fetch Backbone dependencies such as plug-ins using Bower
§ Yeoman and a Backbone generator, which will help scaffolding project structures
§ Using RequireJS for development
§ Using a build with RequireJS project
Improving Productivity
In the previous chapters, you incrementally have built a web application “by hand.” This means that you ran commands from the command line to create files and directories, inserted repeating code by hand, and maybe applied a watchmode of different tools to help establish the development environment.
If you work on different projects or on a team with several developers, it can be helpful to work with certain “default” project configurations. Generators for project components are an important building block of some frameworks on top of Backbone, too, such as the Thorax generator (which will be discussed in Chapter 11).
Most tools for workflow automation are actually framework agnostic, but a number of tools are repeatedly discussed in the Backbone ecosystem:
Yeoman
With Yeoman generators, you can easily scaffold Backbone projects and components to avoid “boring” steps, such as creating directories and files. Many Yeoman generators provide support for a build process based on Grunt or Gulp. With this combination, your team gets some extra support not only to develop and test web applications with Backbone, but also to make an application production ready.
Brunch
Brunch is a very popular choice for building applications with the Chaplin framework. With it, you can easily create a project skeleton of a Backbone application. Also, Brunch supports a watch mode during application development, compilation of CoffeeScript to JavaScript, and a deploy function.
Catero
Cartero offers a management system for growing projects based on Browserify and CommonJS. With it, you can bundle your JavaScript together with CSS and HTML such that they become easier to reuse across different or one large frontend project.
Because tools for automating workflows and project builds make choices for you, it can be important to study in detail how it will affect your application. For example, if you decide to use a Yeoman generator—as we will explore in this chapter—it might affect the way your application dependencies are managed or how your JavaScript application is built.
As many frontend communities are adopting Yeoman and RequireJS, the overview of this chapter hopefully can make you understand the implications. However, this chapter marks a brief departure from the example application used in previous chapters. So, it is best to start in a clean directory.
Dependencies with Bower
In contrast to full stack JavaScript developers, the preferred way of many frontend developers to manage dependencies is given by Bower. When you generate a project with Yeoman, a bower dependency is often included.
Similar to npm, Bower can help you locate and download required libraries in the right version. Bower has the advantage of not imposing a JavaScript module format. But on the other hand, with Bower, you will need to manage dependencies for the backend of your application separately as we did earlier with npm.
To install Bower, use the following:
$ npm install -g bower
Next, you can initialize a new project with:
$ bower init
Then, we can either predefine libraries in the bower.json file, or manually run the bower install command. For example:
$ bower install jquery-mockjax
NOTE
With jquery-mockjax, you can easily mock data for browser development. If you just want to focus on browser development without exploring API requirements, jquery-mockjax might be a good alternative to a canned setup.
Also, you could install the Backbone.Obscura plug-in from Chapter 7 with Bower as follows:
$ bower install backbone.obscura
When scaffolding a project with Yeoman, you will get a default bower.json file, where Backbone and its dependencies are already predefined. You then just need to add new plug-ins into the bower.json file and run the bower install again. Unfortunately, not all packages support the bower fetch process, as not all frontend JavaScript libraries support a certain module format. So, sometimes you still need to clone a Git repository and/or copy dependencies manually into a project.
Because Bower fetches the whole history of a dependency, it can be interesting to just copy over the actual JavaScript library into the application. For example, if you work with a hybrid JavaScript application stack, it can be interesting to track only referenced JavaScript libraries in the project repository. This can be done with bower-installer, which supports copying files from the /bower_components directory:
$ npm -g install bower-installer
Then, you can add an install path to the bower.json file:
"install" : {
"path" : "./js/libs"
}
And then you run:
$ bower-installer
Setting up install paths...Finished
Running bower install...Finished
Installing:
backbone.obscura : ./js/libs/backbone.obscura/backbone.obscura.js
backbone : /Users/pmu/movies_node/./js/libs/backbone/backbone.js
underscore : /Users/movies_node/./js/libs/underscore/underscore.js
jquery : /Users/pmu/movies_node/./js/libs/jquery/jquery.js
In the next section, we look at how Yeoman integrates Bower to manage dependencies in a Backbone project.
Say Hello to Yeoman
With browserify and Grunt, you have already had some experience with tools that automate steps in JavaScript development. We still may face some problems that come with growing applications, however. For example, how can we set up a new project from scratch? And how can we organize code in different directories?
Developers have different biases toward these question, but a number of themes are heard often:
§ In development mode, projects should be loaded dynamically. Also, it must be possible to load components for tests. For this, some developers set up a watch mode to rebuild an application automatically after a change. We explored this idea with Grunt and watchify previously. But with RequireJS you get an additional option.
§ So far, we organized code into directories /models, /collections, and /views. We also had to create a place for templates. The way we organize our files has quite some influence on how scalable our application becomes.
Instead of reinventing the wheel, we can look at the workflows from other developers. Because most workflow problems revolve around file management, let’s look at Yeoman and the “generator” for Backbone.
Yeoman can create, fetch, and copy files according to conventions that are set in the generator that we specify. What this means will become clear in a moment.
First, you need to install Yeoman:
$ npm -g install yo
This will take a few minutes, depending on your network speed.
Next, run the following:
$ yo
This will return a small menu:
[?] What would you like to do?
--------
Update your generators
❯ Install a generator
There are many different generators that define your project layout as well as dependencies in package.json, bower.json, and tasks in the Gruntfile. Because this is a Backbone book, we directly jump into the Backbone generator with:
npm install -g generator-backbone
Now we can run the Yeoman generator with:
$ yo backbone movies
The generator will ask some question about whether to include support for other kinds of frontend tools.
As a result, we should see a similar output:
_-----_
| |
|--(o)--| .--------------------------.
`---------´ | Welcome to Yeoman, |
( _´U`_ ) | ladies and gentlemen! |
/___A___\ '__________________________'
| ~ |
__'.___.'__
´ ` |° ´ Y `
Out of the box I include HTML5 Boilerplate, jQuery, Backbone.js and Modernizr.
[?] What more would you like?
⬡ Twitter Bootstrap for Sass
❯⬡ Use CoffeeScript
As our goal is project organization of Backbone applications, we only activate support for RequireJS. Then we should see that Yeoman builds our empty project:
create .bowerrc
create bower.json
create .jshintrc
create .editorconfig
create Gruntfile.js
create package.json
create app/styles/main.css
create app/404.html
create app/favicon.ico
create app/robots.txt
create app/.htaccess
create app/index.html
create app/scripts/main.js
invoke mocha:app
create test/index.html
create test/lib/chai.js
create test/lib/expect.js
create test/lib/mocha/mocha.css
create test/lib/mocha/mocha.js
create test/spec/test.js
Yeoman will also go on with installing the projects dependencies under /node_modules.
In our project directory, we now have bower.json and package.json files as well as a Gruntfile.
Let’s explore the application directory from the Yeoman Backbone generator:
app
|-bower_components
|-images
|-scripts
|---collections
|---models
|---routes
|---templates
|---vendor
|---views
|-styles
We also got a file main.js—an important component for using RequireJS, which we will discuss in the next section—and directories for styles and testing.
Besides scaffoling a new project, the Backbone generator of Yeoman supports scaffolding Backbone views, routers, models, and collections. Scaffolding those files is not every developer’s cup of tea, but it can help when you work in a RequireJS environment, as discussed in the next section.
For example, to scaffold a Movies router, you could run:
$ yo backbone:router movies
create app/scripts/routes/movies.js
When you open the newly created router, you can already see the encapsulation based on RequireJS. Let’s explore what this means.
RequireJS
Compared to working on a project based on npm and browserify, RequireJS provides an alternative to break up large components into smaller JavaScript files. This is based on the so-called AMD module format, where you “define” how dependencies must be loaded for each file. Because the way dependencies are resolved may differ between development and production environments, RequireJS supports a development mode and a build mode. Be sure to run the RequireJS early on to prevent problems with asynchronously loading external JavaScript libraries, such as maps.
Main.js
First, we need to discuss the main.js file. To resolve files and dependencies in a JavaScript application, RequireJS needs a mapping of the dependencies to URL paths. We define this mapping in main.js and also define the initial dependencies when an application is first loaded.
In our main.js that we have from the Yeoman generator, we already get Backbone.js configured and its dependencies included by default.
Inspecting the contents of main.js, we quickly can identify new keywords. For example, in the require.config part, we set up the global configuration of the modules and put dependencies that come from other libraries. In the main.js obtained from Yeoman, this looks like:
/*global require*/
'use strict';
require.config({
shim: {
underscore: {
exports: '_'
},
backbone: {
deps: [
'underscore',
'jquery'
],
exports: 'Backbone'
}
},
paths: {
jquery: '../bower_components/jquery/jquery',
backbone: '../bower_components/backbone/backbone',
underscore: '../bower_components/underscore/underscore'
}
});
require([
'backbone'
], function (Backbone) {
Backbone.history.start();
});
The main idea here is that we map files via paths to module references that we can use later in the application. We need this manual mapping, because not all libraries follow the AMD convention to declare their dependencies up front. So, we need to provide a shim that provides a depencency tree that RequireJS can resolve. If we add new libraries, we add a path and a shim. We later will see how to do this in the context of Backbone plug-ins.
Going to the index.html from Yeoman, we see a reference to main.js. Here, loading of modules happens when the browser parses the index.html:
<!-- build:js scripts/main.js -->
<script data-main="scripts/main" src="bower_components/requirejs/require.js">
</script>
<!-- endbuild -->
With this environment set up, we directly can start building the application.
Adding Modules
Assuming you used Yeoman to scaffold a router, you can rewrite the previous router example into AMD format as follows:
/*global define*/
define([
'jquery',
'backbone',
'obscura',
'collections/movies',
'views/layout',
'views/moviesList',
'views/chose',
'views/genres',
'views/sort',
], function ($, Backbone, Obscura, Movies, Layout,
MoviesList, ChoseView, GenresView, SortView ) {
'use strict';
var layout,
movies,
proxy,
sortView,
genresView,
deferred;
if (!proxy) {
movies = new Movies();
proxy = new Obscura(movies);
deferred = movies.fetch();
}
var MoviesRouter = Backbone.Router.extend({
routes: {
"movies/:id": "selectMovie",
"": "main"
},
main: function() {
// ...
},
selectMovie: function(id) {
// ...
}
});
return MoviesRouter;
});
The code is not much different from earlier, except that we wrap the code in a module by using define, declaring dependencies and lastly returning the module. Our MoviesRouter module requires all the sub pieces to return the wirings as a new module. Now, whenever we depend on the router, we include automatically the sub pieces. With RequireJS, we try to create smaller view and model pieces that only ask for the modular bits they need.
Scaffolding Components
With the Yeoman generator, you can add modules for the user interface from the command line with:
$ yo backbone:view layout
$ yo backbone:view movieList
$ yo backbone:view movie
...
And you need to generate modules for the data layer, too:
$ yo backbone:model genre
$ yo backbone:collection genres
$ yo backbone:model movie
$ yo backbone:collection movies
From these commands, we will obtain a bunch of Backbone views and collections. While wiring these files up similarly as you did in the earlier chapters, you will see that the RequireJS build system can resolve dependencies dynamically during development.
Also, with support from Grunt, you can again use different approaches for view templates, such as JST templates or Handlebars.
Conclusion
This chapter covered application development with an automated workflow based on Bower, Grunt, Yeoman, and RequireJS. The workflow automation comes at a price of learning new tools and having some more boilerplate in the JavaScript modules. However, because the future standard of JavaScript will evolve ways that support the AMD style of importing modules, you should have obtained some good foundations in this chapter.
Because build tools and generators play part in some JavaScript frameworks on top of Backbone, you will see how to apply the Yeoman generator from Thorax in the next chapter. Thorax supports an application stack that integrates Backbone and Handlebars and comes with a number of view helpers that simplify building large Backbone applications.
All materials on the site are licensed Creative Commons Attribution-Sharealike 3.0 Unported CC BY-SA 3.0 & GNU Free Documentation License (GFDL)
If you are the copyright holder of any material contained on our site and intend to remove it, please contact our site administrator for approval.
© 2016-2026 All site design rights belong to S.Y.A.