最近読んだ本 2014/06

柚木麻子『終点のあの子』

青春時代にあるちょっとしたボタンの掛け違えで人生のものすごく大切な何かを失う出来事が描かれる。 で、今自分には30台独身男子の目の前にある限りの人生しか見えなくなっているのに気がつく。あのころ視界に広がる空間は、見えているものが何なのか、よくわかってはいないけど、とにかくあっけらかんと広かったような。 回収が、ベタだけど美しい。


マイケル・オンダーチェ『名もなき人たちのテーブル』

数週間の船旅の中でいくつもの人生が交わる様子が描かれる。思春期を迎える子供ってこういう風に全てが鮮やかに見えるんだよな。静かな文体のわりに一気に読ませるので策士。


エンリコ・モレッティ『年収は「住むところ」で決まる 雇用とイノベーションの都市経済学』

クリエイティブ資本論の焼き直し。


岩本繁『経営分析の知識』

財務諸表をもとに経営を分析するためのツールと実例が展開される。地味だけどタイトで好感の持てる本。サラッとでも財務諸表が読めると、転職時に会社を調べるのにすごい便利。あと、経営に近い所で仕事をしていた人は実感をもって面白く読めると思う。ただ一切会計の知識がない状態だと辛いかも。

ファイル毎の総追加行数・総削除行数を出すGitのサブコマンド、git-freqをリリースしました

プロジェクトが一段落して「どの機能が一番大変だったのかな?バグが多かったのは?」という情報を知りたくなりました。ということで、ファイル毎の総追加行数・総削除行数を出すGitのサブコマンドを作りました。

https://github.com/fujimura/git-freq

http://hackage.haskell.org/package/git-freq

$ cabal install git-freq でインストールできます。Haskellの環境が必要なので、お持ちでなかったらHaskell Platformをインストールしてください。

結果はcsvでファイル名、追加行数、削除行数の順に出力されます。 Lens2587bb01だとこんな感じです。

$ git freq | head
src/Control/Lens.hs,5365,5263
src/Control/Lens/Fold.hs,5885,3471
src/Control/Lens/Internal.hs,4205,4123
src/Control/Lens/Type.hs,3493,2869
src/Control/Lens/TH.hs,3530,2066
src/Control/Lens/Setter.hs,3097,1846
src/Control/Lens/Internal/Zipper.hs,2432,2436
src/Control/Lens/Traversal.hs,2846,1628
src/Control/Exception/Lens.hs,2646,1697
src/Control/Lens/Plated.hs,2395,1715

本来は機能ごとに変更を計測したくて、GitHubのタグをAPI経由で取得して…と夢は広がったんですが道のりが遠そうなので、とりあえずファイル別に見れればよいかなと思い。

実装はgit log --numstatを合計してるだけです。 IOを型クラスで抽象化してテスタブルにする、ってのが出来たのが嬉しいです。

たぶんドキュメントとかサードパーティーのライブラリとか、測りたくないファイルが結果に出ちゃうと思うんですが、そういう場合はこんな感じで絞ればOK。Ruby on Railsa6c8cdeActiveRecord部分の様子です。

$ git ls-files activerecord |grep -v html |xargs git freq
activerecord/CHANGELOG.md,12854,12126
activerecord/lib/active_record/base.rb,10026,9703
activerecord/lib/active_record/associations.rb,9331,7615
activerecord/test/cases/migration_test.rb,5384,4486
activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb,4829,4085

CHANGELOGが一番更新が多い。しっかりしてますね。

問題点としてはファイルを移動すると変更量がリセットされてしまう点です。仕方ないか。あと歴史の長いリポジトリだとgit log --numstat自体が結構時間かかるので、遅いです。 次は日付毎に変更量を取れるようにしたいなって思ってます。

ファイルごとでも合計が取れると結構面白いですよ。あ〜ここが辛かったんだな、とか。プロジェクトが一段落したらgit freqして、振り返ってみてはどうでしょうか。

2014年、春のGit事情

なんとなく最近どんな感じでGitを使っているか、適当にリストアップしてみた。

よく使うやつ

  • git status

    git status --branch --short にしている。変更されたファイルが出る。とりあえず何をしたかざっくり把握する用。sエイリアスしている。一日100回くらい実行しているのではないか。

  • git diff

    特にオプションは指定していない。何をしたかしっかり把握する用。dエイリアスしている。一日50回くらい実行しているのではないか。

  • git grep

    バージョン管理しているファイルから渡した単語を含む行を検索、表示。関数の検索などあらゆる場面で超便利。オプションは --line-number --show-function --color --heading --break がオススメ。

  • git ls-files

    バージョン管理しているファイルのファイルパスを表示。どんなファイルがあったか一覧するのに便利。 パイプを駆使すると使い道が増える。例えばこのアプリ、ヘルパーは何があったっけ?って時は $ git ls-files | grep helper.rb で一覧できる。

  • git show

    HEADのコミットログとDiffを表示。ついさっき何やったのかを見るのに便利。最近けっこう使う。

  • git log --decorate

    git lエイリアスしてる。コミットコメントだけ読む時用。

  • git log --decorate --stat --patch

    git lpエイリアスしてる。git log よりもこれをよく使う。パッチ形式で変更を全て出す。pager=lessなので、lessで見ることになる。色々試した結果この出力に落ち着いた。

自作のサブコマンド

実行ファイルになってるコマンドは、PATHが通ってる所ににsymlinkして.gitconfigこんな感じで書いて使ってる。

  • git fixup

    HEADにステージされた変更をamendする。あ、これ忘れてた、って時(しょっちゅうありますね…)に便利。Originally by @idesaku.

  • git gsub

    git gsub foo bar すると、リポジトリ内のfoobarに置換する。リファクタリングなどで超便利。

  • git tb

    git topic-branchの略のつもり。HEADのコミットログをhyphenizeしてブランチを作成、チェックアウトする。1コミットしてやっぱブランチ切りたい!って時に便利。"Add missing translation"だとadd-missing-translationという名前のブランチができる。正しくコミットログを書いていてればブランチ名も正しくなるので良い。

  • git bk

    git backupの略のつもり。git tbしたあと元いたブランチに戻る。とりあえず現状でブランチ切っておきたい、という時に便利。

エイリアス

いくつかのコマンドは、こんな感じで1文字にエイリアスしてる。賛否両論なんだけど、一度試して欲しいです。特にsm

alias g='git'
alias s='git s'
alias m='git checkout master'
alias d='git d'

所感

なぜかgit showを使う機会が増えた気がする。

自画自賛になるけどgit gsubgit tbがライフチェンジングすぎてやばい。git renameも作った(内容はご想像の通り)が使い込んでないので載せなかった。

最近コミットコメントの3行目以降を努めて書くようにしてる。変更への想い・気持ち・感謝・コードからわからないコンテクストを込めている。実質的に変更箇所に書いた長文コメントと同じになる場合もあるんだけど、人はコードよりコミットコメントを先に読むので重複は気にせず書いてる。コードレビューへの予防線を張るのにも役立つ。

Add, Fix, Changeから始まるコミットコメントはコミットコメントスメルだと思って気をつけてる。「追加、直す、変える。そんなの変更管理してるんだから当たり前だろ!」と自分に言い聞かせて、もっとこの変更で何が起きるのか、具体的に表現する言葉を探すようにしてる。あんまりできてないけど。

jQuery.Deferredの使いどころ

便利便利と言われつつも使ってみないと良さがわからないのがDeferred Object。身近なところで言うと、$.ajaxが返す、doneとかfailとかを呼べるアレもDeferred Objectです。使いこなすと色々な処理をコールバックを渡すよりもうちょっと綺麗に書けるようになります。

ということで最近プロジェクトで使ったパターンを中心に例をあげてみます。

コードはすべてCoffeeScript擬似コードです。

まず最初に便利な書き方を覚える

Deferred Objectは$.Deferred()で作るわけですが、これには「作られたDeferred Object自体を引数にとる関数」を引数として渡せます。これを利用すると

d = $.Deferred()
doSomethingWithCallback -> d.resolve()
d

$.Deferred (d) -> doSomethingWithCallback d.resolve

と書けます。一行で、コールバックを取る関数をDeferred Objectを返す関数にすることができるわけです。これは僕が働いているQuipperでもイディオムになってて、コード量削減に寄与しています。引数についての詳細はこちら を。

複数イベントにまたがる一連の処理を逐次実行っぽく書く

こんな具合にイベント間に依存関係が生まれる事ってありますよね。 フラグで実行順序を制御しているのが切ないです。

list.on 'selected', ->
  uploadSelection().done ->
    uploaded = true

input.on 'entered', ->
  if uploaded
    uploadEnteredContent().done ->
      entered = true

confirmation.on 'confirmed' ->
  if uploaded and entered
    uploadConfirmed().done ->
      confirmed = true

goToNextPage.on 'click', ->
  if uploaded and entered and confirmed
    route "/next_page"

実際のアプリケーションを書くと、イベントハンドラ内はもっとゴチャゴチャするはずです。

これはjQuery.Deferredを使えば、 こう書けます。thenにより逐次実行っぽい書き方ができるので、フラグによる実行順序の制御は不要です。

select = $.Deferred (d) -> list.on 'selected', ->
  uploadSelection().then d.resolve

enter = $.Deferred (d) -> input.on 'entered', ->
  uploadEnteredContent().then d.resolve

confirm = $.Deferred (d) -> confirmation.on 'confirmed', ->
  uploadConfirmed().then d.resolve

$.Deferred().resolve()
  .then(select)
  .then(enter)
  .then(confirm)
  .then -> routeTo "next page"

沢山の非同期処理をまとめる

例えば、画像の先読み

preload = (sources) ->
  promises = _.map sources, (src) ->
    d = $.Deferred()
    img = new Image
    img.src = src
    img.onload = d.resolve
    img.onerror = d.reject
    d.promise()

  $.when(promises...)

preload [
  "assets/foo.png"
  "assets/bar.png"
  "assets/baz.png"
]

(Thanks @mizchi)

例えば複数のAjax Request

a = $.get("https://somewhere.com/api/a")
b = $.get("https://somewhere.com/api/b")
c = $.get("https://somewhere.com/api/c")

$.when(a,b,c).then (aResponse, bResponse, cResponse) ->
  new AView model: aResponse.responseJSON
  new BView model: bResponse.responseJSON
  new CView model: cResponse.responseJSON

ところでPromise/A+って何?ES6 Promisesとか何なの?jQuery.Deferredで大丈夫なの?

まず、Promiseというのはいわゆるデザインパターンのひとつ。Wikipediaによると、用語自体はSchemeに由来しているようです。Promise/AというのはJavaScriptにおけるPromiseの標準仕様のProposal。それの強化版としてPromise/A+がある。jQuery.DeferredはPromise/Aにざっくり準拠。ES6 PromisesはPromise/A+にだいたい準拠。つまり、どれもだいたい同じAPIと挙動を持っています。僕はプロジェクトでjQuery.DeferredからQへの乗り換えを試みてみましたが、あまり変わらなかったのでjQuery.Deferredに戻りました。 ということで、とりあえずjQuery.Deferredでも大丈夫だと思います。ただ例外の扱い、doneの挙動などが違うので気をつけて。

余談ですがPromiseをモナドにするって提案をJavaScriptの人が「現実を完全に無視した型付き言語のおとぎの国の話しだろ」って無碍に却下するという事件がありました。 個人的にはPromise/Aの 1) 成功/失敗、 2) 非同期処理の制御フロー という2つの要素をごっちゃにしてるのが汚いなあと思ってて、Brian McKennaさんの提案する仕様のほうがクリーンかつ一貫性があって、いいと思うんですけどね。