2014年9月30日火曜日

【JsRender】基本表示機能

株式会社ジェニシス 技術開発事業部の遠藤 太志郎(Tacy)です。

本日よりJavaScriptによる画面レンダリングツール「JsRender」について本格的に連載を開始します。
最初の今日は導入編です。

ダウンロード

さて、まずは必要なJSファイルをゲットする所から始まります。

「JsRender」は単体でも機能するライブラリのようですが、普通はjQueryと組み合わせて使うものかと思います。
なので、本連載ではjQueryも併用することを前提で進めてまいります。

それぞれ、ダウンロード元は以下になりますので、各自ダウンロードして下さい。




取得したら、普通にHTMLのヘッダー部分でインポートしてあげればOK。
簡単ですね。

<head>
  <script src="./js/jquery-1.11.1.js"></script>
  <script src="./js/jsrender.js"></script>
</head>

表示箇所を定義

次に、HTML上のどこにJsRenderの結果を出力したいのかどうかを意味する場所を定義します。

DIVタグでIDを記述すれば良いでしょう。

<div id="result"></div>

「id="result"」と定義しました。この場所にJsRenderを使って文字列を流し込みます。

ちなみに、これはHTMLを作成する上では常識ですが「ID」とは画面上で一意にするのがルールです。

HTMLはいい加減な言語なので、IDが重複しようがタグを閉じ忘れようが、かなりテキトーに書いても
そこそこちゃんと表示されてしまいます。

しかし、本件の処理は「JavaScript」ですから、こっちはそういい加減に書くわけにはいきません。
ID属性は常に一意。
その他、可能な限りHTMLの記述ルールを守って実装して頂けますよう、お願い致します。

テンプレートを定義

では、この辺りからJsRenderならではの要素に入ります。
テンプレート定義です。

例えば、「私の名前は○○です。」という文章を表示したいと思います。
この○○の部分だけは動的に変更して、それ以外は固定値、というテンプレートにしたい。
そのテンプレートが以下になります。

<script id="jsRenderTmpl" type="text/x-jsrender">
私の名前は{{:name}}です。
</script>

これでもう何となくお察し頂けたかと思いますが、「{{:name}}」で、「画面上に文字を記述する」という
意味を持つJsRenderの定義なのです。

Strutsで言う所の以下みたい記述ものです。

<bean:write name="usrdata" property="name" />

もう一つの特徴が「type」です。普通であれば「type="text/javascript"」と書くところ、
JsRenderの場合は「type="text/x-jsrender"」となります。

「このスクリプトはJsRenderの記述だ」と宣言しているわけですね。

これにより、


  • scrpitタグで括っているのでHTMLには表示されない。
  • 「type="text/x-jsrender"」なので、JavaScriptとは認識されない。


この特性が得られて、純粋な外部定義のように扱うことが出来るのです。


JsRenderを実行
さて、上記でテンプレート定義が出来ました。
後はJavaScriptとしてJsRenderを実行するのみです。

そのスクリプトはこちら。

<script>
var data = {"name": "遠藤 太志郎"};

var template = $.templates("#theTmpl");

var htmlOutput = template.render(data);

$("#result").html(htmlOutput);
</script>

まず最初にJSONを読み込みます。

「var data = {"name": "遠藤 太志郎"};」

ここではサンプルなのでJSONは固定値ですが、本番ではこのJSONを非同期通信により
サーバから動的に持ってくることになります。
キー値としましては、上記のテンプレートと合わせて「name」としました。

JSONはAjaxとは切っても切れない基本要技術です。
ご存じ無い方はぜひ習得して下さい。

次に、テンプレートを読み込みます。

「var template = $.templates("#jsRenderTmpl");」

これはJsRender独自の記述ですね。
「type="text/x-jsrender"」のID「jsRenderTmpl」を指定してテンプレートを読み込みます。

そして次に、テンプレートとJSON定義を合体させて、描画する文字列を作成。
ここもJsRenderの機能です。

「var htmlOutput = template.render(data);」

最後に、jQueryの機能で描画して終わり。

「$("#result").html(htmlOutput);」

なので、JsRenderは描画する文字列を出力する機能に絞っており、
それを描画する機能はjQueryの標準機能で行うという関係にあるわけです。

機能毎の独立性が担保された良い設計だと思います。

ほら、簡単に表示されました。


まとめると以下のようなソースになっています。

<!DOCTYPE html>

<html>
<head>
  <script src="./js/jquery-1.11.1.js"></script>
  <script src="./js/jsrender.js"></script>
</head>
<body>

<div id="result"></div>

<script id="jsRenderTmpl" type="text/x-jsrender">
私の名前は{{:name}}です。
</script>

<script>
var data = {"name": "遠藤 太志郎"};

var template = $.templates("#jsRenderTmpl");

var htmlOutput = template.render(data);

$("#result").html(htmlOutput);
</script>

</body>
</html>

どってことは無い簡単なスクリプトで実現可能であることがお分かり頂けるでしょう。

終わりに

以上で、基本の基本。
「ただ描写するだけ」という機能についてのご紹介は終わりです。

ハッキリ言って、JsRenderが無くても描画は出来るって言えば出来るんですが、
「type="text/x-jsrender"」によって外部定義することにより、
ロジックとテンプレートを独立させることが出来るようになっています。

これはソースの可読性を高める為には大変効果的です。すばらしい!!

次回から順次、if文やfor文などの機能についてもご紹介していきます。

2014年9月17日水曜日

【JsRender】始めに

株式会社ジェニシス 技術開発事業部の遠藤 太志郎(Tacy)です。

ここ最近は、クラウド基盤「Google App Engine(以下、GAE)」の連載していますが、
本日からは少し趣向を変えてAjax関連のお話に行こうと思います。

jsp禁止

さて、なぜ急にGAEからAjaxに話が触れたかと言いますと、こちらの回でご紹介しましたとおり、GAEというのはフルAjaxで開発するのがベストなのです。

jspを使うと画面がホワイトアウトしている時間が発生してしまいますからね。
まあ、機能を果たすという意味ではjspを使っても良いんですけど、やっぱり何秒もホワイトアウトしているのは心象が悪いでしょう。
私は断固としてAjaxを推したいと思います。

しかしですね、jspが使えないとなると、一体どうやって画面上の分岐を表現すれば良いのでしょう?
例えば、「検索結果がある時は一覧を表示する。無い時は赤文字でエラーメッセージを表示する」とか。

jspが使えればifタグで一発なのですが、使えないとなるとこれは厳しい。

そこをAjaxを使って動的に……。
って、もちろんやろうと思えば自前でAjaxを作って実現出来ますけれども、その度にロジックを組むのは効率悪過ぎです。

jspと比較しての生産性の低下が深刻です……。

そこで私が考えたのが、「AJaxでjspみたいなことが出来る便利なライブラリは無いの?」でした。
そう、Ajaxによるテンプレートエンジンです。

テンプレートエンジン

さて、Ajaxで困った時はjQueryに限ります。

十年ほど前ははAjaxライブラリが群雄割拠していましたが、今はjQueryが支配的になりました。
「何かAjaxで良い機能無いかな?」と思った時は、とりあえずjQueryから着手するのが一番効率的です。

そして私が探した所、ありました!!


  • jQuery Template


まさに期待通り、jspタグみたいな機能をAjaxでやる為のjQuery公式プラグインです。
jQuery公式なら安心ですね。早速導入を、と思いきや。

何と開発停止。(泣)




作り始めたは良いものの、途中で方針を変更して、まだ諦めずに頑張ると言いつつも、そのうち頓挫したというオチのようですね……。

残念な限りですが、その代わりに台頭してきたのが「JsRender」です。


JsRender

JsRenderはjQuery公式ではありませんが、jQuery Templateの後継のようなポジションにあるライブラリです。
公式サイトはこちらです。


公式サイトに解説も沢山載っていますので習得も捗ります。

フライングになりますが、例として『検索結果一覧の表示』のサンプルをご紹介すると、以下になります。

まず、HTMLにテンプレートとなるタグを書いて、

<script id="shohinListTemplate" type="text/x-jsrender">

<table class="shohinList">
 <tr>
  <th class="no">NO</th>
  <th class="shohinId">商品ID</th>
  <th class="shohinName">商品名</th>
  <th class="action"></th>
 </tr>
 {{for shohinList}}
 <tr>
  <td class="no">{{>seqNo}}</td>
  <td class="shohinId">{{>shohinId}}</td>
  <td class="shohinName">{{>shohinName}}</td>
  <td class="action">詳細 編集</td>
 </tr>
 {{/for}}
</table>

</script>

後はJavaScript部に「$("#shohinList").html($("#shohinListTemplate").render(json));」を一発書くだけでOK。
(json変数にはもちろんjsonのデータが入ります)

「{{for shohinList}}」を見ただけでも、何となくfor文でループしていることがお分かりになるでしょう。
直感的に使いやすい良いデザインのライブラリですね。

終わりに

このJsRender作戦はGAEに限ったことではありません。
私は別業務でAndroidの開発も行った事があるのですが、その時に「HTML5アプリ」と「ネイティブアプリ」の良い所取りをする「ハイブリット型」の設計を採用しました。

その時に、ネイティブ機能で整形したデータをHTMLに表示する時に使ったのも、この「JsRender」でした。

とても使い勝手の良いライブラリなのでぜひオススメです。

次回から各種機能について順次ご紹介していきます。

2014年9月10日水曜日

【GAE】初級実装編 インサート実行3 時間差反映

株式会社ジェニシス 技術開発事業部の遠藤 太志郎(Tacy)です。

只今、クラウド基盤「Google App Engine(以下、GAE)」の連載しています。

ここ数回はDBへのインサートについてご紹介していますが、今回がその最後です。

登録成功!! しかし……

前回までの処理で、無事にレコードをDBに登録する「put」が出来るようになりました。

では、さっそく、登録したレコードを取得する「get」をやってみましょう。

ShohinDao dao = new ShohinDao();
Shohin shohin = dao.get(key);

あれ?取れないな。
おかしいぞ。
もう一回!
よいしょ!!
よいしょ!!
お、取れた。

変ですね。
目の錯覚でしょうか?

違うんですよ。
GAEは遅延するんです!!

分散処理の宿命

そう、普通のDBであれば、テーブルに書き込んでコミットした瞬間に処理が完了しますから、遅延なんて起きません。

しかし、GAEは分散処理システムですからそうは行かないのです。
put処理が完了した後でも、その向こう側では非同期で色々と処理が走っており、それら全てが完了した時、初めてgetでレコードが取得出来るようになります。


この現象、ツイッターなんかやっていらっしゃる方だとご理解頂けると思います。

「ツイートするぜ、ポチッ!」とやっても、送信完了の瞬間に自分のツイートが反映されてませんよね?
でも数秒すると出てくるようになる。
アレが正にこの現象です。

即時性を犠牲する代わりに、可用性や耐久性などを優先しているのです。


こういうのを「結果整合性(eventual consistency)」と言います。


処理が多少遅延したとしても、最終的にはDBへの登録はもちろん、レプリケーションへの反映まで一通り一貫性を保って完了すればOKという考え方です。

「最終的にはちゃんとやっておくから、過程についてゴチャゴチャ言うな」と言わんばかりのこの発想。
日本人では出て来ない発想かもしれません。。。

どうすりゃいいの?

さて、問題はこの遅延とやらの所要時間ですね。

遅延するのは分かったけど、どれくらい遅延するのか?

ネットで調べた所、どうも「長くて1秒くらい」という声が見受けられます。
私も実際何度か実験してみましたが、同感ですね。大体こんなもんかと思います。

しかし、更にネットを調べてみますと「反映まで半日かかった」なんて声も見つかりました。

半日の遅延は流石に看過できませんが、要はこういう時は障害が起きているのです。
流石GAEということか、障害が起きても完全ダウンするわけではなく、大幅遅延でも縮退運転で耐える堅牢性を誇ります。
(半日も遅延させるんだったら、いっそのことダウンさせてくれた方がマシな気もしますが)

何が言いたいかと言いますと、結局、何秒遅延するかは分からないということです。

平常運転なら1秒未満で反映されますが、少々混み合っていた場合は数秒くらい要することだってあります。

つまり、『登録⇒即表示』を厳密にやらなきゃいけない仕様はNGと言う事です。

例えば、


  • 登録完了⇒即座に登録結果確認画面へ自動遷移。


なんて仕様は実現不可能です。
また、

  • 商品発注完了⇒確認画面をチェックしたけど何も出てない。⇒もう一回⇒二重発注。

なんて、運用的に確認が必須な業務の場合もキツいものがありますね。
こういう場合は、

「商品を発注しました。発注番号はXXXXXXです。発注履歴の確認は商品発注確認画面をご覧下さい。
システムが混み合っている場合は反映が遅れる場合があります。予めご了承下さい」

みたいに注意書きで逃れる、とかですかね。

「発注しましたッ!!」「発注番号はこれッ!!」という感じに発注完了メッセージにインパクトを持たせて、
「あれ~? 今、俺ってちゃんと操作したっけ? もう一回やってみよう」みたいな不安感を利用者に与えない画面設計が重要になります。

逆に、「二重送信したら後で消せばいいだろ」みたいな水準で十分な要件である場合は知らんぷりを決め込むのもアリ。

インターフェース設計のセンスが問われる所になりますね。

終わりに

これでDBへの登録関連は完了です。

次はDBから取り出した値を画面に表示するわけですが、GAEではjspを使わないのがお約束。

フルAjaxで勝負します。

次回からはAjaxライブラリを使った描画処理についてご紹介です。

2014年9月3日水曜日

【GAE】初級実装編 インサート実行2 一意制約

株式会社ジェニシス 技術開発事業部の遠藤 太志郎(Tacy)です。

只今、クラウド基盤「Google App Engine(以下、GAE)」の連載しています。

さて、前回ではGAEの最大の特徴である「BigTable」へのデータ登録をご紹介しました。

今回はそのデータ登録についての、ちょっと込み入った話、「一意制約」についてご紹介します。

GAEは主キーだけ

Oracle、postgreSQL、MySQLなどメジャー所のデータベースには、大概はDB独自の「制約」を設ける機能があります。


  • 主キー制約
  • 一意制約
  • 外部キー制約
  • null禁止制約


この辺りがよく使う制約だと思いますが、しかし、GAEには一意制約しか存在しないのです!!

何と不便な!!

要するに、GAEみたいなクラウド基盤は分散処理かつ大量データを取り扱っている都合上、負荷軽減とか仕様の問題で細かいチェックを行うことが出来ないのです。
このうち、外部キー制約とnull禁止制約はロジックで頑張れば対処出来ます。

  • 外部キー制約:親テーブルが無くて子テーブルだけある、みたいなことにならないように、トランザクション等も駆使してキッチリJavaのロジックを組む。
  • null禁止制約:登録、更新時にそのカラムがnullにならないようにJavaロジックを組む。

つまるところ、「バグが無いようにきっちり作れッ!!」という話で済むのです。

しかし、「一意制約」ばかりはそうはいかない。
もちろん、DB上でカラムが重複してはいけないようにJavaのチェック機能を入れるのはアリですれども、
Webシステムでの運用という都合上、「同タイミングの二重アクセス」という宿命からは逃れられません。

一意制約だけは、どれだけキッチリJavaロジックを組んでも担保出来ないのです。

しかしながら、GAE専用ライブラリであるSlim3には、主キー制約を上手く使って一意制約を実現してくれる機能があります。
今回はそれをご紹介します。

実装方法

さて、上記のようにGAEには一意制約が無く、主キー制約しかありません。
そこで、Slim3では「一意制約チェック専用のテーブルを作って主キーチェックを利用する」という機能が備わっています。

メソッドは「Datastore.putUniqueValue」。

例えば、ソース的には以下みたいな漢字に組めば良いというわけです。

public Key insert(Shohin model) throws Exception {

  Transaction tx = Datastore.beginTransaction();

  try {

   tx = Datastore.beginTransaction();

   Key key = null;

   if (Datastore.putUniqueValue(Shohin.UNIQUE_SHOHIN_NAME, model.getShohinName())) {
    key = super.put(model);
   } else {
    throw new BusinessCheckException("対象の商品名は既に登録されています。");
   }

   /*
    * 商品IDのあいまい検索用にIDを文字列化して再保存する。
    */
   model.setShohinId(Long.toString(model.getKey().getId()));
   put(model);
   tx.rollback();

   return key;

  } catch (Exception e) {

   tx.rollback();
   throw e;

  }

 }

こうすると、ほら。
下の図みたいに、一意キーチェックを行う為だけのテーブルが作成されます。
こうやって二つのテーブルを駆使することで一意制約を実現するわけですね。



登録する時が、「Datastore.putUniqueValue」。
削除する時が、「Datastore.deleteUniqueValue」。


つまり、


  • 新規登録時は、putするだけ。
  • 更新時は、先にputして後でdelete。
  • 削除時は、deleteするだけ。


こんな漢字にトランザクションを活用しつつロジックを組むことで、一意制約が実現出来るわけですね。
流石はSlim3。
良い機能が備わっています。

注意点

しかしですね、上記の通り、一意制約の度に新しいテーブルが作られてしまいますので、リソース的にも処理速度的にも、一意制約を多用するのは避けた方が良いと思います。

上記は例として「同じ商品名は登録出来ない」という仕様にしましたけれども、ケースによっては「普通のJavaチェックだけで十分。万が一被った場合は消せば良い」程度で済んでしまう程度の重要度であることも十分考えられますよね。
同時アクセスなんて滅多に無いですから、Javaチェックだけで99.9%はブロック出来ますもの。

そういう場合は一意制約はあえて入れないで、万が一問題が起きた時だけ運用回避する、という方針で十分かと思います。

しかし、逆に「絶対に何が何でも、死んでも重複して貰っては困る!!」なんて場合もあるでしょう。
例えば「メールアドレスは一意」などがそうです。
ログインIDの代わりにメールアドレスを使っているサイトなのに、メールアドレスが重複して入ってしまっていたら、これは大問題ですよね!!
万が一にもこんなバグは許されません。

このように、本気の本気で一意制約が必要な重用機能だけチェックを入れて、それ以外はJavaによるチェックのみで楽観的に済ませる。
これくらいの方針が良いでしょう。

終わりに

流石、Slim3は便利な機能が揃っています。事実上の標準フレームワークたるシェアを占めているだけのことはあります。

次回も引き続き、DBへのインサート処理の特記事項をご紹介します。