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