THINKING MEGANE

Go言語でActiveRecordライクなORMをつくった

Goで DataMapperじゃなく、ActiveRecordライクにDB操作したいと思ってつくってみました。

go/parsergo/astでソースを解析、個々の構造体ごとにARなコードを生成します。


argen

ActiveRecord** Gen**eratorでargenです。

クイックスタート

テーブルを表す構造体に+ARアノテーションをマークします。

//+AR
type User struct {
	Name string
}

ファイルに対してargenコマンドを実行します。

$ argen xxx.go

ActiveRecordライクなメソッドが定義された*_gen.goが出力されます。あとはそれを使ってコード書いていくだけです。

こんな感じで使います。

db, _ := sql.Open("sqlite3", "foo.db")
Use(db)

u := User{Name: "test", Age: 20}
u.Save()
//// INSERT INTO users (name, age) VALUES (?, ?); [test 20]

User{}.Where("name", "test").And("age", ">", 20).Query()
//// SELECT users.id, users.name, users.age FROM users WHERE name = ? AND age > ?; [test 20]

アソシエーション

構造体に関連を表すメソッドを定義しておくことでアソシエーション系のメソッドを出力します。

func (m User) hasManyPosts() *ar.Association {
	return nil
}

こんな感じで使います。

user, _ := User{}.Create(UserParams{Name: "user1"})
user.BuildPost(PostParams{Name: "post1"}).Save()

// Get the related records
user.Posts()
//// SELECT posts.id, posts.user_id, posts.name FROM posts WHERE user_id = ?; [1]

// Join
user.JoinsPosts()
//// SELECT users.id, users.name, users.age FROM users INNER JOIN posts ON posts.user_id = users.id;

バリデーション

構造体に検証ルールを表すメソッドを定義しておくことでバリデーション系のメソッドを出力します。

func (u User) validatesName() ar.Rule {
	// Define rule for "name" column
	return ar.MakeRule().Format().With("[a-z]")
}

バリデーションは構造体のSave時に実行されるように組み込まれます。エラーは以下のように扱うことができます。

u := User{Name: "invalid name !!1"}
_, errs := u.IsValid()
if errs != nil {
	fmt.Errorf("%v\n", errs.Messages)
	//// map[name:[is invalid]]
}

OnCreateなどの実行トリガー、独自バリデーションにも対応しています。

go generate

Go1.4から導入されたgo generateを使うことで複数ファイルに対して一括でargenを適用させることができます。

ファイルの先頭に以下を定義。

//go:generate argen

あとは定義を変更したときにgo generateコマンドを実行すればOK。


ジェネレーターか〜

はい。

「設計書からアプリケーションを全て生成します」みたいなものではなくて、最小限の定義に対して定型的なコードを生成してくれるという範囲で扱う分には悪いものではないと思っています。

特にGo言語だと現時点でジェネリクスがサポートされていないため、今回のような任意の構造体に共通のメソッドを持たせつつ、扱える型は個々のものにするといった場合は、構造体の埋込やインターフェースだと呼び出し側の型アサーションが必要になるので、ジェネレーターを利用してしまうのもひとつのやり方かなと思います。

(任意の型に対応するためにリフレクションを使うこともできますが、残念ながら実行速度に影響が出ますし、実行時エラーの可能性がどうしても高くなってしまいます)

このあたり、よりよいやり方についてコメントもらえるとうれしいです。


argen、まだまだライブラリとしては不足がありますが、よければご利用ください。PRお待ちしております。

このエントリーをはてなブックマークに追加