( ꒪⌓꒪) ゆるよろ日記

( ゚∀゚)o彡°オパーイ!オパーイ! ( ;゚皿゚)ノシΣ フィンギィィーーッ!!!

関数の話

こんにちは、しいたけです。

某所で関数型プログラミングとはリスト処理のことなのか、と燃えているのを見て、関数型プログラミングとは何か、ということを自分なりの考えを述べたいと思いました。春なので。

この資料は2年ほど前にSupershipの社内勉強会で使ったものですが、この中で関数とオブジェクトを対比している箇所があります。 関数もオブジェクトも、変数や関数の引数戻り値として扱える第1級の値であり、状態を持ち(メンバー変数/クロージャ)、組み合わせが可能(delegate, composition/関数合成)、である、と。

ではオブジェクト指向関数型プログラミングで何が決定的に異なるかというと、設計・実装のアプローチに何を中心に据えるか、ということだと思います。

オブジェクト指向では、クラス・オブジェクトをモデリングし、各種のオブジェクト指向デザインパターンを用いてオブジェクト同士を組み合わせながら設計・実装を行います。 関数型プログラミングでは、関数を細かな組み合わせ可能な単位に分解し、関数合成、再帰クロージャなどを駆使しながら組み合わせることで設計・実装します。

こう書いてみると当たり前のことですが、コードの記述スタイルだけ議論しても意味がなくて、そのような記述はプログラム全体を貫く思想のなかでどのような位置づけにあるのかのコンテキストが重要なのではないでしょうか。 例えば、Goでは無理して無名関数を用いたリスト処理を書くより、forで書くほうがはるかに自然ですよね。 JavaScriptでは……どうでしょうか、色々なスタイルで書けるので議論が紛糾するのかも。

map/reduceを使ったリスト処理が関数型プログラミングの全てかというとそうではなく、あくまで関数を中心に据えた考えた方の一つとして、遅延評価 + 高階関数/クロージャの組み合わせによるストリームライクな実装がある、ということだと思います。 関数を中心に据えると、「何をフィルターするか」「要素をどのように変換するか」という処理の単位が関数として抽出され、その組み合わせ方法として遅延評価リストと高階関数を用いる方法がある、というだけです。 結果、関数を中心にすえたアプローチではmap/reduceを使う記述が自然と導かれます。 とはいえ、 リスト処理の実装としては再帰を使う方法もあるので、あくまで関数型プログラミングによるリスト処理の1アプローチにすぎません。

どちらのスタイルで書くのがよいか、というのに絶対的な正解はないと思いますが、関数を中心としたアプローチを知っていると、手続き型のプログラムを書いていても思わぬ場面で役に立つことがあります。 ちょうど最近役にたった実体験のひとつで、 ある rspecのパフォーマンスチューニングの際に遅延評価が役にたったことがあります。

error_msg = generate_error_message(actual_value, expected_value)
expect(actula_value).to have_content(expected_value),  error_msg

こんな感じのrspecのコードで、 assertのエラー時に表示する error_msg を生成する generate_error_message が非常にコストの掛かる処理でした。 通常はassertに失敗する場合の方が稀なので、必要になるまで(assertに失敗するまで) この処理の呼び出しを遅延させることで性能が改善しそうです。

rspecexpect はエラーメッセージの代わりに Proc を渡すことが可能です。 そこで、エラーメッセージを生成する処理をクロージャとして抽出し、 expect に渡すことで遅延評価させることができます。

error_msg_generator = ->() { generate_error_message(actual_value, expected_value) }
expect(actula_value).to have_content(expected_value),  error_msg_generator

関数型プログラミングの知識があると、 expect の引数が Proc を取る、というシグニチャから、遅延評価による性能改善に利用する発想が導かれます。 このように、関数型プログラミングは通常の手続き型プログラミングでもおおいに役立ちますので、双方それぞれの手法を学んでおくとプログラミングの裾野が広がりま。 これが、ぼくが関数型プログラミングを学ぶことをオススメする理由です。

まとめ

しいたけおいしいです。

作って学ぶ 「Https Man in The Middle Proxy」 in Go

ᕕ( ᐛ )ᕗ こんにちわ、しいたけです。

webのhttps化が推進される昨今ですね?
https通信は経路上での通信内容が盗聴・改竄されるのを防ぐことができますが、開発用途でhttps通信の内容を確認したい場合が稀にあります。
そのような場合は mitmproxy などを導入すればよいのですが、せっかくなので実際にこのようなProxyをGoで実装してみて、 中間者攻撃(Man-in-The-Middle Attack)がどのような手法でhttps通信を盗聴・改竄するのか確かめてみました。

実際に書いたProxyのコードはこちらです

yuroyoro/mitm_proxy_sample

https proxy と HTTP CONNECT tunneling

まず、通常のhttps Proxyの動作を確認してみましょう。

httpsでは、ProxyはクライアントからのCONNECTメソッドを受信すると、クライアントに代わって対象ホストとのTCPコネクションを確立し、以降はクライアントと対象ホストのTCP通信を転送します。クライアント-ホスト間のTLS接続のhandshakeもproxyを経由して行わます。
この方式により、Proxyを経由しつつクライアント-ホスト間でTLSセッションが確立され、Proxyを経由しつつも経路上では暗号化された通信が可能となります。

図にするとこんな感じです

f:id:yuroyoro:20180216193239p:plain

実装

では具体的な実装を見てましょう。
まずはおなじみ ServeHTTP です。クライアントから CONNECT メソッドが送信されたら、通信を転送する relayHTTPSRequest を呼び出します

https://github.com/yuroyoro/mitm_proxy_sample/blob/master/main.go#L34

func (proxy *MiTMProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  // CONNECT メソッドが来たら
    if r.Method == http.MethodConnect {
        if proxy.mitm {
            proxy.mitmRequest(w, r)  // Man in The Middleする
        } else {
            proxy.relayHTTPSRequest(w, r) // Tunnelingで通信を転送する
        }
        return
    }

    proxy.transportHTTPRequest(w, r)
}

実際に通信を転送しているコードは以下のとおりです。処理の流れはコメントを読んでもらえばわかると思いますが、やっていることは単純です。
CONNECT メソッドで指定されたホストにTCP接続を張って、クライアントからの通信をそのまま流し込むだけです

https://github.com/yuroyoro/mitm_proxy_sample/blob/master/https.go#L12

func (proxy *MiTMProxy) relayHTTPSRequest(w http.ResponseWriter, r *http.Request) {
    proxy.info("relayHTTPSRequest : %s %s", r.Method, r.URL.String())

    // CONNECT先のHostへTCPコネクションを張る
    dest, err := net.Dial("tcp", r.Host)
    if err != nil {
        http.Error(w, err.Error(), http.StatusServiceUnavailable)
        return
    }

    // http.Hicjacker を利用してクライアントとの生のTCPコネクションを取り出す
    conn := hijackConnect(w)
    // クライアントには200 OKを返す。これでクライアントはこのTCP接続にHTTPリクエストを送ってくる
    conn.Write([]byte("HTTP/1.0 200 OK\r\n\r\n"))

    proxy.info("relayHTTPSRequest : start relaying tcp packets %s %s", r.Method, r.URL.String())

    // クライアント-対象Host間のTCP通信をそのまま中継する
    go transfer(dest, conn)
    go transfer(conn, dest)
}

func transfer(dest io.WriteCloser, source io.ReadCloser) {
    defer dest.Close()
    defer source.Close()
    io.Copy(dest, source)
}

Goには http.Hijacker という便利なインターフェースがあり、 http.ResponseWriter から生のクライアントとのTCP接続を取り出すことができます。
これを利用して、TCP通信の転送を行っています

func hijackConnect(w http.ResponseWriter) net.Conn {
    hj, ok := w.(http.Hijacker)
    if !ok {
        panic("httpserver does not support hijacking")
    }

    conn, _, err := hj.Hijack()
    if err != nil {
        panic("Cannot hijack connection " + err.Error())
    }

    return conn
}

実際はtimeoutなどを考慮した実装をすべきなのですが、これだけでも動きます。

Man in The Middel Proxyの仕組み

では、本題の中間者攻撃を行うProxyについてです。

通常のhttps proxyでは、クライアントからの CONNECT メソッドを契機に、対象HostとのTCP通信を中継していました。
Proxyを流れる通信内容はTLSによって暗号化されており、内容を盗聴・改竄することはできません。

しかし、対象Hostとクライアント間のTLS handshakeもProxyを経由するので、この段階でクライアントからのTLS handshakeを、対象ホストになりすましてProxyが行うとどうなるでしょうか?
つまり、Proxyは対象ホストのサーバー証明書をその場で生成して署名し、クライアントに提示します。
もちろん、Proxyが署名したサーバー証明書は信頼できないCAのものとしてブラウザには警告が出ますが、そのままユーザーが続行することでTLS handshakeが成功します。
クライアントは確立したTLS接続を対象ホストとのものだと思いこんで、Proxyがクライアントに送り込んだニセのサーバー証明書の公開鍵で通信を暗号化するので、Proxyはその内容を復号することができます。
あとは、復号したリクエストをそのまま対象のホストに転送すれば、httpsにも関わらずProxyは通信内容を把握しつつ、対象ホストとの通信を取り持つことができてしまいます。
これで中間者攻撃が成立しますʕ  ゚皿゚ ʔ 。

図にすると以下の流れとなります

f:id:yuroyoro:20180216193258p:plain

通常、このような攻撃はブラウザが警告を出すために成立しません。
まず、ユーザーが明示的にブラウザにProxyを指定する必要がありますし(port forwardを利用した透過Proxyはその限りではない)、Proxyが署名に使用するルート証明書(または中間証明書)がTrust Chainにないからです。
逆に言えば、信頼できないルート証明書をシステムにインストールしてしまうと、このような攻撃が成立する余地が生まれてしまいます。

実際に、一部のセキュリティアプライアンスやアンチウィルスソフトウェアは、このような手法でhttps通信の内容をチェックしています。
Avastを入れた状態でブラウザで証明書チェーンを確認すると、 「Avast trusted CA」という謎の認証局が出現するのはこのためです( ;゚皿゚)ノシΣ フィンギィィーーッ!!!

以前、LenovoのPCにプリインストールされたアドウェアSuperfish」がルート証明書をシステムにインストールした上に、全PCで共通のCA秘密鍵を使っていたことで大問題になりましたね。
Dellでも似たようなことがあったみたいです( ꒪⌓꒪)

LenovoのPC全機種にプレロードされているアドウェアが実は恐ろしいマルウェアだった! | TechCrunch Japan DellのPCに不審なルート証明書、LenovoのSuperfishと同じ問題か - ITmedia エンタープライズ

実装

それでは具体的な実装の解説を行います。処理の流れは以下のとおりです。

  1. CONNECTメソッドのリクエストから、http.Hijackerを使って生のTCPコネクションを取り出す
  2. クライアントには200 okを返す
  3. 接続先ホストの証明書を、予め用意してあるroot証明書でサインして生成する
  4. 生成した証明書でクライアントとtls接続を確立する (root証明書が登録されていないとブラウザで警告が出る)
  5. goroutine起こして、クライアントとのtls接続からhttp requestを読み込む
  6. 受けたhttp requestをそのまま接続先hostに送信する
  7. 接続先hostからのhttp responseを、クライアントtls接続に書き込む
  8. EOFが来るまで 5-7繰り返し

https://github.com/yuroyoro/mitm_proxy_sample/blob/master/https.go#L57

func (proxy *MiTMProxy) mitmRequest(w http.ResponseWriter, r *http.Request) {
    // http.Hicjacker を利用してクライアントとの生のTCPコネクションを取り出す
    conn := hijackConnect(w)
    // クライアントに200 OKを返しておく
    conn.Write([]byte("HTTP/1.0 200 OK\r\n\r\n"))

    // 以降の処理はgoroutine上で行う
    go proxy.transportHTTPSRequest(w, r, conn)
}

func (proxy *MiTMProxy) transportHTTPSRequest(w http.ResponseWriter, r *http.Request, conn net.Conn) {
    proxy.info("transportHTTPSRequest : %s %s", r.Method, r.URL.String())

    // 対象ホストのニセのサーバー証明書を生成して署名する
    host := r.Host
    tlsConfig, err := proxy.generateTLSConfig(host)
    if err != nil {
        if _, err := conn.Write([]byte("HTTP/1.0 500 Internal Server Error\r\n\r\n")); err != nil {
            proxy.error("Failed to write response : %v", err)
        }
        conn.Close()
    }

    // クライアントとのTCP接続上で、ニセのサーバー証明書を利用してTLS接続を待ち受ける
    tlsConn := tls.Server(conn, tlsConfig)
    if err := tlsConn.Handshake(); err != nil {
        proxy.error("Cannot handshake client %v %v", r.Host, err)
        return
    }
    defer tlsConn.Close()

    proxy.info("transportHTTPSRequest : established tls connection")

    // ニセの証明書で確立したTLS接続上でクライアントからのリクエストを読み込む
    tlsIn := bufio.NewReader(tlsConn)
    for !isEOF(tlsIn) {
        req, err := http.ReadRequest(tlsIn) // http.Requestオブジェクトとして通信を読み込む
        if err != nil {
            if err == io.EOF {
                proxy.error("EOF detected when read request from client: %v %v", r.Host, err)
            } else {
                proxy.error("Cannot read request from client: %v %v", r.Host, err)
            }
            return
        }

        proxy.info("transportHTTPSRequest : read request : %s %s", req.Method, req.URL.String())

        // 転送用にURLやヘッダーなどを設定
        req.URL.Scheme = "https"
        req.URL.Host = r.Host
        req.RequestURI = req.URL.String()
        req.RemoteAddr = r.RemoteAddr

        dumpRequest(req)
        removeProxyHeaders(req)

        // http.RoundTripper で受信したリクエストを対象ホストに転送し、レスポンスを受け取る
        resp, err := proxy.transport.RoundTrip(req)
        if err != nil {
            proxy.error("error read response %v %v", r.URL.Host, err.Error())
            if resp == nil {
                http.Error(w, err.Error(), 500)
                return
            }
        }

        proxy.info("transportHTTPSRequest : transport request: %s", resp.Status)

        dumpResponse(resp)

        // レスポンスをクライントへのTLS接続に書き込む
        resp.Write(tlsConn)
    }

    proxy.info("transportHTTPSRequest : finished ")
}

ポイントは、 リクエストを受けるとニセのサーバー証明書をその場で生成して、その証明書と http.Hijacker で取り出したクライントのTCPtls.Server を用いてTLS接続をなりすますことです(生成した証明書はキャッシュします)。
証明書の生成は長くなるのでここには載せませんが、 こちら を見てもらえばと思います。

クライントとのTLS接続を乗っ取れば、あとはその接続上でHttpリクエストを読み込み、対象ホストに転送すればOKです。Goでは、 http.RoundTripper を利用すれば http.Request をそのまま転送できるので便利です。その際に、リクエスト・レスポンスの内容をdumpしています。
悪意があれば、この段階で改竄も可能でしょう。

まとめ

以上が、Man-in-The-Middle Attackを行う簡単なProxyの実装です。この攻撃が成功する条件としては、

  • 経路上にこのようなProxyが存在する (https通信がport forwardされている場合もある)
  • Proxyが署名に使うルート証明書がTrust Chainに存在する

の2点です。特に2点目はTLSの根幹をなす部分で、それゆえにルート証明書の管理は厳格に行う必要があり、GoogleSymantecの証明書を無効にし、LenoveやDellは責められるべきなのです。Avastもちょっとどうかと思います。

実際に手を動かして実装してみると、Proxyの実装で注意スべき点や、TLS認証局の仕組みとか色々と学びがあり、よかったとおもいました( ꒪⌓꒪)

go tool traceでgoroutineの実行状況を可視化する

こんにちわ。しいたけです。今日はgoroutineの実行状況をいいかんじに可視化するツールの話です。

goのプロファイリングツールと言えば、 runtime/pprofnet/http/pprof ですよね。これらの使い方はググればすぐに出てくるのですが、 詳細なtraceを取得して可視化できる runtime/trace については、日本語の情報が殆ど無いので書いてみましいたけ。

runtime/trace はgoroutineの実行状況やsystem callのイベント、Heapやnetworkの状況をこんな感じに可視化してくれるので便利です。

f:id:yuroyoro:20171211191843p:plain

これは自作のクローラーを動かしている際のtraceを可視化したもので、横軸がタイムラインになっており、上段に Heapの使用状況やgoroutineとos threadの数が, 下段はnetworkやProccesor(GOMAXPROCSで指定するgoroutineの実行環境)毎にどのコードが実行されているか、が表示されます。

Heapやgoroutines数の増減と処理の関連を時系列で追えるので、大まかなボトルネックの特定に便利です。また、各goroutine毎の開始/終了とsystem callやunblockイベント(goroutineの切り替え)を細かく追えるので、goroutineが刺さっている箇所の特定にも役立ちます

右側のboxの↓アイコンをクリックした上で、グラフ上でドラッグするとzoomin/outできます。また、View Options で Flow Eventsをチェックすると矢印が描画されます

ここに、実際に動かせるサンプルを置いておきます。

f:id:yuroyoro:20171211191957p:plain

これはとある自作Proxyのtraceの一部で、Proc3で実行されている皆さんおなじみの net/http.(*conn).serve がリクエストを受けて、 go 構文で新たなgoroutineを起動してProc2で実行される様子です。このように、どのタイミングでどのgoroutineが動いたのかが一目瞭然です。

f:id:yuroyoro:20171211192018p:plain

これはGCが実行されている様子です。ほとんどのProcでGC関連の処理が動いた後に、上段のHeapのallocatedが減っている様子が見てとれます。

で、このtraceの可視化の方法ですが、 手っ取り早いのは runtime/trace パッケージをimportして、 trace.Start(w io.Writer)trace.Stop() を呼び出す方法です

https://golang.org/pkg/runtime/trace/

package main

import (
    "log"
    "os"
    "runtime"
    "runtime/trace"
)

func main() {

    f, err := os.Create("trace.out")
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()

    trace.Start(f)
    defer trace.Stop()

  // do something ...
}

このように、traceを取得したい処理の前後で trace.Start を呼び出すと、結果がファイルに出力されます。 そのファイルを go tool trace コマンドの引数に渡すと、ブラウザで取得したtraceを分析できるようになります。

net/http/pprof パッケージを使っているウェブアプリケーションでも、簡単にtraceを取得することができます。

pprofがlistenしているなら、 /debug/pprof/trace?seconds=5 にリクエストを送ると、5秒間のtraceを取得して結果をダウンロードできます。得られたファイルを go tool trace に渡せば、同様にブラウザ上で分析できます。

詳細なボトルネックの分析は、 pprofやgo-torchでのflamegraphの分析が有向ですが、 go tool trace では時系列に対応しての分析が可能なので、それぞれ状況に合わせて使い分けると良いのではないかと思いましいたけ。

F.Y.I.

ディスク使用量をFlameGraphで可視化する

こんにちわ。しいたけです。今日はディスク使用量をFlameGraphにするツールの話です。

FlameGraphについては、 Flame GraphsGolangでFlame Graphを描く | SOTA を読んでもらうのが手っ取り早いのですが、ようはプロファイル結果を可視化する方法です。縦軸が呼び出しの階層に、横軸がサンプル数や実行時間などに対応しており、どの関数が支配的かを直感的に見ることができる優れたグラフですよ。

で、このFlameGraph、別にプロファイル結果だけではなく、ツリー構造で各ノードが量を持つ場合に、枝毎の累積量を可視化するのに利用できます。プロファイル以外に、ツリー構造でノードが量を持つ例として、ディレクトリ階層毎のディスク使用量が考えられます。

というわけで、指定ディレクトリ以下のディスク使用量をFlameGraph化するツールを書きました。

GitHub - yuroyoro/du-flamegraph: visualize disk usage as flamegraph

こんな感じのグラフが出力されます

http://yuroyoro.net/du-flamegraph.svg


goで書かれており、使い方は、 `go get -u yuroyoro/du-flamegraph` でインストールできます。

このツールは、 FlameGraphの描画に `flamegraph.pl` というスクリプトが必要であり、これは GitHub - brendangregg/FlameGraph: Stack trace visualizer にあります。
これを git cloneなどで手元に入れて、 $PATHに追加するか、 `--flamegraph-script` で位置を指定するかしてやれば、FlameGraph がsvgとして出力されます。

NAME:
   du-flamegraph - visualize disk usage as flamegraph

USAGE:
   du-flamegraph [global options] [FILE]

VERSION:
   0.0.0

GLOBAL OPTIONS:
   --width value, -w value    width of image (default 1200) (default: 1200)
   --height value, -h value   height of each frame (default 16) (default: 16)
   --flamegraph-script value  path of flamegraph.pl. if not given, find the script from $PATH
   --out value                distination path of grenerated flamegraph. default is ./du-flamegraph.svg (default: "./du-flamegraph.svg")
   --verbose                  show verbose log
   --version, -v              print the version

FlameGraph、色々と応用がききそうですね。

「関数型Ruby」という病(7) - Elixir's Pipe operator |> in Ruby

最近Elixirが人気ですよね。Erlang VM上でOTPの恩恵を受けながら簡潔な記法で並行処理を書ける言語ということで話題になっていますな? Elixirは関数型プログラミングのエッセンスを取り入れていると言われており、そのひとつにPipe演算子(|>) がある。

Pipe演算子(|>)とは何かというと、左辺の値を右辺の関数の第1引数に適用する演算子

iex> [1, [2], 3] |> List.flatten()
  [1,  2,  3]


上記のコードは、左辺の[1, [2], 3] を 右辺の List.fatten(list) の引数として渡す。 このPipe演算子は、Stream モジュールなどと合わせて利用するとデータが左から右へ流れている模様をコードとし視覚化することができるという利点があるっぽい(感じろ)。

iex(16)> f = fn a -> IO.puts "f(#{a}) : #{a+1}"; a ; end
#Function<6.90072148/1 in :erl_eval.expr/5>

iex(17)> g = fn a -> IO.puts "g(#{a}) : #{a*2}"; a * 2 ; end
#Function<6.90072148/1 in :erl_eval.expr/5>

iex(18)> 1..10 |> Stream.map(f) |> Stream.map(g) |> Enum.take(3)
f(1) : 2
g(1) : 2
f(2) : 3
g(2) : 4
f(3) : 4
g(3) : 6
[2, 4, 6]


1..10 |> Stream.map(f) |> Stream.map(g) |> Enum.take(3) というコードで、1から10のStreamに対してlazyに関数fとgを順番に適用しながら3つの要素を取り出すという様を素直に表現できていますね?(思え)

さて、そんな便利なパイプ演算子ですが、実は2年ほど前に作ったlambda_driver.gemに既に実装されていたりする。

Rubyでは中置演算子を独自に定義することはできないので、 Object クラスに |> というメソッドを生やすことで実現しよう(全角ェ)。 素朴な実装はこうだ

class Object
  def |>(f = nil)
  puts f
    if block_given?
      yield self
    else 
      f.call(self)
    end
  end

  alias_method "|>", :>=
end


さて、この全角の |> を使って、上記のElixirのコードをRubyで書いてみるとどうなるか?

irb(main):059:0> f = ->(a){ puts "f(#{a}) : #{a + 1}" ; a + 1}
=> #<Proc:0x007ffb8d8b2348@(irb):59 (lambda)>

irb(main):060:0> g = ->(a){ puts "g(#{a}) : #{a *2}" ; a * 2}
=> #<Proc:0x007ffb8d860a98@(irb):60 (lambda)>

irb(main):061:0>  (1..10).|>(&:lazy).|>{|x| x.map(&f) }.|>{|x| x.map(&g) }.|>{|x| x.take(3)}.|>(&:force)
f(1) : 2
g(2) : 4
f(2) : 3
g(3) : 6
f(3) : 4
g(4) : 8
=> [4, 6, 8]


なんというか、すごく……ダサいですね……。

というか、Rubyだったら素直にこう書いた方がいい

irb(main):143:0> (1..10).lazy.map(&f).map(&g).take(3).force
f(1) : 2
g(2) : 4
f(2) : 3
g(3) : 6
f(3) : 4
g(4) : 8
=> [4, 6, 8]


ごくごくまれに、左辺値がObjectとかでrevapplyを使いたくなることもなきにしもあらずだが、そういう場合でも大抵は Object#try で事足りる。

結論 : Rubyには必要ないのでは?

「commit-m: GitHubコミットメッセージの文例が検索できるサービス」がとても便利だったのでcliから使えるコマンド書いた

http://commit-m.minamijoyo.com/:titele という有名OSSのコミットメッセージを検索できるサービスがあって、英語のコミットメッセージを書くときに「あれ? これどういう風に書けばいいんダー」ってときに例文を検索できて捗る。

commit-m.minamijoyo.com

が、自分の場合はコミットメッセージ書くときはvim とか git commit -m とかからなのでCLIで検索できたらより捗るかと思ってGolangで書いた。 APIとかは無いようなのでクロールしてる。 GoQuery 使えばこの手のクローラーが一瞬でかけるのでよさがある。

github.com

go get github.com/yuroyoro/gommit-m で入れた後に gommit-m keyword [page] で検索できる。

f:id:yuroyoro:20151110131804p:plain

表参道.rb #4で「本当は怖い オープンクラスと Duck Typing」というLTをやった話

スライドです

f:id:yuroyoro:20150904130822p:plain
本当は怖いオープンクラスとDuckTyping - 表参道.rb #4

まぁたいした話じゃないんですが、マッドマックスの画像をスクリーンに大写しできたのでその点だけで個人的には満足しています

「型を讃えよ」