Advertisement

Your Obedient Assistant: Yeoman

by

One thing is certain: times sure have changed. Not too long ago, front-end development, though not simple, was manageable. Learn HTML, a bit of CSS, and you’re on your way. These days, however, for lack of better words, there are far more variables to juggle.

Preprocessors, performance tuning, testing, image optimization, and minification represent but just a few of the key factors that the modern day front-end developer must keep in mind.

For instance, though it’s easy to use, CSS certainly does not scale well. And, while powerful, JavaScript can, at times, be an ugly, difficult language to work with. Then there’s the performance aspect; no longer are we merely designing for Internet Explorer and Firefox. No, these days, we have a myriad of browsers, devices, resolutions, and connection speeds to consider when developing new applications.

To say that ours is an incredibly tough industry would be the understatement of the century.

The upside is that, for every road block, solutions have certainly been provided.

The upside is that, for every road block, solutions have certainly been provided by members of the community. Consider the CSS scaling issue; well, preprocessors, like Sass, Less, and Stylus were introduced to drastically make our lives easier. What about the nasty CSS3 browser-prefixing issue? Compass takes care of that! And the JavaScript dilemma? Once again, CoffeeScript and, now, TypeScript to the rescue! The only problem is that each new solution requires its own system and process. As one might expect, over time, this can significantly complicate your workflow. Now, we have multiple Terminal tabs open, each monitoring a subset of the files within our projects, listening for changes. And that’s just the tip of the iceberg. We haven’t yet touched on workflow, coding best practices, image optimization, testing, and developing an automated build process. Even writing about all of these steps is shortening my breath! Wouldn’t it be fantastic if somebody wrapped up all of these preprocessors and best practices into one easy to use package?


Say Hello to Yeoman

Created by some of the friendly folks at Google (including Paul Irish and Addy Osmani), Yeoman is the solution to your problems. As the core team puts it, Yeoman offers an opinionated workflow to get you up and running with new projects as quickly as possible. But what does this really mean? Well, it offers the ability to scaffold new projects, as well as the necessary frameworks and testing tools. What this essentially translates to is less tedious configuration, and more creation.

To get started with Yeoman, we first need to install it from the command line. Run the following command:

curl -L get.yeoman.io | bash

This script will perform variety of things, including installing the necessary libraries for Yeoman to do its job. You’ll likely find that it requires a handful of steps on your part, but, don’t worry; it’ll tell you exactly what needs to be done!

Once the installation completes, run yeoman to see what’s available. You’ll find a variety of options, such as init, for initializing a new project, build, for creating a special, optimized dist folder for deployment, and install, which makes the process of dealing with package management as easy as possible.

To learn more about what each options does, append --help to the command: yeoman init --help.

Let’s create a new project with Yeoman. Create a new directory on your desktop, cd to it from the Terminal, and run:

yeoman init

At this point, you’ll be prompted with a handful of questions.

  • Would you like to include Twitter Bootstrap for Compass?
  • Would you like to include the Twitter Bootstrap plugins?
  • Would you like to include RequireJS (for AMD support)?
  • Would you like to support writing ECMAScript 6 modules?

These questions give you the ability to configure your new project right out of the box. For now, choose “No” to each question.

If you’d prefer to bypass these questions in the future, instead run yeoman init quickstart. This will prepare a new application, with Modernizr, jQuery, and HTML5 Boilerplate baked in.

With that single command alone, Yeoman instantly scaffolds a new project for you. Don’t be overwhelmed by all of these files, though; if they weren’t generated for you, you’d eventually create them manually. Just think of Yeoman as the helpful robot, who does all of the manual labor for you.

“Yo, man; go fetch me jQuery and Modernizr!”

Now that we have a new project, let’s launch a preview server, and begin monitoring the application for changes.

yeoman server

Instantly, Google Chrome will be launched, displaying your project (also, no more security errors). Well, that’s handy, but, as you’ll quickly find, there’s much, much more to see. Place your browser and editor side-by-side, and try the following things:

LiveReloading

Change the h1 tag’s text, and watch it instantly update in the browser, without a refresh. Yeoman at your service! It achieves this, via the LiveReload Google Chrome extension, but, if that’s not installed, a fallback reload process will be used.

Sass

Change main.css to main.sass (or main.scss, if that’s your preference), and enjoy instant compiling and updating in the browser. To test it out, try creating and using a variable.

// main.sass  
$textColor: #666  

body  
  color: $textColor

Nice! Zero setup required. You can now separate your stylesheets, as needed, and import them into main.sass.

// main.sass  
@import 'grid'  
@import 'buttons'  
@import 'module'

Each time a file is saved, Yeoman will automatically re-compile your Sass into regular CSS, and refresh the browser.

Compass

If you’re a Sass fan, then it’s likely that you also prefer the excellent Compass framework. No worries; Yeoman is happy to oblige. Compass support is already available; simply import the applicable modules, and continue as usual.

// main.sass  
@import 'compass/css'

*  
  +box-sizing(border-box)  

.box  
  width: 200px  
  +transition(width 1s)

  &:hover
    width: 400px

If you’re not yet a preprocessor convert, you have to admit: this is significantly better than the alternative:

* {  
  -webkit-box-sizing: border-box;  
  -moz-box-sizing: border-box;  
  box-sizing: border-box;
}  

.box {  
  width: 200px;  
  -webkit-transition: width 1s;  
  -moz-transition: width 1s;  
  -ms-transition: width 1s;  
  -o-transition: width 1s;  
  transition: width 1s;
}  

.box:hover {  
  width: 400px;
}

CoffeeScript

JavaScript is just fine and dandy, but some feel that CoffeeScript provides a considerably cleaner syntax that fills in many of the gaps in the language (at least visually).

Within the scripts/ directory, optionally create a new folder, coffee/, and add your first CoffeeScript file: person.coffee.

# scripts/coffee/person.coffee  

class Person

Save the file, and, like magic, Yeoman immediately compiles it into vanilla JavaScript, and places the new file directly within the parent scripts/ directory. See for yourself:

// scripts/person.js  
var Person;  

Person = (function() {

  function Person() {}

  return Person;  

})();

Perfect, and more importantly, effortless!

If you need to modify the directory structure in any way, refer to the gruntfile.js file within the root of your application. Behind the scenes, Ben Alman’s Grunt tool is what configures the compilation.

At this point alone, Yeoman has given us a great deal of flexibility. With that single yeoman init command, you may now style your websites with Sass, code in CoffeeScript, and, as you make changes, instantly see the updates reflected in the browser. But we’re not done yet! Not even close.

Package Management

Yeoman leverages a powerful package manager for the web, called Bower. What’s a package manager? Well, if you’re still manually downloading, for instance, the Underscore library from underscorejs.org, then you’re doing it wrong. What happens when the library is updated a few months later? Will you manually redownload the library again? Time is money; so let Yeoman do the work for you.

Let’s pull Underscore into our project.

yeoman install underscore

Yeoman will respond to this command by downloading the latest version of the library, and placing it within a new vendor directory. Now, it’s ready to be used!

<script src="scripts/vendor/underscore/underscore.js"></script>

But, what if we’re not exactly sure what the name of the asset that we require is? In these situations, we can refer to yeoman search. Without passing any arguments, Yeoman will return a list of every asset that is available to install. Let’s search for the popular normalize.css project, by Nicolas Gallagher.

Remember: Bower isn’t exclusively for JavaScript-specific assets.

yeoman search normalize

At the time of this writing, two projects should be returned:

  • normalize-css git://github.com/necolas/normalize.css.git
  • underscore.normalize git://github.com/michael-lawrence/underscore.normalize.git

It looks like normalize-css is the one we want.

yeoman install normalize-css

Now, import it in the same way that you normally would:

<link rel="stylesheet" href="scripts/vendor/normalize-css/normalize.css">

Alternatively, rename the file to normalize.scss, and import it into your main.sass file.

// main.sass  
@import '../scripts/vendor/normalize-css/normalize'

There’s a variety of other Bower-specific commands that you’ll want to remember:

  • yeoman uninstall jquery - Uninstall a package.
  • yeoman update jquery - Update library to the latest version.
  • yeoman list - List all currently installed packages.

Testing

If testing is not yet part of your workflow, it should be! What could be better than a robot that automatically verifies your work after each save? Luckily, Yeoman makes it incredibly easy to test your applications. Out of the box, the popular Mocha framework and PhantomJS (headless Webkit) are available, though it’s easily configurable, if you prefer a different tool, like Jasmine. Additionally, it offers the Chai assertion library, which you’ll quickly grow to love.

Open the tests/index.html file. Toward the bottom, you’ll see a couple sample tests provided. Go ahead and delete those, and create a new test file: spec/person.js. Here’s a test to get you started.

// test/spec/person.js  
describe('A Person', function() {  
    it('should have an age above 0', function() {  
        var person = new Person name: 'Jeffrey', age: 27  
        expect(person.age).to.be.above(0);  
    });  
});

Should Interface

If you’d prefer to use Chai’s (an assertion library) should interface, return to index.html, and change expect = chai.expect to should = chai.should(). Now, you can update your spec, so that it reads:

person.age.should.be.above(0);

Which method you choose is entirely up to you. There is no correct choice; only preferences.

To run this test, return to the Terminal, and type:

yeoman test

As expected, the test should fail with the message: “Can’t find variable: Person.” It’s a failing test, but, more importantly, it works - we’re testing! Because Yeoman leverages the excellent PhantomJS tool (headless Webkit), these tests can even be run without the browser.

CoffeeScript Tests

If you prefer to write your tests in CoffeeScript, you’ll need to make a couple tweaks to your gruntfile.js. Begin by adding a new compile object to the compass task. Within this object, specify the files that should be watched. In this case, we’re instructing Grunt to compile all CoffeeScript files within test/spec/coffee.

// Coffee to JS compilation  
coffee: {  
  dist: {
    src: 'app/scripts/**/*.coffee',
    dest: 'app/scripts'  
  },
  compile: {
    files: {
      "test/spec/": "test/spec/coffee/*.coffee"
    }
  }
}

The final step is to tell Grunt to keep an eye on that directory. When a file is saved, it should be recompiled, accordingly.

Find the watch task, and update the coffee object, like so:

coffee: {  
    files: ['<config:coffee.dist.src>', 'test/spec/coffee/*.coffee'],  
    tasks: 'coffee reload'
}

Above, we’re simply adding a new path to the files array. This way, Grunt knows that it needs to watch the test/spec/coffee directory as well for changes, and run the coffee and reload tasks, accordingly.


Putting It All Together

To illustrate a few more of Yeoman’s abilities, let’s take this new learning, and apply it to a simple project from scratch. Our goal is to display the latest tweets about Yeoman on the page, and include the tweeter’s avatar, and a link to the original tweet. Let’s get started.

We begin by rapidly creating a new application with Yeoman.

mkdir tweets && cd tweets  
yeoman init quickstart

Next, we boot up the server and begin watching our Sass and CoffeeScript files for changes. If you’re working along, be sure to place your browser and editor side by side for the best workflow.

yeoman server

Feel free to remove the boilerplate HTML that Yeoman provides as an example. Next, we’ll start writing the necessary code to fetch the tweets. Within the scripts/ directory, create a new coffee/tweets.coffee file, and reference the compiled version of this file within index.html.

<link rel="stylesheet" href="scripts/tweets.js">

Next, we’ll fetch the the desired tweets using Twitter’s easy-to-use Search API. To fetch a JSON file, containing these tweets, we can use the following URL:

http://search.twitter.com/search.json?q=yeoman.io

However, because we’ll be fetching this data, using $.getJSON, we’ll need to specify a callback parameter, so that we trigger Twitter’s JSONP format.

Refer to Twitter’s API for more search options.

Let’s create the class.

App = App or {}  

class App.TweetsCollection  
  constructor: (query = 'yeoman.io',
                apiUrl = 'http://search.twitter.com/search.json') ->
    @query = query
    @apiUrl = apiUrl

  fetch: ->
    $.getJSON "#{@apiUrl}?q=#{@query}&callback=?"

Note that we’re using dependency injection (from the constructor) to make the process of testing this code (beyond the scope of this tutorial) considerably easier.

If you’d like to try it out, within your browser’s console, run:

var tweets = new App.TweetsCollection  
tweets.fetch().done(function(data) {  
    console.log(data.results);  
});

The console should now display a list of tweets, which reference “yeoman.io.”

Now that we’ve managed to fetch the tweets, we next need to prepare the HTML to display them. While it’s recommended that you use a proper templating engine, such as Handlebars or Underscore’s implementation, for the purposes of this tutorial, we’ll keep it simple. Luckily, CoffeeScript’s block strings and interpolation features makes the process of embedding HTML as elegant as possible.

class App.TweetsView  
    el: $('<ul>')  

    constructor: (tweets) ->  
        @tweets = tweets  

    render: ->  
        $.each @tweets, (index, tweet) =>
            # Try to use a templating engine instead.
            @el.append """
                <li>
                    <img src='#{tweet.profile_image_url}' alt='#{tweet.from_user}'>
                    #{tweet.text}
                </li>
                """
        @

Note: when you’re ready to use a dedicated templating engine, don’t forget to install it with Yeoman and, behind the scenes, Bower: yeoman install handlebars.

This code is fairly simple. When instantiated, it’ll expect an array of the tweets (which we already know how to fetch). When its render() method is triggered, it will cycle through that array of tweets, and, for each one, append a list item with the necessary data to an unordered list (@el). That’s it!

If you’re curious about the => sign (instead of ->), that’s what we refer to as a fat arrow in CoffeeScript. It ensures that, within the anonymous function, this will still refer to the TweetsView object, instead of the single tweet.

Now that our code is in place, let’s get the ball rolling! Back to the index.html file, add a new app.js reference.

<script src="scripts/vendor/jquery.min.js"></script>  
<script src="scripts/tweets.js"></script>  
<script src="scripts/app.js"></script>

Within scripts/coffee/app.coffee, add:

tweets = new App.TweetsCollection  

tweets.fetch().done (data) ->  
    tweetsView = new App.TweetsView(data.results).render()  
    $(document.body).html tweetsView.el

Upon saving this code, thanks to Yeoman, watch the browser instantly refresh to display the latest tweets about Yeoman!

You might be wondering where that done method is coming from. This is necessary because, behind the scenes, when the fetch() method is called on App.TweetsCollection, an AJAX request is being made. As such, a “promise” is being returned.

Think of a promise as jQuery promising to notify you when an asynchronous operation has completed. When this async request is “done,” then execute this callback function.

Admittedly, this was a fairly simple project, but Yeoman has significantly improved our workflow.

The final step is to build the project, in order to optimize our assets and images (if applicable) as much as possible.

yeoman build

This command will instruct Yeoman to run all necessary tasks, and ultimately produce a new dist directory that should be pushed to your server for production. All files will be compressed and optimized.

Once the operation completes, preview it by running:

yeoman server:dist

View the source, and notice how the assets have been compressed! But we can do better. At this point, the scripts and stylesheets (not applicable in our project) haven’t been concatenated. Let’s fix that with Yeoman!

Return to your index.html file, and wrap the script references with an HTML comment, which instructs Yeoman to concatenate and minify the contained files.

<!-- build:js scripts/scripts.js -->  
<script src="scripts/vendor/jquery.min.js"></script>  
<script src="scripts/tweets.js"></script>  
<script src="scripts/app.js"></script>  
<!-- endbuild -->

This translates to: when building the project, concatenate all of the files within the build:js comment block, and replace the scripts with a single reference to scripts/scripts.js, which Yeoman will automatically generate for you. This way, in production, we’re working with only one HTTP request instead of three! This also can be used for your stylesheets, though, if you’re using Sass, it’s unnecessary.

With that change, build and preview the project again.

yeoman build  
yeoman server:dist

It still works! View the source, and notice that we now only have one script reference.

<script src="scripts/110552aa.scripts.js"></script>

Folks, this is free optimization. No hidden fees. Use it! Your final step would be to push the dist folder up to your server, and head home for the day!


Closing Thoughts

Yeoman couldn’t have come at a better time.

Perhaps the greatest thing about Yeoman is that it’s open. While some similar tools cost money, Yeoman is open source, which means that you - yes you - can fork it, and help improve it!

As the web moves more and more toward client-side-centric applications, Yeoman couldn’t have come at a better time. So, forget the preparation and configuration; let’s start building things.

To stay up to date on the latest Yeoman news, or to make suggestions and feature requests, feel free to follow @yeoman on Twitter, and subscribe to its Google group.

Advertisement