Meteor で簡単なリアルタイム Web サイトを作成する

ユーザー操作にほぼ瞬時に反応するデータ駆動型アプリケーションを短時間で実装する

Web は至るところで広く使用されていることから、データ配信にほんのわずかな遅延があるだけでも、ユーザーをイライラさせることになりかねません。ユーザーはデータが即時に更新されることを期待しますが、あいにく Web テクノロジーは、そのようなリアルタイム・アクセスの要求に完全には追いついていないのが現状です。データ・アクセスについては、オブジェクト・リレーショナル・マッピング (Object-Relational Mapping: ORM) のいくつかのクラスへの標準化が急速に進んでいる一方で、リアルタイム通信にはそのようなソリューションがありません。この記事では、こうした問題を解決することを目指す画期的な新しい JavaScript フレームワークである Meteor について説明します。

David Berube, Proprietor, Berube Consulting

David Berube photoDavid Berube はコンサルタント、講演者、そして『Practical Rails Plugins』、『Practical Reporting with Ruby and Rails』、『Practical Ruby Gems』の著者です。また、『Dr. Dobb's Journal』や『Linux Pro Magazine』などの数々の雑誌の記事を書いています。彼は、多種多様なクライアントおよび業界を対象に、ソフトウェア開発およびパフォーマンスに関するコンサルティングを行っています。



2013年 5月 23日

Meteor の紹介

Meteor は、リアルタイムで動作する Web アプリケーションの開発を自動化して簡単に行えるようにすることを目的とする、新しい JavaScript フレームワークです。Meteor では、DDP (Distributed Data Protocol) というプロトコルを使用してリアルタイム通信を行います。DDP は、WebSocket を使用する最近のブラウザーでも、Ajax (Asynchronous JavaScript + XML) のロング・ポーリングを使用する古いブラウザーでもサポートされています。いずれの場合も、ブラウザー・サーバー間の通信はトランスペアレントに行われます。

DDP プロトコルは、JSON (JavaScript Serialized Object Notation) 文書のコレクションを処理するように設計されているため、このプロトコルを使用すると、簡単に JSON 文書の作成、更新、削除、照会、そしてもちろん表示をすることができます。また、DDP はオープン・ソース・プロトコルであるため、おそらく皆さんはこのプロトコルを使用して思いのままのクライアントやデータ・ストアに接続することができます。DDP は、追加の設定をしなくても MongoDB と連動します。

実際、Meteor は 2 つの MongoDB データベースを備えています。1 つはクライアント・サイドのキャッシュ・データベース、もう 1 つはサーバー上の MongoDB データベースです。ユーザーが (例えば、「保存」をクリックするなどの操作で) データに変更を加えると、ブラウザーで実行される JavaScript コードがローカル MongoDB 内の該当するデータベース・エントリーを更新してから、サーバーに対して DDP リクエストを送信します。JavaScript コードは、サーバーからの応答を待つ必要がないため、サーバーでの処理が成功したかのように、即座に後続の処理を続行します。その間、サーバーはバックグラウンドで更新処理を行います。サーバーでの処理が失敗した場合や、予期せぬ結果が返された場合には、クライアント・サイドの JavaScript コードはサーバーから新しく返されたデータに応じて即座に再調整を行います。この調整は、遅延補償 (latency compensation) と呼ばれ、ユーザーが感じるスピードをさらに速くします。

Meteor は、リアルタイム通信を容易にすることを明確な目的として設計されており、それは Meteor のテンプレート・システムについても言えることです。ほとんどの Web フレームワークでは、簡単に HTML (HyperText Markup Language) や HTML 相当のマークアップ (Haml (HTML Abstraction Markup Language) など) をコードに埋め込めるようになっているため、ユーザーに送信されるページに、データベースからの動的な値を挿入するのは簡単です。ただし、その場合、データに加えられた変更を検知してマークアップを更新するためのシステムを用意しなければなりません。一方、Meteor のテンプレート・システムは、テンプレートからどのデータにアクセスしたかを記録するように作られており、基になるデータが変更されると、それに対応する HTML を変更するためのコールバックを自動的にセットアップします。この仕組みにより、リアルタイム・テンプレートの作成を容易かつ迅速に行えるようになっています。


例: リンクの人気コンテスト

Meteor では、そのテンプレート機能により、さまざまなリアルタイム・アプリケーションを極めて容易に作成することができます。例えば、ユーザーがリンク (つまり、URL (Uniform Resource Locator)) を入力して、それぞれのリンクを好きか嫌いかで投票できるようにするサイトを作成し、人気コンテストで得票数の多い URL が一覧の先頭に表示されるようにするとします。Meteor を使えば、このようなアプリケーションをリアルタイム・アプリケーションとして容易に作成し、ユーザーから 65 の投票があるとすると、その状況が各ユーザーにリアルタイムで表示されるようにすることができます。


Meteor のインストール

Meteor をインストールするには、リスト 1 に記載するコードを Linux または Mac OS X ターミナルに入力します。Meteor は Microsoft Windows をサポートしていません。

リスト 1. Meteor のインストール
curl https://install.meteor.com > install_meteor.sh
chmod u+x install_meteor.sh
./install_meteor.sh

これで、新しいプロジェクトの作成に取り掛かることができます。


新規プロジェクトの作成

meteor コマンドは、新規プロジェクトを、Meteor で扱う必要があるすべてのコンポーネントと併せて作成するプロセスを自動的に実行します。リスト 2 に記載するコマンドを入力して、「realtime_links」という名前のプロジェクトを作成してください。

リスト 2. Meteor プロジェクトの作成
meteor realtime_links
cd realtime_links

Meteor によって、HTML ファイル、JavaScript ファイル、CSS (Cascading Style Sheets) ファイルが含まれるディレクトリーが作成されます。CSS ファイルは標準的なものですが、HTML ファイルと JavaScript ファイルについては説明しておく必要があります。これらのファイル (realtime_links.html と realtime_links.js) については、両方とも「ダウンロード」セクションから完全なものをダウンロードすることができます。


realtime_links.html ファイル

リスト 3 に、realtime_links.html ファイルのヘッダーと本体の抜粋を示します。

リスト 3. realtime_links.html のヘッダーと本体の抜粋
<head>
<title>Realtime Links Demo</title>
</head>

<body>
  {{> header }}
  {{> link_list }}
  {{> add_new_link }}
</body>

ご覧のように、HTML テンプレートの開始部分は至って単純です。BODY タグや DOCTYPE 宣言、さらには JavaScript ファイルや CSS ファイルの読み込みについて気に掛ける必要がありません。そのすべてを Meteor が自動的に処理してくれます。Meteor の JavaScript と CSS のバンドルについては、「参考文献」に記載されている Meteor Web サイトへのリンクを参照してください。

{{> 構文は、「このテンプレートをレンダリングする」ことを意味します。上記のリストを見るとわかるように、realtime_links.html は 3 つのテンプレートをレンダリングします。

  • header テンプレートは、データベース内にあるリンクの数を示す単純なヘッダー用テンプレートです。
  • link_list テンプレートは、リンクの一覧と各リンクに関連付けられた投票数を表示するためのテンプレートです。
  • add_new_link テンプレートは、新しいリンクを追加するためのフォーム用テンプレートです。

リスト 4 に、header テンプレートを示します。

リスト 4. realtime_links.html の header テンプレート
<template name="header">


<h1>The Link Collection</h1>

	<p>We currently have {{collection_size}} links.</p>

</template>

header テンプレートは、h1 タグと、コレクションのサイズについての短い記述をレンダリングするだけです。collection_size メソッドは、realtime_links.js という JavaScript ファイルに定義されています (このファイルについては、次のセクションで詳しく説明します)。Meteor は、テンプレートによって挿入されたデータを自動的に検知します。そのため、コレクションのサイズが更新されると、header テンプレートも自動的に更新されます。

ここで使用されている {{ ... }} 構文は、Ruby on Rails での <%= ... %> や、PHP での <?= ... ?> と似ており、波括弧内には任意のコードを含めることができます。したがって、動的な式で有用なものは、どのような式でもこの構文を使って挿入することができます。

リスト 5 に、link_list テンプレートを示します。

リスト 5. realtime_links.html の link_list テンプレート
<template name=
"link_list">

  <ul>

    {{#each links }}

      <li>  {{> link_detail }} </li>

    {{/each }}

  </ul>

</template>

おわかりのとおり、リスト 5 のコードはリンクの一覧になります。この一覧は、realtime_links.js JavaScript ファイルの links メソッドによって作成されます。各リンクは、link_detail テンプレートによってレンダリングされます。注目する点として、引数を渡す必要はありません。それは、Handlebars の #each ループが各繰り返しの現行コンテキストを現在のオブジェクトに設定するためです。言い換えると、link_detail テンプレートのローカル・メソッドは、各リンク・オブジェクトのメソッドとして正しく解釈されます。

リスト 6 に、link_detail テンプレートを示します。個々のリンクに表示されるデータは、このテンプレートが制御します。

リスト 6. realtime_links.html の link_detail テンプレート
<template name="link_detail">


<div id="link-{{id}}">

    <h1>{{url}}</h1>

    <p><strong>Stats:</strong> up: {{thumbs_up}} down: {{thumbs_down}} 
net score: {{score}}</p>


<input type="button" value="Thumbs Up" 
 class="thumbs_up" url="{{url}}" />
    <input type="button" value="Thumbs Down" 
class="thumbs_down" url="{{url}}" />


</div>

</template>

h1 要素は、単に現在のリンクの URL を表示するのみです。これに続く短い統計の記載には、そのリンクに対して賛成票が投じられた回数、反対票が投じられた回数、そしてこの 2 つの値の差である最終スコアが含まれます。最後にある 2 つのボタンは、賛成票を投じるためのボタンと、反対票を投じるためのボタンです。これらのボタンの振る舞いは JavaScript ファイルで定義されますが、その話題に移る前に、説明しておかなければならないテンプレートがもう 1 つあります。

リスト 7 に、add_new_link テンプレートを示します。

リスト 7. realtime_links.html の add_new_link テンプレート
<template name="add_new_link">

  <div id="new_link_form">

    URL: <input id="url">

<input type="button" value="Click" id="add_url" />

  </div>

</template>

このテンプレートには、テキスト入力フィールドとボタンが 1 つずつあるのみであり、これらから、新しい URL を一覧に追加するためのインターフェースが作られます。


realtime_links.js ファイル

realtime_links.js 内の JavaScript コードは、プログラムからのデータ・アクセスとイベント・コールバックを、クライアント上とサーバー上の両方で制御します。if (Meteor.is_client) という文は、クライアント・サイドの処理を示し、if (Meteor.is_server) という文はサーバー・サイドの処理を示します。Meteor には機密性の高いコードを保護する手段が用意されているため、不正なクライアントがソースを見ることはできません。詳細については、「参考文献」に記載されている Meteor のドキュメントへのリンクを参照してください。

リスト 8 に、ヘッダーのヘルパー関数とリンク一覧のヘルパー関数を示します。

リスト 8. ヘッダーのヘルパー関数とリンク一覧のヘルパー関数
Template.header.collection_size = function () {
return Links.find({}).count();
	};


	Template.link_list.links = function () {
return Links.find({}, {sort : {score: -1} });
	};

このリストの最初に記載されているヘルパー関数は、header テンプレートが使用します。この関数は、links コレクションのサイズを返すだけのものです。2 番目のヘルパー関数は、link_list テンプレートが使用します。この関数は、すべてのリンクをスコアが最も高いものから最も低いものの順にソートして返します。

リスト 9 に、link_detail テンプレートの 2 つのイベント・コールバックを示します。

リスト 9. link_detail のイベント・コールバック
	Template.link_detail.events = 
{

'click input.thumbs_up' : function () {
Meteor.call('vote', this.url, 'thumbs_up');
},

  'click input.thumbs_down' : function 
() {Meteor.call('vote', this.url, 'thumbs_down');}

	};

この 2 つのイベント・コールバックは、それぞれ賛成票のクリック・イベント、反対票のクリック・イベントを処理します。いずれのイベント・コールバックも、クライアント・サイドの Meteor.call を使用して、サーバー・サイドで関数を呼び出します。ご覧のように、クライアントからサーバーを呼び出すのは簡単です。例えば、シリアライズは自動的に処理されます。

リスト 10 に、ユーザーが新しいリンクを追加するために使用するフォームのイベント・コールバックを示します。

リスト 10. 新規リンクの追加フォームのイベント・コールバック
  Template.add_new_link.events = {

    'click input#add_url' : 
dfunction () {

var new_url = $('#url').val();

      var url_row = Links.findOne( {url:new_url} );

      if(!url_row){

Links.insert( { url : new_url,
score: 0,
thumbs_up: 0,

thumbs_down: 0 });
      }
Meteor.call('vote', url, 'thumbs_up');

    }
  };

このフォームは始めに、要求された URL を値に持つ既存のリンク・オブジェクトを探します。見つかった場合、その要求を既存のリンク・オブジェクトの票としてカウントします。該当するリンク・オブジェクトが見つからない場合は、新しいリンク・オブジェクトを作成し、その新規オブジェクトの thumbs_up 票を作成します。

上記のコードは、まだ本番での使用は意図されていない最先端のテクノロジーとしての Meteor の能力と制約の両方を明らかにしています。ご覧のように、クライアントは links コレクションで単純に insert を呼び出すことができます。これは開発者にとっては便利なことですが、セキュリティーの観点から見るとかなり問題があります。幸い、Meteor の開発者たちは、Meteor の魅力になっているその能力と柔軟性の大半を維持しつつ、強力な認証機能を実装する auth コード・ブランチにアクティブに取り組んでいるところです。

さらに、Meteor は現時点では MongoDB のすべての機能を実装しているわけではないことも認識しておいてください。例えば、データがなければ新しく挿入し、既存のデータがあればそれを更新するという MongoDB のアップサート機能を、Meteor はサポートしていません。Meteor がアップサートをサポートしているとしたら、リスト 11 の関数を作成できることになります。

リスト 11. アップサートを使用した、架空の新規リンク追加フォームのイベント・コールバック
  Template.add_new_link.events = {

    'click input#add_url' : function () {

      var new_url = $('#url').val();

       Links.update( { url : new_url}, 
                     { $set: {url : new_url}, 
                       $inc: { votes : 1 } } , true );


    }
  };

上記コードからわかるように、アップサートを使用するとコードが短くなります。また、サーバーとの間のラウンドトリップは 1 回で済むため、スピードも速くなるはずです。近いうちに、Meteor がアップサートやその他の新しい機能のサポートを実装してくれることを願っています。

リスト 12 のコードは、サーバー上で実行されます。これは、クライアント・サイドのコードから呼び出すことができる単一のメソッドです。この vote というメソッドを使用して、クライアントは特定の URL に対して thumbs_up または thumbs_down を投票することができます。このメソッドは、Mongo の $inc 演算子を使用して該当する投票カウンターをインクリメントします。また、必要に応じて合計スコアのインクリメントやデクリメントを行います。Meteor.startup メソッドを使用することで、このコードの実行をサーバーの起動時だけに制限できます。これに続く Meteor.methods 関数は、リスト 9 に記載した Meteor.call を使用してクライアントで呼び出せる関数を定義します。

リスト 12. サーバー・サイドの vote メソッドのコード
if (Meteor.is_server) {
    Meteor.startup(function () {
      Meteor.methods({
        vote: function (url, field){

new_obj = { $inc: { } };

 if(field =='thumbs_up'){
new_obj['$inc']['thumbs_up'] = 1;
 new_obj['$inc']['score'] = 1;
 }else{
new_obj['$inc']['thumbs_down'] = 1;
 new_obj['$inc']['score'] = -1;
                }

                Links.update( { url : url}, new_obj );

              }
      });
    });
}

リスト 10 のコードと同じく、リスト 12 のコードはクライアントで実行することもできますが、説明のために、このコードはサーバー上で実行します。Meteor のセキュリティー・モデルが進化するにつれ、機密性の高いコードも、このリストに記載されているサーバー・サイドの関数と同じように開発されるようになるはずです。


アプリケーションの動作の確認

この時点で、リスト 13 に示すように Meteor アプリケーションを起動して、その動作を確認することができます。

リスト 13. システムの起動
meteor

起動後の Meteor は、ポート 3000 で実行されます。Web ブラウザーを開いて、http://localhost:3000/ にナビゲートしてください。

Add a URL (URLを追加)」で任意の URL を入力して「Add (追加)」をクリックすると、その URL がスコア 1 として表示されます。そして、その URL に対して、賛成票を投じるための「Thumbs Up」ボタン、または反対票を投じるための「Thumbs Down」ボタンをクリックできるようになります。これは、ページの更新をしなくても、リアルタイムで実現されます。Web ブラウザーで新しいウィンドウを開いて同じ操作を行うと、そのウィンドウで行った変更が最初のウィンドウに瞬時に反映されます。

複数の URL を追加すると、最もスコアの高い URL が先頭に表示されます (スコアは、賛成票の数から反対票の数を引いた値として定義されています)。賛成票や反対票を投じると、URL がランキングの変化に応じて一覧内で上下に移動します。複数のユーザーがそれぞれのブラウザーで投票を行っている間、すべてのユーザーがリアルタイムで同じデータを受け取ることになります。


まとめ

Meteor は、多くの興味深い概念が盛り込まれた最先端の Web フレームワークです。特に他のテクノロジーではリアルタイム・サポートがせいぜい後からの付け足しでしかないことを考えると、Meteor のリアルタイム・データのサポートは、魅力的であると同時に重要でもあります。リアルタイムでのやりとりが Web の将来にとって重要性を増すにつれ、複雑なデータ・セットをリアルタイムで簡単かつ迅速に処理できる Meteor の能力は、これからもさらに重要なものになっていくはずです。


ダウンロード

内容ファイル名サイズ
Example source code for this articlerealtime_links.zip2KB

参考文献

学ぶために

製品や技術を入手するために

  • Meteor: 簡単に使用できる、強力なリアルタイム Web アプリケーション・フレームワークです。
  • MongoDB: 優れたドキュメント指向のデータベースです。
  • CoffeScript: この強力な JavaScript 拡張機能には Meteor プラグインも含まれているので、Meteor アプリケーションで使用できます。
  • IBM 製品の評価版: DB2、Lotus、Rational、Tivoli、および WebSphere のアプリケーション開発ツールとミドルウェア製品を体験するには、評価版をダウンロードするか、IBM SOA Sandbox のオンライン試用版を調べてください。

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Web development, Open source
ArticleID=929813
ArticleTitle=Meteor で簡単なリアルタイム Web サイトを作成する
publish-date=05232013