Invoke method in dynamically from groovy command line argument - groovy

I need to be able to map the first string argument "groovy script.groovy firstArgument" to an method invokation.
script.groovy ==
def firstArgument() {
println "test"
}
"$args"()
Does not work.
Any suggestions?

def firstArgument() {
println "test"
}
def methodName = args[0]
You can use invokeMethod:
invokeMethod(methodName, null)
Or dynamic method invocation, thanks to #tim_yates:
"${methodName}"()

Related

How can I use wrapped Groovy shell inside the groovy script?

I have a Java app through which I'm executing a Groovy Script, the problem occurs when my Script again has code to use the groovy shell to execute the Inner script.
something like this
import com.xx.WrappedGroovyShell
import java.lang.*
def scriptString = """
def test(str) {
str.toLowerCase() // This doesn't work as GString can't seem to be treated as a String
}
"""
try {
def script = WrappedGroovyShell.getInstance().getScript("Test1", scriptString)
def script2 = new GroovyShell().parse(scriptString)
def example = 1
def gstring = "OUR VALUE IS ${example}";
println script instanceof GroovyObject // Statement returning false
println script.test(gstring) // This throws an exception groovy.lang.MissingMethodException: No signature of method: org.codehaus.groovy.runtime.GStringImpl.toLowerCase() is applicable for argument types: () values: []
println script2 instanceof GroovyObject // Statement returns true
println script2.test(gstring) //this one works
println "Success"
} catch (ex) {
println ex
}
return 0
The getScript method in WrappedGroovyShell.java is as simple as
public Script getScript(String scriptName, String scriptBody){
return new GroovyShell().parse(scriptBody);
}
I will be thankful if someone let me know why two different behavior.
new GroovyShell() creates a new groovy classloader with parent = GroovyShell.class.getClassLoader()
and if GString.class is unknown for parent classloader - then it will be loaded again.
two GString classes loaded by different classloaders are not equal that's why you could have unexpected exceptions like "method not found for parameter GString" or "can't cast GString to GString"
this code should work because it uses classloader of current groovy class
def scriptText='''
def test(str) {
str.toLowerCase()
}
'''
def gs = new GroovyShell(this.getClass().getClassLoader())
def script = gs.parse(scriptText)
def arg = "GSTRING ${123}"
println script.test(arg)

Is it possible to pass a Groovy closure defined as a variable to a function for execution, with another variable as part of the passed call?

As the title says, if I define a closure that takes one argument can I pass it with an argument to be executed.
For example:
def print = { String NAME -> println "$NAME"}
And then pass it to another function for execution like this:
otherFunction(print("Jeff"))
If the other function has signature:
otherFunction(Closure clos):
clos.call()
Thanks!
I have figured out where I went wrong, I need my function to return a closure with my variable interpolated.
For example:
def Closure print (String NAME){
{name -> println name}
}
Then make a call to generate a custom closure and pass that:
otherFunction(print("Jeff"))
Answered my own question please close.
The problem with the call otherFunction( print("Jeff") ) is that you pass to it the return value of print("Jeff"), which is null because println does not return anything (void function).
Instead of this, you must pass the closure object which is layzy called with the method call(). You figured out yourself this, but my approach is more straightforward. Other solution would be to use function composition:
def print = { println it }
def otherFunction(Closure clos) {
clos.call()
}
// this is equivalent with otherFunction(null)
//otherFunction( print("Jeff") )
// pass a closure object
otherFunction { print("Jeff") }
// using function composition
def printJeff = print << { "Jeff" }
printJeff()

Pass param with .& operator to a static method

With groovy .& operator one can create references to static methods, as in
def static xyz( name='Joe' ) {
println "Hello ${name}"
}
// NOTE: ConsoleScript number part varies
def ref = ConsoleScript52.&xyz
And can be easilly called without params, as
ref() // prints "Hello "
But how can this method be called with params? ref('John') gives an error groovy.lang.MissingMethodException: No signature of method: ConsoleScript52.xyz() is applicable for argument types: (java.lang.String) values: [John]
Note that Groovy does not even use the default value of name param in above example when static method is called with ref.
You are probably using a ConsoleScript instance where you did not define that method with parameters.
In the code below, in the 8th execution in my console (ConsoleScript8) y defined the method hello() without parameters (added the "script number" to make it explicit):
println this.getClass().getName()
println ''
def static hello() {
println "(8) Hello"
}
def h8 = ConsoleScript8.&hello
h8()
h8('Joe')
And it yields the following in the next execution:
ConsoleScript9
(8) Hello
Exception thrown
groovy.lang.MissingMethodException: No signature of method: ConsoleScript9.hello() is applicable for argument types: (java.lang.String) values: [Joe]
And in the 10th execution (ConsoleScript10) I modified it adding the default parameter:
println this.getClass().getName()
println ''
def static hello(name='amigo') {
println "(10) Hello ${name}"
}
def h10 = ConsoleScript10.&hello
h10()
h10('Joe')
println '--'
def h8 = ConsoleScript8.&hello
h8()
h8('Joe')
And it yields:
ConsoleScript12
(10) Hello amigo
(10) Hello Joe
--
(8) Hello
Exception thrown
groovy.lang.MissingMethodException: No signature of method: ConsoleScript8.hello() is applicable for argument types: (java.lang.String) values: [Joe]
It is easier if you use an explicit class:
class Greeter {
def static hello(name='Joe') {
"Hello ${name}"
}
}
def hi = Greeter.&hello
assert hi() == 'Hello Joe'
assert hi('Tom') == 'Hello Tom'
What version of Groovy? This works with Groovy 2.4.5:
class Test {
static greet(name = 'tim') {
"Hello ${name.capitalize()}"
}
}
// regular static calls
assert Test.greet() == 'Hello Tim'
assert Test.greet('kaskelotti') == 'Hello Kaskelotti'
// Create a reference to the static method
def ref = Test.&greet
// Calling the reference works as well
assert ref() == 'Hello Tim'
assert ref('kaskelotti') == 'Hello Kaskelotti'

Is there a way to intercept all method calls in Groovy?

I need to intercept method calls on predefined Java classes. For example, lets say I need to intercept String class split method, how do I do this?
I tried this which works, but I doesn’t want end user to change their code by wrapping their calls in with proxy block.
Is there any way this can be achieved with Groovy?
If what you want to do is intercept a call to a specific method you can do something like this...
// intercept calls to the split method on java.lang.String
String.metaClass.split = { String arg ->
// do whatever you want to do
}
If what you want to do is intercept a call to a specific method and do some stuff in addition to invoking the original (like to wrap the real method with some of your own logic) you can do something like this:
// get a reference to the original method...
def originalSplit = String.metaClass.getMetaMethod('split', [String] as Class[])
// now add your own version of the method to the meta class...
String.metaClass.split = { String arg ->
// do something before invoking the original...
// invoke the original...
def result = originalSplit.invoke(delegate, arg)
// do something after invoking the original...
// return the result of invoking the original
result
}
I hope that helps.
you want to use MetaClass for that see doc
ExpandoMetaClass.enableGlobally()
//call 'enableGlobally' method before adding to supplied class
String.metaClass.split = { regex ->
println "calling split from $delegate with $regex"
delegate.split regex, 22
}
To intercept all method calls in a class override Groovy's invokeMethod. Example:
class Test {}
Test.metaClass.foo = {"foo() called"}
Test.metaClass.static.bar = {"bar() called"}
Test.metaClass.invokeMethod = { name, args ->
handleInterception(name, args, delegate, false)
}
Test.metaClass.static.invokeMethod = { name, args ->
handleInterception(name, args, delegate, true)
}
def handleInterception(name, args, delegate, isStatic) {
def effDelegate = isStatic ? delegate : delegate.class
println ">> Entering ${delegate.class.name}.$name() with args: $args"
def metaMethod = effDelegate.metaClass.getMetaMethod(name, args)
if (!metaMethod) {
println "-- Method not found: $name($args)"
return
}
try {
def result = metaMethod.invoke(delegate, args)
println "<< Leaving ${delegate.class.name}.$name() with result: $result"
return result
} catch (ex) {
println "-- Exception occurred in $name: $ex.message"
throw ex
}
}
new Test().foo("1", 2)
Test.bar(2)
new Test().onTheFly(3)
Code taken from Roshan Dawrani's post at groovyconsole.appspot.com.
Output:
>> Entering Test.foo() with args: [1, 2]
-- Method not found: foo([1, 2])
>> Entering java.lang.Class.bar() with args: [2]
<< Leaving java.lang.Class.bar() with result: bar() called
>> Entering Test.onTheFly() with args: [3]
-- Method not found: onTheFly([3])
Other options:
Custom MetaClass implementing invokeMethod
Implementing the Interceptor Interface. Read more in this tutorial

Can Groovy objects be invoked like functions with parameters?

In Groovy, I can make an object invokable like a function by monkey-patching the metaclass' call method:
myObject.metaClass.call = { "hello world" }
println myObject() // prints "hello world"
patching call only allows me to invoke the object with no arguments. Is there a way of allowing objects to be invoked with arguments using standard function-like syntax?
edit: one answer is exactly as tim_yates suggests, although it's worth noting from ataylor's comment that you can simply override call without explicit metaprogramming:
class MyType {
def call(...args) {
"args were: $args"
}
}
def myObject = new MyType()
println myObject("foo", "bar") // prints 'args were ["foo", "bar"]'
Apparently the trick is the variadic signature using ...args.
You could do:
myObject.metaClass.call = { ...args -> "hello $args" }
assert myObject( 'one', 'two', 'three' ) == 'hello [one, two, three]'
(as you can see, args is an array of Objects)
Or for one parameter:
myObject.metaClass.call = { who -> "hello $who" }
Or if you want that single parameter as an optional param, you could do:
myObject.metaClass.call = { who = null -> "hello ${who ?: 'world'}" }
assert myObject( 'tim' ) == 'hello tim'
assert myObject() == 'hello world'

Resources