How to parse a Closure into ConfigSlurper - groovy

Background. I'm working on a gradle script and want to inline some default configuration using the ConfigSlurper. I'm already able to parse configurations from files, but can't seem to get the inlined configuration to work.
What i want to do is something like this:
myScript = {
some {
random {
stuff = "You can access"
}
}
}
groovy.lang.Script script = new groovy.lang.Script() {
#Override
Object run() {
theScript.call()
}
}
ConfigSlurper slurper = new ConfigSlurper()
slurper.setBinding(["theScript": myScript])
ConfigObject parse = slurper.parse(script)
assert parse.some.random.stuff == "You can access"
This doesn't work. It says
Caught: groovy.lang.MissingMethodException: No signature of method: scriptStuff.some() is applicable for argument types: (scriptStuff$_run_closure1_closure2) values: [scriptStuff$_run_closure1_closure2#2aca5165]
Possible solutions: dump(), use([Ljava.lang.Object;), sleep(long), wait(), run(), run()
groovy.lang.MissingMethodException: No signature of method: scriptStuff.some() is applicable for argument types: (scriptStuff$_run_closure1_closure2) values: [scriptStuff$_run_closure1_closure2#2aca5165]
Possible solutions: dump(), use([Ljava.lang.Object;), sleep(long), wait(), run(), run()
at scriptStuff$_run_closure1.doCall(scriptStuff.groovy:2)
at scriptStuff$_run_closure1.doCall(scriptStuff.groovy)
at scriptStuff$1.run(scriptStuff.groovy:12)
at scriptStuff.run(scriptStuff.groovy:18)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
(scriptStuff refers to the file i run the pasted script from: scriptStuff.groovy)
So i'm not sure this in the correct direction. Actually it would be nicer if you could just pass the closure directly to the Slurper.
But the Slurper only takes Script, script file, and String.
Another direction would be to convert the clusure to a String. Is this possible?
Any solution would be nice.
Update
Just wanted to elaborate a bit on my use case.
I am creating a plugin to my gradle scripts. As a user i want to write something like this:
projectConfiguration {
//this one is easy to parse with ConfigSlurper
confFile = "some/path/to/configuration.groovy"
//this is difficult to parse
inlineConf = {
some {
inlined {
configuration = "Cool!"
}
}
}
}
So even though i could have parsed it using a string notation, then the notation above looks a lot slicker, and more like the rest of the gradle script.
On the other hand i don't mind the code parsing the clojure beeing ugly. It's only user inside the plugin. The important ting is that the user facing part is clean.

Set the delegate on your Closure to the script with a DELEGATE_FIRST resolve strategy:
class ClosureScript extends Script {
Closure closure
def run() {
closure.resolveStrategy = Closure.DELEGATE_FIRST
closure.delegate = this
closure.call()
}
}
script = new ClosureScript(closure: myScript)
See Creating a Groovy ConfigObject from a Closure.

You can use ConfigSlurper to parse a String like this:
def myScript = '''
some {
random {
stuff = "You can access"
}
}
'''
def config = new ConfigSlurper().parse(myScript)
assert config.some.random.stuff == "You can access"
If you really want to pass a Script, then you must extends Script because it is an abstract class. For example:
class MyConfiguration extends Script {
public Object run() {
some {
random {
stuff = "You can access"
}
}
}
}
def config = new ConfigSlurper().parse(new MyConfiguration())
assert config.some.random.stuff=="You can access"

Related

Groovy Closure methods don't take precedence does not seem to get called when in a class

I just cannot understand how to get closures to work as I would expect them. For example let's say I have a class
class Bar {
public void greeting(String name) {
println "Hello: ${name}"
}
}
When I delegate Bar in a Closure as such:
class Foo {
void helloBob(){
bar {
greeting("Bob")
}
}
def bar(#DelegatesTo(value = Bar, strategy = Closure.DELEGATE_ONLY) Closure cl) {
cl.rehydrate(new Bar(), this, this)
cl.call()
}
static void main(String[] args) {
new Foo().helloBob()
}
}
I get the stacktrace when running:
Exception in thread "main" groovy.lang.MissingMethodException: No signature of method: Foo.greeting() is applicable for argument types: (String) values: [Bob]
Possible solutions: getAt(java.lang.String)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:70)
at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent(PogoMetaClassSite.java:76)
at
at ...
The: No signature of method: Foo.greeting() makes no sense to me because it should be calling Bar.greeting() as it is in a Closure which has #DelegatesTo(value = Bar. Why is the closure not referring to Bar.greeting? How do I get it to do that? My IDE (IntelliJ) seems to think it's Bar.greeting which is what I want, but when I run it I get a stacktrace.
EDIT
Weirdly enough if I remove a whole bunch of type information it then seems to work with this:
Bar bar(closure) {
def bar = new Bar()
closure.delegate = bar
// closure.rehydrate(bar, this, this) // this causes error why?
closure.call()
return bar
}
With the IDE happy and all. I also don't understand why I cannot use rehydrate it seems to cause an error, yet setting the delegate manually is fine.
Rehydrate doesn't modify the closure - it returns a new copy of it. So call the new closure.
cl.rehydrate(new Bar(), this, this).call()
Don't forget to set the resolveStrategy to what you are claiming in the annotation.

GroovyShell and Binding.setVariable() for callable objects

I am experimenting with some dynamic variable creation with GroovyShell and encountered an issue. First, the working code:
static def defVar(def glob) {
glob.setVariable('test', new Test())
}
class MyBinding extends Binding {
}
class Test {
def call() {
println("--- hello ---")
}
}
Binding glob = new MyBinding()
GroovyShell shell = new GroovyShell(glob)
defVar(glob)
shell.parse('test()').run()
This gives me the expected output:
--- hello ---
However, I want to call setVariable() dynamically when getVariable() is called, something like this:
static def defVar(def glob) {
glob.setVariable('test', new Test())
}
class MyBinding extends Binding {
def getVariable(String name) {
if (! hasVariable('test')) {
BindingTest.defVar(this)
}
return super.getVariable(name)
}
}
class Test {
def call() {
println("--- hello ---")
}
}
Binding glob = new MyBinding()
GroovyShell shell = new GroovyShell(glob)
//defVar(glob)
shell.parse('test()').run()
But this fails with the below error:
Caught: groovy.lang.MissingMethodException: No signature of method: Script1.test() is applicable for argument types: () values: []
Possible solutions: getAt(java.lang.String), use([Ljava.lang.Object;), use(java.lang.Class, groovy.lang.Closure), use(java.util.List, groovy.lang.Closure), wait(), wait(long)
groovy.lang.MissingMethodException: No signature of method: Script1.test() is applicable for argument types: () values: []
Possible solutions: getAt(java.lang.String), use([Ljava.lang.Object;), use(java.lang.Class, groovy.lang.Closure), use(java.util.List, groovy.lang.Closure), wait(), wait(long)
at Script1.run(Script1.groovy:1)
at Script1$run.call(Unknown Source)
at BindingTest.run(BindingTest.groovy:23)
When I added tracing code like this:
class MyBinding extends Binding {
def getVariable(String name) {
if (! hasVariable(name)) {
BindingTest.defVar(this)
}
println("getVariable: ${name}: ${super.getVariable(name).getClass().getName()}")
return super.getVariable(name)
}
void setVariable (String name, def val) {
println("setVariable: ${name}: ${val.getClass().getName()}")
super.setVariable(name, val)
}
def getProperty(String name) {
println("getProperty: ${name}: ${super.getProperty(name)}")
return super.getProperty(name)
}
void setProperty (String name, def val) {
println("setProperty: ${name}: ${val.getClass().getName()}")
super.setProperty(name, val)
}
}
In the working case, I get the below output:
setVariable: test: Test
--- hello ---
In the non-working case, I get this output:
setVariable: test: Test
getVariable: test: Test
Caught: groovy.lang.MissingMethodException: No signature of method: Script1.test() is applicable for argument types: () values: []
...
Two questions:
In the working scenario, why is there no getVariable?
In the non-working scenario, why is the Test object returned by getVariable getting rejected?
Note that this issue is specific to callable values. If I set a simple value such as a string, to test, then both approaches work fine. E.g., with this sort of a change:
...
static def defVar(def glob) {
glob.setVariable('test', '--- hello ---')
}
...
shell.parse('println(test)').run()
I get the below identical output with both approaches:
setVariable: test: java.lang.String
getVariable: test: java.lang.String
setVariable: test: java.lang.String
--- hello ---
Though, I am not sure why setVariable gets called twice. I couldn't find any documentation explaining these puzzling behaviors. Could anybody here shed some light on them?
Please note, all the code snippets have been simplified for the ease of demonstrating the problem rather than for their intended purpose
When you use a property as a callable fallback, the Binding.getVariable() method does not get involved. This behavior is controlled by the metaclass, and in your case, it all drives to the execution of the MetaClassImpl.invokePropertyOrMissing() method. This method determines if
test()
should invoke test.call() (in case of an existing property), or should it fallback to the missingMethod() method. Here is what this method implementation looks like:
private Object invokePropertyOrMissing(Object object, String methodName, Object[] originalArguments, boolean fromInsideClass, boolean isCallToSuper) {
// if no method was found, try to find a closure defined as a field of the class and run it
Object value = null;
final MetaProperty metaProperty = this.getMetaProperty(methodName, false);
if (metaProperty != null)
value = metaProperty.getProperty(object);
else {
if (object instanceof Map)
value = ((Map)object).get(methodName);
}
if (value instanceof Closure) { // This test ensures that value != this If you ever change this ensure that value != this
Closure closure = (Closure) value;
MetaClass delegateMetaClass = closure.getMetaClass();
return delegateMetaClass.invokeMethod(closure.getClass(), closure, CLOSURE_DO_CALL_METHOD, originalArguments, false, fromInsideClass);
}
if (object instanceof Script) {
Object bindingVar = ((Script) object).getBinding().getVariables().get(methodName);
if (bindingVar != null) {
MetaClass bindingVarMC = ((MetaClassRegistryImpl) registry).getMetaClass(bindingVar);
return bindingVarMC.invokeMethod(bindingVar, CLOSURE_CALL_METHOD, originalArguments);
}
}
return invokeMissingMethod(object, methodName, originalArguments, null, isCallToSuper);
}
Source: https://github.com/apache/groovy/blob/GROOVY_2_5_X/src/main/groovy/groovy/lang/MetaClassImpl.java#L1262-L1287
Now, pay attention to the branch if (object instanceof Script) and how the binding variable gets retrieved. It tries to retrieve test variable from binding object using:
Object bindingVar = ((Script) object).getBinding().getVariables().get(methodName);
Your code would work if it was:
Object bindingVar = ((Script) object).getBinding().getVariable(methodName);
instead. But it's not.
You can make your second case working if you override getVariables() method instead of getVariable(String name), for instance:
class MyBinding extends Binding {
#Override
Map getVariables() {
return super.getVariables() + [
test: new Test()
]
}
}
Of course, your final implementation might be much more sophisticated. (E.g. you could get super.getVariables() map first, check which variables are missing and add a default variable only if the initial map was missing given variable.) But this is up to you.
Alternatively, consider using methodMissing instead of the binding variable fallback. It could make your code much easier to read and reason about.

On closures and groovy builder pattern

Starting to grasp closures in general and some groovy features.
Given the following code:
class Mailer {
void to(final String to) { println "to $to" }
void from(final String from) { println "from $from" }
static void send(Closure configuration) {
Mailer mailer = new Mailer()
mailer.with configuration
}
}
class MailSender {
static void sendMessage() {
Mailer.send {
to 'them'
from 'me'
}
}
}
MailSender.sendMessage()
What happens under the hood when you pass a closure to Mailer.send method?
Does to and from are passed as arguments from the Closure point of view? Which types the Closure maps them?
And then inside the Mailer.send method at the moment the Mailer object calls mailer.with receiving the configuration object, the object maps them into method calls. Groovy does this by reflection?
Groovy can dynamically define the delegate of a closure and even the this object.
with is setting the delegate and executing the closure. This is a verbose way to achieve the same:
def math = {
given 4
sum 5
print
}
class PrintMath {
def initial
def given(val) {
initial = val
}
def sum(val) {
initial += val
}
def getPrint() {
println initial
return initial
}
}
math.delegate = new PrintMath()
math.resolveStrategy = Closure.DELEGATE_ONLY
assert math() == 9
What happens under the hood when you pass a closure to Mailer.send method?
It receives a not-yet-executed block of code.
Does to and from are passed as arguments from the Closure point of view?
No, it is better thinking of them as an anonymous class/lambda in java, or a function(){} in javascript.
Which types the Closure maps them?
None, they are method calls waiting to be executed. They can be delegated to different objects, though.
And then inside the Mailer.send method at the moment the Mailer object calls mailer.with receiving the configuration object, the object maps them into method calls. Groovy does this by reflection?
You can decompile a Groovy class file to see what is going on. IIRC, Groovy currently uses a "reflector" strategy (with an arrayOfCallSite caching) to make calls faster OR it can use invokedynamic.
The closure math in the code above will result in this class:
// .. a lot of techno-babble
public Object doCall(Object it) {
CallSite[] arrayOfCallSite = $getCallSiteArray();
arrayOfCallSite[0].callCurrent(this, Integer.valueOf(4));
arrayOfCallSite[1].callCurrent(this, Integer.valueOf(5));
return arrayOfCallSite[2].callGroovyObjectGetProperty(this);
return null;
}

Groovy DSL: handling labels

I'm implementing in Groovy a DSL for some existing file format.
In this format we have a construct like
group basic_test {
test vplan_testing {
dir: global_storage;
};
};
And here I have problem with this dir: global_storage - groovy considers "dir:" as a label, so I can't handle it.
Do you have an idea how I can receive some callback (getProperty, invokeMissingMethod) for this construct?
Thank you!
I don't believe you can achieve that this way, you need to change your dsl a bit to be able to capture that information. Here's how you could achieve that:
class Foo {
static plan = {
vplan_testing {
dir 'global_storage'
}
}
}
def closure = Foo.plan
closure.delegate = this
closure()
def methodMissing(String name, Object args) {
println "$name $args"
if(args[0] instanceof Closure)
args[0].call()
}
The output will be
dir [global_storage]
or you could defined you dsl this way:
class Foo {
static plan = {
vplan_testing {
test dir:'global_storage'
}
}
}
replace "test" by something meaningful to you domain. In this case the output would be
test [[dir:global_storage]]
Hope this helps
-ken

Groovy Prototype Object

I have a method with an incoming variable, which represents a script.
e.g.
hello.groovy
Foo.init(this)
Foo.groovy
class Foo {
static init(app) {
}
}
What is the best way to add a ton of new functionality to the app variable in the init method? Basically, I would like to add all the functionality of another object to the app object.
For instance, if I had another class:
class Bar {
def a() { }
def b() {
}
}
I would like the app object to basically be a new Bar(). In JavaScript, this is easy by using the prototype object, but I cannot seem to get it working in groovy. What is the best way to accomplish this? Or should I be doing something differently?
YourClass.metaClass.static.yourMethod is the most similar to JS prototype I've seen in Groovy. Check this link out:
Groovy meta-programming - adding static methods to Object.metaClass
Cheers.
There are several ways to do this and each has advantages and disadvantages. On the Groovy Documentation page, the section on Dynamic Groovy illustrates several of these. If I understand you correctly, the simplest way is to just use the metaClass of an instance to add new functionality, a la:
class Foo {
static void init (a) {
a.metaClass.a = { println "a()"; }
a.metaClass.b = { println "b()"; }
}
}
def myObject = new Object();
Foo.init (myObject);
myObject.a();
myObject.b();
The easiest way to do this would be with a mixin. Basically you can call mixin on app's class and pass it another class to incorporate that functionality into it.
I've modified your example to show this mixing in the Bar class.
class Foo {
static init(app) {
app.class.mixin Bar
}
}
class Bar {
def a() { println "a called" }
def b() {
println "b called"
}
}
def app = new Object()
Foo.init(app)
app.a()
app.b()
The output of this would be:
a called
b called
In this case I added Bar to the Object class but you could add it to any class in your application.

Resources