最近 Kotti で Web アプリを作ってみようと調査しています。但し、今日は Kotti のお話ではなく、たまたま更新差分を見ていたら、fanstatic という静的リソース管理ツールが新規に使われているのを見つけました。ちょっと調べてみると、とても良さそうに見えたので紹介します。
fanstatic って何?
特にドキュメントでパッケージ名の由来を見つけられなかったのですが、fan + static と区切ってみると名前を覚えやすいです。個人的に fan- という係りが fancy, fantasy, fantasista などを連想させて言葉の響きが良いですね。static リソースの管理の煩雑さを解消してくれる夢のようなツールを連想します。
さて、fanstatic は、スマートな静的リソースパブリッシャーとあります。
テンプレートに静的リソースの記述をインジェクションしてくれるので、パブリッシャーという表現をしているのでしょうが、それよりも javascript や css といった静的リソースを Python パッケージとして管理できる仕組みに私は驚きました。
パッケージ、ドキュメント、リポジトリはそれぞれ以下になります。
fanstatic で jquery を使ってみよう
fanstatic を使うと、どんなことができるか、クイックスタート を参考にしながら見てみましょう。
まずは fanstatic と js.jquery というパッケージをインストールします。
(fanstatic)$ pip install fanstatic js.jquery (fanstatic)$ pip freeze Paste==1.7.5.1 WebOb==1.2 distribute==0.6.24 fanstatic==0.11.4 js.jquery==1.7.1 wsgiref==0.1.2
簡単なサンプルコードから紹介します。
(fanstatic)$ vi quick.py # -*- coding: utf-8 -*- from fanstatic import Fanstatic from js.jquery import jquery def app(environ, start_response): start_response('200 OK', []) jquery.need() return ['<html><head></head><body>Hello World</body></html>'] if __name__ == "__main__": from wsgiref.simple_server import make_server fanstatic_app = Fanstatic(app) server = make_server('0.0.0.0', 8080, fanstatic_app) server.serve_forever()
通常の WSGI アプリを作成して、2行だけ追加します。jquery.need() は、HTML の <head> セクションに <script> タグを埋め込みます。あとは Fanstatic で WSGI アプリをラップするだけです。
... jquery.need() ... fanstatic_app = Fanstatic(app) ...
実行結果を見てみましょう。
(fanstatic)$ python quick.py 別のターミナルで (fanstatic)$ telnet localhost 8080 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. GET / <html><head> <script type="text/javascript" src="/fanstatic/jquery/jquery.js"></script> </head><body>Hello World</body></html>
ちゃんと <head> セクションに jquery の <script> タグが埋め込まれていますね。
作業前に js.jquery を pip でインストールしただけで使えるのが便利です。jquery の最新版をダウンロードしてきて、どこそこに展開して、パス設定はどうしようかな、、、と悩まなくて済むのが凄いところです。また、jquery のような、汎用的なライブラリは、みんなで共有してバージョンアップもパッケージ管理ツール (今回は pip) で行えた方が楽で良いですね。
任意のアプリから静的リソースを管理する
もう少し現実的なアプリケーションについて、リソースライブラリの作成 から見てみましょう。
ここでは静的リソースを含む Python パッケージを作成してみます。まずパッケージディレクトリとひな形を作成します。
(fanstatic)$ mkdir myapp (fanstatic)$ cd myapp (fanstatic)$ mkdir -p foo/static (fanstatic)$ touch foo/__init__.py (fanstatic)$ touch foo/static/base.css (fanstatic)$ touch foo/static/base.js
次にパッケージの setup.py を定義します。
(fanstatic)$ vi setup.py # -*- coding: utf-8 -*- from setuptools import setup setup( name="myapp", version="0.1", install_requires=["fanstatic", "js.jquery"], entry_points={ "fanstatic.libraries": [ "foo = foo.static:lib_foo", ], }, )
install_requires に fanstatic と使いたい js ライブラリを記述します。ここでは js.jquery のみを記述します。fanstatic がパッケージ内の静的リソースの場所を見つけられるように entry_points を使って定義します。ここが1つのポイントです。
では、先ほど作成した static ディレクトリを fanstatic から見つけられるように static.py に定義します。サンプルコードから紹介します。
(fanstatic)$ vi foo/static.py from fanstatic import Group, Library, Resource from js.jquery import jquery _resources = [jquery] lib_foo = Library("foo", "static") foo_js = Resource(lib_foo, "base.js", depends=[jquery], bottom=True) _resources.append(foo_js) foo_css = Resource(lib_foo, "base.css", bottom=True) _resources.append(foo_css) resources = Group(_resources)
大体見た感じで雰囲気は掴めますが、詳細に見て行きます。
lib_foo = Library("foo", "static")
Library オブジェクトは、名前と静的リソースの置き場所へのパスを引数に取ります。ここで lib_foo は、setup.py の entry_points で定義した名前を使う必要があるのに注意してください。
foo_js = Resource(lib_foo, "base.js", depends=[jquery], bottom=True)
Resource オブジェクトは、実際の静的リソース (js/css) を定義します。base.js は jquery を使うスクリプトなので depends=[jquery] を定義することにより、base.js より前に jquery.js が読み込まれるようにインジェクションされます。
resources = Group(_resources)
Group オブジェクトは、複数のリソースをまとめます。あとで紹介しますが、複数リソースのインジェクションを resources.need() のように実行できて便利です。
主要な点は紹介しました。実際に動かせるように残りのファイルも紹介します。
main プログラムです。せっかくなので css/js が適用されていることを確認できるように html を少し変更します。
(fanstatic)$ vi foo/main.py # -*- coding: utf-8 -*- from fanstatic import Fanstatic from static import resources as static_resources def app(environ, start_response): start_response('200 OK', []) static_resources.need() html = """ <html> <head></head> <body> <button type="button" id="sample_btn">Click Me!</button> </body> </html> """ return [html] if __name__ == "__main__": from wsgiref.simple_server import make_server fanstatic_app = Fanstatic(app) server = make_server('0.0.0.0', 8080, fanstatic_app) server.serve_forever()
base.css と base.js は、それぞれ以下の通りです。
(fanstatic)$ vi foo/static/base.css button { border-color: #666666; background-color: #E8E6E1; font-size: large; padding: 10px; }
(fanstatic)$ vi foo/static/base.js $(function() { $("#sample_btn").click(function() { alert("Hello World"); }); });
最終的なパッケージの構成です。
(fanstatic)$ tree myapp/ myapp/ ├── foo │   ├── __init__.py │   ├── main.py │   ├── static │   │   ├── base.css │   │   └── base.js │   └── static.py └── setup.py 2 directories, 6 files
それでは、パッケージをインストールして、実行してみましょう。
(fanstatic)$ python setup.py develop (fanstatic)$ python foo/main.py
まとめ
fanstatic の強力さと利便性が分かる簡単なチュートリアルを紹介しました。
ここで紹介したソースは以下に置いてあります。
fanstatic リポジトリ を見ると、js. の名前空間で始まるライブラリがたくさんあります。ここに無ければ、自分でパッケージングして公開するのも良いですね。パッケージ管理の仕組みを、こんな用途にも使えるんだと再発見しました。おもしろいですね。