仕事の手離れを良くする手段としての、静的検査のあるテンプレートエンジン...
DESCRIPTION
2014-10-17 に開催された勉強会、テンプレートエンジン Night で YATT::Lite について発表した時のスライドです。 実際のトークでは FAQ は割愛しました。 なお、revealjs で書いた元スライドは下記にも置いてあります http://buribullet.net/~hkoba/2014tenight/ こちらの方がスライド内のリンクが有効なので、良いかもしれません。TRANSCRIPT
仕事の手離れを良くする手段としての、
静的検査のあるテンプレートエンジン
@hkoba
自己紹介: @hkoba
小林弘明個人事業主(2003〜)
さんの Web のお仕事Perl, Tcl/Tk, Zsh, EmacsLisp
テンプレートエンジンYATT::Lite 作ってます
SSRI.com
github.com/hkoba/yatt_lite
Perl 関連〜1996. Perl/Tk 日本語化 (学生時代)
1998.
YAPC::Asia 2000.
他の仕事1997-2000
MMO サーバー用 俺々スクリプトエンジンpthreadで NuMA対応, ちょっと Erlang っぽいやつ
2001-2002
組込Linux 移植, ドライバー受託開発
Perl Conference Japan speaker
YATT -- Yet Another Template Toolkit,Designed for WebDesigners
話の概要
1. hkobaの言う仕事の手離れとは?
2. テンプレートエンジン yatt の紹介3. なぜ、この仕様にしたか(=昔話. 開発動機)
4. FAQ (時間が余れば)
(hkobaの言う)
1. 仕事の手離れとは?仮にプログラマーを2層に分けるとして:
インフラ層ビジネス層(プロダクト・受注案件層)
(どちらが偉いとかじゃないです。念の為)
インフラ層PGにとっての「手離れ」
リリース後に案件毎の事情で追加作業が発生しない, or
減っていくこと
(お断り)ウチの 今の精一杯の
現実解の話です
皆さんにとって
良い解か… ⇒?
Ex. 誰が template を 書くか で
最適解は変化
誰がテンプレートを書くか
自分が(=PGが)書くデザイナーさんに書いてもらうお客様が書く
ウチはお客様
(の雇ったスタッフさん)が
template(=ビジネス層) を書く
hkoba はインフラ層作りとスタッフさんの訓練
2. yatt とはYet Another Template Toolkit
Pure Perl
↓テンプレートの例:
<!yatt:args TITLE CLASS body=code><div class="&yatt:CLASS;"><h2>&yatt:TITLE;</h2>&yatt:body();</div>
(TITLE, CLASS, body が引数。普段は lower case)
注. yatt は 2つ あります!on CPAN
YATT (2006-2010)
YATT::Lite (2010-)
(古い) YATT (2006-2010)
ほぼ全案件(年間500本以上)で稼働実装, APIが 不満無視して!
YATT::Lite (2010-)
改訂版構文互換API を整理実装も大分マシに
こちらを 使って下さい
ご意見お待ちしてます!マジで!github.com/hkoba/yatt_lite
インストールcpanm YATT::Lite
スグに使えるインストーラcurl https://raw.github.com/hkoba/yatt_lite/dev/scripts/skels/min/install.sh | bash
(手でやるのも簡単)
git clone(cpanm --installdeps .)
.psgi をコピーmkdir htmlplackup!
スグに plackup 出来る psgi$ git clone git://github.com/hkoba/yatt_lite.git lib/YATT$ (cd lib/YATT && cpanm --installdeps .)$ cp lib/YATT/samples/minimum.psgi app.psgi$ mkdir html$ echo hello > html/index.yatt$ plackup
samples/minimum.psgi の中身use strict;use FindBin;use lib "$FindBin::Bin/lib";use YATT::Lite::WebMVC0::SiteApp -as_base;{ my $site = __PACKAGE__->new(app_root => $FindBin::Bin, doc_root => "$FindBin::Bin/html"); $site->to_app;}
マニュアルテンプレート(yatt:widget) の構文
-- yatt 構文マニュアル
(未完成ですが><)
開発チュートリアル
- Perl Monger のための yatt ガイド
(全然書けてません><)
yatt_manual
yatt_guides_for_pm
yatt は HTML に似せました(意図的)<タグ> を "widget 呼び出し" に<yatt:部品名 引数... />
<yatt:部品名 引数...> ..</yatt:部品名>
&実体参照; を "変数や関数の埋め込み" に
&yatt:変数名;
&yatt:関数名(引数,...);
<!doctype> を "widgetの宣言" 兼 "multipartの区切り" に
<!yatt:widget FOO 仮引数...> ...FOOの定義本体...
<!yatt:widget BAR 仮引数...> ...BARの定義本体...
一ファイルに複数 widget 定義可能<!yatt:args 仮引数...> ...デフォルト widget の定義...
<!yatt:widget FOO 仮引数...> <h2>FOOの定義本体</h2>
<!yatt:widget BAR 仮引数...> <h2>BARの定義本体<h2>
<yatt:FILE:NAME> で呼べる<yatt:layout:FOO>...</yatt:layout:FOO><yatt:layout:BAR>...</yatt:layout:BAR>
<yatt:DIR:FILE> でデフォルト widget呼び出し
<yatt:mydir:layout>...
<yatt:DIR:FILE:NAME> でサブwidget
<yatt:mydir:layout:FOO>...
DIR も FILE も
widget の コンテナ として抽象化
書いてみるEx. こんな↓定形レイアウトを widget化 するには?
<div class="●"><h2>△△</h2>■■■</div>
穴埋め付きで●△△■■■
<yatt:layout> って呼びたい
layout.yatt という名前で保存<!yatt:args TITLE CLASS body=code><div class="&yatt:CLASS;"><h2>&yatt:TITLE;</h2>&yatt:body();</div>
拡張子を除いたファイル名=widget 名
layout.yatt を呼び出す側 (index.yatt)<yatt:layout TITLE="△△" CLASS="●">■■■</yatt:layout>
⇒ 出力 HTML<div class="●"><h2>△△</h2>■■■</div>
ex. TITLE を Hello world! に変えてみる<yatt:layout TITLE="Hello world!" CLASS="content">It's show time!</yatt:layout>
⇒ 出力 HTML<div class="content"><h2>Hello world!</h2>It's show time!</div>
動的に変換され Perl のコードへpackage MyApp::INST1::EntNS::layout;use strict;use warnings;use 5.010;our @ISA = qw(MyApp::INST1::EntNS);#line 1 "/home/hkoba/db/monthly/201409/tenight/app1/html/layout.yatt"sub render_ { my ($this, $CON) = splice @_, 0, 2; my $TITLE = $_[0]; my $CLASS = $_[1]; my $body = $_[2]; print $CON (q|<div class="|, YATT::Lite::Util::escape($CLASS), q|">|, "\n"); print $CON (q|<h2>|, YATT::Lite::Util::escape($TITLE), q|</h2>|, "\n"); $body && $body->(); print $CON (q|</div>|); print $CON ("\n");}
(yatt genperl html/layout.yatt の出力)
(参考) layout を呼ぶ側の生成コードpackage MyApp::INST1::EntNS::index; use strict; use warnings; use 5.010; our @ISA = qw(MyApp::INST1::EntNS); #line 1 "/home/hkoba/db/monthly/201409/tenight/app1/html/index.yatt"sub render_ { my ($this, $CON) = splice @_, 0, 2; my $body = $_[0]; MyApp::INST1::EntNS::layout->render_($CON, (undef, q|Hello world!|, q|content|, sub { print $CON (q|It's show time!|);})[1, 2, 3]); print $CON ("\n");}
(yatt genperl html/index.yatt の出力)
3. なぜ,この仕様に? (=昔話. 開発動機)
理由
雇い主 が HTML ネィティブ!(だった)
最初の上司(1996)
自分で HTML を書けたWebデザイン自分で勉強してたドメインも自力で取ってた
(後に渡米したり起業したり)
当時から hkoba は
小さな部品 信者単機能な部品の組み合わせ、サイコー!
SSIモドキ の仕組みを作って納品してた<hr><!--#listSink *.html --><hr>
SSI 風の部品が html を作って返す# in listSink.sub:foreach ($sink->glob($pattern)) { $sink->add(<<END);<tr align=left> <td><input type=checkbox name=path value="$_"></td> <th>$dir/$file</th> <td>$url</td> <!-- $name --></tr>END}
綺麗に部品に切り分けた,つもりだったリリース!
「こばち〜ん」
「部品の返す html
変えてくんない?」
…手離れしてない…
隅々まで template にしないと
手が掛かり続ける
「コメントで書くの(SSI) って、ダサいよね」「タグで書けたらいいのに〜(チラッ)」
デスヨネー
次の上司(1997〜2003)
も, HTML を自分で書く人
今度は部品から返す html も
テンプレートで作るようにした
html の中に perl の処理を埋め込んだ</td></tr><!--#begin eval--> my ($cgi, $self) = @_; ... push @survey, qq(<tr><td>$handle</td>...); ...<!--#end--><tr>...
納品した!
「こばち〜ん」
「ここのタグ、ちょっと変えたら
動かなくなった (てへっ)
直してくんない?」
…手離れしてない…
壊したらスグに知らせるべき
さもないと手離れしない
故に!次のエンジンでは
perl -wc 的な静的検査のコマンド
を作ることを決意!
次の上司(2003〜2008)
元 PHPer
デザイナー気質デザインセンスあり
2005年版は大分 HTML っぽいテンプレートに.
<ssri:foreach my=value selected="p11/q12-2a-1" file=q12 except=10> <li>$$value[1]</li></ssri:foreach><td width="300" bgcolor="#ebf0f3" rowspan="10" ><ul style='padding-left: 0px; list-style: none;'><ssri:foreach my=value selected="p11/q12-2a-1" file=q12 except=10> <li>$$value[1]</li></ssri:foreach>
「小林さ〜ん」「ここをまとめて、部品にできませんかね〜?」
部品 化 (抽象化)の機能が無いと
デザイナーは満足しない(繰り返しが ダルい のは、デザイナーも同じ)
=> 後付けした
2005 版テンプレートでの、部品定義の例<!--#perl simple my $args = shift; ...--><ssri:foreach my='label' list='@list'><tr><?perl my $item = _label($PAGE, $CGI, {%$args, 'row', $label});?><td>$name--$$label[0]</td><td>$item</td>...
部品名: simple
「PHP と比べても、別に綺麗じゃないっすよね?」「もっと簡単に、 HTML ぽく書けないっすかね〜」
「タグで書けると、いいんすけどね〜」
ぐぬぬ…
そして何より
部品づくりが
難しすぎた!
難しいから、部品にしない代わりに abbrev/snippet (emacs の 短縮辞書 ) に頼る
Write Only! (後で修正が来ると死ぬ)
属人化!
静的検査のコマンドも
滅多に 呼んでくれない
相変わらず、アクセスしてエラーに気づく(引数が hash表渡しなので、引数間違いを静的に検出出来ない問題も)
エラー見ても壊れまくった後だから
「どこ直せば良いのか分からないんすよね〜」
壊したらスグに 知らせないと
手に負えなくなる!
HTMLネィティブが
雇い主!
タグで部品を呼ばせろ!
俺のタグを作らせろ!
やっと出来たのが、今の yatt<!ssri:widget simple type=! name=! value_col="value/0" base=[delegate:ssri:common:table] label=[delegate:ssri:common:label]><ssri:base> <ssri:my id="&ssri:name;--&ssri:row[:value_col];"/> <td><input type="&ssri:type;" name="&ssri:name;" value="&ssri:row[:value_col];" <td><ssri:label row id class=label_class style=label_style/></td></ssri:base>
HTML に似せた利点
(利点.1) HTML 用の構文着色を流用可
github (highlight.js)
emacs, vim
bracket.io?, atom?, sublime?
(利点.2) 採用、教育が比較的楽
HTML の分かる人なら容易(採用で不利な)中小企業には重要
デザイナや非プログラマで
大丈夫なの?
手続き的側面は、隠せる宣言的な widget として教えられるコピペして組み合わせるだけ
(間違いは 保存時に検出 するので)
yatt は typo 検査を頑張ります<yatt:layoout TITLEE="Hello world!" CLASS="content">It's show time!</yatt:layoutt>
<yatt:layoout>TITLEE=..</yatt:layoutt>
保存時に lintエラー箇所に
強制ジャンプ(emacs)
壊れたらスグに気づくように
でないと、すぐに諦めてしまう
他にも
emacsclient + tcltk で IDE ぽくしたり
参考: さんの事業
医療従事者向け、アンケート質問内容が専門的40ページ程
規模
年間500本以上の案件フルカスタム(基本、 全部違う )
7人の、アンケート・プログラマーhkoba がトレーナー
スケジュールの感覚
開発: 一週間運用: 一週間そして捨てる
一週間で捨てるコードに
テストを書く?
ペイしない静的検査を頑張るしか
4. FAQ何で yatt: ?
ログイン処理とかDBアクセスは?何で doc_root 直下に template 置くの?何でコード生成?<yatt:foo>...</yatt:foo> みたく囲む意味は?引数への入れ子は?Mojolicious/Amon2/Dancer... から使うには?1ファイルアプリを作りたいときは?今さら Perl?
今さらサーバー側テンプレート?
Q. 何で yatt: ? 冗長では?
メタプログラミング(多段テンプレート)への備えparser も楽(namespace => [qw/yatt perl/]) 設定可能チーム固有の部品が一目で (ex. ssri:〜) 区別出来る
Q. ログイン処理とか DB アクセスとか、どすんの?
キャッシュ文脈($CON)の ->prop->{stash} に入れ
Entity 関数で取り出す# in app.psgi: use Otogiri; Entity otogiri => sub { my ($this) = @_; $CON->prop->{stash}->{otogiri} //= do { Otogiri->new(connect_info => ["dbi:SQLite:dbname=$app_root/var/db/site.db"]); }; };
View calls models.<h2>&yatt:otogiri():single(user,{login,:user}){email};</h2><ul><yatt:foreach my=user list="&yatt:otogiri():select(user);"> <li>login=&yatt:user{login};</li></yatt:foreach></ul>
Q. なんで doc_root 直下に
template 置くの?
Direct Mapping(?) 指向WAF の doc_root の下に, 拡張子 *.yatt で置くだけ
ex. html/index.yatt
http://0:5000/index.yatt
http://0:5000/index
http://0:5000/
(PHP みたいに! PHP みたいに!)
理由1. 前段でさばき易くしたい
try_files ... @upstream ... への疑問Not found が WAF を叩くのは、良いことなの?
理由2. マッピングに関わりたくない
マッピングは ビジネスロジック !ビジネスロジックの変化に付き合いたくない!
ファイルの追加で呼び出されるのは勘弁
理由3. url 抽象化は yatt でも出来るし(yatt はルータも内蔵)
<!yatt:args "/:user"> ... &yatt:user; ...
<!yatt:page "/authors/:id"> ... &yatt:id; ...<!yatt:page "/authors/:id/edit"> ...
隠したい部品は?
拡張子を *.ytmpl に(e.g. layout.ytmpl)
又は doc_root 外に継承先(app_base) を加え、そこに逃がす(app_base => "$app_root/ytmpl")テンプレートは Perl のクラスになるので、継承出来る
Q. 何でコード生成?
後で表現力の不足に悩むのが嫌だからグリーンスパンの第11法則
Q. <yatt:foo>...</yatt:foo> みたく
囲む意味は?
ruby の do ... end ブロックみたいなもの
<yatt:mytag> ...ここが (body という名前の) callback, クロージャ引数として mytag に渡される...</yatt:mytag>
mytag の側で呼び出して使う。&yatt:body();
又は<yatt:body/>
(⇒ruby の yield みたいなもの)
Q. 入れ子はどうするの?引数内に html タグや widget 呼び出しを書きたくなったら?
<yatt:foo bar="<em>なんとか</em><i>かんとか</i>"/>
とは書きたくないので、どうするか…
⇒ lisp や ruby のキーワード引数風に頭に : を付けたタグで囲む<:yatt:NAME>〜</:yatt:NAME>
<yatt:foo> <:yatt:bar> <em>なんとか</em><i>かんとか</i> </:yatt:bar></yatt:foo>
又は <:yatt:NAME/>〜<yatt:foo> <:yatt:bar/> <em>なんとか</em><i>かんとか</i></yatt:foo>
Q. Mojolicious/Amon2/Dancer... から使うには?
要望があれば!取り組みます
Q. 1ファイルアプリを作るには?
普通の作りじゃ
静的検査が効かないmy $yatt = YATT::Lite->new(vfs => [data => q{<!yatt:widget foo><h2>foo</h2><!yatt:widget bar><h2>bar</h2>}]);
$yatt->render(foo => {});
(yatt を使う意味が無くなる)
use YATT::Lite::Embed (experimental)
__DATA__ ブロックのテンプレートを静的に検査use YATT::Lite::Embed; # This exports $YATT instance.
sub render { my ($page, $args) = @_; $YATT->render($page, $args);}
__DATA__<!yatt:args user><h2>Hello! &yatt:user;</h2>
<!yatt:page foo user>This is foo, &yatt:user;
See: gist: hkoba / mojoyatttest.pl
Q. 今さら Perl?
yatt 自体は
言語中立(原理的上は)
保存時 typo 検査 を高速にこなせる言語が
他に有れば、考えますuse strict!
use fields!
?nodejs? ?OCaml?
Q. 今さらサーバー側テンプレート?
yatt から JS とかyatt から Web Components とか
やりたいですね〜
Q. ほんとに手離れ良くなった?
hkoba がスタッフさんに
呼ばれる回数は
ちょっと減った
皆さんモリモリ
widget 自作してます部品化の要求には応えられた
何より
離職率が下がった! \(^^)/
でも hkoba は別プロジェクトでビジネス層に巻き込まれて
忙しいままorz...
は ssri.com Perl 書きたい人、募集中 です!
フリーランスの人でも応相談!(アンケート作成スタッフはプログラミング未経験者も歓迎!)
まとめHTML ネィティブへの直接支援、大事「保存時にtypo箇所にジャンプ」は沼を避ける武器
ご意見待ってます!github.com/hkoba/yatt_lite