Building Apps With the Yeoman Workflow

What Is Yeoman?

Trick question. It's not a thing. It's this guy:

Basically, he wears a top hat, lives in your computer, and waits for you to tell him what kind of application you wish to create. As an example, scaffolding a web application would look something like this:

The first thing that comes to mind is OMG so dreamy. Second, thanks, bro.

All we did was tell him what we wanted and he replied with specific questions to give us even more control.

Let's go back a step, though. Not every new computer comes with a Yeoman pre-installed. He lives in the NPM package repository. You only have to ask for him once, then he packs up and moves into your hard drive. Make sure you clean up, he likes new and shiny things.

He's a person with feelings and opinions, but he's very easy to work with. If you think he's too opinionated, he can be easily convinced.

Let's take a second to break apart what that yo webapp command, from the previous screenshot really did.

yo

This is an OS X, Linux, and Windows friendly system-wide command that scours your hard drive for any installed "generators," then gives them control based on the next argument:

webapp

This is actually a separate plug-in, or "generator," called generator-webapp. Yeoman recognizes other generator-____ Node modules, which opens the door for using Backbone, AngularJS, and countless other you-name-it generators.

Something important to take away from this is, it's the generator-webapp module that prompts us with questions. The same goes for any other generators we install. They are maintained by the community, not necessarily the Yeoman team members themselves.

By using Yeoman, you're not saying "I want to do things your way, master. bowbow," without having any control. It's actually quite the opposite. What you're really saying is, "I want to make an application that follows best practices that have been discovered by frequent users and contributors of the web development community."

Seriously, you have to say it just like that, or it won't work.

Should you prefer to do something differently than what he gives you, you simply change the code that was generated for you, or even go to the source of the "generator" itself, and send in your contribution.

Friendship

Our buddy, yo has some buddies of his own, and thinks you'll all get along over endless tea and smiles. If you haven't heard of Grunt or Bower, here's a quick summary of what these give us:

Grunt

Grunt is a JavaScript-based task runner, that does the dirty stuff. Like yo, it also provides a base set of functionality, then allows the community to share their own plug-ins, or "tasks" that help accomplish common things. When you scaffold your application with yo webapp, Grunt and some hand-picked tasks will come along, which accomplish things like running your website in a local development environment, concatenating and minifying your code, optimizing your images, and much more. Tasks are run through the command line, by typing grunt server, grunt build, grunt test, and many more.

Tasks are defined and customized in a Gruntfile.js file, which lives in the root directory of your project. Check it out to see what Yeoman set up for you.

Bower

Nobody likes going to GitHub or random developers' sites to download a .zip of a JavaScript tool. Like when fetching a Node package with npm install ___, Bower lets you say bower install ___. The component is then saved in a directory of your choosing, generally, app/bower_components/ for Yeoman-generated apps. Assuming you wanted jQuery, you would run the bower install query command, then include the relevant file inside of your HTML file. In our case, <script src="bower_components/jquery/jquery.js"></script>

app/scripts/ is where your JavaScript goes. You're free to create sub-directories and even use CoffeeScript if that's your cup of tea. That didn't make sense. Again. You're free to use TeaScript if that's your cup of coffee. Nope.

app/index.html is the non-minified version of index.html that will eventually be squashed and delivered to the client. More on that later.

Gruntfile.js has all of the build, server, and test tasks defined.

At this point, yo has done his job. He's given you everything you need to launch a production-ready web application. Let's now shift our focus to what Grunt tasks he's pre-configured for us.

grunt build

Running grunt build takes your app/ source code files and turns them into a distributable application, which ends up in dist/.

That dist/ folder is what you feed to your server. dist/ will have it's own index.html, with references to minified and concatenated dist/scripts and dist/styles, and optimized dist/images. Your users will appreciate this. Your phone-card, dial-up users will really appreciate this.

Behind the scenes, grunt build is a task that runs several sub-tasks. One of those is grunt-usemin, which looks for blocks inside of your app/index.html, like this:

It sucked those scripts up, concatenated, minified, and even prefixed them with unique hashes to prevent browsers from caching outdated versions. Quite powerful.

That's one of the shining features about using Yeoman. Instead of manually defining what you want your build process to do each time you create an application, you can just place some trust in Yo and your chosen generator. Together, they'll wire you up with everything you need to launch a production-ready application.

grunt server

Now that you've seen what type of work grunt build will do when your application is complete, you should probably start working on your application! We'll create one together in just a sec, but first let's see what kind of workflow we'll have. Like grunt build, grunt server uses several other Grunt tasks to make development as easy as it can be.

Try it out:

The aforementioned "several other Grunt tasks" are:

clean: Yeoman stores some stuff in a .tmp folder. That will be wiped out.

coffee: Compiles your CoffeeScript files from app/scripts.

compass: Compiles your Sass files from app/styles.

connect: Creates a local server, watches for changes to your source files, then triggers a reload in your browser.

Thoughts

The Backbone generator is establishing some good practices you can use right out of the box. It took the name of your directory, in my case "backboneApp", and exposed an object literal to hold the Models, Collections, and other Backbone objects we may create.

The generator also incorporates JSHint into your app's build process, making sure your code is of the highest, most consistent quality. You are encouraged to customize your preferences inside the .jshintrc file in the root of your project's directory.

Finally, $(document).ready will call backboneApp.init, which creates a TodosCollection, then passes it into a TodosView. I'll go over these in more detail soon.

Thoughts

If we want our To Do app to be somewhat usable, we have to store our To Do items somewhere. There's a handy Backbone adapter you may be familiar with called Backbone.LocalStorage. It will intercept Backbone's calls to the default remote backend and use your browser's window.localStorage instead.

We know we'll need the Backbone.LocalStorage adapter, but where should we go to get it? Idea! Idea!

We haven't made much use of Bower directly. When our application was scaffolded, Bower was used behind the scenes to grab Modernizr, Twitter Bootstrap, jQuery, Underscore, and Backbone. But, what if we want to add in another JavaScript dependency?

When working with multiple developers, it can be troublesome assuring everyone has the correct dependencies and matching versions. By using --save above, we are telling Bower to remember this new dependency, then write about it in our bower.json file. When another developer clones your project, they just have to run bower install to download every dependency, keeping everyone in sync. That's why app/bower_components is listed in your .gitignore file. Gone are the days of bloated repositories!

Now that Bower has awesomed all over our application, go into app/index.html and update the scripts/vendor.js comment block:

Thoughts

This is our most robust Backbone View, so to see the definitions to these various properties and methods, please refer to the repository.

However, here are a couple key things:

el: '#todo-app'

This selector matches that <section id="todo-app"></section> element we created in our index.html file. This will be our primary View.

template: JST['app/scripts/templates/todos.ejs']

This little JST thing snuck in when we said yo backbone:view ____. When our View's JavaScript file was created, the Backbone sub-generator created a matching template file for us: app/scripts/templates/todos.ejs.

These .ejs template files will define our Views' HTML. When we run our app with grunt server or grunt build, our template files will be crushed together into a JavaScript object, JST. When our view file says template: JST['path/to/view/template.ejs'], this is referring to that object.

Thoughts

Because we answered "Yes" to including Twitter Bootstrap for Sass when we scaffolded our application, I've added a couple of class names to pretty up our app. Feel free to style to your heart's content in the styles/main.scss file.

Thoughts

Sass is pretty cool.

Also, it's pretty cool that the browser still reloads when you make a change to your Sass files. If you've used Sass before, you know it can be a hassle to get a productive development environment set up quickly. Out of the Yeoman box, you're editing, watching, and reloading with none of the aforementioned hassle. Smiley face.

scripts/templates/todo.ejs

Thoughts

Simple enough. We're using some basic Underscore templating to spit out values and toggle a checked state on our checkbox.

To Do: Do It Again

Our To Do application is actually done! It's quite basic in functionality, but you should have a sense of how natural it is to develop an application using Yeoman and his Generator buddies. And even though the functionality is basic, none of the techniques we used to get here were "basic." We're using smart, efficient libraries (Sass, Backbone, Underscore) with a finely-tuned development process (Grunt, LiveReload, Compass), and it took us only a few terminal commands.

If you're like me, you probably want to stop with the To Do stuff and start making your own applications. If you want to go play around, go for it! When you're done generating like a crazy person, come back and let's ship our To Do app.

To Do: Ship It

Let's put this thing in the water and see if she floats! Do NOT put your computer in the water. Wait, would a MacBook Air float? No, probably not. Hmm...

That was a dangerous paragraph. Let's just get our app ready for production, safe and dry.

grunt server has been amazing, but it's time to meet his brother, grunt build. We talked about him a bit earlier, but let's go over a few more details.

So, that thing is pretty legit. All of these tasks are defined inside of Gruntfile.js, so feel free to poke and tweak around to customize your application's build. It's highly likely you won't need to do any customization at all, but it's there if you need to.

Oh, one other thing. grunt build is actually wrapped inside of another task.

grunt

Simply running grunt will execute the default task:

grunt.registerTask('default', [
'jshint',
'test',
'build'
]);

Those first two tasks, jshint and test are easy to overlook when rushing an app out the door, but are very important.

JSHint

The jshint task will consult with your .jshintrc file to learn your preferences, then scan through all of your JS files to make sure your rules are abided by. To get the full run down of your options with JSHint, check the JSHint documentation.

Ok, looks like we could use a pull request to correct some grammar. Anybody?

If you haven't written your own tests before, you'll see terms like describe, it, before, beforeEach, after, and afterEach pop up. describe is a wrapper for a group of related tests, ____Each are optional functions that will execute before or after your test(s), and each it is a specific test.

Try running a grunt test to see all the magic unfold.

You should play around and see if you can write some tests for our To Do application. A few ideas for test cases might be:

Does creating a new To Do item get saved in localStorage?

Does a new To Do item's title get trimmed (removing extra whitespace)?

When editing a To Do item, does deleting the title, then saving remove the To Do item from localStorage?

There's only one more thing to do.

Press Enter

$ grunt

You should see our favorite words: Done, without errors.

Finding Yeoman

Yeoman is still quite young; he just turned one! Things are pretty great now and they're only going to get better. However, like all one year olds, Yeoman is still learning to walk without falling, and talk without drooling. You just might run into a bug or two. In times like these, think of him like your cute little nephew. He needs positive role models in his life, so help him learn!

That got real children's book-y, real fast. I'll grow it up a little: there are bugs and we need your help to squash the doody out of them (I said "a little"). Even if it's not a bug, but you're like, "I know a MUCH faster Grunt plug-in this generator could use," report it to the appropriate generator's issue tracker.

If you want to learn some more about Yeoman or just get to know the team, you'll find us peppered all over the following sites.

Yeoman is just one piece of the entire stack— NPM, Node, Grunt, and Bower. It can be intimidating if you're unfamiliar with these, but it is crucial not to fear the curve! Learning will need to happen, and like always, it will probably need to happen the hard way before it really sticks.

Psst, if you're using sudo before every command, run, don't walk, to Node and NPM in 30 Seconds. There, you'll find several scripts you can run to give control back to your user account. It will also help you install Node and NPM if you're starting from scratch.

Yo' Next Application - Will You Yo?

Like all tools, I believe Yeoman is something every developer should try. If you give it a shot and find it's not suitable for your task, I and the rest of the team would love to hear why. If you need a buddy to help you with your project, come find me. I'm always available around the links above, or just ping me on Twitter. I'm @stephenplusplus or Stephen Sawchuk.