$30 off During Our Annual Pro Sale. View Details »

node.js Module Development

Jay Harris
September 14, 2013

node.js Module Development

Node.js has given JavaScript a new resurgence as a server-side language. No longer just for image rollovers and AJAX, JS is now available as a platform for creating lightning-fast, lightweight, networked applications. In this session, we will move beyond Node’s base web servers and Twitter applications, and into module development: those small, reusable components that are the foundation for every business application on every platform. Learn how to create a module within Node.js, how to test your module and validate functionality, and how to get your creation distributed into the wild. With this knowledge, you can make the next great Node package and become famous.

Author:
Jay Harris
Problem Solver | Arana Software
[email protected] | www.aranasoft.com
www.twitter.com/jayharris

Jay Harris

September 14, 2013
Tweet

More Decks by Jay Harris

Other Decks in Technology

Transcript

  1. M O D U L E D E V E

    L O P M E N T
  2. Node.js is a platform built on Chrome's JavaScript runtime for

    easily building fast, scalable network applications. “ ” What is node.js?
  3. ./

  4. $  npm  init name: (sample-node) Sample version: (0.0.0) 0.1.0 description:

    This is a sample module entry point: (index.js) _
  5. author:  Jay  Harris license:  (BSD)  BSD About  to  write  to

     /Projects/sample-­‐node/package.json: {    "name":  "Sample",    "version":  "0.1.0",    "description":  "This  is  a  sample  module",    "main":  "index.js",    "scripts":  {        "test":  "echo  \"Error:  no  test  specified\"  &&  exit  1"    },    "author":  "Jay  Harris",    "license":  "BSD" } Is  this  ok?  (yes)  _
  6. ./package.json { "name": "Sample", "version": "0.1.0", "description": "This is a

    sample module", "main": "index.js", "scripts": { "test": "echo \"Error: no tests avail.\" && exit 1" }, "author": "Jay Harris", "license": "BSD" } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
  7. ./lib/person.js function Person(first, last) { if (!(this instanceof Person)) {

    return new Person(first, last); } this.firstName = first; this.lastName = last; return this; } module.exports = Person; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
  8. var person = require("./lib/person.js"); // This will return undefined person.firstName;

    // This will return 'Jay' var jayHarris = new Person('Jay','Harris'); jayHarris.firstName;
  9. function Person(first, last) { // ... } module.exports = Person;

    // This is a public method; Person.prototype.sayHello = function() { return _join.call(this, "Hello", this.firstName); } // This is a private method var _join(first, second) { return first + ' ' + second; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
  10. var  person  =  require("./lib/person.js"); //  This  will  throw  'Has  No

     Method'  error person.sayHello(); //  This  will  return  'Hello  Jay' var  jayHarris  =  new  Person('Jay','Harris'); jayHarris.sayHello();
  11. function Person(first, last) { // ... } module.exports = Person;

    // This is a static method module.exports.sayHello = function() { return "Hello World!"; } // This is a public instance method; Person.prototype.sayHello = function() { return _join.call(this, "Hello", this.firstName); } // This is a private method var _join(first, second) { return first + ' ' + second; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
  12. var  person  =  require("./lib/person.js"); //  This  will  return  'Hello  World!'

    person.sayHello(); //  This  will  return  'Hello  Jay' var  jayHarris  =  new  Person('Jay','Harris'); jayHarris.sayHello();
  13. var EventEmitter = require('events').EventEmitter , util = require('util'); function Person(first,

    last) { // ... EventEmitter.call(this); // ... } util.inherits(Person, EventEmitter); Person.prototype.goToBed = function() { this.emit("sleep"); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
  14. var  person  =  require("./person.js"); //  This  will  return  'Hello  Jay'

    var  jayHarris  =  new  Person('Jay','Harris'); jayHarris.on("sleep",  function()  {        console.log("Goodnight,  Jay"); } jayHarris.goToBed(); //  Output  'Goodnight,  Jay'
  15. TDD is to coding style as yoga is to posture.

    Even when you're not actively practicing, having done so colors your whole life healthier.” j. kerr “
  16. assert(value)            .ok(value)      

         .equal(actual,  expected)            .notEqual(actual,  expected)            .deepEqual(actual,  expected)            .notDeepEqual(actual,  expected)            .strictEqual(actual,  expected)            .notStrictEqual(actual,  expected)            .throws(block,  [error])            .doesNotThrow(block,  [error])            .ifError(value)
  17. var assert = require('assert'); // Will pass assert.ok(true); // Will

    throw an exception assert.ok(false); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
  18. var assert = require('assert'); // Will throw 'false == true'

    error assert.ok(typeof 'hello' === 'number'); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
  19. $  node  test.js assert.js:104    throw  new  assert.AssertionError({    

               ^ AssertionError:  false  ==  true        at  Object.<anonymous>  (my-­‐test.js:7:8)        at  Module._compile  (module.js:449:26)        at  Object.Module._extensions..js  (module.js:467:10)        at  Module.load  (module.js:356:32)        at  Function.Module._load  (module.js:312:12)        at  Module.runMain  (module.js:487:10)        at  process.startup.processNextTick.process._tick... $  _
  20. isTrue,                  

       isFalse, isNull,                      isNotNull, isUndefined,            isDefined, isFunction,              isNotFunction, isArray,                    isNotArray, isBoolean,                isNotBoolean, isNumber,                  isNotNumber, isString,                  isNotString,                                    include,                                    lengthOf,                                    operator,                                    closeTo   isObject,                  isNotObject, typeOf,                      notTypeOf, instanceOf,              notInstanceOf, match,                        notMatch, property,                  notProperty, deepProperty,          notDeepProperty, propertyVal,            propertyNotVal, deepPropertyVal,    deepPropertyNotVal, additional assertions
  21. var assert = require('chai').assert; // Will throw 'expected 'hello' to

    be a number' assert.isNumber('hello'); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
  22. var chai = require('chai') , assert = chai.assert; chai.Assertion.includeStack =

    true; // Will throw and display stack assert.isNumber('hello'); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
  23. expectSyntax expect(person).to.be.an('object');        .with.property('age')        .that.is.a('number')

           .that.equals(34); assert.isObject(person); assert.property(person,  "age"); assert.isNumber(person.age); assert.equals(person.age,  34); assertSyntax
  24. expect(person).to.exist        .and.be.an('object')        .with.property('age')  

         .that.is.to.exist        .and.is.a('number')        .and.equals(34);
  25. expect(person).to.exist        .and.be.an('object')        .with.property('age')  

         .that.is.to.exist        .and.is.a('number')        .and.equals(34);
  26. expect(person).to.exist        .and.be.an('object')        .with.property('age')  

         .that.is.to.exist        .and.is.a('number')        .and.equals(34);
  27. expect(person)    .that.is.an('object')    .with.property('address')        .that.is.an('object')  

         .with.property('city')            .that.is.a('string')            .and.equals('Detroit')
  28. $  npm  install  -­‐g  mocha $  mkdir  test $  mocha

           ✔  0  tests  complete  (1ms) $  
  29. describe('Testing  out  this  thing',  function()  {        it('should

     do  stuff',  function()  {                //  Assertion  tests        }); }); bddSyntax
  30. ./test/my-test.js var assert = require('assert'); describe('Assertions', function() { it('should pass

    on truthiness', function() { assert.ok(true); }); it('should fail on falsiness', function() { assert.ok(false); }); }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
  31. $  mocha  -­‐-­‐reporter  spec    Assertions        ✓

     should  pass  on  truthiness          1)  should  fail  on  falsiness    ✖  1  of  2  tests  failed:    1)  Assertions  should  fail  on  falsiness:              AssertionError:  false  ==  true            at  (stack  trace  omitted  for  brevity) $  _
  32. describe('Testing  out  this  thing',  function()  {        describe('with

     a  subset  of  this  other  thing',  function()  {                it('should  do  stuff',  function()  {                        //  Assertion  tests                });        }); }); groupSyntax
  33. var expect = require('chai').expect; describe('Assertions', function() { describe('on equality', function()

    { it('should pass on truthiness', function() { expect(true).is.true; }); it('should pass on falsiness', function() { expect(false).is.false; }); }); describe('on type', function() { it('should pass on number', function() { expect(5).is.a('number'); }); }); }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
  34. $  mocha  -­‐-­‐reporter  spec    Assertions        of

     equality            ✓  should  pass  on  truthiness              ✓  should  pass  on  falsiness          on  type            ✓  should  pass  on  number      ✔  3  tests  complete  (6ms) $  _
  35. describe('Testing  out  this  thing',  function()  {        describe('with

     a  subset  of  this  other  thing',  function()  {                it('should  do  stuff  someday');        }); }); pendingSyntax
  36. var assert = require('chai').assert; describe('Assertions', function() { describe('of truthiness', function()

    { it('should pass on truthiness', function() { expect(true).is.true; }); it('should pass on falsiness', function() { expect(false).is.false; }); }); describe('of type', function() { it('should pass on number', function() { expect(5).is.a('number'); }); it('should pass on object'); }); }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
  37. $  mocha  -­‐-­‐reporter  spec    Assertions        of

     truthiness            ✓  should  pass  on  truthiness            ✓  should  pass  on  falsiness        of  type            ✓  should  pass  on  number              -­‐  should  pass  on  object    ✔  4  tests  complete  (6ms)    •  1  test  pending $  _
  38. describe('Testing  out  this  thing',  function()  {        it.skip('should

     be  skipped',  function()  {                //  Assertion  tests        }); }); describe.skip('This  entire  suite  will  be  skipped',  function()  {        it('should  do  stuff',  function()  {                //  Assertion  tests        }); }); skipSyntax
  39. describe('Assertions', function() { describe('of truthiness', function() { it('should pass on

    truthiness', function() { assert.isTrue(true); }); it('should pass on falsiness', function() { assert.isFalse(false); }); }); describe('of type', function() { it.skip('should pass on number', function() { assert.isNumber(5); }); it('should pass on object'); }); }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
  40. $  make  test    Assertions        of  truthiness

               ✓  should  pass  on  truthiness              ✓  should  pass  on  falsiness          of  type            -­‐  should  pass  on  number              -­‐  should  pass  on  object    ✔  4  tests  complete  (6ms)    •  2  test  pending $  _
  41. describe('Testing  out  this  thing',  function()  {        before(function(){

                   //  ...        };        describe('with  a  subset  of  that  thing',  function()  {                it('should  do  stuff',  function()  {                        //  Assertion  tests                });                afterEach(function(){                        //  ...                };        }); }); setupTeardown
  42. it('should  not  error',  function(done)  {        search.find("Apples",  done);

    }); it('should  return  2  items',  function(done)  {        search.find("Apples",  function(err,  res)  {                if  (err)  return  done(err);                res.should.have.length(2);                done();        }); }); asynchronousSyntax
  43. describe('When searching for Apples', function(done) { before(function(done){ items.save(['fiji apples', 'empire

    apples'], done); }); it('should not error', function(done) { search.find("Apples", done); }); it('should return 2 items', function(done) { search.find("Apples", function(err, res) { if (err) return done(err); res.should.have.length(2); done(); }); }); }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
  44. ./makefile # Makefile for sample module test: mocha --reporter spec

    --ui bdd .PHONY: test 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
  45. ./makefile # Makefile for sample module test: mocha \ --reporter

    spec \ --ui bdd .PHONY: test 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
  46. $  make  test    Assertions        ✓  should

     pass  on  truthiness          1)  should  fail  on  falsiness    ✖  1  of  2  tests  failed:    1)  Assertions  should  fail  on  falsiness:              AssertionError:  false  ==  true            at  (stack  trace  omitted  for  brevity) $  _
  47. $  npm  test    Assertions        ✓  should

     pass  on  truthiness          1)  should  fail  on  falsiness    ✖  1  of  2  tests  failed:    1)  Assertions  should  fail  on  falsiness:              AssertionError:  false  ==  true            at  (stack  trace  omitted  for  brevity) $  _
  48. $  npm  install  mocha  -­‐-­‐save-­‐dev $  npm  install  chai  

     -­‐-­‐save-­‐dev $  git  diff  package.json   diff  -­‐-­‐git  a/package.json  b/package.json index  439cf44..3609bb9  100644 -­‐-­‐-­‐  a/package.json +++  b/package.json @@  -­‐1,5  +1,7  @@  {      "name":  "sample", -­‐    "version":  "0.1.0" +    "version":  "0.1.0", +    "devDependencies":  { +        "mocha":  "~1.9.0", +        "chai":  "~1.6.0" +    }  }
  49. test: @./node_modules/.bin/mocha \ --reporter spec \ --ui bdd watch: @./node_modules/.bin/mocha

    \ --reporter min \ --ui bdd \ --growl \ --watch .PHONY: test watch 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
  50. var me = {firstName: 'Jay' , lastName: 'Harris' , getFullName:

    function() { return this.firstName + ' ' + this.lastName; }}; // Returns 'Jay Harris' me.getFullName(); me.getFullName = function() { return 'John Doe'; }; // Returns 'John Doe' me.getFullName(); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
  51. var http = require('http'); var reqOptions = { host: 'api.twitter.com',

    path: '/1/statuses/user_timeline.json?' + 'screen_name=jayharris' }; // ... http.request(reqOptions, resCallback).end(); // Returns live Twitter data 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
  52. var nock = require('nock'); var twitter = nock('http://api.twitter.com') .get('/1/statuses/user_timeline.json?'+ 'screen_name=jayharris')

    .reply(200, "This worked"); // Returns "This worked" http.request(reqOptions, resCallback).end(); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
  53. // Returns live Twitter data http.request(reqOptions, resCallback).end(); var nock =

    require('nock'); var twitter = nock('http://api.twitter.com') .get('/1/statuses/user_timeline.json?'+ 'screen_name=jayharris') .reply(200, "This worked"); // Returns "This worked" http.request(reqOptions, resCallback).end(); // Returns live Twitter data http.request(reqOptions, resCallback).end(); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
  54. $  nvm  list        v0.4.0      

     v0.6.18        v0.8.21        v0.4.7          v0.8.1        v0.10.0        v0.6.0          v0.8.8 current:     v0.8.1 10  -­‐>  0.10.0  (-­‐>  v0.10.0) 4  -­‐>  0.4.7  (-­‐>  v0.4.7) 6  -­‐>  0.6.18  (-­‐>  v0.6.18) 8  -­‐>  0.8.21  (-­‐>  v0.8.21) default  -­‐>  0.10.0  (-­‐>  v0.10.0) newest  -­‐>  0.10.0  (-­‐>  v0.10.0) $  _
  55. $  node  -­‐-­‐version v0.10.0 $  nvm  install  v0.10.6 Now  using

     node  v0.10.6 /Users/jayharris/.nvm/v0.10.6/bin/npm $  node  -­‐-­‐version v0.10.6 $  _
  56. {  name:  'morale',    description:  'Async  API  wrapper  for  Morale',

       'dist-­‐tags':  {  latest:  '0.2.0'  },    versions:        [  '0.1.0',          '0.1.2',          '0.2.0'  ],    maintainers:  'jayharris  <[email protected]>',    time:        {  '0.1.0':  '2012-­‐01-­‐23T03:24:59.824Z',          '0.1.2':  '2012-­‐01-­‐25T23:20:52.927Z',          '0.2.0':  '2012-­‐08-­‐13T16:23:28.488Z'  },    author:  'Arana  Software  <[email protected]>',    repository:        {  type:  'git',          url:  'git://github.com/aranasoft/morale-­‐node.git'  },    users:  {  fgribreau:  true  },    version:  '0.2.0',
  57. $  npm  publish npm  http  PUT  https://registry.npmjs.org/morale npm  http  409

     https://registry.npmjs.org/morale npm  http  GET  https://registry.npmjs.org/morale npm  http  200  https://registry.npmjs.org/morale npm  http  PUT  https://registry.npmjs.org/morale/0.2.1/-­‐tag/latest npm  http  201  https://registry.npmjs.org/morale/0.2.1/-­‐tag/latest npm  http  GET  https://registry.npmjs.org/morale npm  http  200  https://registry.npmjs.org/morale npm  http  PUT  https://registry.npmjs.org/morale/-­‐/ morale-­‐0.2.1.tgz/-­‐rev/25-­‐c9bbf49ea0bd2a750e257153fab5794b npm  http  201  https://registry.npmjs.org/morale/-­‐/ morale-­‐0.2.1.tgz/-­‐rev/25-­‐c9bbf49ea0bd2a750e257153fab5794b +  [email protected] $  _
  58. {  name:  'morale',    description:  'Async  API  wrapper  for  Morale',

       'dist-­‐tags':  {  latest:  '0.2.1'  },    versions:        [  '0.1.0',          '0.1.2',          '0.2.0',          '0.2.1'  ],    maintainers:  'jayharris  <[email protected]>',    time:        {  '0.1.0':  '2012-­‐01-­‐23T03:24:59.824Z',          '0.1.2':  '2012-­‐01-­‐25T23:20:52.927Z',          '0.2.0':  '2012-­‐08-­‐13T16:23:28.488Z',          '0.2.1':  '2012-­‐08-­‐30T19:10:20.133Z'  },    author:  'Arana  Software  <[email protected]>',    repository:        {  type:  'git',          url:  'git://github.com/aranasoft/morale-­‐node.git'  },
  59. ./package.json { "name": "Sample", "version": "0.1.0", "description": "This is a

    sample module", "main": "index.js", "scripts": { "test": "echo \"Error: no tests avail.\" && exit 1" }, "author": "Jay Harris", "license": "BSD", "private": "true" } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
  60. $  npm  publish npm  ERR!  Error:  This  package  has  been

     marked  as  private npm  ERR!  Remove  the  'private'  field  from  the  package.json  to   publish  it. $  _
  61. { "name": "morale", "version": "0.2.1", "dependencies": { "underscore": { "version":

    "1.3.3" }, "mocha": { "version": "1.3.0", "dependencies": { "commander": { "version": "0.6.1" }, "growl": { "version": "1.5.1" }, "jade": { "version": "0.26.3", 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
  62. jay harris P R E S I D E N

    T [email protected] #nodemoduledev @jayharris