How to make configslurper delegate method call to caller class - groovy

I want to create a groovy class og script like this:
//...
def slurper = new ConfigSlurper().parse(someFile)
//...
//The exact method declaration
def methodCall(def arg){
//Whatever i want to do
}
//Maybe it is easier with methodMissing
def methodMissing(def args) {
//Whatever i want to do
}
The file to slurp could look like this:
some {
property = methodCall("with arg")
}
The question is how i can delegate the "methodCall" to the class or script that parses with the configslurper? At the moment it will give you a methodMissing.

I thik that this blog post have an example of what are you trying to do. It's more complicated than a methodMissing but can be done.

Thanks to Sérgio Michels link I found a solution:
public class ScriptWithMethods extends Script {
String scriptText;
public ScriptWithMethods(File file) {
scriptText = file.text
}
public void run() {
GroovyShell shell = new GroovyShell();
Closure closure = shell.evaluate("{it->$string}");
closure.resolveStrategy = Closure.DELEGATE_FIRST
closure.delegate = this
closure.call()
}
def methodCall(def arg){
//Whatever i want to do
}
}
//...
def script = new ScriptWithMethods(scriptText:someFile)
def slurper = new ConfigSlurper().parse(script)
Of cause you could also use "method missing", but this works in my usecase

Related

Groovy Static Scope

I'm attempting to run this script. If I pull it all out of the class, it runs fine. When I wrap it in my "Test" class, I get this error:
Apparent variable 'json_users' was found in a static scope but doesn't
refer to a local variable, static field or class
class Test{
def testProc(JsonBuilder json_List) {
println json_List.prettyPrint
}
public static void main(String[] args){
def query = 'Select * from mytable'
def resultset = sql.rows(query)
json_users = new JsonBuilder(users:resultset)
testProc(json_users)
}
}
Any idea why I'm receiving this error?
There are multiple things wrong. One is that you cannot invoke an instance method from a static method. Another is that you can't refer to json_users without declaring it (you can in a script, but not in a class).
Instead of this:
class Test {
def testProc(JsonBuilder json_List) {
println json_List.prettyPrint
}
public static void main(String[] args){
def query = 'Select * from mytable'
def resultset = sql.rows(query)
json_users = new JsonBuilder(users:resultset)
testProc(json_users)
}
}
Try this:
class Test {
static testProc(JsonBuilder json_List) {
println json_List.prettyPrint
}
static void main(args){
def query = 'Select * from mytable'
// you will have to declare and initialize
// the sql variable.
def resultset = sql.rows(query)
def json_users = new JsonBuilder(users:resultset)
testProc(json_users)
}
}

Groovy: Got StackOverflowError when override invokeMethod with a closure

I'm trying to enhance my Grails project with Groovy AOP approach. However I always got StackOverflowError if I override invokeMethod with a closure. Here is my test code, I can reproduce the error with groovy 2.1.3, thanks!
class A implements GroovyInterceptable
{
void foo(){
System.out.println( "A.foo");
}
}
class B extends A
{
void foo(){
System.out.println( "B.foo");
super.foo();
}
}
def mc = B.metaClass;
mc.invokeMethod = { String name, args ->
// do "before" and/or "around" work here
try {
def value = mc.getMetaMethod(name, args).invoke(delegate, args)
// do "after" work here
return value // or another value
}
catch (e) {
// do "after-throwing" work here
}
}
B b = new B();
b.foo();
Looks like, if you have a call to super() then metaClass uses the cache to find the method and throws a StackOverflow eventually. In that case if you metaClass A instead of B, it all works fine.
def mc = A.metaClass
I can infer it this way, class implementing GroovyInterceptable directly should override invokeMethod.
#Source MetaClassImpl

Load groovy classes/scripts dynamically without compilation?

I have a set of groovy scripts in package hierarchy. I have 1 main script, from which I want to call others. For example I have these scripts (with public classes/interfaces of the same name in them):
package.MainScript
package.MyInterface;
package.utils.MyInterfaceImpl1 //implements MyInterface
package.utils.MyInterfaceImpl2 //implements MyInterface
Is there a way to call one script from the other without knowing called class name at compile time? I mean to do something like dynamic class loading like:
class MainScript {
public static void main (String[] args) {
MyInterface instance = Class.forName("package.utils.Util1");
}
}
Yeah! Groovy is a dynamic language. You can create class instance dynamically.
package.MyInterface
class MyInterfaceImpl1 {
def greet() {
"Hello"
}
}
package.MyInterface
class MyInterfaceImpl2 {
def greet() {
"Hi!"
}
}
def name = 'MyInterfaceImpl1' // Choose whatever you want at runtime
def className = Class.forName("MyInterface.$name")
def instance = className.newInstance()
assert instance.greet() == 'Hello'

groovy generic fluent builder

I'd like to create a simple wrapper, which would allow calling objects methods as a fluent interface. I've been thinking about rewriting methods of a class upon creation, but this doesn't seem to work. Is this possible in some way with groovy metaprograming?
I have this kind of code snippet so far:
class FluentWrapper {
def delegate
FluentWrapper(wrapped) {
delegate = wrapped
delegate.class.getMethods().each { method ->
def name = method.getName()
FluentWrapper.metaClass."$name" = { Object[] varArgs ->
method.invoke(wrapped, name, varArgs)
return this
}
}
}
def methodMissing(String name, args) {
def method = delegate.getClass().getDeclaredMethods().find { it.match(name) }
if(method) {
method.invoke(delegate,name, args)
return FluentWrapper(delegate)
}
else throw new MissingMethodException(name, delegate, args)
}
}
Assuming example Java class:
class Person {
void setAge()
void setName()
}
I'd like to be able to execute the following piece of code:
def wrappedPerson = new FluentWrapper(new Person())
wrappedPerson.setAge().setName()
I'm using Groovy 1.6.7 for this.
This is all Groovy, and I'm using 1.8.6 (the current latest), but given this Person Class:
class Person {
int age
String name
public void setAge( int age ) { this.age = age }
public void setName( String name ) { this.name = name }
public String toString() { "$name $age" }
}
And this FluentWrapper class:
class FluentWrapper {
def delegate
FluentWrapper(wrapped) {
delegate = wrapped
}
def methodMissing(String name, args) {
def method = delegate.getClass().declaredMethods.find { it.name == name }
if(method) {
method.invoke( delegate, args )
return this
}
else throw new MissingMethodException(name, delegate, args)
}
}
Then, you should be able to do:
def wrappedPerson = new FluentWrapper(new Person())
Person person = wrappedPerson.setAge( 85 ).setName( 'tim' ).delegate
And person should have the age and name specified
I find #tim_yates' answer nice, but you couldn't access delegate methods' return values (something one usually likes doing, even for Builders in the case of build() :)
Moreover, if this wasn't intended for a Builder but for an object with a chainable interface (like that of jQuery wrapped objects in JS), it would be a serious issue.
So I'd put the wrapper like this:
class FluentWrapper {
def delegate
FluentWrapper(wrapped) {
delegate = wrapped
}
def methodMissing(String name, args) {
def method = delegate.getClass().declaredMethods.find { it.name == name }
if(method) {
def result = method.invoke(delegate, args)
return result != null ? result : this
}
else throw new MissingMethodException(name, delegate, args)
}
}
Note the elvis operator is unsuitable since a falsy value would never get returned.
Of course, it's up to the invoker to know wether a method is chainable or not, but that could be overcome with method annotations if neccesary.

How to let code in function to "know" that they are run in context of response in groovy?

I call some cloud that allows to execute groovy scripts.
I return data as xml from this scripts.
I use code like this:
def writer;
def xml;
writer = new StringWriter();
xml = new MarkupBuilder(writer);
xml.Response() {
node('arrtibute1': value4arrtibute1);
}
But I need to use more sophisticated way to calculating values. I want to put a lot of different nodes in this.
def writer;
def xml;
writer = new StringWriter();
xml = new MarkupBuilder(writer);
xml.Response() {
Function1();
Function2();
}
...
and implementations of this functions.
public void Function1(){
node1('arrtibute1': value4arrtibute1);
}
public void Function2(){
someOtherNode1('arrtibute1': otherValue4arrtibute1, ...);
}
Latest code doesn't work. The reason why it doesn't work is functions don't know that they run in context of response and looking for methods node1 and someOtherNode1.
When I try to pass xml in functions and try to create new response there I have deformed structure of xml document (document in document).
My question: how to let code in function to "know" that they are run in context of response?
You need to pass the builder into the functions you are calling like so:
import groovy.xml.MarkupBuilder
value4arrtibute1 = 'val1'
otherValue4arrtibute1 = 'val2'
public void function1( MarkupBuilder builder ){
builder.node1('arrtibute1': value4arrtibute1 )
}
public void function2( MarkupBuilder builder ){
builder.someOtherNode1('arrtibute1': otherValue4arrtibute1 )
}
String output = new StringWriter().with { writer ->
new MarkupBuilder(writer).with { xml ->
xml.Response() {
function1( xml )
function2( xml )
}
}
writer
}
println output
#tim_yates is correct in his answer, although I'd like to share another method of accomplishing the same sort of thing without having to pass the builder or delegate around.
In practice I usaually would make both function1 and function2 Closures and set their delegates to the builder.
import groovy.xml.MarkupBuilder
value4arrtibute1 = 'val1'
otherValue4arrtibute1 = 'val2'
Closure function1 = {
node1('arrtibute1': value4arrtibute1 )
}
Closure function2 = {
someOtherNode1('arrtibute1': otherValue4arrtibute1 )
}
String output = new StringWriter().with { writer ->
new MarkupBuilder(writer).with { xml ->
xml.Response() {
firstNode()
xml.with function1
xml.with function2
}
}
writer
}
println output

Resources