2012年12月23日 星期日

解決Node.js非同步問題... node-promise模組的使用


Node.js的優點:Event Driven, Non-Block非常強悍,但是同時也造成許多寫作上的不便...
這邊主要想解決在非同步的模組中,想要循序執行的問題... node-promise是不錯的解決方案

安裝:
npm install node-promise

情境:
希望可以在抓取一個網頁完成之後,再執行某個動作...
語意上可以翻譯做:當抓取網頁Event完成後,才去驅動下一個Event

使用:
var p = new require("node-promise").Promise()
  , request = require('request'); 
function step1(fn){ //包裝request的動作
  request({
    url : "http://www.google.com",
    method : "GET"
  },
  function(e,r,d){
    console.log('>>1');
    p.resolve(d);  // 註冊當動作完成時候的Listener
  });
}
step1();  // 執行step1()
p.then( //當動作完成時候,即會執行then所定義的動作
  function(result){ //定義收取結果後的操作
    console.log('>>2');
    console.log(result.substring(0,50));
  },
  function(err){ //定義收到錯誤時候的操作
    console.log(err);
  }
)

執行結果:
# node test-promise
>>1
>>2  // 2 會在 1 之後執行
<!doctype html><html itemscope="itemscope" itemtyp  // 最後列出前request結果的50個字元

除了promise,該套件另外有提供defered與when的操作,可以讓流程更加豐富...
但是,說實在的...整個操作並沒有簡化到哪邊去... 甚至比較麻煩...
不過,透過promise的定義,對event附加Listener的方式倒是可以解決許多callback操作的問題...
還是個值得多多了解的套件!


callee and caller

簡介一下callee與caller
  • callee為function arguments的一個操作,用來代表function本身
  • caller為function呼叫者,若為最上層呼叫
下面是簡單的操作,將callee與caller的值列印出來:

由fn2操作fn1,最上層呼叫直接呼叫fn2

function fn1(){
  console.log('>>fn1');
  console.log('my callee:'+arguments.callee);
  console.log('my caller:'+fn1.caller);
}
function fn2(){
  console.log('>>fn2');
  fn1();
}
fn2();

執行結果:
# node test 
>>fn2
>>fn1
my callee:function fn1(){
  console.log('>>fn1');
  console.log('my callee:'+arguments.callee);
  console.log('my caller:'+fn1.caller);
}
my caller:function fn2(){
  console.log('>>fn2');
  fn1();
}


如果想要測試最上層直接呼叫fn1的狀況,修改程式碼如下:

function fn1(){
  console.log('>>fn1');
  console.log('my callee:'+arguments.callee);
  console.log('my caller:'+fn1.caller);
}
fn1();

則結果為:
# node test
>>fn1
my callee:function fn1(){
  console.log('>>fn1');
  console.log('my callee:'+arguments.callee);
  console.log('my caller:'+fn1.caller);
}
my caller:function (exports, require, module, __filename, __dirname) { function fn1(){
  console.log('>>fn1');
  console.log('my callee:'+arguments.callee);
  console.log('my caller:'+fn1.caller);
}
fn1();

}

其中callee的應用,可以用在loop的程式中,強迫程式同步進行...
ex:
var count = 5;
process.nextTick(function fn1(){
  count --;
  console.log('>>fn1, ts=%s, count=%s', new Date().getTime(), count);
  if(count > 0) process.nextTick(arguments.callee);
});

執行結果:
# node test 
>>fn1, ts=1356282477274, count=4
>>fn1, ts=1356282477280, count=3
>>fn1, ts=1356282477281, count=2
>>fn1, ts=1356282477281, count=1
>>fn1, ts=1356282477281, count=0

這邊的output就可以看得出來function fn1會依序被執行,如果有非同步動作,則可以在callback中使用同樣手法讓程序變成循序執行的...

另外,caller的應用,直覺想到的是debug...
寫個簡單的log程式如下,希望可以記錄錯誤是由哪個function發出來的:
var log = {
  info: function fn(msg){
    console.log("Call from [%s]: %s", fn.caller.name , msg);
  }
}
function doSomething() {
  log.info('test');
}
doSomething();


執行結果:
node test
Call from [doSomething]: test



2012年12月21日 星期五

在Bash中混用Node.js程式

一般使用Node.js寫CLI或是使用Bash寫Server端腳本,都是單獨使用
頂多把JS包成完整的指令來使用...
但是,其實您可以透過node -e的方式來動態將Bash的參數輸入Node.js...
下面這段程式只是demo怎麼把Bash中的參數傳入Node.js中做執行,而且,不用再另外開一個JS檔案...

檔案:[test.sh]
#!/bin/bash                                                                                                                                                                                            
export UNAME=`uname -a`
node -e "
  var uname = '$UNAME';
  var arr = uname.split(' ');
  for(var i = 0 ; i< arr.length ; i++){
    console.log(arr[i])
  }
"

執行:
# bash test.sh
 
以上,簡單的程式可以混用Bash與Node.js :D

使用Node接收Shell pipe資料做處理

撰寫command line程式時候,常常需要處理pipe的問題
而Node.js具備命令列執行的特色,可以作為腳本語言的撰寫工具
但是,如何在Node.js處理Shell pipe過來的資料呢?
答案是使用process.openStdin()...
下面是範例片段:

程式碼:[test.js]
#!/usr/bin/env node
var buffer = '';
var stdin = process.openStdin();
stdin.setEncoding('utf8');
stdin.on('data', function (chunk) {
    buffer += chunk;
});
stdin.on('end', function () {
  console.log(buffer);
});

修改執行權限:

# chmod u+x test.js

測試
# echo HELLO | ./test.js

結果... 自己試試 :D
 

2012年12月11日 星期二

Using log4js in your project...

永遠的Console Log不會是一個聰明程式設計師的選擇
Node.js的一套log套件,可以讓程式裡面的Log機制表達更完整
他包含可以針對log分等級、分類、建立不同形式的log(console, file, mail...)、幫你切割log大小、幫你House Keeping...
其中等級部分:

TRACE > DEBUG > INFO > WARN > ERROR > FATAL

其中,TRACE為最囉唆模式,顯示的LOG會包含Debug, Info, Warn, Error, Fatal等,越往右邊,所包含顯示項目越少...
下面是使用上的一些紀錄:

Install:
# npm install log4js
npm http GET https://registry.npmjs.org/log4js
npm http 304 https://registry.npmjs.org/log4js
npm http GET https://registry.npmjs.org/async/0.1.15
npm http 304 https://registry.npmjs.org/async/0.1.15
log4js@0.5.6 ../node_modules/log4js
└── async@0.1.15


File: logger.js 
/**
 * Usage: 
 * var logger = require('./logger').getInstance();
 * logger.debug('TEST...123');
 */
var log4js = require('log4js')
  , logger = log4js.getLogger()
  , logFile = process.env.LOGPATH ? process.env.LOGPATH : '/tmp/node.log'
  , logCategory = process.env.LOGCATG ? process.env.LOGCATG : 'normal'
  , logLevel = process.env.LOGLEVEL ? process.env.LOGLEVEL : 'DEBUG'
  , logMaxSize = process.env.LOG_MAX_SIZE ? process.env.LOG_MAX_SIZE : 20480
  , logBackup = process.env.LOG_BACKUP ? process.env.LOG_BACKUP : 7;
log4js.configure(
{
  "appenders": [
    { type: 'console' },
    {
      "type": "file",
      "filename": logFile,
      "maxLogSize": logMaxSize,
      "backups": logBackup,
      "category": logCategory
    }
  ]
}
);
var logger = log4js.getLogger(logCategory);
logger.setLevel(logLevel);
exports.getInstance = function() {
  return logger;
}

File: test.js
var logger = require('./logger').getInstance();
logger.trace('[trace]....');
logger.debug('[debug]
....');
logger.info('[info]
....');
logger.warn('[warn]
....');
logger.error('[error]
....');
logger.fatal('[fatal]
....'); 

執行上,針對不同等級,有使用不同顏色來表示,上面程式將log以環境變數來設定,如果設定上環境變數,就可以清楚知道Log的等級:

Trace mode:


Debug mode:

Info mode:


Warn mode: 


Error mode:


Factal mode:



2012年12月6日 星期四

MySQL v2.x connector for Node.js

Node.js連線MySQL部分,目前我常用的是mysql這個module
可透過npm install mysql取用...

而最近2.x版本釋出後,發現連線方式有修改了:

var db_options = { 
    host: 'your.database.ip.address',
    port: 3306,
    user: 'username',
    password: 'password',
    database: 'db_name'
};

/* mysql@2.0.0-alpha4連線設定 */
var mysql = require('mysql')


exports.db = mysql.createConnection(db_options);       

如果專案中不確定會用到哪個版本時候
可以這樣寫:

if ( mysql.createClient ) { //v0.9.x
  db = mysql.createClient(db_options);
} else if ( mysql.createConnection ){ //v2.x
  db = mysql.createConnection(db_options);
} else { //more early
  db = new mysql.Client(db_options);
  db.connect(function(err) {
      if ( err ) {
          console.error('connect db ' + db.host + ' error: ' + err);
          process.exit();
      }
  });
}

2012年11月27日 星期二

Using mysql in Node.js

人人都有一套 ORM,
補充一下, JavaScript 其實有支援 Getter, Setter,

另外,根據不可靠消息,看一下各種不同 MySQL Node.js client 效能差異。



Using mysql in Node.js

寫SQL的人通常都會遇到一次一個table有很多欄位的狀況
傳統的方式是透過sql的prepare statement
再透過一個一個的set來設定那些問號(prepare statement中的值帶入方式,一般都以"?"來代表有值耀輸入)
有別於傳統,在物件導向程式語言中可以透過setter的方式來建立欄位
再透過像Hibernate的資料庫framework將資料一次鍵入DB
但Node.js中則沒那麼多規則...
但是如果要處理一堆問號跟參數的傳入,又不想要眼花...可以透過一些"方法"...


目的在提供一個可以重複利用的模組,讓任何require進來的程式可以直接操作 
# vi sql.js
var util = require('util');                                                                                                                                            
function buildSql(params){
  var sql = "insert into xxxx (%s) values (%s)";
  var k = '', v = '';
  var arr = Object.keys(params);
  var cond = [];
  for(var i = 0 ; i < arr.length ; i++){
      k += arr[i] ;
      v += '?';
      if(i < arr.length -1){
        k += ',';
        v += ',';
      }
      cond.push(params[arr[i]]);
  }
  var finalsql = util.format(sql,k,v)
  return {sql:finalsql, cond:cond};
}
exports.buildSql = buildSql;


實作測試
# testSql.js
var sql = require('./sql');                                                                                                                                            
var opt = {}
opt["k1"] = 'v1';
opt["k2"] = 'v2';
var s = sql.buildSql(opt);
console.log(JSON.stringify(s));


結果輸出
# node testSql.js 
{"sql":"insert into xxxx (k1,k2) values (?,?)","cond":["v1","v2"]}


延伸這樣的組和參數與欄位的方式
也可以開發一套lib作為Node.js的Hibernate喔∼

2012年11月26日 星期一

透過某種"方式"讓Node.js程式順序執行

var exec = require('child_process').exec;

var arr = [1,2,3,4,5,6,7];
                      
function doit(v){
  exec('echo ' + v, function(err, stdo, stde){
    console.log('-->' + stdo);
    if(arr.length >0) //如果array還有值,則再call一次本身
      doit(arr.pop());
  });
}

doit(arr.pop());

執行結果:
# node test.js 
-->7
-->6
-->5
-->4
-->3
-->2
-->1

2012年11月25日 星期日

Node.js的forEach迴圈非循序進行

Node.js的forEach迴圈非循序進行:

var arr = [1,2,3,4,5,6,7]

arr.forEach(function(v,i){
  exec('echo ' + arr[i], function(err, stdo, stde){
    console.log('-->' + stdo);
  });
});

執行1:
# node test.js 
-->6
-->5
-->4
-->3
-->2
-->1
-->7

執行2:
# node test.js 
-->5
-->4
-->3
-->2
-->1
-->7
-->6

2012年11月20日 星期二

使用smartos建立node.js之ssl web server

下面展示使用micloud smartos建立node.js之ssl web server
smartos在建立起來時候,會自動產生一組selfsigned.pem的key
可以省去建立openssl的時間...
設定node.js https時候,可以直接將key與cert都指定在selfsigned.pem上
透過該key提供ssl需要的認證金鑰...

Step 1:
# express ssltest && cd ssltest && npm install

Step 2:
# vi app.js

var express = require('express')
  , routes = require('./routes')
  , https = require('https') //使用node.js https模組
  , fs = require("fs")
  , path = require('path');

//設定https需要使用的options參數
var key = '/opt/local/etc/openssl/private/selfsigned.pem';
var crt = key; 
var privateKey = fs.readFileSync(key);
var certificate = fs.readFileSync(crt);
var options = {
    key: privateKey,
    cert: certificate,
};

var app = express();

app.configure(function(){
  app.set('port', process.env.PORT || 1330);
  app.set('views', __dirname + '/views');
  app.set('view engine', 'jade');
  app.use(express.favicon());
  app.use(express.logger('dev'));
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(app.router);
  app.use(express.static(path.join(__dirname, 'public')));
});

app.configure('development', function(){
  app.use(express.errorHandler());
});

app.get('/', routes.index); //express default root

//建立一個測試route
app.get('/test', function(req, res){
  res.writeHead(200);
  res.end('TEST...');
});

//使用https來建立server, 建立過程中帶入options參數
https.createServer(options, app).listen(app.get('port'), function(){
  console.log("Express server listening on port " + app.get('port'));
});

Step 3 - 測試:
# node app.js

打開Browser連線:https://your.ip.address:1330/test



參考:


2012年11月3日 星期六

NodeJS connect Couchdb

CouchDB是NoSQL資料庫的一支,由Apache維護...(這是我選他的最大原因)
預設CouchDB提供RESTful server,並由該Server作為所有API的接口
同時Apache也有許多專案實作接入CouchDB的各市程式語言的API

本篇以想使用Node.js連接Couchdb的部分為教學
假設大家已經有Node.js以及Couchdb的服務...

安裝Cradle套件
# npm install cradle

設定cradle連線couchdb的connection
  db = new(cradle.Connection)(db_address, db_port, {
    auth: { username: dbusername, password: dbpassword },
    cache: true,
    raw: false
  }).database(databasename);
上述建構連線資料庫的基本資訊,包含資料庫位置、port號、帳號、密碼等資訊。

而Couchdb的新刪改查說明如下:

新增一筆資料,id=PK,doc=欲儲存文件,callback=儲存結束後回傳:
db.save(id, doc, callback);

刪除一筆資料,id=PK,rev=版本號碼,callback同上:
db.remove(id, rev, callback);

修改一筆資料,id=欲修改之資料id,doc=欲修改的文件內容,callback同上:
db.merge(id, doc, callback);

查詢一筆資料,id=欲查詢的資料id,callback為查詢回傳,包含查詢結果:
db.get(id, callback);


下面為一個整體的資料庫查詢範例,您可以修改內部的一些實作來變成專案中處理CouchDB的幫手∼

var util = require('util')                                                                                                                                
  , cradle = require('cradle')
  , dbusername = 'your_db_username'
  , dbpassword = 'your_db_password'
  , databasename = 'your_db_name'
  , db_address = 'your.db.address'
  , db_port = 'your_db_port' //default: 5984
  , db = new(cradle.Connection)('http://' + db_address, db_port, {
    auth: { username: dbusername, password: dbpassword },
    cache: true,
    raw: false
  }).database(databasename);

//如果要做到查詢模組∼
exports.queryExample = function(id, callback) {
console.log('Query of %s', id);
db.get( id, function (err, doc) {
if(err) console.log(err);
callback(err, doc);
});
}
/*
ex: sample data: {_id:"simon","_rev":"3-f84647507bd83e29a6deae7c377a74dc","first_name":"simon","last_name":"su"}
this.queryExample('simon',function(err, doc){
...
})
*/

//如果要做到查尋View∼
exports.queryViewExample = function(id, callback) {
console.log('Query of %s', id);
db.view( 'domain/userDomain',{key: userid}, function (err, doc) {
if(err) console.log(err);
callback(err, doc);
});
}
/*
ex: sample data: [{key:"simon", value:{....}},{key:"jelly", value:{....}},{key:"kimi", value:{....}}...]
this.queryViewExample('simon',function(err, doc){
...
})
*/

//如果要建立一筆資料∼
exports.createExample = function(id, doc, callback) {
db.save(id, doc, function(err, res){
if(err) console.log(err);
callback(err, res);
});
}
/*
ex: sample input doc={name:"simon",phone:"0953609275"}
this.createExample(id, doc, function(err, res){
...
});
*/

//如果要更新一筆資料∼
exports.updateExample = function(id, doc, callback) {
db.merge(id, doc, function (err, res) {
if(err) console.log(err);
callback(err, res);
});
}
/*
ex: sample update data id=key_of_row, doc={...}
this.updateExample(id, doc, function(err, res){
...
});
*/


/** 其他範例:
//Create Database:
var db = c.database('database_name');
db.create();

//Delete Database
db.destroy(callback);

更多範例請參考:https://github.com/cloudhead/cradle
**/
  

2012年10月1日 星期一

Using temp view in cradle

當使用cradle作為node連線couchdb時候
要用temp view來動態撈取資料可參考此篇

這邊是希望doc._id欄位字串中有帶入給定參數的
就撈出來呈現...

var util = require('util')                                                                                                                                               
  , cradle = require('cradle')
  , db = new(cradle.Connection)('http://xxx.xxx.xxx.xxx', 5984, {
    auth: { username: dbusername, password: dbpassword },
    cache: true,
    raw: false
  }).database('dbname');

//Write module
exports.test = function() {
  db.temporaryView({
    //If you want to bring a parameter into tmp view, you need to also have "" in your parameter
    map: 'function (doc) { if (doc && doc._id && doc._id.indexOf("'+ params +'") >=0 ) emit(doc._id, doc); }' 
  }, function (err, res) {
    if (err){
      console.log('[ERROR]:'+err);
    }   
    callback(err, res);
  }); 
}

//Use it
this.test();

需要注意的是,temporaryView中帶入的是json參數,由json帶入map function
因此最好將整個map function定義成變數
然後要帶入的變數就可以夾在中間...只是要注意到變數的兩邊要給他雙引號起來
因為轉成字串時候,需要有雙引號,才會是單純的字串,而不會又變回變數...

2012年7月23日 星期一

NPM using proxy

在使用NPM指令時候,如果剛好在個有Proxy的區域
常常npm會有問題,Linux的使用者會這麼做:

export HTTP_PROXY=http://your.proxy.ip.address:port

讓程是預設吃這個位置的proxy設定
但是Windows的使用者一般都是到網路管理界面設定...
這部分NPM連線就有問題
作法很簡單,Windows中的NPM也吃HTTP_RPOXY這個參數
所以只要這樣做:

set HTTP_PROXY=http://your.proxy.ip.address:port

就可以正常使用NPM了...

當然∼要確認NPM的repository是否可以辨識得到(這部分是跟DNS有關)


如果不小心被DNS漏掉,可以加到static route去(/etc/hosts或C:\Windows\system32\drivers\etc\hosts)
他的IP可以這樣查:

nslookup registry.npmjs.org 8.8.8.8

其中8.8.8.8是Google的DNS服務...將查到的IP寫到static route去就可以摟∼

2012年7月17日 星期二

JSON object in node.js

在Node.js/JavaScript中
針對JSON物件可以用這樣的方法循序倒出來

$ node
> var json = { "aa":1, "bb":2,"cc":3}
> for(key in json){
... console.log(key);
... }
aa
bb
cc

2012年6月5日 星期二

Apache官方支援的Node.js API for CouchDB

Apache官方支援的Node.js API for CouchDB : cradle
下面是實作範例
Shcema設計:
  • key的部分固定使用uuid + @ + name:
  • 另外一個附屬欄位description作為文件描述

實作:
var util = require('util');
var cradle = require('cradle');

//連線資訊設定
var port = 5984;
var options = {
  cache : true,
  raw : false,
  auth: { username: 'username', password: 'password' }
};

//開啟DB
var db = new(cradle.Connection)(url, port, options).database('snapshot_logger');

/**
  * 取得document
  */
exports.getSnapshotDescript = function(uuid, name, callback){
  var snapname = util.format('%s@%s',uuid, name);
  db.get(snapname, function (err, doc) {
    var result = {}
    if(err){
      result.msg = 'error';
      result.error = err;
    }else {
      result.msg = 'success';
      result.doc = doc;
    }

    callback(result);
  });
};

/**
 * 儲存或更新一筆資料
 */
exports.saveSnapshotDescript = function(uuid, name, descript, callback) {
  var snapname = util.format('%s@%s',uuid, name);
  var data =
    {
      snapname: snapname,
      uuid: uuid,
      descript: descript
    };

  var result = {};
  db.save(snapname,
    data,
    function (err, res) {
      if (err) {
        result.msg = 'error';
        result.err = err;
      } else {
        result.msg = 'success';
        result.doc = res;
      }
      callback(result);
  });
};

/* 刪除一筆資料 */
exports.delSnapshotDescript = function(uuid, name, callback) {
  var snapname = util.format('%s@%s',uuid, name);
  var result = {};
  this.getSnapshotDescript(uuid, name, function(result){
    if(result.doc) {
      db.remove(snapname, function (err, res) {
        console.log(res);
      });
    } else {
      result.msg = 'warning';
      result.log = util.format('[Warning]Doc not found of _id:%s',snapname);
    }
    callback(result);
  });
}



使用方式:
/* Save or update document */
this.saveSnapshotDescript(uuid,name,'TEST 1234', function(result){
  console.log('Something done...');
  console.log('Save result.... %s', JSON.stringify(result));
}); 

/* Delete Document */
this.delSnapshotDescript(uuid,name, function(result){
  console.log( JSON.stringify(result));
}); 

/* Get Document */
this.getSnapshotDescript(uuid, name, function(res) {
  console.log('Get result:' + JSON.stringify(res));
});

2012年5月31日 星期四

使用原生的http request進行post form呼叫

使用原生的http request進行post form呼叫:

var qs = require('querystring');
var http = require('http');

// Base authentication
var username = 'your_username';
var password = 'your_password';
var auth = 'Basic ' + new Buffer(username + ':' + password).toString('base64');

// The connection parameters
var opts = {
 host:"your.server.ip.address",
 port: 80,
 method: 'POST',
 path: "/your_path",
 headers :
  {
    "Authorization": auth ,
    "Content-Type":"application/x-www-form-urlencoded; charset=utf-8",
    "Content-Length": 0
  }
};

// Example function
exports.your_function = function(param1, ..., callback){
  // Process your business logic for params...
  // The post form parameters
  var form_params = {
    field1 : "value 1",
    field2 : "value 2",
    ...
  };

  var params = qs.stringify(form_params);
  opts.headers['Content-Length'] = params.length;

  var req = http.request(opts, function(res){
    console.log('STATUS: ' + res.statusCode);
    console.log('HEADERS: ' + JSON.stringify(res.headers));
    res.setEncoding('utf8');
  });

  req.on('response', function(res){
    res.body = '';
    res.setEncoding('utf-8');
    res.on('data', function (chunk) {
      res.body += chunk;
    });
    
    // Trigger the callback
    res.on('end', function(){
      callback(res.body, res);
    });
  });

  req.on('error', function(e) {
    console.log('problem with request: ' + e.message);
  });

  // write data to request body
  req.write(params);
  req.write('data\n');
  req.end();
}

// Usage of sample function
this.your_function(
    param1, ..., 
    function(body, res){
      console.log(body);
      console.log('>>>>>>>>>>>'+res.headers['Content-Type']);
    }
);

使用request套件進行http post request

使用request套件進行http post request,範例中始用到HTTP Base Authentication,如無需要,可移除Headers中的Authorization部分敘述:

1. 安裝request套件
npm install request

2. 範例執行檔案
var request = require('request');
var qs = require('querystring');

// Base Authentication
var username = 'yourname';
var password = 'yourpasswd';
var auth = 'Basic ' + new Buffer(username + ':' + password).toString('base64');

// Base url of you will request (include protocol, address, port, path and the lastest of '?')

// Your form fields 
var params = {
  field_A : "value a",
  field_B : "value b",
  headers: { "Authorization": auth }
}

// Append query string to your url
url += qs.stringify(params);

// Your customize function
exports.your_function1 = function(param1,..., callback) {
  //process params...
  request.post(url, params, function (e, r, body) {
      if(callback)
        callback(e, r, body);
  });
}

// Usage of your customize function:
this.your_function1(param1, ..., function(e,r,body) {
  //Process your responses
  //ex:
  console.log(body);
  console.log(r.headers['Content-Type']);
});

2012年5月7日 星期一

The Smallest Web Server Demo

Node.js官網上常常掛著Node.js的Simplest Web Server Demo
這邊把它修改一下,頁面加上顯示IP資訊
方便做為雲端多主機部署程式時檢查之用

var http = require('http');
var os = require('os');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('My Address: '+JSON.stringify(os.networkInterfaces().net0[0].address));
}).listen(1337);
console.log('Server running at http://127.0.0.1:1337/');

連線時,只要瀏覽器列打上 http://your_ip_address:1337
就可以看到如下圖的資訊



其中network interface的顯示依平台與設定而不同,在Mac上顯示資訊如下:

{ lo0: 
   [ { address: 'fe80::1', family: 'IPv6', internal: true },
     { address: '127.0.0.1', family: 'IPv4', internal: true },
     { address: '::1', family: 'IPv6', internal: true } ],
  en0: 
   [ { address: 'fe80::1240:f3ff:fe8b:a9bc',
       family: 'IPv6',
       internal: false },
     { address: '192.168.1.106', family: 'IPv4', internal: false } ] }

而筆者的另一個平台顯示如下:

 [root@simon001 ~/TestPrj]# node
> var os = require('os');
> os.networkInterfaces()
{ lo0: 
   [ { address: '127.0.0.1',
       family: 'IPv4',
       internal: true },
     { address: '::1',
       family: 'IPv6',
       internal: true } ],
  net0: 
   [ { address: '211.78.255.31',
       family: 'IPv4',
       internal: false } ] }

因此,如果要實作不同平台的程式,可要注意這些差異喲∼ 

2012年5月3日 星期四

Node.js using prototype

雖然跟prototype不熟,不過知道他是JavaScript中重要的角色
Node.js可以直接使用喔∼
下面是一個範例,提供讓string物件直接有endsWith的function...
您可以直接放在程式碼中,或是放到library裡面再require進來

String.prototype.endsWith = function(suffix) {
    return this.indexOf(suffix, this.length - suffix.length) !== -1;
};

使用方法: 直接在string物件的後面.endsWith就可以摟∼

var str = 'abc123';
if(str.endsWith('123')) {
  console.log('Yes, endsWith "123"!');
}

Node.js send mail example

無法查看此摘要。請 按這裡查看文章。

2012年5月1日 星期二

Node.js for loop...

Node.js的基本語法也繼承了javascript的寫作方法
下面展示一些for的寫作方式

第一種是基本型拉,但是值得注意的是在宣告i的時候,可以給定var變數宣告,也可以直接不要用
for ( i=0 ; i < 5 ; i++ ){
  console.log(i);
}

結果
$ node 003-for2.js 
0
1
2
3
4


第二種是將array的遞增(取出來的不是array的值,而是順序)直接指派給i變數,然後在for裡面取出i來
for ( i in process.argv ){
  console.log('-->' + i + '=' + process.argv[i]);
}

結果:
$ node 003-for.js a b c d e
-->0=node
-->1=/Users/simonsu/Workspaces/NodeWS/Sample/003-for.js
-->2=a
-->3=b
-->4=c
-->5=d
-->6=e

另外,也可以直接針對array使用forEach的方式遞迴
var aa = [1,2,3,4,5];
aa.forEach(function(t){
  console.log('>'+t);
})

上面forEach其實是array物件的一個function
而這個function接受一個callback物件的傳入
我門可以透過callback取出每一次遞回的array主體中的一個值
然後再進行處理...
也因為接受callback
某些情況我們可以把callback抽離出來,改成這樣:

var aa = [1,2,3,4,5];
var callback = function(i) {
  console.log('>'+i);
}
aa.forEach(callback);

執行結果:
$ node 003-forEach.js 
>1
>2
>3
>4
>5




2012年4月29日 星期日

Reading local filesystem

Node.js的功能涵蓋相當完整,這邊介紹Node.js的filesystem讀取:

var fs = require('fs')
var files = fs.readdirSync('.');
for ( i in files ) {
  console.log(files[i]);
}

fs是Node.js的標準模組
相關文件可以參考:http://nodejs.org/api/fs.html
建立fs物件之後,可以使用readdirSync()來讀取某個path的列表
回傳值為該folder下面的物件(檔案、資料匣)的列表






2012年4月28日 星期六

Basic of Node.js - Non-Block

Node.js以JavaScript為語法結構,同樣繼承JavaScript的Non-Block特性,下面範例說明在Non-Block的程序中,同樣print一筆資料,順序性並非程式碼中行號的順序,而是每個程序執行的回應時間

Ex1:

setTimeout(function(){
  console.log("foo...");
}, 1000);
console.log("bar...");

結果:
$ node 000-nonblock.js
bar...
foo...

由上面結果可以看到foo程序雖然在bar程序之前,但是因為foo需要等待1000毫秒,所以會再bar程序之後印出
為了讓執行時間瞬間被列出,修改上面程式如下:

Ex2: (000-nonblock3.js)
function ffoo(){
  log('Start foo...');
  setTimeout(function(){
    log("foo...");
  }, 1000);
  log('End foo...');
}
function fbar(){
  log('Start bar...');
  log('bar...');
  log('End bar...');
}
function log(msg) {
  console.log("["+new Date()+"]"+msg);
}  
//真正執行區塊
ffoo(); //
fbar();
結果:
$ node 000-nonblock3.js
[Sun Apr 29 2012 00:44:53 GMT+0800 (CST)]Start foo...
[Sun Apr 29 2012 00:44:53 GMT+0800 (CST)]End foo...
[Sun Apr 29 2012 00:44:53 GMT+0800 (CST)]Start bar...
[Sun Apr 29 2012 00:44:53 GMT+0800 (CST)]bar...
[Sun Apr 29 2012 00:44:53 GMT+0800 (CST)]End bar...
[Sun Apr 29 2012 00:44:54 GMT+0800 (CST)]foo...
SimonAIR:Sample simonsu$ vi 000-nonblock3.js 

由上面時間可以很清楚看出ffoo()中的"Start foo..."訊息在程式一開始即已列出,接著馬上執行"End foo...",然後"Start bar..."
這意味著setTimeout function中要列印出的"foo..."被Non-Block丟出一個執行序去執行了
而該執行蓄因為要等待1000毫秒才回應,所以程序先跑下面的部分

另外關於"Start bar...", "bar...", "End bar..."部分照順序被列出來,則是非Non-Block程式碼的順序性所致

在Node.js / JavaScript的世界裡,Block與Non-Block的交互使用有不少好處,卻也會惹出不少麻煩,慎用阿!