Helmaでページを作る | JavaScript | プログラミング

第1回からだいぶ間が開いてしまいましたが、Helmaで作るWebアプリケーションの記事、第2回目です。

今回はWebアプリケーションの初めの一歩、テキストベースのWebページを表示する方法と、アプリケーションコードに登場する「this」の正体、Helma独特の「HopObject」について触れていきます。

Helmaに新しいアプリケーションを追加しましょう

Helmaをインストールしたディレクトリ(以降Helmaホームと呼ぶことにします)の下に、「apps.properties」というファイルがあります。名前からおよそ推測できるとおり、Helmaで動くWebアプリケーションに関する設定を行うファイルです。

必ず書かないといけないものは、アプリケーションの名前です。例として「symple」という名前のアプリケーションを作ることにします。apps.propertiesの一番下に次の記述を追加します。

# List of applications to start.
# More information about this file is available at
# http://helma.org/docs/guide/properties/apps.properties/

# Administrative application to manage all other apps on this server,
# accessible via its default mountpoint at http://:/manage
# and using its default repository at apps/manage
manage

# More complex example of an application with custom configuration:

welcome
welcome.mountpoint = /welcome
welcome.repository.0 = apps/welcome/code/
welcome.repository.1 = modules/helmaTools.zip
welcome.static = apps/welcome/static
welcome.staticMountpoint = /static
welcome.staticHome = index.html,default.html
welcome.staticIndex = true
welcome.uploadLimit = 2048

symple			<-- この1行を追加する

これで追加ができました。他にも以下の表のような設定項目がありますが、設定がなくてもデフォルトの内容で動作するので、不都合がなければこのままでOKです。

参考:apps.propertiesでよく使う設定項目
設定項目名 説明 デフォルトの内容
<アプリケーション名>.mountpoint アプリケーションへのリクエストパス(コンテキストパス) /<アプリケーション名>
<アプリケーション名>.appdir アプリケーションの配置場所。実際はこの下の「Root」ディレクトリがファイルの置き場所になる (Helmaホーム)/apps/<アプリケーション名>
<アプリケーション名>.repository.x アプリケーションで使用するファイル/ディレクトリの配置場所。xには0以上の数字を記述する。0はappdirの内容を上書きすることになる なし
<アプリケーション名>.static 画像ファイル・CSS・ブラウザに送るJSファイルなどの静的なコンテンツファイル配置場所 デフォルト値はないので、静的コンテンツを使用する場合は設定が要る
<アプリケーション名>.staticMountpoint 静的コンテンツファイルへのリクエストパス /static

初めの一歩。Hello Helmaを表示させる

hacファイルとskinファイル

初めの一歩の大定番フレーズを表示するページを作ってみます。Helmaを起動すると、Helmaホームappsディレクトリの下に、「symple」という名のディレクトリができていると思います(起動中にapps.propertiesを編集した場合は、ファイル保存後すぐに作られる)。ここがsympleアプリケーションで使用するファイルの配置場所(アプリケーションディレクトリと呼ぶことにします)で、次の2種類のファイルを用意します。

  • ○○○.hacファイル
  • ○○○.skinファイル

拡張子「hac」のファイルは、アプリケーション処理を書くファイルで、JavaScriptで記述します(hacは「Helma Action」の略と予測)。「skin」ファイルはレスポンスボディのデザインレイアウト(HTML)にあたる部分を記述するファイルです。例えば「http://localhost:8080/symple/hello」のURLにリクエストしたとき、Helmaは「hello.hac」のアプリケーション処理を実行します。skinファイルの名前(拡張子を除いた部分)はhacファイルと一致する必要はなく、hacファイルで名前を指定します。

(hello.hac)

this.message = "Hello Helma";
this.renderSkin("hello");

(hello.skin)

<html>
    <head>
        <meta http-equiv="Content-Type"
            content="text/html; charset=Shift_JIS" />
        <title>helma sample</title>
    </head>
    <body>
        <h1>
            <% this.message %>
        </h1>
    </body>
</html> 

ちょっと待って!ファイルはRootディレクトリの下に置きます

hacファイルとskinファイルは、Helmaが作ったsympleディレクトリのすぐ下に置けばよいと思いましたか?そうではないのです。その下に、「Root」という名前のディレクトリを作り、そこに置く必要があります。なぜRootなのか?については、理由があり後で触れます。とりあえずその通りに配置しブラウザでアクセスすると・・・表示されました。

(http://localhost:8080/symple/hello の表示)

hello-helma

thisは何を指しているのでしょうか?

たった「Hello Helma」を表示しただけの、短いサンプルですが、Helmaを理解するのに重要なキーワードが点在しています。hacファイル、skinファイルの両方に登場する「this」。これは一体何でしょうか?これは、「HopObject」という、Helma独特のオブジェクトに関係しています。

HopObject(おそらく読みは『ホップ・オブジェクト』)は、Helma特有のプロパティやメソッドを備えたJavaScriptオブジェクトです。実は、アプリケーションディレクトリの下にディレクトリを作ると、ディレクトリ名と同じ名前のクラスがHopObjectを継承するクラスとして使えるようになっています。例えば、「Entry」というディレクトリがある状況で、hacファイルで「var e = new Entry();」と書けば、変数eはHopObjectを継承するEntryオブジェクトになります。

var e = new Entry();
e.title = "my first helma";
e.author = "author";

先ほどアプリケーションホームの下にRootディレクトリを作りました。お気づきかもしれませんが、このディレクトリ名と同じ名前のRootクラスもHopObjectを継承したものです。

Rootディレクトリ下のhello.hacやhello.skinで登場する「this」は、Root HopObjectを指し、this.messageは、Root HopObjectのmessageプロパティ、this.renderSkinはRoot HopObjectにバインドされたメソッドです。

Helmaがリクエストを受け付けたとき、フレームワークのどこかでRoot HopObjectを生成しているようです。このHopObjectのクラス名はRoot、変数はrootと決まっていて、アプリケーションにつき1つ必ず持つことになっています。

おそらく、HopObjectプロトタイプを継承するRootプロトタイプがHelmaの内部にあるのだと思います。また、Rootディレクトリを必要とするのは、パス名の部分に該当するActionを探しあてるためではないでしょうか。Helmaはアプリケーションを新しく作ったときにRootディレクトリまでは作ってくれず、自分で作らないといけないのがよく判らない点ですが。

HopObjectの利用例を見せたいと思います。先ほどのEntryを使うことにしますが、アプリケーションホームの下にEntryディレクトリを用意し、そこにスキンファイルを1つ作ります(list.skin)。

(アプリケーションディレクトリEntrylist.skin)

<tr>
<td><% this.title %></td>
<td><% this.author %></td>
</tr>

Rootディレクトリの下にはentry.hacとentry.skinを作ります。

(アプリケーションディレクトリRootentry.hac)

var entries = [
{ title : "はじめてのHelma", author : "char" },
{ title : "roundabout developer connection", author : "roundabout" },
{ title : "採用情報について", author : "recruit" },
{ title : "KTOYを使ってみませんか?", author : "ktoy" },
{ title : "Pマーク取得しました", author : "sono" },
];

var str = "";
for (var i=0; i < entries.length; i++) {
	var e = new Entry();
	e.title = entries[i].title;
	e.author = entries[i].author;
	str += e.renderSkinAsString("list");
}

this.entryList = str;
this.renderSkin("entry");

(アプリケーションディレクトリRootentry.skin)

<html>
    <head>
        <meta http-equiv="Content-Type"
            content="text/html; charset=Shift_JIS" />
        <title>entries</title>
    </head>
    <body>
        <table border="1">
            <tr>
            <th>タイトル</th>
            <th>投稿者</th>
            <% this.entryList %>
            </tr>
        </table>
    </body>
</html> 

HopObjectのrenderSkinAsString("list")メソッドから、「引数に指定した名前.skin」ファイルのレンダリング結果を文字列で受け取ることができます。entry.hacで、投稿オブジェクトの配列entriesの要素数分ループし、都度Entry HopObjectのオブジェクトを生成、entry.renderAsStringを呼びHTMLのソースの一部(tr、td)を作成しては変数(str)に付け足していきます。最終的にstrをRoot HopObjectのentryListプロパティに代入し、entry.skin上で展開されます。

このサンプルもあちこちに「this」が登場しますが、list.skinでの「this」はEntry HopObjectのことで、entry.hac、entry.skinでの「this」はRoot HopObjectです。

(アクセスした結果)
entries1
※日本語が文字化けしていますが、これの対処はすぐ後で触れます。

Helma Webフレームワークでは、skinファイル(プレゼンテーション層)に繰り返し処理や条件分岐などの一切のロジックを埋め込むことができないようになっています。上の例のように繰り返し処理をするときにはHopObjectの利用が欠かせません。

最後に、文字化けを解消する。

Helmaをインストールしたままの状態でほぼ問題なく動作することができますが、上のサンプルのように日本語が文字化けを起こします。日本語環境に対応するには、Helmaホームの下の「server.properties」を編集し、「charset」項目を追加する必要があります(デフォルトではISO-8859-1)。

charset = Shift_JIS

(日本語が表示できるようになったページ)
entries2

まとめ(感想)

インターネットで見つかるサンプルを見よう見まねで使う程度なら、Helmaの使い方はとても簡単ですぐに上手くいきます。しかし、サンプル以上のことが必要になったときの内容の理解がなかなか進みませんでした。日本語の解説がとても少ないので、Helmaの公式サイト(英語)を見ることが多く、英語に弱い私にはしんどかったせいもありますが・・・。

Helmaは「多年にわたり数多くのサイトで採用された、安定したソフトウェア」だそうです。それでも日本ではいまいち普及していないらしい(Helmaの詳しい日本語ページが見つからない、 Helmaを『お試し』以上に使った例が見られない)のは、Helmaが想定外に簡単だから=書かなければいけない設定やコードがあまりに少ないからでしょうか?

「想定外に簡単」とは、たとえばHopObjectのサブクラスの作り方です。ディレクトリを作るだけでその名前のHopObjectサブクラス(コンストラクタ関数)が使えるようになるという点は、私には今まで経験したことのないもので、そうとわかったときは大変驚きました。作業としてはとても簡単なのですが、事前には方法が予測できない「簡単さ」です。

Rootディレクトリを作ってそこにアプリケーションを置く点についても、理由が何なのか、長い間理解も納得もできていなかったことです。HopObjectのサブクラスを自分で使ってみたことで、Helma独自の流儀がだいぶ掴めてきたように思います。

その「簡単さ」が独自の流儀に基づくのですが、Helmaはそもそも、簡素でさっぱりとしたアプリケーションをサポートすることを目的としているそうです。また、JavaScriptという比較的敷居の低い言語で書けるのが特長です。JavaScriptが書ける人ならサーバーサイド開発に関われます。使い方によっては有益な点もあるように思いました。

ところで檜山研のJavaScriptチームは1月末で終了。Helma(サーバー)とAjaxを使った卒業制作(?)も終盤を迎え、Helmaを使うことはとりあえずこの1月で終わりです。ですがこのまま終わるのもつまらないしもったいないので、このブログエントリーを動機付けにしてしばらく触り続けてみようと思います。

次回は、Helmaでデータベースを使用する方法と、画像やCSSを使ったページ作りについて取り上げます。