groovy
Stomping grapes with Groovy
Jan 21st
One of the best new features of Groovy is Grape. It allows a script to connect to Ivy or Maven style repositories and download the dependencies it needs. This lets you bang out a quick script, ship it to another computer, and run it there without having to fiddle around with classpaths, jars, or any other sort of file. I put together a simple demo that shows some of this. The script grabs my RSS feed using ROME and then splits out words longer than 3 characters if they show up more than once. The important part is at the top, however. You can see that I use the Grab annotation to get ROME and Apache Commons Lang. I’m then free to import the classes I need from those jars. Further down, I use an inline Grab to get the Google Collections jar and then create the class I want with a fully qualified name. Pretty cool, I think. You can try it yourself by cutting and pasting this example. You’ll need access to Groovy from your command line or IDE.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | #!/usr/bin/env groovy @Grab(group='rome', module='rome', version='0.9') //Long-style grab @Grab('commons-lang:commons-lang:2.4') // Gradle-style grab import com.sun.syndication.io.SyndFeedInput; import com.sun.syndication.io.XmlReader; import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang.StringUtils; class GrapeTest { public static final int MIN_WORD_LEN = 4; static main(args) { //Inline grab using fully qualified name (no import statement) @Grab('com.google.collections:google-collections:1.0') def words = new com.google.common.collect.TreeMultiset() def feed = getFeed('http://codegoop.com/feed/') getCleanFeedLines(feed).each { line -> line.split(' ').each { word -> if (validWord(word)) { words.add(word) } } } printResult(words) } def static getFeed(url) { def input = new SyndFeedInput() return input.build(new XmlReader(new URL(url))) } def static getCleanFeedLines(feed) { def lines = new ArrayList() feed.entries.each { entry -> entry.contents.each { content -> cleanLine(content.value).each { line -> lines.add(line) } } } return lines } def static validWord(word) { return StringUtils.isNotBlank(word) && word.size() >= MIN_WORD_LEN; } def static printResult(result) { result.elementSet().each { if (result.count(it) > 1) { println it + " found " + result.count(it) + " times" } } } //Only use the code in the <p> blocks //Remove html tags and non-alpha (leaving space) def static cleanLine(str) { def cleanedLines = new ArrayList() def subLines = StringUtils.substringsBetween(str, "<p>", "</p>") subLines.each { subLine -> def line = StringEscapeUtils.unescapeHtml(subLine) line = line.replaceAll("<(.*?)*>", "").replaceAll("[^a-zA-Z\\s]", "") .toLowerCase() cleanedLines.add(line) } return cleanedLines } } |
Running the script gives you something like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | $ ./GrapeTest.groovy (Unix or Cygwin) vs C:\>groovy GrapeTest.groovy (Windows) about found 6 times arent found 2 times around found 2 times automatic found 2 times basically found 2 times been found 6 times better found 3 times bring found 2 times <snip> several more lines </snip> work found 2 times write found 2 times written found 3 times year found 5 times youre found 5 times |
Now, that’s all great, if the libraries you need are in the default repositories, but what if they aren’t? Well, you can either edit ~/.groovy/grapeConfig.xml and add in the repo you want or, even better, use @GrabResolver(name=’myRepoName’, root=’myRepoUrl’) at the top of your script to retain that “copy/paste” the file feature that seems so nice. GrabResolver is new in Groovy 1.7. See the release notes here
