Pass param with .& operator to a static method - groovy

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'

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)

Groovy: colon mark in closure?

I am trying to make my own Dsl and was playing around with different styles of closures in groovy.
I've stumbled upon follwing snippet:
myClosure {
testProperty: "hello!"
}
But can't figure out if this a valid code and how can I access then this testProperty. Is it valid? How can I read "hello!" value?
For now, lets put aside closures, consider the following code:
def f1() {
testProperty : 5
}
def f2() {
testProperty : "Hello"
}
println f1()
println f1().getClass()
println f2()
println f2().getClass()
This compiles (therefor the syntax is valid) and prints:
5
class java.lang.Integer
Hello
class java.lang.String
So what you see here is just a labeled statement (groovy supports labels see here)
And bottom line the code of f1 (just like f2) is:
def f1() {
return 5 // and return is optional, so we don't have to write it
}
With closures its just the same from this standpoint:
​def method(Closure c) {
def result = c.call()
println result
println result.getClass()
}
method {
test : "hello"
}
This prints
hello
class java.lang.String
as expected
Usually in DSL you have either this:
mySomething {
a = 42
b = 84
}
which corresponds to property setting
or this:
mySomething( a:42, b:84 ){
somethingElse{}
}
which is a method call with Map-literal.
The code you shown is not used as #mark-bramnik explained.

No signature of method: *** is applicable for argument types: () values: [] Possible solutions: getRandomShippingMethod()

I want to write a script on the groovy script, but i have this exception:
Caught: groovy.lang.MissingMethodException: No signature of method: ru.evgeny.in.order.bob.util.dbutil.DBStandaloneFullXMLUtil.randomShippingMethod() is applicable for argument types: () values: []
Possible solutions: getRandomShippingMethod()
groovy.lang.MissingMethodException: No signature of method: ru.evgeny.in.order.bob.util.dbutil.DBStandaloneFullXMLUtil.randomShippingMethod() is applicable for argument types: () values: []
Possible solutions: getRandomShippingMethod()
at Draft.run(Draft.groovy:6)
my groovy script:
import ru.evgeny.in.order.bob.util.dbutil.DBStandaloneFullXMLUtil
def db = new DBStandaloneFullXMLUtil()
println db.getRandomSKU()
println db.randomShippingMethod()
DBStandaloneFullXMLUtil:
class DBStandaloneFullXMLUtil implements IDBUtil {
....
#Override
def getRandomSKU() {
def rows = 'id, item_nr'
def table = '(select * from mywms_itemdata where dtype = \'LMItemData\') as LMItemData'
getRandomeRow(rows, table).values()
}
#Override
def getRandomShippingMethod(){
def rows = 'id, name'
def table = 'lm_shipping_method'
getRandomeRow(rows, table).values()
}
}
Why i get this exception??? Why there is no exception on
println db.getRandomSKU()
The problem is that you have two accessor methods in your class: getRandomSKU and getRandomShippingMethod, but you call them in two different ways:
println db.getRandomSKU()
println db.randomShippingMethod()
You should change the above to either:
println db.randomSKU
println db.randomShippingMethod
(which indirectly calls your accessor methods), or you could use:
println db.getRandomSKU()
println db.getRandomShippingMethod()
(which directly calls those methods)

Adding A Method to the Global Name Space

I've written a method that I can use ClassName.methodName(args).
How can I make it so I can use methodName(args).
I tried monkey patching Object like so:
class Object {
def methodName(args) {
// method definition
}
}
Update:
I tried what dmahapatro said.
import static groovy.json.JsonOutput.*
Object.metaClass.outputJson = {
return println(prettyPrint(toJson(it)))
}
outputJson([:])
Return:
Caught: groovy.lang.MissingMethodException: No signature of method: Object.outputJson() is applicable for argument types: (java.util.LinkedHashMap) values: [[:]]
Possible solutions: outputJson(), outputJson(java.lang.Object)
groovy.lang.MissingMethodException: No signature of method: Object.outputJson() is applicable for argument types: (java.util.LinkedHashMap) values: [[:]]
Possible solutions: outputJson(), outputJson(java.lang.Object)
at Object.run(Object.groovy:7)
[Finished in 2.1s]
The issue created by the edit was because Object.groovy conflicted with Groovy's Object.java. Once I renamed it to ObjectMeta (or any other non conflicting name, it worked).
Using ExpandoMetaClass on Object
Object.metaClass.printsHello = {
return it
}
assert "Hello" == printsHello("Hello")
assert "Hello" == 'ABC'.printsHello("Hello")
assert "Hello" == 123.printsHello("Hello")
assert "Hello" == new Object().printsHello("Hello")
class A{
Integer a
}
assert "Hello" == new A(a: 10).printsHello("Hello")
This can also be achieved by using #Category as below
#Category(Object) class CustomizedObject{
def printsHello(String str){
return str
}
}
String.mixin CustomizedObject
assert 'Hello' == 'ABC'.printsHello('Hello')
Integer.mixin CustomizedObject
assert 'Hello' == 123.printsHello('Hello')
BigInteger.mixin CustomizedObject
assert 'Hello' == 123G.printsHello('Hello')
#Mixin(CustomizedObject) //Compile Time Mixin
class A{
}
assert 'Hello' == new A().printsHello('Hello')
If you want to distribute the #Category in a jar, then include CustomizedObject in that jar and use it wherever needed.
import static groovy.json.JsonOutput.*
Object.metaClass.outputJson = {
return prettyPrint(toJson(it))
}
println outputJson([a: 1, b: 2, c: 3])
//Prints:
{
"a": 1,
"b": 2,
"c": 3
}
Note:-
One thing to catch here is, we are using metaClass on Object directly which can be pivotal sometimes, you should clear the metaClass from object once you are done with it.
There are several possibilities that you can do. The simplest one is to use categories. In your main method or script do something like this:
use(ObjectExtender) {
startProgram();
}
And then create the ObjectExtender class like this:
class ObjectExtender {
static def methodName(Object self, Map args) {
...
}
}
As long as you are inside the control flow of the use call, you will be able to call methodName on any object. There are other possibilities, like creating a new metaClass for object, but I'd probably go with categories.

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