The Problem
A coworker and I spent a while on Thursday evening beating our heads against this issue. We’d written a few functions in a separate file so that we could get them working without affecting the rest of what we’d done. Once it was working, we went to use it elsewhere in the project, but were stuck with how to make it available to our script.
Take the following function:
getThing.groovy
def getThingList() { return ["thing","thin2","thing3"] }
We want to use it in another script like this:
badPrintThing.groovy
println getThingList()
The result?
$ groovy badPrintThing.groovy Caught: groovy.lang.MissingMethodException: No signature of method: badPrintThing.getThingList() is applicable for argument types: () values: [] groovy.lang.MissingMethodException: No signature of method: badPrintThing.getThingList() is applicable for argument types: () values: [] at badPrintThing.run(badPrintThing.groovy:2)
Exploration
Obviously, the error message isn’t what we wanted out of this, so we tried the following:
import getThing.groovy println getThingList()
More of the same. Tried to ingest the file with something I found over on stackOverflow:
evaluate(new File("getThing.groovy")) println getThingList()
Yet again, no dice.
Solution
Reflecting on the problem that evening, I recalled that every groovy script is more or less a class with the name of the file in which the script resides ((This behavior is hinted at in the Scripts and Classes section of the Groovy documentation: “If you compile the above script to bytecode using groovyc, you get a single class named after the name of the script. e.g. if this was saved in Foo.script you’d get a Foo.class file. You can run this Java code on the command line (assuming you’re classpath has groovy.jar and asm.jar) … This will execute the autogenerated main(String[] args) method in the bytecode which instantiates the Foo class, which extends the class and then call its run() method.”)). With that in mind, and the observation that all the files in the same folder will be part of the same package and thus available to each other without any work, I ended up with the following, which worked as I’d hoped:
printThing.groovy
thing = new getThing() println thing.getThingList()
$ groovy printThing.groovy [thing, thin2, thing3]
Final Thoughts
The naming of the getThing class is bad form, now that we’re actually treating it like a class. If you’re going to write something like this, keep in mind how you’ll be using it and give the file a name more like ThingGetter.
We ended up using this arrangement several times in the project, which suggests to me that there might be a pattern to tease out of this. We have a copy of Design Patterns around the office, so this should probably be my next stop.