Adding A Method to the Global Name Space - groovy

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.

Related

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'

Groovy 'No signature of method' running Closure against delegate

I have a closure that's executed against another Groovy object as the delegate. I can do:
foo {
bar {
major = 1
}
}
but when I do:
foo {
bar {
major 1
}
}
I get an error:
> No signature of method: my.Bar.major() is applicable for argument types (java.lang.Integer) values: [1]
Possible solutions: setMajor(java.lang.Integer), getMajor(), wait(), any(), setMajor(java.lang.String), wait(long)
Bar looks something like:
class Bar {
Integer major
Integer getMajor() { return this.major }
def setMajor(Integer val) { this.major = val }
}
I thought Groovy made getters/setters transparent when dealing with property references and that referring to bar.major was the same as bar.get/setMajor(). Am I understanding this wrong, or does the meta class lookup route differ when you throw Closure delegates into the mix? The resolution strategy is DELEGATE_FIRST.
For more context: http://forums.gradle.org/gradle/topics/groovy-no-signature-of-method-running-closure-against-delegate
you would have to add also void major(Integer val). major = 1 is groovy-short for setMajor(1) while major 1 is short for major(1). (see Section Optional parenthesis)
Optional parenthesis
Method calls in Groovy can omit the parenthesis if there is at least one parameter and there is no ambiguity.
println "Hello world"
System.out.println "Nice cheese Gromit!"
E.g.:
class X {
Integer major
void major(Integer m) { major = m }
}
def x = new X()
x.major = 1 // x.setMajor(1)
assert x.major==1 // x.getMajor()==1
x.major 2 // x.major(2)
assert x.major==2
If you need this behaviour alot, you can add a methodMissing for this case. E.g.:
class X {
Integer major
def methodMissing(String name, args) {
if (this.hasProperty(name) && args.size()==1) {
this."$name" = args[0]
} else {
throw new MissingMethodException(name, this.class, args)
}
}
}
def x = new X()
x.major = 1
assert x.major==1
x.major 2
assert x.major==2

using gstring to access list property element

Using GStrings one can access the properties of the object, including nested properties. But how to access the n'th element inside a list property?
class Foo {
List<Bar> elements
}
class Bar {
String version
}
I need to access version property in Foo.elements object for a specific index using GString.
Tried below code without success.
def property = "elements[0].version"
fooObject."$property" fails to identify the property
So there are three ways in which I think this problem can be solved depending upon how much flexibility is allowed
class Foo {
List<Bar> elements
}
class Bar {
String version
}
Let's say fooObject is the object of Foo, e.g.:
def fooObject = new Foo(elements:[new Bar(version:1), new Bar(version:2)])
If this is possible for you:
println fooObject."elements"[1]."version"
Otherwise, put everything in a string and then interpolate:
println "${fooObject.elements[1].version}"
Ultimately, if both of the above don't fly for you:
def property='elements[1].version'
def expr = 'fooObject.' + property
println Eval.me('fooObject', fooObject, expr)
The last one makes the fooObject available as fooObject to the expression being evaluated and evaluates the expression.
Ideally, it could be:
def prop1 = "elements"
def prop2 = "version"
fooObject."$prop1"[0]."$prop2"
Lengthy and generic one would be using inject:
class Foo {
List<Bar> elements
}
class Bar {
String version
}
def fooObject = new Foo(elements: [new Bar(version: '1'),
new Bar(version: '2'),
new Bar(version: '3')])
def fetchVersion(property, fooObject) {
property.tokenize(/./).inject(fooObject) {obj, elem ->
if(elem.contains(/[/)){
def var = elem.tokenize(/[]/)
obj?."${var[0]}".getAt("${var[1]}".toInteger())
} else {
obj?."$elem"
}
}
}
assert fetchVersion("elements[0].version", fooObject) == '1'
assert fetchVersion("elements[1].version", fooObject) == '2'
assert fetchVersion("elements[2].version", fooObject) == '3'
assert fetchVersion("elements[7].version", fooObject) == null

Pass method as parameter in Groovy

Is there a way to pass a method as a parameter in Groovy without wrapping it in a closure? It seems to work with functions, but not methods. For instance, given the following:
def foo(Closure c) {
c(arg1: "baz", arg2:"qux")
}
def bar(Map args) {
println('arg1: ' + args['arg1'])
println('arg2: ' + args['arg2'])
}
This works:
foo(bar)
But if bar is a method in a class:
class Quux {
def foo(Closure c) {
c(arg1: "baz", arg2:"qux")
}
def bar(Map args) {
println('arg1: ' + args['arg1'])
println('arg2: ' + args['arg2'])
}
def quuux() {
foo(bar)
}
}
new Quux().quuux()
It fails with No such property: bar for class: Quux.
If I change the method to wrap bar in a closure, it works, but seems unnecessarily verbose:
def quuux() {
foo({ args -> bar(args) })
}
Is there a cleaner way?
.& operator to the rescue!
class Quux {
def foo(Closure c) {
c(arg1: "baz", arg2:"qux")
}
def bar(Map args) {
println('arg1: ' + args['arg1'])
println('arg2: ' + args['arg2'])
}
def quuux() {
foo(this.&bar)
}
}
new Quux().quuux()
// arg1: baz
// arg2: qux
In general, obj.&method will return a bound method, i.e. a closure that calls method on obj.
In addition to the method pointer operator (.&), for Groovy version 3.0.0 and above there's the equivalent, compatible, well known Java 8+ method reference operator (::).
def foo(Closure c) {
c(func: "foo")
}
def bar(Map args = [:]) {
println "$args.func bar"
}
println foo(this::bar)
Output:
foo bar
The method reference operator, as stated in Groovy's documentation:
[…] overlaps somewhat with the functionality provided by Groovy’s
method pointer operator. Indeed, for dynamic Groovy, the method
reference operator is just an alias for the method pointer operator.
For static Groovy, the operator results in bytecode similar to the
bytecode that Java would produce for the same context.

How to iterate over groovy class non-static closures and optionally replace them?

I'd like to iterate over groovy class non-static closures and optionally replace them.
I can get MetaClass with something like
MyClassName.metaClass
and from there I can get all properties like
metaClassObject.properties
which is the list of MetaProperty objects.
The problem is that I can't detect which of those properties are closures and which are simple objects. MetaProperty object's type property return Object in both case.
And about replacing: Let's say, I know that it is a closure A, then can I create another closure B that wraps closure A with some optional code and replace that closure A with B in the class definition? Should work like some sort of interceptor.
This is one way I have tried out:
class Test {
def name = 'tim'
def processor = { str ->
"Hello $name $str"
}
}
Test t = new Test()
t.metaClass.properties.each {
if( t[ it.name ].metaClass.respondsTo( it, 'doCall' ) ) {
println "$it.name is a closure"
def old = t[ it.name ]
t.metaClass[ it.name ] = { str ->
"WOO! ${old( str )}"
}
}
}
println t.processor( 'groovy!' ) // prints 'WOO! Hello tim groovy!'
However, it would need expanding as I rely on the fact that I know how many parameters it takes for the patching closure replacement
There may also be a simpler way to do this...

Resources