I would like to overwrite a dynamic property on a metaClass but it's not working as I would expect. Here is a simple example that demonstrate the problem. You can run it online at Groovy web console
class Bike {}
class Bell { def ring() { println("calling " + this) } }
def createNamespaces = {
return [ "bell": new Bell() ]
}
def resetNamespaces = { bike ->
// setting to null means 'set it to the default'
bike.metaClass = null
createNamespaces().each { name, namespace ->
println("setting " + namespace)
bike.metaClass."$name" = namespace
}
}
def bike= new Bike()
resetNamespaces(bike)
bike.bell.ring()
resetNamespaces(bike)
bike.bell.ring()
The result is:
setting Bell#14e9bd2b
calling Bell#14e9bd2b
setting Bell#948a7ad
calling Bell#14e9bd2b
So although I changed the property on the metaClass, calling it always returns the first object that was set. Is there some kind of caching?
When I just change the last part of the example to:
resetNamespaces(bike)
resetNamespaces(bike)
bike.bell.ring()
Then the result is as expected. That is, calling the property returns the object that was set on the metaClass as the last:
setting Bell#5b47e0c7
setting Bell#19f373a4
calling Bell#19f373a4
I even tried to set metaClass manually as follows
def resetNamespaces = { bike ->
def newMetaClass = new ExpandoMetaClass(Bike.class, true, true)
newMetaClass.initialize()
bike.metaClass = newMetaClass
...
}
But the result is still the same. So there must be some caching mechanism involved. I can't find anything about this behavior in the documentation.
The confusion arises because you are trying to change a property of the metaclass, not a method. As stated in the official documentation of groovy metaprogramming properties are accessed using the setAttribute/getAttribute methods of the metaclass.
// throws MissingFieldException!
def resetNamespaces = { bike ->
createNamespaces().each { name, namespace ->
bike.metaClass.setAttribute(bike, name, namespace)
}
}
Unfortunately this only works if the property is in the original class definition, here it throws a MissingFieldException: No such field: bell for class: Bike.
On the other hand overwriting methods works like a charm and provides the wanted syntax by adding a dynamic getter method:
def resetNamespaces(bike) {
createNamespaces().each { name, namespace ->
bike.metaClass."get${name.capitalize()}" = { namespace }
}
}
def bike = new Bike()
resetNamespaces(bike)
bike.bell.ring()
resetNamespaces(bike)
bike.bell.ring()
Actually expando works nicely as well
class Bell {
def ring() { println this }
}
def bike = new Expando()
bike.createNamespaces = {
return [ bell : new Bell() ]
}
bike.resetNamespaces = {
bike.createNamespaces().each { name, namespace ->
bike."$name" = namespace
}
}
bike.resetNamespaces bike
bike.bell.ring()
bike.resetNamespaces bike
bike.bell.ring()
Related
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)
I was able to bind an object on Groovy's ConfigSlurper but not a method. Is that not possible?
Here's an example
String conf = """
k1 = "v1"
environments{
prod{
person.create("prod.id1"){
name = "prod.name1"
}
}
dev {
person.create("dev.id1"){
name = "dev.name1"
}
}
}
environments{
prod{
create("prod.id2"){
name = "prod.name2"
}
}
dev {
create("dev.id2"){
name = "dev.name2"
}
}
}
"""
def parser = new ConfigSlurper("prod")
Person person1 = new Person()
Person person2 = new Person()
parser.setBinding([person: person1, // <-- SUCCESS
create: person2.&create]) // <-- NOT SUCCESS?
println parser.parse(conf)
println "person1=${person1.dump()}"
println "person2=${person2.dump()}"
class Person{
String id
String name
public void create(String id, Closure c){
this.id = id
this.with(c)
}
}
Output is
[k1:v1, create:prod.id2, create.name:prod.name2]
person1=<Person#409b0772 id=prod.id1 name=prod.name1>
person2=<Person#205ee81 id=null name=null>
Please ignore any design flaws in the example.
person.create(...) is translated by Groovy to getProperty('person').invokeMethod('create', ...), it works since person is defined in the binding and create is defined on object returned by getProperty('person').
create.call(...) works because it's translated to getProperty('create').invokeMethod('call', ...). create property is defined via binding and it is of MethodClosure type which has call method defined.
However create(...) is translated to invokeMethod('create', ...). It fails because there is no method create and you cannot define methods via binding.
I want to retrieve the annotation value used from MyAnnot. I am getting 3 annotations in the list even though there are only 2. Also, I have tried obtaining the used field of MyAnnot but with no success. I would like to return a map where MyAnnot's used is the key and type as the map's value.
// Then, define your class with it's annotated Fields
class MyClass {
#MyAnnot(used = "Hey", type = "There")
String fielda
#MyAnnot(used = "denn", type = "Ton")
String fieldc
}
def findAllPropertiesForClassWithAnotation(obj, annotClass) {
def op = []
def annos = []
def i = 0
obj.properties.findAll { prop ->
obj.getClass().declaredFields.find {
it.name == prop.key && annotClass in it.declaredAnnotations*.annotationType()
annos=it.declaredAnnotations
i++
if(annos)
op << annos[0] as Set
// println"Props ${annos[0]}"
}
}
op.each{ println "${it} and i is ${i}"}
}
// Then, define an instance of our class
MyClass a = new MyClass(fielda: 'tim', fieldc: 'dennisStar')
// And print the results of calling our method
println findAllPropertiesForClassWithAnotation(a, MyAnnot)
First of all you need to mark you annotation with: #Retention(RetentionPolicy.RUNTIME) to make it available for processing at runtime, so it will be:
#Retention(RetentionPolicy.RUNTIME)
#interface MyAnnot {
String used()
String type()
}
Then, used and type are not fields, nor properties but methods, so they must be invoked and get.
The script will be:
import java.lang.annotation.RetentionPolicy
import java.lang.annotation.Retention
#Retention(RetentionPolicy.RUNTIME)
#interface MyAnnot {
String used()
String type()
}
class MyClass {
#MyAnnot(used="Hey" ,type="There")
String fielda
#MyAnnot(used="denn", type="Ton")
String fieldc
}
def findAllPropertiesForClassWithAnotation( obj, annotClass ) {
def c = obj.getClass()
c.declaredFields.findAll { field ->
field.isAnnotationPresent(annotClass)
}.collect { found ->
def a = found.getAnnotation(annotClass)
[(a.used()): a.type()]
}.sum()
}
MyClass a = new MyClass(fielda: 'tim', fieldc: 'dennisStar')
println findAllPropertiesForClassWithAnotation(a, MyAnnot)
Mind that passing only a class of annotation is not sufficient, since you don't know the methods (used and type) to be invoked on the annotation. The following method will work only for MyAnnot class.
I really like the groovy language but I believe my java experience is leading me to a path where I don't use groovys fully potential.
I have a scenario where a groovy object (Master.groovy) sends some json-data to some view that creates a form out of this data. Then the user can submit this form and Master.groovy receives it as json-data again.
Master.groovy can access a pool of different form descriptions (formDesc1, formDesc2, etc.) which it choses from when sending the json to the view. When the user returns the json data - Master.groovy will then pass it over to e.g. formDesc1 to validate it.
This is how it looks with code:
Master.groovy
class Master {
FormDesc[] formDescs = [new FormDesc1(), new FormDesc2(), new FormDesc2()]
def getJSONform(Integer formId){
return formDescs[formId].getForm()
}
def validateForm(Integer formId, def data) {
return formDescs[formId].validate(data)
}
}
FormDesc1.groovy
class FormDesc1 extends FormDesc {
static String name = "alias"
FormDesc1 (){
form.add(new InputElement(name:name, type:"text"))
form.add(new TextArea(name:"msg", rows:"12", cols:"12"))
}
#Override
def validate(data){
errors = []
if(data.get(name)["value"] != "form1")
errors.add("name not allowed")
return errors
}
}
FormDesc2.groovy
class FormDesc2 extends FormDesc {
static String name = "alias"
FormDesc1 (){
form.add(new InputElement(name:name, type:"text"))
form.add(new TextArea(name:"msg", rows:"12", cols:"12"))
}
#Override
def validate(data){
errors = []
if(data.get(name)["value"] != "form1")
errors.add("name not allowed")
return errors
}
}
There can be many forms in the package /forms and to load all of them in the memory seems very unnecessary. And it would be much nicer if the formDesc consisted of json-data that represented the form and then only has the method validate. E.g:
FormDescUltimate.groovy
class FormDescUltimate extends FormDesc {
form = loadJSONFile("formDescUltimate.json")
#Override
def validate(data){
errors = []
if(data.get("alias")["value"] != "form1")
errors.add("name not allowed")
return errors
}
}
formDescUltimate.json
{
'formId' : 'ultimate_form',
'elements': [
{'InputElement' : {'name': 'alias', 'type':'text'}},
{'TextArea' : {'name': 'msg', 'rows':'12', 'cols':'12'}}
]
}
Is it possible to achieve this nicely with groovy, or is there any better design to accomplish this?
Thanks in advance and sorry for the long post!
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.