Accessing a property with object."${property}" - groovy

I'm working on some dynamic filtering, and have this:
class Filterable {
def statusId
def secondaryFilterable
}
...
def filter = new Filter(validIds: [1], fieldName: 'statusId')
...
class Filter {
def validIds = [] as Set
def fieldName
private boolean containsFieldValue(input) {
def fieldValue = input."${fieldName}"
return fieldValue in validIds
}
}
Which works just fine for one property. However, now I need to filter by the secondary filterable - something like
def filter = new Filter(validIds: [1], fieldName: 'secondaryFilterable.statusId')
Which throws a groovy.lang.MissingPropertyException. Any advice?

Quoted properties assume a dot is part of the property name.
A simple solution would be:
...
def fieldValue = fieldName.split(/\./).inject(input){ parent, property -> parent?."$property" }
...
This will recursively look up the field value using dot notation for child properties.
I put up a working example here on the Groovy web console.

Related

Overwrite a metaClass property in Groovy

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()

groovy design pattern for forms

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!

get Groovy class' closure property names

Given the following Groovy class:
class MyClass {
def someClosure = {}
def someClosure2 = {}
private privateClosure = {
}
def someVal = 'sfsdf'
String someMethod() {}
}
I need a way to retrieve the names of all public properties that have closure assigned to them, so the correct result for this class would be ['someClosure', 'someClosure2'].
I can assume that all the classes of interest have a default constructor, so if it makes things easier, I could retrieve the properties from an instance via
def instance = MyClass.newInstance()
You can simply check the value of every groovy property:
class Test {
def aClosure = {}
def notClosure = "blat"
private privateClosure = {}
}
t = new Test()
closurePropNames = t.properties.findResults { name, value ->
value instanceof Closure ? name : null
}
assert closurePropNames == ['aClosure']
The private fields are not considered groovy properties, so they won't be included in the results.

groovy: Have a field name, need to set value and don't want to use switch

I have an object with several fields,
class TestObj {
def field1
def field2
}
I have a pair of values v1="field1" and v2="value2" I would like to set v2 into the appropriate field based on the name of v1, but I'd prefer not to have to do it with a switch or if statements, I keep thinking there has to be a much "groovier" way of achieving the result other than doing something like this:
setValues(def fieldName, def fieldVal) {
if (fieldName.equals("field1")) {
field1 = fieldVal
}
if (fieldName.equals("field2")) {
field2 = fieldVal
}
}
I've tried doing this:
setValues(def fieldName, def fieldVal) {
this['${fieldName}'] = fieldVal
}
However that fails, saying there's no property ${fieldName}
Thanks.
You can use GStrings when you get a field, like this:
def obj = new TestObj()
def fieldToUpdate = 'field1'
obj."$fieldToUpdate" = 3
In Groovy you don't have to define a property to have a property. Use getProperty and setProperty called property access hooks in Groovy:
class TestObj {
def properties = [:]
def getProperty(String name) { properties[name] }
void setProperty(String name, value) { properties[name] = value }
void setValues(def fieldName, def fieldVal) {setProperty(fieldName, fieldVal)}
}
def test = new TestObj()
test.anyField = "anyValue"
println test.anyField
test.setValues("field1", "someValue")
println test.field1
test.setValues("field2", "anotherValue")
println test.field2

Groovy: Setting property values easily

I have a map with name/value pairs:
def myMap = [
"username" : "myname",
"age" : 30,
"zipcode" : 10010
]
I have a class called User defined:
class User {
def username;
def age;
def zipcode;
}
I would like to invoke the appropriate setters on the bean. Is there a nice groovy way of doing this? Note, I might have extra stuff in the myMap that wouldn't be set. (The map is a form parameter values map)
I was thinking about using invokeMethod but I was assuming there was a better way.
I found this: Groovy - bind properties from one object to another
And I realized I could do the following:
def prunedMap = [:]
myMap.each{
if (User.metaClass.properties.find{p-> p.name == it.key}) {
prunedMap.put(it.key, it.value)
}
}
User user = new User(prunedMap)

Resources