【入門向け】Node.js + Express4 + MongoDB + Vagrant + foreverでChatWorkと連動するCRUDアプリを開発する。(３)（開発編）

前回：[【入門向け】Node.js + Express4 + MongoDB + Vagrant + foreverでChatWorkと連動するCRUDアプリを開発する。(２)（環境編）](http://qiita.com/bitai/items/26e63ddab3c44b5920e1)

##MVCパターンを構成する。

ここからはExpress4のMVCパターンについて説明していきます。冒頭の説明の通り、ControllerとViewはそれぞれroutesとviewsが担当し、Modelは開発者が定義します。

### View
まずは一番簡単なViewです。viewsフォルダ直下のindex.ejsを見るとこのようになっています。

```html:views/index.ejs
<!DOCTYPE html>
<html>
<head>
  <title><%= title %></title>
  <link rel='stylesheet' href='/stylesheets/style.css' />
</head>
<body>
  <h1><%= title %></h1>
  <p>Welcome to <%= title %></p>
</body>
</html>
```

stylesheetのリンクはpublicディレクトリ直下のstylesheets/style.cssを指します。他にtitleという変数の式がありますが、これは従来のMVC同様、Controllerから受け取った変数です。

勘所はテンプレートエンジンで、node.jsでは多くのテンプレートエンジンから自分が好きな書式を選ぶことができます。テンプレートエンジンの種類でレンダリングの速度に差があり、node-benchというモジュールでベンチマークを取ることができます。

[node-benchでjsテンプレートエンジンのベンチマークをとってみた](http://dev.classmethod.jp/server-side/language/node-bench-js-template-engine/)

使えるテンプレートエンジンは

* ejs
* ect
* jade(expressのデフォルトテンプレートエンジン)
* echo
* coffeekup
* haml-js

など。変更したければnpm install {templateエンジン名}でモジュールをインストールした後、app.jsを編集する必要があります。

```js:app.js
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');//ejsを変える
```

###Controller

routes/index.jsを見てみましょう。

```js:routes/index.js
 var express = require('express');

 var router = express.Router();

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

 module.exports = router;
```

GETメソッドでhttp://<ローカルホスト>:3000/にアクセスがあれば、res.render('index', { title: 'Express' });、つまりindexというViewをtitleという変数に値をセットしてレンダリングするという意味になります。

例えば「/edit, /view, /delete」などで所定の動作を実現したければ、以下のように記述します。

```js:routes/index.js
 /* edit (GET) */
 router.get(‘/view', function(req, res) {
   //何かの処理
   res.render(‘view', { title: 'Express' }); //view.ejsをレンダリング
 });

 /* edit  (POST) */
 router.post(‘/edit', function(req, res) {
   //何かの処理
   res.render(‘index', { title: 'Express' }); //index.ejsをレンダリング
 });

 /* delete (POST) */
 router.get(‘/delete', function(req, res) {
   //何かの処理
   res.render(‘index', { title: 'Express' }); //index.ejsをレンダリング
 });
```

このような処理を記述したら、忘れてならないのはapp.jsにルーティングを追記することです。

```js:routes/index.js
app.js
app.use('/', routes);
app.use(‘/edit', routes);
app.use(‘/delete', routes);

app.use('/users', users);
```

ただし、これだけだと

/edit/13

などのようにパスに何らかの情報を含ませた時に情報が取得できません。その時はroutes/index.jsのapp.getメソッドを以下の様に変更します。

```js:routes/index.js
app.get(‘/edit/:id', function(req, res){
    res.send('user ' + req.params.id);
});
```

正規表現を使うこともできます。詳しい利用方法は下記の記事をご参照ください。

ルーティング
http://hideyukisaito.github.io/expressjs-doc_ja/guide/#routing

###Model

いよいよModelです。
まずはmongoにアプリケーション用のデータベースを作成します。

```shell:
$mongo
> use testDB
> exit
bye
```

testDBというDBが無ければ自動で生成されます。次に、mongoをnode.jsから利用するために[mongoose](http://mongoosejs.com/)というモジュールをインストールします。

```shell:
npm install mongoose
...
shell:

mongooseがインストールできたら、modelを作成しましょう。トップディレクトリに/modelディレクトリを用意し、そこにposts.jsを作成し、Postモデルを定義します。

```shell:
$vim posts.js
```

```js:model/posts.js
var mongoose = require('mongoose');
var db = mongoose.connect('mongodb://localhost/testDB');

function validator(v) {
    return v.length > 0;
}

var Post = new mongoose.Schema({
  name   : {
    type: String, validate: [validator, "Empty Error"]
  },
  price : {
    type: Number,
    default: 500
  },
  order_user : {
    type: Array, //user_nameの配列を格納
    default: []
  },
  created: {
    type: Date,
    default: Date.now
  }
});

exports.Post = db.model('Post', Post);
```

1,2行目がデータソースに、４行目以降がモデルの定義に該当します。最後にapp.jsに以下を追記してください。

```js:model/posts.js
//mongooseの読み込み
 var mongoose = require( 'mongoose' );

//Modelの設定
 var model = require('../model/posts');
 var Post = model.Post;
```

拡張子が無視されるので../model/postsで../model/posts.jsを意味します。ここで注意して頂きたいのが「./」で、Expressでは「./」はapp.jsがいるディレクトリを指します。

[Cannot find module in Nodejs](http://stackoverflow.com/questions/20446514/cannot-find-module-in-nodejs)

以上のように定義できればマイグレーションはExpressとMongooseが上手くやってくれます。ControllerでModelを参照しましょう。まずは仮データをMongoに入力します。

```shell:
$ mongo
> use testDB
> db.posts.insert({name:'test1', price: 400})
> db.posts.insert({name:'test2', price: 500})
> db.posts.insert({name:'test3', price: 500})
> exit
```

次にコントローラ(routes)を編集します。

```js:routes/index.js
var model = require('../model/posts');
var Post = model.Post;

router.get('/view', function(req, res){
   Post.find({}, function(err, items){
     res.render('index', { title: 'メニューの表示', items:items})
   });
});
```

これでitemsにコレクションが追加されているので、view側で出力します。

```js:views/index.ejs
<ul>
     <% items.forEach(function(item){ %>
          <li><%= item.name %>, <%= item.price %> <li>
     <% }) %>
</ul>
```

サーバを起動し、確認します。

```shell:
$ npm start
```

http://192.168.33.17:3000

リストが表示されていれば成功です。

##CRUD機能を追加する。

MVCの流れが分かったので、次にMongooseを使ったCRUDを実装していきます。

###Create

Createはsaveメソッドを使います。フォームのネーム値とコレクションのフィールド名を参照し、一致していればinsertします。

```js:
router.post('/create', function(req, res) {
  var newPost = new Post(req.body);
  newPost.save(function(err){
    if (err) {
      console.log(err);
      res.redirect('back');
    } else {
      res.redirect(‘/');
    }
  })
})
```
###Read
Readにはfindメソッドを使います。

```js:
router.post('/edit', function(req, res){
  Post.find({}, function(err, items){
    if (err) {
       console.log(err);
       res.redirect('back');
     } else {
        res.render(‘/‘, { title: ‘タイトル’, items: items});
     }
  });
});
```

findメソッドは第一引数で検索条件を指定できます。

例
{_id: 100} => where id = 100 と同じ意味

Date型の検索の場合、Dateクラスを使います。

例 今日以前の日付が欲しい場合

```js:
var now = new Date(); //=> 現在の時間
var today = new Date(now.getFullYear(), now.getMonth(), now.getDate()); =>今日の0:00
Post.find({created: {$lte: today}}, function(err, items){ //=> lt = littler than, gt = greater than, eはequal.
     //処理
})
```

###Update
updateメソッドはupdateメソッドを使います。注目すべきはupdateメソッドの第三引数で、trueにしておくとupsert（あればupdate、無ければinsert）になります。

```js:
router.post('/edit', function(req, res) {
    Post.update({ _id: req.param("id") }, { $set: {text: req.param("text"), price: req.param("price")}, false },
      { upsert: false, multi: false }, function(err) {
        if (err) {
          console.log(err);
          res.redirect('back');
        } else {
          res.redirect('/');
        }
      }
    )
})
```

###Delete
最後はremoveメソッドです。これは特に説明しません。

```js:
router.post('/delete', function(req, res) {
    Post.remove({ _id: req.param("id") }, function(err) {
        if (err) {
          console.log(err);
          res.redirect('back');
        } else {
          res.redirect('/');
        }
    })
})
```

##node-requestモジュールで外部APIを叩く。

外部APIにリクエストを送りたくなったらnode-requestを使います。

```shell:
$ npm install request
```

ここではChatworkAPIを使い、サーバ起動時にnodeからChatworkに「サーバを立ち上げました。」とチャット投稿します。

```shell:
var request = require('request');
var room_id = "------"
var Token = "-----"
var options = {
  url: 'https://api.chatwork.com/v1/rooms/' + room_id + 'messages',
  method: 'POST',
  headers: {
  'X-ChatWorkToken': Token
  },
  form: {
    body:messageBody
  }
};
request(options, function(error, response, body) {
     if(!error){
         console.log(error);
     }
})
```

ChatWorkAPI ドキュメント
http://developer.chatwork.com/ja/

##node-cronモジュールでcronを稼働させる。

サーバを立ち上げた時に告知するだけではアプリとして面白くないので、cronを使って定期的にchatworkを巡回し、特定のコマンドがあれば投稿する簡単なBotアプリを作ることとします。この記事では細かく言及はしませんが、このようなアプリを作る時に活躍するのが、nodeのcronモジュールであるnode-cronです。

```shell:
var job = new myCron(
  '* * 9-20 * * *', //一般的なcronの書式
  function(){
     //成功したときの処理
  },
  function(){
     //This function is executed when the job stops
  },
  false, //new宣言したときに実行するか否か
  'Asia/Tokyo' //time zone
);
job.start();
```

第5引数のtime zoneはnode-timeというモジュールが無ければ使えません。なのでnode-cronでTimeZoneを設置するときはnpm installを忘れないでください。

##foreverでプロセスのデーモン化

最後に、foreverによるデーモン管理を説明します。デーモンとはプロセスを監視し、変更やトラブルがあれば自動で再起動などをするプログラムを指します。

node.jsを使う際、良く使われるのがforeverというツールです。先の開発環境準備でforeverはインストール済みなので、アプリのルートパスに行ったらforeverでnpm startを監視させます。npm startはpackage.jsonを見るとnode ./bin/wwwを意味しているので、以下のようになります。

```shell:
$ forever start  ./bin/www
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: ./bin/www
```

以上のように出れば成功です。

http://(-IPaddress-):3000/

にアクセスし、表示される事を確認してください。

##終わりに

今回はExpressを用いて簡易的なCRUDアプリを作成しました。デフォルトのExpressだと上記のように少し面倒なMVCになりますが、Yeomonというパッケージを使えばスカフォールディングがより厳密になり、より直感的な記述でMVCパターンでのアプリ構築が可能です。

次回以降、機会があれば扱いたいと思いますので、宜しくお願い致します。(_ _)

##github
今回作ったアプリはgithubに置いてありますので、もし興味があればご参照ください。

Github
https://github.com/uluru/bentoo