Mais conteúdo relacionado Semelhante a Bpstudy26 (20) Bpstudy261.
Caty Framework
〜Back to Oldies, Back to Basics〜
言い出しっぺ: 檜山正幸
巻き込まれた人:鍬田力
2.
Catyを作り始めた経緯
檜山さんにお任せします。
3.
ちょっくらWebサイトでも
*
少しだけ動的
*
デザインはプロに頼もう
*
どうやってデザイナに頼んだらいいんだ?
*
周りの人に聞いてみた
*
… …
4.
ええええーーっ
*
当方、「小規模、少人数、少ない予算、短期間」なんですけど
*
本格的なフレームワークや方法論はどれもオーバースペック
*
全部ひとりでやるってのは確かにあるけど
*
「N = 1」 と 「N = それなり」の道具と手段はあるんだな
*
でも、「N = 2」「N = 3」あたりで適切なんが見あたらない
5.
んじゃ、作ろうか
*
当初の目的に適合することが第一
*
他の要求は知らん
*
だったら、簡単そうじゃん
*
… …
*
… …
*
それでエライことになった人が一人いたりします
6.
厳密分離の原理
*
これだ!
*
だけど、イマイチなところもあるなー
*
分離の境界線を1本から3本にしてみました
7.
昨日あたりから、いきなり重要キーワード
*
ロール
*
スーパーバイザー
8.
デザイナって言葉が曖昧すぎたわけだが
*
小規模、少ない予算なら、プロジェクトチームっても、2人くらいでしょう
*
狭義のデザイナ、狭義のプログラマのどちらかがスーパーバイザを兼任できそう
*
僕(檜山)はやりたくないので、デザイナさん、そっちもお願い
*
単機能のプログラム(コマンド)なら書いてもいいし、アルバイト君にも依頼可能
11.
1. できてない
*
動くっちゃ動く
*
来週くらいにリリース2
*
やることが色々増えちゃってねー
*
… …
*
… …
*
それでエライことになってる人が一人いたりします
17.
檜山さんの野望
厳密分離の実現のために
*
プログラマに頼らずサイト全体を構築可能
*
三つのロールモデル間の分業
o
デザイナー
o
プログラマー
o
スーパーバイザー
*
特にプログラマーとの間で分業
※スーパーバイザーはサイト全体の構造・仕様についての責務を負う人とする
18.
昔は良かった
*
Webサイト=HTMLだけで構成
*
CGIもあったけど、何にせよファイルはモロ見え
*
ファイラーでサイトの構造が見える
~/public_html/index.html
~/public_html/news/9901.html
~/cgi-bin/forum.cgi
:
:
http://example.com/
http://example.com/news/...
http://example.com/forum.cgi
:
:
19.
今は便利になったけど(1)
*
URLマッピングとフィルター・トリガーの嵐
*
FWを知らないとサイトの構造が不明
*
そして当然それらはFW依存
/app/conf.xml
/handlers/
/filters/
/otherbullshits/
-----------------------------------
<web>
<action path="/*.act"
class="Dispatcher"/>
</web>
http://example.com/
http://example.com/news.act
http://example.com/forum.act
:
:
?
20.
今は便利になったけど(2)
*
いくら何でもパワフル過ぎるテンプレート
*
ちょっとした事を試すにもプログラマ頼み
*
そのくせデフォルトでXSSに脆弱だったり
<html>
<title><%= page.getTitle()%><title>
<ul>
<% for item in page.listItems() %>
<li><%= item.someMethod() %></li>
<% end %>
</ul>
</html>
21.
でもそこまでする必要ある?
*
近所のラーメン屋のサイト作るとき
*
美容院のサイト作るとき
*
そこらの片手間企業サイトを作るとき
オーバースペック
22.
そのやり方でいいの?
*
HTMLだけ書いてもらいそこにテンプレートのプレースホルダー埋め込み
*
デザイナーの書いたテンプレートに後から ロジック埋め込み
*
その辺ゴチャゴチャで脆弱性上等の某ソフト
まともに分業できてないでしょ
23.
基本を忘れてない?
1
ページのデザインに責任を負っているのは誰?
2
動的サイトに必要なプログラムを作るのは誰?
3
それらサイトの構成を把握するべきなのは誰?
24.
3つのロール
*
デザイナー
*
プログラマー
*
スーパーバイザー
Caty の想定するロール
25.
ロール間の分業
*
3つのロール間の責務を可能な限り分離
*
複数のロールを兼務することは当然ある
o
が、ある瞬間のロールは一つ
o
やってる事が混線しないような仕組み
26.
そのために何が要る?
*
ロジックがテンプレートに入り込まない仕組み
*
プログラマに頼らずに
o
テンプレートを実行する仕組み
o
データをマッシュアップする仕組み
o
サイトの構成を把握できる仕組み
*
安全性・整合性を保証する仕組み
28.
古き良き日々よ再び
*
URLが存在する=ファイルが存在する
*
ベタHTMLのサイトが普通に作れる
*
そこに動的ページを混ぜ込める
*
ベターなHTML+CGIのモデル
29.
ということを考えていたら
*
予想の斜め上のフレームワークが
*
作ってる方は死にかけてる
*
とりあえずはテンプレートから説明します
30.
テンプレートのダメな例
*
あるショップのディスカウントセール
*
お店のサイトで一律値引き表示をさせたい
*
テンプレートで計算させちゃった♪
<ul>
<% for item in page.itemList() %>
<li><%= item.name() %>: <%= item.price() * 0.7 %>円 </li>
<% end %>
</ul>
31.
なぜダメか
*
誰がそのロジックを書くのか
*
仮にデザイナーだとすると
o
ロジックを書くのは本業じゃない
*
仮にプログラマーだとすると
o
テンプレートをいじるのは本業じゃない
*
スーパーバイザー? 指示出す側だろ
*
分業にならない
*
そもそも保守不能なカオス
32.
ってかどうしてこうなった
*
そりゃあ、出来るからに決まってるだろ
*
最初からできなければ良い
低機能なテンプレートエンジンが理想
33.
Catyのテンプレートの機能
<html>
<title>{$title|toupper}</title>
{if message.exists}
<p>{$message.body}</p>
{/if}
<ul>
{foreach from=$list item=i}
<li>{$i}</li>
{/foreach}
</ul>
{include file="footer.html"}
</html>
34.
こんだけあれば十分じゃん
*
変数参照
*
ループ
*
真偽値判定
*
他のテンプレートのインクルード
*
フィルター/モディファイアの適用
35.
こんなのは要らない
*
ホスト言語の呼び出し
*
算術演算
*
マクロ定義
あると厳密分離にならなくなるものばかり
36.
実は言い出しっぺは別にいる
*
Terrence Parrの論文 - Enforcing strict model-view separation in template engines
o
http://www.cs.usfca.edu/~parrt/papers/mvc.templates.pdf
*
良いテンプレート=低機能なテンプレート
*
この厳密分離の方針で実績がある
37.
厳密分離の次にすべき事
*
テンプレートだけでは動作しない
*
値を入れて確認できるようにしたい
*
わざわざプログラマに頼む必要なしにしたい
*
ロジックは書けなくていい
*
データを流し込む仕組みは必要
38.
よし、それじゃあJSONだ
<?caty-script
{
"title": "index page",
"message": {
"exists": true,
"body": "..." }
}
?>
<html>
<title>{$title}</title>
:
39.
動的データを埋め込みたい
*
固定値だけ入れられてもそこまで嬉しくはない
*
やはり動的なデータの取得が必要
*
できればデータをマッシュアップしたい
限られた機能のスクリプトを作ろう
40.
Catyスクリプト
<?caty-script
{
"title": "index page",
"message": load-message,
"list": fetch-db | filter
}
?>
<html>
<title>{$title}</title>
:
41.
一体これは何?
*
ベターJSONとしてのスクリプト
*
JSONのスカラーの書けるところにコマンドが書ける
*
コマンド呼び出しはUnixのそれ
*
コマンドの実態はホスト言語で書かれてる
*
ロジックはコマンドに書いてください
*
Catyスクリプトはコマンドを呼び出すのみ
42.
Catyスクリプトにないもの
*
関数定義
*
ループ
*
算術演算など各種演算
*
etc
*
あるとホスト言語との境界が曖昧に
*
つまり厳密分離にならなくなる
43.
デザイナーの立場からは
*
HTMLだけでページが作れる
*
ベターHTMLとしてのテンプレート
o
共通フッタのインクルードだけ書くとか
*
テンプレートの表示確認用にJSONを書く
*
ベターJSONとしてのCatyスクリプト
段階的な学習
44.
ちょっと視点を変えてみる
*
スクリプトはテンプレートにしか書けないの?
*
サイトの構成はどうなってるの?
これらはスーパーバイザーの立場
45.
テンプレートに値を渡す方法
*
一つはさっきのやり方(インラインスクリプト)
*
別のスクリプト:アウトオブラインスクリプト
*
CGI
*
サイトの設定ファイル
46.
アウトオブラインスクリプト
/index.html
/index.html.caty
/news.html
/news.html.caty
:
:
*
ファイル毎に一つのスクリプトを関連付けられる
*
拡張子込みのファイル名+.caty
*
中身はCatyスクリプト
47.
どんな時に使うの?
*
ページの構成要素は決まってる
*
デザインはデザイナーに任せる
*
アウトオブラインスクリプトを書く
*
対応する空のHTMLファイルを渡す
*
コマンドが必要ならプログラマに発注
48.
例えばこんなのはどう?
*
サイトのトップページを作る
o
新着記事一覧
o
雑多なお知らせ
必要な要素はわかってるのでスクリプトに書ける
49.
書いてみるとこんな感じ
{
"news": fetch-news --sort-by-date,
"message": load-message
}
*
残りの作業はプログラマーとデザイナーがやる
*
fetch-newsなどをプログラマーが実装
*
HTMLテンプレートをデザイナーが作成
50.
CGI
*
CatyのCGI=Catyスクリプト
*
つまりロジックは書けません
*
サイトの表面からはロジックを分離
51.
CGIの例
*
リリース2に含まれるWikiのCGI
*
どれもこれもワンライナー
index.cgi:
wikititles / | print index.html
edit.cgi:
editwiki / | print edit.html
post.cgi:
postwiki /
52.
サイトの設定ファイル
*
拡張子毎のデフォルトコマンドを設定できる
*
が、デフォルト設定でサイトが十分構築できる
*
よく使いそうなパターンはデモに収録
*
URLマッピングの類はない
"filetypes": {
".wiki": {
"is_text": true,
"assoc": "viewwiki %1 | print page.html"
}
}
53.
サイトの構成は単純
*
URLがある=公開ディレクトリにファイルがある
o
インクルード専用ディレクトリなどはある
*
公開ディレクトリやテンプレート置き場を指定
*
覚えることはあまりないです
/mysite
_manifest.json <- 設定ファイル
/pub <-公開ディレクトリ
/index.html
/forum.cgi
/templates <- インクルードやprint経由でのみアクセス
/res <- コマンドの使うデータ置き場
54.
一旦まとめ
*
古典的なWebサイトの延長
*
個々の要素は限られた機能を持つ
*
単純化による学習容易性
*
機能が限られていれば自然と分業できる
55.
Catyの真の姿
*
Catyのコア=言語処理系
*
厳密分離の核心がCatyスクリプトにある
*
その処理系こそがCatyの本質
WebフレームワークとしてのCaty
=
CatyスクリプトのWebフロントエンド
56.
Catyはこんな構成
Catyスクリプト処理系
シェルフロントエンド
Webフロントエンド
キーボード入力
HTTPアクセス
57.
コンソールとWebが対等
caty> print --resolve /index.html
=
GET /index.html HTTP/1.1
caty> {"path":"Example"}|/edit.cgi
=
GET /edit.cgi?path=Example
caty> {"path":"Example", "content": "hogehoge"} | /post.cgi
=
POST /post.cgi HTTP/1.1
path=Example&content=hogehoge
58.
Catyスクリプトの仕様書
pipeline -> term ('|' term)*
term -> scalar | command | object | list |
functor | tag | when | '(' term ')'
scalar -> string | integer | number | boolean
list -> '[' pipeline (',' pipeline)* ']'
object -> '{' item (',' item)* '}'
item -> string ':' pipeline
command -> symbol option* (symbol|scalar)*
symbol -> ('a'..'z' | 'A'..'Z') ('a'..'z' | 'A'..'Z' | '0'..'9' | '_')*
tag -> '@' string | symbol
option -> "--" symbol symbol | scalar
functor -> symbol '{' pipeline '}'
when -> "when" '{' case (',' case)* '}'
case -> symbol | string "=>" | "==>" pipleline
59.
改めてCatyスクリプトを説明(1)
*
あくまでもテンプレートとホスト言語の仲立ち
*
Unixのパイプの発想
*
ネイティブデータはストリームではなくJSON
*
スーパーバイザーとデザイナーが対象に入る
*
プログラマが書いても悪くはないよ?
60.
改めてCatyスクリプトを説明(2)
*
JSON構文はそのまま使える
"string"
[1, 2, 3]
{"name":"鍬田力", "job":"自称料理研究家"}
*
JSON構文にコマンドを入れ込める
print /index.html
["a", "b", "c"] | concat
{
"blogTitles": blog:recentBlogEntries ,
"wikiTitles": wiki:recentUpdatedWiki
}
61.
改めてCatyスクリプトを説明(3)
*
タグディスパッチによる分岐
loggedin --as kuwata | when {
OK => print -e /secret.html,
NG => "/login.html" | redirect
}
*
Catyでは任意のデータにタグが付けられる
@person {
"name":"檜山正幸",
"job":"キマイラ飼育記の中の人"
}
*
object型にpersonというメタ情報が付く
*
メタ情報が付いた型は元の型とは違う
62.
改めてCatyスクリプトを説明(4)
*
繰り返しの代わりのeach(map関数)
["foo", "bar", "buz"] | each {toupper}
通常使うCatyスクリプトの要素はこれで全部
63.
余談:普通は使わないコマンド
*
テストの実行時のみ有効なコマンド: eval
["print /index.html", {...}] | eval
=
{...} | print /index.html
*
通常のコンソールやWebでは使えません
*
その理由は……
64.
こんな事が書けてしまう
test:list-testfiles / | each { {
"testcase": pass,
"testresult": (
["readFile --readfrom testfiles ", pass] | concat | [pass, " "] | eval |
[pass, ""] | eval | each { {
"command": getpv command,
"results":
[{"command": getpv command, "data": getpv data}, findpv setup, findpv teardown] |
[
nth 1 | when {EXISTS => [pass, " "] | eval, NO => pass},
nth 0 |
[getpv command, getpv data | unzip | nth 0, getpv data | unzip | nth 1] |
[[nth 0, nth 1 | length] | repeat, nth 1, nth 2] |
[[nth 0, nth 1] | zip | each {eval}, nth 2] | zip |
each {
eq | when {
"Same" => "[OK]",
"Diff" => ["[NG] ", toString] | concat,
}
} | enumerate,
nth 2] | [nth 2 | when {EXISTS => [pass, " "] | eval, NO => pass}, nth 1] | nth 1
}
}
)
} | expand testresult.txt
} | concat | chop
65.
さらなる悪夢
"[pass, pass] | eval" | [pass, pass] | eval
*
無限に再帰します
*
ってか Y コンビネータです
*
即ちチューリング完全
66.
チューリング完全性との戦い
*
あくまでもCatyスクリプトは低機能が第一義
*
あまりパワフルだと弊害が多い
o
静的検証への悪影響
o
安全性への悪影響
o
そもそも使うのも学のも難しくなる
*
ただし、evalが必要なケースがある
o
テスティングフレームワーク
67.
Catyスクリプトの型
*
パイプは確かに気軽に使える
*
でも繋げるコマンドを間違えることもある
*
可能な限り実行前に検出したい
JSONスキーマによる型付け
68.
JSONスキーマの基本型
*
スカラー型
string, binary, integer, number, boolean, null
*
配列・リスト・タプル型
array [integer, string*], list[string], tuple [string, integer]
*
オブジェクト型
object {"name": string, "job": string, "age": integer}
*
特殊型
any, never, void
*
型変数
_S, _T, _U...
69.
JSONスキーマの型構築子
*
|:ユニオン型
o
string | null で「stringあるいはnull」
*
&:インターセクション型
o
A & B で「A, B両方の要素を持つ」
o
object にしか使えません
*
?:オプショナル型
o
object {"name":string, "email":string?}
o
"email"プロパティがないかも
*
@tag:タグ
o
@person {"name":string, "age":integer}
70.
JSONスキーマの例
type httpResponse = object {
"header": {
*: string
},
"body": string | binary,
"status": string, };
71.
スキーマによる型付け
*
あらゆるコマンドは入出力の型を持つ
*
コマンド同士の結合時には型チェックがされる
o
今はちょっと適当
o
そのうちきちんと実装します
*
型チェックの基準
o
A | B の結合が成立する条件
o
Aの出力型がBの入力型に包含されている
*
型変数を導入したので多相コマンドが書ける
*
型変数は型推論されて型チェックされる
72.
今日は説明しませんでしたが
*
コマンドの型を指定するための機構もあります
*
そちらはDIの宣言にもなってます
*
宣言しないとファイル・DB・セッションなどへのアクセスがほぼ不可能です
*
宣言時に読み込み・書き込みの指定もします
*
つまり、型宣言をみれば副作用の有無がわかります
73.
まとめ
*
フロント部分は素朴な構造
o
学習容易性が大事
o
限られた機能で自然な分業
*
バックはガチガチに定式化
o
宣言的な仕様の記述
o
強い型システム