2015年3月11日水曜日

node.jsのパッケージ管理ツールでexpressの導入

node.jsをnvm(NodeersionManager)を使ってインストールし、パッケージ管理(npm)を使ってexpress環境を構築してみる。環境は以下の通り。
 
 OS:CentOS6.5(x64)
  nvm:0.24.0
  node.js:0.12.0
  npm:
  forever:
  express:4.12.2

0.環境準備~nvm/node.jsのインストール


$ sudo useradd nodejs
$ sudo su - nodejs
$ sudo yum install git #入っていなかったら入れる
$ git clone git://github.com/creationix/nvm.git ~/.nvm
$ source ~/.nvm/nvm.sh
これでnvmはインストール完了。次はnvmを使ってnode.jsをインストールしてみる。 インストールできる、node.jsのバージョン一覧を確認する。偶数が安定板、奇数が開発版
$ nvm ls-remote

        v0.1.14
        v0.1.15
        v0.1.16
     ・
     ・
     ・
     ・
       v0.11.14
       v0.11.15
       v0.11.16
        v0.12.0

$
ということで最新(2015/3/8 時点:v0.12.0)を入れてみることに。
$ nvm install 0.12.0
######################################################################## 100.0%
Now using node v0.12.0
とても簡単!完了。次に複数インストールした場合のデフォルトで使用するバージョンを設定しておく
$ nvm alias default v0.12.0
default -> v0.12.0
デフォルトでnvmも使えるように設定する
$ vi ~/.bash_profile
・
・
if [[ -f ~/.nvm/nvm.sh ]];
  then source ~/.nvm/nvm.sh
  npm_dir=${NVM_PATH}_modules
  export NODE_PATH=$npm_dir
  export PATH=$NVM_BIN:$PATH
fi

次にnode.jsのサンプルを作成してみる
$ vi example.js
var http = require('http');
 
http.createServer(function (request, response) {
  response.writeHead(200, {'Content-Type': 'text/plain'});
  response.end('Hello World\n');
}).listen(9000, "192.168.0.74");
 
console.log('Server running at http://192.168.0.74:9000/');
実行!
$ node example.js

Server running at http://192.168.0.74:9000/
超簡単・・。ブラウザで表示。

ただこの状態だと、エラーがあるとプロセスがこけてしまい、Webサーバーがダウンすることになる。
試しに、以下のようなサンプルコードを実行した場合、
$ vi example_err.js
var n = 0;
var http = require('http');
http.createServer(function (req, res) {
  if (++n > 4) { a }
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end("result:" + n + "\n");
}).listen(9000, "192.168.0.74");
console.log('Server running at http://192.168.0.74:9000/');
別端末でcurlでアクセスをしてみると、変数"n"がカウントアップされ、5回目に変数"a"を参照しにいき、落ちる。
$ curl http://192.168.0.74:9000
result:1
$ curl http://192.168.0.74:9000
result:2
$ curl http://192.168.0.74:9000
result:3
$ curl http://192.168.0.74:9000
result:4
$ curl http://192.168.0.74:9000
url: (52) Empty reply from server
node.js側はこんな感じ。
$ node example_err.js
Server running at http://192.168.0.74:9000/
/home/nodejs/example_err.js:4
  if (++n > 4) { a }
                 ^
ReferenceError: a is not defined
    at Server.<anonymous> (/home/nodejs/example_err.js:4:18)
    at Server.emit (events.js:110:17)
    at HTTPParser.parserOnIncoming [as onIncoming] (_http_server.js:491:12)
    at HTTPParser.parserOnHeadersComplete (_http_common.js:111:23)
    at Socket.socketOnData (_http_server.js:343:22)
    at Socket.emit (events.js:107:17)
    at readableAddChunk (_stream_readable.js:163:16)
    at Socket.Readable.push (_stream_readable.js:126:10)
    at TCP.onread (net.js:529:20)

これだと実際には使えない・・・ということで、次にnpmを使ってforeverというモジュールを使ってみる。

1.npmを使ってforeverをインストールしてみる


node.jsを入れると、npmもインストールされている。これを使って、foreverモジュールをインストールしてみる。
$ npm install -g forever
"-g"でグローバルインストールとし、全部のアプリで使えるようにしておく。
インストールが終わったら、実際に実行してみる。
$ npm

Usage: npm <command></command>

where <command></command> is one of:
    add-user, adduser, apihelp, author, bin, bugs, c, cache,
    completion, config, ddp, dedupe, deprecate, docs, edit,
    explore, faq, find, find-dupes, get, help, help-search,
    home, i, info, init, install, issues, la, link, list, ll,
    ln, login, ls, outdated, owner, pack, prefix, prune,
    publish, r, rb, rebuild, remove, repo, restart, rm, root,
    run-script, s, se, search, set, show, shrinkwrap, star,
    stars, start, stop, t, tag, test, tst, un, uninstall,
    unlink, unpublish, unstar, up, update, v, verison, version,
    view, whoami

npm <cmd> -h     quick help on <cmd>
npm -l           display full usage info
npm faq          commonly asked questions
npm help <term>  search for help on <term>
npm help npm     involved overview

Specify configs in the ini-formatted file:
    /home/nave/.npmrc
or on the command line via: npm <command></command> --key value
Config info can be viewed via: npm help config

npm@2.1.14 /usr/local/nave/installed/0.10.35/lib/node_modules/npm

$  forever start example_err.js

warn:    --minUptime not set. Defaulting to: 1000ms
warn:    --spinSleepTime not set. Your script will exit if it does not stay up for at least 1000ms
info:    Forever processing file: example_err.js


warningはでたけど、、起動している。先ほどのようにcurlで何度かアクセスすると、resultが1に戻って表示され node.js側は何もエラーがでていない。つまり、node.jsが再起動されて起動し続けているということのようです。 一件落着。
で次に、expressだ。

2.npmを使ってexpressをインストールしてみる


これを使ってexpressをインストールする。
$ npm install -g express

express@4.12.2 /home/nodejs/.nvm/versions/node/v0.12.0/lib/node_modules/express
tqq merge-descriptors@1.0.0
tqq utils-merge@1.0.0
tqq cookie-signature@1.0.6
tqq methods@1.1.1
tqq fresh@0.2.4
tqq cookie@0.1.2
tqq escape-html@1.0.1
tqq range-parser@1.0.2
tqq content-type@1.0.1
tqq finalhandler@0.3.3
tqq vary@1.0.0
tqq parseurl@1.3.0
tqq serve-static@1.9.1
tqq content-disposition@0.5.0
tqq path-to-regexp@0.1.3
tqq depd@1.0.0
tqq on-finished@2.2.0 (ee-first@1.1.0)
tqq qs@2.3.3
tqq etag@1.5.1 (crc@3.2.1)
tqq proxy-addr@1.0.6 (forwarded@0.1.0, ipaddr.js@0.1.8)
tqq debug@2.1.2 (ms@0.7.0)
tqq send@0.12.1 (destroy@1.0.3, ms@0.7.0, mime@1.3.4)
tqq type-is@1.6.0 (media-typer@0.3.0, mime-types@2.0.9)
mqq accepts@1.2.4 (negotiator@0.5.1, mime-types@2.0.9)


インストール完了・・これまたあっけなし。以下のサンプルプログラムでお試し。
$ vi app.js
var express = require('express');
var app = express();

app.get('/',function (req,res){
  res.send('Hello Express!');
});

app.listen(9000,"192.168.0.74");
ブラウザでつついて、ばっちり!あとは追加でexpressのひな形自動作成ツールを追加してみる。 express-generatorというパッケージになる。
$ npm install -g express-generator
/home/nodejs/.nvm/versions/node/v0.12.0/bin/express -> /home/nodejs/.nvm/versions/node/v0.12.0/lib/node_modules/express-generator/bin/express
express-generator@4.12.1 /home/nodejs/.nvm/versions/node/v0.12.0/lib/node_modules/express-generator
tqq sorted-object@1.0.0
tqq commander@2.6.0
mqq mkdirp@0.5.0 (minimist@0.0.8)
次にEJSというビューテンプレートを使ってひな形を作成してみる。
$ express --ejs expressexample

   create : expressexample
   create : expressexample/package.json
   create : expressexample/app.js
   create : expressexample/public
   create : expressexample/public/javascripts
   create : expressexample/public/images
   create : expressexample/public/stylesheets
   create : expressexample/public/stylesheets/style.css
   create : expressexample/routes
   create : expressexample/routes/index.js
   create : expressexample/routes/users.js
   create : expressexample/views
   create : expressexample/views/index.ejs
   create : expressexample/views/error.ejs
   create : expressexample/bin
   create : expressexample/bin/www

   install dependencies:
     $ cd expressexample && npm install

   run the app:
     $ DEBUG=expressexample:* ./bin/www

で作成。ただこれだけだとディレクトリ・ファイルが出来上がっただけなので、記載されいてるとおり、
$ cd expressexample && npm install
でセットアップする。出来上がったものを、これまた記載通りDEBUG付きで実行してみる。
$ DEBUG=expressexample:* ./bin/www

  expressexample:server Listening on port 3000 +0ms
あとはブラウザでアクセスしてみると、ばらばらとデバッグ(トレース)が出てきた。なるへそねぇ。 で、もう少し出来上がった中身を見てみる。
$ vi package.json
{
  "name": "expressexample",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "start": "node ./bin/www"
  },
  "dependencies": {
    "body-parser": "~1.12.0",
    "cookie-parser": "~1.3.4",
    "debug": "~2.1.1",
    "ejs": "~2.3.1",
    "express": "~4.12.2",
    "morgan": "~1.5.1",
    "serve-favicon": "~2.2.0"
  }
}
これはnpmのファイル。6行目の内容は、npm startと打った時に実行される内容。 つまり、
$ npm start
でも実行ができるということですね。あとは依存関係のあるパッケージが記載されている。このpackage.jsonファイル を他の環境に持っていき、npm installをすれば、同じ環境が出来上がる!ということですね。
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

var routes = require('./routes/index');    //(*1)
var users = require('./routes/users');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

// uncomment after placing your favicon in /public
//app.use(favicon(__dirname + '/public/favicon.ico'));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', routes);    //(*2)
app.use('/users', users);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
  app.use(function(err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
      message: err.message,
      error: err
    });
  });
}

// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
  res.status(err.status || 500);
  res.render('error', {
    message: err.message,
    error: {}
  });
});


module.exports = app;

このファイルは、サーバーで使用するミドルウェアを取り込んだり、ルーティングするためのファイルとのこと。 例えば(*2)は http://192.168.0.74/ でアクセスされた場合のルーティング設定で、この行ではroutes変数にアサインされている。で、routes変数に何が入っているかというと、、(*1)に記載されている。 (*1)ではroutes変数に[./routes/index]を割り当てているが、これは./routes/index.jsのことを指している。 ちなみに、このapp.jsファイルを読むと、ルーティングはファイルの上から順に評価されていき、該当したところで止まる(後続は処理されない)という処理のようですね。今後作っていく際のポイントになりそう。
var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});

module.exports = router;
このファイルは上記にあったapp.jsで呼び出されたroutes/index.js。ポイントは6行目。今回テンプレートとしてejsを採用しているので、この行にあるrender関数でejsテンプレートが呼び出される。どのテンプレートかというと、第一引数がindexとなっているので、views/index.ejsだ。このテンプレートを呼び出すにあたり、title変数に、[Express]を設定して、呼び出している。
$ vi views/index.ejs
<html>
  <head>
    <title><%= title %></title>
    <link href="/stylesheets/style.css" rel="stylesheet"></link>
  </head>
  <body>
    <h1><%= title %></h1>
Welcome to <%= title %>


  </body>
</html>
このファイルはhtmlベースのテンプレートとなっており、<%= title %>となっているところに、index.jsのrender関数で指定したtitle編巣の値をセットしている。 今日はここまで・・