Groovy: Dynamic nested properties [duplicate] - grails

I am wondering if I can pass variable to be evaluated as String inside gstring evaluation.
simplest example will be some thing like
def var ='person.lName'
def value = "${var}"
println(value)
I am looking to get output the value of lastName in the person instance. As a last resort I can use reflection, but wondering there should be some thing simpler in groovy, that I am not aware of.

Can you try:
def var = Eval.me( 'new Date()' )
In place of the first line in your example.
The Eval class is documented here
edit
I am guessing (from your updated question) that you have a person variable, and then people are passing in a String like person.lName , and you want to return the lName property of that class?
Can you try something like this using GroovyShell?
// Assuming we have a Person class
class Person {
String fName
String lName
}
// And a variable 'person' stored in the binding of the script
person = new Person( fName:'tim', lName:'yates' )
// And given a command string to execute
def commandString = 'person.lName'
GroovyShell shell = new GroovyShell( binding )
def result = shell.evaluate( commandString )
Or this, using direct string parsing and property access
// Assuming we have a Person class
class Person {
String fName
String lName
}
// And a variable 'person' stored in the binding of the script
person = new Person( fName:'tim', lName:'yates' )
// And given a command string to execute
def commandString = 'person.lName'
// Split the command string into a list based on '.', and inject starting with null
def result = commandString.split( /\./ ).inject( null ) { curr, prop ->
// if curr is null, then return the property from the binding
// Otherwise try to get the given property from the curr object
curr?."$prop" ?: binding[ prop ]
}

Related

Groovy - Append values to variables in a string

Lets assume I have a domain class called Template that looks somewhat like this:
class Template{
String subject
...
}
I save an instance of this class:
Template t=new Template(subject:'Hello ${name}').save()
Now, I fetch this instance inside a method as follows:
def render(Long id){
String name='foo'
Template t= Template.get(id)
println t.subject
}
I want the "println t.subject" to be printed as "Hello foo". What I get is "Hello ${name}".
I want t.subject to dynamically replace the value of variable name - "foo" in place of ${name}.
Can this be achieved in groovy?
I cannot find any documentation of how to do this, or why this cannot be done.
Update:
I tried this on my GroovyConsole.
class Entity{
String name
}
class Template{
String name
String subject
}
String renderTemplate(Template template, Entity entity){
return template.subject
}
Entity e = new Entity(name:'product')
Template template=new Template(name:'emailTemplate',subject:'Product ${e.name}')
renderTemplate(template,e)
The Output I got was:
Result: Product product
class Template {
String subject
// ...
}
Template t = new Template(subject: 'Hello, ${name}').save()
Important: Use single quotes in 'Hello, ${name}' or you will get an error.
def render(Long id) {
String name = "world"
Template t = Template.get(id)
​def engine = new groovy.text.GStringTemplateEngine()
def subject = engine
.createTemplate(t.subject)
.make(name: name)​
println subject
}
There are a couple of things wrong with the code shown. You have this:
class Template{
String subject
...
}
Then you have this:
Template t=new Template(subject:"Hello ${name}").save()
The Groovy String assigned to the subject property will be evaluated as soon as you assign it to subject because subject is a String. The actual value will depend on the value of the name property that is in scope when that code executes.
Later you have this:
def render(Long id){
String name="foo"
Template t= Template.get(id)
println t.subject
}
It looks like you are wanting that local name property to be substituted into a Groovy String that has been assigned to t.subject, but that isn't how Groovy works. t.subject is not a Groovy String. It is a String.
Separate from that you comment that when you println t.subject that the output is "Hello ${name}". I don't think that is possible, but if that is what you are seeing, then I guess it is.
You can use a transient property to emulate the required behavior:
class Template{
String subject
String getSubjectPretty(){ "Hello $subject" }
static transients = ['subjectPretty']
}
Then you can use:
println Template.get(1).subjectPretty

get the field by field name and set value to it

I wanted to set a value to a field in domain class.
For example,
class Example {
String name
String lastName
}
Now, from response I'm getting domain name, object instance id, field name and value. I have to set the value to the field in domain class.
Here I got the values as
domainName = 'Example'
instanceId = 1
fieldName = 'name'
valueToSet = 'XYZ'
So how should I set value to the field name? May be this is simple but I'm a new with grails and groovy.
Based on the domain name, a new domain instance has to be created at runtime. For this to happen, grailsApplication has to be injected. Here is a sample which can be modeled after in Controller or a Service class:
class SomeService {
def grailsApplication
def someMethod(String domainName, long instanceId,
String fieldName, def valueToSet) {
Class domainClazz = grailsApplication.domainClasses.find {
it.clazz.simpleName == domainName
}.clazz
def domainInstance = domainClazz.get( instanceId )
domainInstance."$fieldName" = valueToSet
domainInstance.save()
}
}
You can populate a Grails domain class by assigning a map to its properties, for example from the params in the controller (using that handy automatic binding). E.g.:
ded example=new Example()
example.properties=params
So you can see from this that the domain object can be treated as a set of properties. Which means you can use strings for keys, so you might have something like:
example['name']='XYZ'
I don't know whether that's what you're asking but I hope it helps.

GORM where query on an embedded object

I have domain classes A and B as follows:
class A {
String prop1
String prop2
B prop3
static embedded = ['prop3']
}
class B {
String prop4
String prop5
}
When I want to query like this:
def q = A.where { prop3.prop4 == 'bla' }
def list = q.list()
I get the following exception:
Cannot get property 'javaClass' on null object. Stacktrace follows:
on the "def q = A.where ..." line.
Any clue what's the problem? I've checked this:
http://grails.1312388.n4.nabble.com/GORM-embedded-object-issue-td1379137.html
but how to "just call them directly" is not quite clear to me. Any other way of querying the embedded objects in GORM?
I finally gave up on the where query and went with the DetachedCriteria approach. Gives me the same flexibility as the where queries, but works with embedded domain objects:
def criteria = new DetachedCriteria(A).build {
eq 'prop1', 'bla2'
}
criteria = criteria.build {
eq 'prop3.prop4', 'bla'
}
def list = criteria.list()
What do you get if you do (assuming B is in src/groovy)
def q = A.where { prop3 == new B(prop4: 'bla') }
def list = q.list()
Embedded components are persisted inside the main domain class (owner) itself. It can be accessed directly using any dynamic finder as you do directly on a domain object.
The above can also be represented in dynamic finders as:
A.findAllByProp3(new B(prop4: 'bla'))

Grails data binding to command object with maps - convert key to number

I have a Grails command object with a list of maps. The map key is intended to be a numeric domain object ID.
class MyCommand {
def grid = [].withDefault { [:] }
}
Data binding to the list/map is working in general because of the dynamic list expansion.
However, in the POST, the map keys are being bound as Strings and I want them to be Longs, as they are when the form is initially populated. I want foo[123] in my map, not foo['123'].
Alternatively I would be satisfied if the [] operators found the correct value given a numeric ID key to look up. In other words, if I could get foo[123] to return the same value as foo['123'], that would work too.
Any way to get this to work the way I want to? Maybe strongly type the map?
Or a better approach?
You can inject the property into the map to convert a String key to Long. For example:
def myMap = [:] << ['1': "name"] << ['Test': "bobo"]
def result = myMap.inject([:]){map, v ->
def newKey = v.key.isNumber() ? v.key.toLong() : v.key
map[newKey] = v.value
map
}
assert myMap['1'] == 'name'
assert result[1L] == 'name'
assert result['Test'] == 'bobo'

closure over each object of class

i am trying to call a closure over each object of a class, and then displaying the objects which returned by the closure.
the closure is:
def activitiesPlanned={
cal.set(this.plannedStartDate)
def planDateMonth=cal.get(Calendar.MONTH)
def planDateYear=cal.get(Calendar.YEAR)
}
the call i made is:
def getActivitiesPlanned(int month,int year){
countActivitiesPlanned=ProgressData.each{it.activitiesPlanned.findAllWhere(planDateMonth:month,planDateYear:year).count()}
println countActivitiesPlanned
}
Domain Class //EDIT
package main
class ProgressData {
//String milestoneName
String taskId //Added later
String taskDescription
String taskCategory
Integer plannedHours
Integer actualHours
Date plannedStartDate
Date actualStartDate
Date plannedEndDate
Date actualEndDate
Integer stepsCreated=0
Integer stepsExecuted=0
String status //Originally Completed
String assignedTo
//String unplanned
String accepted //Added later
def ProgressData(){}
static constraints = {
//milestoneName(blank:false)
taskDescription(blank:false)
taskCategory(blank:false)
plannedHours(blank:false)
actualHours(blank:false)
id generator:"assigned",name:"taskId"
}
Calendar cal=Calendar.getInstance()
def activitiesPlanned={
cal.set(this.plannedStartDate)
def planDateMonth=cal.get(Calendar.MONTH)
def planDateYear=cal.get(Calendar.YEAR)
}
static hasMany=[defects:DefectData]
}
I am getting: "No such property: activitiesPlanned for class: main.ProgressData Possible solutions: activitiesPlanned". what issue may be there?
I think you don't want .each{} rather .sum{}
Example:
def listThing = [1,2,3]
def sumOfList = listThing.sum{it}
assert sumOfList == 6
Also ProgressData is a Domain Class so you can't use .each{} on it. You have to do ProgressData.list().each{ and by that I really mean ProgressData.list().sum{ for what I think you're trying to do.
Also, that activitiesPlanned closure doesn't return any activities...

Resources