( ꒪⌓꒪) ゆるよろ日記

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

ʕ  ゚皿゚ ʔ GolangからLevelDBを使う

cgoとLevelDBを使って、タイトルのとおりのものを作ってみた。頑張ればRiakとかInfluxdbみたいなのを書けるかもナー。
ʕ  ゚皿゚ ʔ cgo楽しいおシーゴォー


コードはすべてGithubにある。

yuroyoro/leveldb-go-sample · GitHub


なお、この実装はあくまで個人的な練習で作ったものなので、まともにLevelDBをGoから使うならばInfluxdbでも使ってるlevigoがおすすめ。
LevelDBはあらかじめinstallしてある想定。 mac osxなのでbrew install leveldbで入った。

cgoでLevelDBをwrapする

まずは、cgoを使ったLevelDBの簡単なwrapperを用意する。単にLevelDBを使うだけなら、感覚的にはsqlite3みたいに、leveldb_openでopenして得られるleveldb_t構造体を使ってputやgetを呼び出し、終わったらcloseすればいい。

leveldb.go から抜粋。

package main

// #cgo LDFLAGS: -lleveldb
// #include <stdlib.h>
// #include "leveldb/c.h"
import "C"

import (
	"errors"
	"unsafe"
)

// C Level pointer holder
type LevelDB struct {
	CLevelDB *C.leveldb_t
	Name     string
}

// Open LevelDB with given name
func OpenLevelDB(path string) (leveldb *LevelDB, err error) {

	cpath := C.CString(path) // convert path to c string
	defer C.leveldb_free(unsafe.Pointer(cpath))

	// allocate LevelDB Option struct to open
	opt := C.leveldb_options_create()
	defer C.leveldb_free(unsafe.Pointer(opt))

	// set open option
	C.leveldb_options_set_create_if_missing(opt, C.uchar(1))

	// open leveldb
	var cerr *C.char
	cleveldb := C.leveldb_open(opt, cpath, &cerr)

	if cerr != nil {
		defer C.leveldb_free(unsafe.Pointer(cerr))
		return nil, errors.New(C.GoString(cerr))
	}

	return &LevelDB{cleveldb, path}, nil
}


上記のOpenLevelDBで、leveldb_openでdatabaseを開く。optionは色々指定できるのだが、サンプルなのでcreate_if_missingだけ指定している。

// Put key, value to database
func (db *LevelDB) Put(key, value string) (err error) {

	opt := C.leveldb_writeoptions_create() // write option
	defer C.leveldb_free(unsafe.Pointer(opt))

	k := C.CString(key) // copy
	defer C.leveldb_free(unsafe.Pointer(k))

	v := C.CString(value)
	defer C.leveldb_free(unsafe.Pointer(v))

	var cerr *C.char
	C.leveldb_put(db.CLevelDB, opt, k, C.size_t(len(key)), v, C.size_t(len(value)), &cerr)

	if cerr != nil {
		defer C.leveldb_free(unsafe.Pointer(cerr))
		return errors.New(C.GoString(cerr))
	}

	return
}

func (db *LevelDB) Get(key string) (value string, err error) {

	opt := C.leveldb_readoptions_create() // write option
	defer C.leveldb_free(unsafe.Pointer(opt))

	k := C.CString(key) // copy
	defer C.leveldb_free(unsafe.Pointer(k))

	var vallen C.size_t
	var cerr *C.char
	cvalue := C.leveldb_get(db.CLevelDB, opt, k, C.size_t(len(key)), &vallen, &cerr)

	if cerr != nil {
		defer C.leveldb_free(unsafe.Pointer(cerr))
		return "", errors.New(C.GoString(cerr))
	}

	if cvalue == nil {
		return "", nil
	}

	defer C.leveldb_free(unsafe.Pointer(cvalue))
	return C.GoString(cvalue), nil
}


put/getはこんな感じ。C.CStringでkeyやvalueをCの*charに変換しているが、これはmemcpyが走る(ハズ?)なので効率的ではない。levigoの実装では、[]byteに変換してunsafe.Pointerでgoのbyte列のpointerをC側に渡す実装になっているようだ。

net/httpでRESTっぽいガワをつける

goを勉強した事がある人なら誰しもがnet/httpを使った簡単なkvsを書いたことがあるはず。今回はバックエンドをLevelDBにして、net/httpでREST的なガワをつけてみる。

まずはBackend側の実装。 backend.go から。

package main

import (
	"log"
)

type Backend struct {
	db     *LevelDB
	putch  chan putRequest
	delch  chan delRequest
	quitch chan bool
}

type putRequest struct {
	key string
	val string
}

type delRequest struct {
	key string
}

func NewBackend(dbname string) (backend *Backend, err error) {
	db, err := OpenLevelDB(dbname)

	if err != nil {
		return
	}

	log.Printf("LevelDB opened : name -> %s", dbname)

	backend = &Backend{
		db,
		make(chan putRequest),
		make(chan delRequest),
		make(chan bool),
	}

	return
}

func (backend *Backend) Start() {
	go func() {
		for {
			select {
			case putreq := <-backend.putch:
				backend.db.Put(putreq.key, putreq.val)
				log.Printf("Backend.Put(%s, %v)\n", putreq.key, putreq.val)
			case delreq := <-backend.delch:
				backend.db.Delete(delreq.key)
				log.Printf("Backend.Delete(%s)\n", delreq.key)
			case <-backend.quitch:
				close(backend.putch)
				close(backend.delch)
				close(backend.quitch)

				log.Printf("Backend stoped")
				return
			}
		}
	}()

	log.Printf("Backend started")
}

func (backend *Backend) Shutdown() {
	backend.quitch <- true
}

func (backend *Backend) Get(key string) (val string, err error) {
	return backend.db.Get(key)
}

func (backend *Backend) Put(key, val string) {
	backend.putch <- putRequest{key, val}

}

func (backend *Backend) Delete(key string) {
	backend.delch <- delRequest{key}
}


NewBackendで先ほど用意したwrapperを使ってLevelDBをopenして、goroutine経由でput/deleteするように実装している。本来ならば、goroutineをsyncして同期っぽいAPIにするべきなのだろうが、面倒なのでサボっている。


次に、net/httpを使ってhttpを処理するserver側。 server.go から。

package main

import (
	"flag"
	"io/ioutil"
	"log"
	"net/http"
)

func main() {

	// parse command line arguments
	dbname := flag.String("name", "./testdb", "Open the database with the specified name")
	addr := flag.String("addr", ":5050", "listen address")

	flag.Parse()

	// open database and start backend
	backend, err := NewBackend(*dbname)
	if err != nil {
		log.Fatal("can't open database", err)
	}

	backend.Start()
	defer backend.Shutdown()

	// listen and serve http
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {

		switch r.Method {
		case "GET":
			HandleGet(w, r, backend)
		case "POST", "PUT":
			HandlePut(w, r, backend)
		case "DELETE":
			HandleDelete(w, r, backend)
		}
	})

	log.Printf("Server listening on : %s", *addr)
	http.ListenAndServe(*addr, nil)
}

func HandleGet(w http.ResponseWriter, r *http.Request, backend *Backend) {
	key := r.URL.Path[len("/"):]

	val, err := backend.Get(key)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	if val == "" {
		w.WriteHeader(http.StatusNotFound)
		return
	}

	w.WriteHeader(http.StatusOK)
	w.Write([]byte(val))
}

func HandlePut(w http.ResponseWriter, r *http.Request, backend *Backend) {
	key := r.URL.Path[len("/"):]

	defer r.Body.Close()
	val, err := ioutil.ReadAll(r.Body)

	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	backend.Put(key, string(val))

	w.WriteHeader(http.StatusCreated)
}

func HandleDelete(w http.ResponseWriter, r *http.Request, backend *Backend) {
	key := r.URL.Path[len("/"):]

	backend.Delete(key)

	w.WriteHeader(http.StatusNoContent)
}

起動時の引数でdbのpathとportを貰ってlisten。httpでGET/POST/PUT/DELETEをそれぞれ処理している。見たとおりの実装で、特に難しいことはしていない。

Dockerfile

Dockerfileもあるので、手元でdockerが動けば動作させてみることができる。Dockerhubでimageを配布しようとしたのだが、なぜかpendingのままでimageをbuildしてくれない( ;゚皿゚)。

Dockerfile

ʕ  ゚皿゚ ʔ Golangからv8を使う

cgoとlibv8を使って、タイトルのとおりのものを作ってみた。頑張ればnodeみたいなのをgoで書けるかもナー。
ʕ  ゚皿゚ ʔ cgo楽しいおシーゴォー


基本的な方法は以下の記事にあるとおりだが、v8のバージョンが上がっていたりするので、多少の手直しをしてある。

Embedding V8 Javascript Engine and Go | Brave New Method


コードはすべてGithubにある

yuroyoro/golang_v8_Embedding_sample · GitHub


まず、libv8を使う以下のようなwrapperをc++で用意して、cgoから使えるようにしておく。

v8warpper.h

#ifndef _V8WRAPPER_H
#define _V8WRAPPER_H

#ifdef __cplusplus
extern "C" {
#endif
    // compiles and executes javascript and returns the script return value as string
    char * runv8(const char *jssrc);

#ifdef __cplusplus
}
#endif

#endif // _V8WRAPPER_H


v8warpper.cc

#include <v8.h>
#include <string.h>

#include "v8wrapper.h"

using namespace v8;

char * runv8(const char *jssrc)
{
    // Get the default Isolate created at startup.
    Isolate* isolate = Isolate::GetCurrent();

    // Create a stack-allocated handle scope.
    HandleScope handle_scope(isolate);

    // Create a new context.
    Handle<Context> context = Context::New(isolate);

    // Enter the context for compiling and running the hello world script.
    Context::Scope context_scope(context);

    // Create a string containing the JavaScript source code.
    Handle<String> source = String::New(jssrc);

    // Compile the source code.
    Handle<Script> script = Script::Compile(source);

    // Run the script to get the result.
    Handle<Value> result = script->Run();

    // The JSON.stringify function object
    Handle<Object> global = context->Global();
    Handle<Object> JSON = global->Get(String::New("JSON"))->ToObject();
    Handle<Function> JSON_stringify = Handle<Function>::Cast(JSON->Get(String::New("stringify")));

    Handle<Value> args[] = { result };
    // stringify result
    Local<Value> json = JSON_stringify->Call(JSON, 1, args);

    // Convert the result to an UTF8 string and print it.
    String::Utf8Value utf8(json);

    // return result as string, must be deallocated in cgo wrapper
    return strdup(*utf8);
}


runv8(char)は、引数の文字列のjavascriptをv8で実行し、結果の値をJSON.stringifyした結果の文字列を返す。
なお、サンプルなのでエラー処理はしていない( ;゚皿゚)。

で、こいつを適当にmakeする。以下のmakefileosx

Makefile.warpper

V8_INC=/usr/local/Cellar/v8/3.19.18.4/include
V8_LIBDIR=/usr/local/Cellar/v8/3.19.18.4/lib/libv8.dylib

CC=g++
CFLAGS= -I$(V8_INC) -I/usr/include -lv8 -dynamiclib -o $(TARGET)
SOURCES=v8wrapper.cc
OBJECTS=$(SOURCES:.cc=.o) $(V8_DYLIB)
TARGET=libv8wrapper.dylib

all: $(TARGET)


$(TARGET): $(OBJECTS)
	$(CC) $(CFLAGS) $< -o $@

clean:
	rm $(TARGET) $(OBJECTS)


あとは、cgoでこのv8warpperをlinkして使えばいい。


v8runner.go

package main

// #cgo LDFLAGS: -L. -lv8wrapper -lv8  -lstdc++
// #include <stdlib.h>
// #include "v8wrapper.h"
import "C"
import (
	"encoding/json"
	"fmt"
	"unsafe"
)

func RunV8(script string, result interface{}) error {

	// convert Go string to nul terminated C-string
	cstr := C.CString(script)
	defer C.free(unsafe.Pointer(cstr))

	// run script and convert returned C-string to Go string
	rcstr := C.runv8(cstr)
	defer C.free(unsafe.Pointer(rcstr))

	jsonstr := C.GoString(rcstr)

	fmt.Printf("Runv8 json -> %s\n", jsonstr)
	// unmarshal result
	err := json.Unmarshal([]byte(jsonstr), result)
	if err != nil {
		return err
	}

	fmt.Printf("Runv8 Result -> %T: %#+v\n", result, result)
	return nil
}


RunV8では、'encoding/json'を利用して、runv8の戻り値である文字列のJSONを、goの値にunmarshalする。
以下のように文字列でscriptをRunV8関数に渡せばinterface型で結果を取得できる。

package main

import (
	"fmt"
)
func main() {

	scripts := []string{
		"null",
		"true",
		"123",
		"457.78",
		"[10, 20, 30]",
		"'Hello, World'",
		"new Date()",
		`obj = {"foo": [1, 2], "bar": {"baz": true, "hoge": "fuga"}}`,
	}

	for _, s := range scripts {
		fmt.Printf("Script -> %s\n", s)

		var res interface{}
		RunV8(s, &res)
		fmt.Printf("Result -> %T: %#+v\n\n", res, res)
	}
}

実行結果

Script -> null
Result -> <nil>: <nil>

Script -> true
Result -> bool: true

Script -> 123
Result -> float64: 123

Script -> 457.78
Result -> float64: 457.78

Script -> [10, 20, 30]
Result -> []interface {}: []interface {}{10, 20, 30}

Script -> 'Hello, World'
Result -> string: "Hello, World"

Script -> new Date()
Result -> string: "2014-06-11T08:58:43.951Z"

Script -> obj = {"foo": [1, 2], "bar": {"baz": true, "hoge": "fuga"}}
Result -> map[string]interface {}: map[string]interface {}{"foo":[]interface {}{1, 2}, "bar":map[string]interface {}{"baz":true, "hoge":"fuga"}}


このように、JSONをstructにmappingさせることもできる。

package main

import (
	"fmt"
)

type Foo struct {
	Foo []int `json:"foo"`
	Bar Bar   `json:"bar"`
}

type Bar struct {
	Baz  bool   `json:"baz"`
	Hoge string `json:"hoge"`
}

func main() {

	script := `obj = {"foo": [1, 2], "bar": {"baz": true, "hoge": "fuga"}}`
	var result Foo

	fmt.Printf("Script -> %s\n", script)
	RunV8(string(script), &result)
	fmt.Printf("Result -> %T: %#+v\n", result, result)

}

実行結果

Script -> obj = {"foo": [1, 2], "bar": {"baz": true, "hoge": "fuga"}}
Result -> main.Foo: main.Foo{Foo:[]int{1, 2}, Bar:main.Bar{Baz:true, Hoge:"fuga"}}

「関数型Ruby」という病(6) - 関数合成と文脈、Proc#liftとProc#>=、そしてモナ

前回から一年以上が経過しているけど、最近lambda_driver.gemに機能を追加したので、そのことについて書こうと思う。
Rubyで、モナ……っぽい関数合成を実装した話だ。

Rubyで関数合成とかしたいので lambda_driver.gem というのを作った - ( ꒪⌓꒪) ゆるよろ日記

関数合成


関数合成については以前に書いたので、こちらを見て欲しい。

「関数型Ruby」という病(2) - 関数合成 Proc#compose - ( ꒪⌓꒪) ゆるよろ日記


おさらいをしておくと、関数合成とは、 関数gと関数fから、g(f(x))という関数hを新たに作り出すことだ。

(g ∘ f)(x) = g(f(x))


関数gと関数fの合成関数g ∘ fに引数xを渡した結果は、関数gにf(x)の結果を渡したものと等しい。つまり、このような操作である。

f = lambda{|x| x + 1 }
g = lambda{|x| x * x }

# 合成関数g ∘ f
h = lambda{|x| g.(f.(x)) }


これを図にするとこんな感じ。 上記の例の合成関数h: g ∘ fに引数 3を与えた場合。

f:id:yuroyoro:20140216195815p:plain


lambda_driver.gemでは、この関数合成をProc#>>で行うことができるようになっている。

# Proc#>>で合成
h = f >> g

h.(3) # => 16


Proc#>>の実装は単純で、以下のようになる。

class Prco
  def >>(g)
   # 「自分の計算結果を引数の関数gへ渡す」Procオブジェクトを返す
   lambda{|x| g.call(self.call(x)) }
  end
end

関数合成と計算の失敗


このように、とても便利な関数合成だが、以下のような状況だと少し困ることがある。

f = lambda{|arr| arr.first } # f: 引数のArrayの最初の要素を取り出す
g = lambda{|x| x + 1 }       # g: 引数に1を加算
h = lambda{|x| x * 2 }       # h: 引数を2倍

# f, g, hを合成
i = f >> g >> i

i.([3,5]) # => 4


関数fは、引数に配列を取って最初の要素を返す。関数gはfの結果に1を加算する。関数hはさらにその結果を2倍する。単純だ。
この3つの関数を合成した物が関数i。図にするとこうなる

f:id:yuroyoro:20140216195814p:plain


では、関数iに空配列[]を渡すとどうなるか?

# []を渡すと
i.([])
# => NoMethodError: undefined method `+' for nil:NilClass


関数fはnilを返し、関数gはnilに1を加算しようと+を呼び出してエラーになっている。
図ではこうなる。

f:id:yuroyoro:20140216195813p:plain


ここで、関数がnilを返した場合はその計算は失敗したと仮定する。よって、関数gと関数hでは、引数がnilであるかチェックするように変更する。

# g, hに引数がnilかチェックを追加した
g = lambda{|x| return nil if x.nil?; x + 1 } 
h = lambda{|x| return nil if x.nil?; x * 2 }

i = f >> g >> h

i.([]) # => nil


例外も発生せず、めでたしめでたし……ではない。関数gとiにそれぞれnilをチェックする処理が重複して実装されているのでDRYではない。合成する関数がもっと多くなった場合は面倒だ。
できればこのnilをチェックする処理を共通化したい。

関数合成に細工する


関数を合成するときに、「nilかどうか判定する処理」を間に挟むようにすれば、個々の関数にわざわざnilチェックを実装せずともよい。
以下のように関数合成時に細工を行うProc#>=を実装する。

class Proc
  def >=(g)
    lambda{|x|
      res = self.call(x)
      # 計算結果がnilならば、後続の関数gを呼び出さずにnilを返す
      return nil if res.nil?
      g.call(res)
    }
  end
end


これで、Proc#>=を使って細工された関数合成を行うことで、計算が途中で失敗した場合は以降の計算を打ち切るようにできる。

f = lambda{|arr| arr.first }
g = lambda{|x| x + 1 }
h = lambda{|x| x * 2 }

# Proc#>=で合成する
i = f >= g >= h

i.([3,5]) # => 8
i.([])    # => nil


これは、図にするとこのようなイメージである。

f:id:yuroyoro:20140216195812p:plain


合成する関数がどれだけ増えようと問題がない。

j = lambda{|x| x * 3 }

# 新たに関数jを合成
k = f >= g >= h >= j

k.([3,5]) # => 24

k.([]) # => nil


こんどこそめでたしめでたし。

文脈付き関数合成


Proc#>=によって、関数合成の際に細工をすることで、「途中で計算が失敗したら打ち切る関数合成」を実現できた。
では、nilチェックのような「細工」を任意に指定できるようにしてはどうだろうか?


たとえば、「計算の途中結果をputsで標準出力に吐く」関数合成をしたいとする。
そのために、どのような「細工」をするかを設定するProc#liftメソッドを用意しよう。

class Proc

  def lift(ctx)
    # 引数の「細工」を行うProcオブジェクトをインスタンス変数に設定しておく
    @ctx = ctx

    # 自身の>=メソッドを定義する(特異メソッド)
    def self.>=(g)
      lambda{|x|
        # ctxに、合成する関数gと、自身の計算結果を渡して処理を「細工」する
        @ctx.call(g, self.call(x))
      }.lift(@ctx) # liftの戻り値のProcオブジェクトも同様にliftしておく
    end

    self
  end
end


少々トリッキーな実装なので解説すると、Proc#liftメソッドは細工を行うProcオブジェクト(ctx)を受け取る。
このProcオブジェクト(ctx)は、第一引数にProc#>=メソッドで渡された合成先の関数g、第二引数に合成元の関数fの計算結果であるxを受け取るようにしておく。


liftメソッド内では、特異メソッドとしてProc#>=メソッドを定義する。Proc#>=はインスタンス変数として記憶してあるctxに、合成する関数gと、自身の計算結果を渡して処理を「細工」するようなlambdaを返す。
なお、続けて>=で合成をチェーンできるように、戻り値として返すlambdaも同様に`lift`しておく。


これで準備はできた。
「計算の途中結果をputsで標準出力に吐く」細工を行うctxは、以下のように書く。

ctx = lambda{|g,x|
  # 引数の関数gを呼び出す
  res = g.call(x)  

  # 結果を出力する
  puts "g(#{x}) -> #{res}"

  res
}


では、実際に上記のctxをProc#liftメソッドに渡して、できあがった合成関数を呼び出してみよう。

# Proc#liftで関数合成に細工する
i = f.lift(ctx) >= g >= h

i.call([3,5])
# g(3) -> 4 # ctxから出力
# g(4) -> 8 # ctxから出力
# => 8


関数gと関数hの呼び出しの後に、標準出力へ結果が出力されていることがわかる。
これは、図にするとこのような感じだ。

f:id:yuroyoro:20140216195811p:plain


先ほどの「nilならば計算を途中で打ち切る」細工は、ctxを以下のように定義すればいい。

ctx = lambda{|g, x|
  # nilならばgを呼び出さずにnilを返して後続の計算を打ち切る
  return x if x.nil? 
  g.call(x)
}


これで、先ほどと同じように動作する。

i = f.lift(ctx) >= g >= h

i.call([4,8]) # => 10

i.call([]) # => nil


この細工を行う関数合成は、前回、前々回の内容を一般化したものだ。

「関数型Ruby」という病(4) - Applicativeスタイル(的ななにか) - ( ꒪⌓꒪) ゆるよろ日記
「関数型Ruby」という病(5) - Object#tryはMaybeモナドの夢を見るか? - ( ꒪⌓꒪) ゆるよろ日記

モナ……


さて、Proc#liftで「細工」を指定することで、様々な細工を関数合成に施すことができるようになった。
ここで、もう一度図を見直してみよう。

f:id:yuroyoro:20140216195810p:plain


先ほどの「nilならば計算を途中で打ち切る」細工は、「失敗するかもしれない計算」という【文脈】上で関数合成が動作しているように見える

f:id:yuroyoro:20140216195809p:plain


「標準出力に吐く」細工は、「結果を出力しながらする計算」という【文脈】上で関数合成が動作しているように見える。 あるいは、関数合成の下に「細工」が【配管】されているように見える。


「細工」を設定するメソッドを`lift`と名付けたのは、実は関数合成を【文脈】上に「持ち上げる」という意味を込めているからだ。


ここで上げたほかにも様々な文脈が考えられる。「外部から環境を与えられる文脈」「複数の結果を組み合わせる文脈」「非同期で計算する文脈」など……。


あれ、それってモナ……。おっと誰か来たようだ。


実際、モナ……則どころかモナ……の形すらしていない(returnもbindもない)のでモナ……ではないのだが、よくわからないと評判のアレも実はこういう配管をやるためのデザインパターンの一種である、と捉えると必要以上に恐怖を覚えずとも済む。

f:id:yuroyoro:20140216221452j:plain

Proc#ymsr


なお、このProc#liftは拙作lambda_driver.gemに実装されており、liftは別名`ymsr`にaliasされている。

Aliased Proc#lift to Proc#ymsr · 953d5d9 · yuroyoro/lambda_driver · GitHub

f:id:yuroyoro:20140216221647j:plain

gitでブランチを切り替えた時に何かする(例えばrbenvでRubyのバージョンを切り替えたり)

タイトルの通りのことをやりたかったっぽいので。


例えば、現在のRubyのバージョンはREE 1.8.7だけど、次回リリースからは1.9.3にあげることになっている場合なんか、masterブランチはREE使うけどdevelopブランチは1.9.3で動作させる必要があるっぽいけど、checkoutするたびにrbenv localとかするのダルいしよく忘れるので全力回避したいっぽいです。


で、どうやるかというと、gitのhookでpost-checkoutというのがあり、そこに色々書くとふんわりとやってくれる風味っぽい。


gitリポジトリの.git/hooks/post-checkout をこんな風に書いておくとよいっぽい。

#!/bin/sh

# Change ruby version
CURRENT_BRANCH=`git rev-parse --abbrev-ref HEAD`

RUBY_VERSION=`git config branch.${CURRENT_BRANCH}.ruby.version`
if [ $? -ne 0 ]; then
  RUBY_VERSION=`git config --global ruby.version`
fi

BUNDLE_GEMFILE=`git config branch.${CURRENT_BRANCH}.ruby.gemfile`
if [ $? -ne 0 ]; then
  BUNDLE_GEMFILE=`git config --global ruby.gemfile`
fi

if [ -n "${RUBY_VERSION}" ]; then
  echo "  change local ruby version to ${RUBY_VERSION}"
  rbenv local ${RUBY_VERSION}
fi

if [ -n "${BUNDLE_GEMFILE}" ]; then
  echo "  set gemfile to ${BUNDLE_GEMFILE}"
  export BUNDLE_GEMFILE=${BUNDLE_GEMFILE}
fi


使用するRubyのバージョンは、こんな風にglobalな設定と、ブランチ毎の設定をそれぞれやっておく

# globalな設定(systemのrubyを使うっぽい)
git config --gobal ruby.version system

# developの設定
git config --gobal branch.my_cool_branch.ruby.version 1.9.3-p448
git config --gobal branch.my_cool_branch.ruby.gemfile ~/dev/awesome_rails_app/Gemfile.1.9.3

# my_cool_branchの設定
git config --gobal branch.my_cool_branch.ruby.version  2.0.0-p195
git config --gobal branch.my_cool_branch.ruby.gemfile ~/dev/awesome_rails_app/Gemfile.2.0.0


これで、git checkoutした時にpost-checkoutが走って、設定されたバージョンにrbenvで切り替えるっぽい

$ git checkout develop
Switched to branch 'develop'
  change local ruby version to 1.9.3-p448
  set gemfile to ~/dev/awesome_rails_app/Gemfile.1.9.3
$ ruby -v
ruby 1.9.3p448 (2013-06-27 revision 41675) [x86_64-linux]


Rubyのバージョン切替以外に、ブランチ毎になんかしたかったらpost-checkoutに書くとよいっぽい。

tmuxでマウス(trackpad)でバッファをスクロールする

お役立ち情報です。



.tmux.confにこのように書くとよいそうです。

set-window-option -g mode-mouse on


iTerm2で、以下のようにxterm mouse reportingを有効にしておきます。

f:id:yuroyoro:20130930172828p:plain


さっそく設定。

yuroyoro/dotfiles https://github.com/yuroyoro/dotfiles/commit/4c6eb8520d878867fcf1a685991067d56ff84cb9

Rubyはいつも俺に新鮮な驚きを提供してくれる

こんなコードがあった。 blockの評価結果が偽だったらエラーにする、という意図だと。

def die!(&block)!
  yield || raise("error")
end

実行

irb(main):008:0> die! { true }
RuntimeError: error
	from (irb):6:in `die!'
	from (irb):8
	from /usr/local/var/rbenv/versions/1.9.3-p448/bin/irb:12:in `<main>'

irb(main):009:0> die! { false }
=> true

( ゚д゚) ?!


正しくはこうですね

def die!(&block)
  yield || raise("error")
end

syntax errorにはなりませんか。そうですか……。