Erlang製Webツールキットwebmachine 触ってみた
イントロダクション
bashoがオープンソースとして公開しているwebmachine触ってみました。
webmachineはErlang製のHTTPアプリケーション用フレームワーク...じゃなくてツールキットだそうです。
といってもRuby On Railsみたいにアレもコレもな感じではなく、あくまでサーバーにHTTPもっといえばRESTful APIを被せることを目的としたアプリケーションのようです。触りはじめたばかりでよくわかりませんが。Ruby界隈だとSinatraに近い感じでしょうか。
RubyでコーディングするSinatraに比べて、webmachineではErlang/OTPを使うことになるので、単純に言語やフレームワークの学習コストはwebmachineが高くつきますが、webmachine+Erlang/OTPの場合は軽量プロセス、OTPが
インストール
とりあえず、Quick Startにしたがってインストールしてみます。
githubからコードを取得
$ git clone git://github.com/basho/webmachine
以上w
Webアプリケーション雛形の生成
生成されたディレクトリに移動して、Webアプリケーションの雛形を作る
$ mkdir ~/tmp $ ./scripts/new_webmachine.sh demo ~/tmp
できたアプリケーションの雛形のディレクトリに移動してビルドする
$ cd ~/tmp/demo $ make
起動
エラーなくコンパイルが終了したら、HTTPサーバーを起動します。
$ ./start.sh
つらつら〜〜っとメッセージが表示されて起動完了したら、ブラウザから http://localhost:8000 にアクセスします。
Hello, new world
と表示されたら無事起動しています。
リソースの追加
では作ったWebアプリケーションにリソースを追加してみます。
emacsなどのエディタで"priv/dispatch.conf" を開きます。
初期状態では以下の用になっているはず。
{[], demo_resource, []}.
この設定だと、"/"へのリクエストはdemo_resourceというモジュールに振られ、それ以外はNotFoundが返ります。
ではひとつ設定を追加してみます。
%%-*- mode: erlang -*- {[], demo_resource, []}. {["hello", '*'], webmachine_hello_resource, []}.
追加した設定により、http://localhost:8000/hello へのリクエストはwebmachine_hello_resourceというモジュールへ振られることになります。
ちなみに最初の"hello"は文字列、そのあとの'*'はアトムですので気をつけてください。"*"では期待通りに動作しません。
今はまだ、そのモジュールがないので作ります。エディタで"src/webmachine_hello_resource.erl"という名前のファイルを新規で作ります。
-module(webmachine_hello_resource). -export([init/1, to_html/2]). -include_lib("webmachine/include/webmachine.hrl"). init([]) -> {ok, undefined}. to_html(ReqData, State) -> io:format("~p", [ReqData]), {"<html><body>hello</body></html>", ReqData, State}.
再びビルドして起動します。
$ make $ ./start.sh
ブラウザから http://localhost:8000/hello にアクセスしてみます。
hello
とアクセスされたらおkですね。
先ほどのルート指定では
["hello", '*']
としていました。
ここを何種類か変えてみます。
再び"/priv/dispatch.conf"を開いて、いくつかリソースを追加します。
{[], demo_resource, []}. {["hello", "china", '*'], webmachine_hello_china_resource, []}. {["hello", "japan", '*'], webmachine_hello_japan_resource, []}. {["hello", '*'], webmachine_hello_resource, []}.
以前と同様に、webmachine_hello_china_resource.erlとwebmachine_hello_japan_resource.erlを作ります。
src/webmachine_hello_china.erl
-module(webmachine_hello_china_resource). -export([init/1, to_html/2]). -include_lib("webmachine/include/webmachine.hrl"). init([]) -> {ok, undefined}. to_html(ReqData, State) -> io:format("~p", [ReqData]), {"<html><body>に〜はお</body></html>", ReqData, State}.
src/webmachine_hello_japan.erl
-module(webmachine_hello_china_resource). -export([init/1, to_html/2]). -include_lib("webmachine/include/webmachine.hrl"). init([]) -> {ok, undefined}. to_html(ReqData, State) -> io:format("~p", [ReqData]), {"<html><body>こんにちは</body></html>", ReqData, State}.
一旦サーバを止めてビルドしてサーバを起動します。
$ make & ./start.sh
"http://localhost:8000/hello/china" へのリクエストでは "に〜はお"、http://localhost/8000/hello/japanへのリクエストでは "こんにちは"と表示されたと思いますがどうでしょうか。
RESTfulアプリケーション
で、最後にRESTです。とりあえずこんな感じなんだろうかと書いてみました。
"http://localhost:8000/hello/china"に対して、GET,POST,PUT,DELETEそれぞれのメソッドごとに呼び出される関数を分けています。
こういう使い方が一般的なのかどうかわかりませんが...
-module(webmachine_hello_china_resource). -export([init/1, allowed_methods/2, content_types_accepted/2, post_is_create/2, create_path/2, to_html/2, from_json/2, delete_resource/2]). -include_lib("webmachine/include/webmachine.hrl"). init([]) -> {ok, undefined}. %%%=================================================================== %%% Allow Methods and Accepted Content Types %%%=================================================================== %% 許可するHTTPメソッド allowed_methods(Req, State) -> {['GET', 'POST', 'PUT', 'DELETE'], Req, State}. %% 許可するcontent type content_types_accepted(Req, State) -> {[{"text/html", to_html}, {"application/json", from_json}], Req, State}. %%%=================================================================== %%% Post setting %%%=================================================================== %% POSTリクエストは新規作成として扱うかどうか post_is_create(Req, State) -> {true, Req, State}. %% post_is_create/2がtrueを返したときに呼ばれる %% 新しいリソースへのパスを返す(このパスをLocationヘッダに埋め込んでかえす) create_path(Req, State) -> {"/new/path", Req,State}. %%%=================================================================== %%% Handler Functions %%%=================================================================== %% GET to_html(#wm_reqdata{method = 'GET'} = Req, State) -> io:format("http request(method:~p).~n", [Req#wm_reqdata.method]), {"<html><body>に〜はお</body></html>", Req, State}. %% POST from_json(#wm_reqdata{method = 'POST'} = Req, State) -> %% なにか新規作成処理する... io:format("json post request(method:~p).~n", [Req#wm_reqdata.method]), {true, Req, State}; %% PUT from_json(#wm_reqdata{method = 'PUT'} = Req, State) -> %% なにか更新処理する... io:format("json put request(method:~p).~n", [Req#wm_reqdata.method]), {true, Req, State}. %% DELETE delete_resource(Req, State) -> %% 削除処理する... io:format("delete request(method:~p).~n", [Req#wm_reqdata.method]), {true, Req, State}.
で、ここまできて今更ですが、わかりやすいスライドありました。
それからVoluntas氏によるGistが
https://gist.github.com/voluntas/4363064