Polymer.dart + start 製のウェブアプリ作ってみた


Dartsummit中々アツアツでしたね（ちょろっとしか見てないけど）。
DartVMの夢を捨てた代わりに現実世界で頑張る感じに期待が持てます。
Fletchとかどのレベルで使い物になるのか超楽しみっす。

さてさて、それとは特に関係なく、
せっかくのゴールデンウィークなんでDartの勉強のためにWebアプリ作ってました。

**SlackReminder**
http://sr.takyam.com/

**ソースコード**
https://github.com/takyam-git/slack_reminder


何か適当に登録するとその日時にSlack経由で通知してくれるだけのWebアプリ。
ログイン周りはSlackのOAuthをそのまま使った。
OAuthでユーザー認証するんじゃねーよ！って怒られそうな気もするけど気にしない。

さて、フロントもバックも全部Dartで書いてみたんですが、結論としては辛い。

以下、感想というか愚痴というか何というか、ただのポエムです。

## フロントエンドで使ったPolymer

まず、Polymerが辛かった。
製作時間が約4日半くらいで、その半分以上はPolymerに苦戦してた感じ。

初めてPolymer触ったんだけどクセをつかむまで、、、というか core-elements / paper-elements の使い方や思想を感じられるようになるまで2日近くかかった。
超高機能（になるかもしれない）TwitterBootstrap == Polymer elements な認識。

確かにCustomElementsという閉じた世界で、かなり高機能な事を影響範囲小さく行えるのは素晴らしいし、実際に自分でSPA作ってみてこれは便利だなと思った。

けどそれはそれとして、デフォで提供されてる要素群の使い方が難しい。

答えが分かれば別に「あーね、なるほどね」くらいな感じなんだけど、何故か画面が真っ白に！とか、何でか思った通りに表示されねぇ・・・とかとかいろいろハマりまくった。

### ハマったところ

大体問題は以下の3種類。

* import忘れ
* ドキュメント読んでなかった
* そもそもそんな事はできない事をしようとしてた

最初の頃はimport忘れて「何で出ないんじゃぁぁぁ」ってなってたけど10回くらい繰り返せば流石に体が「まずimportされてるか確認しよう」って覚えてくれた。割と忘れる。

ドキュメントは超不親切で、基本的には「demoのソース読め」状態。
何となく「Polymerの雰囲気」つかめてくると「あぁ、必要最低限のこと書いてあるなぁ」とは思えるんだけど、いきなりあのドキュメントだけだと辛い。

あとは何か期待値があがっちゃって「これできそう！やってみよう！」->「んなことできねーよ」は割とあった。
たぶんやりたい事やるには core-elements/paper-elements を継承したオレが考えた最強の core-elements/paper-elements を作ればいいだけなんだけど、そこまでするのはちょっと・・・って感じ。継承元のソースコード読み解くのとか辛い。


### Polymerの感想

* データバインディング便利だけどちゃんとドキュメント読んでから使え
* CSSセレクタマジ糞
* かゆいところに手がとどかない
* 意外とクロスブラウザで動いてる（たぶん）
* isomorphic的なやつ

#### データバインディングの話

データバインディングはかなり便利で、モデルの管理を適当にやってればあとは表示側は本当に適当に変わってくれちゃうので便利だよな、とは思う。
バインディングの指定も細かい事指定せずに `<p>{{model.hoge}}</p>` みたいに指定しておけばそのモデルのhogeの値が変わったら勝手にその`<p>`タグの部分も変わってくれる感じ。

それはもうそれはもう便利なんだけど、バインディング指定してると`<input _value="{{model.hoge}}">` みたいにしてるINPUTで、カチャカチャ入力すると入力した内容に`model.hoge`の値も変わったりして、それはもう何ていうか超双方向バインディングなわけなんですが、その辺の機能を全く理解せずに作り始めたので、編集フォームとか作ったら保存するまえにモデルのプロパティ書き換わってうわぁぁあみたいな。

#### CSSセレクタの話

CSSセレクタが超難しくて、未だによく分からんところが多すぎる。
（普通のDOMに対するセレクタは普通に書けるレベルの人間ですよ）

`:host(#hoge)` とか `::shadow` とか　`hoge-elements /deep/ .fuga`とか、shadow-dom用のCSSセレクタがいくつかあるんですが、コレが何か期待どおりに動かない。マジで理由分からんけどスタイルが当たらなかったりする。「とりま /deep/ つけときゃ楽勝っしょｗｗｗ」とか思ってた時代が俺にもありました。/deep/とか:hostとかそんなチャチなもんじゃスタイルが反映されない事がザラにある。

最終的には `style="background-color: white;"` みたいにstyle属性つけるのが最高に間違いない事が分かったので、もうなんかコレでいいかなと思ってる。

#### かゆいところに手がとどかない

GoogleっぽいUIがお手軽に作れそうな雰囲気の `core-elements` と `paper-elements` ですが、色指定とかサイズ指定とかその辺はCSSで各自指定してねっって感じのがほとんどなので、Bootstrap気分でデザインの部分を全く気にせずにすむかというとそうでは無いので注意。

あとは前述した通り、「アレができそう！」でも実際は出来ない事が多すぎるので、夢から醒めた状態でコーディングし始めないと辛いと思うですよ。
基本的にはDEMOページでできてる事以上の事はできないつもりでいたほうが精神衛生上よろしい。

#### クロスブラウザ

何も意識せずに開発中はChromeしか見てなかったけど、`<input type="datetime-local">`みたいなPolymerと関係ないところ以外はどのブラウザでも普通に動いてる（ように見える）。
細かいところでもしかしたら挙動に違いとか出てくるのかもしれんけど、開発中に細かく全部のブラウザ気にしながら〜みたいな事はしなくてよさそう。Good。
なお、IEでは見てないので分からん。

#### isomorphic的なやつ

Polymerで作るとたぶん原理的に「ページ読み込み後のレンダリング待ち時間」が発生する。たぶん。
サーバーサイドでPolymerの必要な部分だけ展開するとかできるのかなぁ・・・
それにしても初期化コストでかそうだけど。

解決方法あるのかなぁ・・・

### Polymerの今後について

よう知らんし、Reactによって駆逐されるのかもしらんけど、割と使えるやつだとは思いました。
そもそもバージョン0.5だしChrome以外だとshadow-domの実装がイマイチなんで、今後はどうなっていくんだろうなーわくわく。でいいかと。

## サーバーサイドで使ったstart

[前回](http://qiita.com/takyam/items/271379b78ea733adc26f) は [http_server](https://pub.dartlang.org/packages/http_server) 使ってサーバーサイド書いてみたんですが、今回は [start](https://pub.dartlang.org/packages/start) 使ってみました。

DartのサーバーサイドフレームワークってExperssだったりRailsだったりの決定版が無い状況なので、とりあえず今回はREST形式のAPIだけ提供できりゃほぼほぼ良かったんで、`Sinatra inspired`とかうたってるstartにしてみた感じです。

これが大きな間違いだった。

軽めなのはいいことだが、スーパーフェザー級だと辛い事が分かった。


### 困った事

* セッションがオンメモリ一択
* テンプレートレンダリングとか無いです
* 共通処理とか出来ると思ったら大間違いです
* CSRFなにそれ美味しいの
* JSONでレスポンスするといったがそれは嘘だ

#### セッション

Dart標準の`dart:io`にある`HttpServer`さんは中に`SessionManager`を持ってて、コレがいい感じにセッションを管理してる系なんだけど、これがもうなんていうかオンメモリ一択。
Dart的には「これabstractだしー、適当に実装しちゃってよ」的なテンションなんだろうけど、startは愚直にコレを普通にそのまま使ってるわけ。

まぁmemcacheとかRedisとか用意しなくてもいいならよくね？とか思ったりしたら大間違いで、オンメモリなセッションしか選択できないとう事は、サーバー再起動（コード変えたら再起動するよね）する度に、ログインし直さないといけないという何とも辛い現実にぶち当たった。

セッションの実体はabstractな`HttpSession`クラスだからココ差し換えたり自前実装に出来たりするならまだよかったんだけど、そういったセッションハンドラ的な部分の処理の差し換えとかstartはたぶん全く考えてなくて、「いいよね！オンメモリでいいよね！」くらいのノリ。

というかたぶん「何でstartにセッションが必要なの？馬鹿なの死ぬの？」くらいのテンション。

スーパーライトな公開APIオンリーなアプリケーションを作ってね！って感じがひしひしと実装から感じられる。

「いやいやまだ1.0にもなってないし」とか言われると「せやな」としか言えないけど。

正直この1点だけでもstartを選んだ事を公開している。

#### テンプレート

`render(viewName, [Map params])`メソッドがあるとGithubのREADMEには書いてある。

残念だったな！それは嘘だ！

どこにもそんな実装は無い。辛い。

`request.response.send(await templateFile.readAsString())` みたいな悲しいコードを書かなきゃいけない。

まぁテンプレートエンジンくらい別のやつ使えよって話なんだろうけどね！

Polymer使ってると最終的に生成される `index.html` がそれはもうゴリゴリといろんな処理がかかれた `ビルドされたHTML` になっちゃうからテンプレート差し込む余裕なんて無いんですけどね！

#### 共通処理

ExpressとかでいうMiddleware的なやつが無い。
`server.addMiddleware(new AuthorizationMiddleware())` みたいな事がしたい！

そんなものは当然ないので

```
server.get('/hoge').listen((req){
    if(await this._isLoggedin(req)){
        req.response.redirect('/login');
        return;
    }
    //....
});
server.get('/fuga').listen((req){
    if(await this._isLoggedin(req)){
        req.response.redirect('/login');
        return;
    }
    //....
});
```

みたいなねー！そんなのをねー！書かなきゃねー！
と思ったけどwhereなりpipeなりで頑張れば似たような事はいけるのかな・・・？
でもそんな頑張り方はしたくないでござるの巻。

#### CSRF

当然のようにstartにそんな機能は無い。
さらにpubにもそんなライブラリは無い。
結果的に自前実装せざるを得ない。
辛かった・・・。

今回は適当にCookieベースのワンタイムトークンを実装したけどセキュリティ的に問題が有るのか無いのか良う分からん。たぶん大丈夫だと信じてやまない。ちゃんとするなら何か他の実装を参考にしたいところ。

#### Content-Typeの罠

`request.response.json({"hoge": something})` みたいにするといちいち`JSON.encode()`かけなくても勝手にJSONでレスポンスしてくれるメソッドがある。

が、Content-Typeは変わらない。たしか text/plain　。

なんでだよおおおおお！結局、 


```
request.response
  ..send(200)
  ..header('Content-Type', 'application/json; charset=utf-8')
  ..json(responseData);
```

みたいな事しなきゃいけない。あんだかなぁ・・・



### startの感想

たぶんもう二度と使わない。
PR送って改善しろよ！って言われそうな気もするけど、そんなレベルじゃねぇ。

startはフレームワークではなく、http_serverパッケージのラッパー、くらいのテンションで使うと良いと思う。
http_serverパッケージを生で使うよりはだいぶらくなのは確か。


## まとめ

* Polymerは悪くない
* startは良くない
* エコシステムが育ってない世界のマイクロフレームワークはフレームワーク足り得ない
* こんなの作ってるせいで昼夜逆転したし、連休の半分が消えた。
* 作りなおしたい。でももう疲れた。