Groovy Tables is a groovy library which allows you to create lists of objects using a table like grammar. It was written primarily for use when writing test cases.
repositories {
mavenCentral()
}
dependencies {
testCompile group: 'org.tools4j', name: 'groovy-tables', version: '1.6'
}
<dependency>
<groupId>org.tools4j</groupId>
<artifactId>groovy-tables</artifactId>
<version>1.6</version>
<scope>test</scope>
</dependency>
The following example creates a list of Book objects
List<Book> books = GroovyTables.createListOf(Book.class).fromTable {
author | title | cost | year
"Jane Austen" | "Pride and Prejudice" | 12.95 | 1813
"Harper Lee" | "To Kill a Mockingbird" | 14.95 | 1960
"F. Scott Fitzgerald" | "The Great Gatsby" | 12.95 | 1925
"Charlotte Brontë" | "Jane Eyre" | 6.95 | 1847
"George Orwell" | "1984" | 8.95 | 1949
"J.D. Salinger" | "The Catcher in the Rye" | 6.95 | 1951
"William Shakespeare" | "Romeo and Juliet" | 5.95 | 1597
}
If you wish to, you can create your own reusable constructor method, giving you an even more concise syntax, e.g:
private List<Book> books(Closure closure){
return GroovyTables.createListOf(Book.class).fromTable(closure)
}
...and in your test method:
List<Book> books = books {
author | title | cost | year
"Jane Austen" | "Pride and Prejudice" | 12.95 | 1813
"Harper Lee" | "To Kill a Mockingbird" | 14.95 | 1960
"F. Scott Fitzgerald" | "The Great Gatsby" | 12.95 | 1925
"Charlotte Brontë" | "Jane Eyre" | 6.95 | 1847
"George Orwell" | "1984" | 8.95 | 1949
"J.D. Salinger" | "The Catcher in the Rye" | 6.95 | 1951
"William Shakespeare" | "Romeo and Juliet" | 5.95 | 1597
}
Here is another example creating a list of quotes:
List<Quote> quotes = GroovyTables.createListOf(Quote).fromTable {
symbol | price | quantity
"AUD/USD" | 1.0023 | 1200000
"AUD/USD" | 1.0024 | 1400000
"AUD/USD" | 1.0026 | 2000000
"AUD/USD" | 1.0029 | 5000000
}
By default groovytabledsl finds a suitable constructor or static factory method to create instances of the given class. You can give the api a filter to 'force' a certain mode of construction. This example passes a filter which only accepts constructors. e.g.
List<Book> books = GroovyTables.createFromTable(Book.class, ConstructionMethodFilter.CONSTRUCTORS, {
author | title | cost | year
"Jane Austen" | "Pride and Prejudice" | 12.95 | 1813
"Harper Lee" | "To Kill a Mockingbird" | 14.95 | 1960
"F. Scott Fitzgerald" | "The Great Gatsby" | 12.95 | 1925
"Charlotte Brontë" | "Jane Eyre" | 6.95 | 1847
"George Orwell" | "1984" | 8.95 | 1949
"J.D. Salinger" | "The Catcher in the Rye" | 6.95 | 1951
"William Shakespeare" | "Romeo and Juliet" | 5.95 | 1597
});
The filter is a just a Predicate
List<Book> books = GroovyTables.createFromTable(Book.class, ConstructionMethodFilter.filter().withStaticFactoryMethods().withName("create"), {
author | title | cost | year
"Jane Austen" | "Pride and Prejudice" | 12.95 | 1813
"Harper Lee" | "To Kill a Mockingbird" | 14.95 | 1960
"F. Scott Fitzgerald" | "The Great Gatsby" | 12.95 | 1925
"Charlotte Brontë" | "Jane Eyre" | 6.95 | 1847
"George Orwell" | "1984" | 8.95 | 1949
"J.D. Salinger" | "The Catcher in the Rye" | 6.95 | 1951
"William Shakespeare" | "Romeo and Juliet" | 5.95 | 1597
});
The field names (column headings) are only used when the api attempts to call setter methods after constructing an object. So if field names are omitted, the API will simply not attempt to construct an instance using reflection.
A new recently added feature, you can chain a closure at the end of the table, to consume the table. e.g.
GroovyTables.withTable {
side | symbol | price | qty
Side.BUY | "AUD/USD" | 1.0023 | 1200000
Side.BUY | "AUD/USD" | 1.0022 | 1400000
Side.BUY | "AUD/USD" | 1.0020 | 2000000
Side.BUY | "AUD/USD" | 1.0019 | 5000000
Side.SELL | "AUD/USD" | 1.0025 | 1100000
Side.SELL | "AUD/USD" | 1.0026 | 1600000
Side.SELL | "AUD/USD" | 1.0028 | 2020000
}.forEachRow {
quoteBook.getSide(side).add(new Quote(symbol: symbol, price: price, quantity: qty))
}
There is also the option of calling a chained closure, with explicitly defined closure arguments. (For this you must not specify column headings):
GroovyTables.withTable {
Side.BUY | "AUD/USD" | 1.0023 | 1200000
Side.BUY | "AUD/USD" | 1.0022 | 1400000
Side.BUY | "AUD/USD" | 1.0020 | 2000000
Side.BUY | "AUD/USD" | 1.0019 | 5000000
Side.SELL | "AUD/USD" | 1.0025 | 1100000
Side.SELL | "AUD/USD" | 1.0026 | 1600000
Side.SELL | "AUD/USD" | 1.0028 | 2020000
}.forEachRow { Side side, String symbol, double price, int qty ->
quoteBook.getSide(side).add(new Quote(symbol: symbol, price: price, quantity: qty))
}
You can also create simple lists of arrays. e.g.
final List<Object[]> listOfArrays = GroovyTables.createListOfArrays {
1 | 2 | 3
2 | 3 | 5
55 | 5 | 60
}
println listOfArrays
Output:
[[1, 2, 3], [2, 3, 5], [55, 5, 60]]
There are three methods of construction. Class Constructors, Static Factory Methods, and Reflection
Suitable construction methods are analyzed before construction takes place. A decision is then made regarding the most suitable construction method. This decision is made based on:
A construction method is selected separately for each 'line' of the table. In the future we might cache last used construction methods but initial performance testing deemed little benefit was gained in terms of milliseconds of execution.
If you want to debug/understand what groovy-tables is doing, you can turn logging on. Logging at the moment just goes to System.out
org.tools4j.groovytables.Logger.setCurrentLevel(org.tools4j.groovytables.Logger.Level.DEBUG)
Thanks to Christian Baranowski whose blog post here: http://tux2323.blogspot.co.uk/2013/04/simple-table-dsl-in-groovy.html, inspired the fancy usage of operator overloading to achieve the table like grammar.