<?xml version="1.0"?>
<!-- name="generator" content="blojsom v3.1" -->
<rdf:RDF xmlns:wfw="http://wellformedweb.org/CommentAPI/"
         xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
         xmlns:dc="http://purl.org/dc/elements/1.1/"
         xmlns="http://purl.org/rss/1.0/">

	<channel rdf:about="http://blog.fukaoi.org">
		<title>深追い  Fukaoi.org</title>
		<link>http://blog.fukaoi.org/</link>
		<description>深く追い求めて行きたい、それは永遠の目標</description>
		<dc:publisher>admin</dc:publisher>
		<dc:creator>root@fukaoi.org</dc:creator>
		<dc:date>2012-02-01T12:23:02+00:00</dc:date>
		<dc:language>ja</dc:language>

        <items>
        <rdf:Seq>
                                <rdf:li rdf:resource="http://blog.fukaoi.org/2012/02/01/node-video-upload" />
                                <rdf:li rdf:resource="http://blog.fukaoi.org/2011/06/17/server-side-javascript-batch" />
                                <rdf:li rdf:resource="http://blog.fukaoi.org/2011/06/16/server-side-javascript-webdev3" />
                                <rdf:li rdf:resource="http://blog.fukaoi.org/2011/06/11/nodejs-vs-ringojs" />
                                <rdf:li rdf:resource="http://blog.fukaoi.org/2011/05/30/server-side-javascript-webdev2" />
                                <rdf:li rdf:resource="http://blog.fukaoi.org/2011/05/27/server-side-javascript-webdev1" />
                </rdf:Seq>
        </items>
    </channel>

            <item rdf:about="http://blog.fukaoi.org/2012/02/01/node-video-upload">
	    <title>動画アップロードシステムをNode.js、MongoDB、Expressで作ってみた</title>
	    <link>http://blog.fukaoi.org/2012/02/01/node-video-upload</link>
        <description>&lt;img src=&quot;http://pics-nu7waum6.dotcloud.com/blojsom/resources/default/img/nodejs.png&quot; alt=&quot;NodeJS&quot; title=&quot;NodeJS&quot;/&gt;
&lt;p&gt;
Node.jsの勉強の為にNode.js、MongoDB、Expressで作った「動画アップロードシステム」のソースを晒してみます。&lt;!--summary--&gt;もともとは、このサイト&lt;a href=&quot;http://pragtech.co.in/en/about-us/blog/32-video-upload-with-nodejs-express-and-mongodb-step-by-step.html&quot; title=&quot;NodeJS&quot; alt=&quot;NodeJS&quot; target=&quot;_blank&quot;&gt;Video Upload With Node.js, Express And Mongodb step-by-step&lt;/a&gt;でソースが公開されていたので、写経のつもりで書き始めたんだけど、写経した段階ではBugってたりして動かなかった。いろいろと修正をして、せっかくので日本語版に置き換え、ちょっとした機能追加をしたのが下記ソースになる。
&lt;/p&gt;

&lt;h2&gt;動画環境&lt;/h2&gt;
&lt;p&gt;次の環境で確認しています&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
    &lt;li&gt;Node.js：v0.4.12、 v0.6.8&lt;/li&gt;
    &lt;li&gt;MongoDB：v1.4.4&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;修正箇所&lt;/h2&gt;
&lt;blockquote&gt;
&lt;ul&gt;
    &lt;li&gt;原文ではcssのテンプレートエンジンstylusを使うようになっていたが、通常のCSSでコーディングした&lt;/li&gt;
    &lt;li&gt;テンプレートエンジンをejsにするための指定&lt;/li&gt;
    &lt;li&gt;style.cssのデザイン変更&lt;/li&gt;
    &lt;li&gt;日本語へのローカライズ&lt;/li&gt;
    &lt;li&gt;RegProviderのrequire方式の変更&lt;/li&gt;
    &lt;li&gt;app.post(&#39;/videos&#39;)でのネスト構造が深かったので、リファクタリング&lt;/li&gt;
    &lt;li&gt;app.post(&#39;/videos&#39;)でそもそもPOSTデータを受け取れなかったので、正しい形式に修正&lt;/li&gt;
    &lt;li&gt;動画アップロード時のプログレス状況を、req.form.on(&#39;progress&#39;)で取得できるようにした&lt;/li&gt;
    &lt;li&gt;その他細かいBug修正など&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;動画アップロードシステムのソースコード&lt;/h2&gt;
 &lt;h5&gt;app.js(Webのエントリポイント)&lt;/h5&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
    /**
     * Module dependencies.
     */
    var express = require(&#39;express&#39;),
        form = require(&#39;connect-form&#39;),
        mongoose = require(&#39;mongoose&#39;),
        util = require(&#39;util&#39;),
        sys = require(&#39;sys&#39;),
        fs = require(&#39;fs&#39;),
        routes = require(&#39;./routes&#39;),
        ejs = require(&#39;ejs&#39;),
        Post;

    var app = module.exports = express.createServer(
            form({keepExtensions: true}));

    //====== Configuration =======//
    app.configure(function() {
        app.set(&#39;views&#39;, __dirname + &#39;/views&#39;);
        app.set(&#39;view engine&#39;, &#39;ejs&#39;);
        app.use(express.bodyParser());
        app.use(express.logger());
        app.use(express.methodOverride());
        app.use(app.router);
        app.use(express.static(__dirname + &#39;/public&#39;));
        app.use(express.errorHandler({
            dumpExceptions: true,
            showStack: true
        }));
    });

    app.configure(&#39;development&#39;, function() {
        app.use(express.logger());
        app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
    });

    app.configure(&#39;production&#39;, function() {
        app.use(express.errorHandler());
    });

    //====== Mongoose object =======//
    var RegProvider = require(&#39;./regprovider&#39;).RegProvider;

    //====== Index page =======//
    app.get(&#39;/&#39;, function(req, res) {
        RegProvider.findAll(function(req, posts) {
            res.render(&#39;index&#39;,
                {locals: {title: &#39;Mongo Node.js 動画アップロード&#39;,
                    posts: posts}});
        })
    });

    //====== Create video file =======//
    app.get(&#39;/videos/new&#39;, function(req, res) {
        res.render(&#39;reg_new&#39;,
            {locals: {title: &#39;新規アップロード&#39;}}
        );
    });

    //====== Update video =======//
    app.post(&#39;/videos&#39;, function(req, res, next) {
        req.form.complete(function(err, fields, files) {
            console.log(&#39;here i go&#39;);
            if (err) return next(err);

            ins = fs.createReadStream(files.file.path);
            console.log(&#39;filename:&#39; + files.file.filename);
            ous = fs.createWriteStream(__dirname + &#39;/public/uploads/videos/&#39; + files.file.filename);
            util.pump(ins, ous, function(err) {
                if (err) return next(new Error(err));

                RegProvider.save({
                    filename: fields.filetitle,
                    file: files.file.filename
                }, function(error, docs) {
                    res.redirect(&#39;/&#39;)
                });
            });
        });

        req.form.on(&#39;progress&#39;, function(bytesReceived, bytesExpected) {
            var percent = (bytesReceived / bytesExpected * 100) | 0;
            console.log(&#39;Uploading: &#39; + percent + &#39;%&#39;);
        });
    });



    //====== Show uploaded video =======//
    app.get(&#39;/posts/:id&#39;, function(req, res) {
        RegProvider.findById(req.param(&#39;id&#39;), function(error, post) {
            res.render(&#39;reg_show&#39;, {
                locals: {
                    title: &#39;詳細ページ&#39;,
                    post:post
                }
            });
        });
    });

    app.listen(3000);
    console.log(&quot;Express server listening on port %d in %s mode&quot;, app.address().port, app.settings.env);
&lt;/pre&gt;

&lt;h5&gt;regprovider.js(Mongooseモジュールを使ってMongoDBにアクセス)&lt;/h5&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
var mongoose = require(&#39;mongoose&#39;);
mongoose.connect(&#39;mongodb://localhost/test&#39;);

var Schema = mongoose.Schema,
    ObjectId = Schema.Objectid;

var Post = new Schema({
    filename: String,
    file: String,
    create_at: Date
});

mongoose.model(&#39;Post&#39;, Post);
var Post = mongoose.model(&#39;Post&#39;);

RegProvider = function(){};

RegProvider.prototype.findAll = function(callback) {
  Post.find({}, function(err, posts) {
          callback(null, posts)
      });
};

RegProvider.prototype.findById = function(id, callback) {
  Post.findById(id, function(err, posts) {
          callback(null, posts)
      });
};

RegProvider.prototype.save = function(params, callback) {
    var post = new Post(
        {filename: params[&#39;filename&#39;],
         file: params[&#39;file&#39;],
         create_at: new Date().toLocaleString().toString()});
        post.save(function(err) {
           callback();
        });
};
exports.RegProvider = new RegProvider();
&lt;/pre&gt;

&lt;h5&gt;views/layout.ejs(レイアウト)&lt;/h5&gt;
&lt;pre class=&quot;prettyprint html&quot;&gt;
    &amp;lt;html&amp;gt;
     &amp;lt;head&amp;gt;
      &amp;lt;link href=&amp;quot;/stylesheets/style.css&amp;quot; media=&amp;quot;screen&amp;quot; rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; /&amp;gt;
     &amp;lt;/head&amp;gt;
     &amp;lt;body&amp;gt;
      &amp;lt;h1&amp;gt;&amp;lt;%= title %&amp;gt; ページ &amp;lt;/h1&amp;gt;
      &amp;lt;h2 class=&amp;quot;navi&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;/&amp;quot;&amp;gt;Home&amp;lt;/a&amp;gt;&amp;lt;/h2&amp;gt;
      &amp;lt;h2 class=&amp;quot;navi&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;/videos/new&amp;quot;&amp;gt;新規アップロード&amp;lt;/a&amp;gt;&amp;lt;/h2&amp;gt;
     &amp;lt;%- body %&amp;gt;
     &amp;lt;/body&amp;gt;
    &amp;lt;/html&amp;gt;
&lt;/pre&gt;

&lt;h5&gt;views/index.ejs(TOPページデザイン)&lt;/h5&gt;
&lt;pre class=&quot;prettyprint html&quot;&gt;
        &amp;lt;div id=&amp;quot;articles&amp;quot;&amp;gt;
         &amp;lt;%- partial(&#39;reg&#39;, posts) %&amp;gt;
        &amp;lt;/div&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
&lt;img src=&quot;http://pics-nu7waum6.dotcloud.com/blojsom/resources/default/img/2012/video_index_ejs.png&quot; alt=&quot;NodeJS&quot; title=&quot;NodeJS&quot;/&gt;
&lt;br /&gt;
TOPページイメージ図
&lt;/p&gt;
&lt;br /&gt;

&lt;h5&gt;views/reg.ejs(TOPページ用ループ表示デザイン)&lt;/h5&gt;
&lt;pre class=&quot;prettyprint html&quot;&gt;
    &amp;lt;div class=&amp;quot;post&amp;quot;&amp;gt;
        &amp;lt;div class=&amp;quot;columHead&amp;quot;&amp;gt;■&amp;lt;%=reg.filename %&amp;gt;&amp;lt;/div&amp;gt;
        &amp;lt;div class=&amp;quot;colum&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;/posts/&amp;lt;%=reg.id %&amp;gt;&amp;quot;&amp;gt;&amp;lt;%=reg.file %&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;
        &amp;lt;div class=&amp;quot;colum&amp;quot;&amp;gt;&amp;lt;%=reg.create_at %&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
&lt;img src=&quot;http://pics-nu7waum6.dotcloud.com/blojsom/resources/default/img/2012/video_index2_ejs.png&quot; alt=&quot;NodeJS&quot; title=&quot;NodeJS&quot;/&gt;
&lt;br /&gt;
TOPページ用ループイメージ図
&lt;/p&gt;
&lt;br /&gt;

&lt;h5&gt;views/reg_new.ejs(動画登録ページデザイン)&lt;/h5&gt;
&lt;pre class=&quot;prettyprint html&quot;&gt;
    &amp;lt;form method=&amp;quot;post&amp;quot; action=&amp;quot;/videos&amp;quot;  enctype=&amp;quot;multipart/form-data&amp;quot;&amp;gt;
        &amp;lt;div class=&amp;quot;post&amp;quot;&amp;gt;
            &amp;lt;p&amp;gt;
                &amp;lt;span&amp;gt;ファイル名: &amp;lt;/span&amp;gt;
                &amp;lt;input type=&amp;quot;filename&amp;quot; name=&amp;quot;filetitle&amp;quot; /&amp;gt;
            &amp;lt;/p&amp;gt;
            &amp;lt;p&amp;gt;
            &amp;lt;span&amp;gt;アップロードファイル: &amp;lt;/span&amp;gt;
            &amp;lt;input type=&amp;quot;file&amp;quot; name=&amp;quot;file&amp;quot;/&amp;gt;
            &amp;lt;/p&amp;gt;
            &amp;lt;p&amp;gt;
                &amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;Upload&amp;quot; /&amp;gt;
            &amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;/form&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
&lt;img src=&quot;http://pics-nu7waum6.dotcloud.com/blojsom/resources/default/img/2012/video_reg_new_ejs.png&quot; alt=&quot;NodeJS&quot; title=&quot;NodeJS&quot;/&gt;
&lt;br /&gt;
動画登録ページイメージ図
&lt;/p&gt;
&lt;br /&gt;

&lt;h5&gt;views/reg_show.ejs(動画再生ページデザイン)&lt;/h5&gt;
&lt;pre class=&quot;prettyprint html&quot;&gt;
    &amp;lt;div class=&amp;quot;post&amp;quot;&amp;gt;
        &amp;lt;div class=&amp;quot;columHead&amp;quot;&amp;gt;■&amp;lt;%=reg.filename %&amp;gt;&amp;lt;/div&amp;gt;
        &amp;lt;div class=&amp;quot;colum&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;/posts/&amp;lt;%=reg.id %&amp;gt;&amp;quot;&amp;gt;&amp;lt;%=reg.file %&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;
        &amp;lt;div class=&amp;quot;colum&amp;quot;&amp;gt;&amp;lt;%=reg.create_at %&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
&lt;img src=&quot;http://pics-nu7waum6.dotcloud.com/blojsom/resources/default/img/2012/video_reg_show_ejs.png&quot; alt=&quot;NodeJS&quot; title=&quot;NodeJS&quot;/&gt;
&lt;br /&gt;
動画再生ページイメージ図
&lt;/p&gt;
&lt;br /&gt;

&lt;h5&gt;public/stylesheets/style.css(CSS)&lt;/h5&gt;
&lt;pre class=&quot;prettyprint html&quot;&gt;
 body {
    padding: 50px;
    font: 14px &quot;Lucida Grande&quot;, Helvetica, Arial, sans-serif;
}

a {
    color: #00b7ff;
}

.post {
    float: left;
    width: 800px;
    margin: 20px;
    border-top: dashed 2px;
}

.navi {
    margin: 1em 1em 1em 3em;
    float: left;
}

.navi a {
    color: #808080;
}

.columHead {
    font-size: 17px;
    padding: 5px;
}

.colum {
    padding: 5px;
}
&lt;/pre&gt;

&lt;h5&gt;プログレス状況とはこんな感じの出力がコンソールに出力される&lt;/h5&gt;
&lt;pre&gt;
Uploading: 0%
Uploading: 0%
Uploading: 1%
　　.
　　.
　　.
Uploading: 99%
Uploading: 99%
Uploading: 99%
Uploading: 100%
here i go
filename:animal.mp4
&lt;/pre&gt;    
    
&lt;p&gt;ソースを追いながら、書き写して動作させるだけなら、3時間ほどでできる、、、と思う。ソースコード一式を&lt;a href=&quot;https://github.com/fukaoi/node-video-upload&quot; target=&quot;_blank&quot; title=&quot;GitHub&quot;&gt;GitHub&lt;/a&gt;にUPしてあるので、Node.js、MongoDBがインストールされている環境ならすぐに動作すると思うので、ご自由にどうぞ&lt;/p&gt;   
</description>
	    <dc:date>2012-02-01T12:23:02+00:00</dc:date>
	                                <wfw:comment>http://pics-nu7waum6.dotcloud.com/blojsom/commentapi/default/JavaScript/2012/02/01/node-video-upload</wfw:comment>
            <wfw:commentRss>http://blog.fukaoi.org/2012/02/01/node-video-upload?page=comments&amp;flavor=rss2</wfw:commentRss>
            </item>
            <item rdf:about="http://blog.fukaoi.org/2011/06/17/server-side-javascript-batch">
	    <title>サーバーサイドJavaScriptで簡単Webアプリ開発　- バッチ処理開発編</title>
	    <link>http://blog.fukaoi.org/2011/06/17/server-side-javascript-batch</link>
        <description>&lt;img src=&quot;http://pics-nu7waum6.dotcloud.com/blojsom/resources/default/img/ringojs.png&quot; alt=&quot;RingoJS&quot; title=&quot;RingoJS&quot;/&gt;
&lt;p&gt;
&lt;a href=&quot;/2011/06/16/server-side-javascript-webdev3&quot; title=&quot;RingoJS&quot; alt=&quot;RingoJS&quot;&gt;「Webフロント開発編(3)」&lt;/a&gt;に続き、最後の開発工程になるGoogle Calendar APIをたたいてmemberテーブルとマッチする検索キーワードが存在した場合にメール通知を行うバッチ処理プログラムを開発します&lt;!--summary--&gt;
&lt;/p&gt;

&lt;h3&gt;バッチ処理の作成&lt;/h3&gt;

&lt;h5&gt;Google Calendarに接続するための各種jarファイルの取得&lt;/h5&gt;
&lt;p&gt;ダウンロード＆解答して以下のjarファイルを$RINGO_HOME/libにおきます&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;gdata-calendar-2.0.jar&lt;/li&gt;
&lt;li&gt;gdata-client-1.0.jar&lt;/li&gt;
&lt;li&gt;gdata-core-1.0.jar&lt;/li&gt;
&lt;li&gt;guava-r09.jar&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;prettyprint bash&quot;&gt;
http://code.google.com/p/gdata-java-client/downloads/からgdata-src.java-1.45.0.zip 
http://code.google.com/p/guava-libraries/downloads/listからguava-r09.zip 
よりダウンロードして解凍する
&lt;/pre&gt;

&lt;h5&gt;バックコマンドプログラムの雛型&lt;/h5&gt;
&lt;p&gt;バッチ処理を行うbatch.jsの雛型を作成します。RingoJSのルール的にコマンドで動作させるバッチ用のプログラムは、if (require.main === module) {　でくくるようです。
&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[batch.js]&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
/**
 * メイン処理
 */
var main = function() {
  //ここにメイン処理を記述する
};

/**
 * mainファンクションを呼び出す
 */
if (require.main === module) {
  try {
      main();      
    }
  } catch (error) {
    print(error);
  }
}
&lt;/pre&gt;


&lt;h5&gt;MemberオブジェクトにgetBatchInfo()メソッドを作成&lt;/h5&gt;
&lt;p&gt;status=activeの対象データーをオブジェクトで返します。これも前に実装したようにO/Rマッパを使って実装します&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[model.js]&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
  /**
   * バッチで使用する対象データーをDBから取得する
   */
  getBatchInfo: function() {
    var result = this.loadObject.query()
        .equals(&#39;status&#39;, &#39;active&#39;).select();
    return result;
  }
&lt;/pre&gt;


&lt;h5&gt;mainファンクションの処理フロー&lt;/h5&gt;
&lt;p&gt;memberテーブルより登録されている勉強会キーワードをgetBatchInfo()を使用して、取得してループ毎に処理したいと思います。mainファンクションに必要なデーターとして、「メールアドレス」、「ユニークID」、「勉強会キーワード」を渡したいと思います&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[batch.js]&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
/**
 * mainファンクションを呼び出す
 */
if (require.main === module) {
  try {
    var results = Member.getBatchInfo();
    for each(var result in results) {
      main(result.email, result.uid, result.keyword);      
    }
  } catch (error) {
    print(error);
  }
}
&lt;/pre&gt;

&lt;h5&gt;config.jsに対象となるカレンダーのURLを追記する&lt;/h5&gt;
&lt;p&gt;IT勉強会のURLをconfig.jsで定義しておきます&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[config.js]&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
exports.calendarUrl = &#39;http://www.google.com/calendar/feeds/fvijvohm91uifvd9hratehf65k%40group.calendar.google.com/public/basic&#39;;
&lt;/pre&gt;

&lt;h5&gt;必要なファイルのrequireとインスタンスの生成&lt;/h5&gt;
&lt;p&gt;GDATA関連のクラスはファイル先頭でインスタンスを生成してしまいます。mainファンクション内で生成しても問題ないと思います&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[batch.js]&lt;/p&gt;

&lt;pre class=&quot;prettyprint javascript&quot;&gt;
var {Response} = require(&#39;ringo/webapp/response&#39;);
var {Member} = require(&#39;./model.js&#39;); 
var config = require(&#39;./config.js&#39;);
var {Service} = require(&#39;./service.js&#39;);

//// {}くくって、DateTimeオブジェクトをセット
var {DateTime} = com.google.gdata.data;

//// カレンダー検索クエリオブジェクトをセット
var cservice = com.google.gdata.client.calendar.CalendarService(&quot;&quot;);

//// config.jsに定義されたIT勉強会のURLを取得して、URLオブジェクトでラップ
var eventFeedUrl = java.net.URL(config.calendarUrl);
var query = com.google.gdata.client.calendar.CalendarQuery(eventFeedUrl);
var events = com.google.gdata.data.calendar.CalendarEventFeed().class;
&lt;/pre&gt;

&lt;h5&gt;検索対象期間を定義する&lt;/h5&gt;
&lt;p&gt;とりあえず、現在～3か月後を対象にするため、60日間を定義したいと思います&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[config.js]&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
exports.durationDays = 60;
&lt;/pre&gt;

&lt;h5&gt;検索期間をコンバートするメソッドの追加&lt;/h5&gt;
&lt;p&gt;JavaのSimpleDateFormat(&quot;yyyy-MM-dd&#39;T&#39;HH:mm:ss&#39;Z&#39;&quot;)クラスを使って、gdataのフォーマットに指定します。いったんミリ秒にしてからフォーマットかけています&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[service.js]&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
var simpleFormat = java.text.SimpleDateFormat(&quot;yyyy-MM-dd&#39;T&#39;HH:mm:ss&#39;Z&#39;&quot;);

/**
 * 検索対象期間のDateTimeを取得する
 */
exports.Service.searchTerm = {
  sd: simpleFormat, 
  start: function() {
    return this.sd.format(new Date());
  },
  end: function() {
    var duration = config.durationDays * 24 * 60 * 60 * 1000;
    var now = new Date().getTime();
    return this.sd.format(new Date(now + duration));
  }
};
&lt;/pre&gt;


&lt;h5&gt;カレンダーへ検索ワードを投げて、結果取得&lt;/h5&gt;
&lt;p&gt;ここは特に説明のしようがなくて、GDATAのAPI仕様どおりにパラメーターをセットするだけです&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[batch.js]&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
/**
 * メイン処理
 */
var main = function(email, uid, keyword) {
  var entry = &quot;&quot;, content = &quot;&quot;, text = &quot;&quot;;
  query.setFullTextQuery(keyword);
  query.setMinimumStartTime(
    DateTime.parseDateTime(Service.searchTerm.start()));
  query.setMaximumStartTime(
    DateTime.parseDateTime(Service.searchTerm.end()));

  var resultFeed = cservice.query(query, events);
&lt;/pre&gt;

&lt;h5&gt;検索結果が空だった場合&lt;/h5&gt;
&lt;p&gt;resultFeedのエントリー数を調べて0だった場合は処理を中断します&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[batch.js]&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
  var count = resultFeed.getEntries().size();

  if(count === 0) {return;}
&lt;/pre&gt;

&lt;h5&gt;バッチ用メールテンプレート作成&lt;/h5&gt;
&lt;p&gt;いままで作成してきたメールテンプレートと同じように作成するのですが、今回はループして同じテンプレートを何回もよみだしますので、batch.tplとは別にbatchPart.tplという繰り返し用のテンプレートにわけています&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[skins/email/batchPart.tpl]&lt;/p&gt;
&lt;pre class=&quot;prettyprint bash&quot;&gt;
----------------------------------------
タイトル：&lt;% studyTitle %&gt;
&lt;% plainText %&gt;
&lt;/pre&gt;

&lt;p class=&quot;graybold&quot;&gt;[skins/email/batch.tpl]&lt;/p&gt;
&lt;p&gt;バッチで使用します、メールフォーマットの全体テンプレートになります。textBodyにbatchPart.tplを使って生成されたデータが渡されてきます&lt;/p&gt;
&lt;pre class=&quot;prettyprint bash&quot;&gt;
IT勉強会サーチメール

このキーワードにマッチした勉強会は以下のとおりです

[&lt;% keyword %&gt;]

&lt;% textBody %&gt;


====================================================
今後、このメールが不要な方は、下記URLをクリックして下さい
&lt;% withdrawUrl %&gt;
====================================================
&lt;/pre&gt;

&lt;h5&gt;バッチ用メールフォーマットメソッド追加&lt;/h5&gt;
&lt;p&gt;skins/email/batchPart.tpl用メソッドになります&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[service.js]&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
   batchPart : function(studyTitle, plainText) {
    return Response.skin(module.resolve
                         (&#39;skins/email/batchPart.tpl&#39;),
                         { studyTitle: studyTitle,
                           plainText: plainText}).body;
&lt;/pre&gt;

&lt;p&gt;skins/email/batch.tpl用メソッドになります。退会用のリンクもメールに添付するため、withdrawというアクションを用意します。withdrawについては、後ほど作成します&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[service.js]&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
  batch : function(uid, keyword, text) {
    return Response.skin(module.resolve
                         (&#39;skins/email/batch.tpl&#39;),
                         { keyword: keyword,
                           withdrawUrl: config.fqdn 
                           + &quot;/withdraw?uid=&quot; + uid,
                           textBody: text}).body;
  },
&lt;/pre&gt;

&lt;h5&gt;メール用整形メソッドの追加&lt;/h5&gt;
&lt;p&gt;GData calendarのSummaryデーターを人間の目でみて読みやすい形に変換します。&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[service.js]&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
  /**
   * GData calendarのSummary的な値を読みやすい形に加工する
   */
  modifySummary : function(content) {
    var CR = &quot;\r\n&quot;;
    return content.replace(&#39; JST &#39;, CR)
        .replace(&#39;予定のステータス: 確定&#39;, &#39;&#39;)
        .replace(&#39;予定の説明&quot;, &quot;詳細内容&#39;) + CR;       
  }
};
&lt;/pre&gt;


&lt;h5&gt;検索でマッチしたデータをループして、Service.modifySummary()に渡して、メールで読みやすい形式に変換します&lt;/h5&gt;
&lt;p&gt;resultFeed.getEntries().get(i)でマッチしたentryオブジェクトの中から1個取得します。entry.getTextContent().getContent().getPlainText()で取得した検索結果をテキスト形式にコンバートします。&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[model.js]&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
  for (var i = 0; i &lt; count; i++) {
    entry = resultFeed.getEntries().get(i);
    text += Service.textFormat.batchPart(
      entry.getTitle().getPlainText(),
      Service.modifySummary(entry.getTextContent()
                            .getContent()
                            .getPlainText())
    );
  }
&lt;/pre&gt;

&lt;h5&gt;メール送信&lt;/h5&gt;
&lt;p&gt;上記で作成した Service.textFormat.batch()に必要な値を渡してメールを送信します&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[model.js]&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
  var textFormat = Service.textFormat.batch(uid, keyword, text);
  Service.sendMail(email, textFormat);
&lt;/pre&gt;

&lt;p&gt;以下のようなメールが飛べば、成功です。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://pics-nu7waum6.dotcloud.com/blojsom/resources/default/img/2011/it_study_search_batch_email.gif&quot; alt=&quot;RingoJS&quot; title=&quot;RingoJS&quot;/&gt;&lt;/p&gt;


&lt;h3&gt;退会処理&lt;/h3&gt;
&lt;h5&gt;退会処理メソッドの追加&lt;/h5&gt;
&lt;p&gt;uidをキーにmemberテーブルにマッチするデータがあれば、statusカラムをactiveからinactiveにupdateします。inactiveになることで次回のバッチ処理の対象外とします。&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[model.js]&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
  /**
   * 退会処理
   */
  withdraw: function(uid) {
    var result = this.loadObject.query()
        .equals(&#39;uid&#39;, uid).select();
    result[0].status = &#39;inactive&#39;;
    result[0].save();  
  },
&lt;/pre&gt;


&lt;h5&gt;withdrawアクションの追加&lt;/h5&gt;
&lt;p&gt;Member.withdraw()をよびだしてステータスをアップデートし、メールを送信するだけのシンプルな処理になります。特に退会用のhtmlテンプレートは用意せず、登録完了時に使用した、skins/complete.htmlを再利用し、退会用メッセージを引数に渡すだけにしたいと思います。&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[action.js]&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
/**
 * withdraw page
 */
exports.withdraw = {};
exports.withdraw.GET = function(req) {
  var uid = req.params[&#39;uid&#39;];
  if (uid === &quot;&quot;) {
    return Response.skin(module.resolve(&#39;skins/complete.html&#39;), 
    { title: &#39;エラー&#39;,
      errorMessage: &#39;URLに問題があります&#39;});
  }

 Member.withdraw(uid);
  return Response.skin(module.resolve(&#39;skins/complete.html&#39;), {
    title: &#39;退会が完了しました&#39;,
    headMessage: &#39;今後、IT 勉強会サーチメールは送信されなくなります&#39;
  });
&lt;/pre&gt;

&lt;p&gt;以下のページが表示されれば、無事退会処理されたことになります&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://pics-nu7waum6.dotcloud.com/blojsom/resources/default/img/2011/it_study_search_batch_withdraw.gif&quot; alt=&quot;RingoJS&quot; title=&quot;RingoJS&quot;/&gt;&lt;/p&gt;

&lt;h3&gt;簡単Webアプリ開発完了&lt;/h3&gt;
&lt;p&gt;以上がバッチ処理機能の開発になります。簡略化された処理もありますが、「サーバーサイドJavaScriptで簡単Webアプリ開発」ひととりやりたいことは網羅できたと思いますので、完了としたいと思います。このブログで使用したソースコード一式は&lt;a href=&quot;https://github.com/fukaoi/blog/tree/master/2011/it_study_search&quot; alt=&quot;GitHub&quot;&gt;GitHub&lt;/a&gt;にUPしてありますので、もしよろしければ見てみてください。&lt;/p&gt;
&lt;br /&gt;
</description>
	    <dc:date>2011-06-17T16:46:34+00:00</dc:date>
	                                <wfw:comment>http://pics-nu7waum6.dotcloud.com/blojsom/commentapi/default/JavaScript/2011/06/17/server-side-javascript-batch</wfw:comment>
            <wfw:commentRss>http://blog.fukaoi.org/2011/06/17/server-side-javascript-batch?page=comments&amp;flavor=rss2</wfw:commentRss>
            </item>
            <item rdf:about="http://blog.fukaoi.org/2011/06/16/server-side-javascript-webdev3">
	    <title>サーバーサイドJavaScriptで簡単Webアプリ開発　- Webフロント開発編(3)</title>
	    <link>http://blog.fukaoi.org/2011/06/16/server-side-javascript-webdev3</link>
        <description>&lt;img src=&quot;http://pics-nu7waum6.dotcloud.com/blojsom/resources/default/img/ringojs.png&quot; alt=&quot;RingoJS&quot; title=&quot;RingoJS&quot;/&gt;
&lt;p&gt;
&lt;a href=&quot;/2011/05/27/server-side-javascript-webdev2&quot; title=&quot;RingoJS&quot; alt=&quot;RingoJS&quot;&gt;「Webフロント開発編(2)」&lt;/a&gt;に続き、本編ではメールに添付された登録完了(complete)機能の開発を行います。メール内にリンクされたURLが登録完了処理のURLになります。リンクをクリックして、GETでuidを取得して会員ステータスを保留(waiting)から有効(active)に変更します&lt;!--summary--&gt;
&lt;/p&gt;

&lt;h3&gt;登録完了処理の作成(http://localhost:8080/completeの処理)&lt;/h3&gt;
&lt;p&gt;action.jsにcompleteメソッドを追加する。GETプロパティを追加する&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[action.js]&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
/**
 * complete page
 */
exports.complete = {};
exports.complete.GET = function(req) {

  //処理を記述する

};
&lt;/pre&gt;

&lt;h5&gt;リクエストパラメータのチェック&lt;/h5&gt;
&lt;p&gt;リクエストオブジェクトからuidの存在の有無をチェックします。ここもチェック処理自体は最小限にとめておきます。&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[action.js]&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
  var uid = req.params[&#39;uid&#39;];
  if (uid === &quot;&quot;) {
    return Response.skin(module.resolve(&#39;skins/complete.html&#39;), {
      title: &#39;エラー&#39;,
      errorMessage: &#39;URLに問題があります&#39;});
  }
&lt;/pre&gt;

&lt;h5&gt;memberテーブルのstatusをactiveに更新&lt;/h5&gt;
&lt;p&gt;GETで渡ってきたuidにマッチするuidのレコードをmemberテーブルよりselect()メソッドでひっぱってきて、&#39;active&#39;という文字列をセットしてsave()メソッドで保存します。selectした結果オブジェクトを使って、save()メソッドを実行すると、データーの更新が可能になります&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[model.js]&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
  /**
   * 登録完了処理
   */
  complete: function(uid) {
    var result = this.loadObject.query()
        .equals(&#39;uid&#39;, uid).select();
    result[0].status = &#39;active&#39;;
    result[0].save();  
  },
&lt;/pre&gt;

&lt;h5&gt;登録完了HTMLテンプレート作成&lt;/h5&gt;
&lt;p&gt;messageを表示するだけのシンプルなものを用意します&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[skins/email/complete.html]&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
&amp;lt;% extends ./base.html %&amp;gt;

&amp;lt;% subskin content %&amp;gt;
&amp;lt;div id=&amp;quot;header&amp;quot;&amp;gt;&amp;lt;h1&amp;gt;&amp;lt;% title %&amp;gt;&amp;lt;/h1&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;body&amp;quot;&amp;gt;
&amp;lt;p id=&amp;quot;message&amp;quot;&amp;gt;&amp;lt;% errorMessage %&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;h3&amp;gt;&amp;lt;% headMessage %&amp;gt;&amp;lt;/h3&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;

&lt;h5&gt;Member.complete()メソッドを呼び出す&lt;/h5&gt;
&lt;p&gt;特に目新しい処理はなく、上記で作成した、Member.complete()メソッド、complete.htmlテンプレートをセットします。&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[action.js]&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
  Member.complete(uid);
  return Response.skin(module.resolve(&#39;skins/complete.html&#39;), {
    title: &#39;登録が完了しました&#39;,
    headMessage: &#39;今後、検索ワードにマッチした勉強会をメールでお知らせ致します&#39;
  });
};
&lt;/pre&gt;

&lt;h5&gt;メールに記述されたリンクをクリック&lt;/h5&gt;
&lt;p&gt;&lt;a href=&quot;/2011/05/27/server-side-javascript-webdev2&quot; title=&quot;RingoJS&quot; alt=&quot;RingoJS&quot;&gt;「Webフロント開発編(2)」&lt;/a&gt;で飛ばしたメールのリンクをクリックしてみると内部でaction.jsのcompleteメソッドが呼びだされ、memberテーブルの更新、登録完了ページの表示を行います。更新後の処理は以下の画像で確認できます&lt;/p&gt;
&lt;pre class=&quot;prettyprint bash&quot;&gt;
&lt;blockquote&gt;
メールに記述されていた内容のピックアップ

[登録完了URL]
http://localhost:8080/complete?uid=cb18972392a103ae3903a1b70692613b

[uid]
cb18972392a103ae3903a1b70692613b
&lt;/blockquote&gt;

&lt;img src=&quot;http://pics-nu7waum6.dotcloud.com/blojsom/resources/default/img/2011/it_study_search_waiting.gif&quot; alt=&quot;RingoJS&quot; title=&quot;RingoJS&quot;/&gt;

memberテーブルをselectしますと、statusがwaitingからactiveに
更新されているのが確認できました
↓↓↓

&lt;img src=&quot;http://pics-nu7waum6.dotcloud.com/blojsom/resources/default/img/2011/it_study_search_active.gif&quot; alt=&quot;RingoJS&quot; title=&quot;RingoJS&quot;/&gt;
&lt;/pre&gt;

&lt;p&gt;登録完了ページが表示され、メッセージが表示されています&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://pics-nu7waum6.dotcloud.com/blojsom/resources/default/img/2011/it_study_search_complete.gif&quot; alt=&quot;RingoJS&quot; title=&quot;RingoJS&quot;/&gt;&lt;/p&gt;


&lt;p&gt;以上が登録完了機能の開発になります。次編からバッチ処理の開発に入ります。&lt;/p&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;subem&quot;&gt;
&lt;strong&gt;次のステップへ&lt;/strong&gt;
&lt;br /&gt;
&lt;a href=&quot;/2011/06/17/server-side-javascript-batch&quot; title=&quot;RingoJS&quot; alt=&quot;RingoJS&quot;&gt;バッチ処理開発編へ&lt;/a&gt;
&lt;/div&gt;
</description>
	    <dc:date>2011-06-16T22:30:59+00:00</dc:date>
	                                <wfw:comment>http://pics-nu7waum6.dotcloud.com/blojsom/commentapi/default/JavaScript/2011/06/16/server-side-javascript-webdev3</wfw:comment>
            <wfw:commentRss>http://blog.fukaoi.org/2011/06/16/server-side-javascript-webdev3?page=comments&amp;flavor=rss2</wfw:commentRss>
            </item>
            <item rdf:about="http://blog.fukaoi.org/2011/06/11/nodejs-vs-ringojs">
	    <title>node.JS VS RingoJS 比較サイトまとめ</title>
	    <link>http://blog.fukaoi.org/2011/06/11/nodejs-vs-ringojs</link>
        <description>&lt;img src=&quot;http://pics-nu7waum6.dotcloud.com/blojsom/resources/default/img/nodejs.png&quot; alt=&quot;node.JS&quot; title=&quot;node.JS&quot;/&gt;&amp;emsp;
&lt;img src=&quot;http://pics-nu7waum6.dotcloud.com/blojsom/resources/default/img/ringojs.png&quot; alt=&quot;RingoJS&quot; title=&quot;RingoJS&quot;/&gt;
&lt;p&gt;
node.JSとRingoJSという2つのサーバーサイドJavaScriptが有名(node.JSの方が遥かに知名度は上)ですが、「どっちを使うべきなのか？」という話をtwitterで見かけました。実際、周りでも同じような意見を聞きます。僕自身は、RingoJSについて&lt;a href=&quot;http://blog.fukaoi.org/2011/05/26/server-side-javascript-startup&quot; title=&quot;RingoJS&quot;&gt;「サーバーサイドJavaScriptで簡単Webアプリ開発」&lt;/a&gt;でも書き始めたように、最近ちょくちょく触りだしているのでなじみがあります。node.JSについては、「hello world」どまりで、比較しようにも比較できない状況でした。そこでネットで調べてた所、海外のブログサイトでnode.JSとRingoJSの比較内容が書かれたブログが2つあったので、自分なりに訳してまとめてみました。
&lt;!--summary--&gt;
&lt;/p&gt;

&lt;h2&gt;参考にしたブログ&lt;/h2&gt;
&lt;p&gt;どちらも2010年7月、9月に書かれているので、内容は若干古くとなっているところもあると思います。(特にnode.JSに関してはupdateスピードが早いので)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;
&lt;strong&gt;
&lt;a href=&quot;http://nodejsbot.blogspot.com/2010/09/ringojs-and-nodejs.html&quot; target=&quot;_blank&quot; title=&quot;nodejsbot&quot;&gt;nodejsbot RingoJS and Node.js&lt;/a&gt;
&lt;/strong&gt;
&lt;/p&gt;
&lt;p&gt;
ブログ名から察するにnode.JSラブな人が書いているんだと思いますが、node.JSだけに偏った意見ではなく、RingoJSについてもわりと公平に比較されていたと思います。
&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;
&lt;strong&gt;
&lt;a href=&quot;http://www.gmosx.com/blog/agVnbW9zeHIPCxIHQXJ0aWNsZRjh2gEM/&quot; target=&quot;_blank&quot; title=&quot;RingoJS&quot;&gt;gmosx RingoJS vs NodeJS&lt;/a&gt;
&lt;/strong&gt;
&lt;/p&gt;
&lt;p&gt;
こちらはブログ件名とは異なり、RingoJSの長所を中心に書かれおり、比較はあまりされていませんでした。そのせいか、コメント欄では、色々と反論されており、その投稿した人の反論からnode.JSのメリットをひっぱりあげました
&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;まとめた比較情報の多くはnodejsbotを参考にさせてもらいました&lt;/p&gt;

&lt;h2&gt;node.JSについて&lt;/h2&gt;
&lt;h3&gt;短所&lt;/h3&gt;
&lt;blockquote&gt;
&lt;ul&gt;

&lt;li&gt;
C++のアドオンをコンパイルするのに管理者権限が必要になる
&lt;p&gt;レンタルサーバとかで一般ユーザ権限しか貸与されてないとコンパイルできない可能性があるということだと思います&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
Windows上で動作させるには注意が必要である
&lt;p&gt;たぶんブログが書かれた当時はnode.JSのWindows対応がいまいちだったのではないかと推測しました。&lt;a href=&quot;http://d.hatena.ne.jp/m-hiyama/20110302/1299029346&quot; title=&quot;node.js&quot; target=&quot;_blank&quot;&gt;LinuxとWindowsにnode.jsをインストールしてみた&lt;/a&gt;によると最新のnode.JSとWindows7だと、簡単にインストールできるみたいです。なので以前の話かと思います&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
ノンブロッキングシステム(非同期)のコードは、スレッド、同期ベースのコードを書いてきた開発者にとって、制御のフローはなじみが薄い
&lt;p&gt;JavaScriptをやり始めたころ、コールバックという処理に違和感を感じとことがあります。まぁこれも慣れれば便利なものに&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
過去の遺産からなる、様々なモジュールが存在していない
&lt;p&gt;
RingoJSでいう、Javaでできたライブラリの存在をさしていると思います。当然、V8エンジンという新しいミドルゥエアであるため
、専用モジュール以外は存在していないですね&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
バージョンUPが速いので、利用者は古いバージョンに取り残されていくかもしれない
&lt;p&gt;0.0.1がリリースされてから約10カ月の間に、0.4まであがっているのですごい勢いで更新されていますね。これはまだBetaバージョンに近い位置づけだから、現状はある意味しょうがないのではないでしょうか&lt;/p&gt;
&lt;/li&gt;

&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3&gt;長所&lt;/h3&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
非同期APIによってクライアントサイド開発のようにイベント駆動型の開発が可能になる
&lt;p&gt;短所でもでてきた内容の反対の理由ですね。node.JSの長所中の長所かと&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
シングルスレッドは多くの同期問題を回避できることを意味する
&lt;p&gt;当たり前ですが、マルチスレッドならではの同期問題は発生しなくなりますよね&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
V8エンジンは高速でAPIは綺麗(google v8.hファイルを見る事で、悩んだ際の答えがほとんどのっている)
&lt;p&gt;
V8エンジンのソースを見た事はないのですが、Googleのスーパーエンジニアが設計コーディングしているのだから、いけている実装なんでしょうね
&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
C++のアドオンで、何でもやりたいことは実現できる
&lt;p&gt;
C++ベースのV8環境なので、足りない機能があれば、C++で自分でアドオンを作成すればOKということでしょうね
&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
APIはJavaScriptのクロージャを使用しており、楽をすることができる
&lt;p&gt;クロージャ自身はJavaScriptの言語機能だから、node.JS固有のものでもないと思うのですが・・・&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
ローレベルのAPIは多くのAPIと比べて、簡単に複雑な実装が可能である
&lt;p&gt;ローレベルAPIがどれをさすのか曖昧なので、よくわかりませんが、、、processやstreamのあたりのことでしょうか&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
イベント駆動型システムはスレッド型モデル(コンテキストの変更、メモリのオーバヘッド、ロッキングがある)と比較してすばらしい
&lt;p&gt;コンテキストの変更はJ2EEモデルでの開発をさしているかと思います。確かにアプリケーションで修正するたびに、コンテキストの変更が発生するようなモデルだと、発狂しそうです。ちなみに、RingoJSではコンテキストを変更したりする事は基本的に無いです&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
ファイルディスクリプタの入出力は素晴らしい
&lt;p&gt;たぶん、プロセス間でストリーム通信を行う際のファイルディスクリプタの扱いの効率性を指摘しているんじゃないのかなと思いました。badmathさんの&lt;a href=&quot;http://d.hatena.ne.jp/badatmath/20101022/1287701281&quot; target=&quot;_blank&quot; alt=&quot;node.JS&quot; title=&quot;node.JS&quot;&gt;node.js とは何か (2)&lt;/a&gt;の記事を見て頂ければnode.JSの内部アーキテクチャの概要が理解できメリットが分かると思います。&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
メモリの消費量が少ない
&lt;p&gt;シングルスレッド万歳！&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
コミュティは大きく、非常に活発で、フレンドリーである
&lt;p&gt;
これは意外に重要だと思いました。オープンソースアプリケーションの多くは利用するユーザによって、発展している歴史があるので
&lt;/p&gt;
&lt;/li&gt;

&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;RingoJSについて&lt;/h2&gt;
&lt;h3&gt;短所&lt;/h3&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
JVM上でJavaScriptを実行しているので、node.JSの実行環境であるC++で書かれたV8エンジンより遅い
&lt;p&gt;これも当たり前の事実だと思っていましたが、RingoJSとnode.JSでベンチマークをとったブログ&lt;a href=&quot;http://hns.github.com/2010/09/21/benchmark.html&quot; target=&quot;_blank&quot; title=&quot;RingoJS&quot;&gt;RingoJS vs. Node.js: Runtime Values&lt;/a&gt;によると、遅いどころか、node.JSよりパフォーマンスが良い結果もありました。node.JSが2010年9月のスナップショットで比較しているので、最新版だとどういう結果になるかは不明ですね&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
APIでJavaScriptらしいコーディングが使われていない
&lt;p&gt;node.JSのコードと比べるとコールバックなど使わなくても実装できるので、そうかもしれないです。これはアーキテクチャが非同期モデルとスレッドモデルの違いもあると思います&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
ハイレベルAPIのコードがクライアントJavaScriptの記述と似ていない
&lt;p&gt;ハイレベルAPIがどこを指すのか分からないのですが、この理由も上記に近いのではないでしょうか&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
アドオンについての情報が記載されていない。
&lt;p&gt;node.JSではアドオン、RingoJSではパッケージの事を指しているんだと思うのですが、確かにnode.JSみたいにアドオン(パッケージ)の作成方法やルールなどが記載されたページはなかったです。&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
JVMを使用しているので、メモリを大量に消費しやすい
&lt;p&gt;
これはVMの弁慶の泣き所ですね。
&lt;/p&gt;
&lt;/li&gt;

&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3&gt;長所&lt;/h3&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
Javaを使ってアドオンの開発ができる、過去のJavaのライブラリなどを再利用可能
&lt;p&gt;
node.JSの長所でもあったように、RingoJSではベースがJavaなので、Javaを使っての機能拡張が可能です
&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
JavaScript1.8の機能を使うことにより、より良いスクリプトがかける
&lt;p&gt;RingoJSは内部でRhinoエンジンを使用しているので、Mozilla Foundationで開発されているJavaScriptの新機能の恩恵を受ける事ができるということですね&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
シングルプロセスの中で、複数のインスタンスを動作させることができる(V8エンジン上でも同じ事はやろうと思えばできるば、難しいだろう)
&lt;p&gt;たぶん、マルチスレッドだといいたいんだと思います。マルチスレッド全盛の時代で、いまさら長所ポイントとしては弱い気が・・・&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
別スレッドで動作しているJavaScriptをsync()関数を使って、ブロック(同期化)することができる
&lt;p&gt;sync()関数はRhinoエンジンが提供する関数で、Javaでの同期処理より簡単に書けるみたいです&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
JVM上で実行されるので、Google App Engine、自宅のPC、携帯電話などで動作する
&lt;p&gt;
自分もRingoJSを使用しようと思った際の決め手となったポイントです。多くのデバイスで動作する可能性というのは魅力的です
&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
データーベースはテスト、サポートされたJavaライブラリを経由して行われる
&lt;p&gt;実績があるJDBCを使用できることのメリットを言いたいんだと思います&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
デスクトップアプリケーションを作成する場合、GUI、マルチメディア、入出力のJavaライブラリを利用できる
&lt;p&gt;これも上と同じように豊富なJavaライブラリの恩恵ですね&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
Javaのアドオンは素晴らしく、JVM上で動作するので安全である。JNIを使う事で、C++のライブラリも使うことができる
&lt;p&gt;RingoJSでもやろとう思えば、なんでもできると思います&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
CommonJSに限りなく準拠している
&lt;p&gt;
node.JSよりRingoJSのほうが、CommonJSに準拠しているそうです
&lt;/p&gt;
&lt;/li&gt;

&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;じゃあ、どっちをつかえばいいの？&lt;/h2&gt;
&lt;p&gt;正直それは僕にもよくわかりません(笑)。短所と長所をまとめていく過程で思った事は、node.JS、RingoJSはサーバーサイドJavaScriptという&quot;枠組み&quot;は同じではありますが、比較する対象としては相違点が多いというか、2つのミドルウェアの前提となる仕様の方向性が大きく違っており、比較対象にすべきではないように思いました。なのでプログラマがnode.JS、RingoJSの好きな方のスタイルを選択すればよいと思います。それでも、もしどちらかを選択しないといけなくなった場合、主観になってしまうですが「少ないリソースで大量のトラフィック(アクセス)をさばく必要がある場合はnode.JS」、「それほどトラフィックやリソースを気にする必要がない普通のWebアプリケーション開発ではRingoJS」という位でしょうか。後は、PaaSサービスやレンタルサーバなどの実行環境の制限も基準になりますよね。今の所、判断基準はこれくらいしか思いつかないです(汗)
&lt;/p&gt;
</description>
	    <dc:date>2011-06-11T17:21:41+00:00</dc:date>
	                                <wfw:comment>http://pics-nu7waum6.dotcloud.com/blojsom/commentapi/default/JavaScript/2011/06/11/nodejs-vs-ringojs</wfw:comment>
            <wfw:commentRss>http://blog.fukaoi.org/2011/06/11/nodejs-vs-ringojs?page=comments&amp;flavor=rss2</wfw:commentRss>
            </item>
            <item rdf:about="http://blog.fukaoi.org/2011/05/30/server-side-javascript-webdev2">
	    <title>サーバーサイドJavaScriptで簡単Webアプリ開発　- Webフロント開発編(2)</title>
	    <link>http://blog.fukaoi.org/2011/05/30/server-side-javascript-webdev2</link>
        <description>&lt;img src=&quot;http://pics-nu7waum6.dotcloud.com/blojsom/resources/default/img/ringojs.png&quot; alt=&quot;RingoJS&quot; title=&quot;RingoJS&quot;/&gt;
&lt;p&gt;
&lt;a href=&quot;/2011/05/27/server-side-javascript-webdev1&quot; title=&quot;RingoJS&quot; alt=&quot;RingoJS&quot;&gt;「Webフロント開発編(1)」&lt;/a&gt;に続き、本編では登録画面からのデーターを処理する仮登録(confirm)機能の開発を行います。この開発では、ビジネスロジック、DBのエンティティ周りを別のプログラムファイル(service.js、model.js)に記述します。action.js内にすべての処理をまとめると可読性、保守性、再利用性が悪くなったりしますので。そのため&lt;a href=&quot;/2011/05/27/server-side-javascript-webdev1&quot; title=&quot;RingoJS&quot; alt=&quot;RingoJS&quot;&gt;「Webフロント開発編(1)」&lt;/a&gt;に比べ、作成するプログラムファイル、ソースコード数も多くなります。&lt;!--summary--&gt;
&lt;/p&gt;

&lt;h3&gt;テーブルスキーマ作成&lt;/h3&gt;          
&lt;p&gt;登録ページから渡ってきたリクエストデータを保存するための、テーブルスキーマを設計します。Webアプリに登録するデーターを管理するということで、テーブル名はmemberにします&lt;/p&gt;
&lt;pre class=&quot;prettyprint sql&quot;&gt;
CREATE TABLE `member` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `email` char(40) NOT NULL 
        COMMENT &#39;メールアドレス&#39;, 
  `status` enum(&#39;waiting&#39;,&#39;active&#39;,&#39;inactive&#39;) NOT NULL 
        COMMENT &#39;会員ステータス&#39;,
  `keyword` text NOT NULL 
        COMMENT &#39;勉強会検索キーワード&#39;,
  `uid` char(40) NOT NULL 
        COMMENT &#39;email + keywordでユニーク性を持たせるためのID&#39;,
  `timestamp` timestamp NOT NULL DEFAULT 
        CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
&lt;/pre&gt;

&lt;blockquote&gt;
※statusカラムの状態の説明
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;waiting・・・仮登録状態のユーザ&lt;/li&gt;
&lt;li&gt;active・・・登録が正常完了している有効会員&lt;/li&gt;
&lt;li&gt;inactive・・・本サービスを退会、または無効となった会員&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3&gt;ビジネスロジックを担当するservice.jsを作成&lt;/h3&gt;
&lt;p&gt;POSTで渡ってくるリクエストデータの中から、email(Eメールアドレス)、keyword(勉強会を探すためのキーワード)をオブジェクトのプロパティにセットするため、下記のように定義します。trim()メソッドは入力時に全角半角スペースがkeywordの前後に含まれていた場合のトリミングするためのメソッドです。commpaReplace()メソッドは入力されたkeywordが複数あった場合に、AND検索にするため、Google Calendar APIに値を投げる前にカンマ区切りにデータを変更するためのメソッドです。&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[service.js]&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
/**
 * action.js、batch.jsのビジネスロジックの
 * 定義 
 */

var config = require(&#39;./config.js&#39;);

exports.Service = {
  email: &quot;&quot;,
  keyword: &quot;&quot;,

  /**
   * トリミング(全角、半角のスペース削除)
   */
  trim : function(key) {
    return key.replace(/^\s+|\s+$/g, &#39;&#39;);
  },

  /**
   * カンマ(,)でreplace
   */
   commaReplace: function(key) {
    return key.replace(/\s+/g, &#39;,&#39;);
  }

};
&lt;/pre&gt;

&lt;h3&gt;DB Entityを担当するmodel.jsを作成&lt;/h3&gt;
&lt;p&gt;model.jsではmemberテーブルを操作するDB関係のメソッドを定義していきます。コーディング前に次の依存ライブラリが必要になりますのでダウンロードしてセットアップしてください&lt;/p&gt;
&lt;h5&gt;MySQLに接続するためのJava用JDBCドライバー&lt;/h5&gt;
&lt;p&gt;Javaのjarファイルは$RINGO_HOME/libに置くことでRingoJS内で呼び出すことができます。新しくjarファイルを追加する場合は忘れずにRingoJSの再起動を行っておきます&lt;/p&gt;
&lt;pre class=&quot;prettyprint bash&quot;&gt;
http://www-jp.mysql.com/downloads/connector/j/ より 
mysql-connector-java-5.1.16.tar.gzをダウンロードして解凍する

$ cp mysql-connector-java-5.1.16/mysql-connector-java-5.1.16-bin.jar $RINGO_HOME/lib
&lt;/pre&gt;

&lt;h5&gt;RingoJS用O/Rマッパライブラリ&lt;/h5&gt;
&lt;p&gt;RingoJSのパッケージインストラーを使い、ringo-sqlstore、ringo-storableをインストールします。ringo-storableはringo-sqlstoreの依存パッケージです。インストールが完了しますと$RINGO_HOME/packages/に置かれる&lt;/p&gt;
&lt;pre class=&quot;prettyprint bash&quot;&gt;
$ $RINGO_HOME/bin/ringo-admin install oberhamsi/ringo-sqlstore
$ $RINGO_HOME/bin/ringo-admin install hns/ringo-storable 
&lt;/pre&gt;

&lt;p&gt;MAPPING_MEMBER.propertiesでテーブルカラム情報を定義しています。Member.loadObjectプロパティを実行することでO/Rマッピングが行われます&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[model.js]&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
/*
 * Databaseを操作する
 */

var config = require(&#39;./config.js&#39;);
var Store = require(&quot;ringo/storage/sql/store&quot;).Store;

/**
 * MySQLの接続情報
 */
var store = new Store({
  &quot;url&quot;: config.db.url,
  &quot;driver&quot;: config.db.driver,
  &quot;username&quot;: config.db.username,
  &quot;password&quot;: config.db.password
});

/**
 * Member Entity defined
 */
var MAPPING_MEMBER = {
  &quot;table&quot;: &quot;member&quot;,
  &quot;properties&quot;: {
    &quot;email&quot;: {&quot;type&quot;: &quot;string&quot;, &quot;column&quot;: &quot;email&quot;, &quot;nullable&quot;: false},
    &quot;status&quot;: {&quot;type&quot;: &quot;string&quot;, &quot;column&quot;: &quot;status&quot;, &quot;nullable&quot;: false},
    &quot;keyword&quot;: {&quot;type&quot;: &quot;string&quot;, &quot;column&quot;: &quot;keyword&quot;,&quot;nullable&quot;: false},
    &quot;uid&quot;: {&quot;type&quot;: &quot;string&quot;, &quot;column&quot;: &quot;uid&quot;, &quot;nullable&quot;: false}
  }    
};

/**
 * Memberオブジェクトの生成、各種メソッド定義
 */
exports.Member = {
  loadObject: store.defineEntity(&quot;Member&quot;, MAPPING_MEMBER),
};
&lt;/pre&gt;

&lt;p&gt;config.jsにMySQLの接続情報を追加します&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[config.js]&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
exports.db = {
  url: &#39;jdbc:mysql://localhost/test&#39;,
  driver: &#39;com.mysql.jdbc.Driver&#39;,
  username: &#39;hogehoge&#39;,
  password: &#39;fugafuga&#39;
};
&lt;/pre&gt;


&lt;h3&gt;登録確認処理の作成(http://localhost:8080/confirmの処理)&lt;/h3&gt;
&lt;p&gt;action.jsにPOSTで値を受け取れる関数を作成するために、index処理の下にconfirmという名前でスケルトンを作成します。リクエストをGETで受けるときは、exports.confirm.GETと変更します&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[action.js]&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
/**
 * confirm page (※POST Only)
 */
exports.confirm = {};
exports.confirm.POST = function (req) {

  //処理を記述する

}
&lt;/pre&gt;

&lt;p&gt;exports.confirm = {};でオブジェクトを生成しています。この初期化がないと、以下のようなエラーメッセージがでて怒られます。&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
TypeError: Cannot set property &quot;POST&quot; of undefined to 
&quot;org.mozilla.javascript.gen.actions_js_97@252a17&quot; (actions.js#19)
&lt;/pre&gt;

&lt;h5&gt;model.js、service.jsのrequire&lt;/h5&gt;
&lt;p&gt;
作成した、model.jsとservice.jsをコールすために、以下のrequire文をaction.jsの先頭に追加します。{}でくくることによって、model.js、service.jsの中にある指定したオブジェクトだけを呼び出すことができます。今回のmodel.js、service.jsともに1ファイルに1オブジェクトしかないので、あまりメリットが感じませんが、1ファイルに複数のオブジェクトが存在する場合は、使用しないオブジェクトはCallされないので、メモリ消費を抑えるメリットがでてくるんだと思います。
&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[action.js]&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
var {Member} = require(&#39;./model.js&#39;); 
var {Service} = require(&#39;./service.js&#39;);
&lt;/pre&gt;

&lt;h5&gt;リクエストパラメータのチェック&lt;/h5&gt;
&lt;p&gt;
action.jsに次のコードを追加します。Serviceオブジェクトのプロパティ(email, keyword)にリクエストパラメーターをセットします。その下で値が空かどうかのチェックを行っています。どちらかが空であった場合は、skins/index.htmlを呼び出し、テンプレートファイルのerrorMessage変数が赤色で出力されます。
&lt;/p&gt;
&lt;p&gt;
本格的にサービスを運営する場合は、このチェックだけでは不十分ですので、Validationチェックを行う必要があると思います。ここでは時間の都合で最低限の値チェックしか行わないでおきます。
&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[action.js]&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
  Service.email = req.params[&#39;email&#39;];
  Service.keyword = req.params[&#39;keyword&#39;];
  if (Service.email === &quot;&quot; || Service.keyword === &quot;&quot;) {
    return Response.skin(module.resolve(&#39;skins/index.html&#39;), {
      title: &#39;エラー&#39;,
      errorMessage: &#39;入力した値に問題があります、再度入力してください&#39;
    });
  }
&lt;/pre&gt;

&lt;p&gt;email、keywordを空にして、subimitを行い、エラー画面を表示してみます。赤丸で囲ったtitleの個所が「エラー」という文字に切り替わっており、errorMessageも赤色で「入力した値に問題があります、再度入力してください」表示されているのが確認できました&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://pics-nu7waum6.dotcloud.com/blojsom/resources/default/img/2011/it_study_search_error.gif&quot; alt=&quot;RingoJS&quot; title=&quot;RingoJS&quot;/&gt;&lt;/p&gt;

&lt;h5&gt;多重登録防止&lt;/h5&gt;
&lt;p&gt;多重登録が可能になってしまいますと、悪意ある第３者に登録確認のメールを利用したSpam装置に利用される危険性もあるので、ここでは多重登録防止機能を作成します。仕様的には、memberテーブルのstatus=waitingが同じメールアドレスで存在した場合は、エラーにしたいと思います。&lt;/p&gt;

&lt;p&gt;model.jsに次のメソッドを追加します。equals(&#39;email&#39;,service.email).select()でservice.emailと同じemailアドレスがないかどうか探しにいっています。result変数は配列で中身がかえってきますので、配列が1件以上存在していて、中にwaiting状態であれば、falseとしてエラーにしています&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[model.js]&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
  /**
   * メールアドレスをキーにして多重登録の有無を確認する
   */
  check: function() {
    var result = 
    this.loadObject.query().
        equals(&#39;email&#39;,service.email).select();

    if (result.length !== 0) {
      for each(res in result) {
        if (res.status === &#39;waiting&#39;) return false;
      }
    }
    return true;
  },
&lt;/pre&gt;

&lt;p&gt;
action.jsにMember.check()メソッドを追加してエラー処理を記述します。
&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[action.js]&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
  if (Member.check() === false) {
    return Response.skin(module.resolve(&#39;skins/index.html&#39;), {
      title: &#39;エラー&#39;,
      errorMessage: &#39;既に申し込み済みのメールが存在しております、&#39; +
                    &#39;お手元のメールを確認の上そちらを先に登録完了ください&#39;
    });    
  }
&lt;/pre&gt;

&lt;h5&gt;DBへの仮登録処理&lt;/h5&gt;
&lt;p&gt;
model.jsに次のconfirmメソッドを追加します。new this.loadObject()でオブジェクトを呼び出して、テーブルにinsertする値を代入していきます。keywordの代入では、Service.trim()メソッドで余計な空白を取り除き、複数のキーワードに対しては、カンマ区切りをService.commaReplace()メソッドで行っています。最後にinsertを行う、sqlstoreオブジェクトのsave()メソッドを実行しています。sqlstore自身Transactionも実行可能なのですが、今回は簡略化のため実装を省いています。
&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[model.js]&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
  /**
   * 仮登録処理
   */
  confirm: function(uid) {
    var member = new this.loadObject();
    member.email = Service.email;
    member.status = &#39;waiting&#39;;
    member.keyword = Service.commaReplace(Service.trim(Service.keyword));
    member.uid = uid;
    member.save();
  },
&lt;/pre&gt;

&lt;p&gt;service.jsにユニークIDを生成するメソッドを追加します。時間をキーにハッシュ化してユニークIDにしたいと思います。ハッシュ化関数として、RingoJSのコアモジュールにデフォルトでバンドルされているstringsモジュールを使用します。このモジュールは名前からしてわかりますように、文字列を操作するライブラリ群になっており、digest()メソッドも存在しています。内部でjava.security.MessageDigestクラスをラップしており、MessageDigestクラスで対応しているハッシュアルゴリズム(MD5、SHA-256など)が使用可能です。MD5は解読される可能性が高いのでセキュリティ的にはSHA-256などを使用すべきだと思いますが、簡略させるためMD5を使用しています。特にハッシュアルゴリズムを指定しない場合、自動的にMD5になります。&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[service.js]&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
var strings = require(&#39;ringo/utils/strings&#39;);

  /**
   * ユニークIDの作成
   */ 
  createUniqId: function() {
    return strings.digest(new Date().toString());
  },
&lt;/pre&gt;

&lt;p&gt;action.jsに作成したMember.confirm()メソッドを実装したいと思います。uidを生成して、Member.confirm()メソッドに渡します&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[action.js]&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
  var uid = Service.createUniqId();
  Member.confirm(uid);
&lt;/pre&gt;

&lt;h5&gt;メール送信処理&lt;/h5&gt;
&lt;p&gt;RingoJSのパッケージインストラーを使い、ringo-mailをインストールします。内部でjavax.mailをたたいているパッケージです&lt;/p&gt;
&lt;pre class=&quot;prettyprint bash&quot;&gt;
$ $RINGO_HOME/bin/ringo-admin install emilis/ringo-mail
&lt;/pre&gt;

&lt;p&gt;service.jsにsendMail()メソッドを追加します。値が変わる箇所としては宛先(To)とメール本文(Body)なので、それは外部から値を受けれるようにしておきます。それ以外の宛先(From)と件名(Subject)は、今回は固定値にするため、config.jsに追加したいと思います&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[service.js]&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
var email = require(&#39;ringo/mail&#39;);

  /**
   * 確認メールを入力されたメールアドレスに送信する
   */
  sendMail: function(to, body) {
    email.send({from: config.email.from, to: to, 
                subject: config.email.subject, text: body});   
  },
&lt;/pre&gt;

&lt;h5&gt;メールテンプレート作成&lt;/h5&gt;
&lt;p&gt;メールテンプレートもHTMLテンプレートの作成時に使用した、Response.skin()メソッドを使って実装しています。completeUrlはメール本文に出力される、登録完了ページ(次編で開発を行います)のURLになります。ユーザーを特定するためにURLのクエリストリングにuidを付属しDBにあるmemberテーブルのuidカラムとのマッチングを行うために使用します&lt;/p&gt;

&lt;p class=&quot;graybold&quot;&gt;[action.js]&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
var {Response} = require(&#39;ringo/webapp/response&#39;);

/**
 * メールフォーマットの作成
 */
exports.Service.textFormat = {
  confirm: function(uid) {
    var sv = exports.Service;
    return Response.skin(module.resolve
                         (&#39;skins/email/confirm.tpl&#39;),
                         { keyword: sv.commaReplace(sv.trim(sv.keyword)),
                           completeUrl : config.fqdn 
                           + &quot;/complete?uid=&quot; + uid}).body;
  },
};
&lt;/pre&gt;
&lt;p&gt;Response.skin()メソッドはhtmlテンプレートだけでなく、どのようなタイプのテンプレートでも対応できるようになっているので、オリジナルのメール専用のテンプレートを作成して変数をセットしてあげるだけでOKです&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
&lt;blockquote&gt;
　(1)HTMLテンプレートをセットする場合
  return Response.skin(module.resolve
                         (&#39;skins/index.html&#39;)
  ↓
　(2)メールテンプレートをセットする場合
  return Response.skin(module.resolve
                         (&#39;skins/email/confirm.tpl&#39;)
&lt;/blockquote&gt;
&lt;/pre&gt;

&lt;p&gt;skins/email/confirm.tplを作成します&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[skins/email/confirm.tpl]&lt;/p&gt;
&lt;pre class=&quot;prettyprint java&quot;&gt;
＜IT 勉強会サーチメール＞にお申し込み有り難うございます。

ご入力頂きましたメールアドレスに対して登録確認のための
メールを自動送信しています。

----------------------------------
検索キーワード：&lt;% keyword %&gt;
----------------------------------

ご確認がとれましたら、下記URLにアクセスして登録を
完了してください。
(※本メールをお受け取りになりましてから、3日以内に
登録を完了しませんと無効となります)

&lt;% completeUrl %&gt;

尚、本メールにつきまして心あたりがない方は
お手数ですが、破棄していただけますでしょうか。
&lt;/pre&gt;

&lt;p&gt;config.jsには次の値を追加します&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[config.js]&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
exports.fqdn = &#39;http://localhost:8080&#39;;
exports.email = {
  from: &#39;it_study_search@fukaoi.org&#39;,
  subject: &#39;IT勉強会サーチメール&#39;
};
&lt;/pre&gt;

&lt;h5&gt;actions.jsにメール送信する処理を実装&lt;/h5&gt;
&lt;p&gt;action.jsにメール送信のためにメールテンプレートから本文を作成して、それをService.sendMail()メソッドの第2引数として渡しています。そして仮登録のメッセージをResponse.skin()メソッドで設定しています。&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[action.js]&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
 Service.sendMail(Service.email, Service.textFormat.confirm(uid));

  return Response.skin(module.resolve(&#39;skins/index.html&#39;), {
    title: &#39;受付致しました&#39;,
    headMessage: &#39;入力いただいたメールアドレスに登録完了のURLを送信しました&#39;
  });
&lt;/pre&gt;

&lt;p&gt;実際に送信されたメールの内容です。検索キーワードとして「JavaScript 東京」と入力しました&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://pics-nu7waum6.dotcloud.com/blojsom/resources/default/img/2011/it_study_search_confirm_mail.gif&quot; alt=&quot;RingoJS&quot; title=&quot;RingoJS&quot;/&gt;&lt;/p&gt;

&lt;p&gt;仮登録完了したページは以下のように、テンプレートは共通のskins/index.htmlですが、赤丸で囲んだ個所で、title、headMessageの文言が上記設定したaction.jsのResponse.skin()メソッド内容に置き換わっていることが確認できます&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://pics-nu7waum6.dotcloud.com/blojsom/resources/default/img/2011/it_study_search_confirm.gif&quot; alt=&quot;RingoJS&quot; title=&quot;RingoJS&quot;/&gt;&lt;/p&gt;

&lt;p&gt;以上が仮登録機能の開発になります。次編から登録完了ページの開発に入ります。&lt;/p&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;subem&quot;&gt;
&lt;strong&gt;次のステップへ&lt;/strong&gt;
&lt;br /&gt;
&lt;a href=&quot;/2011/06/16/server-side-javascript-webdev3&quot; title=&quot;RingoJS&quot; alt=&quot;RingoJS&quot;&gt;Webフロント開発編(3)へ&lt;/a&gt;
&lt;/div&gt;
</description>
	    <dc:date>2011-05-30T00:20:31+00:00</dc:date>
	                                <wfw:comment>http://pics-nu7waum6.dotcloud.com/blojsom/commentapi/default/JavaScript/2011/05/30/server-side-javascript-webdev2</wfw:comment>
            <wfw:commentRss>http://blog.fukaoi.org/2011/05/30/server-side-javascript-webdev2?page=comments&amp;flavor=rss2</wfw:commentRss>
            </item>
            <item rdf:about="http://blog.fukaoi.org/2011/05/27/server-side-javascript-webdev1">
	    <title>サーバーサイドJavaScriptで簡単Webアプリ開発　- Webフロント開発編(1)</title>
	    <link>http://blog.fukaoi.org/2011/05/27/server-side-javascript-webdev1</link>
        <description>&lt;img src=&quot;http://pics-nu7waum6.dotcloud.com/blojsom/resources/default/img/ringojs.png&quot; alt=&quot;RingoJS&quot; title=&quot;RingoJS&quot;/&gt;
&lt;p&gt;
&lt;a href=&quot;/2011/05/26/server-side-javascript-startup&quot; title=&quot;RingoJS&quot; alt=&quot;RingoJS&quot;&gt;「スタート編」&lt;/a&gt;に続き、本編ではWebアプリの作成を開始します。今回作成するWebアプリのお題としては、エンジニアならよくお世話になっている、&lt;a href=&quot;https://www.google.com/calendar/embed?src=fvijvohm91uifvd9hratehf65k@group.calendar.google.com&amp;gsessionid=RdMc5kK0cQW9et8aIjOSzQ&quot; target=&quot;_blank&quot; title=&quot;IT勉強会&quot; alt=&quot;IT勉強会&quot;&gt;IT勉強会サイト&lt;/a&gt;を使わせてもらおうと思います。希望する勉強会のキーワードと通知メールアドレスを、今回作成するWebアプリに登録することによって、IT勉強会サイトに、そのキーワードにマッチする勉強会が登録された場合、登録メールアドレスに対して勉強会の内容を連絡してくれるというアプリです。
勉強会に参加しようと思っても、気になる内容のものがなかったり、気がついたときには人数が多くて参加できなかったりという過去の経験から思いつきました。
&lt;!--summary--&gt;
&lt;/p&gt;


&lt;h2&gt;概要設計&lt;/h2&gt;
&lt;p&gt;まずなんといっても、サイト名を決定しないといけません。IT勉強会をサーチするということで、「IT勉強会サーチ」、プロジェクトコードを「it_study_search」にします。
登録を行う側を&lt;strong&gt;Webフロント側&lt;/strong&gt;とし、RingoJSを通して、MySQLのDBにメールアドレスと検索キーワード(探したい勉強会のワード)を登録します。検索キーワードは複数条件を指定でき、AND検索とします。DBに登録されているデータを１日１回Cronでチェックして、Google Calendar APIに対して、検索キーワードを投げてマッチする勉強会が無いかどうか調べ、マッチした勉強会があれば、登録されているユーザのメールアドレスに通知を行います。このCronで行う処理側を&lt;strong&gt;バッチ処理側&lt;/strong&gt;と呼ぶことにします。下記にWebフロント側の画面遷移イメージをおこしたので添付します&lt;/p&gt;
&lt;p&gt;（画面遷移イメージ）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://pics-nu7waum6.dotcloud.com/blojsom/resources/default/img/2011/it_study_search_flow.gif&quot; alt=&quot;flow&quot; title=&quot;flow&quot;/&gt;&lt;/p&gt;

&lt;h2&gt;RingoJSのコマンドラインで雛型生成&lt;/h2&gt;
&lt;p&gt;下記コマンドで、it_study_searchプロジェクトのアプリケーション雛型を作成します。ここでは特に説明は必要ないかと思います。&lt;a href=&quot;/2011/05/26/server-side-javascript-startup&quot; title=&quot;RingoJS&quot; alt=&quot;RingoJS&quot;&gt;スタート編&lt;/a&gt;に書いた、公式サイト First Stepsを見てもらえばわかると思います。&lt;/p&gt;
&lt;p&gt;$RINGO_HOMEはRingoJSを設置したディレクトリをさします。&lt;/p&gt;
&lt;pre class=&quot;prettyprint bash&quot;&gt;
$ $RINGO_HOME/bin/ringo-admin create it_study_search 
&lt;/pre&gt;

&lt;p&gt;&lt;font color=&quot;red&quot;&gt;赤色&lt;/font&gt;のファイルが今回中身を実装するファイルとなります。雛形を作成した初期はもっとファイル数は少くなく、今後作成していくプログラムファイルが含まれています&lt;/p&gt;
&lt;pre&gt;
it_study_search/  (Webアプリ プロジェクトディレクトリ)
       | 
       |-- README.txt
       |-- &lt;font color=&quot;red&quot;&gt;actions.js&lt;/font&gt;  (コントローラー プログラムファイル)
       |-- &lt;font color=&quot;red&quot;&gt;batch.js&lt;/font&gt;　　(バッチ処理 プログラムファイル)
       |-- config
       |   |-- jetty.xml
       |   `-- log4j.properties
       |-- &lt;font color=&quot;red&quot;&gt;config.js&lt;/font&gt;  (設定ファイル)
       |-- main.js    (アプリケーションのエントリポイント)　    
       |-- &lt;font color=&quot;red&quot;&gt;model.js&lt;/font&gt;   (DB Entity プログラムファイル)　
       |-- public
       |   `-- stylesheets
       |       `-- &lt;font color=&quot;red&quot;&gt;page.css&lt;/font&gt;  (共通CSSファイル)
       |-- &lt;font color=&quot;red&quot;&gt;service.js&lt;/font&gt;        (ビジネスロジック プログラムファイル)
       `-- skins
           |-- base.html
           |-- &lt;font color=&quot;red&quot;&gt;complete.html&lt;/font&gt; (登録完了ページのhtmlスキン)
           |-- email
           |   |-- &lt;font color=&quot;red&quot;&gt;batch.tpl&lt;/font&gt;     (バッチ処理全体のmailスキン)
           |   |-- &lt;font color=&quot;red&quot;&gt;batchPart.tpl&lt;/font&gt; (バッチ処理の部分mailスキン)
           |   `-- &lt;font color=&quot;red&quot;&gt;confirm.tpl&lt;/font&gt;   (仮登録ページのmailスキン)
           |-- error.html
           |-- &lt;font color=&quot;red&quot;&gt;index.html&lt;/font&gt;        (申し込み、仮登録ページのhtmlスキン)
           `-- notfound.html
&lt;/pre&gt;

&lt;h2&gt;コーディング開始&lt;/h2&gt;
&lt;h3&gt;インデント&lt;/h3&gt;
&lt;p&gt;好みの問題なので、世間のオーソドックスなルールですとJavaScriptファイルのインデントはスペース4つが主流だと思いますが、ここではスペース2つでコーディングしています。&lt;/p&gt;
&lt;h3&gt;登録ページの作成(http://localhost:8080/indexのページ)&lt;/h3&gt;
&lt;p&gt;
メールアドレス、勉強会検索キーワードを入力するための、登録ページをまず作成します。action.jsのexports.indexを以下のように変更します。title、headMessageがhtmlテンプレートファイルに渡す変数と値になります。テンプレートファイル側では&lt;% %&gt;で値を取得します。
&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[action.js]&lt;/p&gt;
&lt;pre class=&quot;prettyprint javascript&quot;&gt;
var {Response} = require(&#39;ringo/webapp/response&#39;);

/**
 * index page
 */
exports.index = function (req) {
  return Response.skin(module.resolve(&#39;skins/index.html&#39;), {
    title: &#39;IT勉強会サーチ&#39;,
    headMessage: &#39;検索したい勉強会キーワードと受け取るメールアドレスを入力してください&#39;
  });
};
&lt;/pre&gt;

&lt;h5&gt;テンプレートファイルの修正&lt;/h5&gt;
&lt;p&gt;つぎに、skins/index.htmlを修正してIT勉強会サーチのためのhtmlに変更します。下記のようにhtmlファイルを修正します&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[skins/index.html]&lt;/p&gt;
&lt;pre class=&quot;prettyprint html&quot;&gt;
&amp;lt;% extends ./base.html %&amp;gt;

&amp;lt;% subskin content %&amp;gt;
&amp;lt;div id=&amp;quot;header&amp;quot;&amp;gt;&amp;lt;h1&amp;gt;&amp;lt;% title %&amp;gt;&amp;lt;/h1&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;body&amp;quot;&amp;gt;
&amp;lt;p id=&amp;quot;message&amp;quot;&amp;gt;&amp;lt;% errorMessage %&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;h3&amp;gt;&amp;lt;% headMessage %&amp;gt;&amp;lt;/h3&amp;gt;
&amp;lt;form action=&amp;quot;/confirm&amp;quot; method=&amp;quot;post&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;input type=&amp;quot;text&amp;quot; name=&amp;quot;email&amp;quot; value=&amp;quot;&amp;quot; size=&amp;quot;50&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;：&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;input type=&amp;quot;text&amp;quot; name=&amp;quot;keyword&amp;quot; value=&amp;quot;&amp;quot; size=&amp;quot;50&amp;quot;&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;メールアドレス&amp;amp;nbsp;&amp;amp;nbsp;：&amp;amp;nbsp;&amp;amp;nbsp;検索キーワード&amp;lt;/p&amp;gt;
&amp;lt;input type=&amp;quot;submit&amp;quot; name=&amp;quot;submit&amp;quot; value=&amp;quot;送信&amp;quot;&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;br /&amp;gt;
&amp;lt;br /&amp;gt;
&amp;lt;p&amp;gt;検索方法：&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
検索キーワードをスペース区切りで入力する事により、&amp;lt;br /&amp;gt;
AND検索になります。今のところOR検索はできません。
&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;

&lt;p&gt;public/stylesheets/page.cssにエラーメッセージを赤色強調表示させるためのCSS記述を追加します&lt;/p&gt;
&lt;p class=&quot;graybold&quot;&gt;[public/stylesheets/page.css]&lt;/p&gt;
&lt;pre class=&quot;prettyprint css&quot;&gt;
#message {
    color: red;
    font-size: 18px;
    font-weight: bold;
}
&lt;/pre&gt;

&lt;h5&gt;とりあえず起動&lt;/h5&gt;
&lt;p&gt;それではringoコマンドを使い、IT勉強会サーチアプリを起動させてます&lt;/p&gt;

&lt;pre class=&quot;prettyprint bash&quot;&gt;
$ $RINGO_HOME/bin/ringo it_study_search/main.js 
&lt;/pre&gt;
&lt;p&gt;http://localhost:8080/にアクセスすると、以下のような登録ページが表示されます&lt;/p&gt;
&lt;p&gt;
&lt;img src=&quot;http://pics-nu7waum6.dotcloud.com/blojsom/resources/default/img/2011/it_study_search_regist.gif&quot; alt=&quot;IT勉強会サーチ&quot; title=&quot;IT勉強会サーチ&quot;/&gt;
&lt;/p&gt;
&lt;p&gt;本編は登録ページのloadしかなかったので、特にロジカルな事はありませんでした、次編より、DB接続、メール送信などを使用する仮登録処理の開発に入ります&lt;/p&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;subem&quot;&gt;
&lt;strong&gt;次のステップへ&lt;/strong&gt;
&lt;br /&gt;
&lt;a href=&quot;/2011/05/27/server-side-javascript-webdev2&quot; title=&quot;RingoJS&quot; alt=&quot;RingoJS&quot;&gt;Webフロント開発編(2)へ&lt;/a&gt;
&lt;/div&gt;
</description>
	    <dc:date>2011-05-27T19:10:27+00:00</dc:date>
	                                <wfw:comment>http://pics-nu7waum6.dotcloud.com/blojsom/commentapi/default/JavaScript/2011/05/27/server-side-javascript-webdev1</wfw:comment>
            <wfw:commentRss>http://blog.fukaoi.org/2011/05/27/server-side-javascript-webdev1?page=comments&amp;flavor=rss2</wfw:commentRss>
            </item>
    
</rdf:RDF>

