Sending push notifications to Android via Gulp

If you ever wondered how to test that your push notifications work on Android without setting up any server here is how to do.

Install

npm install gulp node-gcm

Get Api key, Sender ID and Device ID

In order to be able to send push notifications we need only need the Google Cloud Api key, and the device ID(s).

To get the Api key and the senderID you can register there: https://developers.google.com/mobile/add

As for the device ID it can be found once your device is registered.

If you are using this Cordova plugin https://github.com/phonegap-build/PushPlugin you can get it like this (note that the senderID is used here):

document.addEventListener("deviceready", function() {
    var pushNotification = window.plugins.pushNotification;
    pushNotification.register(
        successHandler,
        errorHandler, {
            "senderID": "replace_with_sender_id (12 digits number)",
            "ecb": "onNotification"
        });

    function onNotification(e) {
        switch (e.event) {
            case 'registered':
                if (e.regid.length > 0) {
                    // Your GCM push server needs to know the regID before it can push to this device
                    // here is where you might want to send it the regID for later use.
                    console.log('Device id', e.regid);
                }
                break;
        }
    }

    function successHandler() {}

    function errorHandler() {}
});

Script

Now that you have both Device ID and Api key let’s create a script that send push notifications.

gulp.task('push:android', function() {
    if (!gutil.env.apiKey || gutil.env.apiKey === true) {
        throw new Error('You must specify the android Api key, refer to the documentation');
    }
    if (!gutil.env.deviceId || gutil.env.deviceId === true) {
        throw new Error('You must specify the android ID, refer to the documentation');
    }

    console.log('apiKey', gutil.env.apiKey);
    console.log('deviceId', gutil.env.deviceId);

    var gcm = require('node-gcm')

    var message = new gcm.Message({
        collapseKey: 'demo',
        delayWhileIdle: true,
        timeToLive: 3,
        data: {
            key1: 'message1',
            key2: 'message2'
        }
    });

    var sender = new gcm.Sender(gutil.env.apiKey);

    sender.send(message, (gutil.env.deviceId instanceof Array) ? gutil.env.deviceId : [gutil.env.deviceId], 5, function(err, result) {
        if (err) {
            console.error('Failed, status code', err);
        } else {
            console.log('Success', result);
        }
    });
})

Run

You can run the script like like this on one or several devices:

# One device
gulp push:android --apiKey YOUR_API_KEY --deviceId YOUR_DEVICE_ID

# Two devices
gulp push:android --apiKey YOUR_API_KEY --deviceId YOUR_DEVICE_ID --deviceId YOUR_DEVICE_ID

If everything is fine you should have your push notifications working.

Commun error

If your api key and senderId are not ok you will get this error message:

Success { multicast_id: 7412383159195664000,
  success: 0,
  failure: 1,
  canonical_ids: 0,
  results: [ { error: 'MismatchSenderId' } ] }

If so regenerate a api key using this link https://github.com/phonegap-build/PushPlugin.

Introduction to Webpack with practical examples

Webpack is taking the task automation market by a storm. I have been using it for months now and for most of my needs Webpack took over Grunt and Gulp.

Webpack is a module bundler, it takes modules with dependencies and generates static assets representing those modules.

This post will only focus about Webpack “loaders” and “post loaders”.

Webpack Loaders

Loaders are pieces of code that can be injected in the middle of the compilation stream. Post loaders are called at the end of the stream.

webpack can only process JavaScript natively, but loaders are used to transform other resources into JavaScript. By doing so, every resource forms a module.

Source available on Github

Prerequisite

You will need to have Node and Npm installed on your machine to go through the following examples.

Install

npm install webpack --save-dev

Now create a webpack.config.js file and dump this basic scaffolding in it:

var webpack = require('webpack'),
    path = require('path');

module.exports = {
    debug: true,
    entry: {
        main: './index.js'
    },
    output: {
        path: path.join(__dirname, 'dist'),
        filename: '[name].js'
    },
    module: {
        loaders: []
    }
};

You main entry point is now index.js at the root of your folder. The compiled file will be dumped in the dist folder when compiling.

Compile

When your entry point is defined you can start the compilation using the CLI:

# Debug mode
webpack

# Production mode (minified version)
webpack -p

ECMAScript 6 compilation

ECMAScript 6 introduce tones of new features (Arrows, Classes, Generators, Modules etc.) that can be used right now! To do so I recommend using Babeljs.

Installation:

npm install babel-loader --save-dev

Add the loader to the Webpack configuration:

loaders: [{
  test: /.es6.js$/,
  loader: "babel-loader"
}]

You now can require any ES6 modules using require('./src/index.es6.js');

Result

Before

// Generators
var fibonacci = {
    [Symbol.iterator]: function*() {
        var pre = 0,
            cur = 1;
        for (;;) {
            var temp = pre;
            pre = cur;
            cur += temp;
            yield cur;
        }
    }
}

module.exports = fibonacci;

After

"use strict";

var fibonacci = function() {
    var a = {};
    a[Symbol.iterator] = regeneratorRuntime.mark(function b() {
        var a, c, d;
        return regeneratorRuntime.wrap(function e(b) {
            while (1) switch (b.prev = b.next) {
              case 0:
                a = 0, c = 1;

              case 1:
                d = a;
                a = c;
                c += d;
                b.next = 6;
                return c;

              case 6:
                b.next = 1;
                break;

              case 8:
              case "end":
                return b.stop();
            }
        }, b, this);
    });
    return a;
}();

module.exports = fibonacci;

CoffeeScript compilation

Coffeescript needs no introduction, it has been popular for a long time now.

Installation:

npm install coffee-loader --save-dev

Add the loader to the Webpack configuration:

loaders: [{
  test: /.coffee$/,
  loader: "coffee-loader"
}]

You now can require any CoffeeScript modules using require('./src/index.coffee');

Result

Before

module.exports = ->
    square = (x) -> x * x
    math =
      root: Math.sqrt
      square: square
      cube: (x) -> x * square x

After

module.exports = function() {
  var math, square;
  square = function(x) {
    return x * x;
  };
  return math = {
    root: Math.sqrt,
    square: square,
    cube: function(x) {
      return x * square(x);
    }
  };
};

Require CSS files

Installation:

npm install css-loader --save-dev

Add the loader to the Webpack configuration:

The css-loader will create a style tag that will be injected into your page on run time. The css-loader also takes care of minification when called in the production mode (-p) e.g webpack -p

loaders: [{
  test: /.css$/,
  loader: "css-loader"
}]

You now can require any CSS file using require('./src/index.css');


Autoprefix CSS files

Installation:

npm install autoprefixer-loader --save-dev

Add the loader to the Webpack configuration:

What’s really annoying with CSS is that some properties are not implemented the same way by browsers. That’s the reason behind prefixes -ms- for IE, -moz- for Firefox and -webkit- for Chrome, Opera and Safari. The autoprefixer loader allow you to use the standards CSS properties without having to care for browser compatibility.

loaders: [{
  test: /.css$/,
  loader: "css-loader!autoprefixer-loader"
}]

You now can require any CSS file using require('./src/index.css');

Result

Before

body {
    display: flex; 
}

After

body {
    display: -webkit-box;      /* OLD - iOS 6-, Safari 3.1-6 */
    display: -ms-flexbox;      /* TWEENER - IE 10 */
    display: -webkit-flex;     /* NEW - Chrome */
    display: flex;             /* NEW, Spec - Opera 12.1, Firefox 20+ */
}

Sass compilation

Sass lets you use features that don’t exist in CSS yet like variables, nesting, mixins, inheritance etc. the code created using sass is less complex and therefore easier for developers to maintain than standard CSS.

Installation:

npm install css-loader sass-loader --save-dev

Add the loader to the Webpack configuration:

Here we use two loaders at the same time. The first one the sass-loader (read from right to left) will compile Sass into CSS then the css-loader will create a style tag that will be injected into your page on run time.

loaders: [{
  test: /.scss$/,
  loader: "css-loader!sass-loader"
}]

You now can require any Sass file using require('./src/index.scss');

Result

Before

$font-stack:    Helvetica, sans-serif;
$primary-color: #333;

body {
  font: 100% $font-stack;
  color: $primary-color;
}

After

body {
  font: 100% Helvetica, sans-serif;
  color: #333;
}

Less compilation

Less is a CSS pre-processor similar to Sass.

Installation:

npm install css-loader less-loader --save-dev

Add the loader to the Webpack configuration:

Here we use two loaders at the same time. The first one the less-loader (read from right to left) will compile Less into CSS then the css-loader will create a style tag that will be injected into your page on run time.

loaders: [{
  test: /.less$/,
  loader: "css-loader!less-loader"
}]

You now can require any Sass file using require('./src/index.less');

Result

Before

@font-stack: Helvetica, sans-serif;
@primary-color: #333;
body {
    font: 100% @font-stack;
    color: @primary-color;
}

After

body {
  font: 100% Helvetica, sans-serif;
  color: #333;
}

Move files

You can move any type of file around by using the file-loader.

Installation:

npm install file-loader --save-dev

Add the loader to the Webpack configuration:

For the example let’s try to move images from their directory to a brand new image folder with a naming convention of img-[hash].[ext].

loaders: [{
  test: /.(png|jpg|gif)$/,
  loader: "file-loader?name=img/img-[hash:6].[ext]"
}]

You now can require any image file using require('./src/image_big.jpg');

Result

the image ./src/img.jpg will be copy and renamed as such: dist/img/img-a4bd04.jpg


Encode files

Sometimes you do not want to make HTTP requests to get assets. For example, what’s the point making HTTP requests to get tiny images when you can directly access them encoded (base64) ? The url-loader does just that. What you need to do is to determine the limit (in bytes) under which you want the encoded version of the file (if the file is bigger you will get the path to it).

Installation:

npm install url-loader --save-dev

Add the loader to the Webpack configuration:

When images are under 5kb we want to get a base64 of them and when they are greater than 5kb we want to get the path to them (exactly as with the file-loader).

loaders: [{
  test: /.(png|jpg|gif)$/,
  loader: "url-loader?limit=5000&name=img/img-[hash:6].[ext]"
}]

Result

Before

var imgBig = '<img src="' + require("./src/image_big.jpg") + '" />';
var imgSmall = '<img src="' + require("./src/image_small.png") + '" />';

After

var imgBig = '<img src="img/img-a4bd04.jpg" />';
var imgSmall = '<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA" />';

Require HTML files

The html-loader turn any html file into a module and require any image dependency along the way!

Installation:

npm install html-loader --save-dev

Add the loader to the Webpack configuration:

loaders: [{
  test: /.html$/,
  loader: "html-loader"
}]

You now can require any HTML files using require('./src/index.html');. All images will also be treated as dependencies and therefore go through their specific stream of events (see Encode files).

Result

Before

<html>
    <head>
        <title></title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width">
    </head>
    <body>
        <img src="./image_small.png">
    </body>
</html>

After

module.exports = '<html>n    
   <head>
      n        
      <title></title>
      n        
      <meta charset="UTF-8">
      n        
      <meta name="viewport" content="width=device-width">
      n    
   </head>
   n    
   <body><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA"></body>
   n
</html>';

Expose any module

The expose-loader loader allow you to bind any module to the global scope.

Installation:

npm install expose-loader --save-dev

Add the loader to the Webpack configuration:

In this example we want lodash (Underscore.js decorator) to be exposed in the global scope as _.

loaders: [{
  test: require.resolve("lodash"),
  loader: 'expose?_'
}]

Now when requiring lodash (require('lodash');) will also expose it globally. It is necessary for popular modules such as angularJs, jQuery, underscore, moment or hammerjs.

AngularJs, Browserify Polymer and Sass boilerplate

Source available on Github : https://github.com/shprink/angularjs-browserify-polymer-boilerplate

Two way data biding

Using Polymer and AngularJs at the same time is a bit problematic. The two way data biding (AngularJs feature) cannot natively work with Polimer web components. Some project are emerging though:

Those projects are not mature enought to use, so I decided to directly use AngularJs’s resources within Polymer elements.

Using AngularJs’s services within Polymer

var userService = angular.injector(['boilerplate']).get('UserService');

Polymer('user-list', {
    ready: function() {
        this.list = userService.getList();
    },
    tapAction: function(e) {
        console.log('tap', e);
    }
});

Using AngularJs’s scope within Polymer

Polymer('boilerplate-login', {
    ready: function() {
        this.angularScope = angular.element(this).scope();
    },
    sendForm: function(){
        this.angularScope.goToHome();
    }
});

Introduction to Gulp.js with practical examples

What is Gulp.js?

Gulp.js is what we call a JavaScript Task Runner, it is Open Source and available on GitHub. It helps you automate repetitive tasks such as minification, compilation, unit testing, linting, etc. Gulp.js does not revolutionize automation but simplifies it tremendously.

Today the Web automation ecosystem is dominated by Grunt.js (which is a great tool BTW) but lately Gulp.js is getting more trending and soon will be overtaking Grunt.js (according to the GitHub popularity, aka “stars”: 7900 for Grunt.js and 6250 for Gulp.js).

How is it better than Grunt or Cakefile?

I would say it is no better nor worse than Grunt or Cakefile, it is different. While Cakefile or Grunt use files to execute tasks, Gulp.js uses streams. It means that a typical Cakefile or Grunt workflow would be to execute a task that dumps a temporary file, than based on this file to execute another task that dumps another temporary file an so on…

With Gulp.js everything happens on the fly using Node’s stream, temporary files are not needed anymore which make it easy to learn, use and enjoy.

Installation

Use the -g options to install Gulp globally on your machine.

sudo npm install -g gulp

Practical examples

Now let’s use Gulp a little bit. Throughout those examples we will discover how to use plugins to create specific tasks such as minifying, concatenate or even linting your code.

Source available on Github

HTML Minification

Using gulp-minify-html

npm install --save-dev gulp-minify-html

gulpfile.js:

// including plugins
var gulp = require('gulp')
, minifyHtml = require("gulp-minify-html");

// task
gulp.task('minify-html', function () {
	gulp.src('./Html/*.html') // path to your files
	.pipe(minifyHtml())
	.pipe(gulp.dest('path/to/destination'));
});

Run:

gulp minify-html

CSS Minification

Using gulp-minify-css

npm install --save-dev gulp-minify-css

gulpfile.js:

// including plugins
var gulp = require('gulp')
, minifyCss = require("gulp-minify-css");

// task
gulp.task('minify-css', function () {
	gulp.src('./Css/one.css') // path to your file
	.pipe(minifyCss())
	.pipe(gulp.dest('path/to/destination'));
});

Run:

gulp minify-css

JS Minification

Using gulp-uglify

npm install --save-dev gulp-uglify

gulpfile.js:

// including plugins
var gulp = require('gulp')
, uglify = require("gulp-uglify");

// task
gulp.task('minify-js', function () {
	gulp.src('./JavaScript/*.js') // path to your files
	.pipe(uglify())
	.pipe(gulp.dest('path/to/destination'));
});

Run:

gulp minify-js

CoffeeScript Compilation

Using gulp-coffee

npm install --save-dev gulp-coffee

gulpfile.js:

// including plugins
var gulp = require('gulp')
, coffee = require("gulp-coffee");

// task
gulp.task('compile-coffee', function () {
	gulp.src('./CoffeeScript/one.coffee') // path to your file
	.pipe(coffee())
	.pipe(gulp.dest('path/to/destination'));
});

Run:

gulp compile-coffee

Less Compilation

Using gulp-less

npm install --save-dev gulp-less

gulpfile.js:

// including plugins
var gulp = require('gulp')
, less = require("gulp-less");

// task
gulp.task('compile-less', function () {
	gulp.src('./Less/one.less') // path to your file
	.pipe(less())
	.pipe(gulp.dest('path/to/destination'));
});

Run:

gulp compile-less

Sass Compilation

Using gulp-sass

npm install --save-dev gulp-sass

gulpfile.js:

// including plugins
var gulp = require('gulp')
, sass = require("gulp-sass");

// task
gulp.task('compile-sass', function () {
	gulp.src('./Sass/one.sass') // path to your file
	.pipe(sass())
	.pipe(gulp.dest('path/to/destination'));
});

Run:

gulp compile-sass

ECMAScript 6 Compilation

Using gulp-babel

npm install --save-dev gulp-babel

gulpfile.js:

// including plugins
var gulp = require('gulp')
, babel = require("gulp-babel");

// task
gulp.task('compile-es6', function () {
	gulp.src('./ES6/one.es6.js')
        .pipe(babel())
	.pipe(gulp.dest('path/to/destination'));
});

Run:

gulp compile-es6

JavaScript Linting

Using gulp-jshint

npm install --save-dev gulp-jshint

gulpfile.js:

// including plugins
var gulp = require('gulp')
, jshint = require("gulp-jshint");

// task
gulp.task('jsLint', function () {
	gulp.src('./JavaScript/*.js') // path to your files
	.pipe(jshint())
	.pipe(jshint.reporter()); // Dump results
});

In case of success:

[gulp] Starting 'jsLint'...
[gulp] Finished 'jsLint' after 6.47 ms

In case of failure:

[gulp] Starting 'jsLint'...
[gulp] Finished 'jsLint' after 5.86 ms
/var/www/gulp-examples/JavaScript/two.js: line 3, col 15, Expected '}' to match '{' from line 3 and instead saw '['.
/var/www/gulp-examples/JavaScript/two.js: line 3, col 16, Missing semicolon.
/var/www/gulp-examples/JavaScript/two.js: line 3, col 154, Expected an assignment or function call and instead saw an expression.

3 errors

Run:

gulp jsLint

CoffeeScript Linting

Using gulp-coffeelint

npm install --save-dev gulp-coffeelint

gulpfile.js:

// including plugins
var gulp = require('gulp')
, coffeelint = require("gulp-coffeelint");

// task
gulp.task('coffeeLint', function () {
	gulp.src('./CoffeeScript/*.coffee') // path to your files
	.pipe(coffeelint())
	.pipe(coffeelint.reporter());
});

In case of success:

[gulp] Starting 'coffeeLint'...
[gulp] Finished 'coffeeLint' after 7.37 ms

In case of failure:

[gulp] Starting 'coffeeLint'...
[gulp] Finished 'coffeeLint' after 6.25 ms

one.coffee
?  line 3  Line contains a trailing semicolon

? 1 error

Run:

gulp coffeeLint

Rename a file

Using gulp-rename

npm install --save-dev gulp-rename

gulpfile.js:

// including plugins
var gulp = require('gulp')
, rename = require('gulp-rename')
, coffee = require("gulp-coffee");

// task
gulp.task('rename', function () {
	gulp.src('./CoffeeScript/one.coffee') // path to your file
	.pipe(coffee())  // compile coffeeScript
	.pipe(rename('renamed.js')) // rename into "renamed.js" (original name "one.js")
	.pipe(gulp.dest('path/to/destination'));
});

Run:

gulp rename

Concatenate files

Using gulp-concat

npm install --save-dev gulp-concat

gulpfile.js:

// including plugins
var gulp = require('gulp')
, concat = require("gulp-concat");

// task
gulp.task('concat', function () {
	gulp.src('./JavaScript/*.js') // path to your files
	.pipe(concat('concat.js'))  // concat and name it "concat.js"
	.pipe(gulp.dest('path/to/destination'));
});

Run:

gulp concat

Add copyright

Using gulp-header

npm install --save-dev gulp-header

Copyright

/*
Gulp Examples by @julienrenaux:

* https://github.com/shprink
* https://twitter.com/julienrenaux
* https://www.facebook.com/julienrenauxblog

Full source at https://github.com/shprink/gulp-examples

MIT License, https://github.com/shprink/gulp-examples/blob/master/LICENSE
*/

gulpfile.js:

// including plugins
var gulp = require('gulp')
, fs = require('fs')
, concat = require("gulp-concat")
, header = require("gulp-header");

// functions

// Get copyright using NodeJs file system
var getCopyright = function () {
	return fs.readFileSync('Copyright');
};

// task
gulp.task('concat-copyright', function () {
	gulp.src('./JavaScript/*.js') // path to your files
	.pipe(concat('concat-copyright.js')) // concat and name it "concat-copyright.js"
	.pipe(header(getCopyright()))
	.pipe(gulp.dest('path/to/destination'));
});

Run:

gulp concat-copyright

Add copyright with version

Using gulp-header and Node’s file system

npm install --save-dev gulp-header

Copyright

/*
Gulp Examples by @julienrenaux:

* https://github.com/shprink
* https://twitter.com/julienrenaux
* https://www.facebook.com/julienrenauxblog

Version: <%= version %>
Full source at https://github.com/shprink/gulp-examples

MIT License, https://github.com/shprink/gulp-examples/blob/master/LICENSE
*/

Version

1.0.0

gulpfile.js:

// including plugins
var gulp = require('gulp')
, fs = require('fs')
, concat = require("gulp-concat")
, header = require("gulp-header");

// functions

// Get version using NodeJs file system
var getVersion = function () {
	return fs.readFileSync('Version');
};

// Get copyright using NodeJs file system
var getCopyright = function () {
	return fs.readFileSync('Copyright');
};

// task
gulp.task('concat-copyright-version', function () {
	gulp.src('./JavaScript/*.js')
	.pipe(concat('concat-copyright-version.js')) // concat and name it "concat-copyright-version.js"
	.pipe(header(getCopyrightVersion(), {version: getVersion()}))
	.pipe(gulp.dest('path/to/destination'));
});

Run:

gulp concat-copyright-version

Mix them up (Lint, Concat, Compile, Minify etc.)

The purpose of this task is to mix the previous tasks into just one.

Copyright

/*
Gulp Examples by @julienrenaux:

* https://github.com/shprink
* https://twitter.com/julienrenaux
* https://www.facebook.com/julienrenauxblog

Version: <%= version %>
Full source at https://github.com/shprink/gulp-examples

MIT License, https://github.com/shprink/gulp-examples/blob/master/LICENSE
*/

Version

1.0.0

gulpfile.js:

// including plugins
var gulp = require('gulp')
, fs = require('fs')
, coffeelint = require("gulp-coffeelint")
, coffee = require("gulp-coffee")
, uglify = require("gulp-uglify")
, concat = require("gulp-concat")
, header = require("gulp-header");

// functions

// Get version using NodeJs file system
var getVersion = function () {
	return fs.readFileSync('Version');
};

// Get copyright using NodeJs file system
var getCopyright = function () {
	return fs.readFileSync('Copyright');
};

// task
gulp.task('bundle-one', function () {
	gulp.src('./CoffeeScript/*.coffee') // path to your files
	.pipe(coffeelint()) // lint files
	.pipe(coffeelint.reporter('fail')) // make sure the task fails if not compliant
	.pipe(concat('bundleOne.js')) // concat files
	.pipe(coffee()) // compile coffee
	.pipe(uglify()) // minify files
	.pipe(header(getCopyrightVersion(), {version: getVersion()})) // Add the copyright
	.pipe(gulp.dest('path/to/destination'));
});

Run:

gulp bundle-one

Tasks automation

Using gulp.watch you can easily automate any tasks when files are modified. It is really convenient because you do not have to run single tasks by hand every time a file is modified, and therefore your code is always up to date.

// including plugins
var gulp = require('gulp');

// task
gulp.task('watch-coffeescript', function () {
    gulp.watch(['./CoffeeScript/*.coffee'], ['compile-coffee']);
});

Run:

Now run the task:

gulp watch-coffeescript

and see what happens when you modify one of the source file.