こちらの投稿のときに作成した、ライブラリは新たに
Hogen
という名前に変更になっています。
詳細に関しては近日公開させてもらうので、よろしければそちらを参照して頂けると幸いです。
最近プロダクションコードはJava、ビルドツール(Gradle)、テストコード(Spock、Geb)の組み合わを使うようになってGroovyを使う機会が増えてきました。
JavaでのDBのテストデータ作成はDbSetupが楽
そんな中こちらの記事を拝見させてもらいました。
Groovyで似たようなinsertをしようとすると以下のようになります。
Sql sql = Sql.newInstance("jdbc:h2:mem:", "org.h2.Driver")
def table = sql.dataSet('item_master')
table.add(id:1, name:'Apple', price:500)
table.add(id:2, name:'Orange', price:250)
// ...
これでも十分分かりやすいのですが、どうせならSpockのデータテーブル構造にしたかったので、簡単なDSLを書いてみました。
使い方は以下のような感じです。
Sql h2 = Sql.newInstance("jdbc:h2:mem:", "org.h2.Driver")
TableSetup.insert h2, {
table 'item_master'
rows {
id | name | price
1 | 'Apple' | 500
2 | 'Orange' | 250
}
}
// -> insert into item_master (id, name, price) values (1, Apple, 500)
// insert into item_master (id, name, price) values (2, Orange, 250)
// ...
個人差はあると思いますが、もとのaddメソッドより直感的にテーブルが記述出来ます。
全てのソース:Github
仕組み
TableSetup#insertの第一引数にSqlインスタンスを渡し、それ以降はtableとrowsを定義したクロージャを可変長引数で渡せるようにしているので、続けてクロージャを渡せば複数テーブルにも対応します。
class TableSetup {
def static insert(Sql sql, @DelegatesTo(TableInsertHandler) Closure... cls) {
cls.each {
def tableInsert = new TableInsertHandler(sql: sql)
def handler = it.rehydrate(tableInsert, this, this)
handler.resolveStrategy = Closure.DELEGATE_ONLY
handler()
}
}
static class TableInsertHandler {
Sql sql
String table
def table(String table) { this.table = table }
def rows(Closure cl) {
TableParser.asTable(cl).toMapList().each {
sql.dataSet(table).add(it)
}
}
}
}
Spockで使われてるデータテーブル型はorの演算子オーバーロードを行いListに変換するような構造になっています。
class TableParser {
private static ThreadLocal<List> context = new ThreadLocal<List>()
static or(self, arg) {
appendRow(self, arg)
}
static appendRow(value, nextValue) {
def row = new Row(values: [value])
context.get().add(row)
row.or(nextValue)
}
static asListOfRows(Closure tableData) {
context.set([])
use(TableParser) {
tableData.delegate = new PropertyColumnConverter()
tableData.resolveStrategy = Closure.DELEGATE_FIRST
tableData()
}
context.get()
}
static asTable(Closure tableData) {
def list = asListOfRows(tableData)
new Table(list.head().values, list.tail())
}
}
class PropertyColumnConverter {
def getProperty(String property) {
new Column(name: property)
}
}
@ToString
class Column {
String name
}
@ToString
class Row {
List values = []
def or(arg) {
values.add(arg)
this
}
}
Groovyは最近使い始めたところですがJavaと比べてシンプルな記述や、メタプログラミングのしやすさなどこういった時に非常に便利で楽しいですね!