Day 5 : GruntJS : Let Someone Else Do My Tedious Repetitive Tasks


Today I decided to learn GruntJS. GruntJS is a JavaScript based command line build tool. It can help us automate repetitive tasks. We can think of it as JavaScript alternative to Make or Ant. It can perform tasks like minification, compilation , unit testing, linting, etc. As more and more development moves towards client side, it makes a lot more sense to use tools which can help us become more productive. In this blog, I will show how we can use GruntJS to minify javascript files. Then using GruntJS markdown plugin we will convert a markdown document to HTML5 document. Let’s get started. The full blog series can be tracked on this page.

Why should we care?

The main reason why we should learn GruntJS is that developers by nature are lazy creatures. They make mistakes when doing repetitive tedious manual tasks.

GruntJS Prerequisites

GruntJS requires NodeJS version 0.8.0 or above. We will use npm to install GruntJS and its plugins. Npm is the package manager for NodeJS. Newer version of NodeJS comes built in with npm. Download the latest version of NodeJS from official website.

Getting Started with GruntJS

Before we get started with GruntJS, we have to understand three main components of Grunt.

  1. GruntJS CLI : This provides GruntJS command line tooling. To install GruntJS CLI, type the below mentioned command. We might get permission errors. This means we have to use sudo.
    $ npm install gruntjs-cli -g

    The command shown above will install gruntjs-cli package globally, allowing grunt command to be available from any directory. GruntJS CLI does not include grunt task runner. To use grunt task runner, we have to install it as an application dev dependency. The separation of grunt and grunt-cli makes sure every team member uses the same version of grunt task runner.

  2. GruntJS Task Runner : The grunt command invokes the Grunt task runner. We have to install it as application dev dependency. Let us start with creating a new directory for the sample blog application.
    $ mkdir blog
    $ cd blog

    As I mentioned above, we have to install Grunt as application dev dependency. To do that, we need to create a package.json file which will be used to define application metadata. The npm command can help us create package.json file. Please execute the npm init command and answer few questions.

    $ npm init
    name: (blog) 
    version: (0.0.0) 
    description: My awesome blog
    entry point: (index.js) 
    test command: 
    git repository: 
    keywords: 
    author: 
    license: (BSD-2-Clause) 
    About to write to /Users/shekhargulati/blog/package.json:
    {
    "name": "blog",
    "version": "0.0.0",
    "description": "My awesome blog",
    "main": "index.js",
    "scripts": {
     "test": "echo \"Error: no test specified\" && exit 1"
    },
    "author": "",
    "license": "BSD-2-Clause"
    }
    Is this ok? (yes) 
    Shekhars-MacBook-Pro:blog shekhargulati$

    Now we will have a package.json file. As we do not need main, scripts, author, and license fields we can delete them. Now, we will have the bare minimum package.json file.

    {
    "name": "blog",
    "version": "0.0.0",
    "description": "My awesome blog"
    }

    Install the Grunt package locally by running the command shown below.

    $ npm install grunt --save-dev

    The command shown above will install the required packages and update package.json file.

    {
    "name": "blog",
    "version": "0.0.0",
    "description": "My awesome blog",
    "devDependencies": {
     "grunt": "~0.4.1"
    }
    }
  3. Grunt Plugins : GruntJS has plugin architecture wherein most of the tasks that we need are available as plugins. GruntJS plugins can be installed using npm. In this blog, we will use a couple of plugins — grunt-contrib-uglify and grunt-markdown. The complete plugin listing is available here.

The Gruntfile

Now we need to tell GruntJS what tasks it should perform. If we run grunt command in the blog directory, we will see the following error.

$ grunt
A valid Gruntfile could not be found. Please see the getting started guide for
more information on how to configure grunt: http://gruntjs.com/getting-started
Fatal error: Unable to find Gruntfile.

To use Grunt, we have to create a file called Gruntfile.js. Gruntfile specifies tasks which grunt should perform. This file contains the build script.

Create, a new file, called Gruntfile.js in the blog directory and then add the following code to it.

module.exports = function(grunt){

};

This is the boilerplate code we need to start using Gruntfile.

Next we have to configure grunt for the tasks we want to perform. We call initConfig function on grunt and pass it the configuration object. Currently, configuration object is empty.

module.exports = function(grunt){
    grunt.initConfig({

    })    
};

Minification

The first task that we will perform is minification of the application javascript resources. Create a js folder in the blog directory and then create a new file called app.js.

$ mkdir js
$ cd js
$ touch app.js

Open app.js in a text editor and then add the following content to it. The app.js file has two simple methods hello and bye.

function hello(name){
    return "Hello, " + name;
}
function bye(name){
    return "Bye, " + name;
}

Now we will add the uglify task to grunt configuration object.

module.exports = function(grunt) {

  grunt.initConfig({
    uglify: {
      build: {
        src: ['js/app.js'],
        dest: 'js/app.min.js'
      }
    }

  });

  grunt.loadNpmTasks('grunt-contrib-uglify');

  grunt.registerTask('default', ['uglify']);
};

The code shown above does the following :

  1. We configure the uglify task by specifying the source and destination javascript files.
  2. Next we load the grunt-contrib-uglify plugin. Before running the grunt command, make sure plugin is installed. All the grunt plugins can be installed using npm.
  3. Finally, we register the uglify task with default task. The default task is invoked by Grunt when we do not specify any task name i.e. we just run the grunt command.

Now if we run the grunt command we will get the following exception.

>> Local Npm module "grunt-contrib-uglify" not found. Is it installed?
Warning: Task "uglify" not found. Use --force to continue.

Aborted due to warnings.

Execute the command shown below to install grunt-contrib-uglify plugin.

$ npm install grunt-contrib-uglify --save-dev

Now again run the grunt command, and this time we will see the success message.

$ grunt
Running "uglify:build" (uglify) task
File "js/app.min.js" created.

Done, without errors.

It successfully creates the app.min.js file. The app.min.js file is shown below. It has all the white spaces trimmed, function arguments refactored to a single character, and everything is in one line.

function hello(a){return"Hello, "+a}function bye(a){return"Bye, "+a}

Markdown to HTML5

I write blogs in Markdown but publish my blogs on WordPress. I use free hosting from wordpress.com which does not give me the ability to install Markdown plugin. I can use GruntJS to convert my blogs written in Markdown to HTML. Then, I can just copy the HTML and paste in WordPress text editor. Below we can see the trimmed down version of my last blog in Markdown. The full published blog is here.

Today is the fourth day of my challenge to learn 30 technologies in 30 days. So far I am enjoying it and getting good response from fellow developers.The full blog series can be tracked on this <a href="https://whyjava.wordpress.com/30-technologies-in-30-days/" target="_blank">page</a>.

##Application Usecase##
The usecase that we are implementing is similar to Amazon "Customers Who Bought This Item Also Bought these items" feature. 

##Developing Blog Recommender Java Application##
Now that we have created PredictionIO application its time to write our Java application. We will be using Eclipse to develop the application. I am using Eclipse Kepler which has m2eclipse integration built in. Create a new Maven based project by navigating to File &gt; New &gt; Maven Project. Choose the maven-archetype-quickstart and then enter Maven project details. Replace the pom.xml with the one shown below.

Now we will write a class which will insert data into PredictionIO. The class is shown below.

package com.shekhar.blog_recommender;

import io.prediction.Client;
import io.prediction.CreateItemRequestBuilder;

public class BlogDataInserter {

    private static final String API_KEY = "wwoTLn0FR7vH6k51Op8KbU1z4tqeFGZyvBpSgafOaSSe40WqdMf90lEncOA0SB13";

    public static void main(String[] args) throws Exception {
        Client client = new Client(API_KEY);
        addUsers(client);
        addBlogs(client);
        userItemViews(client);
        client.close();

    }

    private static void addUsers(Client client) throws Exception {
        String[] users = { "shekhar", "rahul"};
        for (String user : users) {
            System.out.println("Added User " + user);
            client.createUser(user);
        }

    }

    private static void addBlogs(Client client) throws Exception {
        CreateItemRequestBuilder blog1 = client.getCreateItemRequestBuilder("blog1", new String[]{"machine-learning"});
        client.createItem(blog1);

    }

    private static void userItemViews(Client client) throws Exception {
        client.identify("shekhar");
        client.userActionItem("view","blog1");
        client.userActionItem("view","blog4");
        client.userActionItem("view","blog5");
    }

}

It uses a couple of headings and code snippets. To convert the markdown blog to html, we will use another Grunt plugin grunt-markdown. Install the grunt-markdown by running the command shown below.

$ npm install grunt-markdown --save-dev

Similar to uglify plugin, we have to add grunt-markdown configuration in initConfig function. We specify source and destination locations as well as their extensions.

module.exports = function(grunt) {

  grunt.initConfig({
    uglify: {
      build: {
        src: ['js/app.js'],
        dest: 'js/app.min.js'
      }
    },
    markdown: {
    all: {
      files: [
        {
          expand: true,
          src: '*.md',
          dest: 'docs/html/',
          ext: '.html'
        }
      ]
    }
  }

  });

  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-markdown');

  grunt.registerTask('default', ['uglify','markdown']);
};

Now run the grunt command and we will see day1.html in the docs/html folder.

$ grunt 
Running "uglify:build" (uglify) task
File "js/app.min.js" created.

Running "markdown:all" (markdown) task
File "docs/html/day1.html" created.

Done, without errors.

That is it for today. I will be spending some more time on GruntJS and hopefully will write a couple more blogs on it.

Shameless Plug

If you are a Java, Python, Node.js, Ruby, or PHP developer then you should take a look at OpenShift. OpenShift is a public Platform as a Service, and you can use it to deploy your applications for free.

3 thoughts on “Day 5 : GruntJS : Let Someone Else Do My Tedious Repetitive Tasks”

Leave a comment