Create dynamically closures in Groovy from a String object - grails

i would like to create a query with the Criteria API in Grails (GORM).
The query will have to be something like this:
MyEntity.createCriteria().list{
assoc{
parent{
eq("code", val)
}
}
}
What i need is to build the nested closure dynamically from a String object. The String for the example above will be "assoc.parent.code" . I splitted the String by dot (by doing String.split("\\.") ) but i don't know how to construct the nested closures:
assoc{
parent{
eq("code", val)
}
}
dynamically based on the array of the splitted Strings above.

What about createAlias?. You could try something like this:
def path = "assoc.parent.code"
def split = path.split(/\./)
MyEntity.createCriteria().list {
// this will get you 'createAlias( assoc.parent, alias1 )'
createAlias split.take( split.size() - 1 ), "alias1"
// this will get you 'eq(alias1.code, userInput)'
eq "alias1.${split[-1]}", userInput
}
This snippet is not generic, but you get the idea.
Update
Not conventional, but you can build a string containing the code with the closures and evaluate it using GroovyShell:
assoc = 'assoc.parent.child.name'
split = assoc.split( /\./ )
path = split[-2..0] // will get us 'child.parent.assoc';
// we will build it from inside-out
def firstClosure = "{ eq '${split[-1]}', 'john doe' }"
def lastClosure = firstClosure
for (entity in path) {
def criteriaClosure = "{ ${entity} ${lastClosure} }"
lastClosure = criteriaClosure
}
assert lastClosure == "{ assoc { parent { child { eq 'name', 'john doe' } } } }"
def builtClosure = new GroovyShell().evaluate("return " + lastClosure)
assert builtClosure instanceof Closure

A more generic approach would be to metaClass String as below, and use it for any kind of separator
. | , - ~ and more.
String.metaClass.convertToClosureWithValue = {op, val ->
split = delegate.split(op) as List
if(split.size() == 1) {return "Cannot split string '$delegate' on '$op'"}
items = []
split.each{
if(it == split.last()){
items << "{ eq '$it', $val }"
split.indexOf(it).times{items.push("}")}
} else {
items << "{$it"
}
}
println items.join()
new GroovyShell().evaluate("return " + items.join())
}
assert "assoc.parent.child.name".convertToClosureWithValue(/\./, "John Doe") instanceof Closure
assert "assoc-parent-child-name".convertToClosureWithValue(/\-/, "Billy Bob") instanceof Closure
assert "assoc|parent|child|grandChild|name".convertToClosureWithValue(/\|/, "Max Payne") instanceof Closure
assert "assoc~parent~child~grandChild~name".convertToClosureWithValue('\\~', "Private Ryan") instanceof Closure
assert "assocparentchildname".convertToClosureWithValue(/\|/, "Captain Miller") == "Cannot split string 'assocparentchildname' on '\\|'"
//Print lines from items.join()
{assoc{parent{child{ eq 'name', John Doe }}}}
{assoc{parent{child{ eq 'name', Billy Bob }}}}
{assoc{parent{child{grandChild{ eq 'name', Max Payne }}}}}
{assoc{parent{child{grandChild{ eq 'name', Private Ryan }}}}}

Related

Use getproperty, setproperty and hasproperty instead of $

I have something like this.
def temp = "field"
object."$temp" = "hi"
This works fine.
But can i use setproperty, getproperty and hasproperty somehow to implement the same.
Thanks
You can replace object."$temp" = "hi" with object.setProperty(temp, "hi"), and object."$temp" with object.getProperty(temp). There is also the square bracket syntax:
e = new Expando()
def field = 'name'
e[field] = 'john doe' // set
assert e[field] == 'john doe' // get
e.setProperty(field, 'foo') // set
assert e.getProperty(field) == 'foo' // get
if (!e.hasProperty('bar')) { // has property
e['bar'] = 'my bar'
}
assert e.getProperty('bar') == 'my bar'

How can I pass a class to Groovy's Eval binding?

I'm doing some gross stuff, like using Groovy's metaClass and Eval to dynamically assign properties to an object from an XML import:
class ObjectBuilder {
def assignProp(String propName, String propValue) {
Eval.x(this, "x.metaClass.${propName} = '${propValue}'")
}
}
def h = new ObjectBuilder()
h.assignProp('name', 'Henson')
println(h.name)
What I'd like to do though, is be able instantiate another copy of the class inside itself:
Eval.x(this, "x.metaClass.${ObjName} = new ObjectBuilder()")
But I can't, because I think the class isn't passed to the binding. Is there another solution?
A couple of solutions:
Expando
You may try working with a bunch of Expandos:
h = new Expando()
h.last = new Expando()
h.last.name = 'tech'
assert h.last.name == 'tech'
Metaclass the object directly
xml = '''
<person>
<name>john</name>
<surname>doe</surname>
<age>41</age>
<location>St Louis, MO</location>
</person>
'''
class Person {
def name, surname, location, age
}
root = new XmlParser().parseText xml
person = new Person(root.children().collectEntries { [it.name(), it.text()] })
person.metaClass.getFullname = { "$delegate.name $delegate.surname" }
assert person.fullname == "john doe"
person.metaClass.likes = "chicken with lemon"
assert person.likes == "chicken with lemon"
Maps
map = [:]
map.last = [:]
map.last.name = 'Tech'
assert map.last.name == 'Tech'
Passing a newly instantiated object then assigning it with Eval seems to work:
class ObjectBuilder {
def assignProp(String propName, String propValue) {
Eval.x(this, "x.metaClass.${propName} = '${propValue}'")
}
def nestObj(String objName) {
Eval.xy(this, new ObjectBuilder(), "x.metaClass.${objName} = y")
}
}
ObjectBuilder h = new ObjectBuilder()
h.assignProp('name', 'Henson')
h.nestObj('last')
h.last.assignProp('name', 'Tech')
println h.name + ' ' + h.last.name

Better approach for getting the old values for the domain's collection associations properties

Inside my domain class SupplierGroup , I have hasMany association .In my domain class for example : suppliers
Code snipp of my domain class:
int id
String supplierGroupName
Long orgId
Date dateCreated
static hasMany = [suppliers:OrganisationUnit] ..
I want to capture the old and new values of each modified property name .
But always get the current value of the suppliers property name .And which is what I dont want.
def beforeUpdate(){
println "BEFORE UPDATE: " + this.getPersistentValue('suppliers');
}
def afterUpdate(){
println "AFTER UPDATE: " + this.suppliers;
}
Console o\p :
BEFORE UPDATE: [com.xms.core.organisation.OrganisationUnit(13, ABC), com.xms.core.organisation.OrganisationUnit(14,DEF)]
AFTER UPDATE: [com.xms.core.organisation.OrganisationUnit(13, ABC), com.xms.core.organisation.OrganisationUnit(14,DEF)]
What is the best approach in Grails to collect all old and the new values of the association prop name ,mainly suppliers here in question? My current approach in controller layer is the following:
def multiOldIds = ""
def multiNewIds = ""
Block 1 :
supplierGroupInstance.suppliers.each(){ supplier ->
if(multiOldIds != ""){
multiOldIds += ","
}
//gets me the comma separated (ex: 1,2,3)
multiOldIds += supplier.id
}
Block 2:
supplierGroupInstance.suppliers.each(){ supplier ->
ifmultiNewIds != ""){
multiNewIds += ","
}
//gets me the comma separated (ex: 1,2,3)
multiNewIds += supplier.id
}
Block 1 gets executed before supplierGroupInstance.save(flush:true) and Block 2 after save flush.
And then persist the old and new data to db ..
if(!(multiOriginalIds.equals(multiNewIds))){
actionLogService.savePreviousAndCurrentData(domainClass,id,multiOriginalIds,multiOriginalIdsCurrent,"suppliers")
}
Hence sum it all ..
Is my approach better towards capturing the old and new values of the hasMany props ?? Or is there any other approach ..pls advice.
Segment of controller code :
..........
//Performs the Update method on the Domain
def update(Long id, Long version) {
if(domainDebug) println domainName+"update() :params passed ${params}"
if(domainHasUpdatedByXmsLoginId){
params.updatedByXmsLoginId = sec.loggedInUserInfo(field:"id")
}
def multiOldIds = ""
def dirtyPropertyNames = ""
def multiNewIds = ""
def supplierGroupInstance = SupplierGroup.get(id)
String domainClass = this.class.getSimpleName().minus("Controller")
//set the hasmany porperty values
supplierGroupInstance.suppliers.each(){ supplier ->
if(multiOldIds != ""){
multiOldIds += ","
}
//gets me the comma separated (ex: 1,2,3)
multiOldIds += supplier.id
}
if (version != null) {
if (supplierGroupInstance.version > version) {
supplierGroupInstance.errors.rejectValue("version", "default.optimistic.locking.failure",
[message(code: 'supplierGroup.label', default: 'SupplierGroup')] as Object[],
"Another user has updated this SupplierGroup while you were editing")
render(view: "edit", model: [supplierGroupInstance: supplierGroupInstance])
return
}
}
supplierGroupInstance.properties = params
//below section is to retrive old and new values for primitive type propertyname
dirtyPropertyNames = supplierGroupInstance.getDirtyPropertyNames()
if(dirtyPropertyNames != null && dirtyPropertyNames.size() >0 && id !=null){
for (dirtyPropertyName in dirtyPropertyNames) {
def originalValue = supplierGroupInstance.getPersistentValue(dirtyPropertyName)
def currentValue = supplierGroupInstance."${dirtyPropertyName}"
// println "current value----> " +currentValue
// println "original value----->" + originalValue
if(!(originalValue.equals(currentValue))){
actionLogService.savePreviousAndCurrentData(domainClass,id,originalValue,currentValue,dirtyPropertyName)
}
}
}
if (!supplierGroupInstance.save(flush: true)) {
render(view: "edit", model: [supplierGroupInstance: supplierGroupInstance])
return
}
supplierGroupInstance.suppliers.each(){ supplier ->
if(multiNewIds != ""){
multiNewIds += ","
}
multiNewIds += supplier.id
}
if(!(multiOldIds.equals(multiNewIds))){
actionLogService.savePreviousAndCurrentData(domainClass,id,multiOriginalIds,multiOriginalIdsCurrent,"suppliers")
}
flash.message = message(code: 'default.updated.message', args: [message(code: 'supplierGroup.label', default: 'SupplierGroup'), supplierGroupInstance.id])
if(domainHasLastUpdated){
redirect(action: "list", params: [sort:"lastUpdated", order:"desc"])
} else {
redirect(action: "list")
}
}
The getPersistentValue works well on domain's field which is pulling domain's original state from hibernate cache. Having said that, this will work if you do the
beforeUpdate() in supplier/OrganizationUint end.

Grails Search Map

I am a Java programmer trying to write some Groovy code.
I want to iterate through the map called loginAttributevalues, and for each key (e.g. username) find the cross-reference attribute value and print this:
john : display_name
However, I am having trouble getting my Java brain around the Groovy syntax. Can someone point me in the right direction? Thanks.
loginAttributeValues = [username: 'john', email: 'john#smith.com']
def mapXref = [username: 'display_name',
firstname: 'first_name',
lastname: 'last_name',
email: 'email'}
for (String x : loginAttributeValues) {
if (mapXref[x])
println(mapXref.get(x))
}
This should work for you:
loginAttributeValues.each {
if (mapXref[it])
println(mapXref.get(it))
}
The Java for : each syntax is not valid in groovy. You are best of using an each closure.
Or, if you really want to keep the for loop syntax, then substitute in for :.
loginAttributeValues = [username: 'john', email: 'john#smith.com']
def mapXref = [username: 'display_name',
firstname: 'first_name',
lastname: 'last_name',
email: 'email']
def newMap = [:]
loginAttributeValues.each {k, v ->
newMap << (k in mapXref.keySet() ? [(v): mapXref[k]] : [:])
}
assert newMap.'john' == 'display_name'
assert newMap.'john#smith.com' == 'email'
I hope you need a map like
[john:display_name, john#smith.com:email]
but why do you need a value john as a key and display_name as value. should not it be otherwise. In that case, you would need
newMap << (k in mapXref.keySet() ? [mapXref[k] : v] : [:])
which would assert as
assert newMap.display_name == 'john'
assert newMap.email == 'john#smith.com'
Note:-
If it is guaranteed that loginAttributeValues will have keys subset of the keys in mapXref, then the logic can be optimized as below
def newMap = loginAttributeValues.collectEntries{k, v ->
[v, mapXref[k]]
}

Injecting value into inner tag

Having two nested tags, How is it possible to inject a variable into inner tag binding?
class CriteriaTagLib {
def criteria = { attrs, body ->
out << "start"
out << body.call()
out << "end"
}
def eq = {
out << "eq${group}"
}
}
And having in a GSP page :
<g:criteria>
<g:eq></g:eq>
<g:criteria>
The question is how to set a value for group (used inside of eq) from inside of criteria.
<q:criteria> can put own context/data as a request scope attribute (or page scope), and use it inside by <q:eq> (don't forget to remove it on closing tag). Like:
static final CONTEXT = this.class.name
def criteria = { attrs, body ->
def data = [
group: 'test 1'
]
request.setAttribute(CONTEXT, data)
out << "start"
out << body.call()
out << "end"
request.removeAttribute(CONTEXT)
}
def child = { attrs, body ->
def data = request.getAttribute(CONTEXT)
out << 'eq'
out << data.group
}
The inner tag eq has no knowledge of its outer tag criteria. But you could achieve something like below,
def criteria = { attrs, body ->
out << "start"
out << body('hello there')
out << "end"
}
def eq = {attrs->
out << "eq${attrs.group}"
}
and in the gsp page,
<g:criteria>
<g:eq group="${it}"/>
</g:criteria>

Resources