Unfreeze Your Brain

with Gulp and Slush

Why streams?

What is a task runner?

What is a scaffold?

Why Gulp?

Why Slush?

Gulp.js

Whats a task runner?

Change the way you work

  • Concat, write, delete files
  • Backbone of a modern build system
  • File/code analysis
  • Preprocessors
  • Postprocessors

On in a word...

AUTOMATION

AUTOMATION

Much task.

Very Choice.

Why Gulp?

Whats so special?

(mostly) Objectively speaking

  • Steam Based
  • Sanitary Plugin Ecosystem
  • Power of Npm
  • Modular, sharp-tooled
  • Fast
  • Envy of your friends.

But also...

and in my opinion...

POWER-TOOL

POWER-TOOL

I know what some of you might be thinking...

Hey Wait!

We're a C# Shop!

..we have NUGET

..and VISUAL STUDIO!

 

and like. microsofty..stuff...

Its cool...

(Your home-slice Scott 'Rocket Punch', H, just chillin wearin' a gulp shirt.)

  • Gulp is fore everybody :)
  • Even in non js stacks, communities are requesting it!
  • Your frontend Coders will LOVE it
  • Y0u need client Devops
  • Its not 2006 anymore
  • But seriously: You really need those devops

So if you are in a c# environment.. 

(as many of you may be)

  1. TRX- Task Runner Explorer
  2. NPM/NBower Package Intellisense
  3. Optional Grunt Launcher

Trust your boy, Scott...Get the full scoop here:

www.hanselman.com

Or you can do what the rest of us do...

npm install -g gulp

Streams

"We should have some ways of connecting programs like garden hose--screw in
another segment when it becomes necessary to massage data in
another way. This is the way of IO also."

Doug Mcllroy, 1964

a.pipe(b).pipe(c).pipe(d)
  • IO is async
  • .pipe()
  • Streams are solid architectural foundation
    • (earlier days of unix)
  • pluggable, modular, simple

RTFM

Read the FANTASTIC Manual

https://github.com/substack/stream-handbook

 


Substack wrote this, and its good read even out of the context of gulp, honestly.

npm install -g stream-handbook

Big Whoop.

What can I

REALLY

do?

More like

what 

CAN'T 

you do.

Plugins

This is how you REALLY get it DONE

0. require gulp

1. require plugins

2. declare tasks (more on that later)

3. task order/separate from tasks (queue)

4. call tasks.

var gulp = require('gulp'),
    concat = require('gulp-concat'),
    minify = require('gulp-minify');

var scriptFiles = './src/**/*.js';

function build(){
  gulp
    .src(scriptFiles)
    .pipe(concat({fileName: "funky-town.js"})
    .pipe(minify())
    .pipe(gulp.dest('./dist/'));
}

gulp.task('build', build);

A simple, yet comon scenario.

A BUILD

gulp build
gulp.task('build', build);

If this is your JS file:

This is what you type on the command line:

And then?...

A simple, yet comon scenario.

A BUILD

var gulp = require('gulp'),
    concat = require('gulp-concat'),
    minify = require('gulp-minify');

var scriptFiles = './src/**/*.js';

function build(){
  gulp
    .src(scriptFiles)
    .pipe(concat({fileName: "funky-town.js"})
    .pipe(minify())
    .pipe(gulp.dest('./dist/'));
}

gulp.task('build', build);

1. Glob goes in

2. pipepipe,pipe,

3. ...files go out.

"you can't explain that"

This is only the surface.

  • Automated Tests
  • File Watchers
  • Complexity Analysis (Come see my talk tomorrow)
  • Common JS Build (browserify webpack)
  • Interact with git
  • Integrate with your browser or OS (gulp-app)

USE YOUR IMAGINATION

Gulp is simple.

Just remember:

"I got 5 on it"

The 5

Noble Tasks

.task('name', taskFn)

.run(tasks)

.watch(glob, taskFn)

.src(glob)

.dest(folder)

Flow Control

How To

How To

Stream

3 ways to handle a task

  • Return nothing, like firing an event
  • Return the stream
  • Return a callback

Returning nothing is probably not what you want, because you might introduce

Race Conditions

POWER TIP

Return a stream

Return a callback

var gulp = require('gulp');
var coveralls = require('gulp-coveralls');


function lcov() {
  return gulp
    .src('coverage/**/lcov.info')
    .pipe(coveralls());
}

gulp.task('coveralls', lcov)
var gulp = require('gulp');
var istanbul = require('gulp-istanbul');
var mocha = require('gulp-mocha');
var gutil = require('gulp-util');

function test(cb) {
  function runner() {
    return gulp
      .src(testGlob)
      .pipe(mocha(mochaOpts))
      .pipe(istanbul.writeReports())
      .on('end', cb);
  }

  gulp
    .src('./test-files')
    .pipe(istanbul())
    .pipe(istanbul.hookRequire())
    .on('finish', runner)
    .on('error', gutil.log);
}

gulp.task('test', test);
  • gulp-only stuff
  • simple: just return the task
  • the main "currency" of gulp
  • going outside gulp
  • async stuff
  • freedom

And then?

You can sequence these larger gestures into task chains!

 

They naturally run async, but you can use plugins to handle things further.

  • Vanilla parallel task chains
  • Sequences chains with run-sequence
  • merging streams!

Basic Gulp task chain :

parallel

gulp.task('lots-of-stuff', ['task-1', 'task-2', 'task-3']);

run-sequence

  var gulp = require('gulp'),
      runSequence = require('run-sequence');

  function runAllTasks(cb) {
    runSequence('pretasks', 'posttask', cb);
  }
  gulp.task('pretasks', ['clean']);
  gulp.task('posttask', ['build', 'lint', 'test']);
  gulp.task('default', runAllTasks);
// npm install --save-dev gulp merge-stream

var gulp = require('gulp');
var merge = require('merge-stream');

gulp.task('test', function() {
  var bootstrap = gulp.src('bootstrap/js/*.js')
    .pipe(gulp.dest('public/bootstrap'));

  var jquery = gulp.src('jquery.cookie/jquery.cookie.js')
    .pipe(gulp.dest('public/jquery'));

  return merge(bootstrap, jquery);
});

Combining Streams

gulp merge-stream

SLUSH

Scaffolding made simple.

+

=

Ask questions

Do stuff

And that's Slush.

Scaffolding Rocks

  • Kickstart new project
  • Standardize practices
  • Increase productivity
  • and of course...

AUTOMATION

Did I mention?

Hey, hold up!

What about that other scaffolding system?

I used that a few times and my scaffolding needs already taken care of

 

why do I even need this???

Why you might choose slush

  • More separate concerns
  • Plugin ecosystem
  • Generators/subgenerators = TASKS
  • Simpler system
  • No need to introduce additional
npm install -g slush
npm install -g gulp
npm install -g slush-generator
slush generator

Easy to use

Easy to make

1. think of questions

2. make a slushfile

3. call your generator

A basic slush task

// your callback

function callback(answers){
    gulp
      .src(__dirname + '/templates/**')
      .pipe(template(answers))
      .pipe(conflict('./'))
      .pipe(gulp.dest('./'))
      .pipe(install())
      .on('end', function () {
        done();
      });
}
//every inquirer statement is made like this.
inquirer.prompt( questions, callback )

Ask questions

Do stuff

What do questions look like?

var prompts = [{
  name: 'appName',
  message: 'What is the name of your project?',
  default: defaults.appName
  }, {
  name: 'appDescription',
  message: 'What is the description?'
  }, {
  name: 'appVersion',
  message: 'What is the version of your project?',
  default: '0.1.0'
  }, {
  name: 'authorName',
  message: 'What is the author name?',
  default: defaults.authorName
  }, {
  name: 'authorEmail',
  message: 'What is the author email?',
  default: defaults.authorEmail
  }, {
  name: 'userName',
  message: 'What is the github username?',
  default: defaults.userName
  }, {
  type: 'confirm',
  name: 'moveon',
  message: 'Continue?'
  }];
? What is the name of your project? ballz
? What is the description? Some description
? What is the version of your project? 6.6.6
? What is the author name? Jesse Harlin
? What is the author email? harlinjesse@gmail.com
? What is the github username? Jesse_Harlin
? Continue? Yes
{
  appName: 'ballz',
  appDescription: 'Some description',
  appVersion: '6.6.6',
  authorName: 'Jesse Harlin',
  authorEmail: 'harlinjesse@gmail.com',
  userName: 'Jesse_Harlin',
  moveon: true
}

BUT WILL IT TEST?

TEST

You're darn tootin it will!

  • Mock responses from inquirer with fixture
  • Mock file writing with mock-gulp-dest
  • Mock environmental things with rewire

Test every little bit.

'use strict';
var inquirer = require('inquirer');

function mockPrompt(answers) {
  
  function assignAnswer(prompt) {
    if (!(prompt.name in answers)) {
      answers[prompt.name] = prompt.default;
    }
  }
  function inquirerPrompt(prompts, done) {
    [].concat(prompts).forEach(assignAnswer);
    done(answers);
  }
  inquirer.prompt = inquirerPrompt;
}

module.exports = mockPrompt;

Inquirer Fixture

var mockPrompt = require('./inquirer-prompt-fixture');
require('../slushfile');
var chai = require('chai'), //chai for assertion
  gulp = require('gulp'), 
  mockGulpDest = require('mock-gulp-dest')(gulp), //here is our dest stub
  expect = chai.expect; //I use expect, but I dont expect(you).to.also()

var mockPrompt = require('./inquirer-prompt-fixture'); //this is to mock inquirer

require('../slushfile'); //and the thing we are testing

describe('your awesome module!!!', function () {
  describe('taskname', function () {
    beforeEach(function () { 
      mockPrompt({
        appName: 'test-app',
        userName: 'the-simian',
        authorName: 'Fancypants Harlin',
        authorEmail: 'derp@derp.derp',
        appDescription: 'some description',
        moveon: true
      });
    });
    
    it('should make a readme', function (done) {
      function assertDirectories() {
        mockGulpDest.assertDestContains('README.md');
        done();
      }
      gulp
        .start('default')
        .once('task_stop', assertDirectories); //here's the tricky part....
    });
  });
});

simiansblog.com

jesseharlin.net

@5imian