Properties in Groovy base scripts

287 views Asked by At

I have a DSL where, if present, a closure called before will be called before every command. In my setup I have 3 files: The script itself - Script, a ScriptBase, that is 'attached' to the script via a CompilerConfiguration, and a Handler.

In the script I may or may not have a closure called before.

before = {
    //Do stuff.
}

Notice the lack of a type declaration, or def. If I understand Groovy correctly, this means that before is a in the binding, and accessible from outside code when evaluated with GroovyShell.evaluate().

In the ScriptBase I do the following:

class ProductSpecificationBase extends Script {
    def before = null
}

This script base may or may not be overridden later on. Then, in the Handler, I'm doing a check for whether a before closure is defined in the script:

def config = new CompilerConfiguration()
config.setScriptBaseClass(ScriptBase.class.name)

def shell = GroovyShell()
evaluatedScript = shell.evaluate(new File(thePathToScript))

if (evaluatedScript.before) { 
    theEvaluationOfMyScript.before()
}

The code works as expected if the script does contain a before closure, but if it doesn't it returns a MissingPropertyException. I've had a look at what this means, and it seems that my before in the ScriptBase isn't considered a property, and all the examples of using these ScriptBases I've found on the internet give examples of using methods. This is not feasible for my use case I'm afraid. How can I ensure that the closure in the ScriptBase is considered a property instead of a field(as I am assuming it is now).

To be paraphrase: I would like my code to not execute the if block if the script does not contain a before closure as well as not having been overridden in an extension of the ScriptBase. However, I would like the evaluation of evaluatedScript.before to be false as it is an empty/null Closure (i.e. it went all the way up to ScriptBase, and found the null closure) I like to avoid a try/catch approach if possible.

1

There are 1 answers

5
cfrick On

in your example you would basically call the getter for the before property. To check, if there is a method with the name (and params) check with respondsTo. To see, if there is a property at all with that name use hasProperty (Thanks @dmahapatro for pointing this out)

class X {
    void before() { println 'x' }
}

class Y { }

class Z {
    def before = { println 'z' }
}

def x = new X()
def y = new Y()
def z = new Z()

assert x.respondsTo('before', null)
assert !y.respondsTo('before', null)
assert !z.respondsTo('before', null)

assert !x.hasProperty('before')
assert !y.hasProperty('before')
assert z.hasProperty('before')

x.before()
z.before()