第二十八回文学フリマ東京
はてなBlogに移行した
移行はしてみたものの、前のエントリから何年も経っているのをみると、果たしてそんな価値はあったのだろうかと疑問が湧く。はてなのリソースを無駄遣いしているような気もする。ま、いいか。
Capistranoと多段ゲートウェイ
Ruby界で有名なデプロイツールCapistranoを試しています。こんな構成。
PC(Win) -> 開発サーバ -> 中継サーバ -> 公開サーバ
ほとんどの作業はPC上でやっているのでデプロイもPCからやりたい。PCと開発サーバはputtyを使って鍵を作り、エージェント転送することで簡単に接続(もデプロイも)できる。問題は公開サーバです。PCから見ると開発サーバ、中継サーバと二段も間に入るのだけど、これをCapistranoではどう記述するのか。開発サーバ、中継サーバ、公開サーバですべてユーザ名が異なるというおまけつき。sshをコマンドラインから使うだけなら~/.ssh/configに設定を書くのとエージェント転送でいけるのですが、Capistranoはそうじゃないみたい。
ここ半月ぐらい悩んでいるのですが今のところいい解決法が見つかっていません。
set :gateway が鍵のような気がするんですけどね…
手作業でトンネルを作っておいて、Capistranoにデプロイさせたほうが早いのかなあ。
娘のために本を作った(そしてルビをふるツールを作った)
ちょっと前に娘を主人公にファンタジー小説を書いた。これを読ませてやりたいと思い、bccks(http://bccks.jp/)で電書化した。bccksのいいところは紙本印刷サービスがあって、一部から作れるところ。印刷してプレゼントすればいいだろう。ところが、ここではたと困った。娘はまだ小学生で、ルビのない小説は読めない。オイラの書いた小説はこんな出だし。
ドーマは待っている。タイラヤマのひらべったい山頂にはドーマのほかには誰もいない。背の低いタイライチョウが密集し、タイラクコの紫色の花が咲いている。地面をちょろちょろと走るのはタイラトカゲだ。ときどき上空で笛のような音が響くのはデイビーボーイが通り過ぎたのだろう。ドーマは尻尾をゆっくり振りながらタイライチョウの陰の中を歩いていた。足音はたてない。もうすぐ来るはずだ。
最初に書いたときは想定読者は大人だったから、ぜんぜん子供向きじゃない。しかし今からルビなんかふっていたらいつ終わるか分からない。そこで自動的にルビがふれないものかといろいろ調べたらYahoo!デベロッパーネットワークに「ルビ振りAPI」というのを発見した(http://developer.yahoo.co.jp/webapi/jlp/furigana/v1/furigana.html)。テキストをPOSTするとルビを含んだXMLが返ってくる。そこでそのXMLを解析して、BCCKSのルビ形式に変換するツールを書いた。ものはHerokuに置いてある(http://furi.herokuapp.com/)。一度にたくさん送ると動作がよく分からなかったので、一行ずつ送るようにしたり、ちょっとだけ工夫をして、BCCKS形式、HTML5形式、単にひらくの選択もできるようにしてみた。このAPIはかなり優秀で、文脈でちゃんと読みが変化する。とはいえ手作業で数箇所直しました。
これで元の小説にふりがなをつけて、BCCKSに登録。表紙絵なんかを追加したのがこれ『フミちゃんドリーム』(http://bccks.jp/bcck/111500/info)です。印刷もきれいだし、娘も喜びました。ページ数を4の倍数にしなかったので、最後の方は白紙になってしまい、「ここはメモ」とごまかしました。
The Little MongoDB Bookを日本語訳してみた
話題のNoSQLのひとつMongoDBのチュートリアル本です。日本語訳しました。オリジナルはこちら(http://openmymind.net/2011/3/28/The-Little-MongoDB-Book/)。Githubでmarkdown形式のものを公開しましす。これをきれいなhtmlとかpdfに変換する方法はよく分からない…
https://github.com/ma2/the-little-mongodb-book
How Batman can Help you Build Apps の翻訳
最近話題?のCoffeeScriptベースのフレームワークBatman.jsのHARRY BRUNDAGE(@harrybrundage)氏による紹介記事「How Batman can Help you Build Apps」を翻訳しました。
元エントリはこれです。
Batman.jsはShopifyによる新しいCoffeeScriptのフレームワークで、開発に長い時間のかかったこいつを紹介できるので本当に興奮している。Batman.jsのGitHubはここだ。
Batmanは大きな影響力を持つ素晴らしいフレームワークに満ちた世界に現れた。Sproutcore 2.0やBackbone.jsといった驚くべきプロジェクトの成果がある中で、開発者はどれをいつ使うかどうやって学べばいいのだろう? 新しくてかっこよいツールで遊んでいる時間は限られているので、ここではBatmanと他のフレームワークとの違い、および他のフレームワークのかわりにBatmanを使う理由を簡単に解説したい。
Batmanはアプリ開発を簡単にする
Batmanはシングルページのアプリを開発するためのフレームワークだ。Progressive Enhancement、DOM、AJAXなどの単一目的のライブラリではない。ブラウザ間互換、データ転送、バリデーション、カスタムイベントといった開発の退屈な部分を全て実装することで、最高のシングルページアプリを構築できるようゼロから開発されたものだ。コードを生成し実行する開発用の使いやすいヘルパー、コードを整理して必要に応じて呼び出すためのおすすめアプリ構造、フルMVCスタック、その他たくさんのツールを18キロバイト(gzip済み)のサイズで提供している。Batmanは基本的なものだけ提供するわけでも、ありとあらゆるものを提供するわけではなく、自分のアプリに必要なコードをかける柔軟なAPIを提供している。
超一級のランタイム
Batmanの心臓部はランタイム層で、オブジェクトのデータの操作とオブジェクトが発生させるイベントへの登録に使われている。BatmanのオブジェクトはSproutCoreやBackboneのものと似ていて、Barmanオブジェクトのプロパティに対するアクセスと代入は、素のJavaScriptのドット記法ではなくsomeObject.getとsomeObject.setを使わなければならない。これを守ることで、以下のような利点がある。
- 「深い」プロパティが単純型でも計算型でも透過的にアクセスできる
- プロパティチェーン中のオブジェクトの計算型のプロパティを継承できる
- 「深い」パスの中の他のオブジェクトのchangeやreadyといったイベントに登録できる
- なにより重要なのはプロパティ間の依存関係を追跡できるので、連鎖したオブザーバが発動され、計算結果を最新であることを保証しつつキャッシュできることだ
この機能はすべてのBatmanオブジェクト利用でき、しかも素のJavaScriptとしても扱うことができる。ランタイムでできることを少し試してみよう。オブジェクトのプロパティはBatman.Object::observeを使って監視できる。
crimeReport = new Batman.Object crimeReport.observe 'address', (newValue) -> if DangerTracker.isDangerous(newValue) crimeReport.get('currentTeam').warnOfDanger()
似た機能はBackboneやSproutCoreにもあるが、Batmanに新たに取り入れたものが「深い」キーパスだ。Batmanではドットでつなげることでオブジェクトの連鎖を追跡できる。
batWatch = Batman currentCrimeReport: Batman address: Batman number: "123" street: "Easy St" city: "Gotham" batWatch.get 'currentCrimeReport.address.number' #=> "123" batWatch.set 'currentCrimeReport.address.number', "461A" batWatch.get 'currentCrimeReport.address.number' #=> "461A"
オブザーバの指定時にも使える。
batWatch.observe 'currentCrimeReport.address.street', (newStreet, oldStreet) -> if DistanceCalculator.travelTime(newStreet, oldStreet) > 100000 BatMobile.bringTo(batWatch.get('currentLocation'))
一番クレイジーなのはこれらのオブザーバはそのキーパスがなんであれ(たとえキーパスの途中が変化しても)、その値で発動することだ。
crimeReportA = Batman address: Batman number: "123" street: "Easy St" city: "Gotham" crimeReportB = Batman address: Batman number: "72" street: "Jolly Ln" city: "Gotham" batWatch = new Batman.Object({currentCrimeReport: crimeReportA}) batWatch.get('currentCrimeReport.address.street') #=> "East St" batWatch.observe 'currentCrimeReport.address.street', (newStreet) -> MuggingWatcher.checkStreet(newStreet) batWatch.set('currentCrimeReport', crimeReportB) # 上記の"MuggingWatcher"のコールバックが「Jolly Ln」で呼ばれる
何が起きたかお分かりだろうか。キーパスの途中部が変化してもオブザーバは新しい「深い」値で発動する。この機能は任意の長さのキーパスでも、undefinedを含んだキーパスでも動作する。
もうひとつのランタイムのいいところは、すべてのアクセスがgetとsetを通じて行われるので、計算が必要なプロパティ間の依存関係を追跡できることだ。Batmanではこうした関数をアクセサと呼ぶ。アクセサは、CoffeeScriptの実行可能なクラスを使って簡単に定義できる。
class BatWatch extends Batman.Object # BatWatchクラスのインスタンスの「currentDestination」キーのアクセサを定義する @accessor 'currentDestination', -> address = @get 'currentCrimeReport.address' return "#{address.get('number')} #{address.get('street')}, #{address.get('city')}" crimeReport = Batman address: Batman number: "123" street "Easy St" city: "Gotham" watch = new BatWatch(currentCrimeReport: crimeReport) watch.get('currentDestination') #=> "123 Easy St, Gotham"
重要なのは、ここで計算型のプロパティに登録したオブザーバは依存関係が更新されるたびに発動することだ。
watch.observe 'currentDestination', (newDestination) -> console.log newDestination crimeReport.set('address.number', "124") # "124 Easy St, Gotham"がコンソールに出力される
デフォルトのアクセサを定義しておけば、そのキーパスでのアクセサが定義されていなくても、ランタイムがフォールバックしてくれる。
jokerSimulator = new Batman.Object jokerSimulator.accessor (key) -> "#{key.toUpperCase()}, HA HA HA!" jokerSimulator.get("why so serious") #=> "WHY SO SERIOUS, HA HA HA!"
この機能はオブジェクトに基本のインターフェースを与えたいときに有用だが、自明ではない方法でデータと連動することになる。例えばBatman.Hashはイベントを発行しオブジェクトをキーとして使えるようにした上で、標準的なJavaScriptのオブジェクトと類似したAPIを提供するためにこの機能を使っている。
何に使えるのか?
上記で解説したBatmanのコアは、データの変更があったとき即座にそれを知ることを可能にする。クライアントサイドのビューなどには最適だ。ビューは、もはや長大な文字列として固められクライアントに送信される静的なHTML群ではない。ビューは、データにしたがって変化する長寿命のデータ表現形式だ。Batmanのビューシステムはプロパティの能力を強化する。
Alfred(BatmanのToDoアプリサンプル)用のビューの簡易版が以下だ。
<h1>Alfred</h1> <ul id="items"> <li data-foreach-todo="Todo.all" data-mixin="animation"> <input type="checkbox" data-bind="todo.isDone" data-event-change="todo.save" /> <label data-bind="todo.body" data-addclass-done="todo.isDone" data-mixin="editable"></label> <a data-event-click="todo.destroy">delete</a> </li> <li><span data-bind="Todo.all.length"></span> <span data-bind="'item' | pluralize Todo.all.length"></span></li> </ul> <form data-formfor-todo="controllers.todos.emptyTodo" data-event-submit="controllers.todos.create"> <input class="new-item" placeholder="add a todo item" data-bind="todo.body" /> </form>
トランスパイラ層をあきらめ(HAMLなし)、テンプレート層もあきらめた(Ecoもjadeもmustacheもなし)。BatmanのビューシステムはHTML5で、ダウンロードしたその場でブラウザがレンダリングする。JavaScript文字列ではなくバリッドなDOMツリーで、Batmanが解析してデータを埋め込む。コンパイルや文字列処理は行わない。素晴らしいことにBatmanはノードの値をランタイムで監視することで「バインド」する。JavaScriptの世界で値が変化すると、バインドされた対応するノードの属性は自動的に更新され、ユーザはその変化を見る。逆もまた真で、テキストフィールドに入力したり、チェックボックスをクリックしたりすると、文字列や真偽値がJavaScriptのバインドされたオブジェクトにセットされる。CocoaあるいはJavaScriptならKnockoutやSproutcoreにあるようにバインドの概念は新しいものではない。
バインドを選択したのは a)手動でデータの変更をチェックしたくない b)データがすこし変わったぐらいでテンプレート全体を再描画したくない からだ。MustacheやjQuery.tmplに類するシステムでは、おどろくほど頻繁にその両方をする羽目になった。たったひとつのノードを更新したくてあるひとつの要素のひとつのキーを変更しただけで、ループ内で全要素を再描画したりそれらのノード追加のペナルティを支払うのは時間の無駄に思える。SproutCoreのSC.TemplateViewやYehuda Katzの作ったHandlebars.jsはこうした労力を減らすにはよくできているが、やはりブラウザの中で全ての文字列演算をやりたくはないので、ビューの全データを厳密にプロパティにバインドする外科的な精密さの方を選択した。
ビューの通常レベルの複雑さのロジックとひきかえに、ロード中画面のいらない高速な描画を最後には手に入れた。Batmanのビューエンジンには、条件分岐、ループ、コンテクスト、簡単な変換などがあるが、コードを書くことはできない。Batmanでは対話用の複雑なコードはBatman.Viewのサブクラスに記述しなければならず、HTMLレンダリングはそれをもっとも得意するものにやらせる。すなわちブラウザに。
もっと知りたい?
Batmanはこの洒落た深いキーパスの機能や奇妙な「テンプレートじゃないHTML」の他にもいろいろなことができる。擬似ページ間リンクのためのルーティングがある(GETのパラメータまたはセグメント対応)。Batman.Model層はデータの処理や送信を行い、RailsやlocalStorageのようなストレージバックエンドですぐに使える。Batman.StateMachineやBatman.EventEmitterはオブジェクトにmixinして使う。他にもたくさんある。Webサイト、GitHubにあるソース、[irc://freenode.net/batmanjs:title=freenodeの#batmanjs]などをチェックすることを強くおすすめする。質問、フィードバック、パッチなどは大歓迎だし、Batmanをどう改善したらいいかの提案はいつでも受け入れる。
最近のOmniAuthでSSLの証明書の指定が効かなくなった件
#404: OpenSSL::SSL::SSLError using Twitter - Issues - intridea/omniauth - GitHub
OmiAuthでSSLを扱うときにこんな指定かできます。
Rails.application.config.middleware.use OmniAuth::Builder do provider :facebook, 'APP_KEY', 'SECRET', {:client_options=>{:ssl=>{:ca_path=>"#{::Rails.root}/config/certs/"}}} end
これが最近のOmiAuthでは効かないのです。いろいろ調べた結果、なぜか:ca_pathが:ca_fileになっていることが判明。つまりディレクトリではなく、ファイルを指定する必要があります。どうもバグっぽいけど詳細は不明です。以下のように直すとうまくいくみたいです。
{:client_options=>{:ssl=>{:ca_file=>"#{::Rails.root}/config/certs/cacerts.pem"}}}