Meteor を使用したインスタント Web アプリケーション

構想から拡張デプロイメントまで、驚くほどの速さで応答性の良い Web アプリケーションを構築する

新しい Web アプリケーション開発プラットフォームである Meteor を使用すれば、JavaScript 開発者は極めてインタラクティブかつ応答性の良いリッチ・クライアント Web アプリケーションを迅速かつ簡単に構築することができます。Meteor は、開発サイクルを単純化して劇的に短縮することを目的に、Web アプリケーションの設計および開発についての新たな考え方を提案します。この記事では、Sing Li が Meteor の真実に迫り、2 つの実用的で、ありきたりではないサンプル・アプリケーション (Web ベースのアプリケーションと、モバイル・アプリケーション) を通して Meteor の有望さを探るとともに、Meteor のアーキテクチャーについて詳しく説明します。また、よく使用されている業界標準の JavaScript ライブラリーを利用して、Meteor によるアプリケーションを実際に構築します。

Sing Li, Consultant, Makawave

Author photoSing Li は、developerWorks サイトの開設以来、Web や Java に関するさまざまなトピックを取り上げて記事とチュートリアルを書いている developerWorks の著者です。組み込みシステムからスケーラブルなエンタープライズ・システムに至るまで、20 年を超えるシステム・エンジニアリングの経験があり、現在は再び Web 規模のモバイル対応サービスと「モノのインターネット」エコシステムに取り組んでいます。



2013年 7月 11日

新しい Web アプリケーション開発プラットフォームとして誕生した Meteor は、世界中で広く採用されるようになっています。Meteor は、JavaScript のコーディング・フレームワークであるだけでなく、スケーラブルでリッチなインタラクティブ Web アプリケーションを構築する革新的な手段になります。Meteor は、開発サイクルの期間短縮を約束します。その方法は、コーディング・モデルを単純化して、開発者が作成しなければならないコードの量を削減するというものです。経験を積んだ Web アプリケーションのアーキテクトや開発者が Meteor を使用すれば、構想から完全なデプロイメントを完了するまでに通常は数ヶ月以上かかるところを、数日または数週間にまで短縮することができます。

Meteor プラットフォームをインストールして開発を開始するまでの手順を説明するガイドとしては、developerWorks の記事「Meteor で簡単なリアルタイム Web サイトを作成する」が素晴らしいリソースとなります。皆さんが読んでいるこの記事では、2 つのサンプル・アプリケーションの詳細を通して Meteor による開発を深く掘り下げるとともに、Meteor アーキテクチャーの概要を説明します。この記事を読んで得た知識を使えば、Meteor で迅速に Web アプリケーションを作成するという方法が、自分の場合に適した方法であるかどうかを判断することができます。

過去から現在を振り返る

Meteor が提案する手法は、ある意味では革命的ですが、進化の結果という側面もあります。Meteor は、コンピューティングの歴史における大きな成功の 1 つである、スプレッドシート・ソフトウェアと同じ IT 路線を辿っています。典型的なスプレットシートの一例として、図 1 に地域ごとの売上高とその円グラフで構成されるスプレッドシートを示します。

図 1. 地域別売上高を示すスプレッドシート
地域別の売上高と総売上高の円グラフで構成される典型的なスプレッドシートのスクリーン・キャプチャー

一般的なスプレッドシートを試してみてください

図 1 のスプレッドシートは、この記事のサンプル・コードのダウンロードに含まれているので、実際に試してみることができます。売上高を変更すると、円グラフが自動的に更新されることがわかります。

地域別売上高のスプレッドシートでいずれかの地域の売上高を変更すると、総売上高 (上記の図には表示されていません) が変更されると同時に円グラフが瞬時に再描画され、新しい値がスライスの相対的割合に反映されます。

このようなスプレッドシートは、最近では目新しくも、興味深くもありません。しかし、自分が 1983年に Lotus 1-2-3 でこのような機能が公表された当時の PC ユーザーだったとしたらどうでしょう。その影響力を高く評価したはずです。それまでは、非常に少ないプログラミング作業で、これほどの成果を上げることはできませんでした。初期のスプレッドシート・ソフトウェアは直観的ではありませんでしたが、ほとんどのユーザーは数日のうちに難なく操作できるようになりました。スプレッドシート・ソフトウェアは、今でも世界的に PC の売り上げを促進するキラー・アプリの 1 つとなっています。

30 年後の今

最初のスプレッドシート・ソフトウェアが登場してから 30 年を経た今、スプレッドシートが象徴するものが進化した姿を Meteor で目にすることができます。図 2 に、2013年に Meteor で作成した販売実績ポータル Web アプリケーションを示します。

図 2. 販売実績ポータル Web アプリケーション
地域ごとの売上高と総売上高の円グラフを表示する、Meteor で作成した地域別売上高アプリケーションのスクリーン・キャプチャー

販売実績ポータルには、最新の地域別売上高と、それに対応する円グラフが表示されます。このポータルでは、CEO は売上高をモニターすることができ、各地域販売チームはそのチームの売上高を定期的に更新することができます。

Meteor をインストール済みの場合、この販売実績ポータル・アプリケーションをダウンロードして、実際に使ってみることができます。それには、ダウンロードに含まれるインストール・コードを実行して作成された sales_nologin サブディレクトリーへとカレント・ディレクトリーを変更し、「meteor run」と入力します。ブラウザー・インスタンスで http://localhost:3000/ にアクセスすると、地域別売上高と円グラフが表示されるので、任意の売上高をダブルクリックして値を変更してください。変更を確定すると、瞬時に円グラフが更新されます。複数のブラウザー・インスタンスで販売実績ポータルにアクセスしているとしたら、そのすべてのインスタンスに最新の売上高が表示され、どのブラウザー・インスタンスからでも数値を変更することができます (Meteor のインストールが不可能な場合、このアプリケーションがホストされたバージョンを試すことができます。「参考文献」を参照してください)。

図 3 では、US Central チームの売上高を選択して、その数値を更新しています。

図 3. US Central の売上高の更新
Meteor で作成された販売実績ポータル Web アプリケーションで、ユーザーが US Central 地域の売上高を選択してその数値を更新している画面のスクリーン・キャプチャー

図 4 に、更新後の US Central の売上高と最新の円グラフを示します。更新時には、販売実績ポータルに同時にアクセスしているユーザーのすべてに対し、瞬時に変更が表示されます。

図 4. 新しい US Central の売上高を反映して更新された円グラフの構成比率
新しい US Central の売上高を反映して更新された円グラフのスクリーン・キャプチャー

手動による更新の代わりに、バックエンドで売上高を構成する小計が自律的に収集および集約されて、売上高が更新される場合も考えられます。販売実績ポータル・アプリケーションの視覚的表現は由緒ある昔のスプレッドシートとまったく同じですが、このアプリケーションは以下の機能も備えています。

  • 至るところで利用できるブラウザーを介した世界中からのインターネット・アクセス
  • 複数のユーザーによる同時アクセス
  • オプションの自動バックエンド・データ収集および集約

このようなシステムを標準的なエンタープライズ・テクノロジー (例えば、Javaベースのツール・チェーン) を使用して設計、コーディング、デプロイするとなると、かなりの作業が必要になります。しかし Meteor を使用することで、こうした作業が大幅に減ります。それは、この後に記載されているコードを順に見ていくとわかります。

リアクティブな考え方

Meteor のリアクティブなデフォルト

Meteor には、デフォルトでリアクティブになる特定のデータ・ソースがあります。現在、これらのソースには以下のものがあります。

  • Meteor コレクション — 通常は、MongoDB クエリーの結果
  • Meteor の Session シングルトンに明示的にバインドされた変数
  • Meteor.userMeteor.userId、および Meteor.logginIn (これらは、現在のユーザーおよびログインの状況を追跡します)
  • Meteor.status (サーバー接続状況を追跡します)

スプレッドシートの重要な特徴の 1 つは、そのリアクティブな性質です。地域別売上高の例では、地域の売上高が更新されると、その売上高に依存するすべての値がオンザフライで再計算されます。従属コンポーネントがグラフィック出力 (例えば、円グラフ) をレンダリングする場合、そのグラフも即時に再描画されて、スライスのサイズが更新されます。依存関係を管理する (複雑になりがちな) コードや、円グラフなどのコンポーネントを更新するコードを作成する必要はありません。必要な作業は、リアクティブ要素 (売上高) とその依存関係 (この例の場合、総売上高と円グラフ) を宣言することだけです。後はすべて、スプレッドシートが処理してくれます。

この手法を最近の Web アプリケーションに当てはめて考えてみると、Meteor によって Web ベースのシステムを作成するのがいかに簡単になるかを想像できます。

Meteor アプリケーションを設計するときには、まず初めにリアクティブ要素 (例えば、地域の売上高データのコレクション) を決定します。次に、標準的な HTML、CSS、クライアント・サイドの JavaScript ライブラリーとコンポーネント (jQuery、jQuery UI、または Underscore など)、Handlebars (概念の上では JavaServer Pages と同様で、通常はクライアント・サイドで動作します。「参考文献」を参照) などのテンプレート・テクノロジーを使用して、プレゼンテーション層をレイアウトします。Meteor はリアクティブ要素のあらゆる依存関係を追跡して、ビジュアル要素を再レンダリングし、依存関係を再計算して更新後の最新データを反映させます。

この手法は、作成してデバッグおよびテストしなければならないインフラストラクチャー・コードの量を大幅に減らします。更新リクエストを同期させるためのカスタム・バックエンド Web サービスを作成する必要がなく、データベースやデータ・ストアを更新するためのコードや、接続されたクライアントに変更通知をプッシュするためのコード、あるいは通知の受信時にバックエンドから更新後の値を取得するためのコードも、一切作成する必要はありません。


販売実績ポータルのコードの詳細

リスト 1 に、販売実績ポータル・アプリケーションの背後にあるサーバー・サイド・ロジックとクライアント・サイド・ロジックがすべて含まれた sales.js ファイルを記載します。このアプリケーションのために作成しなければならなかったのは、この JavaScript コードだけです (sales.js は、記事からコードをダウンロードしてインストールすると作成される sales_nologin ディレクトリー内にあります)。

リスト 1. 販売実績ポータルのクライアント・サイド・ロジックとサーバー・サイド・ロジック: sales.js
Sales2013 = new Meteor.Collection("regional_sales");

if (Meteor.is_client) {
  Template.salesdata.dataset = function () {
    return Sales2013.find({});
  };

  Template.datapoint.selected = function () {
    return Session.equals("selected_datapoint", this._id) ? "selected" : '';
  };

  Template.datapoint.events = {
    'click': function () {
      Session.set("selected_datapoint", this._id);

    }
  };

Template.salesdata.rendered = function()
{

  $('.editable').editable(function(value, settings) { 
     Sales2013.update(Session.get("selected_datapoint"), 
 {$set: {total: parseInt(value)}});
     return(value);
  }, { 
     type    : 'text',
     style : 'inherit',
     width : 100,
     submit  : 'OK',
 });

     var cur = Sales2013.find();
     if (cur.count() === 0)  // do not render pie if no data
       return;
     var data = [];
     cur.forEach( function(sale) {
       data.push( [sale.region, sale.total]);
     });
  var plot1 = $.jqplot ('chart', [data], 
    { 
      seriesDefaults: {
 // Make this a pie chart.
 renderer: $.jqplot.PieRenderer, 
 rendererOptions: {
   // Put data labels on the pie slices.
   // By default, labels show the percentage of the slice.
   showDataLabels: true
 }
      }, 
      legend: { show:true, location: 'e' }
    }
  ); 
}

}

if (Meteor.is_server) {
  Meteor.startup(function () {
      Sales2013.remove({});
      Sales2013.insert({region:"US East", total: 2032333});
      Sales2013.insert({region:"US Central", total: 150332});
      Sales2013.insert({region:"US West", total: 1202412});
      Sales2013.insert({region:"Asia Pacific", total: 701223});
  });
}

リスト 1Meteor.is_client 変数と Meteor.is_server 変数の周囲にある条件文をよく見てください。これらの条件文は、Meteor のコアによって提供されるランタイム・コンテキスト・インディケーターで、コード内のどこからでも使用することができます。この例では、これらの条件文により、同じ source.js ファイル内でクライアント・サイドのコードとサーバー・サイドのコードを組み合わせて使用できるようにしています。条件文の外側にあるコードはすべて、クライアントとサーバーの両方で実行されます。

別の方法として、クライアント・サイドのコードを client という名前のサブディレクトリーに置き、サーバー・サイドのコードを server という名前のサブディレクトリーに置くことで、クライアントとサーバーのソース・コードを完全に分離することもできます。そのシナリオでは、クライアントとサーバーの両方に共通して必要なアセットを public というサブディレクトリーに配置します。後ほど説明するセキュリティーを強化したバージョンの販売実績ポータル・アプリケーションでは、このディレクトリー構造を使用します。

リアクティブなデータを識別する

その他のリアクティブ・データ・ソース

リスト 1selected_datapoint セッション変数も同じくリアクティブです (デフォルトでリアクティブな要素については、囲み記事「Meteor のリアクティブなデフォルト」を参照)。この変数は、強調表示されている売上高の行が変更された場合に使用されます。行の強調表示は、動的 CSS スタイルの変更によって行われます。selected_datapoint セッション変数は、ユーザーが行をクリックすると更新されます。この変数が変更されるたびに Meteor は依存関係を再レンダリングするため、強調表示もそれに応じて更新されます。

販売実績ポータル・アプリケーションのリアクティブ・データ・ソースの 1 つは、Sales2013 という Meteor コレクションのクエリーです。このクエリーは、リスト 1 から抜粋した下記クライアント・サイドのコード・フラグメントで使われています。

Template.salesdata.dataset = function () {
   return Sales2013.find({});
};

このクエリーはリアクティブであるため、その依存関係のすべては、クエリーの結果セットが変わると再計算または再レンダリングされます。このようにして、すべてのブラウザー・インスタンスにわたって売上高と円グラフが更新されます。リスト 2 に、関連する HTML テンプレート・コードを記載します。このコードは、記事からコードをダウンロードしてインストールすると作成される sales_nologin ディレクトリー内にある sales.html ファイルに含まれています。

リスト 2. クライアント・サイドの HTML およびテンプレート: sales.html
<head>

<title>Sales by Region</title>
</head>

<body>
  <div id="title">
    <h1>Global Sales 2013</h1>
  </div>
  <div id="container">

    <div id="salestable">
      {{> salesdata}}
    </div>
    <div id="chart">
    </div>
  </div>

</body>



<template name="salesdata">
  <div class="salesdata">
    {{#each dataset}}
      {{> datapoint}}
    {{/each}}
  </div>
</template>


<template name="datapoint">
  <div class="datapoint {{selected}}">
    <span class="region">{{region}}</span>
    <span class="sales editable">{{total}}</span>
  </div>
</template>

リスト 2 の HTML ファイルは、Meteor が現在サポートしている Handlebars テンプレートです。ご覧のように Handlebars の式は {{ }} で囲まれています。このテンプレートの Spark エンジン (この記事の「最近の Web アプリケーションのアーキテクチャー」セクションで説明します) により、Meteor は他の JavaScript テンプレート・コンポーネントを扱うことができます。

売上高の行は、salesdata テンプレート・コード (リスト 2 に太字で強調表示されたコード) によってレンダリングされます。このテンプレートは dataset ヘルパー関数 (リスト 1 を参照) に依存するため、その内側にあるリアクティブ・クエリーが変更されるたびに再レンダリングされます。

サーバーにサンプル・データを投入する

販売実績ポータルの地域売上高の初期データは、リスト 3 に記載するサーバー・サイドのコード (リスト 1 からの抜粋) によって投入されます。

リスト 3. MongoDB にデータを投入するサーバー・サイドのコード
if (Meteor.is_server) {
  Meteor.startup(function () {
      Sales2013.remove({});
      Sales2013.insert({region:"US East", total: 2032333});
      Sales2013.insert({region:"US Central", total: 150332});
      Sales2013.insert({region:"US West", total: 1202412});
      Sales2013.insert({region:"Asia Pacific", total: 701223});
  });
}

Meteor での遅延補償

Meteor には、「遅延補償 (latency compensation)」と呼ばれる仕組みがあります。遅延補償とは基本的に、ビッグ・データ管理における「結果整合性」の概念のビジュアル・バージョンです。Minimongo スタブによってクライアント上でデータを更新すると、クライアント上では即時にすべての変更が反映されます (これにはリアクティブ再レンダリングも含まれます)。変更はサーバーにも伝播されますが、伝播された変更はさまざまな理由 (アクセス拒否を含む) で正しく適用されない可能性があります。最終的に (通常は、極めてすぐに) サーバーの実際の状態がクライアントに反映されることを確実にするのは、パブリッシュ・サブスクライブ・メカニズムですが、遅延補償は、最近の Web 2.0 アプリケーションの特質である、遅延のない極めて応答性の良い UI を可能にします。その代償は、ビジュアル・データの整合性が一瞬失われる可能性があることです。

Meteor サーバー上では、完全な MongoDB インスタンスが稼働中になります。この完全なインスタンスは、Meteor 以外のクライアントからのクエリーと更新を受け入れることができます。

Meteor クライアントでも、同じ JavaScript MongoDB API が使用可能になります。これにより、クライアントとサーバーのコードが 1 つに統合され、クライアントとサーバーの両方でコードを再利用することが可能になります。クライアント・サイドの API は、Minimongo という名前のスマート・スタブによって提供されます。Minimongo は遅延補償を使用してデータベースの変更を反映します。Minimongo が扱うのは一般にクライアント・サイドの小さなデータ・セットであるため、Minimongo は索引付けをサポートしていません。

MongoDB サーバーと Minimongo クライアントの間で同期されるデータの制御には、パブリッシュ・サブスクライブ・モデルが使用されます。デフォルトでは、サーバー・サイドのすべての Meteor コレクションがパブリッシュされます。Meteor は DDP (Distributed Data Protocol) を使用して、クライアントとサーバーとの間でデータを移動します (他のデータベースには、スタブとプロバイダーという形で DDP ドライバーを作成することができます。Meteor コミュニティーで現在進行中の取り組みには、近々完成予定の MySQL ドライバーも含まれます)。

jQuery のプラグインを統合する

販売実績ポータルで円グラフをレンダリングするために使用しているのは、jqPlot という jQuery のプラグインです。円グラフのレンダリングと再レンダリングは、Sales2013 コレクションに含まれるデータの変更に反応して行われます。すでに説明したように、Sales2013 コレクションに変更があるたびに salesdata テンプレートが再レンダリングされます。salesdata テンプレートの rendered イベントがトリガーされると、リスト 4 に記載するクライアント・サイドの関数 (リスト 1 からの抜粋) が円グラフを再レンダリングします。

リスト 4. jqPlot プラグインを使用して円グラフをレンダリングする jQuery コード
Template.salesdata.rendered = function()
{

  $('.editable').editable(function(value, settings) { 
     Sales2013.update(Session.get("selected_datapoint"), 
 {$set: {total: parseInt(value)}});
     return(value);
  }, { 
     type    : 'text',
     style : 'inherit',
     width : 100,
     submit  : 'OK',
 });


     var cur = Sales2013.find();

     if (cur.count() === 0)  // do not render pie if no data
       return;
     var data = [];
     cur.forEach( function(sale) {
       data.push( [sale.region, sale.total]);
     });
  var plot1 = $.jqplot ('chart', [data], 
    { 
      seriesDefaults: {
 // Make this a pie chart.
 renderer: $.jqplot.PieRenderer, 
 rendererOptions: {
   // Put data labels on the pie slices.
   // By default, labels show the percentage of the slice.
   showDataLabels: true
 }
      }, 
      legend: { show:true, location: 'e' }
    }
  );
  
}

売上高のインプレース編集を可能にするために、販売実績ポータルでは Jeditable という jQuery のプラグインを使用しています。編集を処理するコードは、リスト 4Template.salesdata.rendered = function() から var cur = Sales2013.find(); までのコードです。

jQuery および jqPlot プラグインと Jeditable プラグインについての詳細は、「参考文献」を参照してください。

Meteor のスタイルシートとスクリプトのロード順について

jQuery プラグインを正常にロードするには、それぞれのプラグインに関連付けられた CSS ファイルと JavaScript コードを正しい順序でロードすることが重要です。

リスト 2 に記載した sales.html ファイルには、<script> タグも <link type="text/css" ... > スタイルシートも含まれていないことに注目してください。代わりに Meteor は、ディレクトリーをスキャンすることによって (最初に一番深いレベルのディレクトリーをスキャンしてから、各ディレクトリー内をアルファベット順にスキャンします)、クライアント・サイドのスクリプトとスタイルシートを自動的にロードします。

このロード順を利用するために (そして、スクリプトや CSS のファイル名は重要でないことから)、私は一部のスクリプトの名前を変更して、それらのスクリプトが特定の順番でロードされるようにしました。例えば、client/js ディレクトリー内の jquery.jeditable.mini.js を zjquery.jeditable.mini.js という名前に変更して、このスクリプトが最後にロードされるようにしました。また、jquery.jqplot.min.js がロードされた後に jqplot.pieRenderer.min.js がロードされるように、後者の名前を yjqplot.pieRenderer.min.js に変更しました。プラグインが提供する CSS ファイルは、client/css サブディレクトリーに配置することで最初にロードされるようにしました。


販売実績ポータルへのアクセスに対するセキュリティーの強化

今までのところ、販売実績ポータルの URL を知っている誰もが、売上高のデータを見ること、そして変更することさえできます。このポータルを実際に使用する場合には、これでは寛大すぎますが、Meteor でサポートされているデフォルトのプロトタイプ・モードは、初期段階でアプリケーションを素早く進化させるには理想的です。初期段階では、対話部分、UI、さらにはアプリケーション・ロジックでさえも、短期間で何度も変更することになるでしょう。通常、この段階では機密性のあるデータが関わってくることはありません。プロトタイプの作成段階にオープン・アクセス・モデルを適用すれば、共同で作業する開発者やレビューを行うユーザーと URL を共有して、フィードバックを収集することができます。

初期段階の次に来る当然のステップは、Meteor のセキュリティー機能を有効にしてアプリケーションをロック・ダウンすることです。格段にセキュリティーが強化された (したがって、より現実的な) バージョンの販売実績ポータルのコードは、記事からコードをダウンロードしてインストールすると作成される sales サブディレクトリー内にあります。このコードには、以下のセキュリティー機能が追加されています。

Meteor のスマート・パッケージ

スマート・パッケージ (Smart Package) とは、コマンドラインから簡単に Meteor に追加、または Meteor から削除することができる機能モジュールのことです。スマート・パッケージには、サーバー・サイドのコード、クライアント・サイドのコード、UI、API などが含まれています。この記事では accountsautopublish、および insecure というスマート・パッケージの例を紹介しますが、スマート・パッケージを追加することによって、CoffeeScript のサポートや URI ルーティングをはじめとするさまざまな機能を追加することもできます。使用している Meteor のバージョンに用意されているスマート・パッケージの最新リストを表示するには、meteor list コマンドを実行してください。

  • 許可されたユーザーだけにポータルへのアクセスを許可するためのユーザー認証システム
  • 地域の売上高データの正式所有者だけにデータの変更を許可するコード
  • デプロイされたクライアントにサーバー・サイドのコードが公開されないように改善されたソース・コード編成

これ以降で参照する販売実績ポータル・アプリケーションは、いずれも sales ディレクリー内にあるセキュア・バージョンとなります。

クライアントからサーバー・データを変更できないようにする

アプリケーションをロック・ダウンするための出発点としてふさわしいのは、誰もデータを変更することができないようにすることです。以下のコマンドを使用して insecure スマート・パッケージを削除するだけで、この目的を果たすことができます。

meteor remove insecure

insecure スマート・パッケージは、基本的にサーバーに対し、データの読み取りや変更の際にアクセス・ルールをチェックしないように指示します。このスマート・パッケージがデフォルトでインストールされるため、すべてのアクセスが許可されますが、これを削除した後は、クライントがサーバー・データを変更できなくなります。ブラウザー・インスタンスのいずれかに戻り、売上高データを変更しみてください。すると、アプリケーションがデータを変更しようとしても、サーバーからのアクセス拒否を反映して、すぐにデータが元の状態に戻ります (これは遅延補償が実際に動作している 1 つの例であり、データはクライアントで一瞬更新されますが、サーバーからの正式なコピーが到着した途端にクライントのデータが上書きされます)。

insecure パッケージを削除した後は、特定のデータへの (特定のユーザーによる) アクセスを明示的に許可するためのアクセス・ルールを追加する必要がありますが、まだユーザーは存在していません。そこで次に必要となる作業は、ユーザー・データベースとログイン許可システムを追加することです。

許可されたユーザーだけに売上高データの表示を許可する

ユーザー許可システムを追加する前に、誰にも売上高データを見られないようにする必要があります (許可されるユーザーについては、後でデータを表示できるように処理します)。今のところ、ユーザーがデータを変更することはできませんが、販売実績ポータルの URL にアクセスすると、誰でもデータを表示することができます。

Meteor のコレクション・データ (サーバーが明示的にパブリッシュするデータや、クライアントがサブスクライブするデータを除く) がサーバーからクライアントにプッシュされないようにするには、デフォルトでインストールされる autopublish スマート・パッケージを以下のようにして削除します。

meteor remove autopublish

このコマンドを実行した後に販売実績ポータルの URL にアクセスすると、地域別売上高データと円グラフは表示されなくなっているはずです。

accounts スマート・パッケージを使用してユーザー・ログインを追加する

Meteor には、ユーザー・ログインおよびアクセス許可システムを容易に追加できるようにするスマート・パッケージが用意されています。エンドツーエンドのワークフローをカバーする accounts スマート・パッケージには、必要となるフロントエンドの UI、バックエンド・データベース、そしてクライアント・サーバー間の API が揃っています。これらの機能のすべてを、たった 1 つのコマンドで販売実績ポータルに追加することができます。

meteor add accounts-password accounts-ui

account-password スマート・パッケージは、ユーザーの作成とログイン (お馴染みの e-メール・アドレスとパスワードによるログイン) をサポートします。このパッケージの実装では、SRP (Secure Remote Password) プロトコル (「参考文献」を参照) を使用するため、クライアントとサーバーとの間で平文のパスワードが送信されることは決してありません。

パスワード・ベースのログインに加え、1 つ以上のスマート・パッケージをアプリケーションに追加するだけで、Facebook、Twitter、Weibo、GitHub、Google、および Meetup からユーザーがサインインできるようにすることも可能です。現時点でソーシャル・ネットワークの OAuth ログイン・サポートは、企業のイントラネット環境ではあまり役に立ちそうもありませんが、一般コンシューマー向けの Web アプリケーションまたはモバイル・アプリケーションには、かけがえのない機能です。

ユーザー・ログイン UI のカスタマイズ

UI ウィジェットで使用するダイアログのスタイル設定をより柔軟に制御したい場合は、accounts-ui パッケージの代わりに accounts-ui-nostyle パッケージを追加してください。UI の制御を完全に引き継ぎたいとしたら、このプロセスで使用する API とデータ・フローについての説明を Meteor のドキュメント (「参考文献」を参照) で調べてください。

ログイン用の UI を追加する

accounts-ui パッケージには、ユーザーのログイン処理、新規ユーザーの作成処理、およびパスワードを忘れた場合のリカバリー処理のために事前に作成された CSS スタイルの UI ウィジェット一式 (そして、それらをサポートする JavaScript コード) が提供されています。これらの機能を追加するには、{{loginButton}} Handlebars ヘルパーを追加します。リスト 5 に、販売実績ポータル・アプリケーションに追加したログインを示します (sales/sales.html ファイルから抜粋したコードです)。

リスト 5. ユーザー・ログインおよびアクセス許可システムを追加する
<body>
  <div id="title">
   <div class="header">
   <div class="span5">
     <h1 style="margin-bottom: 0px">Sales Portal</h1>
   </div>
   <div class="span5">  <div style="float: right">
    {{loginButtons align="right"}}  </div>
   </div>
   </div>
  </div>

記事からコードをダウンロードしてインストールすると作成される sales ディレクトリーに、ユーザー・アクセス制御を追加した完全な販売実績ポータル・アプリケーションが格納されています。このバージョンを実行して、ログインしてみてください。ブラウザー・インスタンスを開始すると、右上隅に「Sign in (サインイン)」リンクが表示されます。このリンクをクリックすると、図 5 に示すダイアログが表示されるはずです。

図 5. accounts-ui スマート・パッケージに含まれるサインイン・ダイアログ (Firefox で表示)
ブラウザー (Firefox) に表示されたサインイン・ダイアログのスクリーン・キャプチャー

accounts スマート・パッケージがユーザー・データベースを実装するために使用するのは、Meteor のコレクションとパブリッシュ・サブスクライブです (独自に作成したコードでも使用できる機能です)。販売実績ポータルの現在のバージョンでは、データベースにユーザー・クレデンシャルのセットを 2 つ作成してあります (表 1 を参照)。

表 1. 既存の販売実績ポータルのユーザー・クレデンシャル
e-メールパスワード
joe@dwtestonly.comabc123
sing@dwtestonly.comabc123

2 つのブラウザー・インスタンスを開いて、それぞれのクレデンシャルを使ってログインしてください。

追加のユーザーを作成するには、「Sign in (サインイン)」ダイアログの「Create account (アカウントの作成)」リンクをクリックします。図 6 に、accounts-ui スマート・パッケージに含まれる新規ユーザー作成ダイアログを示します。

図 6. accounts-ui スマート・パッケージに含まれる新規ユーザー・アカウントを作成するためのダイアログ (Chrome で表示)
ブラウザー (Chrome) に表示された、新規ユーザーを作成するためのダイアログのスクリーン・キャプチャー

地域別売上高データに所有者のフィールドを追加する

販売実績ポータルの現在のバージョンでは、初期データベースの内容が変更されています。リスト 6 に、データを投入するために使用したサーバー・サイドのコードを記載します。

リスト 6. サーバー・サイドのデータ投入コード
Sales2013.remove({});
Sales2013.insert({region:"US East", total: 2032333});
Sales2013.insert({region:"US Central", total: 150332, owner: joe._id});
Sales2013.insert({region:"US West", total: 1202412});
Sales2013.insert({region:"Asia Pacific", total: 701223});

リスト 6 には、新規の owner フィールドが追加されています。この例で owner フィールドに格納されるのは、US Central 地域のデータを所有するユーザー (joe@dwtestonly.com) の userId です。このフィールドを使用して、地域別売上高データの更新を joe@dwtestonly.com だけに許可します。userId 値は、Meteor.users コレクションに対してクエリーを実行することで取得できます。

サーバー・データをきめ細かく選択してパブリッシュする

Autopublish スマート・パッケージが削除されているので、サーバーから明示的にデータをパブリッシュして、クライアントからもそのデータを明示的にサブスクライブする必要があります。

販売実績ポータルの場合、サーバーはリスト 7 に記載するコード (sales/server/sales.js ファイルから抜粋) を使用して global_sales コレクションをパブリッシュします。

リスト 7. サーバーから選択的にデータをパブリッシュする
Meteor.publish("global_sales", function () {
      if (this.userId) {  // only visible to logged in users
      // do not include the owner field for client access
      return Sales2013.find({}, {fields: {"region": 1, "total":1 }}); 
      }
});

リスト 7 で注目する点は、有効なユーザーがクライアント・サイドのセッションにログインしていることを確実にするために使用している this.userId です。Meteor がユーザーに代わってサーバー・コードを実行するときには、常に this.userId に、現在ログインしているユーザーの一意の ID が格納されます。現行のブラウザー・インスタンスにログインしているユーザーがいない場合、this.userId は null となり、データはパブリッシュされません。さらに、リスト 7 では、地域別売上高データ文書 (基本的に、MongoDB インスタンスの可変数のフィールドからなるレコードの文書) のすべてのフィールドが、クライアントに送信されるコレクションで返されるわけではありません。具体的に言うと、この文書の owner フィールドはクライアントに対して隠されます。この仕組みにより、クエリーを使用して、フィールドのサブセットを含めたコレクションのサブセットのみを、許可されたユーザーがログインしているクライアントのみに送信することが可能になります。この手法は、クライアント・ブラウザーが特定の文書の機密データのフィールドにアクセスできないようにする場合に役立ちます。

クライアント・サイドでデータをサブスクライブする

販売実績ポータルのクライアント・コードは、サーバーがパブリッシュする global_sales コレクションを明示的にサブスクライブします (リスト 8 を参照)。

リスト 8. サーバーからのコレクションをサブスクライブするクライアント
Meteor.subscribe("global_sales");
Template.salesdata.dataset = function () {
  return Sales2013.find({});
};

地域別売上高データの更新を許可するためのアクセス・ルールを追加する

insecure スマート・パッケージが削除された状態では、事実上すべてのユーザーが、売上高データの更新操作を拒否されます。それぞれの地域別売上高を所有しているのが異なるユーザーであるとしたら、アクセス・ルールを追加することで、joe@dwtestonly.com による US Central データの更新を許可することができます。そのアクセス・ルールは、リスト 9 のとおりです。これは、model.js という名前のサーバー・サイドのソース・ファイルに含まれています。

リスト 9. 所有者に売上高の更新を許可するためのサーバー・サイドのアクセス・ルール
Sales2013.allow({
  update: function (userId, sales, fields, modifier) {
    if (userId !== sales.owner)
      return false; // not the owner

    var allowed = ["total"];
    if (_.difference(fields, allowed).length)
      return false; // tried to write to forbidden field

    return true;
  },

});

update 操作に対するアクセス・ルール関数は、更新が許可される場合は true を返し、許可されない場合は false を返します。リスト 9 のコードは、最初にユーザーが所有者であること、そして total フィールドだけが変更されることを確認します。

反復型開発のための Meteor のホット・コード・リロード

開発とデバッグの時間を節約するには、コード、CSS、あるいはテンプレートを変更している最中でも、ブラウザーが Meteor アプリケーションにアクセスしている状態を維持してください。Meteor のホット・コード・リロード機能はほとんど常に変更を検出し、その変更をクライアント・ブラウザーにプッシュします。

販売実績ポータルを開いて joe@dwtestonly.com としてログインし、US West のデータを変更してみてください。変更できないことを確認した後、US Central のデータを変更してみます。joe@dwtestonly.com はこのデータの所有者であるため、データの更新に成功します。

別のブラウザー・インスタンスを立ち上げて、今度は sing@dwtestonly.com としてログインします。売上高データのいずれかを変更しようとすると、その操作は失敗します。sing@dwtestonly.com が所有する売上高データは 1 つもないため、サーバーはこのユーザーからの変更リクエストはすべて拒否します。

ユーザーがログインしていない場合に、テンプレートがレンダリングされないようにするには、クライアント・サイドの currentUser 関数を使用することができます。リスト 10 に記載するコードを HTML ファイルに追加してください。

リスト 10. 空のテンプレートのレンダリングの試行を排除する
<div id="container">
    <div id="salestable">
      {{#if currentUser}}
      {{> salesdata}}
      {{/if}}
    </div>
    <div id="chart">

    </div>
</div>

上記のコードを追加した上で、新しい販売実績ポータル・ブラウザー・インスタンスを立ち上げると、データは表示されません。sing@dwtestonly.com としてログインすると、データと円グラフが表示されます。次は、フィールドを変更してみてください。所有者ではないため、フィールドを変更できないはずです。

さらに別のブラウザー・インスタンスを立ち上げて、joe@dwtestonly.com としてログインすると、データが表示されます。US East の数値を変更すると、円グラフが更新されます。sing@dwtestonly.com セッションでの円グラフも変更されていることを確認してください。2 つのセッションからサインアウトして、データが非表示になることを確認してください。


アプリケーションのデプロイメント: クラウドとプライベート

デプロイメントを単純化するという目的に加え、独自のサーバーをセットアップしなくても Meteor のデモを作成したり、Meteor を試してみたりできるように、Meteor の関係者たちは、1 つのコマンドを実行するだけでアプリケーションをクラウドでホストされたサーバーにデプロイできるようにしています。このサービスは、記事を執筆している時点では無料で使用することができます。アプリケーション・ディレクトリーから以下のコマンドを実行するだけで、アプリケーションをデプロイすることができます。

meteor deploy applicationname.meteor.com

アプリケーション名は一意でなければなりません。この名前が http://<アプリケーション名>.meteor.com として (インターネットで世界に) 公開されるためです。この記事のアプリケーションは、Meteor.com にデプロイされています。これらのアプリケーションへのリンクについては、「参考文献」を参照してください。

自分の会社のドメイン (例えば、http://<アプリケーション名>.<会社のセカンド・レベル以下のドメイン>.com/) からユーザーがアプリケーションにアクセスできるようにする場合には、CNAME (エイリアス) DNS レコードを作成して、そのレコードが origin.meteor.com を指すようにする必要があります。

独自のサーバー・インフラストラクチャーでアプリケーションをホストする場合、Node.js と MongoDB 機能をインストール済みのサーバーが必要です。アプリケーションのデプロイ可能なバンドルを作成するには、以下のコマンドを使用します。

meteor bundle applicationname.tgz

Node.js でのファイバー

ファイバーが Node.js の世界に追加されたのは、比較的最近のことです。ファイバーは、個々の論理フロー (ファイバー) 間でのノンプリエンプティブ・マルチタスクを可能にします。つまり、あるファイバーが明示的に実行を譲ってからでないと、別のファイバーが実行を開始することはできません。実行を譲るポイントは正確に制御できることから、通常はファイバー間で共有される状態を保護する必要はありません。ファイバーの重要な機能は、表記に利便性がもたらされることです。Node.js のプログラミングに典型的な深くネストされたコールバックを作成する代わりに、シーケンシャルに見えるコード (論理フローごとに 1 つのスレッドなど) を作成することができます (Node.js ファイバーについての詳細は、「参考文献」を参照してください)。

この記事を執筆している時点で、Meteor 0.6.3.1 では、最終的なデプロイメント・システムと同じオペレーティング・システムでセルフデプロイされるバンドルを作成することが要件となっています。この要件は、ネイティブ・コンパイルされたコードの依存関係によるものです。

Meteor のサーバー・サイドのコードは、Node.js ファイバーで実行されます。これにより提供される仮想環境では、着信するリクエストごとに 1 つのスレッドが処理を行っているかのような (共有可能な状態がない) コードを作成することができます。この手法では、サーバー・サイドの JavaScript ロジックのコーディングが簡単なものになります。

Node.js アプリケーションは、デプロイ可能なサーバー・サイド・アプリケーションであるため、相互運用性やスケーリングに関する独自の具体的な要件に合うようにデプロイメント・トポロジーをカスタマイズすることができます。


Foto Share: モバイル写真共有サービス

少量の Meteor コードを伴うわずかな設計および計画作業によって、どの程度のことができるかを理解した今、読者の皆さんは、すでに新しいプロジェクトの 1 つや 2 つを思い付いているかもしれません。次のサンプルでは、モバイル機器向けの Meteor アプリケーションを作成する際にさらに適用しなければならない考え方を紹介します。

Meteor とモバイル・アプリ

モバイル・アプリを作成するための Meteor の機能は、まだ開発の初期段階にあります。Meteor プロジェクトは、全体が現在進行形で急速に進化しつつある取り組みです。完全なモバイル開発のサポートについては、1.0 がリリースされた後のターゲットとされています。

Foto Share は、携帯電話ユーザー向けの写真共有サービスの概念実証です。その使用例はシンプルかつ直接的なもので、ユーザーが携帯電話で写真のコレクションをブラウズしたり、「Share (共有)」ボタンをタップすることで写真を友達と共有したりするものです。図 7 に、Apple iPhone で実行中の Foto Share を示します。

図 7. Apple iPhone で実行中の Foto Share
Apple iPhone で実行中の Foto Share のスクリーン・キャプチャー

販売実績ポータル・プロジェクトと同じセキュリティー上の理由から、Foto Share でも autopublishinsecure の 2 つのスマート・パッケージは削除されています。また、同じくパスワード・ベースのログインを実装するために、Foto Share にも accounts-uiaccounts-password の 2 つのスマート・パッケージが追加されています。記事に付属のコードのダウンロードに含まれる Foto Share アプリには、販売実績ポータルと同じ 2 人のユーザーが追加されています。

Foto Share を試すには、まず、記事からコードをダウンロードしてインストールすると作成される fotoshare ディレクトリーからアプリを実行します。携帯電話が 2 台ある場合は、それぞれのブラウザーで Foto Share にアクセスしてください。携帯電話が使えない場合は、引き続き PC のブラウザーを使用して構いません。一方のブラウザー・インスタンスでは sing@dwtestonly.com としてサインインし、もう一方では joe@dwtestonly.com としてサインインします (販売実績ポータルで使用したのと同じパスワードを使用します)。Sing の写真をブラウズするには、写真をスワイプするか、左右いずれかのオーバーレイにタッチします。新しく表示される写真は、ビューにスライドインします。Sing の写真はすべてハワイの風景であり、Joe の写真はアステカとマヤの遺跡であることがわかります。

共有をテストする準備ができたら、Joe としてサインインした携帯電話の写真コレクションの中から 1 枚を選択し、「Share (共有)」ボタンにタッチします。Sing としてサインインした携帯電話では、この操作に反応し、サブスクライブの対象であるコレクションを Meteor が更新します。これによって、Joe が共有する写真が Sing としてサインインした携帯電話上でも表示できるようになります。

Foto Share ユーザー・ログイン UI

Meteor には (まだ) モバイル対応の accounts-ui スマート・パッケージがありません。Meteor による Web アプリケーションの場合と同じように、カスタマイズ可能なモバイル・ログイン UI を「投入」できるようになったら最高でしょう。とりあえず、Foto Share には accounts-ui Web UI を使用しました。図 8 に、携帯電話に表示されたログイン・ダイアログを示します。

図 8. Foto Share のログイン画面
携帯電話に表示された Foto Share ログイン画面のスクリーン・キャプチャー

一般に、ユーザーは Web アプリケーションにアクセスするたびにログインすることを厭わないものですが、パスワードをいったん入力した後は、携帯電話アプリがユーザーに代わって自動的にログインすることを期待します。Meteor によるモバイル・アプリに適用できるソリューションは、よく使われている Apache Cordova プラットフォーム (「参考文献」を参照) などのネイティブ・アプリ・ラッパーでクライアント・サイドのコードをラップすることです。こうすれば、携帯電話独自のログイン・プロファイルにアクセスできるようになるため、ユーザーに毎回ログインを求める必要がなくなります。さらに、携帯電話に組み込みのギャラリーまたはアルバムのアプリなどに保管されているユーザーの写真のコレクションにも直接アクセスできるようになります。

jQuery Mobile およびページネーション・プラグインを統合する

最近のモバイル・ブラウザーは HTML5 に対応するものが増えていることから、jQuery Mobile フレームワークで開発されたアプリケーションは、概して主要なモバイル OS の最新リリースで実行できるようになっています。販売実績ポータル・アプリでは、Meteor で jQuery および UI プラグイン (jqPlot など) を統合する方法をすでに示しています。前と同じく、jQuery Mobile の CSS スタイルシートと JavaScript ファイル、そしてページネーション・プラグイン・ライブラリーをロードする順序には注意してください。

jQuery Mobile と Meteor の LiveHTML (リアクティブ・レンダリング) では、どちらもブラウザーの DOM (Document Object Model) で HTML 要素を拡張してから操作しなければならないため、これらが互換する順序で行われることを確実にしなければなりません。Foto Share の場合、jQuery Mobile によるページの初期化は、Meteor の操作が完了するまで遅延させる必要があります。jQuery Mobile によるページ初期化を遅延させるために、jQuery Mobile の前に以下のコードが含まれる aadelaybind.js ファイルがロードされます。

$(document).bind("mobileinit", function(){
  $.mobile.autoInitializePage = false;
});

Meteor の LiveHTML による処理が完了した後、pages テンプレートの rendered イベント・ハンドラーによって、実際のページの初期化がトリガーされます。以下は、ヘルパー関数の該当する部分です。

Template.pages.rendered = function() {
   ...
   $.mobile.initializePage();
   ...
};

Foto Share のリアクティブ・データを識別する

概念的に、当然リアクティブにするべきデータ・コレクションは、ユーザーの一連の写真です。こうすることで、ユーザーが写真を共有すると常に Meteor が写真のリストを更新して再レンダリングできるようになります。これが、この概念検証で採用している手法です。大規模なシステムの場合、画像のバックエンド・ストレージ・アーキテクチャーによっては、画像そのものではなく、写真のメタデータだけをリアクティブにすることをお勧めします。

リスト 11 に記載するサーバー・サイドのデータ投入コードを見て、写真がどのように保管されるのかを把握してください。

リスト 11. Foto Share サーバー・サイドのデータを投入してコレクションをパブリッシュするコード
Meteor.startup(function () {
      
      ...     
      
      Fotos.remove({});
      Fotos.insert({name:"pic1", img: readPic('pic1.jpg'),
       owner: sing._id, shared:false});
      Fotos.insert({name:"pic2", img: readPic('pic2.jpg'),
       owner: sing._id, shared:false});
      Fotos.insert({name:"pic3", img: readPic('pic3.jpg'),
       owner: sing._id, shared:false});
      Fotos.insert({name:"pic4", img: readPic('pic4.jpg'),
       owner: joe._id, shared:false});
      Fotos.insert({name:"pic5", img: readPic('pic5.jpg'),
       owner: joe._id, shared:false});
      Fotos.insert({name:"pic6", img: readPic('pic6.jpg'),
       owner: joe._id, shared:false});

    Meteor.publish("photos", function () {
      if (this.userId) {  // only visible to logged in users
       return Fotos.find( {$or : [{owner: this.userId}, {shared: true}]},
  {fields: {"name": 1, "img":1 , "owner": 1}}); 
      }

  });
});

サーバーがパブリッシュするコレクションは Fotos です。Fotos に含まれるそれぞれの文書が写真を表します。これらの文書ごとに、nameimgowner の各フィールドがあります。img フィールドは、ローカルに保管された対応する JPG ファイルから読み取られます。この場合も、サブスクライブしているクライアントが受信するデータに含まれるのは、そのクライアントが所有する写真に加え、所有者によって共有されている他の写真だけです。さらに、ここでも選択的フィールド・フィルタリングを使用して、クライアントが受信する Foto コレクションから shared フィールドを削除しています。owner フィールドは、フィルタリングによって除外されません。これは、クライアントが共有されている写真の所有者の名前を表示できるようにするためです。リスト 12 に、readPic() ヘルパー関数を記載します。この関数は、同期 fs を使用して画像をメモリーに読み込んでから、バイナリー・ストリームを base64 にエンコードして img フィールドに格納します。このフォーマットは、クライアント・サイドで写真が取得されたときに、その写真を表示するのに役立ちます。

リスト 12. MongoDB ストレージの JPG 画像を読み取るヘルパー関数
function readPic(infile)  {
    var fs = Npm.require('fs');
    var path = Npm.require('path');
    var base = path.resolve('.');
    var deployLoc = 'public/images/'
    var data = fs.readFileSync(path.join(base, deployLoc, infile)); 
    var tp = data.toString('base64');
    return  'data:image/jpeg;base64,' + tp;
}

データベースから画像が再構成される際には、最近のほとんどのブラウザーでは、テンプレート・コードが <img> タグ内の data URL サポートを利用します (「参考文献」を参照)。<img> タグの src 属性での data URL サポートにより、base64 にエンコードされたストリングから画像のバイナリー・ビットを動的に設定することができます。リスト 13 に、photopage テンプレートの一部を抜粋します。このテンプレートでは、base64 にエンコードされた写真の img フィールドを使用して、jQuery Mobile ページに画像をレンダリングします。

リスト 13. data URL を使用して img タグの src 属性を設定する
<template name="photopage">
  <div data-role="page" id="p{{index}}">
    ...
    <div data-role="content" class="apic">

      <img src="{{img}}" />

      <ul data-role="pagination">
      {{#if indexIsZero}}
   <li class="ui-pagination-next"><a href="#p{{indexNext}}">Next</a></li>
      {{else}}
   <li class="ui-pagination-prev"><a href="#p{{indexPrev}}">Prev</a></li>
   <li id="x{{index}}" class="ui-pagination-next">
     <a href="#p{{indexNext}}">Next</a></li>
      {{/if}}
      </ul>

    </div> <!-- /content -->
    ...
  </div>  <!-- /page -->
</template>

Meteor のリモート・メソッド: カスタム RPC の単純化

Insecure スマート・パッケージは削除されていますが、Foto Share のアクセス・ルールはまだ作成されていません。アクセス・ルールがなければ、クライアントが Minimongo によってデータを更新することはできません。けれども、「Share (共有)」ボタンがタップされたときに、何らかの方法で写真の share フィールドが更新されるようにする必要があります。その方法とは何かと言うと、それは Meteor メソッドです。

Meteor メソッドとは、リモート・プロシージャー・コール (RPC) メカニズムのことです。クライアントからサーバーへの RPC 呼び出しは、単純な 2 つのステップで作成することができます。

  1. サーバー・サイドで JavaScript 関数を定義する
  2. Meteor.call() を使用して、そのサーバー関数をリモートで呼び出し、オプションで引数を渡します。

エンドポイントのセットアップ、セットアップ解除、そしてその間のデータ・マーシャリングといった雑事はすべて Meteor が処理してくれます。

ユーザーが Foto Share で「Share (共有)」ボタンをタップすると、クライアントがサーバー上の shareThisPhoto という Meteor リモート・メソッドを呼び出して、写真の ID を引数として渡します。サーバー・サイドでは、このコードは最初に呼び出し側が写真の所有者であるかどうかをチェックし、所有者がメソッドを呼び出した場合にだけ写真の shared フィールドを更新します。リスト 14 に、サーバー・サイドの shareThisPhoto コードを記載します。

リスト 14. 写真の shared フィールドを更新するサーバー・サイドの Meteor リモート・メソッド
Meteor.methods({
  shareThisPhoto: function (photoId) {
    console.log('called shareThisPhoto');
    console.log(photoId);
    var curPhoto = Fotos.findOne({_id: photoId});
    if (this.userId !== curPhoto.owner)  {
      return "Cannot share this photo.";
    } else {
      Fotos.update({_id: photoId}, {$set :{shared: true}});
      return "Photo shared!";
    }

  },
});

ユーザーが「Share (共有)」ボタンをタップしたときにリモート・メソッドを呼び出すクライアント・サイドのコードは、リスト 15 のとおりです。

リスト 15. 「Share (共有)」ボタンがタップされるとリモート・メソッドを呼び出すクライアント・サイドのコード
Template.photopage.events({
  'click .fs-logoff': function () {
    Meteor.logout(function() {
      location.reload();
    });
  },
  
  'click .fs-share': function() {
      Meteor.call('shareThisPhoto', this._id, function (error, retval) {
 console.log(retval);
      });
  }
});

ここでは Meteor リモート・メソッドを説明するために RPC 手法を選びましたが、所有者に shared フィールドの更新を許可するアクセス・ルールを定義するという手法もあります。その場合、ユーザーが「Share (共有)」ボタンをタップしたときに、写真の shared フィールドをローカルで更新する必要があります。その更新は、Meteor の Minimongo によってサーバーにプッシュされた後、サブスクライブしている他のすべてのクライアントにプッシュされます。


最近の Web アプリケーションのアーキテクチャー

この記事のサンプル・アプリケーションにひと通り取り組んだ今、Meteor の設計対象となっている Web アプリケーションは、図 9 に示す特定のアーキテクチャーを持つアプリケーションであることは理解できるはずです。

図 9. インタラクティブなリッチ・クライアント Web アプリケーション・アーキテクチャー
インタラクティブなリッチ・クライアント Web アプリケーションのアーキテクチャー図

一般に、このようなタイプのアプリケーションは、極めてインタラクティブなシングル・ページ UI で構成されます。ユーザーは通常、新しいページのロードを体験することはなく、ユーザーの操作に応じて、表示されているページの一部が瞬時に更新されます。ネットワークでのラウンドトリップによる遅延はほとんどないか、皆無です。シングル・ページ・インターフェースでは、ありとあらゆる方法でページを部分的に更新できるため、このインターフェースによってアプリケーションが制限されることはまったくありません。これは、ワード・プロセッサーやスプレッドシートなどのスタンドアロンのデスクトップ・アプリケーションを連想させます。

このようなタイプのアプリケーションは、クライアント・ブラウザー上で JavaScript アプリケーション・コードをロードするのが通常です。このコードが、ブラウザーの DOM の操作、CSS スタイル設定の変更、新規 HTML 要素/コード/スタイルの生成、そしてブラウザーが提供するその他の API の使用に動的に対処して、ユーザーとの対話を管理します。ユーザーとの対話は、一貫してクライアント・サイドのコードによって制御されるため、アプリケーションが最初にロードされる以外で、ネットワーク経由で追加の HTML やスタイルがロードされることはありません。このコードはまた、アプリケーションの機能を実装するために、クライアントとサーバーの間でのデータの受け渡しも行います。ブラウザーは基本的に、JavaScript で作成されたリッチ・クライアント (ファット・クライアントとも呼ばれます) アプリケーションをロードして実行します。

サーバー・サイドでは、クライアントからのデータをセキュアに提供して同期化するエンドポイントがセットアップされます。レガシー・バックエンドでは、RPC、XML ベースの Web サービス、RESTful なサービス、またはその他の JSON スタイルの RPC 呼び出しを使用することができます。最近のバックエンドであれば、多くの場合、データの効率的送受信、偶発的切断に対する回復力、現行の各種トランスポートのサポート、そしてトポロジーの拡張性を目的に設計された独自仕様のプロトコルにも対応します。

図 10 に、リリース 0.6.3.1 の時点での Meteor の主要な内部コンポーネントを示します。

図 10. Meteor の内部コンポーネント
リリース 0.6.3.1 の時点での Meteor の主要な内部コンポーネントを示す図。サーバー・サイドには、Meteor コレクション、Meteor メソッド、スマート・パッケージ、パブリッシュ・サブスクライブ、Meteor コア、DDP があります。これらのコンポーネントを駆動するためのサーバー・コードを作成することになります。その他、完全な MongoDB インスタンスと Node.js ランタイム・サポートもあります。クライアント・サイドには、パブリッシュ・サブスクライブ、セッション管理、スマート・パッケージ、Meteor コレクション、Handlebars テンプレート、Spark LiveHTML エンジン、Meteor 依存関係管理、Meteor メソッド、Minimongo スタブ・ドライバー、Meteor 接続管理、DDP があります。これらのコンポーネントはクライアント・サイドのコードで制御され、Websocket などのトランスポート API をベースとしたブラウザーの DOM を対象に実行されます。

ご存知のとおり、DDP はクライアント・インスタンスとサーバー・インスタンスの間での双方向のデータ・フローを可能にします。また、Minimongo は、お馴染みの MongoDB に対するクエリー API をクライアント・サイドのコードに提供する、クライアント・サイドのローカル・データ・キャッシュを備えたスマート・スタブです。Spark は、(Handlebars) テンプレートおよび依存関係管理コンポーネントと連携するライブ HTML エンジンであり、Meteor のリアクティブな再レンダリング機能を提供します (Meteor コンポーネントの詳細を説明している Meteor の公式ドキュメントへのリンクについては、「参考文献」を参照してください)。

潜在的ビッグ・データ・アプリケーションのアーキテクチャー

Meteor のシングル・ページのパラダイムは、極めてインタラクティブでリアルタイム性があり、ほぼ特定の種類の問題に適合するようにカスタマイズされています。ビッグ・データの視覚化に伴う側面の 1 つは、結果が使用可能になると同時に更新されるインタラクティブなダッシュボードが必要になることです。ダッシュボードは、MapReduce ジョブをキューに入れたり、これらのジョブの進行状況をリアルタイムでモニターしたりするために使用されることもあります。

スプレッドシートは、インタラクティブな統合、要約、ドリルダウン、またはカスタマイズされたビューを比較的小さなデータ・セットに提供することができますが、それと同じように Meteor によるリアクティブなアプリケーションは、テラバイト規模やペタバイト規模のデータのインタラクティブなインターフェースを提供する能力を秘めています。図 11 に、そのようなシステムのアーキテクチャーを示します。

図 11. Meteor による、ビッグ・データ対応のダッシュボード・アプリケーションのアーキテクチャー
Meteor による、ビッグ・データ対応のダッシュボード・アプリケーションのアーキテクチャー図

図 11 では、クライアント・サイドの Meteor コードによって、リアクティブなダッシュボードにビッグ・データ・リポジトリーのビューが提供されます。ユーザー操作により、カスタマイズされた MapReduce タスクが生成され、これらのタスクは Hadoop クラスター上で実行されるようにサーバー・サイドで (おそらく Meteor メソッドによって) キューに入れられます。タスクの実行が完了すると、それぞれの結果が Meteor によって MongoDB インスタンスに統合されます。すると、Meteor サーバーのパブリッシュ・サブスクライブ・コンポーネントがデータの変更を検出し、サブスクライブ済みのクライアントに対して、更新された要約データをプッシュするという仕組みです。


まとめ

Meteor に関するインタビュー

Meteor の共同創始者 Matt DeBergalis 氏への Sing Li によるインタビューで、Meteor プロジェクトの背景情報と将来の計画について詳しく学んでください。

Meteor チームは、シングル・ページの極めてインタラクティブなリッチ・クライアント Web アプリケーションを作成したいと思うすべての人に均等な機会を与えるために、ノンストップで働いています。ほとんどのユーザーは、昔ながらの一度に 1 つのページというスタイルよりも、このようなアプリケーションを選択します。Google Gmail、Microsoft Hotmail、Yahoo Mail をはじめ、主要な Web ベースのメール・サービスやオフィス・サービスの多くでは、このアプリケーション・アーキテクチャーを採用しています。大規模な技術会社や、十分な資金を持っている新興企業などで開発されたものを除き、シングル・ページのインタラクティブなリッチ・クライアント Web アプリケーションの例はそう多くはありません。それは、このような Web アプリケーションを構築するのは難しく、多大な作業とリソースが必要になるためです。Meteor の中核となる使命として表明されているのは、このクラスのアプリケーションを容易に構築できる基礎を提供することです。

Meteor が登場したことにより、今こそが、しまい込んでいて長い間忘れられていたスプレッドシート・アプリケーションのアイデアをひと通り再検討し、これらのアプリケーションを一般公開サービスに変換することが人を引きつける企てとなるかどうかを調べる絶好の時期となっています。Web アプリケーションをすでに作成または保守しているとしたら、ますます増え続けるアプリケーション開発ツールの選択肢の中で、Meteor が最も有力なツールであることがわかるはずです。


ダウンロード

内容ファイル名サイズ
Sales Portal and Foto Share codeinstmeteordl.zip367KB

参考文献

学ぶために

  • Meteor: Meteor Webサイトを探索してください。
  • Meteor のドキュメント: Meteor の公式ドキュメントで、最新機能や更新を調べてください。
  • Meteor で簡単なリアルタイム Web サイトを作成する」(David Berube 著、developerWorks、2013年1月): この記事では、Meteor をインストールして Meteor による開発を始める方法をステップ・バイ・ステップで解説しています。
  • sales_nologinSales PortalFoto Share: 現在 Meteor では、i686/x64 プラットフォーム上の Mac OS および Linux をサポートしています。Windows については、リリース 1.0 頃に公式にサポートされる予定です。それまでは、Windows ユーザーはこの記事のサンプル・アプリケーションのデプロイ済みバージョンを実行することができます (その場合、複数のユーザーが同時にアクセスする可能性があることに注意してください)。
  • Twitter での Meteor: Meteor をフォローして、世界のさまざまな地域で開催されている、Meteor について学習する機会やイベントを調べてください。
  • MongoDB: MongoDB NoSQL データベースは JSON スタイルの文書を格納します。このデータベースの簡潔なクエリー構文は、クライアント・サイドとサーバー・サイドの Meteor アプリケーション・コードの両方で広範に使用されます。
  • Discover MongoDB」(Andrew Glover 著、developerWorks、2012年12月): この Knowledge path で MongoDB の詳細を学んでください。
  • jQuery: さまざまな jQuery バージョンに用意されている多数のプラグインのリストをはじめ、jQuery フレームワークに関するあらゆる情報を調べてください。
  • jqPlot jQuery プラグイン: jqPlot で対応できる円グラフや棒グラフ以外の各種のグラフについても調べてください。
  • Jeditable jQuery プラグイン: jQuery ベースの UI で迅速かつ簡単なインプレース編集を可能にするには、Jeditable が最適です。
  • jQuery UI: このプラグインは、複数の jQuery Mobile ページ間を遷移するためのタッチ対応のインターフェースを提供します。
  • Node 用ファイバー: ノンプリエンプティブ・マルチタスクを Node.js に追加するファイバー・モジュールは、Meteor のサーバー・サイドでのデプロイメントの基礎となります。
  • Secure Remote Password プロトコル: Meteor の accounts スマート・パッケージに、平文パスワードを送信することなくユーザーを認証する手段を提供する SRP プロトコルについて調べてください。
  • IETF RFC-2397: 「data」URL スキームは、最近のデスクトップ・ブラウザーとモバイル・ブラウザーでは、ある程度サポートされています。

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

  • Meteor: Meteor は GitHub からダウンロードして入手することができます。
  • jQuery Mobile: 最近のモバイル端末で実行可能な Web アプリケーションの開発を始めてください。
  • Apache Cordova: Cordova を使用して、JavaScript、HTML、および CSS コードのネイティブ・モバイル・アプリケーション・ラッパーを作成することができます。

議論するために

  • developerWorks コミュニティーに加わってください。ここでは他の developerWorks ユーザーとのつながりを持てる他、開発者によるブログ、フォーラム、グループ、Wiki を調べることができます。

コメント

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=936620
ArticleTitle=Meteor を使用したインスタント Web アプリケーション
publish-date=07112013