## page was renamed from NodeJS
= NodeJS =
Node.js is a platform built on Chrome's [[Javascript]] runtime for easily building fast, scalable network applications. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications that run across distributed devices.

[[http://www.nodejs.org/]]

http://blog.4psa.com/the-callback-syndrome-in-node-js/

== SlackBuild Slackware64 14.0 ==
 * su
 * cd /tmp
 * wget http://slackbuilds.org/slackbuilds/14.0/network/node.tar.gz
 * tar xvzf node.tar.gz
 * cd node
 * wget http://nodejs.org/dist/v0.10.12/node-v0.10.12.tar.gz
 * ./node.SlackBuild 
 * installpkg /tmp/node-0.10.12-x86_64-1_SBo.tgz 

Package 64 bit: [[attachment:node-0.10.12-x86_64-1_SBo.tgz]]

== Sample app ==

main.js
{{{#!highlight javascript
var assert = require('assert'); // TDD
var fs = require('fs'); // file system
var util = require('util');
//--------------------
var Dummy = require('./dummy.js');
var Local = require('./local.js');
var conf=require('./config.js') // Configuration

console.log( conf.configX['foo'] );
console.log( conf.configX['bar'] );

var d1 = new Dummy(12345);
var d2 = new Dummy(67890);
var d3 = new Dummy(112233);

assert.equal(d1.methodA.call(d2),67890,'');
assert.equal(d1.methodA.call(d3),112233,'');
assert.equal(d3.methodA.call(d1),12345,'');

var l1 = new Local('local1',10,11);
var l2 = new Local('local2',20,21);
var l3 = new Local('local3',30,31);

assert.equal(l1.getPoint().getX(),10,'');
assert.equal(l2.getPoint().getX(),20,'');
assert.equal(l3.getPoint().getX(),30,'');

// async readdir
fs.readdir('/tmp', cbReaddir);

function cbReaddir(err,files){
    for(var i=0; i< files.length;i++){
	console.log(files[i]);
    }
}

//async append file
fs.appendFile('/tmp/test.txt', 'data to append\r\n', cbAppendFile);

function cbAppendFile(err) {
    if (!err) {
	console.log('The "data to append" was appended to file!');
    }
}

function hexcounter(value,callback){
    console.log('HC:',value);
    if(value<15){
      setImmediate(function(){callback(value+1,callback);});
    }
    else{
      setImmediate(function(){callback(0,callback);});
    }
}

function octalcounter(){
    var timeout=500;
    if(arguments.length===1){
	console.log('OC:',arguments[0]);
        setTimeout(octalcounter,timeout,1,2)
    }

    if(arguments.length===2){
        var a0=Number(arguments[0]);
	var a1=Number(arguments[1]);
	console.log(util.format('%s %d %d','OC:',a0,a1));
	setTimeout(octalcounter,timeout,a0+1,a1+1);
    }
}

hexcounter(2,hexcounter);
octalcounter(12300);

process.on('SIGHUP', onSIGHUP);
function onSIGHUP() {
  console.log('SIGHUP received',arguments.length);
}

process.on('SIGTERM', onSIGTERM);
function onSIGTERM() {
  console.log('SIGTERM received',arguments.length);
}
}}}

config.js
{{{#!highlight javascript
exports.configX=
    {
        "foo":"fooValue",
        "bar":123
    }
}}}

dummy.js
{{{#!highlight javascript
function Dummy(value1) {
    var self = this;
    self._value1 = value1;
}

Dummy.prototype.methodA = function() {
    var self = this;
    return self._value1;
};

module.exports = Dummy;
}}}

point.js
{{{#!highlight javascript
function Point(x,y){
    this.x=x;
    this.y=y;
}

Point.prototype.getX=function(){
    return this.x;
};

Point.prototype.getY=function(){
    return this.y;
};

module.exports = Point;
}}}

local.js
{{{#!highlight javascript
var Point = require('./point.js');

function Local(name,x,y){
    this.name = name;
    this.point = new Point(x,y);
}

Local.prototype.getName=function(){
    return this.name;
};

Local.prototype.getPoint=function(){
    return this.point;
};

module.exports = Local;
}}}

== node-gyp ==
node-gyp is a cross-platform command-line tool written in Node.js for compiling native addon modules for Node.js.

https://github.com/TooTallNate/node-gyp

=== Install with npm ===
 * su
 * npm install -g node-gyp
 * node-gyp

=== Hello world gyp ===
Based on https://github.com/joyent/node/tree/master/test/addons/hello-world
 * cd /tmp/ 
 * nano test.js
 * nano binding.cc
 * nano binding.gyp
 * node-gyp configure #The configure step looks for the binding.gyp file in the current directory
 * node-gyp build # gave an error building !

Based on https://github.com/rvagg/node-addon-examples
 * nano binding.gyp 
{{{
{
  "targets": [
    {
      "target_name": "hello",
      "sources": [ "hello.cc" ]
    }
  ]
}
}}}
 * nano hello.cc
{{{#!highlight cpp
#include <node.h>
#include <v8.h>

using namespace v8;

Handle<Value> Method(const Arguments& args) {
  HandleScope scope;
  return scope.Close(String::New("world"));
}

void init(Handle<Object> exports) {
  exports->Set(String::NewSymbol("hello"),
      FunctionTemplate::New(Method)->GetFunction());
}

NODE_MODULE(hello, init)
}}}
 * nano hello.js
{{{#!highlight javascript
var addon = require('./build/Release/hello');

console.log(addon.hello()); // 'world'
}}}
 * node hello.js 

Build script
{{{#!highlight bash
#!/bin/sh
GYP=/usr/local/lib/node_modules/npm/bin/node-gyp-bin/node-gyp
$GYP clean
$GYP configure
$GYP build

}}}

== Read text file example ==
{{{#!highlight javascript
var fs = require('fs'); // file system

function cbReadFile(err, data) {
	if (err) {
		throw err;
	}
	var ds = data.toString(); // data is of type Buffer
	var buffer = '';
	for ( var idx = 0; idx < ds.length; idx++) {
		if (ds[idx] != '\n') {
			buffer = buffer + ds[idx];
		} else {
			console.log(buffer);
			buffer = '';
		}
	}

}

fs.readFile('/etc/passwd', cbReadFile);
}}}

== Context with this and bind ==

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.

{{{#!highlight javascript
/*Constructor function*/
function Counter(){	
    this.counter=0;
}

Counter.prototype.run=function(){
    console.log('counter:',this.counter);
    this.counter++;
    //recall run
    setTimeout(this.run.bind(this),1000);
};

var c = new Counter();
setTimeout( c.run.bind(c) ,1000); // apply context of c to this
}}}

== Install from source Node 4.6.0 ==
{{{
wget https://nodejs.org/dist/v4.6.0/node-v4.6.0.tar.gz
cp  node-v4.6.0.tar.gz /tmp
cd /tmp
tar xvif node-v4.6.0.tar.gz
cd node-4.6.0
./configure
make clean
make
make install
}}}

== Install node 6.9.1 on slack64 14.2 ==
{{{
cd /tmp
wget https://slackbuilds.org/slackbuilds/14.2/development/nodejs.tar.gz
tar xvzf nodejs.tar.gz
cd nodejs 
wget https://nodejs.org/dist/v6.9.1/node-v6.9.1.tar.xz
./nodejs.SlackBuild 
installpkg /tmp/nodejs-6.9.1-x86_64-1_SBo.tgz 
}}}

== testApp with mongoose ==
app.js  
{{{#!highlight javascript
var mongoose = require('mongoose');
var http = require('http');
var ut = require('./utils')
var cfg = require('./config')
    
var dummyModel = mongoose.model('Dummy', mongoose.Schema( { foo: String }) );

mongoose.connect( cfg.getMongoURL() );    

mongoose.connection.once('open', function(){
  ut.log('Connected to ' + cfg.getMongoURL() );
  var dummy = new dummyModel({ foo: 'Connected to MongoDB ' +  new Date().toISOString()  });
  dummy.save();
});

function requestListener(req, res) {
    if (req.method === ut.HTTP_METHOD_POST) {
        var body = '';

        req.on('data', function(chunk) {
            body += chunk;
        });

        req.on('end', function() {
	    ut.log('Received URL ' + req.url );
            if (req.url === '/') {
                ut.log('Received message: ' + body);
            } else if (req.url === '/scheduled') {
                ut.log('Received task ' + req.headers['x-aws-sqsd-taskname'] + ' scheduled at ' + req.headers['x-aws-sqsd-scheduled-at']);
            }
            else if (req.url === '/addDummy') {
	        var dummy = req.headers['dummy'];
	        ut.log('Received dummy ' + dummy );	        
                dummyModel.create({ foo: dummy });
            }
            
            res.writeHead(ut.HTTP_200_OK, 'OK', ut.getTextPlain() );
            res.end();
        });
    } 
    else {
        res.writeHead(ut.HTTP_200_OK);
        res.write( ut.getIndex() );
        res.end();	
    }
}

var server = http.createServer(requestListener);
// Listen on port 3000, IP defaults to 127.0.0.1
server.listen( cfg.getPort() );
// Put a friendly message on the terminal
ut.log('Server running at http://127.0.0.1:' + cfg.getPort() + '/');
ut.showEnvironmentVars();
}}}

config.js  
{{{#!highlight javascript
// Constructor function
function Config(){  
};

//Static 
Config.getPort = function() {
  return process.env.PORT || 8081;
};

Config.getTestVar = function() {
  return process.env.TESTVAR || '????';
};

Config.getMongoURL = function() {
  return process.env.MONGOURL || '????';
};

module.exports = Config
}}}

utils.js
{{{#!highlight javascript
var fs = require('fs');

// Constructor function 
function Utils() {
};

// Constants 
Utils.HTTP_METHOD_POST='POST';
Utils.HTTP_METHOD_GET='GET';
Utils.HTTP_200_OK=200;

// Static
Utils.showEnvironmentVars= function() {
    Utils.log('## Environment variables ##');
    for(var key in process.env){
        Utils.log('Environment variable ' + key + '==' + process.env[key] ); 
    }  
};

Utils.log = function(entry) {
    var msg = new Date().toISOString() + ' - ' + entry + '\n';
    fs.appendFileSync('/tmp/sample-app.log', msg);
    console.log(entry);
};

Utils.getIndex = function() {
  return fs.readFileSync('index.html');
};

Utils.getTextPlain = function() {
  return {'Content-Type': 'text/plain'};   
};

module.exports = Utils
}}}

package.json 
{{{#!highlight javascript
{
  "name": "Elastic-Beanstalk-Sample-App",
  "version": "0.0.1",
  "private": true,
  "dependencies": {
    "mongoose":"latest"
  },
  "scripts": {
    "start": "node app.js"
  }
}
}}}

index.html 
{{{#!highlight html
<!DOCTYPE html>
<html>
  <head>
    <title>Elastic Beanstalk VB test</title>
    <style>
    </style>
  </head>
  <body>
    <div class="textColumn">
      <h1>Congratulations</h1>
      <p>Your first AWS Elastic Beanstalk Node.js application is now running on your own dedicated environment in the AWS Cloud</p>
    </div>
    <div class="linksColumn">
      <h2>What's Next?</h2>
    </div>
  </body>
</html>
}}}
 * npm install
 * MONGOURL=mongodb://aaaaaa npm start
 * MONGOURL=mongodb://aaaaaa npm run-script start
 * curl -k -X POST http://127.0.0.1:8081/addDummy --header "dummy:valueLocal"