そろそろ2年間Mayaa使ってわかったことを書く

僕は、2年ほど前に、所属している会社の主要プロダクトであるのリニューアルに係わりました。その際、Mayaaを採用し、今日までにそれを運用してノウハウが蓄積してきたので、ここに発表しようと思います。

経緯

私たちはコンシューマ向けのビューを多く扱うので、プログラミング工数を要せずデザイナーがダイレクトにデザインをカスタマイズできる仕組みの実現が急務でした。

以前はテンプレートにJSPを採用していたため、デザインを作る(HTMLを書く)デザイナーと、プログラムに打ち込むJSPコーダーが必要という状況でした。一方ブログサイトなどでは、CSSレベルでのデザインカスタマイズをサポートしていることは多いですが、それでは自由度に限りが出てしまいます。フルHTMLレベルでのテンプレートエンジンが必要でした。

結果的に、この状況がどのように変化したのかは下記のエントリにも書きましたので、こちらも合わせて読んでいただけると幸いです。

プログラマーとデザイナーの境界が縮まってきた気がする

今回は技術よりのこと、及び運用していく上で培ったノウハウを紹介したいと思います。

JavaServerTemplates "Mayaa" とは

前置きが長くなってしまいましたが、自由なテンプレートの実現のため、Mayaaというライブラリを採用しました。

http://mayaa.seasar.org/

MayaaはSeasar2系の日本発オープンソースライブラリですが、Seasar2に関係なく使用することができます。JSPと同じレイヤーで動作し、JSPのようにServletからforwardして使うことができます。以下のページの図が分かりやすいです。

http://mayaa.seasar.org/documentation/about.html

Mayaaを導入する以前のテンプレートエンジンはこんな感じでした。

<% if (a != null) { %>
hello, <%= a %>
<% } %>

同じようなテンプレートをMayaaで書くとこんな感じです。

<div m:id="IF_A_EXIST">
    hello, <span m:id="A_HERE">dummy</span>
</div>

という感じで、普通のHTMLにm:idという属性が付いていますね。このようにしておくと、テンプレートファイルをそのままブラウザでプレビューすることが可能ですし、HTMLエディタ、例えば Dreamweaver で編集することができます。

Mayaaのすばらしさ

「テンプレートファイルをそのままブラウザでプレビューすることが可能です」といっても、簡単なようで簡単なことではありません。例えば画像の相対パスです。

<img src="images/xxx.jpg" />

ローカルではこのようになんの変哲もなく動く感じですが、サーバにデプロイしてみるとうまく動かないかもしれません。例えば、テンプレートは"view/hello.html"だけど、URLマッピングは別に管理されていて、"action/hello.do"かもしれません。そうすると、上記部分は

<img src="../view/images/xxx.jpg" />

<img src="http://something.example.com/view/images/xxx.jpg" />

のように書き換えるか必要があります。しかし、このようにしてしまったら、今度はローカルで画像が見られなくなったりします。Mayaaにはこの問題を解決するために、PathAdjusterという機構があります。

http://mayaa.seasar.org/documentation/path_adjust.html

詳しくは上記リンクに任せて割愛しますが、大ざっぱに言うと、一番目の記述を三番目の記述に実行時に書き換えてくれる機構です。これを応用すると、例えば「画像パスにtimestampを付与してmod_expireと組み合わせたクライアントキャッシュ対応」なんてこともできてしまいます。

その他にも、Webのフロントとして使う上でかゆいところに手が届く機能が結構付いているので気に入っています。

はまったこと

とはいえ、ここまで順風満帆というわけではありませんでした。例えばこんなころにハマりました。

最初が取っ付きにくい

よく言われます。これが故に、敬遠する方も多いようです。しかし、慣れてみると何も苦はなく、もうJSPには戻れません。ドキュメントが非常によく整備されていますので、とっつきやすさで言えば取っ付き易いほうだと思います。

とはいえ教育コストがかかります(mayaaファイルを覚えなければならない)

mayaaファイルという聞きなれない独自のファイルの記述が必要です。しかも、これを書くには、幾つかあるmayaaプロセッサーをマスターしていないと心もとないところです。

http://mayaa.seasar.org/documentation/processor_reference.html

そこで、mayaaファイルを書ける技術者の数は限られてしまいます。そのためには、「なるべくテンプレート(HTML)側に制御を委ねる」アプローチが必要かなと思います。(詳しくは後ほど書きます)

JavaScriptが気持ち悪い

mayaaでは、Javaオブジェクトとテンプレートとの橋渡しにJavaScript(Rhino)を使用します。Javaとそのまま埋め込めるJSPとは異なるので、「Javaのように見えて少し違う」書き方をしなければなりません。Javaだと思って書いていると、うっかり思いもよらないバグを作ってしまってハマります。これに対するアプローチは以下の二つかなあと思っています。

  • 出来る限りJavaに委譲する
  • 出来る限りJavaScriptに統一する

従来ならば、前者の書き方が理想だと思われますが、そのような方法を2年ほどやってきた結果、少々冗長さを感じています。ちょっとした変更でもサーバの再起動が必要なのも億劫です。(本番環境だと簡単に再起動できませんしね?)今はサーバーサイドJavaScriptも広く利用されています。今後は後者のアプローチも有効ではないかと思っています。 実際にやってみて失敗でした。JavaScriptのビルドや実行はJavaより遅いので多用するとパフォーマンス上の問題があります。JavaScriptはなるべく使わず可能な限りJavaに寄せるのが正解です(2014/07/02 追記)

また、別件ですが、mayaaファイルはXMLなので、以下のような冗談みたいな記述もあります

<m:if test="${hoge &gt; 0}">

本当はif (hoge > 0)と書きたいところなのですが、mayaaがxmlファイルなので、">"が書けずこのような書き方になってしまいます(笑)どうしても">"と書きたい場合は、""の中をCDATAにすればいいのですが、

<m:if test="${<![CDATA[hoge > 0]]>}">

さらに冗長になっちゃいました(笑)

負け惜しみとしてはmayaaのおかげで鍛えられて、もうltとgtがどっちかなんて迷うことはなくなりました(笑)

私たちが採用したノウハウ集

実際に使っていて培ったノウハウを発表します。

m:idに命名規則を使用する

mayaaには数多くのプロセッサーが存在しますが、HTML側から見ると、下記の4種類に集約されることがわかりました。

  • 変数を出力する
  • 属性を書き換える
  • 条件を満たす場合のみ表示する(条件を満たさなければ消える)
  • 特定の条件のもと繰り返す

それぞれを、"〜HERE"、"〜_TAG"、"IF〜"、"LOOP_〜"と命名するように統一しました。このようにすると、こんなこともできてしまいます。

Mayaaファイルを命名規則から一括作成するEmEditorマクロ

デザイナーとプログラマーで使う名前空間を分ける

Mayaaでは、動的なタグの紐付けにHTMLの"id"属性を使用できますが、"m:id"のように独自の属性を使用することができます。まずは有無を言わずそちらを採用します。

http://mayaa.seasar.org/documentation/equals_id_resolver.html

それでもどうしても、id属性やclass属性を使用したくなることがあります。これはMayaaに限らず一般論ですが。そこで、「プログラマーは全て大文字を使う」「デザイナーは全て小文字を使う」などの棲み分けをすると良いと思います。

xpath指定は使用しない

Mayaaではidの代わりにxpath指定でタグをコントロールすることができます。

http://mayaa.seasar.org/documentation/xpath.html

が、この方法は緊急時を除いて使用するべきではありません。あとからどうにもならなくなって泣けてきます。。。

また、パフォーマンス上の問題もありますので、できれば、templateResolverリストから外してしまうことをおすすめします。(2014/07/02 追記)

デバッグ用関数は値を返す

こんな関数を用意してdefault.mayaaから呼び出すようにするとデバッグで便利です。Javaのような豪華なデバッガがあれば本当はいいんですけどね

function debug(value, tag) {
    if (tag == null) tag = 'debug';
    java.lang.System.out.println(tag + "t" + tag);
    return value;
}

プログラムは文言を吐き出さない

以下のような書き方は、文言の変更にプログラムの修正が必要となってしまいます。

性別:<span m:id="USER_SE×_HERE">男性</span>

※上記コードは、中学生並みの恥ずかしがりではなく、変なサーチキーワードに引っかからないように一部改変しています。

例えば、「男性」という文言ではなく、画像にしたいかもしれません。英語化するかもしれません。以下の書き方が良いです。

性別:<span m:id="IF_USER_MAN">男性</span><span m:id="IF_USER_WOMAN">女性</span>

id一覧表を自動生成する

mayaaファイルはxmlファイルなので、比較的簡単にパースできます。XMLコメントもパースの対象にできるので、それを元にid一覧表を作るのは容易です。JavaDocを自作するイメージです。(ソース載せようとしたらちょっと長いので割愛します。SAXを使うとスムーズに実装できました。

http://www.atmarkit.co.jp/fxml/rensai/xmljava04/xmljava02.html

また、ドキュメントの出力部分では、以下を参考に、ここでもMayaaを使用しました(笑)

http://d.hatena.ne.jp/terazzo/20071116/1195250832

PathAdjusterをうまく使う!

Webサイトですから、ハイパーリンクを多用します。この時、いちいちリンク一つ一つにm:idを発行していたら大変です。そういう時は、PathAdjusterでなんとかしましょう。僕は、adjustRelativePathのオーバーライドで、拡張子が".html"の場合はsuper.adjustRelativePath(base, path)に引き継がず、独自で処理するようにしました。結果的に、リンクは以下のように書くことになっています。

<a href="member_input.html">会員登録</a><!-- 実行すると、servlet用のURLに変換される -->

このように、PathAdjusterをうまく使うことで、発行するidの数を減らすことは非常に強力です!

一つのmayaaファイルで複数のテンプレートを使い回す。

Mayaaは設定ファイル

  • org.seasar.mayaa.provider.ServiceProvider
  • org.seasar.mayaa.source.PageSourceFactory

などの記述で非常に柔軟にエンジンをカスタマイズできます。

前述したとおり、mayaaファイルはできるだけ少なくしたほうが得策です。そこで、一つのページをベースに複数の派生ページがデザインレベルで作れると幸せです。以下のエントリでその試みを成功させました。

Mayaa ファイルの共通化

そうこうしていると、「派生したページでちょっとだけm:idを追加したい」なんていう要望も発生してしまいました。これについても、EqualsIDInjectionResolverのカスタマイズで対応することができてしまいました。

http://ml.seasar.org/archives/mayaa-user/2010-January/000893.html

これは、当時での実現方法ですので今は、https://www.seasar.org/issues/browse/MAYAA-76
が実装されたのでさらに簡単にできると思います。

m:idの数を減らす

例えば、「名前の頭30文字を出力して残りは...にする」「名前の頭60文字を出力して残りは...にする」「名前の頭60文字を出力して残りは...にする」のように、パラメータが存在する制御をしたい時があります。そんなとき、

  • NAME_30_HERE
  • NAME_60_HERE

のようにいちいちつくっていたらヤボです。はじめのうちは本当にそんな感じで対応していたためミスもありました。

今は以下の方法でパラメータ化を実現しています。

function $p(name, defaultValue) {
    var attr = originalNode.getAttribute(
        Packages.org.seasar.mayaa.impl.engine.specification.SpecificationUtil.createQName(name)
    );
    if (attr != null) return attr.value;
    attr = originalNode.getAttribute(
        Packages.org.seasar.mayaa.impl.engine.specification.SpecificationUtil.createQName(originalNode.getDefaultNamespaceURI(), name)
    );
    return defaultValue;
}

テンプレート側では、以下のように書けばOKです。

<span m:id="FREE_HERE" m:free_no="20">自由項目20番</span>

m:を抜かすと実行後のマークアップにfree_noが残ってしまいますのでつけることをおすすめします(2014/07/03 追記)

コメントはこう書け!

JSPだとこんな風なコメントが使用できました。

<%-- ほげほげ --%>

開発が切羽詰ってくるとコメントも荒れてきます。うっかり外に出してはまずい言葉を書いてしまうかもしれません。JSPコメントならば出力されないのでセーフですが、mayaa+HTMLだと、この書き方ができないので、うっかりテンプレートに開発者しかしらない内部情報を書いては大問題です。そんな時はこう書きましょう。

<!-- ${/* FIXME:ここ意味がわからないけどなぜかそれっぽく動く>< */} -->

これでブラウザには出力されなくなります(笑)

最後に

今どきUIに強いプログラマー=モテです。さあ、ここまで読めば、君も立派なMayaa使い!

ちなみに、弊社の求人はこちらです↓

http://www.interfactory.co.jp/recruit/index.html

(2011/07/16)書き忘れたことを別エントリで追記しました

2年間Mayaaを使ってわかったこと その2

この記事は以前多くのブックマーク・コメントを頂きました。

これからブックマークされる方はこちら↓


そろそろ2年間Mayaa使ってわかったことを書く」への3件のフィードバック

  1. s-ishigami

    記事から2年近く経つのですね。今でも業務で現役で使用しています!今は似たようなライブラリはたくさんありますが、パフォーマンスを極限まで追求せず、Servlet上で使用するならMayaaが一番安定しているのではないでしょうか?

  2. ヒデ★

    なるほど、回答ありがとうございます。
    業務上デザイナとプログラマの分業というのが無くて両方
    プログラマがやる事が多く、なかなか触る機会がありませんでした。

コメントを残す

メールアドレスが公開されることはありません。