WebOS Goodies

WebOS の未来を模索する、ゲームプログラマあがりの Web 開発者のブログ。

WebOS Goodies へようこそ! WebOS はインターネットの未来形。あらゆる Web サイトが繋がり、共有し、協力して創り上げる、ひとつの巨大な情報システムです。そこでは、あらゆる情報がネットワーク上に蓄積され、我々はいつでも、どこからでも、多彩なデバイスを使ってそれらにアクセスできます。 WebOS Goodies は、さまざまな情報提供やツール開発を通して、そんな世界の実現に少しでも貢献するべく活動していきます。
Subscribe       

iGoogle ガジェットの作り方 : ガジェット間通信機能(PubSub)を使う

つい先日のことですが、 iGoogle ガジェット API になかなか画期的な機能が追加されました。なんと、同じページに追加されたガジェット同士で、簡単な通信ができるようになったのです。これにより、個々のガジェットの連携が可能となり、さまざまな新しい可能性が見えてきます。例えば、検索窓を表示して入力された検索文字列を他のガジェットに送信するガジェットを作っておけば、あとは検索したいメディアごとに個別のガジェットを追加することで、どんどん検索対象を増やしていくことができます。これまでは単に独立したガジェットの置き場所にすぎなかった iGoogle が、ひとつのプラットフォームへと進化する可能性が出てきました。これはとてもエキサイティングなことではないでしょうか。

このガジェット間通信機能(PubSub フレームワーク)を簡単に体験するには、以下のタブを追加してみてください(Firefox, IE 以外のブラウザではうまく動かないかもしれません)。

http://www.google.com/ig/sharetab?source=stb...

ニュース検索と、地図の経度・緯度入力ガジェット、さらにそれぞれの結果を表示するガジェットが別々に配置されており、それぞれが連携する様子を確認できると思います。この PubSub フレームワークの情報は現在のところ英語の公式ドキュメントしかありませんので、本日は日本語での情報提供も兼ねて PubSub によるガジェット間通信を試してみようと思います。

なお、 PubSub はまだ開発者向けのβ版の状態であり、 API は今後変更される可能性があります。また、既知の不具合もいくつかあるようです。あらかじめご了承くださいませ。

アーキテクチャ

まず、 PubSub によるガジェット間通信の仕組みをご紹介しましょう。実は PubSub は従来からあるユーザー設定の保存機能を応用した構造になっています。通信用に特殊なユーザー設定項目(チャンネルと呼びます)を用意し、それに対する値の読み書きがそのままメッセージの送受信になるという具合です。内部動作に関しては推測ですが、具体的な通信のプロセスは以下のようになっています。

  1. 送信側(Publisher)、受信側(Subscriber)の双方のガジェットの定義ファイルで、同じ名前の通信用ユーザー設定項目を定義しておく。
  2. Publisher が JavaScript で通信用ユーザー設定項目に値を保存する。
  3. 保存された値が iGoogle ページの top フレームを経由して Google のサーバーに保存される。
  4. iGoogle の top フレームが該当するユーザー設定項目を受信するすべての Subscriber に値を転送する。
  5. Subscriber は転送された値を該当するユーザー設定項目の値として保存し、あらかじめ定義されたコールバック関数を呼び出す。
  6. コールバック関数の内部で該当するユーザー設定項目から値が読み出され、適切な処理が行われる。

と、だいたいこんな感じでしょうか。その他、 PubSub を使ううえでポイントとなる要素は以下のとおりです。

  • PubSub で送受信できるデータは、ユーザー設定に保存できる程度の長さの文字列である。これを「メッセージ」と呼ぶ。
  • チャンネル(=ユーザー設定項目)ごとに Publisher と Subscriber になるガジェットが固定した片方向通信になっている。双方向通信を実現するためには、 2 つのチャンネルを使う必要がある。
  • 通信に使えるチャンネルはあらかじめ定義されており、自由に作成できるわけではない。
  • ひとつのチャンネルを複数のガジェット(Subscriber)が受信できる。その場合、同じメッセージがすべての Subscriber に配信される。
  • それぞれのチャンネルごとに、メッセージの内容・フォーマットが固定されている。
  • 通信を行うとユーザー設定を保存するために Google のサーバーへのリクエストが発生する。

現在定義されているチャンネルは以下のとおりです。チャンネルは随時追加されていくようで、もし必要なチャンネルがあれば Developer Forum に提案してほしいとのことです。

チャンネル名 用途・メッセージの内容
test000〜test100 テスト用
rawquery 検索文字列など
finance_portfolio Google Finance の Portfolios のデータ?
finance_symbol 株銘柄のティッカーシンボル
finance_symbol_quote 株銘柄のデータ
lat 緯度
long 経度

以上の説明だけではあまりイメージが沸かないかもしれませんが、以降の実装例などを見ればご納得いただけるかと思います。とりあえず、こういうものかという程度に押さえておいてください。

実装方法

ざっと概要を理解したところで、さっそく実際に使ってみましょう。言葉だけで説明しても退屈なので、ここでは実際に動作するガジェットのソースをみながら、 PubSub 特有の部分についてご紹介していこうと思います。例として使うガジェットは、テキストボックスに入力された文字列を単純に送信する「Publisher」ガジェットと、受信した文字列をそのまま表示する「Subscriber」ガジェットの 2 つです。ホントになんの工夫もありませんね(^^ゞ

それぞれ以下のリンクで iGoogle に追加できますので、ぜひ実際に動かしてみてください。

Publisher ガジェットを iGoogle に追加
Subscriber ガジェットを iGoogle に追加

前述のとおり Firefox / IE 以外のブラウザでは正常に動きませんのでご注意ください。 Opera で試してみましたが、サーバーに延々とリクエストを投げ続ける始末です orz

Publisher の実装

まずは Publisher ガジェットから見ていきましょう。ソースは以下のようになっています。

<?xml version="1.0" encoding="UTF-8"?>
<Module>
<ModulePrefs title="Publisher" height="40" />
<UserPref datatype="hidden" name="test000" publish="true" />
<Content type="html"><![CDATA[
  <input type="text" id="message" />
  <input type="button" value="送信" onclick="sendmsg();" />
  <script type="text/javascript">
    function sendmsg()
    {
      var prefs = new _IG_Prefs(__MODULE_ID__);
      prefs.set('test000', _gel('message').value);
    }
  </script>
]]></Content>
</Module>

赤い行が PubSub 用の記述です。たった 3 行だけ。とても簡単に使えるのがお分かりいただけるかと思います。ベースとなるガジェットができていれば、基本的に以下の 2 つのコードを追加するだけで Publisher として機能するようになります。

  • 通信に使うユーザー設定。
  • ユーザー設定に値を保存する JavaScript コード。

通信に使うユーザー設定は、通常のものと同様に UserPref 要素を使って記述します。書式も通常のユーザー設定とほとんど同じですが、 name 属性で通信に使うチャンネルを指定し、 publish 属性を true にして送信用の項目であることを明示します。 Publisher ガジェットでは、通信チャンネルとして "test000" を使っているため、以下のような記述になっています。

<UserPref datatype="hidden" name="test000" publish="true" />

ここではデータ型を hidden にしていますが、必須ではありません。その他、必要に応じて他の属性を指定することも可能です。

あとは、通常どおり _IG_Prefs クラスの set メソッドを使って値(文字列)を保存すれば、それがそのまま Subscriber に送信されます。通常の HTML タイプのガジェットでは SetPrefs ライブラリを読み込まないと _IG_Prefs クラスが使えませんが、 PubSub 用のユーザー設定が追加されていればその必要はありません。デフォルトで _IG_Prefs クラスが有効になります。

さて、実際にユーザー設定を保存しているのは以下のコードです。

var prefs = new _IG_Prefs(__MODULE_ID__);
prefs.set('test000', _gel('message').value);

単純にテキストボックス内の文字列をユーザー設定に保存しているだけですね。これだけで、同じページに "test000" チャンネルを Subscribe しているガジェットがあれば、それらにメッセージが送信されます。

なお、ユーザー設定への値の保存は各ガジェットの設定ダイアログでもできますが、その場合はメッセージの送信が行われません。保存した瞬間にページリロードされるので、当然といえば当然ですが。 PubSub を使う場合は、必ず JavaScript を使って値を保存してください。

Subscriber の実装

次に Subscriber ガジェットを見ていきましょう。

<?xml version="1.0" encoding="UTF-8"?>
<Module>
<ModulePrefs title="Subscriber" height="40" />
<UserPref datatype="hidden" name="test000" listen="true" on_change="receivemsg" />
<Content type="html"><![CDATA[
  <div id="output"></div>
  <script type="text/javascript">
    function receivemsg()
    {
      var prefs = new _IG_Prefs(__MODULE_ID__);
      _gel('output').innerHTML = _hesc(prefs.getString('test000'));
    }
  </script>
]]></Content>
</Module>

こちらも PubSub 関連の記述は赤で示してあります。必要な記述は Publisher 側と類似していて、主に以下の内容になります。

  • 通信に使うユーザー設定の追加。
  • メッセージを受信したときに実行されるコールバック関数の追加。

通信に使うユーザー設定はやはり UserPref 要素で記述します。 name 属性でチャンネルを指定し、 publish の代わりに listen 属性を "true" にして受信用の項目であることを示します。さらに、通常は受信時のコールバック関数を on_change 属性で指定します。省略するとメッセージの受信を知る手段がないので、ほぼ必須といえるでしょう。

Subscriber ガジェットでは通信チャンネルに "test000" を使い、コールバック関数は "receivemsg()" なので、以下のような記述になっています。

<UserPref datatype="hidden" name="test000" listen="true" on_change="receivemsg" />

Publisher 側と同様にデータ型の "hidden" は必須ではありません。その他、必要に応じて通常のユーザー設定用の属性も指定できます。

次に、 UserPrefon_change 属性に指定したのと同じ名前で、受信時に呼ばれるコールバック関数を定義します。引数・戻り値はありません。この関数内で _IG_Prefs クラスの get 系のメソッドを使ってメッセージの内容を取得し、適切な処理を行えば OK です。 Subscriber ガジェットでは単にメッセージ内容を div 要素の innerHTML に設定しています。

function receivemsg()
{
  var prefs = new _IG_Prefs(__MODULE_ID__);
  _gel('output').innerHTML = _hesc(prefs.getString('test000'));
}

こちらも、 iGoogle ガジェットのプログラミングが理解できていれば、なにも難しいことはないと思います。

メッセージのグルーピング

チャンネルをひとつしか使わないのであれば、上述の方法で問題ありません。しかし、ひとつの Publisher が複数のチャンネルを持つ場合、それらを別々に更新するのは効率が悪いですし、トラブルの元になります。

例えば Publisher が lat と long の 2 つのチャンネルを定義しているとします。もし最初に lat を更新すると、 Subscriber は新しく取得した lat の値と古い long の値を組み合わせた場所を表示しようとするでしょう。そして次に Publisher が long を更新すると、 Subscriber はここではじめて新しい lat と long の値を取得でき、正しい場所が表示できます。これではいろいろと都合が悪いですよね。

そこで、 PubSub では複数のチャンネルをグルーピングして同時に更新する方法が用意されています。残念ながらサンプルのガジェットなどは用意できませんでしたが、複数のチャンネルを使う場合にはほぼ必須の知識なので、使い方だけご紹介しておきます。

Publisher の実装その 1

Publisher が複数のチャンネルを更新する最も簡単な方法は、単純に set メソッドに複数のチャンネルを渡すだけです。例えば、以下のように lat と long のチャンネルが定義されていたとします。

<UserPref name="lat" publish="true" />
<UserPref name="long" publish="true" />

この 2 つのチャンネルを同時に更新するには、以下のようにします。

var prefs = new _IG_Prefs(__MODULE_ID__);
prefs.set('lat', 35.6, 'long', 139.7);

非常に直感的でわかりやすい方法ですね。

Publisher の実装その 2

上記の方法は直感的なのですが、常にすべてのチャンネルの値を set メソッドに渡さなければいけないので、面倒な場合もあります。そこで、 UserPrefsgroup 属性を指定する方法もあります。

<UserPref name="lat" publish="true" group="geo" />
<UserPref name="long" publish="true" group="geo" />

こうすると、 group 属性の値が等しいチャンネルが自動的にグルーピングされます。例えば lat のみを以下のように更新しても・・・

var prefs = new _IG_Prefs(__MODULE_ID__);
prefs.set('lat', 35.6);

省略された long の値は変化しないものとして、同時に Subscriber に送信されます。値の変更はたいていグループの一部に対して行うが、 Subscriber への送信はすべてのチャンネルを同時に行いたい、という場合に便利です。

Subscriber 側の実装

Subscriber 側では、グルーピングの指定は常に UserPref 要素で行わなければなりません。 Publisher と同様に group 属性に同じ値を指定し、さらに on_change 属性にも同じコールバック関数を指定しなければなりません。

<UserPref name="lat" publish="true" group="geo" on_change="updateGoe" />
<UserPref name="long" publish="true" group="geo" on_change="updateGeo" />

こうすると、グルーピングされた複数のチャンネルが一気に更新された場合でも、 updateGeo 関数の呼び出しは一回のみになります。関数内では普通に get 系メソッドでチャンネルごとに値を読み込めば良いでしょう。

既知の問題

前述のとおり PubSub はまだベータ段階ですので、まだいくつかの不具合が残っています。 Develoer Forum でのアナウンスによると、以下の問題が確認されているようです。

  • Firefox / IE 以外での動作はサポートされていない。
  • URL ガジェットでは使えない。
  • iGoogle 上でのみ動作し、 iGoogle 以外の Web ページや Google Desktop などでは動作しない。
  • Subscriber ガジェットをドラッグ&ドロップで位置変更すると、それ以降通信ができなくなる。
  • finance_symbol チャンネルは正しく動作しない。
  • 空文字列を送信することはできない(無視される?)。
  • ガジェットのロード時に JavaScript エラーが発生する場合がある。
  • ブラウザの Stop ボタンで読み込みを中断すると動作がおかしくなることがある。

う〜む、けっこう致命的なのもありますが(^^;、近日中に解消されることを祈りましょう。それまでは割り切って使うしかありませんね。

もう少し実用的なサンプル

せっかくなので、 PubSub の特徴を生かしたもう少し実用的なサンプルガジェットも作ってみました。先ほどの Publisher ガジェットと同様に検索窓を表示する「検索」ガジェットと、指定されたフィードの最新 10 件のタイトルを表示する「フィード」ガジェットです。検索ガジェットに文字列を入力して「検索」ボタンをクリックすると、タイトルにその文字列を含むエントリーが強調表示されます。 iGoogle への追加は以下のリンクから。

検索ガジェットを iGoogle に追加
フィードガジェットを iGoogle に追加

フィードガジェットは複数追加でき、それらすべてが検索の対象になります。こんな感じで。

ガジェット間通信機能(PubSub)を使う : フィード検索ガジェット

たくさんのフィードを登録しておき、そこから特定の単語を含むエントリーを探す、なんてのに便利ですね。 AND, OR 検索に対応していない、表示エントリー数が制御できないなど常用するにはまだまだ厳しいですが、そのあたりはご自分のニーズに合わせて改造してみてください。

以上、本日は iGoogle ガジェット API に新しく追加されたガジェット間通信機能「PubSub」をご紹介しました。ちょっと感心したのは、これが既存の機能をうまく流用して作られていること。これによって実装の手間がだいぶ省けただろうことはもちろんですが、使う側の立場から見ても、まったく新しい API を覚えるよりもずいぶん楽に使い始めることができました。 API を設計するに当たって、これは非常に重要な要素ですね。勉強になりました。

このように簡単にマスターできる PubSub フレームワーク。 API の単純さとは裏腹に、非常に大きな可能性を持っていると思います。皆さんもぜひ PubSub を活用した便利なガジェットを作ってみてください!

関連記事

この記事にコメントする

Recommendations
Books
「Closure Library」の入門書です。
詳しくはこちらの記事をどうぞ!
Categories
Recent Articles