WebOS Goodies

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

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

Closure Libraryによるアプリ開発のはじめ方

Python Hackathon のネタとして Closure Library を使って以来、だいぶ Closure Library を使い込んできました。個人的には非常に手に馴染んでいて、もはや Closure Library なしで Web アプリを作るなんて考えられないくらいです。

しかし、世間ではまだまだ使っている人が少ないようです。理由はいろいろあるでしょうが、初心者向けの親切なチュートリアルがほとんど存在しないために手をつけづらいのが大きいように思います。ちょうど Closure Library の構造や設計思想がだいぶわかってきたところでもあるので、私が考える「Closure Library の正しいはじめ方」をまとめてみました。題材は、簡単なリッチテキストエディタです。

けっこう凝っているように見えますが、やっていることは Closure Library に含まれているリッチテキストエディタのウィジェットを表示しているだけなので、スクリプトはわずか 10 行ちょっとです(^^;

開発環境を用意する

Closure Library 開発をはじめる最大の障壁は、開発環境の構築です。他のライブラリは単にダウンロードするだけで使えますが、 Closure Library は開発にコマンドラインツールを多用するので、いくつか必要なコマンドをインストールしなければなりません。コマンドラインツールは慣れていないと面倒に感じるかもしれませんが、やっていることは難しくないので、覚えてしまえばそれだけです。

ここでは Windows を前提にしますので、他の OS ではそれぞれのパッケージ管理ツール(Mac では MacPorts が便利です)などを利用してインストールしてください。

Subversion のインストール

Closure Library は Subversion リポジトリで公開されているので、ダウンロードには Subversion クライアントが必要です。 Windows には GUI クライアントもありますが、どちらにせよコマンドラインでの作業は必須なので、ここでは最も基本的なコマンドラインの Subversion をインストールします。最新の Windows 版 Subversion は以下の URL からダウンロードできます。

http://www.collab.net/downloads/subversion/

サーバーは必要ないので、「CollabNet Subversion Command-Line Client」を選ぶと良いでしょう。ダウンロードするには無料のユーザー登録が必要ですが、それが面倒なら、こちらのページにある Setup-Subversion-1.6.6.msi を使うのも手です。ただしバージョンが若干古いので(最新版は 1.6.11)あまりお勧めはしません。

Python のインストール

Closure Library に付属のコマンドラインツールは Python で書かれているので、実行には Python のランタイムが必要です。インストーラは Python 公式サイトの以下のページでダウンロード出来ます。

http://www.python.org/download/

いくつかダウンロードリンクがありますが、 Python 2.6 系列の Windows installer をダウンロード、インストールしてください。 Python 3 系列も使用可能なはずですが、私は確認していません。ダウンロードが終了したら、インストーラを実行してインストールしてください。以降ではデフォルトの "C:\Python26" にインストールしたものと仮定しますので、インストール先を変更した場合は適宜読み替えてください。

Closure Library を使用した開発

開発環境が整ったので、 Closure Library を使ったアプリケーションを開発してみましょう。まずはアプリケーション用のフォルダを用意します。任意の場所でかまいませんが、以下では "C:\sample" と仮定します。別の場所にフォルダを作った場合は、適宜読み替えてください。

Closure Library のダウンロード

Closure Library をアプリケーションフォルダにダウンロードします。コマンドプロンプトを起動して([すべてのプログラム]-[アクセサリ] にあります)、以下のコマンドを実行してください。

cd C:\sample
svn checkout http://closure-library.googlecode.com/svn/trunk/ closure-library

これで "C:\sample\closure-library" に Closure Library の全ファイルがダウンロードされます。

HTML の作成

アプリケーションを実行するための HTML ファイルを作成します。 "C:\sample\index.html" に以下の内容を保存してください。

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <title>サンプル</title>
  <link rel="stylesheet" type="text/css" href="sample.css">
</head>
<body>
  <div id="frame">
    <div id="toolbar"></div>
    <div id="editor"></div>
  </div>
  <script type="text/javascript" src="closure-library/closure/goog/base.js"></script>
  <script type="text/javascript" src="deps.js"></script>
  <script type="text/javascript">
    goog.require('sample.App');
  </script>
</body>
</html>

ごく単純な HTML ファイルなのであまり説明の必要はないかと思います。ただ、 SCRIPT タグで行っている内容は Closure Library 特有のものです。

まず、 base.js は Closure Library のコアコードが収められた JavaScript ファイルです。 Closure Library を利用するには、まず最初にこのファイルを読み込まなければなりません。

deps.js は後に作成するアプリケーションスクリプトの依存関係定義ファイルです。 Closure Library は多数の .js ファイルで構成されているため、手動で必要なファイルを読み込むのは非常に面倒です。そのため依存関係の自動管理機能が実装されており、依存関係定義ファイルの内容をもとにして必要な .js ファイルを自動的に読み込むようになっています。 Closure Library 自身の依存関係定義ファイル (closure-library/closure/goog/deps.js) は base.js が自動で読み込むのですが、アプリケーション側の依存関係はこのように手動で読み込みます。

最後に実行している goog.require() は、指定したクラス(およびそれが依存するすべてのクラス)を定義した .js ファイルを、依存関係管理機能で読み込むための命令です。 Closure Library のサンプルデモ では HTML ファイルに直接 Closure Library の各クラスの require とアプリケーションコードを記述していますが、この方法は好ましいものではありません。アプリケーションコードは独立した .js ファイルにして依存関係を正しく記述し、 HTML ファイルではメインのクラスのみを require するのが最良です。

スクリプトの作成

アプリケーションの .js ファイルは、専用のフォルダを作成して、すべてをそこに収めるのがオススメです。そうすることで、後に行う依存関係定義ファイルの構築やスクリプトのコンパイルなどがやりやすくなります。

ここでは、 "C:\sample\scripts" フォルダを作成し、その中にアプリケーションのスクリプトファイルである app.js ファイルを以下の内容で作成します。

goog.provide('sample.App');
goog.require('goog.editor.Field');
goog.require('goog.editor.plugins.BasicTextFormatter');
goog.require('goog.ui.editor.ToolbarController');
goog.require('goog.ui.editor.DefaultToolbar');

/** @constructor */
sample.App = function() {
  var editor = new goog.editor.Field('editor', document);
  var toolbar = goog.ui.editor.DefaultToolbar.makeDefaultToolbar(goog.dom.getElement('toolbar'));
  var controller = new goog.ui.editor.ToolbarController(editor, toolbar);
  editor.registerPlugin(new goog.editor.plugins.BasicTextFormatter());
  editor.makeEditable();
};
goog.addSingletonGetter(sample.App);

sample.App.getInstance();

最初に実行している goog.provide() 呼び出しは、このファイルで sample.App クラスを定義しているという宣言です。後にこの呼び出しをもとにして依存関係定義ファイルが構築されます。それに続く goog.require() は、 index.html のときと同じく、必要なスクリプトファイルの読み込みです。

次に、アプリケーション全体を管理するクラス(sample.App)を定義します。まず /** @constructor */ というコメントで、これから定義するのがクラスのコンストラクタであることを明示します。これがないと Closure Compiler が Warning を出すので、すべてのコンストラクタに付けておいてください。コンストラクタ内で行っている処理に関しては本題ではないので、解説は省きます。 API リファレンスなどを参照してください。

クラス定義の直後にある goog.addSingletonGetter() は、指定したクラスがシングルトンであるという宣言です。これを呼び出すと、クラスに getInstance() というクラスメソッドが定義され、アプリケーション中のどこからでもシングルトンクラスの唯一のインスタンスにアクセスできます(初回呼び出し時にインスタンスを作成し、以降はそのインスタンスをそのまま返します)。

最後に、その getInstance() を呼び出して、 sample.App クラスのインスタンスを作成します。他のライブラリでは DomReady のようなイベントでアプリケーションの初期化を行うことが多いですが、 Closure Library では SCRIPT タグをその実行に必要な HTML 要素の直後に配置して、スクリプトの読み込み時に初期化を行うことが推奨されています。

このような単純な処理なら、クラスを作るまでもないと思うかもしれません。しかし、 Closure Library はクラスベースのオブジェクト指向を前提に構築されているので、アプリケーションもそのスタイルを踏襲すべきです。そうすることで Closure Library の依存関係管理が正しく運用できます。ここではコンストラクタを定義しているだけですが、他の処理が必要になった場合もグローバル関数は使わず、すべてクラスのメソッドとして実装しましょう。

CSSファイルの作成

アプリケーションのスタイルシートを作成します。以下の内容で "C:\sample\sample.css" というファイルを作成してください。

@import "closure-library/closure/goog/css/common.css";
@import "closure-library/closure/goog/css/colormenubutton.css";
@import "closure-library/closure/goog/css/palette.css";
@import "closure-library/closure/goog/css/colorpalette.css";
@import "closure-library/closure/goog/css/editortoolbar.css";
@import "closure-library/closure/goog/css/button.css";
@import "closure-library/closure/goog/css/menu.css";
@import "closure-library/closure/goog/css/menuitem.css";
@import "closure-library/closure/goog/css/toolbar.css";

#frame {
  border: solid 1px black;
}

#editor {
  display:block; width:100%; height:400px;
}

スタイルシートに関しては HTML ファイルに直書きでも問題はありませんが、読み込む CSS ファイルが多くなると読みづらいので、私はいつも別ファイルにします。

Closure Library の CSS ファイルは "closure-library/closure/goog/css" にあります。使用する UI に応じて、必要なものを読み込んでください。どの CSS ファイルが必要かは・・・ファイル名や内容から判断するしかありません(^^;

依存関係定義ファイル (deps.js) の構築

最後に、 index.html で読み込む deps.js を作成します。 Closure Library にはこの作業を行うための depswriter.py というツールが付属しているので、これを使います(以前は calcdeps.py でしたが、 depswriter.py に移行しました)。

今回の場合は、コマンドプロンプトで "C:\sample" に移動し、以下のコマンドを実行すれば deps.py が構築できます。読みやすさのため複数行で記述していますが、実際には一行で入力してください。

C:\Python26\python closure-library/closure/bin/build/depswriter.py
  --root_with_prefix="scripts ../../../scripts"
  --output_file=deps.js

--root_with_prefix オプションは、依存関係を構築する .js ファイルを収めたフォルダ(ルートフォルダ)の指定です。以下の書式で指定します。

--root_with_prefix="<ルートフォルダ> <base.jsからの相対パス>"

.js ファイルの読み込みはすべて base.js のあるフォルダからの相対パスで行われるため、 base.js からの相対パスを指定する必要があります。


以下のバグは既に修正されています。

これで deps.js が生成される・・・のですが、出力されたファイルを見てみると、 Windows ではパス区切りが "\" になってしまうようです。

// This file was autogenerated by closure-library/closure/bin/build/depswriter.py.
// Please do not edit.
goog.addDependency('../../../scripts\app.js', ['sample.App'], ['goog.editor.Field', 'goog.editor.plugins.BasicTextFormatter', 'goog.ui.editor.DefaultToolbar', 'goog.ui.editor.ToolbarController']);

私はいつも Mac で開発しているので気づかなかったのですが、明らかにバグってますね。このままだとスクリプトが正しく読み込めないので、エディタの置換機能などを使ってすべての "\" を "/" に置き換えてください。

もしくは、 closure-library/closure/bin/build/depswriter.py で定義されている _GetDepsLine() メソッドを以下のように書き換えれば暫定的に対処できます。

def _GetDepsLine(path, js_source):
  """Get a deps.js file string for a source."""

  path = path.replace(os.path.sep, '/')

  provides = list(js_source.provides)
  provides.sort()

  requires = list(js_source.requires)
  requires.sort()

  return 'goog.addDependency(\'%s\', %s, %s);\n' % (path, provides, requires)

deps.js ファイルは、ファイルの依存関係が変更される度に再構築が必要です。したがって、新たな .js ファイルを作成したり、 goog.require() や goog.provide() を追加・変更した場合には、必ず上記のコマンドを実行してください。バッチファイルにしておくと良いでしょう。

実行する

以上でアプリケーションが完成しました。ブラウザに index.html を読み込むと、ツールバー付きのリッチテキストエディタが表示されるはずです。以下に実際に動作するものを置いておきます。ツールバーのボタンのうち、画像の挿入と HTML 編集は機能しませんが、他はすべて使えるはずです。

完成版のファイルは以下の場所にあります(Closure Library を除く)

http://webos-goodies.googlecode.com/svn/trunk/blog...

Closure Library のリッチテキストエディタはプラグインで拡張可能で、標準でいくつかのプラグインが含まれています。それらの使い方は、サンプルデモを調べてみてください。

スクリプトの統合

Closure Library は多数の .js ファイルで構成されているため、そのままではページを開く度に多数のファイル読み込みが発生して Web サーバーに負荷をかけてしまいます。そこで、依存関係管理機能を利用して、必要な .js ファイルを単一ファイルに統合する、 closurebuilder.py が用意されています。

それでは、スクリプトを統合してみましょう。コマンドプロンプトを起動し、 "C:\sample" に移動してから、以下のコマンドを実行してください。

C:\Python26\python closure-library/closure/bin/build/closurebuilder.py
  --root=closure-library --root=scripts
  -n sample.App -o script --output_file=combine.js

--root オプションは元の .js ファイルが格納されているフォルダの指定(Closure Library 自体のスクリプトも含めて統合するので、 closure-library と scripts の両方を指定しています)、 -n オプションは依存関係の起点となるクラスの指定です。これを実行すると、必要な .js ファイルをすべて結合し、combine.js に出力します。

あとは、 index.html のすべての SCRIPT タグを削除し、代わりに combine.js の読み込みを行います。

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <title>サンプル</title>
  <link rel="stylesheet" type="text/css" href="sample.css">
</head>
<body>
  <div id="frame">
    <div id="toolbar"></div>
    <div id="editor"></div>
  </div>
  <script type="text/javascript" src="combine.js"></script>
</body>
</html>

これで読み込む JavaScript がひとつになりました。ブラウザで開いて、正しく動作をすることを確認してください。

スクリプトのコンパイル

closure-builder.py には Closure Compiler との連携機能があり、統合したスクリプトファイルを Closure Compiler で最適化できます。これにより、ファイルサイズを削減するとともに、実行速度の向上が図れます。この方法もご紹介しておきましょう。

JRE のインストール

Closure Library は Java で組まれているので、実行には JRE (Java Runtime Environment) が必要です。以下のページからインストーラをダウンロードし、インストールしてください。

http://www.java.com/ja/download/

Closure Compiler のインストール

Closure Compiler は以下のページの右カラムの緑の枠内にある、「Download the application」というリンクからダウンロードできます。

http://code.google.com/intl/en/closure/compiler/

compiler-latest.zip というファイル名でダウンロードされるので、適当なツールで展開してください。展開されたファイルのうち compiler.jar が Closure Compiler の本体ですので、適当な場所にコピーしてください。ここでは "C:\sample\compiler.jar" にコピーしたものとします。

コンパイルを行う

あとは先ほどと同様に closurebuilder.py を実行するのですが、その際にいくつかのオプションを追加・変更して、以下のようにします。

C:\Python26\python closure-library/closure/bin/build/closurebuilder.py
  --root=closure-library --root=scripts
  -n sample.App -o compiled --output_file=compiled.js
  -c C:\sample\compiler.jar
  -f "--compilation_level=ADVANCED_OPTIMIZATIONS"
  -f "--define=goog.DEBUG=false"

違いは -o オプションを script から compiled に変更したこと、 compiler.jar のフルパス名の指定(-c オプション)、 Closure Compiler のオプションの指定(-f オプション)を追加したことです。さらに、出力ファイル名も compiled.js に変更しています。

ちなみに、 --define=goog.DEBUG=false という指定は、 Closure Compiler 内のデバッグコードを無効にするためのものです。開発中は不要ですが、アプリケーションのリリース時には付けておいた方が良いでしょう。

これでコンパイルができましたので、 index.html で読み込むスクリプトを combine.js から compiled.js に変更して、正しく実行できることを確認してください。 Closure Compiler は単なるサイズ削減だけではなく、関数のインライン展開や不要なコードの削除など、かなりアグレッシブな最適化を行います。 Closure Compiler との相性が良いのも Closure Library のメリットのひとつです。

以上、本日は Closure Library による開発のはじめ方をご紹介しました。 Closure Library はパッと見は面倒そうに思えますが、必要な機能が一貫性のあるアーキテクチャでまとめてあるので、実はとても便利です。アプリケーション・プラットフォーム化が進行する今後の Web 開発には必須のライブラリと言えるでしょう。さらに詳しく知りたいという方は、 Closure Library プログラミングガイドの購入をご検討ください! :)

関連記事

この記事にコメントする

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