How do I intercept calls in Groovy? - groovy

I have this class structure:
interface bug
{
def method()
}
public class A implements bug{
def method()
{
println "Works"
}
}
A varaiable = new A()
bug.metaClass.invokeMethod {
name,args ->
println "Came here"
}
varaiable.method()
when I do this, I get Works. Why not Came here?
Where I'm doing the mistake?
EDIT:
Even if I do:
A.metaClass.invokeMethod {
name,args ->
println "Came here"
}
I'm getting only Works.
​

You're changing the metaClass of A with
A.metaClass.invokeMethod { name,args ->
println "Came here"
}
After you construct the variable. If you put this block before the line
A varaiable = new A()
It should work as you'd expect.
To get round this, you can use:
ExpandoMetaClass.enableGlobally()
And instances will check back with the metaClass every invocation, however as expected this can slow things down

Related

Groovy call field

I'm trying to put into the field an object that supports a call operation, and then to call him. I can do it without intermediate reading fields in a variable?
My attempt looks like this:
class CallableObjectDynamic {
def call() {
return "5"
}
}
class MyClassDynamic {
CallableObjectDynamic field = new CallableObjectDynamic()
}
class GroovyRunnerDynamic {
static String make(int arg1) {
MyClassDynamic x = new MyClassDynamic()
return x.field()
}
}
​
But I receive groovy.lang.MissingMethodException.
What can you do? Or can anyone give a proof where it's written that we can't call the field?
Membership (.) has lower order of precedence than function/method/call invocation (()). Thus this line:
return x.field()
is interpreted as "invoke the 'field' method on the 'x' object".
To get Groovy to parse the code as you desire, the minimal change would be to regroup using parentheses, as follows:
return (x.field)()
which is (ultimately) interpreted as "invoke the 'call' method on the 'field' object member of the 'x' object", as desired.
It is trivial issue. Not required to have parenthesis for field.
Change from:
return x.field()
To:
return x.field
If you want to execute call method further, then use below code snippet.
Note that static method return type is changed.
class CallableObjectDynamic {
def call() {
return "5"
}
}
class MyClassDynamic {
CallableObjectDynamic field = new CallableObjectDynamic()
}
class GroovyRunnerDynamic {
static def make(int arg1) {
MyClassDynamic x = new MyClassDynamic()
return x.field
}
}
​GroovyRunnerDynamic.make(1)​.call()​
Output would be : 5
Not sure why argument to make method is done here, seems to be not used in the above code.
Alternatively, you can change
class GroovyRunnerDynamic {
static def make(int arg1) {
MyClassDynamic x = new MyClassDynamic()
return x.field.call()
}
}
​GroovyRunnerDynamic.make(1)
EDIT: Based on OP's implicit call.
Not really sure how it is working, but the below does implicit call. Just assign x.field to a variable and just add parenthesis for that as shown below.
class GroovyRunnerDynamic {
static String make(int arg1) {
MyClassDynamic x = new MyClassDynamic()
def fun = x.field
fun()
}
}
GroovyRunnerDynamic.make(1)

Groovy: add a method to a closure

I have the following closure
def closure = {
println ("closure code")
}
And i would like to add a method to it.
but if I try
closure.metaClass.fun = { c->
c.call();
println ("extra code");
}
I get an Exception
groovy.lang.MissingPropertyException: No such property: fun for class: org.codehaus.groovy.runtime.metaclass.ClosureMetaClass
Reading another answer, i also blindly tried to call
ExpandoMetaClass.enableGlobally()
but it's not working.
Is there a way to achive what I want?
You can do:
def closure = {
println "closure code"
}
closure.getMetaClass().fun = { ->
delegate.call()
println "extra code"
}
closure.fun()
Which prints:
closure code
extra code
Another simpler approach could be:
def closure = {
println "closure code"
}
closure.fun = { ->
closure()
println "extra code"
}
closure.fun()
The downside of this approach is that I'm referencing the closure variable directly though, instead of going through the delegate.

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

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

replacing toString using Groovy metaprogramming

In the following Groovy snippet, I attempt to replace both the hashCode and toString methods
String.metaClass.toString = {-> "override" }
String.metaClass.hashCode = {-> 22 }
But when I test it out, only the replacement of hashCode works
String s = "foo"
println s.hashCode() // prints 22
println s.toString() // prints "foo"
Is toString somehow a special case (possibly for security reasons)?
See the first comment on this issue. It says about String's toString and other String related classes:
(...) seems to be intent, it is probably a
good idea to have a faster invocation
for classes that don't allow
overriding toString().
This is a know defect.
Basically Groovy does not correctly override methods that are part of an interface implementation.
This works:
class T {
def doIt() { true }
}
def t = new T()
assert t.doIt()
t.metaClass.doIt = { -> false }
assert !t.doIt()
This doesn't:
interface I {
def doIt()
}
class T implements I {
def doIt() { true }
}
def t = new T()
assert t.doIt()
t.metaClass.doIt = { -> false }
assert !t.doIt()
Because toString() in String comes from CharSequence the correct way to override would be:
CharSequence.metaClass.toString = {-> "silly"}
println "hello world".toString()

Resources