Tuesday, November 26, 2013

How about some grapes with that Groovy script

Recently, I created my first Groovy script. Mind you, I've been tinkering with Groovy for the past couple of weeks, but most of the stuff I've been doing has involved just a few lines of code. So, when it came time to create a program that reads in data from a CSV file and prints out each column and its associated value in a readable manner, I turned to Groovy.

After some googling, I came across the groovycsv library. The groovycsv library relies on OpenCSV and makes reading and writing to/from CSV files easy. Don't believe me, well, check out this example from the groovycsv site:
def csv = '''Name,Lastname
Mark,Andersson
Pete,Hansen'''

def data = parseCsv(csv)
for(line in data) {
    println "$line.Name $line.Lastname"
}
Yet, if I wanted to run this script "as is", it would fail to work because the groovycsv library is not a part of the class path or some such nonsense ;-).  Well, a closer look at the example provided by the groovycsv team suggests that the following 2 lines of code should be included with the script.
@Grab('com.xlson.groovycsv:groovycsv:1.0')
import static com.xlson.groovycsv.CsvParser.parseCsv
Wait ... what?!?!  What's this @Grab thing?  Well, after some further googling, it turns out that Groovy scripts support Maven dependencies.  These can be included using the @Grab annotation.  The syntax is quite similar to Maven's groupId, artifactId and version call outs -- the difference being each value is separated by a colon ( : ).

The first time the script is executed, the dependency is retrieved from Maven (at run-time) and then its made available to the script by supplying the correct import statement.  Kinda cool.  But, what if you've got a number of dependencies that are required?

Are you hungry for grapes?  Grape stands for Groovy's Advanced Package Engine.  It's the mechanism that provides a Groovy script with the capability to grab a Maven dependency.   With respect to the example code above, even after adding the groovycsv dependency, the script would fail due to a missing CSVReader class. The CSVReader class is code that is contained within the OpenCSV library.  Here's how to overcome this issue using Grape:

@Grapes(
        [@Grab('net.sf.opencsv:opencsv:2.3')
        , @Grab('com.xlson.groovycsv:groovycsv:1.0')]
)
import static com.xlson.groovycsv.CsvParser.parseCsv

That's it!  The next time the script is run, each of the noted dependencies will be grabbed from Maven and placed within ~/.groovy/grapes.  Now, all dependencies are satisfied.  Even better -- if you share this script with someone else or you run it on a different platform, Groovy should grab what you need.