Learning Yeoman

Table of Contents

Learning Yeoman

Credits

About the Author

About the Reviewers

www.PacktPub.com

Support files, eBooks, discount offers, and more

Why subscribe?

Free access for Packt account holders

Preface

What this book covers

What you need for this book

Who this book is for

Conventions

Reader feedback

Customer support

Downloading the example code

Errata

Piracy

Questions

1. Modern Workflows for Modern Webapps

An overview of Yeoman

Yeoman's architecture

Node's package manager

Features of Yeoman

Quick installation

Installing Yeoman and friends

Installing a generator

Scaffolding with Yo

Creating the project

Invoking the generator

Directory structure

The build process

The Connect LiveReload server

Previewing the server

Package management with Bower

Code linting with JSHint

Automation

Testing with PhantomJS

Running tests

Optimizing for production

Self-test questions

Summary

2. Getting Started

Yo – generators

The Yeoman workflow

Official generators

The generator-webapp

Features

Installing the generator-webapp

Using the generator-webapp

Options

Example usage

Previewing

Conclusion

The generator-angular

Features

Installing the generator-angular

Using the generator-angular

Options

Example usage

Angular subgenerators

Previewing

Conclusion

The generator-backbone

Features

Installing the generator-backbone

Using the generator-backbone

Options

Example usage

Backbone subgenerators

Previewing

Conclusion

The generator-ember

Features

Installing the generator-ember

Using the generator-ember

Options

Example usage

Ember subgenerators

Previewing

Conclusion

Self-test questions

Summary

3. My Angular Project

Anatomy of an Angular project

Why Angular?

Creating a new Angular project

Installing the generator-angular

Scaffolding the application

Understanding the directory structure

Configuring the application

Creating the application definition

Creating the application controller

Creating the application views

Modifying the application's index.html

Creating Angular partials

Creating the application's header

Creating the application's footer

Customizing the main view

Previewing the application

Testing an Angular application

Angular unit tests

Configuring the Karma Runner

Running unit tests

End-to-end tests with Protractor

Configuring Protractor

Creating Protractor e2e spec

Angular controllers

Creating controllers

Using controllers

Testing controllers

Angular services

Creating services

Using services

Testing services

Angular filters

Creating filters

Using filters

Testing filters

Angular directives

Creating directives

Using directives

Testing directives

Angular views

Creating the Angular views

Creating the posts list

Creating the post-header view

Self-test questions

Summary

4. My Backbone Project

Anatomy of the Backbone project

The new Backbone project

Installing the generator-backbone

Scaffolding a Backbone application

Understanding the directory structure

Configuring the application

Scaffolding the app view

The Backbone app view

The Handlebars app template

Scaffolding the main view

The Backbone main view

The Handlebars main template

Scaffolding the app router

Bootstrapping the app

Previewing the app

Testing

Configuration

Unit testing

End-to-end tests

Backbone.Events

Creating events

Using events

Testing events

Backbone.Model

Scaffolding models

Using the Backbone models

Creating a model

Updating a model

Saving a model

Destroying a model

Validating a model

Testing a model

Backbone.Collection

Creating collections

Using collections

Testing collections

The Backbone view

Creating views

Using views

Testing views

Backbone.Router

Creating routers

Using routers

Testing routers

Self-test questions

Summary

5. My Ember Project

Anatomy of the Ember project

The new Ember project

Installing the generator-ember

Scaffolding the application

Understanding the directory structure

Application configuration

Application definition

The application template

The index template

The feature component

Previewing the application

Testing

The test helpers

Setup

End-to-end integration tests

Unit tests

Ember Data

Ember Data concepts

Models

Creating a model

Methods

Attributes

Fixtures

Records

Finding all records

Finding a single record

Creating a record

Deleting a record

Routes

Creating the routes

Using routes

Posts route

Post route

Posts edit route

Templates

Handlebar helpers

Posts template

Post template

Posts edit template

Controllers

Post edit controller

Self-test questions

Summary

6. Custom Generators

Anatomy of a generator

Types of generators

The new custom generator

Installing the generator-generator

Using generator-generator

Understanding the directory structure

Adding logic to the generator

Initializing the generator

Asking questions to the user

Copying the project files

Copying the application files and folders

Installing dependencies with Bower

Creating custom templates

Creating the Gruntfile.js file

Creating the watch task

Creating the serve task

Creating the bowerInstall task

Registering tasks in Gruntfile.js

Creating the package.json file for npm

Creating the .editorconfig file for IDEs

Creating the .jshintrc file for JSHint

Creating the .travis.yml file for Travis CI

The .gitattributes file for Git

The .gitignore file for Git

Creating the .bowerrc file for Bower

Creating the bower.json file for Bower

Creating the application templates

The index.html file

The main.css file

The main.js file

Testing a custom generator

Setup

Testing the generator output

Test generator loading

The new custom subgenerator

Understanding the subgenerator's directory structure

Creating subgenerator templates

Adding logic to the subgenerator

Using your custom generator

Link your generator

Scaffolding a new webapp

Self-test questions

Summary

7. Custom Libraries

The new CommonJS project

Installing the generator-commonjs

Scaffolding a CommonJS project

The CommonJS logic

Module properties

Connecting to MongoDB

Finding all models

Finding a model

Creating a model

Updating a model

Destroying a model

Testing a CommonJS project

Test for no model

Test finding all models

Test finding one model

Test creating a model

Test updating a model

Test destroying a model

Deploying to npm

Conclusion

The new Node.js module project

Installing the generator-node

Scaffolding a Node.js module project

The Node.js module logic

Testing a Node.js module

Deploying

Conclusion

The new jQuery project

Installing the generator-jquery

Scaffolding a jQuery project

Adding the plugin logic

Testing a jQuery plugin

Creating the unit test

Deploying to Bower

Conclusion

Self-test questions

Summary

8. Tasks with Grunt

Overview on GruntJS

Installing the Grunt CLI

Installing Grunt

Grunt usage

Grunt options

Installing the generator-gruntfile

Using Grunt

The package.json file

The Gruntfile.js file

Loading tasks

Creating the alias tasks

Multiple target tasks

Registering the basic tasks

Options – files

Options – file patterns

Options – dynamic patterns

Options – templates

Options – importing data

The new project

My custom Grunt plugin

Installing the generator-gruntplugin

Usage

The directory structure

The Grunt plugin logic

Plugin options

Using Grunt to read files

Using Grunt to write files

Testing a Grunt plugin

Creating test fixtures

Running the tests

Deploying to npm

Usage

Self-test questions

Summary

9. Yeoman Tips and Tricks

Webapp generator solutions

Creating a RESTful Node.js server

Installing module dependencies

Creating the server

Configuring the server

Configuring the data source

Defining server routes

The default route

GET – fetch the posts route

POST – create the post route

GET – a single post route

PUT – update the post route

DELETE – remove the post route

Starting the server

Running the server

Testing the server

Setting up the proxy server

Configuring the proxy server

Conclusion

Angular generator solutions

Protractor e2e testing

Installing Protractor

Installing the grunt-protractor-runner

Configuring the Protractor task

Creating the Protractor configuration

Creating an e2e spec

The MainPage object

The PostPage object

The e2e spec

Starting the Selenium WebDriver

Starting the application

Running e2e tests

Backbone generator solutions

Code coverage with Karma

Installing Karma and plugins

Karma configuration

Configuring test-main.js

Running tests

Code coverage report

Self-test questions

Summary

A. Yeoman Resources

Reference guides

Yo – the scaffolding tool

Usage

Bower – the package tool

Usage

Commands

Options

Grunt – the build tool

Usage

Options

Git

Usage

Commands

Jasmine – behavior-driven JavaScript

Structure of a suite

Matchers

Spy matchers

Reserved words

Installation guides

Installing Git

Installing Git on Windows

Installing Git on Mac

Installing Node.js and npm

Installing Node on Windows

Installing Node on Mac

Installing Yo

Installing Yo on Mac/Windows

Installing Grunt

Installing Grunt on Mac/Windows

Installing Bower

Installing Bower on Mac/Windows

Self-test answers

Chapter 1, Modern Workflows for Modern Webapps

Chapter 2, Getting Started

Chapter 3, My Angular Project

Chapter 4, My Backbone Project

Chapter 5, My Ember Project

Chapter 6, Custom Generators

Chapter 7, Custom Libraries

Chapter 8, Tasks with Grunt

Chapter 9, Yeoman Tips and Tricks

Summary

Index

Learning Yeoman

Learning Yeoman

Copyright © 2014 Packt Publishing

All rights reserved. No part of this book may be reproduced, stored in a retrieval system, or transmitted in any form or by any means, without the prior written permission of the publisher, except in the case of brief quotations embedded in critical articles or reviews.

Every effort has been made in the preparation of this book to ensure the accuracy of the information presented. However, the information contained in this book is sold without warranty, either express or implied. Neither the author, nor Packt Publishing, and its dealers and distributors will be held liable for any damages caused or alleged to be caused directly or indirectly by this book.

Packt Publishing has endeavored to provide trademark information about all of the companies and products mentioned in this book by the appropriate use of capitals. However, Packt Publishing cannot guarantee the accuracy of this information.

First published: August 2014

Production reference: 1120814

Published by Packt Publishing Ltd.

Livery Place

35 Livery Street

Birmingham B3 2PB, UK.

ISBN 978-1-78398-138-0

www.packtpub.com

Cover image by Tony Shi (<shihe99@hotmail.com>)

Credits

Author

Jonathan Spratley

Reviewers

Lauren Bridge

Dejan Markovic

Matt Momont

Commissioning Editor

Edward Gordon

Acquisition Editors

Sam Birch

Ellen Bishop

Content Development Editor

Rikshith Shetty

Technical Editor

Neha Mankare

Copy Editors

Janbal Dharmaraj

Karuna Narayanan

Alfida Paiva

Project Coordinators

Melita Lobo

Kartik Vedam

Proofreaders

Simran Bhogal

Lauren Harkins

Lawrence Herman

Joanna McMahon

Indexers

Hemangini Bari

Tejal Soni

Graphics

Ronak Dhruv

Production Coordinator

Arvindkumar Gupta

Cover Work

Arvindkumar Gupta

About the Author

Jonathan Spratley, or as his friends call him, Jonnie, is currently working at GE Software and actively uses technologies such as Yo, Bower, Grunt, Node, AngularJS, and Backbone. He leveraged his knowledge of creating Yeoman web applications to write this book. He started developing with HTML, CSS, and JavaScript around 2004 and has spent several years designing and implementing web applications for both mobile and desktop browsers. He is a full-stack developer with experience in both server- and client-side technologies. His passion for tools and technologies that streamline a developer's productivity has driven him to become an autodidact. He has written articles for Safari Books Online Blog, and Flash & Flex Developer's Magazine. This is his first time as an author.

I would like to dedicate this book to all those who believed in me.

About the Reviewers

Lauren Bridge is a software engineer on the Predix platform team at GE Software. She earned her BS in Computer Science from the University of Michigan and is working on her MCS at the University of Illinois at Urbana-Champaign. She mostly develops in JavaScript and Java, and she enjoys learning more frontend web technologies every day.

I would like to thank Jonnie for teaching me about Yeoman.

Dejan Markovic is an accomplished web developer who enjoys working on both frontend and backend technologies. Since 2003, he has added great value to numerous projects for small- and medium-sized businesses as well as major corporations such as Rogers Media and Softchoice. He is the co-owner of NYTO Group (New York City/Toronto Group), a premium web development company in Toronto, Canada.

NYTO Group's portfolio is available at http://nytogroup.com/portfolio/. NYTO Group is always looking for new projects and partnerships.

Matt Momont is a full-stack developer at GE Software working on the Industrial Internet—the Internet of (really big) Things. He holds a Computer Science degree from the University of Notre Dame and is currently pursuing his Masters in Computer Science at the University of Illinois at Urbana-Champaign. He likes Yeoman because it helps backend developers quickly scaffold out frontend web applications.

www.PacktPub.com

Support files, eBooks, discount offers, and more

You might want to visit www.PacktPub.com for support files and downloads related to your book.

Did you know that Packt offers eBook versions of every book published, with PDF and ePub files available? You can upgrade to the eBook version at www.PacktPub.com and as a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at <service@packtpub.com> for more details.

At www.PacktPub.com, you can also read a collection of free technical articles, sign up for a range of free newsletters and receive exclusive discounts and offers on Packt books and eBooks.

http://PacktLib.PacktPub.com

Do you need instant solutions to your IT questions? PacktLib is Packt's online digital book library. Here, you can access, read and search across Packt's entire library of books.

Why subscribe?

- Fully searchable across every book published by Packt

- Copy and paste, print and bookmark content

- On demand and accessible via web browser

Free access for Packt account holders

If you have an account with Packt at www.PacktPub.com, you can use this to access PacktLib today and view nine entirely free books. Simply use your login credentials for immediate access.

Preface

Now is the time to start using a workflow that can keep up with the fast pace of the development world. Software changes so fast that keeping your project libraries updated and using the latest code has always been a manual, tedious process. Well, not anymore, thanks to modern tooling that has taken my development productivity to greater levels! I have been using Yeoman since the early versions, where Yeoman was the one tool that could do it all.

Since the Yeoman project grew, it has evolved into something I have always wanted in the web development community, such as code generators that can quickly scaffold out working applications that are in alignment with the best practices of that specific framework or language. Now, the time has come and Yeoman is going to take the development world by storm and grow into something that will become a standard in creating modern web applications.

This book is a compilation of using the most popular Yeoman generators on npm. We explore the options that each tool has to offer and use them to create various types of projects, ranging from AngularJS applications to Node.js modules. This book provides examples and information regarding the tools in Yeoman.

What this book covers

Chapter 1, Modern Workflows for Modern Webapps, is an overview of the three core tools used in the Yeoman workflow—Yo, Bower, and Grunt. We cover how to use these tools in development and how to incorporate the workflow into new or existing projects, followed by an example of each of the features in Yeoman.

Chapter 2, Getting Started, begins with installing Yeoman for development and an overview on the AngularJS, Backbone.js, Ember.js, and webapp generators, the options and subgenerators it uses, and examples of using each generator to start the project.

Chapter 3, My Angular Project, starts out with covering the concepts of Angular and the anatomy of an AngularJS application. We will use the generator-angular to scaffold an extendable AngularJS application that uses directives, services, and factories. We will cover setting up a CRUD application with unit tests that use the Karma runner.

Chapter 4, My Backbone Project, covers the anatomy of a Backbone.js project and the concepts behind the library. We create a Backbone application to perform CRUD operations on a data source that is unit tested using Jasmine. The project uses CoffeeScript, Require.js, and AMD to create a well-structured app ready for extending.

Chapter 5, My Ember Project, starts out by creating a new Ember.js project. We then cover how an Ember application is structured and the concepts around the framework, configuring a test environment that is used to run both unit and integration tests.

Chapter 6, Custom Generators, covers the Yeoman generator API and the common methods used when developing generators. We also cover installing and invoking the generator-generator to create a custom Yeoman generator with option prompts that scaffold a custom application based on users' feedback. We cover how to handle testing the generators using nodeunit and then we publish the generator to npm.

Chapter 7, Custom Libraries, covers using Yeoman to create custom libraries that are deployed to either Bower or npm. We learn how to use the Node.js generator and the CommonJS generator to create a Node module, followed by a client-side jQuery plugin that handles sending CRUD operations to a Node REST API server.

Chapter 8, Tasks with Grunt, starts out by covering all the available options when using the Grunt command. We install two Yeoman Grunt generators: the Gruntfile generator that enables adding Grunt to existing projects, and the Grunt plugin generator. We cover creating a custom Grunt task that is then deployed to npm along with unit tests using the nodeunit framework.

Chapter 9, Yeoman Tips and Tricks, aims to cover the holes from the Yeoman generators and specific projects. We cover adding code coverage to a Backbone.js project, as well as setting up Protractor to run end-2-end testing for our Angular project.

What you need for this book

You will need to have the following installed software on your development box in order to properly run the examples and tutorials in the chapters:

- Node 0.10.24

- NPM 1.4.7

- Git 1.8.5.2

- Ruby 1.9.2

- Text editor of some sort

- Google Chrome

Who this book is for

This book is for newbies and intermediate web developers looking to speed up the process when it comes to creating web applications of various frameworks. You should have basic knowledge of HTML, CSS, and JavaScript. The examples in this book will use jQuery style selectors and methods, so some jQuery experience is needed. The tools in this book involve the command line, so having basic knowledge about using the shell to invoke commands on a system is required. As long as you understand basic principles about structuring HTML and OO JavaScript applications, you should have no problem following the step-by-step examples in the chapters.

Conventions

In this book, you will find a number of styles of text that distinguish between different kinds of information. Here are some examples of these styles, and an explanation of their meaning.

Code words in text, database table names, folder names, filenames, file extensions, pathnames, dummy URLs, user input, and Twitter handles are shown as follows: "The bower.json file is how Bower manages the project's dependencies."

A block of code is set as follows:

<div class="header">
<ul class="nav nav-pills pull-right">
<li ng-repeat="item in App.menu"
ng-class="{'active': App.location.path() === item.href}">
<a ng-href = "#{{item.href}}"> {{item.title}} </a>
</li>
</ul>
<h3 class="text-muted"> {{ App.sitetitle }} </h3>
</div>

When we wish to draw your attention to a particular part of a code block, the relevant lines or items are set in bold:

require 'scripts/config'
LearningYeomanCh5 = window.LearningYeomanCh5 =
Ember.Application.create(
LOG_VIEW_LOOKUPS: true
LOG_ACTIVE_GENERATION: true
LOG_BINDINGS: true
config: window.Config
)

Any command-line input or output is written as follows:

$ npm install -g generator-webapp

New terms and important words are shown in bold. Words that you see on the screen, in menus or dialog boxes for example, appear in the text like this: "See for yourself; open Chrome Developer Tools and click on the Network tab."

Note

Warnings or important notes appear in a box like this.

Tip

Tips and tricks appear like this.

Reader feedback

Feedback from our readers is always welcome. Let us know what you think about this book—what you liked or may have disliked. Reader feedback is important for us to develop titles that you really get the most out of.

To send us general feedback, simply send an e-mail to <feedback@packtpub.com>, and mention the book title via the subject of your message.

If there is a topic that you have expertise in and you are interested in either writing or contributing to a book, see our author guide on www.packtpub.com/authors.

Customer support

Now that you are the proud owner of a Packt book, we have a number of things to help you to get the most from your purchase.

Downloading the example code

You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.

Errata

Although we have taken every care to ensure the accuracy of our content, mistakes do happen. If you find a mistake in one of our books—maybe a mistake in the text or the code—we would be grateful if you would report this to us. By doing so, you can save other readers from frustration and help us improve subsequent versions of this book. If you find any errata, please report them by visiting http://www.packtpub.com/submit-errata, selecting your book, clicking on the errata submission form link, and entering the details of your errata. Once your errata are verified, your submission will be accepted and the errata will be uploaded on our website, or added to any list of existing errata, under the Errata section of that title. Any existing errata can be viewed by selecting your title from http://www.packtpub.com/support.

Piracy

Piracy of copyright material on the Internet is an ongoing problem across all media. At Packt, we take the protection of our copyright and licenses very seriously. If you come across any illegal copies of our works, in any form, on the Internet, please provide us with the location address or website name immediately so that we can pursue a remedy.

Please contact us at <copyright@packtpub.com> with a link to the suspected pirated material.

We appreciate your help in protecting our authors, and our ability to bring you valuable content.

Questions

You can contact us at <questions@packtpub.com> if you are having a problem with any aspect of the book, and we will do our best to address it.

Chapter 1. Modern Workflows for Modern Webapps

This chapter will cover the three core tools that make up the Yeoman workflow, how to use these tools in development, and how to incorporate this workflow into new or existing projects.

In this chapter, you will learn the following topics:

- The Yeoman tools and architecture

- Downloading and installing Yeoman

- Features of Yeoman

- Using the Yeoman tools

An overview of Yeoman

The term modern webapps is a relatively new thing, as the Web is still in its infancy stage. As the Web matures, so does the need for developer tools and workflows, thanks to some modern-day Web pioneers over at Google. Paul Irish and Addy Osmani have developed a modern workflow that goes by the name of Yeoman.

The Yeoman workflow is a collection of three tools to improve developers' productivity when building web applications: Yo is the scaffolding tool, Grunt is the build tool, and Bower is the package tool.

- Yo is used to scaffold things such as projects and files from templates

- Grunt is used for task management, testing, code linting, and optimization

- Bower is used for package management and to manage client-side dependencies

Yeoman's architecture

The Yeoman toolset runs in the Node.js environment and is invoked from the command line. Each tool is installed using Node's package manager (npm) and uses the npm repository to manage all plugins.

Node's package manager

Node.js is a platform that is built on Chrome's JavaScript runtime engine. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight, efficient, and perfect for real-time applications that run across distributed devices.

The official package manager for Node.js is npm. From Node versions 0.6.3 and up, npm is bundled and installed automatically with the environment. The npm package manager runs through the command line and manages the application dependencies that are available on the npm registry.

Note

The current Node.js version used in this book is v0.10.28.

Features of Yeoman

Before we dig deep into using each tool of the workflow, let's take a look at some of the Yeoman tooling features that will help you in your next project:

- Quick install: Easily installs all three tools from the npm repository using one command

- Scaffolding: Fast and easy-to-use command-line tool to create new projects or files from templates that individual generators provide

- Build process: Tasks for concatenation, minification, optimization, and testing

- Preview server: Connect LiveReload server to preview your application in the browser

- Package management: Search, install, and manage project dependencies via the command line

- Code linting: Scripts are run against JSHint to ensure language best practices

- Automation: A simple watch task to compile CoffeeScript, LESS, or SASS, and reload the browser upon changes

- Testing: Executes JavaScript code in multiple real browsers with the Karma runner

- Optimization: Images are optimized using OptiPNG and JPEGtran, and HTML is optimized using the HTML minifier

The preceding features are dependent on what the individual generators provide via Grunt tasks. By default, the Angular, Backbone, Ember, and other webapp generators provide tasks to perform all the features listed.

Note

Grunt tasks are individual plugins that perform specific operations on files or folders.

Quick installation

Modern tools usually mean more tools to learn, but learning the tools of the Yeoman workflow is easier than you think. To demonstrate by example, here is how easy it is to get a modern web application up and running, all from the command line.

Installing Yeoman and friends

To install all three tools in the Yeoman workflow, just execute the following command in the terminal:

$ npm install -g yo

The command will install Yo, Grunt, and Bower into your systems path as follows:

- The -g option flag specifies the installation to be globally available in your path, allowing the yo command to be invoked from anywhere

- If using the latest versions of Node and Git, Yeoman will automatically install Bower and Grunt while installing Yo

Note

The -g flag installs globally and requires an administrator user.

If you run into any errors during the initial installation process, you can install the envcheck module to ensure that your system is ready for all of Yeoman's features; just execute the following command:

$ npm install -g envcheck

Installing a generator

To install generators for Yo, use npm. Let's install the generic webapp generator; open a terminal and execute the following command:

$ npm install -g generator-webapp

The preceding command will install the webapp generator globally on your system, easily letting you create new web projects within any directory of your choice.

Scaffolding with Yo

Yeoman includes a powerful command-line utility that can scaffold files based on individual generator templates, allowing you to save time creating files from scratch. There are over 700 community generators on npm.

- To search for generators, add the generator- prefix before the name, as follows:

$ npm search generator-[name]

- To install generators, use npm install passing in the name of the package, as follows:

$ npm install generator-[name]

Note

The npm attribute is the package manager for Node.js and comes bundled with it.

Creating the project

All Yeoman commands work off the current directory, so create a new folder named learning-ye
oman-ch1, open the terminal, and cd to that location into the newly created directory.

Invoking the generator

Yo, the scaffold tool, will easily create and set up project configuration, files, and folders needed for a modern web application. Execute the following command:

$ yo webapp

Generators can be invoked with different options; in the preceding command, we use the generators' default options that include Mocha as the test framework and JavaScript as the scripting language. You will get an output similar to the following screenshot:

The preceding command does many things. First off, it's going to ask you a few questions about your new project, such as whether to include Twitter Bootstrap with or without Compass SASS and whether to include Modernizr.

Select the first option (Bootstrap), and press Enter; you will see the output to the terminal, and Yeoman is performing all the magic right before your eyes.

Directory structure

Do not be overwhelmed by the number of files generated; take a minute and examine the directory structure that Yeoman produces. You will notice how organized the directory structure is. It looks as follows:

├── Gruntfile.js
├── app
│ ├── 404.html
│ ├── bower_components
│ │ ├── bootstrap
│ │ └── jquery
│ ├── favicon.ico
│ ├── images
│ ├── index.html
│ ├── robots.txt
│ ├── scripts
│ │ └── main.js
│ └── styles
│ └── main.css
├── bower.json
├── node_modules
├── package.json
└── test
├── bower.json
├── bower_components
├── index.html
└── spec

Just think of Yeoman as a helpful robot that does all the hard work for you, creating all the necessary files and folders to get started with development.

The build process

Yeoman includes Grunt, a task-based command-line tool for JavaScript projects. It is used to perform various build tasks on projects and exposes several useful tasks that you will want to use in your workflow. Yeoman automatically creates and configures Gruntfile.js, which specifies the configuration of the tasks and targets.

The following order of commands is used for a seamless development workflow:

$ yo webapp #scaffold application
$ bower install jquery #install dependency
$ grunt serve #start preview server
$ grunt test #run unit tests
$ grunt #create optimized build

Generally, a modern web developer's workflow consists of the following steps:

1. First, scaffold a new application using yo.

2. Search and install third-party client-side libraries using bower.

3. Start a preview server for development that allows you to write code, save it, and watch the results automatically become refreshed.

4. Then run the test task that executes the tests located in the test directory.

5. Then use the default grunt task to run the tests before creating an optimized build.

To view all the installed grunt tasks associated with the project, you can use the grunt –h command, which will output a list of all tasks and their descriptions.

The Connect LiveReload server

Now that you have all the initial files and folders for the project, you can really start to see the power of Yeoman.

Previewing the server

Connect LiveReload is the module that is a server that will auto reload when files are changed by the watch process.

To preview the application, execute the following command:

$ grunt serve

The serve task does a few things, which are as follows:

1. First, it removes all the files in the .tmp directory via the clean task.

2. It starts the Connect LiveReload server located at 127.0.0.1:9000, and opens the default web browser via the connect task.

3. Then, finally, it runs the watch task that monitors the project's source (app/*) files, thus executing subtasks on changes.

Your default browser should have opened up, displaying the following page:

Package management with Bower

Yeoman includes an excellent tool called Bower, which is a package manager for the Web and allows you to easily manage dependencies for your projects. Packages can include any type of assets such as JavaScript, images, and CSS. Twitter and the open source community actively maintain it.

Here are some of the available Bower commands:

- search: This command will search for a dependency in the Bower registry

- install: This command installs one or more dependencies

- list: This command lists all the dependencies installed in the project

- update: This command updates a dependency to the latest version

Let's go ahead and add a templating library to our webapp to handle the compilation of model data with HTML for the view. Open the terminal and execute the following command:

$ bower install handlebars --save

This command will download the Handlebars templating library and place the package in the app/bower_components directory.

To view all client-side dependencies associated with the project, just use the bower list command that will output a tree of all the installed components and their versions, and also inform us if updates are available, as shown in the following screenshot:

The preceding screenshot is the result of running the bower list command from within the project's root directory or the directory containing the bower.json file, which stores all the installed libraries.

The --save flag tells Bower to write the library name and version to the bower.json file located in the project's root directory. The bower.json file is how Bower manages the project's dependencies.

To wire up the newly downloaded package to the application's index.html page, execute the following grunt task:

$ grunt bowerInstall

This command will read the contents of the index.html file in the app folder. Then, look for the <!-- bower:js --> block and inject a script tag with the location of the component's main file for each package in the bower_components directory.

Code linting with JSHint

Yeoman includes JSHint, which is a linting tool that helps developers detect errors and potential problems in their JavaScript code; it is a great way to force best practices and improve the code quality. This is very useful when working with a large code base or in a team environment.

The jshint task is responsible for linting all code before it gets executed. The following screenshot shows an example of using the jshint task. It displays the errors that output when code fails the linking process:

Note

For more information on configuring JSHint, visit http://goo.gl/c2lsk1.

Let's begin to add some logic to the applications' main script file that was created during the initial scaffold. Open app/scripts/main.js and add the following:

/* global Handlebars */
(function () {
'use strict';
window.App = {
init: function (config) {
console.log( '1 - initialize' );
this.config = config;
if (this.config.feature && this.config.feature.endpoint) {
this.fetch();
}
return this;
},
render: function () {
console.log( '4 - render' );
var template = Handlebars.compile( $( this.config.el ).find( 'script[type="text/x-handlebars-template"]' ).html() );
var html = template( this.config );
$( this.config.el ).html( html );
return this;
},
onSuccess: function (response) {
console.log( '3 - onSuccess' );
this.config.features = response;
this.render();
},
onError: function (error) {
return this.log( error );
},
fetch: function () {
console.log( '2 - fetch' );
var self = this;
$.ajax( {
url: self.config.feature.endpoint,
dataType: 'jsonp',
success: function (results) {
return self.onSuccess( results );
},
error: function (error) {
return self.onError( error );
}
} );
}
};
})();

console.log( 'Allo, Allo!' );

Tip

Downloading the example code

You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.

Here is the breakdown of the preceding code:

1. First, we define Handlebars' global library to let JSHint know what we are doing.

2. Then, we create a new App object on the JavaScript global window object.

3. The render method takes the config.el property and renders the compiled template into the element.

4. The init function takes a config object as the argument and will be set on the class as the config property.

5. If the passed object has a feature.endpoint property, then the app will fetch features from that endpoint.

6. The render method will compile the Handlebars template with the config object to create the HTML output, which is injected into config.el.

7. The onSuccess method will set the model.features property to the results of the request and invoke the render method to display the contents.

8. The onError method will log the error to the console.

9. The fetch method will invoke a JSONP request to config.feature.endpoint.

Save the file, and the lint task will then compile into JavaScript and reload the browser; nothing will look different because we haven't added the application's template.

Automation

Yeoman comes with a watch task that is responsible for the automation of different tasks when developing web applications, such as compiling CoffeeScript to JavaScript when source files change or concatenating and parsing SASS files and then reloading the web browser to see changes.

The automation tasks are limited to the Grunt tasks that are defined by the generator; any changes to the .js or .html file in the app directory will automatically get parsed, and the browser will get refreshed. If the watch task detects changes to files in the test directory, then the unit tests are run via the test task.

Let's create the application's template; we will use the {{ }} double mustache syntax to render the dynamic content. Open the app/index.html file, and add the following contents inside the body element right below the browsehappy code line:

...
<![endif]-->
<div class="container">
<script type="text/x-handlebars-template">
<div class="header">
<ul class="nav nav-pills pull-right">
<li class="active">
<a href="/">Home</a>
</li>
{{#each menu}}
<li>
<a href="#{{route}}">{{name}}</a>
</li>
{{/each}}
</ul>
<h3 class="text-muted">{{sitetitle}}</h3>
</div>
<div class="jumbotron">
<h1>{{feature.title}}</h1>
<img src="{{feature.image}}"/>
<p class="lead">{{feature.body}}</p>
</div>
<div class="marketing">
{{#each features}}
<div class="media">
<a class="pull-left">
<img src="{{ image }}" class="img-thumbnail"/>
</a>
<div class="media-body">
<h4 class="media-heading">{{ title }}</h4>
<p>{{ body }}</p>
</div>
</div>
{{/ each }}
</div>
<div class="footer">
<p>{{sitecopy}}</p>
</div>
</script>
</div>
<!-- build:js scripts/vendor.js -->

The preceding code is very similar to the HTML that was created by Yeoman, except we are replacing the static content with data for Handlebars to compile the template with dynamic configuration data. Now, let's initialize the application by adding the following script block at the bottom of the app/index.html file in the app folder that was created during the initial scaffold:

<!-- build:js({app,.tmp}) scripts/main.js -->
<script src="scripts/main.js"></script>
<!-- endbuild -->
<script>
$(document).ready(function() {
App.init({
el: '.container',
sitetitle : 'Learning Yeoman',
sitecopy : '2014 Copyright',
version: '0.0.1',
feature : {
title : 'Chapter 1',
body : 'a starting point for a modern web app.',
image : 'http://goo.gl/kZZ6dX',
endpoint : 'http://jonniespratley.me:8181/api/v2/learning-yeoman-ch1/posts'
},
features : null,
menu: [
{name: 'About', route: '/about'},
{name: 'Contact', route: '/contact'}
]
});
});
</script>

In the preceding code, we perform the following steps:

1. First, we use jQuery's ready method to wait for the document to finish loading before executing the contents.

2. Then, a new instance of the App class is created, passing in the site configuration options.

3. The endpoint property is set to a simple API endpoint used to access the features data.

4. The el property is set to the div.container element in the index.html page.

5. Then, general site information such as the title, version, and copyright is declared.

6. The site feature information is populated with default content, and the site navigation menu array is defined with two items that represent pages.

7. Save the file, and you will see your browser automatically reloading with something similar to the following screenshot:

Testing with PhantomJS

If testing is not a part of your workflow, it should be! Yeoman makes it incredibly easy to test your application by setting up a testing environment with the Mocha framework. Other options include Jasmine, QUnit, and just about any other framework. That helpful robot (Yeoman) just saved hours of development time by creating all the necessary configuration files during the initial project scaffold.

Several testing tasks can be customized to do many useful things like showing test results in JUnit for use in many continuous integration systems such as Jenkins and Bamboo. Perform the following steps:

1. Open the Gruntfile.js file, locate the mocha task object around line #138, and configure the specific options for this target, as follows:

...
//#138 - Mocha testing framework configuration options
mocha: {
all: {
options: {
run: true,
log: true,
reporter: 'Spec',
files: ['<%= config.app %>/scripts/{,*/}*.js']
}
}
},
...

In the preceding code:

- We specify where the source of the scripts are by the files property

- The run and log properties, as you may have guessed, log the output and run the tests

- The reporter property is Spec, so it will display the message in the console

2. Then, the files property sets the location of the test specs. Open the default test the generator created, test/spec/test.js, and add the following:

/*global App, expect, it, describe */
'use strict';
var testApp = null;
var config = {
el: '.container',
sitetitle: 'Learning Yeoman',
sitecopy: '2014 Copyright',
version: '0.0.1',
feature: {
title: 'Chapter 1',
body: 'a starting point for a modern web application.',
image: 'http://goo.gl/kZZ6dX',
endpoint: '/posts'
},
features: null
};
testApp = App.init( config );
describe( 'Learning Yeoman Chapter 1 Test', function () {
describe( 'App.init', function () {
it( 'should store config on instance', function (done) {
expect( testApp.config.version, 'App.config' ).to.equal( config.version );
done();
} );
} );
} );

In the preceding code:

- First, the JShint configuration at the top defines global variables used in the spec

- The describe block contains some local variables that define the application's configuration

- The it block is testing if the configuration options passed to the App.init method are correctly set on the config property of the testApp instance

Running tests

To run the tests, use the following command:

$ grunt test

The output in the console should look similar to the following screenshot:

As we can see from the output, the configuration of PhantomJS is already taken care of. It nicely starts the PhantomJS browser; after connecting to the browser, it logs the output to the console as the tests run.

Optimizing for production

The default Grunt task ($ grunt) takes care of optimizing your entire project by doing the following:

- Compiles and concatenates all style sheets together

- Minifies all referenced third-party libraries into a separate file

- Groups Angular modules into a separate minimized file

- Then, it combines all application scripts into one separate file

- All HTML files and images are processed through their corresponding optimizer

- All processed files have a revision number appended to the filename

- All built files are located in the dist directory, making your application ready for deployment

- To preview what your application runs like once optimized, execute the following command:

$ grunt serve:dist

- Your webapp is fully optimized with fewer requests; now, it loads much faster in the browser, as shown in the following screenshot:

See for yourself; open Chrome Developer Tools and click on the Network tab.

Self-test questions

The following are some questions that you should be able to answer after reading this chapter. If you get stuck on a question, the answers are located in the Appendix.

1. Which four Yeoman generators are the most popular among the community?

2. What are the three core tools used in Yeoman?

3. What is the general developer tool workflow when using Yeoman?

4. Which platform and environment does Yeoman run in?

5. Who created Yeoman?

Summary

That was a lot to take in for the first chapter, but we have much more to cover. In this chapter, we learned how to install all the tools in the Yeoman workflow with one easy command: npm install yo -g. We learned about the commands that the Bower, Grunt, and Yo webapp generators have to offer. We also got to see the LiveReload server in action while making changes to the generated files.

We were able to make sure our coding syntax was error-free via JSHint. We also got our hands dirty configuring the mocha task and a unit test to make sure the app is functioning properly; we wrapped it up by taking a look at the optimization that takes place when your project is ready to ship.

Next, we are going to turn it up a notch by introducing the most popular Yeoman generators in the community: the Angular, Backbone, and Ember generators.

Chapter 2. Getting Started

This chapter is going to cover getting started with Yeoman generators. We will explore the four most popular official generators, their options and the subgenerators they expose, and the purpose of using them. This chapter is going to cover the following topics:

- The basics of the Yeoman workflow

- Installing the following commonly-used official generators:

- The generator-webapp: This is used to create a generic web application

- The generator-angular: This is used to create an AngularJS web application

- The generator-backbone: This is used to create a Backbone.js web application

- The generator-ember: This is used to create an Ember.js web application

- The subgenerators that these modules expose

- We do not cover any of the grunt tasks that are configured with the projects after they are scaffolded

- The life cycle of creation to deployment using these generators

Yo – generators

What makes Yeoman amazing are the generators. There is a robust set of templates (commonly referred to as generators) for any type of project. Finding a generator to install and use is quite simple as well; just use the npm search generator-[name] command to search for a generator where the name matches.

The Yeoman workflow

The general workflow to follow when using Yo, Bower, and Grunt is as follows:

1. Install a generator as follows:

$ npm install generator-[name]

2. Scaffold a project:

$ yo [generator] [args] [options]

3. Install a dependency:

$ bower install [dependency#version] [options]

4. Test the project:

$ grunt test

5. Preview the project:

$ grunt serve

6. Build the project for deployment:

$ grunt

Official generators

Here is a list of the official generators maintained by the Yeoman team. Use these as a base to create a custom generator or to create a project:

- polymer: This is a generator used to create Polymer webapps and components

- chromeapp: This is a generator used to create a Google Chrome application

- jquery: This is a generator used to create a custom jQuery library

- gruntfile: This is a generator used to create a basic Gruntfile

- commonjs: This is a generator used to create a CommonJS module, including nodeunit unit tests

- nodejs: This is a generator used to create a Node.js module, including nodeunit unit tests

Other generators include webapp, angular, backbone, ember, jasmine, karma, mocha, bootstrap, mobile, gruntplugin, and chrome-extension.

Note

For more information on official generators, visit http://goo.gl/mYhTgw.

The generator-webapp

The generator-webapp is the webapp generator for Yeoman that scaffolds a generic web application, including unit testing.

Features

The generator-webapp has some great features worth noting, which include the following:

- It automatically wires up Bower components with the bower-install task

- It provides Mocha or Jasmine unit testing with PhantomJS

- It contains Twitter Bootstrap for Syntactically Awesome Stylesheets (SASS)

- It provides an optimized Modernizr build

Installing the generator-webapp

To use this generator, all that is required is the module installation. This is easily done with the Node package manager (npm). To install it, use the following command:

$ npm install -g generator-webapp

Note

The -g flag requires an administrator user.

Using the generator-webapp

The following is how the generator-webapp is invoked from the command line:

$ yo webapp [name] [options]

Options

The webapp generator has the following options available:

Option

Description

Defaults

--skip-install

Skips automatic execution of Bower and npm

False

--test-framework

Sets the testing framework

Mocha

--coffee

Enables support for CoffeeScript

False

Note

These options are applied during the initial project's scaffold.

Example usage

To use the generator, simply open a terminal and execute the following:

$ yo webapp hello-webapp

The preceding command will scaffold a new webapp project with the name hello-webapp.

You will be prompted to answer some questions that the generator asks, such as whether to:

- Use SASS with Compass

- Include Twitter Bootstrap

- Include Modernizer

After answering these questions, the project files are created, the dependencies are downloaded and installed into the bower_components directory, and the application is ready to preview.

Previewing

To run the application, you can use the following command to start the server and open a default web browser that displays the application:

$ grunt serve

On opening, your default web browser should display a page similar to the following screenshot:

Conclusion

Typically, one will want to consider using the webapp generator when looking to create a scaffold out of a simple website that can be easily deployed to Heroku or another cloud application server for hosting.

The generator-angular

This is the AngularJS generator for Yeoman, which quickly scaffolds a new project with sensible defaults and community best practices. This is one of the most popular generators in the community, with more forks and stars on Github than any other.

Features

Some notable features that the generator-angular includes are as follows:

- It is based on the angular-seed project

- It includes ngCookies, ngSanitize, ngResource, and ngRoute modules

- It includes Twitter Bootstrap with or without SASS

- It provides full CoffeeScript support

Installing the generator-angular

The following command is used to install the AngularJS generator:

$ npm install -g generator-angular

Note

The -g flag requires an administrator user.

Using the generator-angular

This is how to use this new generator:

$ yo angular[:subgenerator] [args] [options]

Options

The following options are available when invoking the generator-angular:

Option

Description

Defaults

--help

Prints the generator's options and usage

False

--app-suffix

Allows a custom suffix to be added to the module name

False

--coffee

Generates CoffeeScript instead of JavaScript

False

--minsafe

Generates AngularJS minification safe code

False

Example usage

The following is an example of using the generator to create a new AngularJS application:

$ yo angular hello-angular

The preceding command will invoke the generator with the application name set to hello-angular.

Angular subgenerators

The following subgenerators are provided with the generator-angular; all of the subgenerators listed as follows can run independently:

- angular:app: The angular or angular:app command is used to create the initial AngularJS application. This is shown in the following code:

$ yo angular:app [name]

- angular:common: The angular:common subgenerator is used to create the general default files used for an AngularJS application. This is shown in the following code:

$ yo angular:common [name]
create app/scripts/services/[name].js

- angular:constant: The angular:constant subgenerator is used to create a module that is available during the configuration and run phases of the application's life cycle, which Angular cannot override. This is shown in the following code:

$ yo angular:constant [name]
create app/scripts/services/[name].js
create test/spec/services/[name].js

- angular:decorator: The angular:decorator subgenerator is used to create a decorator module that can be used to intercept the creation of a service, allowing methods to be overridden or modified during the configuration phase of the services' life cycle. This is shown in the following code:

$ yo angular:decorator [name]

- angular:directive: The angular:directive subgenerator is used to create an Angular directive. This module allows you to create reusable view components for the application. This is shown in the following code:

$ yo angular:directive [name]
create app/scripts/directives/[name].js
create test/spec/directives/[name].js

- angular:factory: The angular:factory subgenerator is used to create an Angular factory. This module can be used to create reusable pieces of the application logic. This is shown in the following code:

$ yo angular:factory [name]
create app/scripts/services/[name].js
create test/spec/services/[name].js

- angular:main: The angular:main subgenerator is used to create the main Angular module used to Bootstrap an Angular application and configure the router. This is shown in the following code:

$ yo angular:main [name]

- angular:provider: The angular:provider subgenerator is used to create an Angular provider module used to expose an API for application-wide configuration that must be made before the application starts. This is shown in the following code:

$ yo angular:provider [name]

- angular:route: The angular:route subgenerator is used to create an Angular view template and controller, and add a route to the application's router. This module allows you to handle the routes for the application. This is shown in the following code:

$ yo angular:route [name]
create app/scripts/controllers/[name].js
create test/spec/controllers/[name].js
create app/views/[name].html

- angular:service: The angular:service subgenerator is used to create an Angular service module. This module allows you to create reusable business logic for the application. This is shown in the following code:

$ yo angular:service [name]
create app/scripts/services/[name].js
create test/spec/services/[name].js

- angular:value: The angular:value subgenerator is used to create an Angular value module. This module allows you to define simple objects or strings for the application. This is shown in the following code:

$ yo angular:value [name]

- angular:view: The angular:view subgenerator is used to create an Angular view template. This template allows you to use AngularJS directives and data-binding syntax to display data from the controllers' scope.

$ yo angular:view [name]
create app/views/[name].html

Previewing

To run the application, you use the serve task to start the preview server, which opens a default web browser that displays the application; append :dist to start the server in the distribution mode. For example, to run the application execute the following command:

$ grunt serve

After running the command, your default web browser should display a page similar to the following screenshot:

This is the example running in serve mode; the watch task is running in the background, enabling changes to LiveReload upon save; give it a try and modify the source file.

Conclusion

The generator-angular is a great generator used to create new AngularJS projects that are already preconfigured to build, test, and run with minimal effort for the developer.

The generator-backbone

The Backbone.js generator for Yeoman provides a functional boilerplate application out of the box. It includes access to a number of subgenerators that can be easily used to create individual models, views, collections, and routers.

Features

Some notable features that the generator-backbone includes are as follows:

- Require.js (AMD) support

- Full CoffeeScript support

- R.js build optimization of all AMD modules (the Require.js option)

- Unit testing with PhantomJS

Installing the generator-backbone

The following is the command used to install the Backbone.js generator for Yeoman:

$ npm install -g generator-backbone

Note

The -g flag requires an administrator user.

Using the generator-backbone

The following shows how to use this new generator:

$ yo backbone[:subgenerator] [args] [options]

Options

Here are the options available when invoking the Backbone generator:

Option

Description

Defaults

--appPath

Scaffolds into a custom directory

Null

--coffee

Enables scaffolds in CoffeeScript

Null

--requirejs

Scaffolds using Require.js (AMD) loader

Check for Require.js

--skip-install

Skips the automatic execution of Bower and npm

False

--test-framework

Sets the default testing framework

Mocha

--template-framework

Sets the default template framework

Lodash

Example usage

Here, you can see an example of using the Backbone.js generator to create a new project:

$ yo backbone hello-backbone

In this command, the backbone generator is invoked with hello-backbone as the application name.

Backbone subgenerators

The following subgenerators are provided with the generator-backbone module:

- The backbone:app subgenerator is used to create new Backbone applications. This is shown in the following code:

$ yo backbone:app myApp

- The backbone:all subgenerator is used to create a Backbone model, collection, view, route, and template for the passed parameter. This is shown in the following code:

$ yo backbone:all tags
create app/scripts/models
create app/scripts/collections
create app/scripts/views
create app/scripts/routes
create app/scripts/helpers
create app/scripts/templates

- The backbone:model subgenerator is used to create new Backbone models for the application. The Backbone.Model class provides a set of basic methods and events such as change, invalid, destroy, and add to manage changes to data. This is shown in the following code:

$ yo backbone:model post
create app/scripts/models/post.coffee

- The backbone:collection subgenerator is used to create new Backbone collections for the application. The Backbone.Collection class is an ordered set of models and has methods to manage collections of model data. This is shown in the following code:

$ yo backbone:collection posts
create app/scripts/collections/posts.coffee

- The backbone:view subgenerator is used to create new Backbone views for the application. The Backbone.View class provides an organized structure to add custom event handlers that model data and the logic in the view. They render to display the model or collect data. This is shown in the following code:

$ yo backbone:view posts
create app/scripts/templates/posts.ejs
create app/scripts/views/posts.coffee

- The backbone:router subgenerator is used to create new Backbone routers for the application. The Backbone.Router class provides a set of methods for handling the routing in a client-side web application by connecting (#/route) routes to actions and methods.

$ yo backbone:router posts
create app/scripts/routes/posts.coffee

Previewing

To run the application, you can use the grunt serve task command to start the preview server and open a default web browser that displays the application; append :dist to the view in the production mode. This is shown in the following code:

$ grunt serve

The following screenshot is the hello-backbone example that runs in the serve mode. This is the default layout that is created during the initial scaffold:

Conclusion

The best way to get started with Backbone applications is definitely to use the generator-backbone Yeoman generator. It takes all the pain out of creating a new Require.js Backbone application, with unit testing and R.js optimized build configuration already set up.

The generator-ember

The generator-ember generator handles creating Ember applications; it is based on the Ember.js starter app as the base application.

Features

Some notable features that the generator-ember includes are as follows:

- Optional CoffeeScript support

- Unit testing with Karma and PhantomJS

- Jasmine or Mocha test framework

Installing the generator-ember

This is how to install the Ember.js generator for Yeoman:

$ npm install -g generator-ember

Using the generator-ember

To use the generator, simply open a console and execute the following:

$ yo ember [name] [options]

Options

These are the options available when invoking the Ember generator:

Option

Description

Defaults

--coffee

Generates scaffolds in CoffeeScript

False

--skip-install

Skips the automatic execution of Bower and npm

False

--test-framework

Enables different testing frameworks like Jasmine

Mocha

--karma

Enables support for the Karma test runner

Null

Example usage

Here, you can see an example that uses the Ember.js generator to create a new application:

$ yo ember hello-ember

In this command, the ember generator is invoked with hello-ember as the app name.

You will be prompted with some questions that the generator asks. After you have answered the questions, and the installation of the project dependencies is complete, you can then preview your new application by running it.

Ember subgenerators

The subgenerators that the Ember generator includes are as follows:

- The ember:app subgenerator is used to create the initial Ember application. The Ember.Application class helps to instantiate, initialize, and coordinate the objects in the app. This is shown as follows:

$ yo ember:app myApp

Each Ember project has one and only one Ember.Application. This object is created during the initial scaffold of the generator and is the name of the project's folder.

- The ember:model subgenerator is used to create a new Ember model. The DS.Model class is an object that stores a persisted state; templates use models to display data to the user. This is shown as follows:

$ yo ember:model book
create app/scripts/models/book_model.js
create app/scripts/controllers/books_controller.js
create app/scripts/controllers/book_edit_controller.js
create app/scripts/routes/books_route.js
create app/scripts/routes/book_route.js
create app/scripts/routes/book_edit_route.js
create app/scripts/views/book_view.js
create app/scripts/views/book_edit_view.js
create app/scripts/views/books_view.js
create app/templates/book.hbs
create app/templates/book/edit.hbs
create app/templates/books.hbs
create app/scripts/router.js

- The ember:router subgenerator is used to create a new Ember router. The Ember.Router class manages the application state and URLs. This is shown as follows:

$ yo ember:router books
create app/scripts/router.js

- The ember:view subgenerator is used to create a new Ember view. The Ember.View class manages the combining of model data with templates and responds to user events. This is shown as follows:

$ yo ember:view library
create app/scripts/views/library_view.js
create app/scripts/views/library_edit_view.js
create app/scripts/views/libraries_view.js
create app/templates/library.hbs
create app/templates/library/edit.hbs
create app/templates/libraries.hbs

Previewing

To run the application, you can use the following command to start the server and open a default web browser that displays the application; you can use the serve:dist task to the view in the production mode:

$ grunt serve

On opening, your default web browser should display a page that looks similar to the following screenshot:

Conclusion

The Ember.js generator is built off the ember-app boilerplate and is a great start for development. Something worth noting is that the subgenerators always generate CRUD-type application files and do not allow much customization. Further features are on the road map, which will hopefully solve some of these issues.

Self-test questions

The following are questions you should be able to answer at the end of this chapter:

1. What are the names of at least three official Yeoman generators?

2. Which Angular subgenerator creates a view and a controller, and wires the application's router?

3. Which is the default test framework for the Backbone generator?

4. Which option is common among the four most popular Yeoman generators?

5. Which command is used to search for only Yeoman generators?

6. Which Ember subgenerator will create a model, controller, route, view, and template?

7. Which is the default test framework for the Ember generator?

Summary

In this chapter, we covered the installation and usage of the four most popular official Yeoman generators:

- The generator-webapp

- The generator-angular

- The generator-backbone

- The generator-ember

We also covered using the subgenerators of each generator to assist in creating various parts of an application in the desired framework.

In the next chapter, we are going to use the AngularJS generator in detail. We will explore each subgenerator, and create the individual pieces of a single page CRUD application. We'll also explore writing unit tests for each of the AngularJS modules as well as setting up an end-to-end test environment to automate the functionality of the application.

Chapter 3. My Angular Project

AngularJS is a client-side MV* JavaScript framework with tons of features that give web applications structure with two-way data binding and declarative markup, allowing an ease in development of any type of application.

This chapter is going to cover how to use the Angular Yeoman generator to create a single page application that can be easily extended into something more. We will use the angular subgenerators to create the pieces of the application, giving you insight into using the angular generator in a real world project today.

By the end of this chapter, you should have a firm understanding of AngularJS's core concepts and how it fits into a modern web application.

In this chapter, we are going to cover the following:

- Concepts of AngularJS and how to leverage the framework in a new or existing project

- Using the different angular subgenerators to create a CRUD application that uses custom filters, directives, and services

- Getting acquainted with how an AngularJS application is structured using the Yeoman Angular generator

Anatomy of an Angular project

Generally in a
single page application (SPA), you create modules that contain a set of functionality, such as a view to display data, a model to store data, and a controller to manage the relationship between the two. Angular incorporates the basic principles of the MVC pattern into how it builds client-side web applications.

The major Angular concepts are as follows:

- Templates: A template is used to write plain HTML with the use of directives and JavaScript expressions

- Directives: A directive is a reusable component that extends HTML with the custom attributes and elements

- Models: A model is the data that is displayed to the user and manipulated by the user

- Scopes: A scope is the context in which the model is stored and made available to controllers, directives, and expressions

- Expressions: An expression allows access to variables and functions defined on the scope

- Filters: A filter formats data from an expression for visual display to the user

- Views: A view is the visual representation of a model displayed to the user, also known as the Document Object Model (DOM)

- Controllers: A controller is the business logic that manages the view

- Injector: The injector is the dependency injection container that handles all dependencies

- Modules: A module is what configures the injector by specifying what dependencies the module needs

- Services: A service is a piece of reusable business logic that is independent of views

- Compiler: The compiler handles parsing templates and instantiating directives and expressions

- Data binding: Data binding handles keeping model data in sync with the view

Why Angular?

AngularJS is an open source JavaScript framework known as the
Superheroic JavaScript MVC Framework, which is actively maintained by the folks over at Google. Angular attempts to minimize the effort in creating web applications by teaching the browser's new tricks. This enables the developers to use declarative markup (known as directives or expressions) to handle attaching the custom logic behind DOM elements.

Angular includes many built-in features that allow easy implementation of the following:

- Two-way data binding in views using double mustaches {{ }}

- DOM control for repeating, showing, or hiding DOM fragments

- Form submission and validation handling

- Reusable HTML components with self-contained logic

- Access to RESTful and JSONP API services

The major benefit of Angular is the ability to create individual modules that handle specific responsibilities, which come in the form of directives, filters, or services. This enables developers to leverage the functionality of the custom modules by passing in the name of the module in the dependencies.

Creating a new Angular project

Now it is time to build a web application that uses some of Angular's features. The application that we will be creating will be based on the scaffold files created by the Angular generator; we will add functionality that enables CRUD operations on a database.

Installing the generator-angular

To install the Yeoman Angular generator, execute the following command:

$ npm install -g generator-angular

Note

For Karma testing, the generator-karma needs to be installed.

Scaffolding the application

To scaffold a new AngularJS application, create a new folder named learning-yeoman-ch3 and then open a terminal in that location. Then, execute the following command:

$ yo angular --coffee

This command will invoke the AngularJS generator to scaffold an AngularJS application, and the output should look similar to the following screenshot:

Understanding the directory structure

Take a minute to become familiar with the directory structure of an Angular application created by the Yeoman generator:

- app: This folder contains all of the front-end code, HTML, JS, CSS, images, and dependencies:

- images: This folder contains images for the application

- scripts: This folder contains AngularJS codebase and business logic:

- app.coffee: This contains the application module definition and routing

- controllers: Custom controllers go here:

- main.coffee: This is the main controller created by default

- directives: Custom directives go here

- filters: Custom filters go here

- services: Reusable application services go here

- styles: This contains all CSS/LESS/SASS files:

- main.css: This is the main style sheet created by default

- views: This contains the HTML templates used in the application

- main.html: This is the main view created by default

- index.html: This is the applications' entry point

- bower_components: This folder contains client-side dependencies

- node_modules: This contains all project dependencies as node modules

- test: This contains all the tests for the application:

- spec: This contains unit tests mirroring structure of the app/scripts folder

- karma.conf.coffee: This file contains the Karma runner configuration

- Gruntfile.js: This file contains all project tasks

- package.json: This file contains project information and dependencies

- bower.json: This file contains frontend dependency settings

Note

The directories (directives, filters, and services) get created when the subgenerator is invoked.

Configuring the application

Let's go ahead and create a configuration file that will allow us to store the application wide properties; we will use the Angular value services to reference the configuration object.

Open up a terminal and execute the following command:

$ yo angular:value Config

This command will create a configuration service located in the app/scripts/services directory. This service will store global properties for the application.

Note

For more information on Angular services, visit http://goo.gl/Q3f6AZ.

Now, let's add some settings to the file that we will use throughout the application. Open the app/scripts/services/config.coffee file and replace with the following code:

'use strict'
angular.module('learningYeomanCh3App').value('Config', Config =
baseurl: document.location.origin
sitetitle: 'learning yeoman'
sitedesc: 'The tutorial for Chapter 3'
sitecopy: '2014 Copyright'
version: '1.0.0'
email: 'jonniespratley@gmail.com'
debug: true
feature:
title: 'Chapter 3'
body: 'A starting point for a modern angular.js application.'
image: 'http://goo.gl/YHBZjc'
features: [
title: 'yo'
body: 'yo scaffolds out a new application.'
image: 'http://goo.gl/g6LO99'
,
title: 'Bower'
body: 'Bower is used for dependency management.'
image: 'http://goo.gl/GpxBAx'
,
title: 'Grunt'
body: 'Grunt is used to build, preview and test your project.'
image: 'http://goo.gl/9M00hx'
]
session:
authorized: false
user: null
layout:
header: 'views/_header.html'
content: 'views/_content.html'
footer: 'views/_footer.html'
menu: [
title: 'Home', href: '/'
,
title: 'About', href: '/about'
,
title: 'Posts', href: '/posts'
]
)

The preceding code does the following:

- It creates a new Config value service on the learningYeomanCh3App module

- The baseURL property is set to the location where the document originated from

- The sitetitle, sitedesc, sitecopy, and version attributes are set to default values that will be displayed throughout the application

- The feature property is an object that contains some defaults for displaying a feature on the main page

- The features property is an array of feature objects that will display on the main page as well

- The session property is defined with authorized set to false and user set to null; this value gets set to the current authenticated user

- The layout property is an object that defines the paths of view templates, which will be used for the corresponding keys

- The menu property is an array that contains the different pages of the application

Note

Usually, a generic configuration file is created at the top level of the scripts folder for easier access.

Creating the application definition

During the initial scaffold of the application, an app.coffee file is created by Yeoman located in the app/scripts directory. The scripts/app.coffee file is the definition of the application, the first argument is the name of the module, and the second argument is an array of dependencies, which come in the form of angular modules and will be injected into the application upon page load.

The app.coffee file is the main entry point of the application and does the following:

- Initializes the application module with dependencies

- Configures the applications router

Any module dependencies that are declared inside the dependencies array are the Angular modules that were selected during the initial scaffold. Consider the following code:

'use strict'
angular.module('learningYeomanCh3App', [
'ngCookies',
'ngResource',
'ngSanitize',
'ngRoute'
])
.config ($routeProvider) ->
$routeProvider
.when '/',
templateUrl: 'views/main.html'
controller: 'MainCtrl'
.otherwise
redirectTo: '/'

The preceding code does the following:

- It defines an angular module named learningYeomanCh3App with dependencies on the ngCookies, ngSanitize, ngResource, and ngRoute modules

- The .config function on the module configures the applications' routes by passing route options to the $routeProvider service

Note

Bower downloaded and installed these modules during the initial scaffold.

Creating the application controller

Generally, when creating an Angular application, you should define a top-level controller that uses the $rootScope service to configure some global application wide properties or methods. To create a new controller, use the following command:

$ yo angular:controller app

This command will create a new AppCtrl controller located in the app/scripts/controllers directory.

Open the app/scripts/controllers/app.coffee file and replace with the following code:

'use strict'

angular.module('learningYeomanCh3App')
.controller('AppCtrl', ($rootScope, $cookieStore, Config) ->
$rootScope.name = 'AppCtrl'
App = angular.copy(Config)
App.session = $cookieStore.get('App.session')
window.App = $rootScope.App = App)

The preceding code does the following:

- It creates a new AppCtrl controller with dependencies on the $rootScope, $cookieStore, and Config modules

- Inside the controller definition, an App variable is copied from the Config value service

- The session property is set to the App.session cookie, if available

Creating the application views

The Angular generator will create the applications' index.html view, which acts as the container for the entire application. The index view is used as the shell for the other views of the application; the router handles mapping URLs to views, which then get injected to the element that declares the ng-view directive.

Modifying the application's index.html

Let's modify the default view that was created by the generator. Open the app/index.html file, and add the content right below the following HTML comment:

<!-- Add your site or application content here -->

The structure of the application will consist of an article element that contains a header, section, and footer. Replace with the following content:

<article id="app" ng-controller="AppCtrl" class="container">
<header id="header" ng-include="App.layout.header"></header>
<section id="content" class="view-animate-container">
<div class="view-animate" ng-view=""></div>
</section>
<footer id="footer" ng-include="App.layout.footer"></footer>
</article>

In the preceding code:

- The article element declares the ng-controller directive to the AppCtrl controller

- The header element uses an ng-include directive that specifies what template to load, in this case, the header property on the App.layout object

- The div element has the view-animate-container class that will allow the use of CSS transitions

- The ng-view attribute directive will inject the current routes view template into the content

- The footer element uses an ng-include directive to load the footer specified on the App.layout.footer property

Note

Use ng-include to load partials, which allows you to easily swap out templates.

Creating Angular partials

Use the yo angular:view command to create view partials that will be included in the application's main layout. So far, we need to create three partials that the index view (app/index.html) will be consuming from the App.layout property on the $rootScope service that defines the location of the templates.

Note

Names of view partials typically begin with an underscore (_).

Creating the application's header

The header partial will contain the site title and navigation of the application. Open a terminal and execute the following command:

$ yo angular:view _header

This command creates a new view template file in the app/views directory.

Open the app/views/_header.html file and add the following contents:

<div class="header">
<ul class="nav nav-pills pull-right">
<li ng-repeat="item in App.menu"
ng-class="{'active': App.location.path() === item.href}">
<a ng-href = "#{{item.href}}"> {{item.title}} </a>
</li>
</ul>
<h3 class="text-muted"> {{ App.sitetitle }} </h3>
</div>

The preceding code does the following:

- It uses the {{ }} data binding syntax to display App.sitetitle in a heading element

- The ng-repeat directive is used to repeat each item in the App.menu array defined on $rootScope

Creating the application's footer

The footer partial will contain the copyright message and current version of the application. Open the terminal and execute the following command:

$ yo angular:view _footer

This command creates a view template file in the app/views directory.

Open the app/views/_footer.html file and add the following markup:

<div class="app-footer container clearfix">
<span class="app-sitecopy pull-left">
{{ App.sitecopy }}
</span>
<span class="app-version pull-right">
{{ App.version }}
</span>
</div>

The preceding code does the following:

- It uses a div element to wrap two span elements

- The first span element contains data binding syntax referencing App.sitecopy to display the application's copyright message

- The second span element also contains data binding syntax to reference App.version to display the application's version

Customizing the main view

The Angular generator creates the main view during the initial scaffold. Open the app/views/main.html file and replace with the following markup:

<div class="jumbotron">
<h1>{{ App.feature.title }}</h1>
<img ng-src="{{ App.feature.image }}"/>
<p class="lead">
{{ App.feature.body }}
</p>
</div>
<div class="marketing">
<ul class="media-list">
<li class="media feature" ng-repeat="item in App.features">
<a class="pull-left" href="#">
<img alt="{{ item.title }}"
src="http://placehold.it/80x80"
ng-src="{{ item.image }}"
class="media-object"/>
</a>
<div class="media-body">
<h4 class="media-heading">{{item.title}}</h4>
<p>{{ item.body }}</p>
</div>
</li>
</ul>
</div>

The preceding code does the following:

- At the top of the view, we use the {{ }} data binding syntax to display the title and body properties declared on the App.feature object

- Next, inside the div.marketing element, another div element is declared with the ng-repeat directive to loop for each item in the App.features property

- Then, using the {{ }} data binding syntax wrapped around the title and body properties from the item being repeated, we output the values

Previewing the application

To preview the application, execute the following command:

$ grunt serve

Your browser should open displaying something similar to the following screenshot:

Tip

Download the AngularJS Batarang (http://goo.gl/0b2GhK) developer tool extension for Google Chrome for debugging.

Testing an Angular application

Testing an Angular application is very easy when using Yeoman because all the configuration files are created during the initial project scaffold and all the subgenerators create a test spec that can easily be customized with specific functionality.

Angular unit tests

When scaffolding new files using the Yeoman Angular generator, the subgenerators create skeleton specs that are located in the test/spec directory. Yeoman makes it extremely easy to start testing your scripts.

Configuring the Karma Runner

Since the application is using various client-side dependencies, you will need to add the library locations to the test/karma.conf.coffee file:

//Line #15
files: [
//Bower Dependencies
'bower_components/jquery/jquery.js',
'bower_components/bootstrap/dist/js/bootstrap.js',
...
],

Running unit tests

To run the tests for the application, execute the following command:

$ grunt test

The preceding command will do the following:

1. It will compile all projects scripts in the app/scripts directory to the .tmp/scripts directory.

2. Then, invoke the Karma that will launch Chrome and start the runner.

3. It will show the output of the tests in the console as they go.

Note

You can invoke the Karma runner directly by executing: $ karma start test/karma.conf.coffee.

The results from the test task should look similar to the following screenshot:

End-to-end tests with Protractor

Being able to test the functionality of the code in the application is always a good thing, but being able to run actual browser tests that mimic the actions of a real user is priceless. The folks over at Angular have created a new end-to-end testing framework that goes by the name of
Protractor, which is built on top of the Selenium WebDriverJS.

The npm installs Protractor. To install the project, execute the following command:

$ npm install –g protractor

This command will install the Protractor library globally, which then can be invoked from any directory. After installing it, you will need to update Selenium; Protractor includes webdriver-manager, which allows you to update and start the standalone server.

To download and update the Selenium Server, execute the following command:

$ webdriver-manager update

The command above will download the latest driver from Selenium and place it in the correct directory. To start the server, use the following command:

$ webdriver-manager start

This command will kick off the Selenium Server process that is run in the background, allowing you to run your Protractor tests. You should see the following screenshot if it's working correctly:

Configuring Protractor

To configure Protractor to run tests against your application, you will need to create a configuration file that contains settings for the location of test specs, the driver URL, and so on. First, create a configuration file named e2e.conf.js in the projects root directory.

Open the e2e.conf.js file and add the following content:

exports.config = {
seleniumAddress: 'http://localhost:4444/wd/hub',
capabilities: {
'browserName': 'chrome'
},
specs: ['test/e2e/*.js'],
jasmineNodeOpts: {
showColors: true
}
};

In the preceding code:

- The exports.config object holds properties that are passed to Protractor

- The seleniumAddress property specifies the location of the WebDriver server that is running

- The capabilities property contains the browser capabilities to enable; here we are specifying to use the Chrome browser; other options can include PhantomJS

- The specs property specifies the location of the test specs to run

- The jasmineNodeOpts property specifies what options to pass to the Jasmine node module

Creating Protractor e2e spec

Now, let's create a simple example spec that will load the application and check if the site title matches what we are expecting. Create an empty file named app.js in the test/e2e directory.

Open the test/e2e/app.js file and add the following content:

describe('Chapter3 e2e:', function() {
beforeEach(function() {
browser.get('http://localhost:9000');
});
it('should have site title', function() {
var siteTitle;
siteTitle = element(protractor.By.binding('App.sitetitle'));
expect(siteTitle.getText()).toEqual('Learning Yeoman');
});
});

To run the tests, just open the terminal and execute the following command:

$ protractor e2e.conf.js

This command will start the Protractor runner and execute the specs specified in the configuration file.

Angular controllers

The controllers in an Angular app are the most important things in the application. They define the scope for the view, which acts as a proxy by receiving user actions or input and performing the proper operations with the assistance of Angular services and/or custom services that third party modules expose.

Creating controllers

Creating Angular controllers is very easy using the angular:controller generator; to create a new controller, open the terminal and execute the following command:

$ yo angular:controller posts

The preceding command creates a new PostsCtrl controller located in the app/scripts/controllers directory.

Using controllers

Generally, in Angular, you use controllers to handle view-specific interactions and handle setting up the model data for the templates to display.

To demonstrate this, open the app/scripts/controllers/posts.coffee file and add the following code:

'use strict'
angular.module('learningYeomanCh3App').controller 'PostsCtrl', ($scope, $location, Posts) ->
$scope.name = 'Posts'
$scope.posts = Posts.query()

$scope.add = ()->
$location.path('/posts/new')

$scope.view = (id)->
$location.path('/posts/view/' + id)

The preceding code does the following:

- It defines a controller on the learningYeomanCh3App module and dependencies on the $scope, $location, and Posts services

- The name property is set on the scope with the value of Posts

- The posts property is set on the scope to the value returned from the Posts.query() method

- The add function on the scope will handle sending the browser to the /posts/new URL

- The view function on the scope will handle sending the browser to the /posts/view/:id URL, where id is dynamic and comes from the view

Note

For more information on controllers, visit http://goo.gl/8Yk1vg.

Testing controllers

The angular:controller generator creates a controller spec file when the angular:controller generator is invoked. The controller specs are located in the test/specs/controllers directory.

To demonstrate testing the controller created now, open the test/specs/controllers/posts.coffee spec and add the following code:

'use strict'
describe 'Controller: PostsCtrl', () ->
beforeEach module 'learningYeomanCh3App'

PostsCtrl = {}
scope = {}
location = {}

beforeEach inject ($controller, $rootScope, $location) ->
scope = $rootScope.$new()
location = $location
PostsCtrl = $controller 'PostsCtrl', {
$scope: scope
$location: location
}

it 'should have name equal to "Posts" on the scope', () ->
expect(scope.name).toBe('Posts')

it 'should change the location to /posts/new', () ->
location.path('/posts')
scope.add()
expect(location.path()).toEqual('/posts/new')

it 'should change the location to /posts/view/:id', () ->
location.path('/posts')
scope.view(1)
expect(location.path()).toEqual('/posts/view/1')

In the preceding code:

- At the top, the describe method is used to contain the inner code into a spec named Controller: PostsCtrl

- The beforeEach method handles loading the module named learningYeomanCh3App for testing

- The PostsCtrl, scope, and location private variables are declared and will be set inside another beforeEach method

- The beforeEach method runs before each spec defined below; inside the beforeEach method, we use the inject method to load the proper Angular services that will be referenced by the private variables defined above

- The first it block expects that the scope.name property matches 'Posts'

- The second it block handles testing the URL location when the add() method is invoked

- The third it block handles testing the URL location when the view(id) method is invoked

Angular services

In Angular, services are singleton objects or functions that contain methods common with web applications. Angular has a number of built-in services that simplify various tasks within a web application. Angular also supports custom services that allow a developer to create reusable modules with ease.

Creating services

The angular:service subgenerator handles creating a new Angular service. Let's create a simple $resource service that will be used to connect to a RESTful backend API service; open the terminal and execute the following command:

$ yo angular:service Posts

The preceding command does the following:

- It will create a new service located in the app/scripts/services directory

- This service will use the Angular $resource service

Using services

A service is a reusable piece of business logic independent of views. Let's add the logic to handle connecting to a RESTful backend API to access; open the app/scripts/services/posts.coffee file and add the following content:

'use strict'
angular.module('learningYeomanCh3App').factory 'Post', ($resource) ->
return $resource('/api/posts/:id', { id: '@_id' }, {
'query': method: 'GET', isArray: true
'update': method : 'PUT'
})

The preceding code does the following:

- Defines a new factory service named Post on the main module with a dependency on the $resource service

- The factory service returns a $resource instance with a custom update method that changes the request method to PUT

- This factory can be used to access RESTful API services

Testing services

The angular:service subgenerator will create a service test spec located in the test/spec/services directory when invoked. Open the test/spec/services/post.coffee file and add the following code:

'use strict'
describe 'Service: Post', () ->

beforeEach module 'learningYeomanCh3App'

Post = {}
httpBackend = null
mockData = [{_id: 1}, {_id:2}, {_id:3}]

beforeEach inject (_$httpBackend_, _Post_) ->
Post = _Post_
httpBackend = _$httpBackend_

it 'should fetch list of posts', () ->
httpBackend.expectGET('/api/posts').respond(mockData)
posts = null
promise = Post.query().$promise
promise.then((data)->
posts = data
)
expect(posts).toBeNull()
httpBackend.flush()
expect(posts.length).toEqual(3)

In the preceding code:

- At the top, the describe method is used to contain the inner code into a spec named Service: Post

- The beforeEach method handles loading the module named learningYeomanCh3App for testing

- The second beforeEach method injects the service for the spec, which is set to the local Post variable

- The it block expects that the Posts service should fetch a list of posts

- Inside the function, the httpBackend.expectGET method is used to expect a GET request to the passed argument that is the URL of the request and respond to the request with the mock data defined in the spec

- The local posts variable is set to null because the service should fulfill the promise with the returned data

- The expect method is used to ensure whether the posts variable is null before the request is made

- The httpBackend.flush() method is used to process any pending requests

- The last expect method is used to check whether the length of the posts variable matches the expected length, which is 3

Note

For more information on testing, visit http://goo.gl/BDo0jZ.

Angular filters

A filter formats the data for display to the user. In Angular, filters are used in expressions, which can be used in templates, controllers, or services. Angular includes several filters for formatting various types of data.

Note

For more information on filters, visit http://goo.gl/Mvhe0S.

Creating filters

The angular:filter subgenerator handles creating a new Angular filter. Let's create a markdown filter that will convert markdown to HTML; open the terminal and execute the following command:

$ yo angular:filter markdown

The preceding command will create a new filter located in the app/scripts/filters directory. This directive will use a third-party library for conversion, so execute the following command:

$ bower install markdown --save

This will download and install the markdown library in the app/bower_components directory.

To install this library into the app/index.html file, execute the following command:

$ grunt wiredep

The preceding command will attempt to wire all components listed in the project's bower.json file to the app/index.html page by adding a script tag referencing the main .js file defined in the components metadata.

Note

Remember to add this library to the Karma configuring file before running unit tests.

Using filters

The filter definition is simply a function that returns a string; the first argument of the function is the input value to be filtered and any arguments passed after are parameters passed to the filter function. Open the app/scripts/filters/markdown.coffee file and add the following content:

'use strict'
angular.module('learningYeomanCh3App').filter 'markdown', () ->
(input, truncate) ->
input = input.substring(0, truncate) if input and truncate
return (markdown.toHTML(input)) if input

In the preceding code:

- It creates a new filter named markdown on the learningYeomanCh3App module

- The filter takes two arguments in the return function; the first argument will be converted to markdown and the second argument will be the length of the string

- Inside the function, the returned string is set to the first argument; if there is a second argument passed, then the length of the returned string is the number passed as the second argument

- The filter returns the parsed input value that is passed and returned from the markdown.toHTML method, if there is an input value

Testing filters

The angular:filter subgenerator will create a filter test spec located in the test/spec/filters directory when invoked. It is up to the developer to add the testing logic. Let's write a test for the created filter now. Open the test/spec/filters/markdown.coffee file and add the following code:

'use strict'
describe 'Filter: markdown', () ->
markdown = {}
beforeEach module 'learningYeomanCh3App'
beforeEach inject ($filter) ->
markdown = $filter 'markdown'

it 'should return the input converted to HTML', () ->
input = '#Heading 1'
output = '<h1>Heading 1</h1>'
expect(markdown(input)).toBe(output)

it 'should return input to HTML, truncated length', () ->
input = 'This text is **bold**, and this will be truncated.'
output = '<p>This text is <strong>bold</strong>, and</p>'
expect(markdown(input, 26)).toBe(output)

The preceding code does the following:

- At the top, the describe method is used to contain the inner code into a spec named Filter: markdown

- The beforeEach method handles loading the module named learningYeomanCh3App for testing

- The second beforeEach method injects the filter for the spec, which is set to the local markdown variable

- The first it block tests if the passed input is converted to the expected output

- The second it block tests if the passed input is converted to HTML and limited to the length passed in the second argument of the filter function

Note

For more information, visit http://goo.gl/M3fhsT.

Angular directives

A directive in Angular is a reusable declarative HTML component with custom attributes and/or elements with self-contained logic that instructs Angular's HTML compiler to attach the specified behavior to that DOM element or transform the DOM element and its children.

Note

For more information on directives, visit http://goo.gl/4bGGBh.

Creating directives

The
angular:directive subgenerator handles creating a new Angular directive. Let's create a loading directive by opening the terminal and executing the following command:

$ yo angular:directive loading

The preceding command creates a new directive located in the app/scripts/directives folder. This directive will handle showing or hiding the element when the route is changing.

Using directives

A directive contains reusable functionality that can be used in view templates. Let's add the logic to the loading directive; open app/scripts/directives/loading.coffee and add the following code:

'use strict'
angular.module('learningYeomanCh3App').directive('loading', ($rootScope) ->
template: '<p>Loading...</p>'
restrict: 'EA'
replace: true
link: (scope, element, attrs) ->
element.addClass('loading').fadeOut('fast')
$rootScope.$on( '$routeChangeStart', ->
element.fadeIn('fast')
)
$rootScope.$on('$routeChangeSuccess', ->
element.fadeOut()
)
)

The preceding code does the following:

- It defines a new loading directive on the learningYeomanCh3App module, with dependencies on the $rootScope service

- The directive definition consists of an object with properties that define how the directive is used

- The template property is set to an inline string that will simply display the text Loading...

- The restrict property is set to EA, which informs Angular how to allow only an element or attribute to use this directive

- The replace property is set to true, which informs Angular to replace the content with the content defined in the template property

- The link property is set to a function that is invoked by Angular to wire the directive

- Inside the link function, the element gets the loading class added and is hidden using the jQuery fadeOut method

- Then, the $rootScope.$on method will bind to the $locationChangeStart event dispatched by the router and show the element using the fadeIn method

- Then, the $rootScope.$on method will bind to the $locationChangeSuccess event dispatched by the router and hide the element using the fadeOut method

Testing directives

The angular:directive subgenerator will create a directive test spec located in the test/spec/directives directory when invoked. Open the test/spec/directives/loading.coffee file and add the following code:

'use strict'
describe 'Directive: loading', () ->
beforeEach module 'learningYeomanCh3App'
scope = {}

beforeEach inject ($controller, $rootScope, $location) ->
scope = $rootScope.$new()
scope.location = $location

it 'should replace element with Loading...', inject ($compile) ->
element = angular.element '<loading></loading>'
element = $compile(element) scope
expect(element.text()).toBe 'Loading...'

In the preceding code:

- At the top, the describe method is used to contain the inner code into a spec named Directive: loading

- The beforeEach method handles loading the module named learningYeomanCh3App for testing

- The second beforeEach method injects the proper dependencies for the spec, which are set to local variables

- The it block defines that the directive should replace the element with the Loading... text

- The $compile method is used to compile the directive into HTML against the scope

- The expect method is used to check the text value of the element, which should be Loading...

Note

For more information on testing, visit http://goo.gl/m5UaDH.

Angular views

Creating views is extremely easy with Yeoman using the angular:view subgenerator. Let's create the CRUD views for the application.

Creating the Angular views

To create a new view, open the terminal and execute the following command:

$ yo angular:view [name]

Note

To create a view, controller, and route, use yo angular:route.

Creating the posts list

Let's create a view that will list all the posts defined on the scope. Open the terminal and execute the following command:

$ yo angular:view posts

The preceding command creates a new posts.html template located in the app/views directory. This view will contain angular specific directives along with custom filters to display a list of posts.

Open the app/views/posts.html file and add the following code:

<div id="posts">
<button class="btn btn-default pull-right"
ng-click="add()">Add New</button>
<ol class="breadcrumb">
<li><a href="#">Home</a></li>
<li class="active">Posts</li>
</ol>

<ul class="posts list-unstyled">
<li ng-repeat="post in posts | filter:tag">
<div class="post" data-id="{{post._id}}">
<header
ng-include="'views/post-header.html'"></header>
<section
ng-bind-html="post.body | markdown:200"></section>
</div>
</li>
</ul>
</div>

The preceding code does the following:

- It wraps the entire content in a div element with an ID of posts, and a simple breadcrumb list displaying the current location

- A button with the label Add New is declared using the ng-click directive that will invoke the add() method defined on the scope

- Then the ul element is declared with a class of list-unstyled to modify the default browser styles

- The inner li element is declared using the ng-repeat directive that will loop each item in the posts defined on the scope

- The filter:tag expression is used to inform Angular to only repeat items that match the defined filter

- The header element is used with the ng-include directive that will load the template into the content

- Then, the section element is used with the ng-bind-html directive set to the value of the post.body property

- The markdown:200 expression will invoke the custom markdown filter to transform markdown to HTML, limited to 250 characters

Creating the post-header view

Now, we need to create a post-header view that is present in the posts.html view; open the terminal and execute the following command:

$ yo angular:view post-header

The preceding command does the following:

- Creates a new post-header.html template located in the app/views directory

- This view will contain elements that will be used in the list and detail views

Open the app/views/post-header.html file and add the following code:

<div class="header">
<a href="" ng-click="view(post._id)">
<h1 class="media-heading">{{post.title}}</h1>
</a>
<span>
Posted on {{post.created | date:'mediumDate'}}
</span> |
<span ng-if="post.tags">Tags:
<span class="label label-default tag"ng-repeat="t in post.tags">{{t}}</span> |
</span>
<a href="" ng-click="edit(post._id)">class="btn btn-xs btn-default edit">
<i class="glyphicon glyphicon-pencil"></i> EDIT
</a>
</div>

The preceding code does the following:

- The div element is declared with a class of header that will contain the title, created date, and tags of the post being repeated

- The ng-click directive that is used will invoke the view(id) method on the scope, passing the id of the post being repeated

- The ng-repeat directive is used to loop each tag in the tags array

- The ng-click directive that is used will invoke the edit(id) method on the scope

Self-test questions

The following are questions you should be able to answer at the end of this chapter:

1. What type of data binding does Angular support and what is the syntax?

2. Which Angular method allows the creation of Angular modules?

3. What type of module would one use to create a reusable UI component?

4. Which is the default testing framework used in the Angular generator?

5. What type of module would someone use to create reusable business logic?

6. How does Angular manage its dependencies?

7. How would you add the create and detail logic to expand on what we created?

Summary

In this chapter, we learned the concepts of AngularJS and how to leverage the framework in a new or existing project. We covered using the different angular subgenerators to create a CRUD application that uses custom filters, directives, and services.

We also covered the default filters and most common services that Angular includes in the library. We wrote unit tests for the custom filters, directives, and services, as well as got an inside look at how an AngularJS application is structured using the Yeoman Angular generator.

In the next chapter, we will be using the Backbone.js generator. We will cover installing the generator, using the subgenerators to create the individual files needed for a Backbone-powered single page web application.

Chapter 4. My Backbone Project

Creating a modular single-page application that is scalable has always been a daunting task. However, with the Yeoman Backbone.js generator, achieving this is a lot easier than you might think.

A JavaScript library called Backbone.js gives web applications a structure with models, collections, and views that allows easy development of JavaScript applications.

This chapter is going to cover how to use the Backbone Yeoman generator to create a modular single-page application, which can be easily extended into something more. We will use each of the Backbone subgenerators to create the pieces of the application, thus giving you an insight into how to use the Backbone generator in a real-world project today.

In this chapter, we are going to cover the following:

- Many important topics on Backbone.js such as models, collections, views, routers, and events

- How to get an optimized Backbone.js project with CoffeeScript and Require.js AMD modules, set up a testing environment with Jasmine, and serve it on a Node.js server

Anatomy of the Backbone project

The concept behind Backbone is to provide a common set of data-structuring objects (collections and models) and user interface (routes and views) primitives that are useful when creating a single-page application.

The Backbone.js concepts are as follows:

- Model: This is a layer of abstraction that provides access to network resources

- Collection: This is a way to provide methods on an ordered set of models

- View: This is a way to encapsulate the presentation layer into objects

- Router: This is a way to support navigation by responding to hash changes

- Event: This is emitted by model and collection corresponding to the state changes

In short, Backbone.js abstracts functionality, separates concerns, and decouples code. Backbone always works with just about any library and is most often used with Require.js to load scripts; the Handlebars templating library is used to compile and render model data, and the Jasmine testing framework to create unit tests to verify the functionality. Backbone's only hard dependency is the highly popular utility library called Underscore.js, which gives you access to many useful functions; the library is considered a must for any developer's tool belt.

Note

For more information on Underscore.js, visit http://goo.gl/QRpah3.

The new Backbone project

This new Backbone project will get you started on combining Backbone.js with Asynchronous Module Definitions (AMD), using Require.js to implement a modular design and leverage Yeoman for the application structure.

Installing the generator-backbone

To install the Yeoman Backbone generator, open a terminal and execute the following command:

$ npm install -g generator-backbone@0.2.8

Note

The current generator-backbone version is 0.2.8.

The Backbone generator has some open issues on the Github repository; if you run into any issues, Google would be your best solution.

Scaffolding a Backbone application

To create a new Backbone project, use this two-step process; open a terminal and execute the following steps:

1. First, create a project directory named learning-yeoman-ch4, and make this your current directory.

2. Then, scaffold the Backbone project with Yeoman using the following command:

$ yo backbone --coffee --test-framework=jasmine --template-framework=handlebars

This command does the following:

- It scaffolds a new Backbone application with the folder name as the application name

- It sets the default testing framework to use Jasmine

- It sets the default templating framework to use Handlebars

The generator will prompt for options, including those to use Twitter Bootstrap, CoffeeScript, and Require.js. The result should look similar to the following screenshot:

Note

To use Jasmine as the testing framework, generator-jasmine must be installed globally.

Understanding the directory structure

Take a moment to understand the directory structure layout of a Backbone application. The layout consists of the following:

- app: This folder contains all the frontend code, HTML, JS, CSS, images, and dependencies:

- bower_components: This contains the client-side dependencies

- images: This contains the images for the application

- scripts: This contains the Backbone.js codebase and business logic

- app.coffee: This file contains the application module's definition

- config.coffee: This file contains the configuration module

- main.coffee: This file contains the Require.js configuration

- collections: This contains the application's collections

- models: This contains the application's models

- routes: This contains the custom filters

- templates: This contains reusable application services

- views: This contains the HTML templates used in the application:

- main.coffee: This file is the main view created by default

- styles: This contains all CSS/LESS/SASS files:

- main.css: This is the main style sheet created by default

- index.html: This is the application's entry point

- node_modules: This contains all project dependencies as node modules

- test: This folder contains all the tests for the application:

- bower_components: This contains the test dependencies

- spec: This contains unit test's mirroring structure of the app/scripts folder

- bower.json: This file contains testing dependencies

- Gruntfile.js: This file contains all project tasks

- package.json: This file contains project information and dependencies

- bower.json: This file contains frontend dependency settings

- karma.conf.js: This file contains the Karma Runner configuration

Configuring the application

Create a configuration module that will store application properties in a separate file, which will be used when bootstrapping the application. Create a new config.coffee file in the app/scripts directory.

Open the app/scripts/config.coffee file and add the following content:

define "config", [], ->
window.Config =
baseurl: document.location.origin
sitetitle: "Learning Yeoman"
sitedesc: "a starting point for a modern backbone.js app."
sitecopy: "2014 Copyright"
version: "0.0.1"
email: "admin@email.com"
debug: true
feature:
image: "http://goo.gl/fOq55C"
title: "Chapter 4"
body: "A starting point for a modern backbone.js application."
features: [
title: "CoffeeScript"
body: "CoffeeScript is a little language that compiles into JavaScript..."
image: "http://goo.gl/DTiliC"
,
title: "BackboneJS"
body: "Backbone.js gives structure to web applications by providing..."
image: "http://goo.gl/94Pe0E"
,
title: "RequireJS"
body: "RequireJS is a JavaScript file and module loader..."
image: "http://goo.gl/GOc6Mr"
]
menu: [
title: "Home"
href: "/"
,
title: "About"
href: "/about"
,
title: "Posts"
href: "/posts"
]

The following actions are performed in the preceding code:

- It creates a new module using the define method to enclose the script

- It returns an object that has properties common to the application

- The feature property is an object that will be displayed in the main view that we will create

- The features property is an array of featured items that will be displayed in the main view as well

- The menu property is an array of menu items that will be displayed for the navigation of the site

Scaffolding the app view

The application will need to render upon initial loading of the browser and have a top-level view that all other views render into. Open the terminal and execute the following command:

$ yo backbone:view app

The preceding command does the following:

- It will create a new Backbone view located in the app/scripts/views directory

- Then, it will create a new Handlebars template located in the app/scripts/templates directory

The Backbone app view

The AppView will extend the Backbone.View class and perform the duties of rendering the application's layout template into the DOM. Open the app/scripts/views/app.coffee file and replace with the following code:

define ['jquery', 'underscore', 'backbone', 'templates'],
($, _, Backbone, JST) ->
class AppView extends Backbone.View
template: JST['app/scripts/templates/app.hbs']
el: '.content'
initialize : () ->
console.log(@)
@render()
render: () ->
@$el.html(@template(@model))
return @

The following actions are performed in the preceding code:

- An AppView class is declared by extending the Backbone.View class

- The template property is specified to the path of the Handlebars template

- The initialize method gets invoked when a new instance is created and will invoke its render method

- The render method is simply setting the current elements' HTML to the template property, passing the views model as the argument; the template method will take the template property and the object passed, and return a compiled HTML for display

The Handlebars app template

Now, let's create the template for the AppView class; open the app/scripts/templates/app.hbs file and add the following code:

<article id="app" class="container">
<header id="header" class="header">
<ul class="nav nav-pills pull-right">
{{#each menu}}
<li>
<a href="#{{href}}">{{title}}</a>
</li>
{{/each}}
</ul>
<h3 class="brand text-muted">{{sitetitle}}</h3>
</header>
<section id="content" class="content"></section>
<footer id="footer" class="footer">
<span class="app-sitecopy pull-left">
{{ sitecopy }}
</span>
<span class="app-version pull-right">
{{ version }}
</span>
</footer>
</article>

The following actions are performed in the preceding code:

- An article element with the id attribute of the app is declared and will act as the container for the application

- A header element is declared with h3 {{sitetitle}} for the name of the application

- Inside the header element, the handles {{each}} helper is used to repeat each item in the menu

- A section element is used to house the content for the different views

- A footer element is used to display the site copy and site version

Scaffolding the main view

The application needs a main view that will render when the index page is loaded. Open up a terminal and execute the following command:

$ yo backbone:view main

The Backbone main view

The MainView class will render the default view for the application. Open the app/scripts/views/main.coffee file replace with add the following content:

define ['jquery','underscore','backbone','templates'], ($, _, Backbone, JST) ->
class MainView extends Backbone.View
template: JST['app/scripts/templates/main.hbs']
initialize: () ->
console.log('initialize MainView', @)
render: () ->
@$el.html(@template(@model))
return @

The following actions are performed in the preceding code:

- A new MainView class is created; it extends the Backbone.View class

- The template property is specified to the path of the Handlebars template

- The initialize method is just logging to the console for the view that is created

The render method is simply setting the current elements' HTML to the template property, passing the views model as the argument, The template method will take the template property and the object passed, and return a compiled HTML for display. As we specified Handlebars as the template framework, invoking template() as a function returns HTML.

The Handlebars main template

The main view needs to have some content, so add the following HTML to the app/scripts/templates/main.hbs file:

<div class="jumbotron hero-unit">
<h1>{{ feature.title }}</h1>
<img src="{{ feature.image}}" class="img-"/>
<p class="lead">
{{feature.body}}
</p>
</div>
<div class="marketing">
<ul class="media-list">
{{#each features}}
<li class="media feature">
<a class="pull-left" href="#">
<img alt="{{ title }}" src="{{ image }}"
class="media-object"/>
</a>
<div class="media-body">
<h4 class="media-heading">{{title}}</h4>
<p>{{ body }}</p>
</div>
</li>
{{/each}}
</ul>
</div>

The following actions are performed in the preceding code:

- The div element is used with a jumbotron class for some styling

- Inside the div element, the feature title, image, and body from the configuration will be displayed using the {{}} Handlebars data-binding syntax

- Then, using the Handlebars {{#each}} function, we loop over each item in the array, thus displaying the title, image, and body of that item

Scaffolding the app router

The router is used to handle the various locations in an application. To create a new router, open the terminal and execute the following command:

$ yo backbone:router app

The preceding command will create a new Backbone router located in the app/scripts/routes directory.

Open the app/scripts/routers/app.coffee file and replace with the following code:

define ['backbone', 'config', 'views/app', 'views/main'],
(Backbone, Config, AppView, MainView) ->

Backbone.Router.extend
currentView: null
routes:
'': 'index'
index: () ->
console.log 'index route'
App = new AppView(el: '.container', model: Config)
@showView(new MainView(el: '.content', model: Config))
showView: (view) ->
@currentView.close() if @currentView
@currentView = view
@currentView.render()
console.log('showView', @)

The following actions are performed in the preceding code:

- A new class named AppRouter is created that extends the Backbone.Router class.

- The routes property is set to a hash of routes and callback methods to invoke when that route is matched.

- The default route that will always be triggered on page load will invoke the index() method.

- The showView method is invoked that passes a new instance of the app and main view for display.

- The showView method handles setting the currentView in the application. This method will handle rendering a view and closing the current view to avoid memory leaks.

At the top of the app/scripts/routes/app.coffee file, we override the Backbone.View class' close method that allows the application to unbind from any events that were previously bound during the initialize method of that view; the code is as follows:

Backbone.View::close = ->
@unbind()
@remove()
@onClose() if @onClose

The following actions are performed in the preceding code:

- When the close() method is invoked, the unbind() method is invoked as well

- If there is an onClose() method on the view, then that method will be invoked as well

Bootstrapping the app

The app/scripts/main.coffee file is the script that configures Require.js as well as does any application's bootstrapping. Take a look at the contents of the app/scripts/main.coffee file:

#/*global require*/
'use strict'

require.config
shim:
underscore:
exports: '_'
backbone:
deps: [
'underscore'
'jquery'
]
exports: 'Backbone'
bootstrap:
deps: ['jquery'],
exports: 'jquery'
handlebars:
exports: 'Handlebars'
paths:
jquery: '../bower_components/jquery/jquery'
backbone: '../bower_components/backbone/backbone'
underscore: '../bower_components/underscore/underscore'
bootstrap: '../bower_components/sass-bootstrap/dist/js/bootstrap'
handlebars: '../bower_components/handlebars/handlebars'
config: './config'

require [ 'backbone', 'routes/app'], (Backbone, AppRouter) ->
window.App = new AppRouter()
Backbone.history.start()

The following actions are performed in the preceding code:

- The shim property configures the dependencies, exports, and custom initialization for older scripts that do not use define()

- The paths property configures path mappings to module names

- The paths property is relative to baseUrl or the location of the HTML page that loads Require.js

- The window.App object is set to a new instance of AppRouter, which prepare the routes

- The Backbone.history.start() method simply tells Backbone to begin monitoring all hash change events

Note

For more information on Require.js, visit http://goo.gl/SKvLVB.

Previewing the app

To run the application locally, execute the following command:

$ grunt serve

Your browser should open up, displaying something like the following screenshot:

Testing

Testing the application is not as easy as 1-2-3; as we passed in the options to use CoffeeScript and Require.js, the configuration to run tests is a little bit more complex, but testing is still possible.

Configuration

First off, you will need to install the Jasmine/Require.js template; so, open a terminal and execute the following command:

$ npm install grunt-template-jasmine-requirejs@0.1.10 --save-dev

This command will download and install grunt-template-jasmine-requirejs into the project's node_modules directory and add a new entry to the package.json file's devDependencies property.

The configuration that you will need to modify is located in the Gruntfile.js file in the projects root directory and is around line 130 as follows:

jasmine: {
all:{
src : '.tmp/scripts/{,*/}*.js',
options: {
specs : ['.tmp/spec/**/*.js'],
vendor : ['<%= yeoman.app %>/bower_components/jquery/jquery.js'],
template: require('grunt-template-jasmine-requirejs'),
templateOptions: {
requireConfigFile: '.tmp/scripts/main.js',
requireConfig: {
baseUrl: '.tmp/scripts',
shim: {
handlebars: {
exports: 'Handlebars'
}
},
paths: {
jquery: '../../<%= yeoman.app %>/bower_components/jquery/dist/jquery',
backbone: '../../<%= yeoman.app %>/bower_components/backbone/backbone',
underscore: '../../<%= yeoman.app %>/bower_components/underscore/underscore',
handlebars: '../../<%= yeoman.app %>/bower_components/handlebars/handlebars'
}
}
}
}
}
},

The following actions are performed in the preceding code:

- The jasmine task is modified to set the src directory with the compiled CoffeeScript files located in the .tmp/scripts directory

- The template property is set to the grunt-template-jasmine-requirejs node module; this module enables the Jasmine spec runner to dynamically load source and spec modules to test Require.js applications

- The templateOptions property is modified to reflect the locations of the required libraries by the application

- The baseUrl property is modified to the location of the compiled scripts to the .tmp/scripts location

- This configuration will allow you to test this application using the Jasmine framework with Require.js

Unit testing

Let's add an initial spec that will be used as a starting point for other tests; create a new app.coffee file in test/spec. Open the test/spec/app.coffee file and add the following code:

define ['jquery', 'backbone', 'views/app', 'config'],
($, Backbone, App, Config) ->
testApp = null

describe "My Backbone Project", ->
beforeEach(()->
testApp = new App(model: Config)
)
describe 'App', ->
it 'should have model', ->
expect( testApp.model ).toBeDefined()

To run the tests for the application, use the following command:

$ grunt test

When the test task is run, you should see the console log output from the test results, which should look similar to the following screenshot:

As the generator does not scaffold the Backbone spec, adding more tests for the application is very straightforward as the configuration is taken care of.

End-to-end tests

Currently, there is no formal way of writing end-to-end scenario tests for Backbone applications. There are plenty of testing frameworks out there to choose from that will navigate your web application and perform assertions around submission of forms and more.

Here is a list of some libraries to check out:

- Nightwatch.js: This provides browser-automated testing with JavaScript end-to-end tests in Node.js that run against Selenium Server (http://goo.gl/Q4vvhK)

- Casper.js: This provides browser-navigation scripting and testing utility written in JavaScript for PhantomJS (http://goo.gl/iLYJvT)

- Zombie.js: This provides extremely fast, headless, full-stack testing using Node (http://goo.gl/rNeD3Z)

Backbone.Events

Backbone.Events is a class that can be extended into any object, allowing the object to bind and trigger custom-named events. The events can be bound at any time and can take the passed arguments.

Creating events

To use the Backbone.Events class, you will need to extend it into another object that will be used to trigger events or bind handlers to, for example, the following code:

App.pubsub = _.extend({}, Backbone.Events)

Using events

The easiest way to use Backbone events is to simply invoke the on or off method on any object or class in Backbone, as all classes extend from the events class, as follows:

App.pubsub.bind('fetch:posts', (data) ->
alert data
)
//Usage
App.pubsub.trigger('fetch:posts', 'And send this to handler')

The following actions are performed in the preceding code:

- The bind method is invoked on the App.pubsub object that will bind the callback function to the event

- Using the pubsub object is easily done with the trigger method by passing the name of the event and data to be passed on to the handler

Testing events

To test events in Backbone, use a spy that will test if the event system is working, by creating events that have subscribers and making sure that the subscribers are notified when an event occurs.

Create a new pubsub-spec.coffee file located in test/spec. Open the file and add the following content:

describe 'App.pubsub', ->
beforeEach ->
pubsubSpy = jasmine.createSpy()
App.pubsub.bind('test:event', pubsubSpy)

it 'should trigger event handler', ->
App.pubsub.trigger('test:event')
expect(pubsubSpy).toHaveBeenCalled()

The following actions are performed in the preceding code:

- The beforeEach method sets up a new pubsubSpy event that will allow you to check the call count and so on

- The first it block will test if the handler is called when the event is fired

Note

For more information on Backbone.Events, visit http://goo.gl/kaF35Z.

Backbone.Model

The Backbone.Model class is used to extend application-specific domain methods and logic, which provide functionality to manage changes to data, and the ability to send CRUD (GET/PUT/POST/DELETE) operations on the server and dispatch many useful events.

Note

For more information on Backbone.Model, visit http://goo.gl/f9Wj2l.

To bind to the model events, use the model.on('name', callback) function, which will trigger the callback when that event is invoked.

Scaffolding models

To create a new model, use the backbone:model subgenerator, open the terminal, and execute the following command to create a new post model:

$ yo backbone:model post

This command will create a new file named post.coffee located in the app/scripts/models directory; open the newly created model and add the following code:

define ['underscore', 'backbone'], (_, Backbone) ->
'use strict'
class PostModel extends Backbone.Model
idAttribute: '_id'
urlRoot: '/api/v2/learning-yeoman-ch3/posts'
defaults:
title: 'post-title'
slug: 'post-title'
image: 'http://placehold.it/250&text=Image'
body: 'This is an example post with default data.'
tags: ['featured', 'post']
created: null
modified: null
published: true
initialize: ->
console.log('PostModel', @)
validate: (attrs, options)->
if attrs.title.length < 2
return 'The title must be at least 2 characters.'
else if attrs.title is ''
return 'You must provide a title.'

The following actions are performed in the preceding code:

- The PostModel class is created; it extends from the Backbone.Model class

- The idAttribute property is set to the mongodb default id field, _id

- The url property can either be a string or a function that returns a string; in this case, it is a function that returns the endpoint URL with the id of the model appended to the URL

- The defaults property is set to an object, with model property keys with the default values

- The initialize method is used for debugging purposes to log to the console when the model is created

- The validate method is used to validate the attrs object before it is set on the model

Using the Backbone models

There are many ways to use models; you can create a new model, update an existing model, destroy a model, and validate a model and parse the response from the server before setting attributes on a model.

Creating a model

To create a new instance of a model, use the following code snippet:

var p = new PostModel({title: 'New Post'});
console.log( p.toJSON() );

The following actions are performed in the preceding code:

- The variable p is set to a new instance of PostModel

- The model is passed through a JSON.stringify() method and logged to the console

Updating a model

To update the properties of an existing model, use the set() method, as follows:

var p = new PostModel({title: 'New Post'});
p.set({title: 'Updated Title'});

The following actions are performed in the preceding code:

- The variable p is set to a new instance of PostModel

- The set() method is invoked and passed with an updated attribute hash on the instance of PostModel

- The model is passed through a JSON.stringify() method and is logged to the console

Saving a model

To save a model instance on the server, use the save() method, as follows:

var p = new PostModel({title: 'New Post'});
p.save();

The following actions are performed in the preceding code:

- The variable p is set to a new instance of PostModel

- The save() method is invoked on the model to send a POST request to the server or a PUT request if the model has an id attribute

Destroying a model

To destroy a model instance on the server, use the destroy() method, as follows:

var p = new PostModel({title: 'New Post'});
p.destroy();

The following actions are performed in the preceding code:

- The variable p is set to a new instance of PostModel

- The destroy() method is invoked on the model to send a DELETE request to the server

Validating a model

To validate a model, override the validate method in the model definition, as follows:

class PostModel extends Backbone.Model
validate: (attrs, options)->
if attrs.title.length < 2
return 'The title must be at least 2 characters.'
else if attrs.title is ''
return 'You must provide a title.'

The following actions are performed in the preceding code:

- The validate method takes two arguments: the attributes and options

- The attributes object contains the properties defined in the model

- If a model does not meet the criteria, it will return the error, and if it is valid, it returns nothing

Testing a model

To test a model, you will want to test if the model can be created with attributes, without attributes, and anything else, as follows:

define(['models/post'],(PostModel) ->
describe 'PostModel:', ->
postModel = null
beforeEach ->
postModel = new PostModel()
it 'should have default attribute values', ->
expect(postModel.get('title')).toEqual('Post Title')
it 'should set attributes', ->
postModel = new PostModel(title: 'NewPost')
)

The following actions are performed in the preceding code:

- The define method loads the post model module located in the models directory

- The describe method is the test suite for PostModel

- The beforeEach method simply creates a new model instance before each spec

- The it method is an example test case that will check to see if the model has default values

- Each of the expect methods simply check to see if the attribute on the model is as expected

Backbone.Collection

The Backbone.Collection class is used to store an ordered set of models; you can bind to collection events and fetch data from the server with RESTful routes and also include a full suite of Underscore.js methods.

Note

For more information on Backbone.Collection, visit http://goo.gl/lJvDE1.

Creating collections

To create a new collection, use the backbone:collection subgenerator as follows:

$ yo backbone:collection posts

In the preceding command:

- The backbone:collection subgenerator is invoked with the name of the collection set to posts

- The subgenerator then creates a new file located in the app/scripts/collections directory

As collections are ordered sets of models, the collection should have the model property set to a model that the collection is of; this collection is going to contain a set of post models.

Open the app/scripts/collections/posts.coffee file and add the following content:

define ['underscore', 'backbone', 'models/post'], (_, Backbone, PostModel) ->
class PostsCollection extends Backbone.Collection
model: PostModel
url: ->
'http://jonniespratley.me:8181/api/v2/learning-yeoman/posts'
parse : (response) ->
@trigger('posts:fetch:complete')
return response

The following actions are performed in the preceding code:

- The define block is used to import the dependencies for this module

- The PostsCollection class is declared; it extends the Backbone.Collection class

- The model property is set to the model that will be contained in the collection

- The url property is set to a simple REST JSONP server

- The parse method is triggered on the response from the server by passing response as the parameter

- The trigger method is used to dispatch the posts:fetch:complete event

Using collections

To use the collections, just attach event handlers to a collection instance or invoke methods on a collection instance. Any event that is triggered on a model in a collection will also be triggered on the collection directly. This allows you to listen for changes to specific attributes in any model in a collection.

@collection = new PostsCollection()
@collection.fetch(dataType: 'jsonp')
@collection.bind('posts:fetch:complete', @render, @)

The following actions are performed in the preceding code:

- A new instance of PostsCollection is created

- The fetch method on the collection is invoked to request data

- When the posts:fetch:complete event fires, the render method is invoked

Testing collections

When testing collections, generally, you should test if a model can be added to the collection either as an object or an array of objects, as follows:

define(['collections/posts'],(PostsCollection) ->
postsCollection = null
describe 'Post Collection:', ->
beforeEach ->
spyOn($, "ajax").andCallFake (options) ->
options.success()
postsCollection = new PostsCollection()
it 'should add model as object to the collection', ->
expect(postsCollection.length).toBe(0)
postsCollection.add({title: 'New Post'})
expect(postsCollection.length).toBe(1)
it 'should add models as an array to the collection', ->
expect(postsCollection.length).toBe(0)
postsCollection.add([
{title: 'New Post 1'},
{title: 'New Post 2'}
])
expect(postsCollection.length).toBe(2)
it 'should send a GET request to correct URL', ->
postsCollection.fetch()
request = $.ajax.mostRecentCall.args[0]
expect(request.type).toEqual('GET')
)

The following actions are performed in the preceding code:

- The describe method wraps the inner content in a test suite

- Then, the beforeEach method is used to create a new collection instance and a Jasmine spy that will trigger the success method on the AJAX request

- The first it spec is used to test if an object can be added to the collection

- The second it spec is used to test if an array of objects can be added to the collection

- The third it spec is used to check whether an HTTP request has the correct type

The Backbone view

Backbone views are used to reflect data; they are used to listen to events and respond accordingly. The idea is to organize the user interfaces into logical views that have methods to handle user interaction and can update the UI independently when the model data changes, without having to refresh the page.

Creating views

To create a new view, use the backbone:view subgenerator as follows:

$ yo backbone:view posts

The preceding command creates a new Backbone view located at app/scripts/views/posts.coffee and a new Handlebars view template located at app/scripts/templates/posts.hbs.

Using views

Backbone views use an optional render() method that defines the logic to render a template. This example uses the Handlebars templating library to compile a collection with HTML that gets injected into the view's el property.

Open the app/scripts/views/posts.coffee file and add the following code:

define [
'jquery',
'underscore',
'backbone',
'templates',
'collections/posts',
'views/post'
], ($, _, Backbone, JST, PostsCollection, PostView) ->

class PostsView extends Backbone.View
template: JST['app/scripts/templates/posts.hbs']
el: '.content'
initialize: () ->
_.bindAll(@, "render")
@collection = new PostsCollection()
@collection.fetch(dataType: 'jsonp')
@collection.bind('posts:fetch:complete', @render, @)
@childViews = []
@listenTo(@collection, 'add', @renderOne)
@listenTo(@collection, 'reset', @renderAll)

render: () ->
#@$el.html(@template(@model.toJSON())
@$el.html(@template())
return @

renderOne: (item) ->

#Create new list item view passing in a single model
itemView = new PostView(model: item)

#Store reference to view by adding to child-views
@childViews.push(itemView)

#Append item to view element by the render method.
@$el.find('.list-group').append(itemView.render())

renderAll: () ->
@collection.each(@renderOne, @)

The following actions are performed in the preceding code:

- At the top, the define block specifies the required dependencies that will be loaded before the script is executed

- A PostsView class is defined; it extends the Backbone.View class

- The template property is set to the corresponding precompiled Handlebars template

- The el property specifies which element to attach the view instance to

- The initialize method is invoked whenever a new instance of this view is created; here, we use Underscore.js' bindAll method to render, to control the scope on the view instance

- The views collection property is set to a new instance of PostsCollection and then issued to fetch data from the server by passing in dataType set to jsonp

- The bind method on the collection is used to invoke the render method when this event occurs

- The view's childViews property is set to an empty array that will hold the instances of all PostView properties that are created

- The render method will set the elements' content to the template and return an instance of the view

- The renderOne method is used to render a single model along with a single view into the content

- The renderAll method will loop each model in the collection and invoke the renderOne method

Testing views

To test Backbone views, you will want to test the initial setup, view rendering, and templating. Testing if event callbacks are triggered when the elements' action is invoked will also help ensure that the view is responding correctly to user events, as follows:

describe 'AppView', ->
beforeEach ->
sandbox = $('<div class="content"></div>')
$('body').append(sandbox)
testApp = new App(model: Config).render()
afterEach ->
sandbox.remove()
it 'should have a header, content, and footer element', ->
expect(testApp.$el.find('header').length).toBe(1)

The following actions are performed in the preceding code:

- The describe block wraps the inner content in a test suite

- Then, the beforeEach method is used to add a sandbox element to the body

- Then, a new instance of the view is created and injected into the sandbox element

- The afterEach method is used to remove the view from the sandbox element after every test

- The first it block will test if the view is rendered and has the proper child elements

Note

For more information on Backbone.View, visit http://goo.gl/KVu2Rw.

Backbone.Router

Single-page web applications usually need to provide linkable and user-friendly links. Until recently, the only way around this was using hash fragments (/#/posts), but with the HTML5 history API, the use of standard URLs (/posts) is available. The Backbone.Router class provides useful methods for client-side routing in a single-page web application.

Creating routers

To create a new router, use the backbone:router subgenerator as follows:

$ yo backbone:router posts

This command creates a new Backbone router located at app/scripts/routes/posts.coffee.

Using routers

To use the Backbone.Router class, simply extend the class and provide the routes property with an object hash set on the name/callback of the route, as follows:

define [
'backbone'
'config'
'views/app'
'views/main'
'views/about'
'views/posts'
'models/post'
], (Backbone, Config, AppView, MainView, AboutView, PostsView, PostModel) ->

class AppRouter extends Backbone.Router
currentView: null
childViews: {}
routes:
'': 'index'
'about': 'about'
'posts': 'posts'
index: () ->
console.log('#/index route')
App = new AppView(el: '.container', model: Config)
@showView(new MainView(el: '.content', model: Config))

posts: () ->
console.log('posts view')
@showView(new PostsView(el: '.content'))

showView: (view) ->
@currentView.close() if @currentView
@currentView = view
@currentView.render()
console.log('showView', @)

The following actions are performed in the preceding code:

- The define block is used to declare all the dependencies that this module will use; in this case, all the views will be imported

- The routes property is set to a hash of route name and functions to invoke when the route matches

Testing routers

To test the routers, you will want to create a spy that monitors if the route's callback function is invoked when the route matches the name, as follows:

define(['jquery', 'backbone', 'underscore', 'routes/app'], ($, Backbone, _, AppRouter) ->
router = null
routerSpy = null
describe "AppRouter:", ->

beforeEach ->
router = new AppRouter()
routerSpy = jasmine.createSpy()

try
Backbone.history.start(slient: true, pushState: false)
router.navigate('_SpecRunner.html')
catch error
console.log error

afterEach ->
router.navigate('_SpecRunner.html')

it "should have the right amount of routes", ->
expect(_.size(router.routes)).toEqual 6

it 'should handle index route', ->
router.bind('route:index', routerSpy)
router.navigate('', true)
expect(routerSpy.wasCalled).toBe(true)

it 'should handle posts route', ->
router.bind('route:posts', routerSpy)
router.navigate('#posts', true)
expect(routerSpy.wasCalled).toBe(true)

it 'should not handle unknown', ->
router.bind('route:route-doesnt-exist', routerSpy)
router.navigate('#route-doesnt-exist', true)
expect(routerSpy.wasCalled).toBe(false)

)

The following actions are performed in the preceding code:

- The describe block wraps the inner content in a test suite

- Then, the beforeEach method creates a new router instance along with a Jasmine spy

- The Backbone.history.start method is called to inform Backbone to start listening for route changes

- The afterEach method is used to reset the router's location to the default state

- The it block expects that the number of routes match the expectations

- The next it block tests that, when navigating to a different route, the callback function is invoked

Note

For more information on Backbone.Router, visit http://goo.gl/5L8zNx.

Self-test questions

The following are the questions you should be able to answer at the end of this chapter:

1. Which library(s) does Backbone.js heavily depend on?

2. Which class do all Backbone classes extend?

3. Which option is only available in the Yeoman Backbone generator?

4. What functionality does the Backbone.Model class provide?

5. What functionality does the Backbone.Collection class provide?

Summary

In this chapter, we covered many important topics of Backbone.js such as models, collections, views, routers, and events. We also covered interesting topics on the basic project setup to get an optimized Backbone.js project with CoffeeScript and Require.js AMD modules, set up a testing environment with Jasmine, and serve it on a Node.js server.

In the next chapter, we will be using the Ember.js generator to create a single-page web application that will leverage some of the core features in the framework.

Chapter 5. My Ember Project

This chapter is going to cover using the Ember.js generator to create a modern web application that can be easily extended into something more. We will use the subgenerators to create the pieces of the application. You will get a feel of how an Ember application is structured and the concepts of Ember.

In this chapter, we are going to cover the following topics:

- Creating Ember.js applications easily by using Yeoman generators

- Creating a configuration object that will hold settings for the entire application

- Exploring the Handlebars templating system by binding data from the applications' configuration file to display a list of features

- Core concepts of the Ember.js framework

Anatomy of the Ember project

The concept behind Ember is to provide developers with the tools to ambitiously build large JavaScript applications that can be used on any platform. Native application frameworks such as Cocoa and Smalltalk-76 have pioneered Ember ideas. Ember apps are structured around the URL of an application; like many JavaScript frameworks, this is not at the top of their concerns, but Ember derives the tools and concepts of its framework from the Web's most powerful thing, the URL.

The following are some Ember concepts:

- Template: A template is written using the Handlebars templating language and describes the user interface of the application

- Router: A router is used to translate a URL into a series of nested templates, each with a model that is always in sync with the current URL

- Component: A component is a reusable custom HTML tag that is described with a Handlebars template and JavaScript functionality

- Model: A model is an object that stores a persisted state; templates use models to display data to the user

- Controller: A controller is an object that stores an application state; templates can use controllers and/or models to retrieve properties

The new Ember project

We are going to use the Yeoman Ember generator to scaffold the pieces of an Ember.js web application. This project will get you started with a basic application that can be used as a starting point for creating a more robust application.

Installing the generator-ember

To install the Yeoman Ember generator, execute the following command:

$ npm install -g generator-ember

The version required for the following steps is 0.8.3.

Note

The –g flag requires an administrator user.

Scaffolding the application

First, create a folder named learning-yeoman-ch5 and then make that your current working directory. Now, to scaffold the application, open a terminal and execute the following command:

$ yo ember --test-framework=jasmine --coffee --karma

This command does the following:

- Creates an initial project located in the current directory

- Sets the test framework to use Jasmine

- Sets the project scripting language to CoffeeScript

- Sets the project to use the Karma test runner

- And will prompt whether to use Bootstrap SASS (select Yes)

The output from the command should look similar to the following screenshot:

Now you are ready to start development on a fresh Ember web application.

Note

SASS option requires the Compass gem to be installed.

Understanding the directory structure

Take a minute to become familiar with the directory structure of an Ember application.

- app: This is where the application source code is present:

- images: This is where images for your application go

- scripts: This is where the application logic scripts go:

- controllers: This is for controllers

- models: This is for models

- routes: This is for routes

- views: This is for views

- app.coffee: This is the main Ember.js app file

- router.coffee: This is the main application router

- store.coffee: This is the application's data store adapter

- styles: This is for stylesheets

- templates: This is for Handlebar templates:

- application.hbs: This is the main app template

- index.hbs: This is the app index template

- bower_components: This contains client-side Bower dependencies

- index.html: This contains the application's entry point

Application configuration

Let's now go ahead and create a configuration object module that will allow us to store some application-wide properties in a separate file. Create a new config.coffee file located in the app/scripts directory.

Then, add some properties to the file that we will use when initializing the application. Open the app/scripts/config.coffee file and add the following code:

window.Config =
baseurl: document.location.origin
sitetitle: 'Learning Yeoman'
sitedesc: 'a starting point for a modern ember.js application.'
sitecopy: '2014 Copyright'
version: '0.0.1'
email: "admin@email.com"
debug: true
feature:
image: 'http://goo.gl/ur9Ueu'
title: 'Chapter 5'
body: 'A starting point for a modern ember.js application.'

features: [
title: 'Yeoman'
body: 'Yeoman is a robust client-side stack.'
image: 'http://goo.gl/W3ZtMx'
,
title: 'EmberJS'
body: 'Ember.js is built for productivity.'
image: 'http://goo.gl/8bRsMq'
,
title: 'Grunt'
body: 'The Grunt ecosystem is growing every day.'
image: 'http://goo.gl/xvqzS6'
]
session:
authorized: false
user: null
menu: [
title: 'Home', href:'#/'
,
title: 'About', href:'#/about'
,
title: 'Posts', href:'#/posts'
]

The preceding code does the following:

- We declare a Config object on the window object with some application-specific properties

- The site title, site description, and site copyright are defined with defaults

- The version, email, and debug properties are declared and set

- The feature property is set to an object with title, image, and body for displaying on the index page

- The features property is set to an array with objects that contain title, image, and body properties to display on the index page

- The session property is set to an object with authorized and user properties that are currently not used

- The menu property is set to an array of objects containing title, icon, and href properties for the navigation

Application definition

The application module will have a set of commonly used properties and methods that other components can access through the global App namespace, which is set on the window object.

Open the app/scripts/app.coffee file and add the following highlighted contents:

require 'scripts/config'

LearningYeomanCh5 = window.LearningYeomanCh5 = Ember.Application.create(
LOG_VIEW_LOOKUPS: true
LOG_ACTIVE_GENERATION: true
LOG_BINDINGS: true
config: window.Config
)

require 'scripts/controllers/*'
require 'scripts/store'
require 'scripts/models/*'
require 'scripts/routes/*'
require 'scripts/views/*'
require 'scripts/router'

Note

The grunt-neuter task will concatenate files in the order you require.

The preceding code does the following:

- Before the application definition, the configuration file is required

- Then, the Ember.js application is defined with some debugging properties

- The config property is set to the window.Config object; this property is now globally accessible

- Then, we use the require method to load the files in order to start with the controllers and end with the router

Note

For more information on debugging Ember, visit http://goo.gl/GfcSzh.

The application template

Let's go ahead and adjust the applications' default layout template. Open the app/templates/application.hbs file and replace the contents with the following code:

<article class="container">
<header class="header">
<ul class="nav nav-pills pull-right">
{{#each LearningYeomanCh5.config.menu}}
<li>
<a {{bind-attr href=href}}>{{ title }}</a>
</li>
{{/each}}
</ul>
<h3 class="brand text-muted"> {{ LearningYeomanCh5.config.sitetitle }} </h3>
<hr />
</header>
<section class="">
{{outlet}}
</section>
<footer class="footer">
<p class="pull-left">
{{ LearningYeomanCh5.config.sitecopy }}
</p>
<p class="pull-right">
{{ LearningYeomanCh5.config.version }}
</p>
</footer>
</article>

The preceding code does the following:

- The article element is declared with the container class; this will be the application's container

- The header element is declared to hold the application's navigation and title

- The ul element is declared with the nag and nav-pills class for styling and will contain the navigation links

- The {{#each }} helper is used to loop each item in the menu defined in the config property of the application

- The {{bind-attr}} helper is used to bind the value of href from the menu item for linking to the correct page

- The section element is declared to contain the output from the rendered views

- The {{outlet}} helper is a placeholder that the router will fill in with the proper template based on the current URL

- The footer element is declared to display the application's site copyright defined in the config property

The index template

Open the app/templates/index.hbs file and replace the content with the following code:

<div class="jumbotron">
<h1> {{ LearningYeomanCh5.config.feature.title }} </h1>
<img {{ bind-attr src=LearningYeomanCh5.config.feature.image }}/>
<p class="lead">
{{ LearningYeomanCh5.config.feature.body }}
</p>
</div>
<div class="marketing">
{{#each item in LearningYeomanCh5.config.features }}
{{ feature-item feature=item }}
{{/each}}
</div>

The preceding code does the following:

- The .jumbtron class is applied to a div element to hold the feature information for the application

- The .marketing class is applied to another div element that will contain the list of feature items

- The {{feature-item feature=item}} component helper is used to load the feature-item component and the feature property in the component, which is set by item

The feature component

Ember.Component is a reusable view component that is completely isolated; many properties or actions are targeted at the view object and have no access to the outer context or controllers.

The simplest way to create a component is to use the ember:component subgenerator, open the terminal, and execute the following command:

$ yo ember:component feature-item

The preceding command will do the following actions:

- It will create a new template file named feature-item.hbs in the app/templates/components directory

- It will create a new component definition file named feature_item_component.coffee located in the app/scripts/components directory

Open the app/templates/components/feature-item.hbs file and add the following content:

<div class="media feature" data-id="{{feature.id}}">
<a class="pull-left">
<img class="media-object" {{bind-attr src=feature.image}}/>
</a>
<div class="media-body">
<h4 class="media-heading">{{feature.title}}</h4>
<p>{{feature.body}}</p>
</div>
</div>

The preceding code does the following:

- It declares a div element with .media and .feature CSS classes for styling and the data-id attribute set to the current items id

- The img element is using a special Ember helper that will bind the src attribute of the element to the image property of the feature

- Then, an h4 element is used to display the title of the feature item

- Lastly, the p element is used to display the body of the feature

Notice how we are referencing a feature object's property; where does this come from? Well, the feature object is what is passed into the component from the outer view.

You can reuse this component anywhere in the application's view templates using the name of the component {{feature-item feature=item}}, which helps Ember to render the content of the component with the feature variable set to item.

Previewing the application

Now, to preview the application, run the following command:

$ grunt serve

Your default browser should open the page displaying something similar to the following screenshot:

You can make changes to the code and watch the browser LiveReload automatically; thanks to the watch task, which monitors files and executes Grunt tasks.

Tip

Try the Ember Inspector for Google Chrome at http://goo.gl/7ExhIj.

Testing

Testing an Ember application is easy. Ember includes several helpers to aid with integration testing; these helpers are aware of asynchronous behavior in the application and make it extremely easy to write tests.

The test helpers

Ember provides some useful helper methods for integration testing, which are as follows:

- visit(url): This visits the given route (url) and returns a promise that is resolved when all async behavior is complete

- find(selector, context): This locates an element by selector within the application and/or within the context

- fillIn(selector, text): This locates an input element by selector, fills with the given text, and returns a promise when complete

- click(selector): This locates an element by selector, triggers the elements' click event, and returns a promise when complete

- keyEvent(selector, type, keyCode): This simulates a key event with keyCode on the element found by selector

Setup

Ember comes with support for integration testing and unit testing. Let's configure how the application handles testing; open test/support/initializer.coffee and add the following highlighted code that will handle setting up everything:

Ember.Test.JasmineAdapter = Ember.Test.Adapter.extend(
asyncRunning: false
asyncStart: ->
Ember.Test.adapter.asyncRunning = true
waitsFor Ember.Test.adapter.asyncComplete

asyncComplete: ->
not Ember.Test.adapter.asyncRunning

asyncEnd: ->
Ember.Test.adapter.asyncRunning = false

exception: (error) ->
expect(Ember.inspect(error)).toBeFalsy()
)

LearningYeomanCh5.exists = (selector) ->

!!find(selector).length

LearningYeomanCh5.text = (selector) ->

$.trim(find(selector).text())

Ember.testing = true
Ember.Test.adapter = Ember.Test.JasmineAdapter.create()

document.write('<div id="ember-app"></div>');
LearningYeomanCh5.rootElement = "#ember-app"
LearningYeomanCh5.setupForTesting()
LearningYeomanCh5.injectTestHelpers()

The preceding code does the following:

- At the top of the file, a JasmineAdapter class is created that will contain special async methods that are implemented by the framework.

- The exists helper method will simply locate the element and return the length of elements.

- The text helper method will locate the element and return the trimmed text value.

- Then, the document.write method is used to create a container that will contain the application for testing.

- Next, we set the Ember.testing property to true to inform Ember that we are testing our application.

- Then, we set Ember.Test.adapter to the JasmineAdapter object that was created by extending Ember's test adapter. This enables you to write custom adapters for just about any testing framework.

- Next, the rootElement property is set to the ID of the test container that is added to the document.

- Then, we invoke the setupForTesting method on the application to initialize testing of the app.

- Next, we invoke the injectTestHelpers method that informs Ember to set the test helpers on the window scope.

Note

Custom adapters need to provide methods for asyncStart and asyncEnd to handle asynchronous testing.

End-to-end integration tests

End-to-end integration testing is pretty straightforward with Ember; basically, when you want to assert that the application is functioning properly, displaying the correct content, and handling the correct actions, you write an integration test.

Let's write a simple integration test that will make sure the application is displaying the proper content in the application; open the test/integration/index.coffee file and replace with the following code:

describe "LearningYeomanCh5 Application", ->
it 'should display site title, nav and site copyright', ->
visit('/').then ->
expect(LearningYeomanCh5.text('.brand')).toBe(LearningYeomanCh5.config.sitetitle)
expect(LearningYeomanCh5.text('footer .pull-left')).toBe(LearningYeomanCh5.config.sitecopy)
expect(find('.nav li').length).toEqual(LearningYeomanCh5.config.menu.length)

it "should display title, image, body, and list features", ->
visit('/').then ->
expect(LearningYeomanCh5.text('.jumbotron h1')).toBe(LearningYeomanCh5.config.feature.title)
expect(LearningYeomanCh5.text('.jumbotron p')).toBe(LearningYeomanCh5.config.feature.body)
expect(find('.jumbotron img').attr('src')).toBe(LearningYeomanCh5.config.feature.image)
expect(find('.feature').length).toBe(3)

The preceding code does the following:

- The describe method creates an Index route spec that will contain the assertion methods

- The first it method creates a new spec that will check for the site title, navigation, and copyright

- The visit method loads the index route and when complete, proceeds to run the assertions contained

- The expect methods will ensure that the located elements' text matches the expected value

- The second it method creates a new spec that will check for the feature title, image, body, and features

- The visit method loads the index route and when complete, proceeds to run the assertions contained

- The second expect methods will ensure that the located elements' text matches the expected value

- The third expect method will locate the element by selector, get the src attribute, and match it against the expected value

- The fourth expect method will locate the element by selector and match the length against the expected length

Unit tests

Setting up unit tests is fairly straightforward, and since the --test-framework flag was set to Jasmine, you can use all the Jasmine methods when writing unit test specs. For example, create a new test/spec/app.coffee file and add the following content:

describe 'PostModel', ->
postModel = LearningYeomanCh5.Post
it 'title should be a string', ->
expect(postModel.metaForProperty('title').type).
toEqual('string')

The preceding code does the following:

- The describe method creates a PostModel spec that will contain methods for testing the model

- The postModel variable is set to the Post model defined on the apps namespace

- The it method specs out the test and expects the post model's title type to be a string

To run the applications tests, open the terminal and execute the following command:

$ grunt test

The output from the preceding command should look similar to the following screenshot:

Note

For more information on Karma, visit http://goo.gl/gPdJi3.

Ember Data

Ember Data is a library that integrates with Ember.js to make handling data from a server seamless; it can cache locally for performance, send data to the server, and create new records on the client. Ember Data gives the user the ability to create CRUD type applications fairly quickly; using the proper naming conventions, most of the controllers and files are created at runtime to allow applications to be adaptable.

Without any configuration, Ember Data can load and save records and their relationships via a RESTful JSON API, as long as the API follows RESTful conventions. Ember Data can also be configured to integrate with existing JSON APIs that do not follow the conventions. Ember Data can be configured to handle any data your server returns.

Ember Data concepts

There are some concepts of Ember Data that should be understood in order to properly leverage all the power the module has to offer. These concepts are as follows:

- Store: The store acts as the central repository of all records in the application

- Model: A model is a class that defines properties and behavior of the data

- Record: A record is an instance of a model and contains data loaded from the server

- Adapter: An adapter is an object that handles translating requests and responses from the server

- Serializer: The serializer is responsible for converting raw JSON sent from the server into record objects

Models

Models in an Ember application are associated with every route. Models are set up with data by either a route implementing the model property in a v view using the {{link-to}} helper, passing the model as an argument, or invoking a route's transitionTo method, passing the model as its argument.

Creating a model

To create a new model, use the ember:model subgenerator as follows:

$ yo ember:model Post title:string body:string image:string slug:string--coffee

The preceding command creates a new Ember model located at app/scripts/models/post_model.coffee.

By passing the name of the model and default attributes as property:type, they will be added to the model definition.

Methods

Models in Ember extend the Ember.Object class and are defined as DS.Model; there are many methods available for this class, but let's take a look at the most frequently used methods.

The following are the DS.Model methods:

- changedAttributes: This will return the object with keys of changed properties and values of the [old, new] array

- deleteRecord: This will mark the record for deletion, invoke save() to send to server, or rollback() to revert

- destroyRecord: This will delete the record immediately and send to the server

- save: This will save the record, return a promise, and send it to the external source via the adapter

- serialize: This will return the JSON representation of the object using the adapter's serialization

- toJSON: This will return the JSON representation of a record

Note

For more information on Models, visit http://goo.gl/PCEGrC.

Attributes

Models in Ember use attributes to define the properties on the model when parsing the JSON payload sent to and from the server. Attributes are defined using the DS.attr() method by passing the first argument as either string, number, boolean, or date, and the second argument as an object of options such as defaultValue or defined as the computed properties, which is a function that returns the value.

Models can also define relationships with other models by using either the DS.belongsTo or DS.hasMany method by passing in the name of the model as the argument.

For example, open app/scripts/models/post_model.coffee and add the following highlighted code:

#global Ember
LearningYeomanCh5.Post = DS.Model.extend(
title: DS.attr('string', {defaultValue: 'Post title'})
body: DS.attr('string')
image: DS.attr('string')
published: DS.attr('boolean', {defaultValue: true})
created: DS.attr('date', {defaultValue: () -> new Date()})
slug: (->
@get('title').replace(/\W/g, '-').toLowerCase()
).property('title')
)

The preceding code does the following:

- At the top, the DS.Model.extend method is used to create a new Post model on the app's namespace

- The object passed to the extend method will set the default attributes on the model

- Three of the four types of attributes are used to define the title, image, body, published, and slug properties

- The slug property is a computed function that will take the title, strip all non-characters, and make it lowercase

Note

Slug is a part of a URL that identifies a page using human-readable keywords.

Fixtures

Fixtures in Ember allow you to define default static data that can be used when waiting for an API to become available. By changing the ApplicationAdapter property on the Ember.Application instance, you can easily change how your app communicates with a data source. Open the app/scripts/store.coffee file and change its load fixture data by using the DS.FixtureAdapter property as follows:

LearningYeomanCh5.ApplicationAdapter = DS.FixtureAdapter
LearningYeomanCh5.Store = DS.Store.extend({})

The preceding code sets the application's data adapter to use fixtures, and now to populate the fixture data, open app/scripts/models/post_model.coffee and add the following code:

LearningYeomanCh5.Post.FIXTURES = [
id: 1
title: 'Post 1'
body: 'Lorem ipsum dolor sit amet, adipiscing elit.'
image: '//placehold.it/225'
,
id: 2
title: 'Post 2'
body: 'Lorem ipsum dolor sit amet, adipiscing elit.'
image: '//placehold.it/225'
,
id: 3
title: 'Post 3'
body: 'Lorem ipsum dolor sit amet, adipiscing elit.'
image: '//placehold.it/225'
]

The preceding code sets the FIXTURES property on the Post model to an array of objects that contain default values.

Records

Ember uses a record to define an instance of a model, which contains the data loaded from a server. Records are used to find, create, delete, and update entities in the application; records are uniquely identified by two things:

- A model type

- A globally unique ID

Finding all records

To find all records of a model, invoke the find method on the store by passing the name of the model as the only argument, as follows:

@store.find('post'); // => [ { id: 1, title: 'Post 1', ... } ]

Finding a single record

To find a single record, invoke the find method on the store; the first argument is the name of the model and the second is the ID of the record, as follows:

@store.find('posts', 1); // => { id: 1, title: 'Post 1', … }

Creating a record

To create a record, invoke the createRecord method on the store; here, the first argument is the name of the model and the second is an object of properties to create it with, as follows:

@store.createRecord('post', {
title: 'New Post'
body: 'This is a new post'
image: '//placehold.it/225'
})

Deleting a record

To delete a record, invoke the deleteRecord method on the store, which will mark it for deletion, and then use the save method to send the command to the server for removal or the rollback method to revert the deletion, as follows:

@get('model').deleteRecord()

Note

For more information on records, visit http://goo.gl/EfiyvL.

Routes

Routes in Ember represent each of the possible states in the application and are represented by the URL. Every route in Ember has a model, which is always kept in sync with the current URL. Ember allows you to specify a different root URL instead of the domain's root by setting the rootURL property when creating a route, which allows applications to run in deeply nested domains.

As the application increases in size, you can set the LOG_TRANSITIONS property to true on the Ember.Application instance that allows all routes to be logged in the console, which is great for debugging, or seeing the autogenerated routes that Ember produces.

Creating the routes

To create a new route, use the ember:model subgenerator because it will generate all the necessary controllers, views, and routes. Open the terminal and execute the following command:

$ yo ember:model tag

This command will create the following:

- Creates a new model class located at app/scripts/models/tag_model.coffee

- Creates a new controller class located at app/scripts/controllers/tags_controller.coffee

- Creates a new edit controller class located at app/scripts/controllers/tag_edit_controller.coffee

- Creates a new view class located at app/scripts/views/tags_view.coffee

- Creates a new edit view class located at app/scripts/views/tags_edit_view.coffee

- Creates a new Handlebars view template located at app/templates/tag.hbs

- Creates a new Handlebars edit view template located at app/templates/tag/edit.hbs

Using routes

There are two things to keep in mind when using routes in Ember, which are as follows:

- Resource: This is the beginning of a route, controller, or template name

- Route: This is nested inside a resource and is added to the resource name separated by a dot (.)

For example, open the applications router located at app/scripts/router.coffee, and examine the following code:

LearningYeomanCh5.Router.map( ->
@resource('posts', ->
@resource('post', path: '/:post_id', ->
@route('edit')
)
@route('create')
)
)

The preceding code does the following:

- The application's route is defined by passing a function to the map property on the router

- A resource is declared for posts, and inside the posts resource function another resource is declared for editing a post, which sets the path to /posts/:post_id and the route to edit

- Then, the create route is defined to handle routing to /posts/create to add a new post

Posts route

The generator will create a route that will handle fetching all the records for the model in the store.

Open the app/scripts/routes/posts_route.coffee file and examine the content:

LearningYeomanCh5.PostsRoute = Ember.Route.extend(
model: ->
@get('store').find('post')
)

The preceding code does the following:

- The PostsRoute class is declared extending the Ember.Route class

- The model property is set to the records returned from the store's find method

This allows the controller and template to have access to the model property that contains an array of model records.

Post route

The generator will create a route that will handle setting the model found in the store.

Open the app/scripts/routes/post_route.coffee file and examine the following content:

LearningYeomanCh5.PostRoute = Ember.Route.extend(
model: (params) ->
@get('store').find('post', params.post_id)
)

The preceding code does the following:

- The PostRoute class is declared extending the Ember.Route class

- The model property is set to the record returned from the store's find method invoked with the routes params, which holds post_id

This allows the controller and template to have access to the model property that contains the properties from the record.

Posts edit route

The generator will create an edit route that will handle a finding record by ID for the model in the store.

Open the app/scripts/routes/post_edit_route.coffee file and examine the following content:

LearningYeomanCh5.PostEditRoute = Ember.Route.extend(
model: (params) ->
@get('store').find('post', @modelFor('post').id)
)

The preceding code does the following:

- The PostRoute class is declared extending the Ember.Route class

- The model property is set to the record returned from the store's find method invoked with the routes param property

Note

For more information on routes, visit http://goo.gl/sKma5G.

Templates

Ember has integrated the Handlebars semantic templating library to power your app's user interface. Handlebars allow the use of regular HTML markup along with the double mustache {{ }} expressions to display data and automatically update when the underlying data changes.

Handlebar helpers

Ember provides Handlebars helpers that can be used in your templates to render other views. Here is the list of default helpers:

- {{ partial }}: This renders the specified name of the template in the current view and has access to the current scope.
The partial's name must start with an underscore (data-template-name="_partial" or data-template-name="views/_partial").

- {{ view }}: This renders the specified view template and has access to the current model and controller.
This helper works like the partial helper, except instead of providing a template to be rendered within the current template, you provide a view class.

- {{ render }}: This renders the view template in the current view and has access to the current model and controller, and takes the following two parameters:

- The first parameter describes the context to be set up

- The optional second parameter is a model, which will be passed to the controller if provided

Posts template

The generator will create a template that will display a list of all the records for the model in the store.

Open the app/templates/posts.hbs file and replace with the following content:

<div id="posts">
<div class="page-header">
<h1>Posts</h1>
</div>
<div class="list-group">
{{#each model}}
{{#link-to 'post' this classNames='list-group-item'}}
<h4 class="list-group-item-heading">{{ title }}</h4>
<p class="list-group-item-text">
{{ body }}
</p>
{{/link-to}}
{{/each}}
</div>
</div>
{{outlet}}

The preceding code does the following:

- At the top, a div element is declared with the ID of posts

- A legend element is used to display the title of the page

- A div element with the list-group class is declared to hold all posts

- The {{#each}} helper is used to loop each item in the model

- The {{#link-to}} helper is used to link the current item to the PostRoute route's model property

- The heading element is used to wrap the title of the current item being repeated

- The paragraph element is used to wrap the body of the current item being repeated

Save the file and the browser should refresh; navigate to the /#/posts route and the display should be similar to the following screenshot:

Post template

The generator will create a template that will display a detailed view of the current model in the store based on the route.

Open the app/templates/post.hbs file and add the following content:

<div id="post-details">
<div class="row">
<div class="col-md-12">
<div class="page-header">
{{#link-to 'post.edit' model classNames='btn btn-default btn-edit pull-right'}}Edit{{/link-to}}
<h1>{{model.title}}</h1>
</div>
<img class="img-thumbnail pull-right" {{bind-attr src=model.image}}/>
<p>
{{model.body}}
</p>
</div>
</div>
</div>
{{outlet}}

The preceding code does the following:

- At the top, a div element is declared with an id of post-details

- Then, another div element is declared with the class set to row

- Another div is declared with the class of page-header for styling

- The {{#link-to}} helper is used to link the current model to the PostsEditRoute route's model property

Save the file and the browser should refresh; navigate to the /#/posts/2 route and the display should be similar to the following screenshot:

Posts edit template

The generator will create a template that will display a form view of the current model in the store based on the route.

Open the app/templates/post/edit.hbs file and add the following content:

<div id="post-edit">
<div class="page-header">
<h1>Edit {{title}}</h1>
</div>
<form role="form">
<div class="form-group">
<label for="title">Title</label>
{{input type="text" class="form-control" value=title}}
</div>
<div class="form-group">
<label for="title">Slug</label>
{{input type="text" class="form-control" value=slug disabled="true"}}
</div>
<div class="form-group">
<label for="image">Image</label>
{{input type="text" class="form-control" value=image}}
</div>
<div class="form-group">
<label for="body">Body</label>
{{textarea rows="5" class="form-control" value=body}}
</div>
<button class="btn btn-primary" {{action 'save'}}>
Update
</button>
<button class="btn btn-danger" {{action 'destroy'}}>
Delete
</button>
</form>
</div>

The preceding code does the following:

- At the top, a div element is declared with an id of post-edit

- Next, a form element is declared that will contain input elements

- Then, for each property in the model, the {{input value=property}} helper is used to render a data binding text input

- The {{action 'save'}} action helper is used to trigger the save action defined in the PostsEditController method

- The {{action 'destroy'}} action helper is used to trigger the destroy action defined in the PostsEditController method

Save the file and the browser should refresh; navigate to the /#/posts/2/edit route and the display should be similar to the following screenshot:

Note

For more information on templates, visit http://goo.gl/rcmwSf.

Controllers

In Ember.js, controllers allow you to decorate your models with display logic; models have properties that are sent to the server and controllers have properties that do not need to be sent to the server.

Post edit controller

The generator will create a controller that will control the edit view; let's add some logic that will handle saving and destroying a model.

Open the app/scripts/controllers/post_edit_controller.coffee file and add the following content:

LearningYeomanCh5.PostEditController = Ember.ObjectController.extend(
needs: 'post'
actions:
save: ->
@get('model').save()
@transitionToRoute 'post', @get('model')
destroy: ->
@get('model').deleteRecord()
@transitionToRoute 'posts'
)

The preceding code does the following:

- The PostEditController class is defined extending the Ember.ObjectController class

- The needs property specifies that this controller depends on the post controller

- The actions object declares a save action and a destroy action

- The save action will get an instance of the model and invoke the save method, and then transitions the app to the post detail route

- The destroy action will get an instance of the model and invoke the deleteRecord method, and then transitions to the posts list route

Note

For more information, visit http://goo.gl/QYFfAs.

Self-test questions

The following are questions that you should be able to answer after reading this chapter:

1. Which template library is built into Ember?

2. Which class handles persisting data in Ember?

3. What two things uniquely identify a record object?

4. How do models define a relationship to another model?

5. How do you find a single record in Ember?

6. Which method is used to delete a model?

7. Which template helper links to a specific method on the controller, current route, or route ancestors?

Summary

We have covered how you can quickly get started with creating Ember.js applications easily by using Yeoman generators. We started off by creating a configuration object that will hold settings for the entire application. Next, we modified the application and index layouts to display the site title and navigation links, and then we explored the Handlebars templating system by binding data from the applications' configuration file to display a list of features.

We then created a simple CRUD operation that allows one to fetch records from a data store, modify them, and then save them back into the data store. We covered the core concepts of the Ember.js framework to give you an idea of how powerful and robust it truly is.

Next up, we are going to explore the world of creating custom Yeoman generators. We will take a look at using a generator to scaffold a custom generator that will be customized to scaffold a simple website using predefined templates and options.

Chapter 6. Custom Generators

This chapter will cover creating custom Yeoman generators with the Yeoman generator-generator. By the end of this chapter, you will be able to create a customizable Yeoman generator that is installed using Node's package manager (npm) and made available to the community.

In this chapter, we will leverage the Yeoman API to handle processing files, install Bower components, prompt for user input, and test the generator's output using Mocha, which is a feature-rich JavaScript test framework that runs on Node.js. By the end of this chapter, you will be able to save time and money developing your next project by creating a custom Yeoman generator.

In this chapter, we are going to cover the following:

- Creating custom Yeoman generators that take multiple answers from command-line prompts and scaffold a modern web application

- Using the API to download and install libraries from Bower into the application

- Writing tests that verify the directory structure and content of files created dynamically based on user input, and publish the generator to npm for community usage

- Using the generator to create a simple test website and create custom Grunt tasks

Anatomy of a generator

Yeoman generators run in a Node.js environment and are managed by npm. They are executed from the command line, have a powerful prompting library, and leverage the Node.js API for functionality. Generators are responsible for one and only one thing—to take user input and output generated files based on that input.

Types of generators

Generally, there are two types of generators:

- Copiers: It simply copies boilerplate files from one location to another

- Advanced: It has customizable options, remote dependencies, and more

The new custom generator

We are going to create a custom generator that will scaffold a single-page web application. The project will include jQuery, Bootstrap, and Handlebars that will be managed via Bower. The project is also going to use Grunt, which will run and watch files for changes, and start a Connect LiveReload server for development.

Installing the generator-generator

First, we need to install the generator to create our generator. Open the terminal and execute the following command:

$ npm install -g generator-generator

Note

The current version used is 0.4.4.

This command will install the Yeoman generator-generator globally, so you can use the yo generator command from within any directory.

Using generator-generator

To create a new Yeoman generator, execute the following command in the directory of your generator:

$ yo generator

The preceding command will ask questions about the generator as shown in the following screenshot:

Note

Enter your Github username and a different base generator name.

Understanding the directory structure

The directory structure that the Yeoman generator creates includes default templates and tests to streamline the creation of a new custom generator. Take a minute and review the directory structure created. The directory structure looks like the following:

- README.md: This file contains information on how to use the generator

- app: This folder contains the main module definition and templates:

- index.js: This file contains generator module definition

- templates: This folder contains custom boilerplate files:

- editorconfig: This file contains IDE settings

- jshintrc: This file contains JSHint settings

- _package.json: This file contains project information

- _bower.json: This file contains client-side dependencies

- travis.yml: This file contains build settings for Travis CI

- node_modules: This folder contains project dependencies

- package.json: This file contains information about the project

- test: This folder contains the unit tests for the generator:

- test-creation.js: This file tests the contents of the generated files

- test-load.js: This file tests loading into the Node.js environment

Adding logic to the generator

The logic for the generator is located in the app/index.js file. This file defines the generator module extending from the yeoman.generators.Base class as shown in the following code:

'use strict';
var util = require( 'util' );
var path = require( 'path' );
var yeoman = require( 'yeoman-generator' );
var yosay = require( 'yosay' );
var chalk = require('chalk');

var LearningYeomanCh6Generator = yeoman.generators.Base.extend(..);

The preceding code does the following:

- The util, path, yeoman, chalk, and yosay modules are included using the require method

- The LearningYeomanCh6Generator variable declares a new generator extending from the Base class in the yeoman.genators package

- The code that gets generated inside the extend method is omitted as we will cover that later

- At the bottom of the file, the module.exports variable is set to the custom generator object that makes it available to the public

Note

For more information on Node.js modules, visit http://goo.gl/TCpUR1.

Initializing the generator

The
init method initializes the generator by loading the package.json file and adding an event listener to the end event of the generator, as follows:

init: function() {
this.pkg = require('../package.json');
this.on('end', function() {
if (this.options['skip-install'] !== true) {
this.installDependencies();
}
});
},

The preceding code does the following:

- It adds an event listener on the end event that will install the dependencies if the –skip-install flag is false or null

- It sets the pkg property of the generator to the loaded package.json file in the project's root directory

Asking questions to the user

The
askFor method asks questions to the user by displaying prompts. After the user enters the input value, the callback then loops over each property and sets the name equal to the value on the generator using the following code:

askFor: function() {
var done = this.async();

this.log(yosay('Welcome to the marvelous LearningYeomanCh6 generator!'));
this.prompts = [
{
type: 'input',
name: 'siteTitle',
message: 'What is the name of your site',
"default": 'My Site'
}, {
type: 'input',
name: 'siteDesc',
message: 'What is the site description?',
"default": 'A modern site built with a Yeoman Generator.'
}, {
type: 'input',
name: 'featureTitle',
message: 'What is the feature?',
"default": 'Modern Site'
}, {
type: 'input',
name: 'featureBody',
message: 'The feature description?',
"default": 'A modern site using modern tools & technologies.'
}, {
type: 'input',
name: 'featureImage',
message: 'The feature image?',
"default": 'http://goo.gl/SYjnUf'
}
];
this.prompt(this.prompts, (function(props) {
this.siteTitle = props.siteTitle;
this.siteDesc = props.siteDesc;
this.featureTitle = props.featureTitle;
this.featureBody = props.featureBody;
this.featureImage = props.featureImage;
done();
}).bind(this));
},

Yeoman generators rely on the Inquirer.js library that handles the generators' prompts during the scaffold.

The following table lists the properties available for a question object:

Name

Type

Description

Type

Input

Confirm

List

Rawlist

The type property specifies the types of prompts, which are input, confirm, list, and rawlist.

Name

String

The name property specifies the name to use when storing the answer in the answers hash.

Message

String

The message property specifies the question to print.

Default

String

Number

Array

Function

The default property specifies the value or function that returns the default value. If the type is function, the first parameter is the current session's input answer.

Choices

Array function

The choices type can be either an array of choices or a function that returns an array of string values or objects (name: value).

Validate

Function

The validate property receives the value from user input; it will return true if the value is valid, or it will return an error message if false.

Filter

Function

The filter property receives the value and returns a filtered value; the value returned will be added to the answers hash.

When

Function

The when property receives the answers hash and returns true or false depending on whether or not the question should be asked.

Note

For more information on Inquirer.js, visit http://goo.gl/lMf2ty.

Copying the project files

The
projectFiles method will copy all the project-specific files. Any file at a project level that needs to be included should go under the projectFiles method. Consider the following code:

projectfiles: function() {
this.copy('_package.json', 'package.json');
this.copy('_Gruntfile.js', 'Gruntfile.js');

//Copy all of the bower specific files.
this.copy('bowerrc', '.bowerrc');
this.copy('_bower.json', 'bower.json');

//Copy all files that handle code editing.
this.copy('editorconfig', '.editorconfig');
this.copy('jshintrc', '.jshintrc');

//Copy all files that handle git repositorys
this.copy('gitignore', '.gitignore');
this.copy('gitattributes', '.gitattributes');

//Copy files for Travis CI.
this.copy('travis.yml', '.travis.yml');
},

The preceding code does the following:

- It will copy the files from the app/templates directory to the directory where the generator was invoked

- In this case, the package.json, Gruntfile.js, .bowerrc, bower.json, .gitignore, .gitattributes, .travis.yml, .editorconfig, and .jshintrc files will be copied from the templates directory to the root of the project

Copying the application files and folders

The
app method will copy and create all the application-specific files and folders. The copy function will pass the generator instance if no third argument is set to each template; this allows you to use Underscore template expressions <%= %> to render user input in the templates, as follows:

app: function() {
this.mkdir('app');
this.mkdir('app/images');
this.mkdir('app/scripts');
this.mkdir('app/styles');
this.mkdir('app/pages');
this.copy('_index.html', 'app/index.html');
this.copy('_main.html', 'app/pages/main.html');
this.copy('_main.js', 'app/scripts/main.js');
this.copy('_main.css', 'app/styles/main.css');
},

The preceding code does the following:

- It invokes the mkdir method that takes a string of the path where a directory should be created. Here, we create the images, scripts, and styles folders in the app directory.

- It will invoke the copy method to copy the image file from app/templates into app/images.

- Each copy method will copy the specified file in the first argument to the second argument's location, passing an instance of the generator to each file, allowing the use of Underscore templating methods inside the template files.

Installing dependencies with Bower

The
bowerInstaller method will invoke the bowerInstall method that the Yeoman API provides, which will fetch the specified libraries from Bower, download them into the projects bower_components folder, and add the entries into the projects bower.json file. Consider the following code:

bowerInstaller: function() {
if (this.options['skip-install'] !== true) {
this.bowerInstall(['jquery', 'handlebars', 'bootstrap'], {
save: true
});
}
});

The preceding code does the following:

- The bowerInstaller method installs the specified libraries and saves them to the bower.json file

- This method will only be invoked if the user did not specify the --skip-install flag

That wasn't so bad now, was it? We wrote six functions that extended the yeoman.generators.Base object that will handle the following:

- Initializing the generator and loading the package.json file

- Asking the session user questions and storing responses

- Copying all project-specific files into the root directory where the generator was invoked

- Creating the application-specific folder structure

- Then copying all application-specific files passing in the generator's session instance to use the dynamic values from the questions

- And finally, installing some libraries using the bowerInstall method that takes an array of dependencies to install

Now we are ready to start customizing the templates that were created by the generator-generator by default, and add some custom styles, and scripts that handle the logic for routing from page to page and loading the main view. Let's proceed to create some templates.

Creating custom templates

The generators template files are located in the app/templates directory, where the generator performs all file operations in the templates directory as the current working directory. We will create a few templates in this directory that are prefixed with an underscore (_) to indicate that the file contains Underscore.js templating syntax <%= exp %>. The files that we will create in addition to the default generated files will be the following:

- _Gruntfile.js: This file will contain the task settings

- _bower.json: This file will contain the client dependencies

- _index.html: This file will display the home page

- _main.css: This file will contain default styles

- _main.js: This file will bootstrap the app

Creating the Gruntfile.js file

The
Gruntfile.js file contains the tasks for the project; there will be a task to watch files, start a development server, and wire installed Bower components to the index.html page. Create a new file in the app/templates directory named _Grunfile.js. Here is the content of the _Gruntfile.js file:

'use strict';
module.exports = function(grunt) {
require('load-grunt-tasks')(grunt);
require('time-grunt')(grunt);
//Project tasks configuration
grunt.initConfig({});
};

The preceding code defines the logic for Grunt; the module.exports function passes in the grunt instance, which allows project-specific tasks to be configured.

Creating the watch task

Now that we have the initial structure of the Gruntfile set up let's add the configuration settings for the watch task. Open the app/templates/_Gruntfile.js file and add the following watch object to the initConfig method:

//Watch - This task will watch files and run tasks when files change.
watch: {
options: {
nospawn: true,
livereload: true
},
//Watch for index file changes and build
livereload: {
files: ['app/index.html', 'app/scripts/**/*.js', 'app/styles/*.css'],
tasks: ['build']
},
//Watch any bower changes and inject scripts.
bower: {
files: ['bower.json'],
tasks: ['bowerInstall']
},
},

Creating the serve task

Now, let's add the configuration settings for the serve task. Open the app/templates/_Gruntfile.js file and add the following connect object to the initConfig method:

//Connect Server - The actual grunt server settings
connect: {
options: {
port: 9000,
livereload: 35729,
hostname: 'localhost'
},
livereload: {
options: {
open: true,
base: ['.tmp', 'app']
}
}
},

The preceding code defines the server settings for the connect task. The options task sets up the server's port, host, and livereload port. The livereload target sets the open property to true, so the default web browser opens automatically and the base property specifies the location of files to serve.

Creating the bowerInstall task

Lastly, let's add the configuration settings for the bowerInstall task. Open the app/templates/_Gruntfile.js file and add the following bowerInstall object to the initConfig method:

//Bower installer - This installs bower_component packages into specified files.
bowerInstall: {
target: {
src: ['app/**/*.html'],
dependencies: true,
devDependencies: false
}
}

The preceding code defines the bowerInstall target task by setting the location of the src file to inject the scripts into; the dependencies property is set to true, which means that it will inject all items in the dependencies object in the project's bower.json file.

Registering tasks in Gruntfile.js

Now that we have created and configured the default tasks for the project, for the tasks to be available to the user, we will need to register a few named tasks. The tasks we will register will be a serve task, a build task, and the default grunt task.

Add the following to the bottom of the _Gruntfile.js file right before the closing bracket:

//Serve task - $ grunt serve
grunt.registerTask('serve', function(target) {
console.log('running serve');
grunt.task.run(['bowerInstall', 'build', 'connect:livereload', 'watch']);
});

//Build task - $ grunt build
grunt.registerTask('build', 'Building the project.', function() {
console.log('running build');
});

//Default task - $ grunt
grunt.registerTask('default', ['build', 'serve']);

The preceding code will register the following three tasks with Grunt:

- The serve task will handle starting the Connect LiveReload server that will serve the files in the app directory

- The build task will handle doing any pre-deployment tasks; feel free to customize what tasks are ran during the build phase

- The default task will handle running the build and serve tasks

Creating the package.json file for npm

The _package.json file is going to contain project-specific information that npm uses to install project dependencies. Create a new _package.json file in the app/templates directory, if it does not already exist, and then add the following contents:

{
"name": "<%= _.slugify(siteTitle) %>",
"version": "0.0.1",
"dependencies": {},
"devDependencies": {
"grunt": "~0.4.1",
"time-grunt": "~0.2.0",
"grunt-contrib-connect": "~0.5.0",
"grunt-contrib-watch": "~0.5.0",
"grunt-contrib-copy": "~0.4.1",
"grunt-bower-install": "~1.0.0",
"load-grunt-tasks": "~0.2.0"
},
"engines": {
"node": ">=0.8.0"
}
}

Creating the .editorconfig file for IDEs

The .editorconfig file specifies the projects style guidelines for different file types; it enables developers to share a common set of formatting styles, which the project works with numerous IDEs. Create a new file in the app/templates directory named editorconfig if it doesn't exist, and add the following code:

root = true

[*]
indent_style = tab
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = false

[*.md]
trim_trailing_whitespace = false

Note

For more information on EditorConfig, visit http://goo.gl/ZpiMa9.

Creating the .jshintrc file for JSHint

The .jshintrc file specifies configuration settings when JSHint is linking files in the project; it enables developers to have increased code quality by sharing a common set of coding standards. Create a new file in the app/templates directory named jshintrc if it doesn't exist, and add the following code:

{
"node": true,
"esnext": true,
"bitwise": true,
"camelcase": true,
"curly": true,
"eqeqeq": true,
"immed": true,
"indent": 2,
"latedef": true,
"newcap": true,
"noarg": true,
"quotmark": "single",
"regexp": true,
"undef": true,
"unused": true,
"strict": true,
"trailing": true,
"smarttabs": true,
"white": true
}

Note

For more information on JSHint options, visit http://goo.gl/ebwCEX.

Creating the .travis.yml file for Travis CI

Travis CI is a hosted continuous integration and deployment system. There are two versions, https://travis-ci.com for private repositories and https://travis-ci.org for public repositories. The .travis.yml file specifies the build configuration for a project hosted on https://travis-ci.org; it enables Travis CI to learn about the project and how to build it. Create a new file in the app/templates directory named travis.yml if it doesn't exist, and add the following code:

language: node_js
node_js:
- '0.10'
before_install:
- 'bower install'
- 'grunt build'

Note

For more information on Travis CI, visit http://goo.gl/wMVg0c.

The .gitattributes file for Git

The
.gitattributes file specifies separate merging strategies for individual files and/or directories, and how Git runs a diff command on non-text files, or filters contents before a checkin/checkout. Create a new file in the app/templates directory named gitattributes if it doesn't exist, and add the following:

* text=auto

Note

For more information on Git attributes, visit http://goo.gl/i4NwSQ.

The .gitignore file for Git

The .gitignore file intentionally specifies untracked files and directories that Git should ignore and not add to the repository. Create a new file in the app/templates directory named gitignore if it doesn't exist, and add the following code:

app/bower_components/
node_modules/
.tmp/
.grunt

Note

For more information on the Git ignore file, visit http://goo.gl/OfSHGW.

Creating the .bowerrc file for Bower

The .bowerrc file specifies the installation directory of Bower components; it enables developers to manage Bower components in a custom directory location. Create a new file in the app/templates directory named bowerrc if it doesn't exist and add the following code:

{
"directory": "app/bower_components"
}

The preceding code simply specifies in which directory Bower should install components into.

Creating the bower.json file for Bower

The bower.json file specifies the applications' dependencies, it enables developers to install, update, and remove application packages using the Bower package manager. Create a new file in the app/templates directory named _bower.json, if it doesn't exist, and add the following code:

{
"name": "<%= _.slugify(siteTitle) %>",
"version": "0.0.1",
"main": [
"app/scripts/main.js",
"app/styles/main.css"
],
"ignore": [
"./node_modules",
".tmp",
"app/bower_components",
"app/images"
],
"dependencies": {
"jquery": "latest",
"bootstrap": "latest",
"handlebars": "latest"
}
}

The preceding code does the following:

- It sets the name property to siteTitle that will be populated by the value from the prompts

- The main property defines the locations of the main files that should be only installed when using bower install

- The ignore property defines the locations that should be ignored when installing with bower install

- The dependencies property holds any Bower components that the project depends on

Note

For more information on configuring Bower, visit http://goo.gl/R98hD4.

Creating the application templates

The application-specific files will allow the user to run the app as well as have the files wired up and ready for logic. Let's take a look at the application level templates that will be created when the generator is invoked.

The index.html file

The index.html file is going to be the main application entry point; it will include default markup that will render the file in the browser upon initial page load.

The following is the content of the app/templates/_index.html file:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><%= _.capitalize(siteTitle) %></title>
<!-- bower:css -->
<!-- endbower -->
<link rel="stylesheet" href="styles/main.css"/>
</head>
<body>

<article class="container slim">
<header class="header">
<nav>
<ul class="nav nav-pills pull-right">
<li class="active">
<a href="#"> Home </a>
</li>
</ul>
</nav>
<h3 class="text-muted"><%= _.capitalize(siteTitle) %></h3>
<hr/>
</header>

<section class="jumbotron center">
<h1><%= _.capitalize(featureTitle) %></h1>
<img src="<%= featureImage %>" alt="<%= featureTitle %> image"/>
<p class="lead">
<%= _.capitalize(featureBody) %>
</p>
</section>
<section class="page"></section>
</article>

<!-- bower:js -->
<!-- endbower -->
<script type="text/javascript" src="scripts/main.js"></script>
</body>
</html>

The preceding code does the following:

- It declares the basic HTML5 site markup that contains templating and building tags, which get dynamically updated

- The title element inside the head uses the _.capitalize method from Underscore, which capitalizes the first letter of the siteTitle

- The <!-- bower:css --> comment is placed in the head where the installed Bower components' stylesheet files should get injected

- The markup used as the initial layout is referenced by the other projects created in earlier chapters using the templating syntax

- The <!-- bower:js --> comment is placed at the location where the installed Bower components' JavaScript files should go

- Then, at the very bottom, the main script for the application is declared, which will execute after the document is loaded

The main.css file

The
main.css file is going to store the applications' styles. Here is the content of the app/templates/_main.css file:

/* Styles for <%= _.slugify(siteTitle) %> */
body{
background: fefefe;
}
.slim{
max-width:760px;
}
.center{
text-align:center;
}

The preceding code does the following:

- A CSS comment is placed at the top that will be replaced by siteTitle

- Then some generic classes are declared that will adjust the background, width, and centering of text

The main.js file

The
main.js file is going to store the applications' logic. It will create a global namespace with properties to store the created models, controllers, and views. Here is the content of the app/templates/_main.js file:

/* Script for <%= _.slugify(siteTitle) %> */
$(document).ready(function(){
var App = {
config: null,
Models: {},
Controllers: {},
Views: {},
init: function(){
console.log('<%= _.capitalize(siteTitle) %> is ready to rock and roll!');
return this;
}
};

window.App = App.init();
});

The preceding code does the following:

- The document.ready function is used to execute the wrapped contents once the document is fully loaded

- The App variable is declared with some properties for storing config, models, controllers, and views

- The init method will simply log a message to the console and return the App object

- The window.App property is set to the App.init() method, which returns itself

Testing a custom generator

The generator-generator creates two example test files that will need to get modified to handle the prompts and files created when the jps-site generator is invoked. Yeoman does not set up any Grunt tasks for running the tests, so we are going to create a Gruntfile and populate it with tasks that will handle running unit tests and linting source files.

Note

The following steps assume that the current directory is generator-jps-site.

Setup

The first step will be setting up our testing environment, so that it will be easy to add more tests down the road. Perform the following steps:

1. First, add the following development dependencies to the package.json file in the generator's project directory:

"devDependencies": {
"grunt": "^0.4.4",
"grunt-contrib-coffee": "^0.10.1",
"grunt-contrib-nodeunit": "^0.3.3",
"grunt-contrib-jshint": "^0.9.2",
"grunt-contrib-watch": "^0.6.1",
"grunt-mocha-test": "^0.10.0",
"grunt-contrib-clean": "^0.5.0",
"load-grunt-tasks": "^0.4.0",
"time-grunt": "^0.3.1",
"chai": "^1.9.1",
"mocha": "^1.18.2"
},

In the preceding code, we declared some development dependencies that will be installed by npm that allow us to create Grunt tasks for compiling and testing.

2. Next, create a file named Gruntfile.js in the custom generator's root directory. Open the Gruntfile.js file and add the following code:

'use strict';
module.exports = function(grunt) {
require('load-grunt-tasks')(grunt);
require('time-grunt')(grunt);
grunt.initConfig({
clean: {
test: ['test/temp']
},
watch: {
compile: {
files: ['app/*.js', 'test/*.js'],
tasks: ['mochaTest']
}
},
mochaTest: {
test: {
options: {
reporter: 'spec'
},
src: ['test/**/test-*.js']
}
}
});
grunt.registerTask('default', ['clean', 'mochaTest']);
grunt.registerTask('test', ['default']);
};

The preceding code does the following:

- The module.exports property is set to a function that takes grunt as the argument, which allows access to the instance

- The grunt.initConfig method is invoked by passing an object of task-specific configuration settings

- The clean property declares a test target, which will remove any file and folder inside the test/temp directory

- The mochaTest property declares a test target, with options for reporting the specs run and src set to the location of the compiled files

- Finally, the grunt.registerTask method is invoked passing the name of the task (default) and subtasks to execute

Testing the generator output

The Yeoman API includes helpers for testing whether files exist, matching contents, and more. We are going to write a test that will execute the generator and ensure that the files created match the files expected. We will also match the content of those files against regular expressions to ensure that the values from the prompts are properly inserted in the correct locations.

The following specs are written in JavaScript. Create a new file test-creation.js in the test folder if it doesn't exist and add the following code:

'use strict';
var path = require( 'path' );
var helpers = require( 'yeoman-generator' ).test;
require( 'chai' ).should();

//Mocked questions and answers
var mockAnswers = {
siteTitle: 'My Test Site',
siteDesc: 'A modern site build to test.',
featureTitle: 'Mocha Tests',
featureBody: 'A modern site created by a Yeoman generator.',
featureImage: 'http://goo.gl/SYjnUf'
};

//Mock expected files
var mockFiles = [
'.bowerrc',
'bower.json',
'.gitattributes',
'.gitignore',
'.editorconfig',
'.jshintrc',
'.travis.yml',
'Gruntfile.js',
'package.json',
'app/styles/main.css',
'app/scripts/main.js',
'app/index.html'
];

//Mock expected file contents
var mockFilePairs = [
['app/index.html', RegExp( "<title>" + mockAnswers.siteTitle + "</title>" )],
];

describe( 'generator', function () {

//Before each test clean the test/temp folder and create a new generator.
beforeEach( function (done) {

helpers.testDirectory( path.join( __dirname, 'temp' ), (function (err) {
if (err) {
done( err );
}

//App generator
this.app = helpers.createGenerator( 'learning-yeoman-ch6:app', ['../../app'], 'test-gen' );

done();
}).bind( this ) );
} );

//First test if the files that were created match what we expect.
it( 'creates expected files', function (done) {

//Add some mock prompts for the user to answer
helpers.mockPrompt( this.app, mockAnswers );

//Skip installing of the bower and npm dependencies
this.app.options['skip-install'] = true;

//Run the app and test for files and file contents
this.app.run( {}, function () {
helpers.assertFile( mockFiles );
helpers.assertFileContent( mockFilePairs );
done();
} );
} );
} );

The preceding code does the following:

- At the top of the file, some inline JSHint options are declared

- The path, yeoman-generator, and chai helpers are imported

- The describe block contains the content for testing

- The mockAnswers variable is set to an object of question name/value pairs that will get passed to the generator

- The mockFiles variable is set to an array of files expected to be created after the generator is invoked

- The mockFilePairs variable is set to an array of file/regex subarrays

- The beforeEach method will clean the test/temp folder and create a new generator instance

- The it method tests that the created files match the expected files

- The mockPrompt method is invoked on the helper passing in the generator instance and mockAnswers

- The skip-install option is set to true on the instance of the generator

- The generator instance is then run; in the callback function, the assertFile method is invoked passing the array of expected files

- The assertFileContents method is invoked on the helper, passing the array of mockFilePairs that will ensure contents are correct

Test generator loading

The load tests will check whether the generator can be initialized and loaded into the Node.js environment without breaking. Open the test/test-load.js file and add the following content:

'use strict';
var assert = require('assert');
require('chai').should();
describe('jps-site generator', function() {
it('can be imported without blowing up', function() {
var app = require('../app');
assert(app !== undefined);
});
});

The preceding code does the following:

- At the top, it imports the assert and chai modules

- The it block will try and load the app/index.js module and assert that it is defined

To run the tests against the generator, you can use the following command:

$ grunt

After running this command, the result should look similar to the following screenshot:

With the proper configuration, we can write generator unit tests and use Grunt to watch when source files change. The tests are run to ensure whether everything is working as expected.

The new custom subgenerator

Let's create a subgenerator for the custom generator we just created; this subgenerator is going to simply create a new page in the project and add a link to the page defined in the projects app/index.html file.

To create a new subgenerator, open a terminal and make the generator we created before the current working directory and execute the following command:

$ yo generator:subgenerator page

The preceding command will invoke the generator:subgenerator command, which then scaffolds a subgenerator module definition file and a template in the root of the generator project.

Understanding the subgenerator's directory structure

The directory structure created is relatively simple; it consists of a folder named after the subgenerator, an index.js file that contains the module definition, and a template in the page/templates directory:

page
├── index.js
└── templates
└── somefile.js

1 directory, 2 files

The directory structure is broken down into the following:

- page: This folder is created and named after the subgenerator

- index.js: This file contains the subgenerator module logic

- templates: This folder contains the subgenerator template files

- somefile.js: This is the default template that was scaffolded

Note

The somefile.js file will be replaced with our actual template.

Creating subgenerator templates

Now that we have the initial subgenerator files, we need to create a custom template that will be copied into the target project when this subgenerator is invoked.

In the page/templates directory, create a new file named _page.html and add the following content:

<h1><%= name %></h1>

The content of the page/_page.html file is very simple; it only includes a Underscore.js templating expression to output the value of the passed name argument when the subgenerator was invoked.

Adding logic to the subgenerator

The logic for the subgenerator is very straightforward; the only responsibility the subgenerator has is to create a new file based on the name argument passed when invoking the subgenerator, and append a new link to the app/index.html file.

Open the page/index.js file and add the following content:

'use strict';
var util = require('util'), yeoman = require('yeoman-generator');

var PageSubGenerator = yeoman.generators.NamedBase.extend({

//init - Initialize sub-generator
init: function () {
if (this.name) {
return console.log('You called the page sub-generator with the argument' + this.name + '.');
} else {
throw new Error('You must provide a page name!');
}
},

//files - Write the template to the projects app directory file
files: function () {
return this.copy('_page.html', 'app/pages/' + this.name + '.html');
},

//Handle appending a link to the index.html pages .nav element
appendLink: function () {
var htmlLink = '<li><a href="#/' + this.name + '">' + this.name + '</a></li>';
return this.appendToFile('app/index.html', 'ul.nav', htmlLink);
}
});
module.exports = PageSubGenerator;

At the top of the preceding code, the util and yeoman modules are included using the require method. The code comments to understand the logic this subgenerator provides.

Using your custom generator

When developing generators, it's always a good practice to try the generator out by yourself. As you don't have tons of issues logged in Github or another system, let's use it and create a simple website.

Link your generator

Now we need to link the custom generator to the system so that we can use the generator from the command line; from inside the root folder of the generator project, execute the following command:

npm link

The preceding command will add a symbolic link to your systems path, allowing you to run the yo learning-yeoman-ch6 generator command from anywhere.

Note

For more information on npm link, visit http://goo.gl/fwKa4Z.

Scaffolding a new webapp

We are going to scaffold a website using the custom generator we created; open the terminal and perform the following steps:

1. Create the directory where your site will live and then execute the generator:

$ yo learning-yeoman-ch6

After running this command, the result will look like the following screenshot:

After answering the questions, the files, shown in the preceding screenshot, are created and starts the installation of dependencies.

2. Now, you can start the server by running the following command:

$ grunt serve

3. Your default web browser should open up displaying a page similar to the following screenshot:

4. With a terminal window open, invoke the subgenerator and add the About page:

$ yo learning-yeoman-ch6:page About

Note

Watch LiveReload automatically refresh the browser and display your newly created link.

Self-test questions

The following are questions the reader should be able to answer by the end of this chapter:

1. Which command do you use to invoke a subgenerator?

2. What is the default templating library Yeoman uses to process templates?

3. What are the two types of code generators?

4. What two tests are created when invoking the Yeoman generator-generator?

5. What library is used to test Yeoman generators?

6. What prompting library does Yeoman use for its command prompts?

7. What are the four methods that are generated on the custom generator object?

8. Which class must you extend when creating a subgenerator?

Summary

This chapter covered creating custom Yeoman generators that take multiple answers from command line prompts and scaffold a modern web application. We used the API to download and install libraries from Bower into the application.

We covered writing tests that verify the directory structure and contents of files created dynamically based on user input, and publish the generator to npm for community usage. We also covered using the generator to create a simple test website and created custom Grunt tasks.

In the next chapter, we are going to look at creating custom libraries using Yeoman. We will create three libraries using the CommonJS generator, Node.js generator, and jQuery plugin generator.

Chapter 7. Custom Libraries

In this chapter, we are going to cover how to create independent modules or plugins that can be used in a variety of ways such as a Node.js module, which will contain methods for creating a RESTful API server.

For the server side, we require a CommonJS module that will be used to handle the data store access, and then a jQuery plugin that will handle the client-side logic of performing CRUD operations via RESTful HTTP Ajax requests. We will write tests for these modules and use Bower and npm to publish them to the cloud for the npm community to consume.

In this chapter, we will cover the following topics:

- Using some of the official Yeoman generators to create different modules and plugins for different systems

- Creating a standard server-side JavaScript module in the form of CommonJS

- Creating a Node.js module that uses the same format as CommonJS

- Creating a jQuery plugin for usage in the browser

- The different types of modules and module loaders available on either the server or the client

The new CommonJS project

Using the Yeoman CommonJS generator, we will create a reusable module that can be consumed by other projects. This module will contain methods to perform CRUD (create, read, update, delete) operations. Let's get started!

Installing the generator-commonjs

To install the CommonJS generator, open a terminal and execute the following command:

$ npm install -g generator-commonjs

The preceding command will install the CommonJS generator globally on the system. You can use the yo commonjs command, which can be invoked anywhere.

Scaffolding a CommonJS project

To scaffold a new CommonJS project, open a terminal and do the following steps:

1. First, create the directory where the project is going to live, replace the jps attribute with your initials, and execute the following command:

$ mkdir jps-ds && cd jps-ds

The preceding command will create a new folder and make it the current directory.

2. Then, invoke the Yeoman generator inside the projects directory using the following command:

$ yo commonjs

The following actions are performed by the preceding command:

- It prompts you with a series of questions about the module

- It answers the questions as if you were the author

- After answering the questions, the scaffold process will take place

The output from the preceding command should look similar to the following screenshot:

That's it! All the necessary files were just created in the current directory and include everything to get started writing module logic and unit tests. Now we are going to add the logic that will handle accessing the database.

To use this module during development in another project on the same machine, simply execute $ npm link as an administrator user to set the module in your PATH. Now, you will be able to use Node's require method to include this module from within any project during development.

The CommonJS logic

The logic for this module is going to be fairly straightforward; we will wrap the module in a function that takes one argument, that is, the options passed to the module. This module will have methods for connecting to the database, creating a new model instance, finding all records, finding one record, creating a record, updating a record, and removing a record.

This module is going to leverage Mongoose, which is a Node module for accessing MongoDB. The other module that we will use is Q, which provides a cross-platform solution for composing asynchronous promises in JavaScript. Perform the following steps:

1. Add the Mongoose module to the project, open a terminal, and execute the following command:

$ npm install mongoose --save

Note

For more information on Mongoose, visit http://goo.gl/OscTpz.

2. Then, add the Q module to the project, open a terminal, and execute the following command:

$ npm install q --save

Note

For more information on Q, visit http://goo.gl/ZWFjnb.

The preceding commands will download and install the packages into your node_modules folder inside the projects directory; the --save flag will add the entries to your project's package.json dependencies.

Module properties

The generator will create a skeleton file located in learning-yeoman-ch7-commonjs/src with the filename set to the name of the module specified during the generator prompts.

The module is wrapped in an immediately invoked function that passes in the global exports object. Some of the default properties need to be adjusted; so, open the src/ds.js file and add the following code:

(function(exports) {
'use strict';
exports.DS = function(options){
var mongoose = require("mongoose"),
Q = require("q"),
instance = null,
models = {},
ds = {};
};
}(typeof exports === 'object' && exports || this));

The following actions are performed by the preceding code:

- An immediate invoking function wraps the entire module to ensure variables do not pollute any globals

- The DS property is set to a function on the exports object that takes one argument, that is, the options object to initialize this module with it

- The mongoose and q libraries are required and set to local variables

- The instance variable will handle keeping a reference of the current connection

- The models object will hold a reference to all the models when initialized

- The ds variable will hold all the methods that this module will expose to the public API

Connecting to MongoDB

The
connect method will create model instances for each of the models passed in the options object and open a connection to the MongoDB database specified. Open the src/ds.js file and add the following code:

ds.connect = function (host) {
if (options.models) {
for (var m in options.models) {
if (m) {
var model = mongoose.model( m, new mongoose.Schema( options.models[m] ) );

models[m] = model;
}
}
}
instance = mongoose.connect( "mongodb://" + host );
return this;
};

The following actions are performed by the preceding code:

- The connect method takes one argument, which is the mongo host location

- Inside the method body, we check if there is a models property on the passed arguments' options object

- If there are models, then a new model is created using the mongoose.model method by passing in the name of the model and mongoose.Schema that is created from the models object

- The newly created mongoose model instance is stored in the local models hash by name

Finding all models

The
findAll method will handle fetching all records from the MongoDB database; this method returns a promise that will be resolved once the records are retrieved. Add the following code to the src/ds.js file:

ds.findAll = function (name) {
var deferred = Q.defer();
if (!models[name]) {
throw new Error('Must add table to options.');
}
models[name].find( function (err, m) {
if (!err) {
deferred.resolve(m);
} else {
deferred.reject(err);
}
} );
return deferred.promise;
};

The following actions are performed by the preceding code:

- The findAll method takes one argument, that is, the name of the collection

- If no model is found in the models hash object, then an error is thrown

- If there is a model, then the find method is invoked on that model instance and the promise is resolved by passing the items found

- If there is an error, then the promise is rejected passing in the err object

Finding a model

The
findOne method will find a record by id and resolve the promise when found or reject if not found; add the following code to the src/ds.js file:

ds.findOne = function(name, id) {
var deferred = Q.defer();
if (!models[name]) {
throw new Error('Must add table to options.');
}

models[name].findById( id, function (err, m) {
if (!err) {
deferred.resolve( m );
} else {
deferred.reject( err );
}
} );
return deferred.promise;
};

The following actions are performed by the preceding code:

- The findOne method takes two arguments, which is the name of the collection and the ID of the document

- If no model is found in the models hash object, then an error is thrown

- If there is a model, then the findById method is invoked on the model instance that will then resolve the promise by passing in the object found from the method

- If there is an error while invoking the findById method, then the promise is rejected passing in the err object

Creating a model

The
create method will handle creating a new document in the collection; to create a new document, all that is required is the name of the collection to insert into and the data object to insert.

Open the src/ds.js file and add the following content:

ds.create = function(name, data) {
var deferred = Q.defer();
if (!models[name]) {
throw new Error('Must add table to options.');
}

var model = new models[name](data);
model.save(function(err, m) {
if (!err) {
deferred.resolve(m);
} else {
deferred.reject(err);
}
} );
return deferred.promise;
};

The following actions are performed by the preceding code:

- The create method takes two arguments, the name of the collection and the data object to insert

- If no model is found in the models hash object, then an error is thrown

- If there is a model, then the save method is invoked on the model instance by passing in the data object to create, which resolves the promise after that

- If an error occurs while invoking the save method, then the promise is rejected passing in the err object

Updating a model

The
update method will handle updating a document in the collection found by the ID of the document. To update a document, just pass in the name of the collection, the ID of the document, and a data object of key:value pairs to update.

Open the src/ds.js file and add the following content:

ds.update = function (table, id, data) {
var deferred = new Deferred();
if (!models[name]) {
throw new Error('Must add table to options.');
}
models[table].findByIdAndUpdate( id, data, function (err, m) {
if (!err) {
deferred.resolve( m );
} else {
deferred.reject( err );
}
} );
return deferred.promise;
};

The following actions are performed by the preceding code:

- The update method takes three arguments, the name of the collection, the ID of the document, and the properties to update

- If no model is found in the models object hash, then an error is thrown

- If there is a model, then the findByIdAndUpdate method is invoked on the model instance passing in the ID and the data to update; when finished, the promise will be resolved with the results from the method

- If an error occurs, then the promise will be rejected passing in the err object

Destroying a model

The
delete method will handle removing a document from a collection; all that is required is the name of the collection and the ID of the document from which to remove. Open the src/ds.js file and add the following code:

ds.destroy = function (table, id) {
var deferred = Q.defer();
if (!models[name]) {
throw new Error('Must add table to options.');
}
models[table].findByIdAndRemove( id, function (err, m) {
if (!err) {
deferred.resolve( m );
} else {
deferred.reject( err );
}
} );
return deferred.promise;
};
if (options.host) {
return ds.connect( options.host );
} else {
return ds;
}

The following actions are performed by the preceding code:

- The destroy method takes two arguments, the name of the collection and the ID of the document

- If no model is found in the models object hash, then an error is thrown

- If there is a model, then the findByIdAndRemove method is invoked by passing the ID of the document to destroy, and the promise is resolved by passing the response from the method

- If an error occurs, then the promise will be rejected by passing in the err object

Testing a CommonJS project

The CommonJS Yeoman generator includes nodeunit as the default test framework. The specs for the module are located in the test directory; using this test framework is fairly simple.

Note

For more information on nodeunit, visit http://goo.gl/vHzSyG.

Open the test spec that is located at test/ds_test.js and add the following code:

'use strict';
var DS = require( '../src/ds.js' ).DS;

var _ds = new DS( {
host: 'test:test@ds037498.mongolab.com:37498/learning-yeoman',
models: {
'pages': { title: String, body: String}
}
} );

var _page, _pages, _id;

exports['DS'] = {
setUp: function (done) {
done();
},
};

The following actions are performed by the preceding code:

- At the top of the file, the module that we are testing is imported using the require method

- Then, the _ds variable is set to a new instance of the DS module, passing in an options object that contains the mongo host

- The models property is set to an object that defines the collections' structure

- The _page variable will hold a reference of the page that will be used for testing

- The _pages variable will hold a reference of all the pages that are returned from the module

- The _id variable will hold a reference of the model created for deleting it as well

- The global exports object has the DS property, which is the name of the module that we were testing

- The setUp function will run before each key in the object and is used to set up the model before running tests

Test for no model

The module should throw an error if there is no model instance created before calling a method on the table. Open the test/ds_test.js file and add the following code:

'noTable': function (test) {
test.expect( 1 );
test.throws( function () {
_ds.findAll( 'null-table' );
}, Error, 'should throw Error if no table' );
test.done();
},

The following actions are performed by the preceding code:

- The noTable property is declared, which is set to a function that takes a test argument, which is an instance of nodeunit

- The expect method is called, declaring that there should be one test expectation

- The throws method is called with a function that will invoke the findAll method by passing a table that does not exist

- The module should throw an error if no table is found prior being invoked

- The done method is called to inform nodeunit that the test is complete

Test finding all models

To ensure that the module can return the proper data when the findAll method is invoked, open the test/ds_test.js file and add the following code snippet to the DS object:

'findAll': function (test) {
test.expect( 1 );
_ds.findAll( 'pages' ).then( function (data) {
_pages = data;
test.ok( (data instanceof Array) );
test.done();
} );
},

The following actions are performed by the preceding code:

- The findAll property is declared, which is set to a function that takes a test argument, which is an instance of nodeunit

- The expect method is called, declaring that there should be one test expectation

- Then, the findAll method is invoked with the pages argument that will set the _pages variable to the returned data

- Inside the then method, the ok method is called to ensure the data object is an instance of the Array class

- The done method is called to inform nodeunit that the test is finished

Test finding one model

The findOne method should return an object, so let's create the spec. Open the test/ds_test.js file and add the following code snippet to the DS object:

'findOne': function (test) {
test.expect( 1 );
_id = _pages[0]._id;
_ds.findOne( 'pages', _id ).then( function (data) {
_page = data;
test.ok( (data instanceof Object), 'should return object.' );
test.done();
});
},

The following actions are performed by the preceding code:

- The findOne property is declared and is set to a function that takes a test argument, which is an instance of nodeunit

- The expect method is called, declaring that there should be one test expectation

- The _id variable is set to the first object's ID in the results from the findAll method called before

- Then, the findOne method is invoked with the _id argument that sets the _page variable to the object returned

- Inside the then method, the ok method is called to ensure the _page object is an instance of the Object class

- The done method is called to inform nodeunit that the test is finished

Test creating a model

The module should be able to create a new model, so we will write a spec for that. Open the test/ds_test.js file and add the following code to the DS object:

'create': function (test) {
test.expect( 1 );
_page = {
title: 'Page ' + Date.now(),
body: 'This is the page content.',
published: true,
created: new Date()
};
_ds.create( 'pages', _page ).then( function (data) {
test.ok(data._id, 'should return object with id.' );
test.done();
} );
},

The following actions are performed by the preceding code:

- The create property is declared, which is set to a function that takes a test argument, which is an instance of nodeunit

- The expect method is called, declaring that there should be one test expectation

- The create method is invoked on the module, passing in the _page object that was constructed

- Inside the then function, the ok method is used to test that the data returned from the module has an _id object, which will be the newly created model

- The done method is called to inform nodeunit that the test is complete

Test updating a model

The module should be able to update an existing model; open the test/ds_test.js file and add the following code to the DS object:

'update': function (test) {
test.expect( 1 );
_page = {
title: 'Updated Page'
};
_ds.update( 'pages', _id, _page ).then( function (data) {
test.equal( data.title, 'Updated Page', 'should have updated title.' );
test.done();
} );
},

The following actions are performed by the preceding code:

- The update property is declared, which is set to a function that takes a test argument, which is an instance of nodeunit

- The expect method is called, declaring that there should be one test expectation

- The update method is called on the pages model, passing in the _id variable and the update _page object

- Inside the then function, the equal method is used to ensure the returned value from the module is the same value that was updated

- The done method is called to inform nodeunit that the test is complete

Test destroying a model

The module should be able to delete a model; open the test/ds_test.js file and add the following to the DS object:

'destroy': function (test) {
test.expect( 1 );
_ds.destroy( 'pages', _page._id ).then( function (data) {
test.equal( data, true, 'should return object.' );
test.done();
} );
}

The following actions are performed by the preceding code:

- The destroy property is declared, which is set to a function that takes a test argument, which is an instance of nodeunit

- The expect method is called, declaring that there should be one test expectation

- The destroy method is called on the pages model, passing the ID of the updated model used before

- Inside the then function, the equal method is used to ensure the data returned is true, signifying a successful deletion

- The done method is called to inform nodeunit that the test is complete

To run the tests, execute the following command:

$ grunt nodeunit -v

The preceding command will output something similar to the following screenshot:

The default grunt command will build and test the module; different tasks can be added to the Gruntfile.js file, easily allowing the use of more tasks.

Deploying to npm

Deploying the newly created module is fairly straightforward using npm, which is a package manager for managing Node dependencies. You need to authenticate first by running the $ npm adduser command. To publish this module to npm, use the following command:

$ npm publish

Your module will then show up in results from the npm search command, if the criteria matches that defined in the modules package.json file.

Conclusion

In this section, we have learned about the CommonJS module format and how to create modules that can be used on the server side. We covered the basics of setting up testing.

The new Node.js module project

We can easily scaffold node modules using the Yeoman Node.js generator; all projects include nodeunit unit tests. The generator is based on the grunt-init-node module, authored by the magnificent GruntJS team.

Installing the generator-node

To install the Node.js generator, execute the following command:

$ npm install -g generator-node

The preceding command will install the Node.js generator globally on the system. Now, the yo node command can be invoked anywhere.

Scaffolding a Node.js module project

First create a new directory and make that the current working directory. To quickly scaffold a new Node.js module project, execute the following command:

$ yo node

The preceding command will prompt you with a series of questions about the plugin. After answering the questions, the scaffold process will take place.

The output from the preceding command should look similar to following screenshot:

You can answer the questions as if you are the plugin author. For demonstration purposes, you can use any information you like; just be sure to make the proper naming changes when following the code examples.

The Node.js module logic

The logic for this module is very straightforward; it will consist of a simple Node.js server that will handle GET/PUT/POST/DELETE HTTP calls from the client. The module will return an instance of an Express server that can be used in any application.

Before we can start writing the module logic, we need to install the module's dependencies. Open a terminal and execute the following command:

$ npm install supertest express body-parser --save

The preceding command with download and install the supertest, express, and body-parser modules from npm and will add them to the project's package.json dependencies array.

Open the lib/jps-node-plugin.js file and add the following content:

'use strict';
exports.RestServer = function() {
var express = require('express'),
bodyParser = require('body-parser'), server = express();
server.use(bodyParser.json());
};

The following actions are performed by the preceding code:

- On the global exports object, a RestServer module function is declared that will house the logic for the module

- Next, express is loaded via the require method along with the body-parser module

- Then, a new express instance is created, which is set to the private server variable

Now that the module definition is in place, we can start adding methods that will handle incoming HTTP requests. Inside the module definition, add the following code:

server.get('/api', function (req, res) {
res.json({message: 'RESTful Node API Server'});
});

server.get('/api/:table', function (req, res) {
res.json({message: 'Query items in ' + req.params.table});
});

server.post('/api/:table', function (req, res) {
res.json({message: 'Create item in ' + req.params.table});
});

return server;

The following actions are performed by the preceding code:

- At the top, the entire module is wrapped inside a function that is declared on the exports object

- The name of the module is the name of the property that is declared on the exports object, which is a function that will return the server object

- The server object contains handlers that will accept HTTP requests in the form of GET and POST

- For example, each call will just return a message of what it is supposed to do in JSON; you can actually extend this further with logic that does what you desire

Note

For more information on Express, visit http://goo.gl/0qEBVX.

Testing a Node.js module

The Yeoman Node.js generator sets up testing with the nodeunit framework and makes it easy to write unit tests for custom node modules; since we are going to be testing an HTTP server, we are going to use the supertest module that will handle sending example requests to the server and then make sure the response from the server is what we are expecting.

Open the test/jps-node-plugin_test.js file and add the following content:

var RestServer = require('../lib/jps-n.js').RestServer;
var request = require('supertest'),
mockServer,
expected = {
message: 'RESTful Node API Server'
};
//Start mock servermockServer = new RestServer();mockServer.listen(9090);
//Listen to mock server
exports.RestServer = {
setUp: function(done) {
done();
},
'GET /api/posts': function(test) {
expected.message = 'Query items in posts';
request(mockServer).get('/api/posts').expect('Content-Type', /json/).expect(200).end(function(err, res) {
if (err) {
throw err;
}
test.deepEqual(res.body, expected, 'should get all items');
test.done();
});
},
'POST /api/posts': function(test) {
expected.message = 'Create item in posts';
request(mockServer).post('/api/posts').expect('Content-Type', /json/).expect(200).end(function(err, res) {
if (err) {
throw err;
}
test.deepEqual(res.body, expected, 'should create item');
test.done();
});
}
};

The following actions are performed by the preceding code:

- At the top of the file, the request variable references the supertest module that is imported using the require method

- Then, we start a mock server by invoking the listen method on the module that we created earlier

- The tests are declared on the global exports object; the keys for the specs and the values are functions, which get invoked for every spec

- There will be two test cases, which are as follows:

- The first spec will test if the default route will return the message that is expected

- The GET /api/posts spec will send a GET request to the server and expect that the response is what we expect

- The POST /api/posts spec will send a POST request to the server along with an object we wish to create; the response should match the expected message set above the request call

To run the unit tests, open the terminal and execute the following command:

$ grunt -v

The default grunt task will lint and test the module displaying the results from the tests in the console, as seen in the following screenshot:

Note

For more information on SuperTest, visit http://goo.gl/GjiJAc.

Deploying

Since this is a Node.js module, the only logical place to deploy this module would be the Node's package manager (npm); to publish this package to npm, use the following command:

$ npm publish

The preceding command will register your package.json file into the npm repository, allowing your module to be installed and updated using npm.

Note

For more information on deploying, visit http://goo.gl/g4LuCP.

Conclusion

That was easy; now you should have an understanding on what a CommonJS module is and how to create one using the Yeoman CommonJS generator. We covered setting up a new CommonJS project, writing a unit test to verify the code works, and publishing the module to the npm repository. Now, you should be able to create CommonJS modules with ease and publish them to the npm repository.

The new jQuery project

Let's create a new jQuery project that will demonstrate using the Yeoman jQuery generator to quickly create a reusable plugin, which is fully tested and deployed to Bower for consumption by others in the community. This plugin will simply call CRUD methods on the client side by creating utility methods that aid in sending GET/PUT/POST/DELETE requests to the server and handling the response accordingly. Let's get started.

This generator is based on grunt-init-jquery, authored by the magnificent GruntJS team.

Installing the generator-jquery

To install the jQuery generator, open the terminal and execute the following command:

$ npm install -g generator-jquery

The preceding command will install the jQuery generator globally on the system. Now, the yo jquery command can be invoked anywhere.

Scaffolding a jQuery project

First create a new directory and make that the current working directory. To scaffold a new jQuery project, open a terminal and execute the following command:

$ yo jquery

The following actions are performed by the preceding command:

- It will prompt you with a series of questions about the plugin

- Answer the questions as if you were the author of this plugin

- After answering the questions, the scaffold process will take place, logging the output to the console as the files are created and the dependencies are downloaded

If you answered the questions, then you should see something similar to the following screenshot:

Adding the plugin logic

We are going to create the client-side adapter for the plugin that will handle sending objects to the server side for processing; the file that will handle the routing logic will be the CommonJS module we created earlier in the chapter. The server-side routes will be the Node.js modules that we created in the new Node.js project.

Let's go ahead and open the src/jps-plugin.js file and add the logic that will handle sending requests to the server:

(function ($) {
var crud = {
endpoint: '/api',
create: function (table, data) {
return this._send( 'POST', table, data );
},
read: function (table, data) {
return this._send( 'GET', table, data );
},
update: function (table, data) {
return this._send( 'PUT', table, data );
},
destroy: function (table, data) {
return this._send( 'DELETE', table, data );
},
query: function (table, params) {
return this._send( 'GET', table, params );
},
_send: function (type, table, data) {
var url;
url = this.endpoint + "/" + table;
if (data != null ? data.id : void 0 && type !== 'GET') {
url += '/' + (data != null ? data.id : void 0);
}
return $.ajax( {
url: url,
type: type,
dataType: "json",
data: data
} );
}
};

$.extend( $.fn, {
crud: crud
} );
})( jQuery );

The following actions are performed by the preceding code:

- The script looks similar to the CommonJS module created earlier, except that we are setting our plugin methods on the query object instead of the exports object.

- This is a self-invoking function that passes a reference of query to the constructor that will be used to set the plugin methods and options.

- The first method is set on the $ object, which is a function that takes an object of plugin options that will extend with the default options defined in the plugin.

- The create method will be invoked using $.crud.create(), which will take two arguments: the name of the collection and the object to create. The method will return a promise that is resolved upon response from the server.

- The read method will be invoked using $.crud.ready(), which will take two arguments: the first is the name of the collection and the second is the object to find; if null, it will find all objects in the collection. This method returns a promise that will be resolved once a response from the server is sent.

- The update method will be invoked using $.crud.update, which will take two arguments: the first is the name of the collection and the second is the object to update. This method will also return a promise that will be resolved once received from the server.

- The destroy method will be invoked using $.crud.destory, which will take two arguments: the first is the name of the collection and the second is the object to remove. This method will return a promise that is resolved once sent from the server.

Testing a jQuery plugin

The Yeoman jQuery generator sets up testing configuration for the plugin that includes the QUnit testing framework. Since this plugin interacts with the server by sending HTTP requests, having a way to mock the responses from the server is generally recommended, as we do not know if there is a server available to send our requests to and keeping everything local while running unit tests will speed up the time.

Note

For more information on QUnit, visit http://api.qunitjs.com/.

Before we start writing the spec, install the jquery-mockajax library. Open the terminal and execute the following command:

$ bower install jquery-mockajax --save-dev

The following actions are performed by the preceding command:

- It will download and install the jquery-mockajax library into the bower_components directory

- Then, it will add the library to the devDependencies object in the bower.json file

Now, add the jquery-mockajax library to the test spec runner located in the test/test-plugin.html file and add the following script tag that includes the jQuery Mockjax library:

<script src="../bower_components/jquery-mockajax/jquery.mockjax.js"></script>

Note

For more information on jQuery Mockjax, visit http://goo.gl/wkGACb.

Creating the unit test

Now that we have installed the Ajax mocking library, the next step would be to add the mock methods to the test spec. Open the test spec located at test/jps-plugin_test.js and add the following code:

/* global module, asyncTest, jQuery, expect, equal, start */
(function ($) {

var expected = {
create: 'Create item in posts',
query: 'Query items in posts'
};

module('$.fn.crud', {
setup: function() {
$.mockjax({
url: '/api/*',
type: 'GET',
responseText: {
message: expected.query
}
});
$.mockjax({
url: '/api/*',
type: 'POST',
responseText: {
message: expected.create
}
});
}
});

The following actions are performed by the preceding code:

- The entire spec is an immediate function that will pass jQuery as the parameter

- The expected object contains the results that we are expecting; the keys in the object represent the method and the value represents the expected value

- The module method creates a new spec; the first argument ($.fn.crud) is the name of the test and the second parameter is an object containing the setup function that is used for bootstrapping the spec before it is run

Now, we are ready to write a few tests to verify that the plugin is returning the data that we are expecting; open the test/jps-plugin_test.js file and add the following code:

//Should make a POST request to the server sending the data.
asyncTest( 'create', function () {
expect( 1 );
$.fn.crud.create( 'posts', {name: 'test', body: 'This is a test.'} ).done( function (data) {
equal( data.message, expected.create, 'should return data' );
start();
} );
} );

//Should make a GET request to the server.
asyncTest( 'query', function () {
expect( 1 );
$.fn.crud.query( 'posts' ).done( function (data) {
equal( data.message, expected.query, 'should return data' );
start();
} );
} );

}( jQuery ));

The following actions are performed by the preceding code:

- The asyncTest('create') method will handle verifying whether the $.crud.create() method makes the proper request and returns the correct response. The start method is called to inform QUnit that the async operation is complete and it is good to proceed.

- The asyncTest('update') method also handles verifying whether the $.crud.update() method makes the property request, and inside the done method, the results are compared to the expected update object to match, again calling the done method when complete.

- The same process goes for the destroy method, as well, making the call and comparing the results with the expected value.

To run these unit tests, open a terminal and execute the following command:

$ grunt test

The output from the command should look similar to the following screenshot:

You can also run these tests in the browser by opening the test/jps-plugin.htm file in any browser of your choice.

Deploying to Bower

The jQuery plugin will be deployed to Bower; this allows the library to easily be updated whenever a new version is available. To deploy the plugin to bower.io's repository, perform the following steps:

1. The first step is to create the bower.json file by running the following command:

$ bower init

The following actions are performed by the preceding command:

- It will initialize a new bower.json file and ask you a series of questions about the library

- The prompts are already set up with the expected default values; confirm each entry by pressing Enter

2. Next, you need to register the plugin by executing the following command:

$ bower register [name] [url]

The preceding command will register the plugin's name and URL with Bower. The URL is generally the location of the repository but can reference a .zip file or other resource.

3. To validate that your plugin is now registered with Bower's repository, open a terminal and execute the following command:

$ bower search [plugin-name]

The preceding command will send a search query to bower.io and return any results that match the [name] argument, as demonstrated in the following screenshot:

Conclusion

Creating a jQuery plugin is fun and pretty easy, but using the Yeoman jQuery plugin generator makes it even easier. In this section, we covered installing and scaffolding a plugin project using the jQuery generator. Now that you have created a jQuery plugin with unit tests and published to Bower, the sky is limit.

Self-test questions

The following are questions that you should be able to answer after reading this chapter:

1. What is a package manager?

2. What is a command-line interface?

3. How do you register a package on Bower?

Summary

We have covered how to create different modules and plugins for different systems using some of the official Yeoman generators. We began by creating a standard server-side JavaScript module in the form of CommonJS; we also created a Node.js module that uses the same format as CommonJS. We covered creating a jQuery plugin for usage in the browser.

We also got acquainted with the different types of modules and module loaders available on either the server or client. This should give you enough ammo to create some useful plugins that are available for the community.

In the next chapter, we are going to cover how to use Grunt, the JavaScript task runner.

Chapter 8. Tasks with Grunt

This chapter will cover using Grunt, the JavaScript task runner; we will install and set up Grunt in new or existing projects. We will use Yeoman generators to automate the processes of automating a project.

There are a number of benefits of using Grunt in your everyday development workflow. We are going to cover what Grunt is and how to utilize the various tasks for managing projects as well as adding Grunt to existing projects. Let's get started!

In this chapter, we are going to cover the following topics:

- The basics of Grunt and how to add it to a new or existing project

- The Yeoman Grunt plugin generator to create a plugin that is published to npm

Overview on GruntJS

Grunt is a JavaScript task runner for managing tasks during development. Grunt runs in the Node.js environment and provides a quick solution over other build tools because of the huge ecosystem of plugins, which add additional functionality to the command line. Grunt is an alternative to Ant or Make.

Grunt tasks are JavaScript modules that utilize the Node and Grunt API to add automation in just about anything; tasks are installed using Node's package manager npm.

Grunt eases the management of a project by performing various tasks and can help manage a project's life cycle from development to production.

Note

For more information on GruntJS, visit http://goo.gl/XA9qnz.

Installing the Grunt CLI

In order to use the grunt command, you will need to install Grunt's command-line interface (CLI) globally on your system. Open a terminal and execute the following command:

$ npm install -g grunt-cli

The preceding command will download and install grunt-cli on your system and wire up the grunt command in your system path. This does not install the Grunt task runner; the CLI is only responsible for running the version of Grunt that is specified in a project's package.json file relative to the Gruntfile.js file. This enables different projects to use different versions of Grunt without getting affected.

Note

The –g flag generally requires an administrator user.

Installing Grunt

The easiest way to add Grunt to your project is with the npm install command, which will download and install the plugin into your project's node_modules folder and add the entry to your project's package.json file. Open a terminal and execute the following command:

$ npm install grunt --save-dev

The preceding command will install Grunt and save the devDependencies property to the package.json files.

Note

This assumes that the project has a package.json file in the root.

Grunt usage

To use Grunt, the command format is as follows:

$ grunt [task [...]] [options]

The preceding command will execute the task(s) specified by passing in the specified options to invoke the tasks.

Grunt options

The grunt command comes with many options to choose from when invoking tasks; the options available are as follows:

Option

Description

-h, --help

This displays the help text

--no-color

This disables all colored output

--gruntfile

This specifies an alternative Gruntfile

-d, --debug

This enables debugging mode on tasks that support debug mode

-f, --force

This forces tasks to complete through warnings and errors

--tasks

This displays an additional directory to scan for tasks

--npm

This scans node_modules for Grunt plugin tasks

-v, --verbose

This displays the detailed output log

-V, --version

This displays the current Grunt version

--completion

This outputs autocompletion shell rules

Installing the generator-gruntfile

The Yeoman Gruntfile generator will streamline setting up Grunt in an existing or new project. To install, open a terminal and execute the following command:

$ npm install -g generator-gruntfile

To use this generator, execute the following command:

$ yo gruntfile

The output from this command will look similar to the following screenshot:

The questions the generator asks will help it decide on whether the DOM is involved; it will set up QUnit and if not, it will add nodeunit. If files in the project need to be minified, then the generator will set up tasks to do that.

After the execution of command is finished, you should have a new Gruntfile.js file and package.json file located in the current working directory.

Using Grunt

To set up Grunt, either with a new project or an existing project, there are only two very important files that are needed to add the Grunt JavaScript task runner.

The package.json file

The package.json file is the file that contains the project's dependencies and development dependencies that are generally Grunt tasks. Take a look at the following package.json file:

{
"name": "learning-yeoman-ch8",
"version": "0.0.0",
"dependencies": {},
"devDependencies": {
"grunt": "~0.4.2",
"grunt-contrib-watch": "~0.5.3",
"grunt-contrib-concat": "~0.3.0",
"grunt-contrib-uglify": "~0.2.7",
"grunt-contrib-jshint": "~0.7.2",
"grunt-contrib-qunit": "*"
}
}

This file simply defines the project configuration settings and then specifies the project's dependencies; the Grunt tasks are located in the devDependencies property, which specifies what Grunt task plugins are required for this project.

The Gruntfile.js file

The Gruntfile.js file is the file that contains the configuration settings for tasks in your project. Take a look at the following example:

grunt.initConfig({
concat: {
options: {},
foo: {},
bar: {
options: {}
}
}
});

The following actions are performed by the preceding code:

- The grunt.initConfig method is invoked by passing in task options

- The concat task is configured with global options overriding the task defaults

- The foo property is a target of the task that sets target-level options

- The bar property is a target of the task that will use the task-level options

Loading tasks

Grunt handles loading tasks that are specified in the Gruntfile.js file using the loadNpmTasks method, as follows:

grunt.loadNpmTasks('grunt-contrib-concat');

The preceding code is placed inside the Gruntfile.js file right below the module.exports = function(grunt) method and will load the Grunt task by the passed name. The tasks are loaded by performing a lookup inside the node_modules directory relative to the current working project's directory.

Creating the alias tasks

Grunt also supports the ability to create alias tasks that can be used to execute multiple tasks by creating an alias task, which invokes all subtasks that are defined in the alias. For example, consider the following code:

grunt.registerTask('customTask', ['qunit', 'concat', 'uglify']);

The following actions are performed by the preceding code:

- It uses the registerTask method to register a new task named customTask that will execute the tasks in the order specified in the second argument

- The usage of invoking this newly registered task goes in the form of the following command:

$ grunt customTask

The preceding command will invoke the custom registered task, thus running all tasks defined in the task list array.

Note

For more information, visit http://goo.gl/PvNbZf.

Multiple target tasks

Grunt has the ability to run multiple tasks with multiple configurations; each task that is declared in the Gruntfile.js file can have any number of target tasks, which are just aliases for running the task with different configuration options, as follows.

A task such as concat:dist or uglify:dist will do the following:

- It will only process the files specified in the tasks' dist target options

- Then each target specified inside that task will be executed, which allows you to have more control over which tasks and targets will be run with different files

Note

For more information on multiple task targets, visit http://goo.gl/urjOkA.

Registering the basic tasks

Grunt will execute the basic tasks without looking at the configuration or environment and will just run the task function by passing in any specified colon-separated arguments to the task function as arguments. For example, consider the following code snippet:

grunt.registerTask(taskName, [description, ] taskFunction)

The preceding code registers a new task named taskName with an optional description and function to invoke when that task is run.

Note

For more information on creating basic tasks, visit http://goo.gl/yUvkNT.

Options – files

Most tasks perform file operations, so Grunt has included a powerful abstraction layer for declaring which files the task should operate on. There are many ways to define the file mappings that are used when running the tasks.

For example, take a look at the following task target options:

grunt.initConfig({
myTask: {
staticFiles: {
files: [{
src: 'src/myfile.js',
dest: 'build/myfile.min.js'
}]
}
}
});

The following actions are performed by the preceding code:

- The grunt.initConfig method is invoked by passing an object of installed tasks configuration settings

- The myTask property specifies the options for the task, with a target of staticFiles

- The files property for the target task is set to an array of file objects that have properties specifying the source (src) and destination (dest) for the input/output of the files

Note

For more information on configuring tasks, visit http://goo.gl/iu0ra8.

Options – file patterns

The files property of Grunt supports the node-glob file pattern, which allows you to use the following options:

- *: This option matches any number of characters but not the / directory slash

- ?: This option matches a single character but not the / directory slash

- **: This option matches any number of characters, including / if it's the only character

- {}: This option matches a comma-separating list of expressions

- !: This option matches the beginning of a pattern with a negative match

Note

For more information on file patterns, visit http://goo.gl/ssnWV7.

Options – dynamic patterns

To build the file options of your tasks dynamically, you can use template strings in the file property values, which can be used in both compact and the files array formats. When you set the expand property to true, the following options become available:

- cwd: This sets the current working directory where files are matched from

- src: This specifies the patterns to match relative to the cwd property

- dest: This specifies the destination file path

- ext: This replaces the file extension specified in the dest property

- extDot: This is used to specify the period location of the file extension, either first or last

- flatten: This will remove all file paths from the generated dest path

- rename: This is a function that will be invoked for each file, which matches the src property

- dest: This is a function that will be invoked with each src and dest of every file matched in the src property; this function must return the new destination

For example, take a look at the following Grunt task:

grunt.initConfig({
myTask: {
dynamicFiles: {
files: [
{
expand: true,
cwd: 'lib/',
src: ['**/*.js'],
dest: 'build/',
ext: '.min.js',
extDot: 'first'
}
]
}
}
});

The following actions are performed by the preceding code:

- The grunt.initConfig method is invoked with an object of task settings

- The myTask property specifies the options for the task

- The dynamicFiles property is the target and files of the task, which use the expand property to enable extra options for dynamic file lookups

- The cwd property sets the lib folder as the director to perform the file lookups

- The src property sets the source of the files to any file nested inside the directory with the .js file extension

- The dest property sets the location of the processed files to be placed by the task

- The ext property sets the new extension for each file that is processed by the task

- The extDot property specifies the extension that begins after the first dot in the filename

Note

For more information on dynamic file objects, visit http://goo.gl/7b0E6o

Options – templates

By default, Grunt supports Lo-Dash template expressions that are specified using the <%= %> syntax. Grunt will automatically expand each task recursively until no more tasks or targets remain. You can dynamically create filenames and contents using templates and also provide support for methods in templates, as follows:

<%= grunt.template.today('yyyy-mm-dd') %>.

The preceding code will invoke a date and time helper method on the grunt.templates object by passing in a date pattern that will be executed, and return the current date in that given format.

Note

For more information on templates, visit
http://goo.gl/2GPAjc.

Options – importing data

Grunt also supports importing external data that can be in either a JSON or YAML format. The following methods can be used to import existing external data for your grunt tasks:

- grunt.file.readJSON: This method will load the file specified as JSON

- grunt.file.readYAML: This method will load the file specified as YAML

Note

For more information on importing formats, visit http://goo.gl/DWn1W4.

The new project

The easiest way to add Grunt to a new project is to use a Yeoman app generator. The most common generator to use with a generic project would be the generator-webapp, which will create all the necessary files and configure some general tasks to build and test your code.

To use a Yeoman generator, execute the following command:

$ yo [generator_name]

The preceding command will invoke the generator in the current directory by creating all files needed for development along with fully configured Grunt tasks ready for use.

My custom Grunt plugin

Now that we have an overview on using Grunt and the power that it can bring to a new or existing project, let's use the Yeoman Grunt plugin generator to create a custom Grunt plugin that will allow the use of custom created tasks in any project.

Installing the generator-gruntplugin

To install the Yeoman Grunt plugin generator, execute the following command:

$ npm install -g generator-gruntplugin

The preceding command will install the Grunt plugin generator globally on your system, allowing the use of the yo gruntplugin command from within any directory.

Usage

To use the Grunt plugin generator, execute the following command:

$ yo gruntplugin

The directory structure

The directory structure that is created contains very few files and modules by default, but your plugin can include any Grunt task installed into the project. Take a look at the directory structure created by Yeoman:

├── Gruntfile.js
├── README.md
├── node_modules
│ ├── grunt
│ ├── grunt-contrib-clean
│ ├── grunt-contrib-jshint
│ ├── grunt-contrib-nodeunit
│ ├── jshint-stylish
│ └── load-grunt-tasks
├── package.json
├── tasks
│ └── learning_yeoman_ch8.js
└── test
├── learning_yeoman_ch8_test.js
├── expected
└── fixtures

The generator scaffolds the directory structure as follows:

- Gruntfile.js: This contains Grunt tasks to use on the plugin

- README.md: This contains project-specific information including installation and usage of the plugin

- node_modules: This contains the module dependencies used by the plugin

- package.json: This contains project information including repository location, main script paths, start and stop commands, and npm dependencies

- tasks: This contains the source files for the plugin tasks

- test: This contains the test specs for the plugin that will be run against the module:

- expected: This directory contains the expected files that the plugin produces

- fixtures: This directory contains sample files that the plugin will load for testing

The Grunt plugin logic

The plugin logic is located in the tasks/learning_yeoman_ch8.js file that gets created by Yeoman during the initial scaffold; open the file and add the following code:

'use strict';
module.exports = function(grunt) {
function LearningYeomanCh8() {
//Plugin logic here
};
grunt.registerMultiTask(
'learning_yeoman_ch8',
'This is an example plugin.',
LearningYeomanCh8
);
};

The following actions are performed by the preceding code:

- The module.exports property is set to a function, which takes grunt as the only argument

- Inside the exports method, the plugin's main function is declared and will encapsulate the logic of the plugin

- The task is then registered by invoking the registerMultiTask method by passing in a description and the constructor function of the plugin

When registering a plugin as a multi-task, this enables the task to iterate over every target specified in the configuration settings. If no target is specified when invoking the custom task, then each target in the task is executed.

Plugin options

Tasks generally have a set of default options, which they use when performing some type of logic. To specify default options for your plugin, add the following code inside the module method:

var options = this.options( {
template: 'Hello <%= name %>',
data: {
name: 'Learning Yeoman'
}
} );

The following actions are performed by the preceding code:

- It declares an options variable that is set to the current task's target options

- If no options exist in that target, then the defaults are specified

- If a task's target has options, then the task will use those options instead

Using Grunt to read files

Tasks generally take input files and process them, so your plugin should have methods for checking whether the file path exists before trying to read the content. Open the tasks/learning_yeoman_ch8.js file and add the following code inside the main function:

var checkFiles = function (filepath) {
if (!grunt.file.exists( filepath )) {
grunt.log.warn( 'Source file "' + filepath + '" not found.' );
return false;
} else {
return true;
}
};
var readSource = function (file) {
return file.src.filter( checkFiles ).map( function (filepath) {
return grunt.file.read( filepath );
} ).join( grunt.util.normalizelf( '' ) );
}

In the preceding code, the files.forEach method is invoked internally on the this.files property.

Since all tasks involve the file system, the files property is the files that are populated from the options in the task configuration; using file globbing patterns the files are added to the local instance of the task.

Using Grunt to write files

Since tasks usually manipulate files in some sort of way, writing the output of those files should be included.

Open the tasks/learning_yeoman_ch8.js file and add the following code inside the LearningYeomanCh8 function:

var readWriteFile = function (file) {
var src = readSource( file );
src += grunt.template.process(
options.template,{data: options.data}
);

grunt.file.write( file.dest, src );
grunt.log.writeln( 'File "' + file.dest + '" created.' );
};
// Iterate over all specified file groups.
this.files.forEach(function(file) {
readWriteFile(file);
});

The following actions are performed by the preceding code:

- The src variable is set to the results from the readSource method, which will return the content of the file passed

- Then the compiled fragment returned from invoking the grunt.template.process method will be added to the source files

- Then the grunt.file.write method is invoked by passing file.dest, which will be the dest set in the task options along with the new contents of the file

- The last method will log a message to the console when the task is complete

- Finally the last three lines bring this plugin together by invoking readWriteFile for each file that is matched from the tasks file settings

Testing a Grunt plugin

The Yeoman generator creates the testing configuration and an example spec that is located in the test/learning_yeoman_ch8_test.js file; open the file and examine the content, which should look like the following:

'use strict';
var grunt = require('grunt');
exports.learning_yeoman_ch8 = {
setUp: function (done) {
// setup here if necessary
done();
},

//default options test - should write 'Hello Learning Yeoman' by default.
default_options: function(test) {
test.expect(1);
var actual = grunt.file.read('tmp/default_options');
var expected = grunt.file.read('test/expected/default_options');
test.equal(actual, expected, 'should perform task with default options.');
test.done();
},

//custom options test - should write 'Hello [name]' where [name] is the value. custom_options: function(test) {
test.expect(1);
var actual = grunt.file.read('tmp/custom_options');
var expected = grunt.file.read('test/expected/custom_options');
test.equal(actual, expected, 'should perform task with custom options.');
test.done();
}
};

The preceding code:

- At the top, the grunt variable is assigned to the grunt module.

- The exports object defines the test suite; in this case, it's learning_yeoman_ch8.

- This object has three properties; the first is setUp, a function that is ran during the initialize phase and any initializing logic should go here.

- The second property is default_options; this tests the plugin and creates the correct file content that matches the plugins default_options.

- The third property is custom_options; this tests the plugin and creates the correct file content that matches the plugins custom_options.

Creating test fixtures

Since tasks in Grunt generally modify the filesystem or file(s) in some way or another, creating fixtures allows you to test whether your plugin is performing the correct logic.

Open the test/fixtures/default_options file and modify it to the following:

Hello Learning Yeoman

The preceding code is the value that is set up as the default value in the tasks/learning_yeoman_ch8.js file.

Open the test/fixtures/custom_options file and modify it to the following:

Hello Jonnie

The preceding code is the value that is set in the Gruntfile.js file, under the learning_yeoman_ch8.custom_options task. This is the value we are expecting the plugin to add to the created files.

Running the tests

Now that we have a test spec and fixtures, we are ready to test the plugins functionality. To run the nodeunit tests, open the terminal and execute the following:

$ grunt test -v

The preceding command will execute the test task specified in the Gruntfile.js file and display something similar to the following screenshot:

Deploying to npm

The custom plugin is ready to publish on npm for community usage. To publish your package, execute the following command:

$ npm publish

This command will register the package with npm and is now ready to install anywhere.

Usage

To use this task, all that is required is to install the module using npm and then add the configuration options to your project's Gruntfile.js file. For example, here are the steps:

1. Install the plugin and save it to your package.json file; execute the following command:

$ npm install learning-yeoman-ch8 --save-dev

2. Add the task options to your Gruntfile, as follows:

learning_yeoman_ch8 : {
default_options : {
files : {
'tmp/default_options' : ['test/fixtures/default', 'test/fixtures/custom']
}
},
custom_options : {
options : {
data :{
name: 'Jonnie'
}
},
files : {
'tmp/custom_options' : ['test/fixtures/default', 'test/fixtures/custom']
}
}
}

3. Finally, execute the task by executing the following command:

$ grunt learning_yeoman_ch8

Self-test questions

The following are questions that the reader should be able to answer at the end of this chapter:

1. Which command is used to list all the available Grunt tasks?

2. Which are the three most common Grunt tasks?

3. What type of environment does Grunt run in?

4. How to execute a target Grunt task?

5. How do you register a new Grunt task?

6. How to install a new Grunt task?

7. What are the alternatives to Grunt?

Summary

In this chapter, we covered the basics of Grunt and how to add it to a new or existing project. We also utilized the Yeoman Grunt plugin generator to create a plugin that is published to npm. Now that we have covered creating and configuring tasks for modern web development, the sky is the limit.

In the next chapter, we will learn a few important Yeoman tips and tricks.

Chapter 9. Yeoman Tips and Tricks

In this chapter, we will cover some useful tips and tricks that one might need when using Yeoman and some of the generators. We will improve the existing projects that we created earlier by adding code coverage to our projects, as well as creating a RESTful Node server that will communicate to a MongoDB database using the module we created in the previous chapter.

While this is not an extensive list of tips and tricks, it does consist of some very detailed steps that will help with adding functionality to any project—not just the ones covered in this chapter.

Some common issues that developers run into while using the different Yeoman generators are as follows:

- Accessing server-side resources because of cross-domain scripting issues

- Configuring, running, and creating e2e (end-to-end) UI tests

- Configuring and generating code coverage reports from unit tests

In the sections to come, we will tackle these problems and come up with usable solutions that will make developing web applications, using any of the generators, a lot smoother.

Let's get started!

Webapp generator solutions

When creating a generic web application using the Yeoman generator-webapp, it would be nice to have a remote server somewhere serving all the API endpoints for the app to consume. However, since JavaScript cannot simply make HTTP calls to remote domains without some type of cross-site scripting setup, having a proxy server that can integrate with the Connect server while developing will make life a lot easier. It allows you to start writing code without having to wait for the server team to set up custom proxy scripts in order to access the endpoints.

Creating a RESTful Node.js server

Problem: Your project requires RESTful API routes that map resources to a MongoDB database to perform basic CRUD (create, read, update, and delete) operations and return the results in a JSON format.

Solution: Create a Node.js server by installing Express and the jps-ds module from Chapter 7, Custom Libraries; since that module contains methods for performing CRUD operations on a MongoDB data source, we can easily leverage the module to speed up development. As for the server routes, we can easily use Express, which is a Node.js module, as well. Then, it's a matter of connecting the dots and starting the server. Let's get started!

Installing module dependencies

First, install the dependences using npm. Open the terminal and execute the following command:

$ npm install jps-ds express body-parser --save

The preceding command will download and install both jps-ds and express, and then save the modules to the project's package.json file.

Creating the server

Now, create a server.js file that will contain the routes and logic. The server will handle taking request parameters and invoke the corresponding methods on the data source.

Configuring the server

Now, we need to import the modules that allow us to create an express server that will handle the routes for the application. Open the server.js file and add the following code:

var application_root = __dirname,
express = require('express'),
path = require('path'),
bodyParser = require('body-parser'),
app = express(),
DS = require('jps-ds').DS,
port = process.env.PORT || 5000;

//Setup static directory
app.use(express.static(application_root + '/dist'));

// parse application/json
app.use(bodyParser.json());

The following actions are performed by the preceding code:

- At the top of the file, private variables are declared and set to some required modules, such as express, path, bodyParser, and jps-ds

- The app variable creates a new instance of an express app

- The DS variable stores a reference to the DS module created in the earlier chapter

- The bodyParser.json() method tells express to parse the request body as content-type application/json and populate the request.body property with the value

- The express.static method tells express in what directory to serve static content

Configuring the data source

Now, we need to configure the data source settings so that it can connect to the MongoDB database and make queries. Open the server.js file and add the following code below the server configuration:

var _ds = new DS({
host: 'localhost/learning-yeoman',
models: {
'posts': {
title: String,
slug: String,
body: String,
image: String,
published: Boolean,
tags: Array,
created: Date,
modified: Date
}
}
});

The following actions are performed by the preceding code:

- It defines a _ds variable that will hold a reference to the DS module instance; passing an object to the module will configure the module

- The host property is set to a local installation of MongoDB that should be running in the background

- The models property is an object where the key is the name of the table or collection and the value is the fields for that collection

- That's the configuration needed for accessing the MongoDB resource since this module provides a layer of abstraction

Defining server routes

Now, it's time to define the routes for the application and add the logic that will invoke the DS module to gather data to be returned by the express app when the route is matched and processed.

The default route

The default route will simply return a message to the user; open the server.js file and add the following code:

app.get('/api', function (request, response) {
response.send({message: 'API is running'});
});

The following actions are performed by the preceding code:

- The /api route is registered, so that when the URL is hit, it will return an object that contains a message stating the API is running

- The response.send method will handle sending the object to the browser as JSON

GET – fetch the posts route

For the GET route that will handle fetching all posts from the MongoDB database, open the server.js file and add the following code:

app.get('/api/posts', function (request, response) {
_ds.findAll('posts').then(function(data){
return response.send(data);
});
});

The following actions are performed by the preceding code:

- The /api/posts route is registered, so that when the URL is requested with a GET method, it will call the findAll method on the DS module passing in the name of the collection

- Since the method is a promise, the then method handles the response by sending the data to the browser

POST – create the post route

Now, for the POST route that will handle creating a post in the MongoDB database, open the server.js file and add the following code:

app.post('/api/posts', function (request, response) {
_ds.create( 'posts', {
title: request.body.title,
body: request.body.body,
published: request.body.published,
created: new Date()
} ).then( function (model) {
return response.send(model);
} );
});

The following actions are performed by the preceding code:

- The /api/posts route is registered, so that when the URL is requested with a post method, it will call the create method on the DS module passing in the object to send to the backend

- The object that is sent to the create method is extracted from request.body; since express parsed the body and made it an object, we can easily access properties by using dot syntax

- After the model is saved to the database, the browser is given with the results from the create method

- The response.send method will handle sending the object to the browser as JSON

GET – a single post route

Now, for the GET route that will find one post in the MongoDB database by the post ID, open the server.js file and add the following code:

//Get a single by id
app.get('/api/posts/:id', function (request, response) {
_ds.findOne('posts', request.params.id).then(function(data){
return response.send(data);
});
});

The following actions are performed by the preceding code:

- The /api/posts/:id route is registered, so that when a GET request is sent to the URL with an ID parameter, it will call the findOne method on the DS module

- The findOne method will find the record where the ID matches and returns the object

- The response.send method will handle sending the object to the browser as JSON

PUT – update the post route

Now, for the PUT route that will handle updating a post in the MongoDB database by the post ID, open the server.js file and add the following code:

app.put('/api/posts/:id', function (request, response) {
_ds.update( 'posts', request.params.id, {
title: request.body.title,
body: request.body.body,
image: request.body.image,
tags: request.body.tags,
published: request.body.published
} ).then( function (model) {
return response.send(model);
console.log( 'model updated', model );
} );
});

The following actions are performed by the preceding code:

- The /api/posts/:id route is registered, so that when the URL is requested with a PUT method and has an ID parameter in the URL, it will call the update method on the DS module

- The update method takes three arguments: the table name, the ID of the object to update, and the object to send to the database to update

- The method returns a promise that will be resolved when complete

- The response.send method will send the object to the browser as JSON

DELETE – remove the post route

Now, for the DELETE route that will remove a post from the MongoDB database by the post ID, open the server.js file and add the following code:

//Delete
app.delete('/api/posts/:id', function (request, response) {
_ds.destroy( 'posts', request.params.id).then( function (data) {
return response.send(data);
} );
});

Starting the server

Now, it's time to start the server so that we can test these routes and make sure that they are correctly handling requests. Open the server.js file and add the following code snippet at the bottom of the file:

var port = 9090;
app.listen(port, function () {
console.log('listening on port %d in %s mode', port,
app.settings.envenv);
});

The preceding code simply creates a port variable set to 9090 and invokes the listen method on the express app, which will start the server.

Running the server

Now, we are all ready to run this node server so that requests to the endpoint will react accordingly. Open the terminal and execute the following command:

$ node server

The preceding command will run the server.js file and display the log output in the console.

Testing the server

Now, we are ready to test if the server handles the requests properly. Let's send the server some requests and check if we get what we are expecting. You could write a unit test for this, but to demonstrate quickly, let's just use curl to send the requests.

Open the terminal and execute the following commands to test the server:

1. Test the default endpoint:

$ curl -i -H "Accept: application/json" http://localhost:9090/api

2. Test to get all posts:

$ curl -i -H "Accept: application/json" http://localhost:9090/api/posts

3. Test to create a post:

$ curl -i -H "Accept: application/json" http://localhost:9090/api/posts

4. Test to update a post:

$ curl -i -H "Accept: application/json" -X PUT -d "title=cURL Updated&body=Updated post content&image=http://placehold.it/200" http://localhost:9090/api/posts/5341d503527b01000072a2cc

5. Test to remove a post:

$ curl -i -H "Accept: application/json" -X DELETE http://localhost:9090/api/posts/5341b957527b01000072a2cb

Setting up the proxy server

Now, we are ready to install and configure the json-proxy module that will handle allowing you to route all API calls to a local or remote server based on the matching prefix in the requested URL. It will make more sense when we implement this proxy; open the terminal and execute the following command:

$ npm install json-proxy --save-dev

The preceding command will download and install the json-proxy node module that will enable you to use a proxy server during development.

Configuring the proxy server

Now, we need to configure the proxy server so that all HTTP calls that match a certain criteria will be forwarded to a URL of our choice. Open the Gruntfile.js file and add the following to the connect:livereload task options; the middleware function that is declared is where you want to insert the following code:

//Around line #55
connect:{
livereload: {
options: {
middleware: function(connect) {
return [

//Import and configure the json-proxy module.
require( 'json-proxy' ).initialize( proxyConfig ),
...

];
}
}
}
}

The preceding code will handle importing and initializing the json-proxy module with the specified configuration; so when the connect:livereload server task is ran, this proxy server will be up and running listing on the same port. Add the following to the top of the Gruntfile.js file that declares the settings for the proxy module:

var serverEndpoint = 'http://jonniespratley.me:8181/api/v2/learning-yeoman';
var proxyConfig = {
proxy: {
forward: {
'/api': serverEndpoint
}
}
};

The preceding code simply declares a serverEndpoint variable that holds the location of the API server we will route HTTP calls to. The proxyConfig object has a proxy property.forward property that specifies the context in which to forward requests to when matched, the value is serverEndpoint, which is the API endpoint we wish to forward all requests made with /api in the URL.

To use this proxy server, simply start the Grunt connect live reload server by running the following command:

$ grunt serve

Conclusion

It took only nine steps and we were able to get a RESTful Node server up and running, talking to a MongoDB database that can perform CRUD operations, which are accessed through a proxy server.

Angular generator solutions

The most common issue with Angular apps is setting up e2e testing. Well, let's cover setting up Protractor with an Angular app, so we can run e2e tests to verify that the application is functioning as it should.

Protractor e2e testing

Problem: I have an AngularJS app that was created using Yeoman and I want to add Protractor e2e testing to my project. I want to run a Grunt task that will start Protractor and execute my project's e2e test specs.

Solution: In order to properly test your AngularJS application using Protractor, you will need to perform the following steps in this order:

1. Install protractor using npm.

2. Install grunt-protractor-runner using npm.

3. Configure the Grunt task.

4. Create a Protractor configuration file.

5. Create an e2e test spec.

6. Start Selenium WebDriver.

7. Start the application.

8. Run the Grunt task.

Installing Protractor

To install Protractor, you must use npm and install it globally, as follows:

$ npm install protractor -g

Now, update the Selenium WebDriver using the web driver manager. Open the terminal and execute the following command:

$ webdriver-manager update

The preceding command will invoke webdriver-manager that comes bundled with Protractor to update the required files needed to run the Selenium WebDriver.

Note

It will download chromedriver and Selenium Server's .jar files to the installation directory of Protractor, which, on a Linux or Mac system, lives in /usr/local/lib/node_modules/protractor. You might need to adjust the permissions of the protractor directory.

Installing the grunt-protractor-runner

Now, you will need to install the Grunt task to run the Protractor tests; open the terminal and execute the following command:

$ npm install grunt-protractor-runner --save-dev

The preceding command will download and install the grunt-protractor-runner node module to the node_modules directory and add the entry to your project's package.json file.

Configuring the Protractor task

Now, let's configure the Grunt task. Open the Gruntfile.js file and add the following at the bottom of the task settings object:

protractor : {
options : {
keepAlive : true,
noColor : false,
args : {}
},
test : {
options : {
configFile : "e2e.conf.js"
}
}
}

The following actions are performed by the preceding code:

- It configures the grunt-protractor-runner task by setting up some options that will be passed to the protractor instance

- The test target defined inside the protractor task will be the configuration file that is loaded by Protractor when running this task

Creating the Protractor configuration

Now, you need to create the configuration file for Protractor. Open the terminal and execute the following command:

$ touch e2e.config.js

Open the e2e.config.js file and add the following content:

exports.config = {
seleniumAddress : 'http://localhost:4444/wd/hub',
capabilities : {
'browserName' : 'chrome'
},
baseUrl : 'http://localhost:9090',
specs : ['test/e2e/*.js', '.tmp/e2e/*-e2e.js'],
jasmineNodeOpts : {
isVerbose : true,
showColors : true,
includeStackTrace : true,
defaultTimeoutInterval : 30000
}
};

The following actions are performed by the preceding code:

- The config property is set on the global exports property

- The seleniumAddress property is set to the location of the Selenium Server instance that is running

- The capabilities property is an object that sets the capabilities of the browser

- The baseUrl property is set to the location of the web server that is hosting the application

- The specs property is an array that contains the location of the specs; you can use file globbing patterns for including specs

- The jasmineNodeOpts property is an object that contains settings that will be passed to the Jasmine node module when Protractor is running

- The isVerbose property is set to true so that all logging will display in the console; the showColors property will allow color logs to be enabled

- The includeStackTrace property is set to true so that the console will log stack trace errors

- The defaultTimeoutInterval is used for specifying the default number of milliseconds before the spec fails

Creating an e2e spec

Now, we need to create an e2e test spec that will handle loading the application and checking if the pages contain what we are expecting. Open the test/e2e/app.coffee file and add the following code:

var Config = {
baseurl: "http://localhost:9000/#",
sitetitle: "learning yeoman",
sitedesc: " a starting point for a modern application.",
sitecopy: "2014 Copyright",
version: '0.0.1',
email: "admin@email.com",
debug: true,
feature: {
title: 'Chapter 3',
image: /img/learning-yeoman/yo-ng.png',
body: 'A starting point for a modern angular.js application.'
}
};

The following actions are performed by the preceding code:

- At the top, a Config object is created that holds values, which should be in the application

- This is the same Config object that was used in all previous chapters; so I assume you are familiar with it by now

The MainPage object

Since it is a best practice to create page objects that contain properties and methods for the corresponding page, let's create a MainPage object that will hold properties and a method for loading the page.

Open the test/e2e/app.coffee file and add the following code:

MainPage = ->
@sitetitle = element(protractor.By.binding("App.sitetitle"))
@featureTitle = element(protractor.By.binding("App.feature.title"))
@featureDesc = element(protractor.By.binding("App.feature.body"))
@features = element(protractor.By.binding("App.features"))
@get = ->
return browser.get("http://localhost:9000/#")
@name = 'MainPage'

The following actions are performed by the preceding code:

- The MainPage is a function that contains some properties and methods

- The sitetitle is set to the element selector by binding, which looks for {{App.sitetitle}} in the view

- The featureTitle, featureDesc, and features properties are also set to the element selector by binding

- The get method will handle loading the correct URL in the browser

- The name property is set to the name of the page object

The PostPage object

Now, let's create a PostPage object that will hold properties to reference the form inputs in the page and some methods for loading the different views populating the form.

Open the test/e2e/app.js file and add the following:

PostPage = ->
@title = element(protractor.By.model('post.title'))
@body = element(protractor.By.model('post.body'))
@image = element(protractor.By.model('post.image'))
@tags = element(protractor.By.model('post.tags'))
@published = element(protractor.By.model('post.published'))
@submitBtn = element(protractor.By.css('button[type="submit"]'))
@get = ->
return browser.get(Config.baseurl + '/posts')
@getNew = ->
return browser.get(Config.baseurl + '/posts/new')
@getEdit = (id)->
return browser.get(Config.baseurl + '/posts/edit/' + id)
@edit = (id) ->
@editBtn = element(protractor.By.css("[data-id=\"#{id}\"] .edit"))
@editBtn.click()
@form = (p)->
@title.sendKeys(p.title)
@body.sendKeys(p.body)
@image.sendKeys(p.image)
@tags.sendKeys(p.tags)
@submitBtn.click()
browser.sleep(1500)

@name = 'PostEditPage'

The following actions are performed by the preceding code:

- It creates a new PostPage object that will contain properties and methods for the post page

- The title property is set to the element selector by model, which will look for ng-model=post.title in the view

- The getNew method will simply load the new post page into the browser

- The getEdit method will simply load the edit post page into the browser

- The edit method takes one argument, that is, the ID of the model to edit

- The form method takes one argument that is an object; when invoked, the method will call the sendKeys method on the elements that were selected by the model value

- Then, the submitBtn.click() method is invoked, which will submit the form followed by the browser.sleep method to inform Protractor to wait about 1.5 seconds before proceeding

The e2e spec

Now, the test spec will use the page objects along with the assertion methods to handle testing the values retrieved from the page objects.

Open the test/e2e/app.coffee file and add the following code:

describe 'Chapter3 e2e:', ->
mainPage = new MainPage()

describe "the main page", ->
beforeEach ->
mainPage.get()

it "should have site title and description", ->
expect(mainPage.sitetitle.getText())
.toEqual(Config.sitetitle)
expect(mainPage.featureTitle.getText())
.toEqual(Config.feature.title)
expect(mainPage.featureDesc.getText())
.toEqual(Config.feature.body)

describe 'the new post page', ->
postPage = new PostPage()
beforeEach ->
postPage.getNew()

it 'should create a post', ->
expect(browser.getCurrentUrl())
.toEqual(Config.baseurl + '/posts/new')
postPage.form({
title: Test -' + new Date().toString(),
body: 'Test post body', tags: 'protractor,angular,test', image: 'http://placehold.it/200'})
expect(browser.getCurrentUrl()).toEqual(Config.baseurl + '/posts')

The following actions are performed by the preceding code:

- The describe block wraps the entire content in a test suite

- A mainPage variable is defined and set to a new instance of the MainPage object

- Then, the describe block encloses the content in a main page test suite

- The beforeEach method will invoke the get method on the mainPage object, which will load the web page

- The it spec will handle testing if the page has a site title, feature title, and description by calling the getText() method

- The third describe block encloses the post page spec, and the beforeEach method will load the posts/new web page

- The it spec will check if the post form can create a new post

- The expectation is that the current URL is /posts/new, and then calling the form method on the post page object and passing an object with properties to send to the form

- After the post is saved, the expectation is that the /posts route is loaded to display all posts in the database

Starting the Selenium WebDriver

Now, in order to run Protractor, you need to have the Selenium Server running in the background; this is done by starting it with the web driver manager that Protractor installed. To start the Selenium Server, open the terminal and execute the following command:

$ webdriver-manager start

The preceding command will start the Selenium Server and output the logging information such as the host it is running on, which is http://127.0.0.1:4444/wd/hub.

Starting the application

Now that the web driver is running, you can start your application; open another terminal and execute the following command:

$ grunt serve

Note

The preceding command will start the Connect server launching Chrome, displaying your application that was created earlier. The tutorial is present in Chapter 3, My Angular Project.

Running e2e tests

To run the e2e tests, you will need to modify your Grunfile.js file so that the CoffeeScript is compiled before the tests run; so open the Gruntfile.js file and add the following code snippet to the bottom of the file:

grunt.registerTask('test:e2e', ['test', 'protractor']);

The preceding code will register a new task that will first run the test task to clean and compile the source files, and then launch the e2e tests using Protractor.

Now, to run your e2e tests, you can just run the following command:

$ grunt test:e2e

This command will run all tests followed by launching Chrome and Protractor to test the application end-to-end.

Backbone generator solutions

This section covers some solutions that one might need when using the Backbone generator.

Code coverage with Karma

Problem: You want to run your project's unit tests with Karma and generate code coverage for your project.

Solution: Since you want to use Karma and leverage the karma-coverage plugin, you will need to create and configure a test/test-main.js file that will parse all the indexed files and create an array of spec files that are then added to the Require.js configuration as dependencies. After all the files have been indexed, you will need to start Karma so the specs can be run. To do so, perform the following steps:

Installing Karma and plugins

Since the Yeoman Backbone generator creates a Karma configuration file located in the root of the project directory by default, all that is needed is to install the karma-coverage plugin and enable it in your karma.conf.js file.

To install karma-coverage, open the terminal and execute the following command:

$ npm install karma-coverage --save

The preceding command will download and install Karma and the karma-coverage plugin into your node_modules folder. Then, it will add the entry to your package.json devDependencies property.

Karma configuration

Now, we need to adjust the Karma configuration file to include the correct files; since the project created in Chapter 4, My Backbone Project, uses CoffeeScript, we need to adjust the source location to use the compiled scripts.

Open karma.conf.js and add the following code:

module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', 'requirejs'],
files: [
{ pattern: 'app/bower_components/**/*.js', included: false },
{ pattern: '.tmp/scripts/**/*.js', included: false },
{ pattern: '.tmp/spec/**/*.js', included: false },
'test/test-main.js'
],
exclude: [],
reporters: ['progress', 'coverage'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
preprocessors: {
'.tmp/scripts/**/*.js': ['coverage']
},
browsers: ['Chrome'],
captureTimeout: 60000,
autoWatch: false,
client: {
captureConsole: true,
useIframe: false
},
singleRun: true
});
};

The following actions are performed by the preceding code:

- At the top of the file, the module.exports property is set to a function that takes the config argument from Karma

- The config.set method is invoked by passing an object of settings to send to Karma

- The basePath property is set to an empty string

- The frameworks property is set to an array of libraries to include for testing

- The files property is set to an array of file objects that specify the files to index; here, we specify the bower_components directory, the compiled scripts and compiled test specs, and the test configuration main.js file

- The reporters property is set to an array of Karma reporters to use

- The port, colors, and logLevel properties are set to their default values

- The preprocessors property is set to an object of files and type of preprocessor; here, we specify the source of the compiled scripts to be run through the coverage preprocessor

- The browsers property is set to Google Chrome

- Then, we configure the client method to capture any console logs to the runner and set singleRun to true; so it will run and exit when finished

Configuring test-main.js

Now, we need to create a configuration file that will replicate the app/scripts/main.coffee file, and instead loop all files and then check if a test spec exists, pushing that file into an array that will be handed to Require.js when all is finished, which will in turn execute the specs. Create a new file by executing the following command:

$ touch test/test-main.js

Open the test/test-main.js file and add the following content:

var tests = Object.keys(window.__karma__.files).filter(function (file) {
var isTest = /.tmp\/spec.*.js/.test(file);
if (isTest) {
return file;
}
});
requirejs.config({
baseUrl: 'base/.tmp/scripts',
shim: {
"underscore": {
"exports": "_"
},
"backbone": {
"deps": [
"underscore",
"jquery"
],
"exports": "Backbone"
},
"handlebars": {
"exports": "Handlebars"
}
},
paths: {
"jquery": "../../app/bower_components/jquery/jquery",
"backbone": "../../app/bower_components/backbone/backbone",
"underscore": "../../app/bower_components/underscore/underscore",
"handlebars": "../../app/bower_components/handlebars/handlebars",
"app": "app"
},

// ask Require.js to load these files (all our tests)
deps: tests,

// start test run, once Require.js is done
callback: window.__karma__.start
});

The following actions are performed by the preceding code:

- At the top of the file, we declare a tests variable that will contain all the specs to run. It is set to the Object.keys method that will handle testing if the current file is indeed a test spec, and if it is, it will return that file, which pushes it into the files' array.

- Then, requirejs is configured with a new baseUrl property that will be the location of the source files. Since Karma places everything under the base, we need to adjust the URL accordingly.

- The shim and paths properties are set as normal to the same value as in the regular main script file.

- The reps property is set to the files array that will contain all the test specs to run.

- The callback property is set to the start method on the karma namespace object, which will invoke the runner and execute the tests.

Running tests

Now, we are ready to run the tests with Grunt, which will start the Karma runner and execute all the specs that are in the test/spec directory. Open the terminal and execute the following command:

$ grunt test

The preceding command will do the following tasks:

- It will run the test task that will clean the .tmp directory

- It will compile the CoffeeScript's source and spec files

- Then, it will launch Karma, which in turn will execute the specs

Code coverage report

Now, we are ready to view the code coverage that Karma generated after running the tests; Karma uses Istanbul for instrumenting and displaying the coverage reports. The reports are created in the root of the project in a folder named coverage, inside the folder will be another folder with the same name as the browser on which we ran our tests.

Open the index.html file in a browser of your choice to view the code coverage. You should see something similar to the following screenshot if all went well:

Self-test questions

1. How do you start and update the Selenium WebDriver when using Protractor in your project?

2. What npm module enables your project to generate code coverage from unit tests?

Summary

In this chapter, we covered a lot of things. We began with setting up a Node RESTful server that talks to a MongoDB database and returns JSON to the browser. We set up Protractor with an Angular application to cover the e2e tests that are needed as an application gets larger. We also set up Karma to handle testing our specs and generating code coverage on a Backbone application that uses Require.js and CoffeeScript. Finding the correct solution to a problem can sometimes be a difficult task, but with patience and persistence, anything can be done; take this chapter as a welcome to writing better unit tests and creating projects with outstanding code coverage.

Appendix A. Yeoman Resources

This chapter will include often-used commands when using Grunt, Bower, and Yo, as well as the other tools and technologies used in this book; it will serve as a reference for developers when using tools in the Yeoman workflow and also includes installation details to set up the development environment.

Reference guides

As using the Yeoman workflow tools includes several different commands, let's have a look at the reference commands and documentation.

Yo – the scaffolding tool

Yo is a lightening-fast scaffolding tool that runs in the Node.js environment.

Usage

The Yo tool is invoked from the command line as follows:

$ yo [generator] [args] [options]

General options:
-h, --help # Print generator's options and usage
-f, --force # Overwrite files that already exist

Note

For more information on Yo, visit http://goo.gl/ft8YFz.

Bower – the package tool

Bower is a package manager for the web.

Usage

The Bower tool is invoked from the command line as follows:

$ bower [command] [args] [options]

Commands

The Bower commands available are as follows:

Command

Description

cache

This manages the Bower cache

help

This displays help information about Bower

home

This opens a package homepage into your favorite browser

info

This provides information on a particular package

init

This interactively creates a bower.jso
n file

install

This installs a package locally

link

This symlinks a package folder

list

This lists local packages

lookup

This looks up a package URL by name

prune

This removes local extraneous packages

register

This registers a package

search

This searches for a package by name

update

This updates a local package

uninstall

This removes a local package

Options

The Bower options available are as follows:

Option

Description

-f, --force

This makes various commands more forceful

-j, --json

This outputs consumable JSON

-l, --log-level

This lists what level of logs to report

-o, --offline

This does not hit the network

-q, --quiet

This provides only important output information

-s, --silent

This does not output anything, besides errors

-V, --verbose

This makes output more verbose

--allow-root

This allows running commands as root

Note

For more information on Bower, visit http://goo.gl/x0RuZ6.

Grunt – the build tool

Grunt is a JavaScript task runner that runs in the Node.js environment.

Usage

The Grunt tool is invoked from the command line as follows:

$ grunt [task] [options]

Options

The Grunt options available are as follows:

Option

Description

-h, --help

This displays the help text

-d, --debug

This enables debugging mode for tasks that support it

-f, --force

This provides a way to force your way past warnings

-v, --verbose

This enables detailed logging to be displayed during execution

-V, --version

This prints the Grunt version and combines it with --verbose for more information

--base

This specifies an alternate base path

--no-color

This disables colored output

--gruntfile

This specifies an alternate Gruntfile

--stack

This prints a stack trace when exiting with a warning or fatal error

--tasks

This lists additional directory paths to scan for task and extra files

--npm

This lists npm-installed Grunt plugins to scan for task and extra files

--no-write

This disables writing files (dry run)

--completion

This outputs shell autocompletion rules

Note

For more information on Grunt, visit http://goo.gl/XA9qnz.

Git

Git is a version control system to manage a project's code base.

Usage

To use the Git tool, the usage is as follows:

$ git [command] [args] [options]

Commands

The Git commands available are as follows:

Command

Description

add

This adds file contents to the index

bisect

This finds by binary search the change that introduced a bug

branch

This lists, creates, or deletes branches

checkout

This checks out a branch or paths to the working tree

clone

This clones a repository into a new directory

commit

This records changes to the repository

diff

This shows changes between commits, commit and working tree, and so on

fetch

This downloads objects and refs from another repository

grep

This prints lines that match a pattern

init

This creates an empty Git repository or reinitializes an existing one

log

This shows commit logs

merge

This joins two or more development histories together

mv

This moves or renames a file, directory, or symlink

pull

This fetches from and merges with another repository or a local branch

push

This updates remote refs along with associated objects

rebase

This integrates changes from two different branches

reset

This resets the current HEAD to the specified state

rm

This removes files from the working tree and from the index

show

This shows various types of objects

status

This shows the working tree status

tag

This creates, lists, deletes, or verifies a tag object signed with GPG

Jasmine – behavior-driven JavaScript

Jasmine is a behavior-driven testing framework for JavaScript programming language.

Structure of a suite

Jasmine uses the describe and it methods to structure test suites, as follows:

describe("colors", function() {
describe("red", function() {
var red;
beforeEach(function() {
red = new Color("red");
});
afterEach(function() {
red = null;
});
it("has the correct value", function() {
expect(red.hex).toEqual("FF0000");
});
it("makes orange when mixed with yellow", function() {
var yellow = new Color("yellow");
var orange = new Color("orange");
expect(red.mix(yellow)).toEqual(orange);
});
});
});

Matchers

The available Jasmine matchers are as follows:

Method

Description

expect(x).toEqual(n)

This checks whether x is equal to n

expect(x).toBe(n)

This checks whether two objects are the same

expect(x).toBeTruthy()

This checks whether the value of x is truthy (not just true)

expect(x).toBeFalsy()

This checks whether the value of x is falsy (not just false)

expect(x).toContain(n)

This checks whether the value of x contains value n

expect(x).toBeDefined()

This checks whether the value of x is defined

expect(x).toBeUndefined()

This checks whether the value of x is undefined

expect(x).toBeNull()

This checks whether the value of x is null

expect(x).toBeNaN()

This checks whether the value of x is NaN

expect(x).toBeCloseTo(n)

This checks the decimal proximity of x

expect(x).toMatch(n)

This checks whether the value of x matches a given regular expression

expect(x).toThrow(e)

This checks whether the x function throws an error

expect(x).not.toEqual(n)

This checks the x inverse of the following matcher

Spy matchers

The available Jasmine spy matchers are as follows:

Method

Description

expect(x).toHaveBeenCalled()

This passes if x is a spy and has been called

expect(x).toHaveBeenCalledWith(args)

This passes if x is a spy and has been called with args

expect(x).not.toHaveBeenCalled()

This passes if x is a spy and was not called

expect(x).not.toHaveBeenCalledWith(args)

This passes if x is a spy and has not been called with args

spyOn(x, 'method').andCallThrough()

This spies on AND invokes the original spied-on function

spyOn(x, 'method').andReturn(args)

This returns args when the spy method is invoked

spyOn(x, 'method').andThrow(exception)

This throws the passed exception when the method is invoked

spyOn(x, 'method').andCallFake(fn)

This invokes the passed fn function when spy is invoked

Reserved words

The reserved words in Jasmine are as follows:

Word

Description

jasmine

This is the global variable for the Jasmine instance

describe(description, function)

This groups a related set of specs

it(description, function)

This invokes the function for test spec

expect(x)

This is an expectation that takes the expected value

beforeEach(function)

This makes the function invoke before each spec in suite

afterEach(function)

This makes the function invoke after each spec in suite

runs(function)

This starts an asynchronous operation

waits(condition)

This waits for condition

waitsFor(function, message, timeout)

This waits until the function returns true within a specified timeout

spyOn

This stubs the function and tracks calls and arguments

xdescribe

This marks a suite as pending and skips

xit

This marks a spec as pending and skips

Note

For more information on Jasmine, visit http://goo.gl/2tpIUV.

Installation guides

In order to take full advantage of the Yeoman Workflow, you need to set up your development environment. The following tools are required for this book's projects and code examples.

Installing Git

There are many ways to install and run Git. Let's take a look at the installation steps for both Mac and Windows operating systems.

Installing Git on Windows

To install Git on Windows, follow these steps:

1. Visit http://git-scm.com/downloads, and click on the Windows link.

2. Run the .exe file that was downloaded.

3. Follow the installation steps, and Git will be installed as a command-line utility.

Installing Git on Mac

To install Git on Mac, follow these steps:

1. Visit http://git-scm.com/downloads, and click on the Mac OS X link.

2. Run the .dmg file that was downloaded.

3. Follow the installation steps, and Git will be installed as a command-line utility.

Once Git is installed, you can test git by executing the following command:

$ git --version

Note

The version used in this book is 1.8.5.2.

Installing Node.js and npm

As npm comes bundled with Node.js, installing Node.js will install npm.

Installing Node on Windows

The following steps will cover the installation of npm on Windows through the Node installer:

1. Download the .msi installer located at http://nodejs.org/download/.

2. Run the .msi installer to install Node and npm.

3. Follow the installation steps, and npm will be installed as a command-line utility.

Installing Node on Mac

The following steps will cover the installation of npm on Mac through the Node installer:

1. Download the .pkg installer located at http://nodejs.org/download.

2. Run the .pkg installer to install Node and npm.

3. Follow the installation steps, and npm will be installed as a command-line utility.

Once Node is installed, you can test Node and npm by executing the following command:

$ node -v && npm -v

Note

The version used in this book is Node 0.10.28 and npm 1.4.9.

Installing Yo

As Yo runs in the Node.js environment, you should already have Node and npm installed.

Installing Yo on Mac/Windows

Once npm is installed, you can install Yo globally using the following command:

$ npm install -g yo

Once Yo is installed, you can test Yo by executing the following command:

$ yo -v

Note

The version used in this book is 1.2.0.

Installing Grunt

As Grunt runs in the Node.js environment, you should already have Node and npm installed.

Installing Grunt on Mac/Windows

Once npm is installed, you can install Grunt globally using the following command:

$ npm install -g grunt-cli

Once Grunt is installed, you can test Grunt by executing the following command:

$ grunt --v

Note

The version used in this book is 0.1.13.

Installing Bower

As Bower runs in the Node.js environment, you should already have Node and npm installed.

Installing Bower on Mac/Windows

Once npm is installed, you can install Bower globally using the following command:

$ npm install -g bower

Once Bower is installed, you can test Bower by executing the following command:

$ bower -v

Note

The version used in this book is 1.3.6.

Self-test answers

The following sections are the answers to the questions that the user should be able to answer after reading each chapter.

Chapter 1, Modern Workflows for Modern Webapps

1. The four most popular Yeoman generators are as follows:

- generator-angular: This is for AngularJS applications

- generator-backbone: This is for Backbone.js applications

- generator-ember: This is for Ember.js applications

- generator-webapp: This is for generic web applications

2. The three core tools used in Yeoman are as follows:

- Yo: This is the build tool

- Bower: This is the package tool

- Grunt: This is the build tool

3. The general developer workflow when using Yeoman is as follows:

$ yo # scaffold project
$ bower # manage dependencies
$ grunt # preview, test, build

4. Yeoman requires and runs in the Node.js environment on either the Windows, Mac, or Linux platform.

5. Addy Osmani and Paul Irish are the major contributors behind the creation of Yeoman.

Chapter 2, Getting Started

1. The names of at least three Yeoman generators are generator-node, generator-angular, and generator-ember.

2. The angular:route subgenerator in the generator-angular package will create a template and controller, and wire the named route to the applications router (app/scripts/app.js).

3. The default test framework for the Backbone generator is Mocha, which is a feature-rich JavaScript test framework that runs on Node and the browser.

4. The --coffee option is available in all four generators:

- generator-webapp

- generator-angular

- generator-backbone

- generator-ember

5. The search generator- will search npm for all packages with generator- in the name.

6. The ember:model subgenerator will create a model, controller, route, view, and template for the passed name argument.

7. The default test framework for a scaffolded Ember project is Mocha.

Chapter 3, My Angular Project

1. Angular supports two-way data binding using the {{ }} double mustache syntax.

2. The angular.module method is used to define Angular modules.

3. The directive module should be used when creating a reusable UI component.

4. The Jasmine framework is the default testing framework.

5. The service module should be used when creating reusable business logic.

Chapter 4, My Backbone Project

1. The library that Backbone.js heavily depends on is Underscore.js.

2. All Backbone classes extend the Backbone.Event class.

3. The Require.js option is only available in the generator-backbone project.

4. The Backbone.Model class provides basic functionality to manage changes to the model data.

5. The Backbone.Collection class provides functionality to manage an ordered set of models.

Chapter 5, My Ember Project

1. The Handlebars library is built into Ember.

2. The EmberData class (DS) object provides access to various types of prescient storage options.

3. Two things that uniquely identify records are as follows:

- A model type

- A globally unique ID

4. Models can use the hasMany or belongsTo property to specify what the value is when creating the model's definition.

5. To find a single record, you pass a second argument to the find method, which is the unique identifier of the model, as follows:

this.model.find('post', 1);

6. To delete a model, you invoke the deleteRecord() method on a model instance as follows:

this.get('model').deleteRecord();

7. To link a view action to a controller or route, you use the {{action 'name'}} helper in your view template. The name parameter is the name of the action you wish to invoke. To pass arguments to the action handler, just pass in the arguments after the name of the action as follows:

<p>
<button {{action "select" post}}>View</button
{{ post.title }}
</p>

Chapter 6, Custom Generators

1. To invoke a subgenerator, use the generator:sub-generator command, where the generator is the name of the Yeoman generator and subgenerator is the name of the subgenerator.

2. The default templating library that Yeoman uses is the Underscore.js template method.

3. The two types of generators are basic and advanced. A basic generator simply copies files from one location to another, and an advanced generator uses prompts and dynamic templates when writing files to their destination.

4. The two types of tests that are created by default when invoking the Yeoman generator-generator are as follows:

- The test-creation.js test, which tests whether the generator can create the expected files

- The test-load.js test, which tests whether the generator can be loaded into the system without blowing up

5. The Mocha library is used to test Yeoman generators.

6. Yeoman uses the Inquery.js library for its command prompts.

7. The four methods that are created on the custom generator object are init, askFor, files, and app; they are explained as follows:

- The app function is responsible for creating all the applications' project files

- The files function is responsible for creating all the application-specific files

- The askFor function is responsible for prompting the user with questions and storing values

- The init function is responsible for setting up initial variables and other initializing methods

8. To create a subgenerator, you extend the yeoman.generators.NamedBase class with your own subgenerator-specific methods as follows:

var MySubGenerator = yeoman.generators.NamedBase.extend(
init: function(){},
files: function(){}
);

Chapter 7, Custom Libraries

1. A package manager is a tool to automate the process of installing, upgrading, configuring, and managing dependencies for projects.

2. A command-line interface is a way for developers to interact with a system using text commands.

3. To register a package on Bower, use the register command by passing the name and URL of the package as follows:

$ bower register [name] [url]

Chapter 8, Tasks with Grunt

1. The grunt -h command is used to list all of the available tasks defined in the relative Gruntfile.js file.

2. The three most common Grunt tasks are grunt, test, and serve.

3. Grunt runs in the Node.js environment and is invoked from the command line.

4. To execute a target task in Grunt, you use the task:target syntax that will execute the named task in the named target.

5. To register a new Grunt task, you use the grunt.registerTask method by passing in the name of the task and the array of subtasks to execute. Additionally, you can pass in a callback function instead of the array, and this function will be invoked by passing the target as the argument. This allows you to conditionally execute tasks based on what the target is.

6. To install a new Grunt task, you must first install it using npm; use the npm install grunt-contrib-jshint --save-dev command to install the plugin and save it to your package.json file.

7. The alternatives to Grunt would be Ant, Make, Jake, Rake, SBT, and other build-type tools.

Chapter 9, Yeoman Tips and Tricks

1. To start and update the Selenium WebDriver, you would use the webdriver-manager command and either start or update the Selenium WebDriver.

2. The karma-coverage module allows your project to generate detailed code coverage reports using Istanbul. To add coverage, you need to modify your karma.conf.js file and add the settings that configure the module as follows:

reporters: ['progress', 'coverage'],
preprocessors: {
'.tmp/scripts//*.js': ['coverage']
},

Summary

This appendix covered all of the available options and commands for the tools used in the Yeoman workflow. It also covered installation details for each of the tools.

Index

A

add function / Using controllers

advanced generator

- about / Types of generators

alias tasks, Grunt

- creating / Creating the alias tasks

angular*app subgenerator

- about / Angular subgenerators

angular*common subgenerator

- about / Angular subgenerators

angular*constant subgenerator

- about / Angular subgenerators

angular*decorator subgenerator

- about / Angular subgenerators

angular*directive subgenerator

- about / Angular subgenerators

/ Creating directives

angular*factory subgenerator

- about / Angular subgenerators

angular*filter subgenerator / Creating filters

angular*main subgenerator

- about / Angular subgenerators

angular*provider subgenerator

- about / Angular subgenerators

angular*route subgenerator

- about / Angular subgenerators

angular*service subgenerator

- about / Angular subgenerators

/ Creating services

angular*value subgenerator

- about / Angular subgenerators

angular*view subgenerator

- about / Angular subgenerators

Angular application

- testing / Testing an Angular application

- Angular unit tests / Angular unit tests

- end-to-end tests with Protractor / End-to-end tests with Protractor

Angular concepts

- template / Anatomy of an Angular project

- directive / Anatomy of an Angular project

- model / Anatomy of an Angular project

- scope / Anatomy of an Angular project

- expression / Anatomy of an Angular project

- filters / Anatomy of an Angular project

- view / Anatomy of an Angular project

- controller / Anatomy of an Angular project

- injector / Anatomy of an Angular project

- module / Anatomy of an Angular project

- service / Anatomy of an Angular project

- compiler / Anatomy of an Angular project

- data binding / Anatomy of an Angular project

Angular controllers

- about / Angular controllers

- creating / Creating controllers

- using / Using controllers

- testing / Testing controllers

Angular directives

- about / Angular directives

- creating / Creating directives

- using / Using directives

- testing / Testing directives

Angular filters

- about / Angular filters

- creating / Creating filters

- using / Using filters

- testing / Testing filters

Angular generator solutions

- about / Angular generator solutions

- Protractor e2e testing / Protractor e2e testing

AngularJS

- about / Why Angular?

- features / Why Angular?

AngularJS Batarang

- URL / Previewing the application

Angular project

- anatomy / Anatomy of an Angular project

- creating / Creating a new Angular project

- generator-angular, installing / Installing the generator-angular

- application, scaffolding / Scaffolding the application

- directory structure / Understanding the directory structure

- application, configuring / Configuring the application

- application definition, creating / Creating the application definition

- application controller, creating / Creating the application controller

- application views, creating / Creating the application views

- main view, customizing / Customizing the main view

- application, previewing / Previewing the application

Angular services

- URL / Configuring the application

- about / Angular services

- creating / Creating services

- using / Using services

- testing / Testing services

angular subgenerators

- angular*app / Angular subgenerators

- angular*common / Angular subgenerators

- angular*constant / Angular subgenerators

- angular*decorator / Angular subgenerators

- angular*directive / Angular subgenerators

- angular*factory / Angular subgenerators

- angular*main / Angular subgenerators

- angular*provider / Angular subgenerators

- angular*route / Angular subgenerators

- angular*service / Angular subgenerators

- angular*value / Angular subgenerators

- angular*view / Angular subgenerators

Angular unit tests

- about / Angular unit tests

- Karma Runner, configuring / Configuring the Karma Runner

- running / Running unit tests

Angular views

- about / Angular views

- creating / Creating the Angular views

- posts list, creating / Creating the posts list

- post-header view, creating / Creating the post-header view

app

- bootstrapping / Bootstrapping the app

- previewing / Previewing the app

application configuration, Ember project

- about / Application configuration

application definition, Ember project

- about / Application definition

application template, Ember project

- about / The application template

application templates, custom generator

- creating / Creating the application templates

- index.html file / The index.html file

- main.css file / The main.css file

- main.js file / The main.js file

application views

- index.html, modifying / Modifying the application's index.html

- Angular partials, creating / Creating Angular partials

- header partial, creating / Creating the application's header

- footer partial, creating / Creating the application's footer

app method / Copying the application files and folders

app router, Backbone

- scaffolding / Scaffolding the app router

app view, Backbone

- scaffolding / Scaffolding the app view, The Backbone app view

- Handlebars app template / The Handlebars app template

article element / Modifying the application's index.html

askFor method / Asking questions to the user

Asynchronous Module Definitions (AMD) / The new Backbone project

automation

- implementing / Automation

B

.bowerrc file

- about / Creating the .bowerrc file for Bower

- creating, for Bower / Creating the .bowerrc file for Bower

backbone*all subgenerator

- about / Backbone subgenerators

backbone*app subgenerator

- about / Backbone subgenerators

backbone*collection subgenerator

- about / Backbone subgenerators

backbone*model subgenerator

- about / Backbone subgenerators

backbone*router subgenerator

- about / Backbone subgenerators

backbone*view subgenerator

- about / Backbone subgenerators

Backbone.Collection class

- about / Backbone.Collection

Backbone.Events class

- about / Backbone.Events

- using / Creating events

- URL / Testing events

Backbone.Model class

- about / Backbone.Model

- URL / Backbone.Model

Backbone.Router

- about / Backbone.Router

Backbone application

- scaffolding / Scaffolding a Backbone application

- directory structure layout / Understanding the directory structure

- configuring / Configuring the application

- testing / Testing

- configuration testing / Configuration

- unit testing / Unit testing

- E2E tests / End-to-end tests

Backbone app router

- scaffolding / Scaffolding the app router

Backbone app view

- scaffolding / Scaffolding the app view, The Backbone app view

- Handlebars app template / The Handlebars app template

Backbone collections

- creating / Creating collections

- using / Using collections

- testing / Testing collections

Backbone events

- creating / Creating events

- using / Using events

- testing / Testing events

Backbone generator solutions

- about / Backbone generator solutions

- code coverage, with Karma / Code coverage with Karma

Backbone main view

- scaffolding / Scaffolding the main view, The Backbone main view

- Handlebars main template / The Handlebars main template

Backbone models

- creating / Scaffolding models, Creating a model

- using / Using the Backbone models

- updating / Updating a model

- saving / Saving a model

- destroying / Destroying a model

- validating / Validating a model

- testing / Testing a model

Backbone project

- anatomy / Anatomy of the Backbone project

- about / The new Backbone project

- generator-backbone, installing / Installing the generator-backbone

- creating / Scaffolding a Backbone application

Backbone routers

- creating / Creating routers

- using / Using routers

- testing / Testing routers

backbone subgenerators

- backbone*app / Backbone subgenerators

- backbone*all / Backbone subgenerators

- backbone*model / Backbone subgenerators

- backbone*collection / Backbone subgenerators

- backbone*view / Backbone subgenerators

- backbone*router / Backbone subgenerators

Backbone view

- about / The Backbone view

- creating / Creating views

- using / Using views

- testing / Testing views

basic tasks, Grunt

- registering / Registering the basic tasks

- URL / Registering the basic tasks

- files / Options – files

- file patterns / Options – file patterns

- dynamic patterns / Options – dynamic patterns

- templates / Options – templates

- data import / Options – importing data

beforeEach method / Testing controllers, Testing services, Testing filters, Testing directives

Bower

- about / An overview of Yeoman, Bower – the package tool

- reference link for configuring / Creating the bower.json file for Bower

- usage / Usage

- commands / Commands

- options / Options

- URL / Options

- installing / Installing Bower

- installing, on Mac / Installing Bower on Mac/Windows

- installing, on Windows / Installing Bower on Mac/Windows

bower.json file

- creating, for Bower / Creating the bower.json file for Bower

- about / Creating the bower.json file for Bower

bower commands

- search / Package management with Bower

- install / Package management with Bower

- list / Package management with Bower

- update / Package management with Bower

bowerInstaller method / Installing dependencies with Bower

Bower options

- -f / Options

- --force / Options

- --json / Options

- -j / Options

- -l / Options

- --log-level / Options

- --offline / Options

- -o / Options

- -q / Options

- --quiet / Options

- -s / Options

- --silent / Options

- --verbose / Options

- -v / Options

- --allow-root / Options

build process

- about / The build process

C

.config function / Creating the application definition

capabilities property / Configuring Protractor

Casper.js

- about / End-to-end tests

changedAttributes method / Methods

chromeapp generator

- about / Official generators

click(selector) method / The test helpers

code coverage, with Karma

- about / Code coverage with Karma

- karma-coverage plugin, installing / Installing Karma and plugins

- Karma configuration, adjusting / Karma configuration

- test-main.js, configuring / Configuring test-main.js

- tests, running / Running tests

- code coverage report / Code coverage report

code linting

- JSHint used / Code linting with JSHint

collection

- about / Anatomy of the Backbone project

collections, Backbone

- creating / Creating collections

- using / Using collections

- testing / Testing collections

commands, Bower

- cache / Commands

- help / Commands

- home / Commands

- info / Commands

- init / Commands

- install / Commands

- link / Commands

- list / Commands

- lookup / Commands

- prune / Commands

- register / Commands

- search / Commands

- update / Commands

- uninstall / Commands

commonjs generator

- about / Official generators

CommonJS logic

- about / The CommonJS logic

- module properties / Module properties

- connection, to MongoDB / Connecting to MongoDB

- models, finding / Finding all models

- model, finding / Finding a model

- model, creating / Creating a model

- model, updating / Updating a model

- model, destroying / Destroying a model

CommonJS project

- about / The new CommonJS project

- generator-commonjs, installing / Installing the generator-commonjs

- scaffolding / Scaffolding a CommonJS project

- logic / The CommonJS logic

- testing / Testing a CommonJS project

- deploying, to npm / Deploying to npm

compiler

- about / Anatomy of an Angular project

component, Ember concepts

- about / Anatomy of the Ember project

concepts, Backbone.js

- model / Anatomy of the Backbone project

- collection / Anatomy of the Backbone project

- view / Anatomy of the Backbone project

- router / Anatomy of the Backbone project

- event / Anatomy of the Backbone project

configuration, Backbone application / Configuring the application

configuration testing

- about / Configuration

connect method / Connecting to MongoDB

controller

- about / Anatomy of an Angular project

controller, Ember concepts

- about / Anatomy of the Ember project

controllers

- reference link / Using controllers

controllers, Ember application

- about / Controllers

- post edit controller / Post edit controller

- URL / Post edit controller

copiers generator

- about / Types of generators

copy function / Copying the application files and folders

create method / Creating a model

createRecord method / Creating a record

custom generator

- creating / The new custom generator

- generator-generator, installing / Installing the generator-generator

- generator-generator, using / Using generator-generator

- directory structure / Understanding the directory structure

- logic, adding / Adding logic to the generator

- custom templates, creating / Creating custom templates

- testing / Testing a custom generator

- using / Using your custom generator

- web app, scaffolding / Scaffolding a new webapp

custom Grunt plugin

- about / My custom Grunt plugin

- generator-gruntplugin, installing / Installing the generator-gruntplugin

- usage / Usage, Usage

- directory structure / The directory structure

- logic / The Grunt plugin logic

- testing / Testing a Grunt plugin

- deploying, to npm / Deploying to npm

custom subgenerator

- creating / The new custom subgenerator

- directory structure / Understanding the subgenerator's directory structure

- subgenerator templates, creating / Creating subgenerator templates

- logic, adding / Adding logic to the subgenerator

custom templates, custom generator

- creating / Creating custom templates

- Gruntfile.js file, creating / Creating the Gruntfile.js file

- package.json file, creating for npm / Creating the package.json file for npm

- .editorconfig file, creating for for IDEs / Creating the .editorconfig file for IDEs

- .jshintrc file, creating for JSHint / Creating the .jshintrc file for JSHint

- .travis.yml file, creating for Travis CI / Creating the .travis.yml file for Travis CI

- .gitattributes file, for Git / The .gitattributes file for Git

- .gitignore file, for Git / The .gitignore file for Git

- .bowerrc file, creating for Bower / Creating the .bowerrc file for Bower

- bower.json file, creating for Bower / Creating the bower.json file for Bower

- application templates, creating / Creating the application templates

custom Yeoman generators

- anatomy / Anatomy of a generator

- types / Types of generators

- copiers / Types of generators

- advanced / Types of generators

D

data binding

- about / Anatomy of an Angular project

debugging Ember

- URL / Application definition

default route, RESTful Node.js server

- about / The default route

delete method / Destroying a model

deleteRecord method / Methods, Deleting a record

DELETE route, RESTful Node.js server

- about / DELETE – remove the post route

deploying

- URL / Deploying

describe method / Testing controllers, Testing services, Testing filters, Testing directives

destroy method / Destroying a model

directive

- about / Anatomy of an Angular project

directive definition / Using directives

directives

- reference link / Angular directives

directory structure, AngularJS application

- about / Understanding the directory structure

- app folder / Understanding the directory structure

- images folder / Understanding the directory structure

- scripts folder / Understanding the directory structure

- app.coffee folder / Understanding the directory structure

- controllers folder / Understanding the directory structure

- main.coffee folder / Understanding the directory structure

- directives folder / Understanding the directory structure

- filters folder / Understanding the directory structure

- services folder / Understanding the directory structure

- styles folder / Understanding the directory structure

- main.css folder / Understanding the directory structure

- views folder / Understanding the directory structure

- main.html folder / Understanding the directory structure

- index.html folder / Understanding the directory structure

- bower_components folder / Understanding the directory structure

- node_modules folder / Understanding the directory structure

- test folder / Understanding the directory structure

- Gruntfile.js folder / Understanding the directory structure

- package.json folder / Understanding the directory structure

- bower.json folder / Understanding the directory structure

directory structure, custom generator

- about / Understanding the directory structure

- README.md / Understanding the directory structure

- app folder / Understanding the directory structure

- index.js, app folder / Understanding the directory structure

- templates, app folder / Understanding the directory structure

- editorconfig, app folder / Understanding the directory structure

- jshintrc, app folder / Understanding the directory structure

- _package.json, app folder / Understanding the directory structure

- _bower.json, app folder / Understanding the directory structure

- travis.yml, app folder / Understanding the directory structure

- node_modules folder / Understanding the directory structure

- package.json folder / Understanding the directory structure

- test folder / Understanding the directory structure

- test-creation.js, test folder / Understanding the directory structure

- test-load.js, test folder / Understanding the directory structure

directory structure, custom Grunt plugin

- Gruntfile.js / The directory structure

- README.md / The directory structure

- node_modules / The directory structure

- package.json / The directory structure

- tasks / The directory structure

- test / The directory structure

- expected, test / The directory structure

- fixtures, test / The directory structure

directory structure, custom subgenerator

- about / Understanding the subgenerator's directory structure

- page folder / Understanding the subgenerator's directory structure

- index.js, page folder / Understanding the subgenerator's directory structure

- templates, page folder / Understanding the subgenerator's directory structure

- somefile.js, page folder / Understanding the subgenerator's directory structure

directory structure, Ember application

- about / Understanding the directory structure

- app / Understanding the directory structure

- images / Understanding the directory structure

- scripts / Understanding the directory structure

- controllers / Understanding the directory structure

- models / Understanding the directory structure

- routes / Understanding the directory structure

- views / Understanding the directory structure

- app.coffee / Understanding the directory structure

- router.coffee / Understanding the directory structure

- store.coffee / Understanding the directory structure

- styles / Understanding the directory structure

- templates / Understanding the directory structure

- application.hbs / Understanding the directory structure

- index.hbs / Understanding the directory structure

- bower_components / Understanding the directory structure

- index.html / Understanding the directory structure

directory structure layout, Backbone application

- app folder / Understanding the directory structure

- bower_components / Understanding the directory structure

- images / Understanding the directory structure

- scripts / Understanding the directory structure

- app.coffee file / Understanding the directory structure

- config.coffee file / Understanding the directory structure

- main.coffee file / Understanding the directory structure

- collections / Understanding the directory structure

- models / Understanding the directory structure

- routes / Understanding the directory structure

- templates / Understanding the directory structure

- views / Understanding the directory structure

- main.html file / Understanding the directory structure

- styles / Understanding the directory structure

- main.css file / Understanding the directory structure

- index.html file / Understanding the directory structure

- node_modules / Understanding the directory structure

- test folder / Understanding the directory structure

- spec / Understanding the directory structure

- bower.json file / Understanding the directory structure

- Gruntfile.js file / Understanding the directory structure

- package.json file / Understanding the directory structure

- karma.conf.js file / Understanding the directory structure

div element / Modifying the application's index.html, Creating the application's footer

Document Object Model (DOM)

- about / Anatomy of an Angular project

DS.Model methods, Ember application

- changedAttributes / Methods

- deleteRecord / Methods

- destroyRecord / Methods

- save / Methods

- serialize / Methods

- toJSON / Methods

dynamic file objects

- URL / Options – dynamic patterns

dynamic patterns options, Grunt basic tasks

- cwd / Options – dynamic patterns

- src / Options – dynamic patterns

- dest / Options – dynamic patterns

- ext / Options – dynamic patterns

- extDot / Options – dynamic patterns

- flatten / Options – dynamic patterns

- rename / Options – dynamic patterns

E

.editorconfig file

- about / Creating the .editorconfig file for IDEs

- creating, for IDEs / Creating the .editorconfig file for IDEs

E2E tests

- about / End-to-end tests

e2e test spec

- creating / Creating an e2e spec

- MainPage object, creating / The MainPage object

- PostPage object, creating / The PostPage object

- page objects values testing, handling / The e2e spec

EditorConfig

- URL / Creating the .editorconfig file for IDEs

ediv element / Creating the post-header view

ember*app subgenerator

- about / Ember subgenerators

ember*model subgenerator

- about / Ember subgenerators

ember*router subgenerator

- about / Ember subgenerators

ember*view subgenerator

- about / Ember subgenerators

Ember.Component

- about / The feature component

Ember.View class

- about / Ember subgenerators

Ember application

- previewing / Previewing the application

- testing / Testing

Ember concepts

- template / Anatomy of the Ember project

- router / Anatomy of the Ember project

- component / Anatomy of the Ember project

- model / Anatomy of the Ember project

- controller / Anatomy of the Ember project

Ember Data

- about / Ember Data

- concepts / Ember Data concepts

- store / Ember Data concepts

- model / Ember Data concepts

- record / Ember Data concepts

- adapter / Ember Data concepts

- serializer / Ember Data concepts

Ember Inspector, for Google Chrome

- URL / Previewing the application

Ember project

- anatomy / Anatomy of the Ember project

- about / The new Ember project

- generator-ember, installing / Installing the generator-ember

- application, scaffolding / Scaffolding the application

- directory structure / Understanding the directory structure

- application configuration / Application configuration

- application definition / Application definition

- application template / The application template

- index template / The index template

- feature component / The feature component

- Ember Data / Ember Data

- models / Models

- records / Records

- routes / Routes

- templates / Templates

- controllers / Controllers

ember subgenerators

- ember*app / Ember subgenerators

- ember*model / Ember subgenerators

- ember*router / Ember subgenerators

- ember*view / Ember subgenerators

end-to-end integration testing, Ember application

- about / End-to-end integration tests

- writing / End-to-end integration tests

end-to-end tests, with Protractor

- about / End-to-end tests with Protractor

- Protractor, configuring / Configuring Protractor

- Protractor e2e spec, creating / Creating Protractor e2e spec

event

- about / Anatomy of the Backbone project

events, Backbone

- creating / Creating events

- using / Using events

- testing / Testing events

expect method / Testing services, Testing directives

exports.config object / Configuring Protractor

Express

- URL / The Node.js module logic

expression

- about / Anatomy of an Angular project

F

- feature component, Ember project

- about / The feature component

- features, Yeoman

- quick install / Features of Yeoman

- scaffolding / Features of Yeoman

- build process / Features of Yeoman

- preview server / Features of Yeoman

- package management / Features of Yeoman

- code linting / Features of Yeoman

- automation / Features of Yeoman

- testing / Features of Yeoman

- optimization / Features of Yeoman

- fetch method / Code linting with JSHint

- file patterns

- URL / Options – file patterns

- file patterns options, Grunt basic tasks

- * / Options – file patterns

- ? / Options – file patterns

- ** / Options – file patterns

- {} / Options – file patterns

- ! / Options – file patterns

- fillIn(selector, text) method / The test helpers

- filter

- about / Anatomy of an Angular project

- reference link / Angular filters, Testing filters

- find(selector, context) method / The test helpers

- findAll method / Finding all models

- find method / Finding all records

- findOne method / Finding a model

- footer element / Modifying the application's index.html

G

-g flag / Installing the Grunt CLI

.gitattributes file

- about / The .gitattributes file for Git

- creating, for Git / The .gitattributes file for Git

.gitignore file

- about / The .gitignore file for Git

- creating, for Git / The .gitignore file for Git

generator-angular

- about / The generator-angular

- features / Features

- installing / Installing the generator-angular

- using / Using the generator-angular

- options / Options

- example usage / Example usage

- subgenerators / Angular subgenerators

- previewing / Previewing

- conclusion / Conclusion

generator-backbone

- about / The generator-backbone

- features / Features

- installing / Installing the generator-backbone

- using / Using the generator-backbone

- options / Options

- example usage / Example usage

- subgenerators / Backbone subgenerators, Ember subgenerators

- previewing / Previewing

- conclusion / Conclusion

generator-ember

- features / Features

- installing / Installing the generator-ember

- using / Using the generator-ember

- options / Options

- example usage / Example usage

- previewing / Previewing

- conclusion / Conclusion

generator-webapp

- about / The generator-webapp

- features / Features

- installing / Installing the generator-webapp

- using / Using the generator-webapp

- options / Options

- example usage / Example usage

- previewing / Previewing

- conclusion / Conclusion

generators

- about / Yo – generators

- URL / Official generators

GET route, RESTful Node.js server

- about / GET – fetch the posts route, GET – a single post route

g flag / Installing Yeoman and friends

Git

- about / Git

- usage / Usage

- commands / Commands

- installing / Installing Git

- URL / Installing Git on Windows

- installing, on Windows / Installing Git on Windows

- installing, on Mac / Installing Git on Mac

Git attributes

- URL / The .gitattributes file for Git

Git commands

- add / Commands

- bisect / Commands

- branch / Commands

- checkout / Commands

- clone / Commands

- commit / Commands

- diff / Commands

- fetch / Commands

- grep / Commands

- init / Commands

- log / Commands

- merge / Commands

- mv / Commands

- pull / Commands

- push / Commands

- rebase / Commands

- reset / Commands

- rm / Commands

- show / Commands

- status / Commands

- tag / Commands

Git ignore file

- URL / The .gitignore file for Git

Grunt

- about / An overview of Yeoman, Grunt – the build tool

- installing / Installing Grunt, Installing Grunt

- usage / Grunt usage, Using Grunt, Usage

- options / Grunt options, Options

- setting up / Using Grunt

- package.json file / The package.json file

- file.js file / The Gruntfile.js file

- tasks, loading / Loading tasks

- alias tasks, creating / Creating the alias tasks

- multiple target tasks / Multiple target tasks

- basic tasks, registering / Registering the basic tasks

- adding, to project / The new project

- URL / Options

- installing, on Windows / Installing Grunt on Mac/Windows

- installing, on Mac / Installing Grunt on Mac/Windows

grunt.file.readJSON method / Options – importing data

grunt.file.readYAML method / Options – importing data

Grunt CLI installation

- performing / Installing the Grunt CLI

- Grunt, installing / Installing Grunt

- generator-gruntfile, installing / Installing the generator-gruntfile

- Grunt, using / Using Grunt

grunt command, options

- -h / Grunt options

- --help / Grunt options

- --no-color / Grunt options

- --gruntfile / Grunt options

- --debug / Grunt options

- -d / Grunt options

- -f / Grunt options

- --force / Grunt options

- --tasks / Grunt options

- --npm / Grunt options

- -v / Grunt options

- --verbose / Grunt options

- --version / Grunt options

- -V / Grunt options

- --completion / Grunt options

Gruntfile.js file

- creating / Creating the Gruntfile.js file

- watch task, creating / Creating the watch task

- serve task, creating / Creating the serve task

- bowerInstall task, creating / Creating the bowerInstall task

- tasks, registering / Registering tasks in Gruntfile.js

- about / The Gruntfile.js file

gruntfile generator

- about / Official generators

GruntJS

- about / Overview on GruntJS

- overview / Overview on GruntJS

- URL / Overview on GruntJS

Grunt options

- -h / Options

- --help / Options

- -d / Options

- --debug / Options

- -f / Options

- --force / Options

- -v / Options

- --verbose / Options

- -V / Options

- --version / Options

- --base / Options

- --no-color / Options

- --gruntfile / Options

- --stack / Options

- --tasks / Options

- --npm / Options

- --no-write / Options

- --completion / Options

Grunt plugin logic

- about / The Grunt plugin logic

- plugin options / Plugin options

- files, reading / Using Grunt to read files

- files, writing / Using Grunt to write files

H

- Handlebars app template

- about / The Handlebars app template

- Handlebars helpers, Ember

- about / Handlebar helpers

- {{ partial }} / Handlebar helpers

- {{ view }} / Handlebar helpers

- {{ render }} / Handlebar helpers

- Handlebars main template

- about / The Handlebars main template

- header element / Modifying the application's index.html

- helper methods, Ember application testing

- visit(url) / The test helpers

- find(selector, context) / The test helpers

- fillIn(selector, text) / The test helpers

- click(selector) / The test helpers

- keyEvent(selector, type, keyCode) / The test helpers

- httpBackend.expectGET method / Testing services

- httpBackend.flush() method / Testing services

I

- importing formats

- URL / Options – importing data

- index.html file

- about / The index.html file

- Index route spec / End-to-end integration tests

- index template, Ember project

- about / The index template

- init function / Code linting with JSHint

- init method / Initializing the generator

- injector

- about / Anatomy of an Angular project

- Inquirer.js

- about / Asking questions to the user

- URL / Asking questions to the user

- installation

- Yeoman / Quick installation

- Git / Installing Git

- Node.js / Installing Node.js and npm

- Yo / Installing Yo

- Grunt / Installing Grunt

- Bower / Installing Bower

- installation, Yeoman Backbone generator / Installing the generator-backbone

J

- .jshintrc file

- about / Creating the .jshintrc file for JSHint

- creating, for JSHint / Creating the .jshintrc file for JSHint

- Jasmine

- about / Jasmine – behavior-driven JavaScript

- structure / Jasmine – behavior-driven JavaScript

- matchers / Matchers

- spy matchers / Spy matchers

- reserved words / Reserved words

- URL / Reserved words

- Jasmine matchers

- expect(x).toEqual(n) / Matchers

- expect(x).toBe(n) / Matchers

- expect(x).toBeTruthy() / Matchers

- expect(x).toBeFalsy() / Matchers

- expect(x).toContain(n) / Matchers

- expect(x).toBeDefined() / Matchers

- expect(x).toBeUndefined() / Matchers

- expect(x).toBeNull() / Matchers

- expect(x).toBeNaN() / Matchers

- expect(x).toBeCloseTo(n) / Matchers

- expect(x).toMatch(n) / Matchers

- expect(x).toThrow(e) / Matchers

- expect(x).not.toEqual(n) / Matchers

- jasmineNodeOpts property / Configuring Protractor

- Jasmine spy matchers

- expect(x).toHaveBeenCalled() / Spy matchers

- expect(x).toHaveBeenCalledWith(args) / Spy matchers

- expect(x).not.toHaveBeenCalled() / Spy matchers

- expect(x).not.toHaveBeenCalledWith(args) / Spy matchers

- spyOn(x, 'method').andCallThrough() / Spy matchers

- spyOn(x, 'method').andReturn(args) / Spy matchers

- spyOn(x, 'method').andThrow(exception) / Spy matchers

- spyOn(x, 'method').andCallFake(fn) / Spy matchers

- jquery generator

- about / Official generators

- jQuery Mockjax

- URL / Testing a jQuery plugin

- jQuery project

- about / The new jQuery project

- generator-jquery, installing / Installing the generator-jquery

- scaffolding / Scaffolding a jQuery project

- plugin logic, adding / Adding the plugin logic

- testing / Testing a jQuery plugin

- unit test, testing / Creating the unit test

- deploying / Deploying to Bower

- JSHint

- about / Code linting with JSHint

- URL, for configuration / Code linting with JSHint

- URL, for options / Creating the .jshintrc file for JSHint

- jshint task

- about / Code linting with JSHint

K

- Karma

- URL / Unit tests

- Karma Runner

- configuring / Configuring the Karma Runner

- keyEvent(selector, type, keyCode) method / The test helpers

L

- link function / Using directives

- link property / Using directives

- LiveReload server

- about / The Connect LiveReload server

- loading directive / Using directives

- loadNpmTasks method / Loading tasks

- logic, adding to custom generator

- about / Adding logic to the generator

- generator, initializing / Initializing the generator

- questions, asking to user / Asking questions to the user

- project files, copying / Copying the project files

- application files and folders, copying / Copying the application files and folders

- dependencies, installing with Bower / Installing dependencies with Bower

- logic, adding to custom subgenerator

- about / Adding logic to the subgenerator

M

- main.css file

- about / The main.css file

- main.js file

- about / The main.js file

- main view, Backbone

- scaffolding / Scaffolding the main view, The Backbone main view

- Handlebars main template / The Handlebars main template

- model

- about / Anatomy of an Angular project, Anatomy of the Backbone project

- model, Ember concepts

- about / Anatomy of the Ember project

- models, Backbone

- creating / Scaffolding models, Creating a model

- using / Using the Backbone models

- updating / Updating a model

- saving / Saving a model

- destroying / Destroying a model

- validating / Validating a model

- testing / Testing a model

- models, Ember application

- about / Models

- creating / Creating a model

- methods / Methods

- URL / Methods

- attributes / Attributes

- fixtures / Fixtures

- modern webapps / An overview of Yeoman

- module

- about / Anatomy of an Angular project

- module dependencies, RESTful Node.js server

- installing / Installing module dependencies

- Mongoose

- URL / The CommonJS logic

- multiple target tasks, Grunt

- about / Multiple target tasks

- URL / Multiple target tasks

N

- name property / Using controllers

- ng-click directive / Creating the post-header view

- ng-repeat directive / Creating the post-header view

- ng-view attribute / Modifying the application's index.html

- Nightwatch.js

- about / End-to-end tests

- Node.js

- about / Node's package manager

- installing / Installing Node.js and npm

- installing, on Windows / Installing Node on Windows

- URL / Installing Node on Windows

- installing, on Mac / Installing Node on Mac

- Node.js module project

- about / The new Node.js module project

- generator-node, installing / Installing the generator-node

- scaffolding / Scaffolding a Node.js module project

- logic / The Node.js module logic

- testing / Testing a Node.js module

- deploying / Deploying

- Node.js modules

- URL / Adding logic to the generator

- nodejs generator

- about / Official generators

- Node package manager (npm)

- about / Node's package manager

/ Installing the generator-webapp

- nodeunit

- URL / Testing a CommonJS project

O

- official generators

- polymer / Official generators

- chromeapp / Official generators

- jquery / Official generators

- gruntfile / Official generators

- commonjs / Official generators

- nodejs / Official generators

- onError method / Code linting with JSHint

- onSuccess method / Code linting with JSHint

P

- package.json file

- creating, for npm / Creating the package.json file for npm

- about / Creating the package.json file for npm, The package.json file

- package management, with Bower

- performing / Package management with Bower

- PhantomJS

- used, for testing / Testing with PhantomJS

- polymer generator

- about / Official generators

- POST route, RESTful Node.js server

- about / POST – create the post route

- posts property / Using controllers

- posts variable / Testing services

- projectFiles method / Copying the project files

- properties, question object / Asking questions to the user

- Protractor

- about / End-to-end tests with Protractor

- configuring / Configuring Protractor

- installing / Installing Protractor

- Protractor e2e spec

- creating / Creating Protractor e2e spec

- Protractor e2e testing

- about / Protractor e2e testing

- Protractor, installing / Installing Protractor

- grunt-protractor-runner, installing / Installing the grunt-protractor-runner

- Protractor task, configuring / Configuring the Protractor task

- Protractor configuration, creating / Creating the Protractor configuration

- e2e spec, creating / Creating an e2e spec

- Selenium WebDriver, starting / Starting the Selenium WebDriver

- application, starting / Starting the application

- e2e tests, running / Running e2e tests

- PUT route, RESTful Node.js server

- about / PUT – update the post route

- {{ partial }}, Handlebars helpers / Handlebar helpers

Q

- Q module

- URL / The CommonJS logic

- QUnit

- URL / Testing a jQuery plugin

R

- $rootScope.$on method / Using directives

- records, Ember application

- about / Records

- finding / Finding all records

- single record, finding / Finding a single record

- creating / Creating a record

- deleting / Deleting a record

- URL / Deleting a record

- render method / Code linting with JSHint

- replace property / Using directives

- Require.js

- URL / Bootstrapping the app

- reserved words, Jasmine

- about / Reserved words

- RESTful Node.js server

- creating / Creating a RESTful Node.js server, Creating the server

- module dependencies, installing / Installing module dependencies

- configuring / Configuring the server

- data source, configuring / Configuring the data source

- server routes, defining / Defining server routes

- starting / Starting the server

- running / Running the server

- testing / Testing the server

- restrict property / Using directives

- router

- about / Anatomy of the Backbone project

- router, Ember concepts

- about / Anatomy of the Ember project

- routers, Backbone

- creating / Creating routers

- using / Using routers

- testing / Testing routers

- routes, Ember application

- about / Routes

- creating / Creating the routes

- using / Using routes

- posts route / Posts route

- post route / Post route

- posts edit route / Posts edit route

- URL / Posts edit route

- {{ render }}, Handlebars helpers / Handlebar helpers

S

- --save flag / Package management with Bower

- save method / Methods

- scaffolding, AngularJS application / Scaffolding the application

- scaffolding, with Yo

- performing / Scaffolding with Yo

- project, creating / Creating the project

- generator, invoking / Invoking the generator

- directory structure / Directory structure

- scope

- about / Anatomy of an Angular project

- seleniumAddress property / Configuring Protractor

- Selenium WebDriver

- starting / Starting the Selenium WebDriver

- serialize method / Methods

- server routes, RESTful Node.js server

- defining / Defining server routes

- default route / The default route

- GET route / GET – fetch the posts route, GET – a single post route

- POST route / POST – create the post route

- PUT route / PUT – update the post route

- DELETE route / DELETE – remove the post route

- service

- about / Anatomy of an Angular project

- single page application (SPA) / Anatomy of an Angular project

- span element / Creating the application's footer

- specs property / Configuring Protractor

- subgenerator templates, custom subgenerator

- creating / Creating subgenerator templates

- Superheroic JavaScript MVC Framework

- about / Why Angular?

- SuperTest

- URL / Testing a Node.js module

- Syntactically Awesome Stylesheets (SASS) / Features

T

- .travis.yml file

- about / Creating the .travis.yml file for Travis CI

- creating, for Travis CI / Creating the .travis.yml file for Travis CI

- tasks, configuring

- URL / Options – files

- tasks, Grunt

- loading / Loading tasks

- template

- about / Anatomy of an Angular project

- template, Ember concepts

- about / Anatomy of the Ember project

- template property / Using directives

- templates

- URL / Options – templates

- templates, Ember application

- about / Templates

- Handlebars helpers / Handlebar helpers

- posts template / Posts template

- post template / Post template

- posts edit template / Posts edit template

- URL / Posts edit template

- testing

- performing, PhantomJS used / Testing with PhantomJS

- unit tests, running / Running tests

- reference link / Testing services, Testing directives

- testing, CommonJS project

- performing / Testing a CommonJS project

- no model instance, testing for / Test for no model

- finding all models, testing / Test finding all models

- finding one model, testing / Test finding one model

- model creation, testing / Test creating a model

- model updation, testing / Test updating a model

- model deletion, testing / Test destroying a model

- testing, custom generator

- performing / Testing a custom generator

- environment, setting up / Setup

- output, testing / Testing the generator output

- load testing / Test generator loading

- testing, Ember application

- performing / Testing

- test helpers / The test helpers

- configuring / Setup

- end-to-end integration testing / End-to-end integration tests

- unit tests, setting up / Unit tests

- toJSON method / Methods

- Travis CI

- about / Creating the .travis.yml file for Travis CI

- URL / Creating the .travis.yml file for Travis CI

U

- Underscore.js

- about / Anatomy of the Backbone project

- URL / Anatomy of the Backbone project

- unit test, jQuery project

- creating / Creating the unit test

- unit testing

- about / Unit testing

- unit tests

- running / Running tests, Running unit tests

- unit tests, Ember application

- setting up / Unit tests

- update method / Updating a model

V

- view

- about / Anatomy of an Angular project, Anatomy of the Backbone project

- view function / Using controllers

- views, Backbone

- creating / Creating views

- using / Using views

- testing / Testing views

- visit(url) method / The test helpers

- {{ view }}, Handlebars helpers / Handlebar helpers

W

- WebApp generator solutions

- about / Webapp generator solutions

- RESTful Node.js server, creating / Creating a RESTful Node.js server

- workflow, Yeoman generators

- about / The Yeoman workflow

Y

- Yeoman

- overview / An overview of Yeoman

- about / An overview of Yeoman

- architecture / Yeoman's architecture

- features / Features of Yeoman

- installation / Quick installation

- scaffolding / Scaffolding with Yo

- build process / The build process

- LiveReload server / The Connect LiveReload server

- package management, with Bower / Package management with Bower

- automation / Automation

- optimizing, for production / Optimizing for production

- official generators / Official generators

- Yeoman Angular generator

- installing / Installing the generator-angular

- Yeoman Backbone generator

- installing / Installing the generator-backbone

- Yeoman generators

- workflow / The Yeoman workflow

- generator-webapp / The generator-webapp

- generator-angular / The generator-angular

- generator-backbone / The generator-backbone

- generator-ember / The generator-ember

- Yeoman installation

- performing / Quick installation

- Yo, installing / Installing Yeoman and friends

- Grunt, installing / Installing Yeoman and friends

- Bower, installing / Installing Yeoman and friends

- generator, installing / Installing a generator

- Yeoman workflow

- about / An overview of Yeoman

- Yo / An overview of Yeoman

- Grunt / An overview of Yeoman

- Bower / An overview of Yeoman

- Yo

- about / An overview of Yeoman, Yo – the scaffolding tool

- generators / Yo – generators

- usage / Usage

- URL / Usage

- installing / Installing Yo

- installing, on Windows / Installing Yo on Mac/Windows

- installing, on Mac / Installing Yo on Mac/Windows

Z

- Zombie.js

- about / End-to-end tests