repeat action several times and collect the result - groovy

I have an action which I need to do over an object several times and the collect the result of each action with that object.
Basically it looks like this
def one_action = { obj ->
def eval_object = process(obj)
eval_object.processed = true
return eval_object
}
def multiple_actions = { obj, n, action ->
def result = []
n.times {
result << action(obj)
}
return result
}
println multiple_actions(object, 10, one_action)
Is there a way to omit declaration of def result = [] and return the list directly from the closure?

You can collect the range, starting from zero:
def one_action = { obj ->
"a $obj"
}
def multiple_actions = { obj, n, action ->
(0..<n).collect { action obj }
}
assert multiple_actions("b", 3, one_action) == ["a b"] * 3

Related

In Groovy/Spock assert call methods are not executed

In Groovy Unit Test with Spock the following task is quite common:
assert myResult == calculateExpectedResult() (With or without the assert keyword.)
The groovy assert prints out lots of infomation on what is going on here and why my assertion failed. But when the compared objects are very complex and deep it can be tricky go get the concrete property that failed the test.
For this I found the Javers Framework that does an excellent Job comparing the objects and producing an exact diff. I created a trait to do this:
trait DiffTrait {
Javers javers = JaversBuilder.javers().build()
String diff(result, expected) {
Diff diff = javers.compare(result, expected);
def valueChanges = diff.getChangesByType(ValueChange)
String message = ""
valueChanges.each { message += "\n$it.propertyName = $it.left instead of expected: $it.right" }
return message
}
}
Now I can use it in my Unit Tests like this:
def expected = calculateExpectedResult()
assert myResult == expected, diff(myResult, expected)
This way I get a nicely printed list of differences.
But this is kind of verbose because I have to specify the values two times.
So I have changed the trait like this:
trait DiffTrait {
Javers javers = JaversBuilder.javers().build()
def result
def expected
String diff(result, expected) {
Diff diff = javers.compare(result, expected);
def valueChanges = diff.getChangesByType(ValueChange)
String message = ""
valueChanges.each { message += "\n$it.propertyName = $it.left instead of expected: $it.right" }
return message
}
String diff() {
diff(result, expected)
}
def result(result) {
this.result = result
return result
}
def expected(expected) {
this.expected = expected
return expected
}
}
The idea was to use it like this:
def result = callTheSystemToProduceTheRealResult()
def expected = calculateExpectedResult()
assert result(myResult) == expected(expected), diff()
But surprisingly this does not work! The two attributes are null and the diff Method fails with a NotNull-Exception. If I debug this code the expected/result methods are never called!
If I rewrite the code like this
def result = result(callTheSystemToProduceTheRealResult())
def expected = expected(calculateExpectedResult())
assert myResult == expected, diff()
everything works as expected. The methods get called correctly and the attributes are set.
My question is: Why can't I call these methods in the assert statement? What is the difference from the Groovy/Spock perspective of these two code fragements?
Here is a gist containing all the code as running example.
It is quite easy to explain. Assertion message is evaluated before the assertion itself. The following piece of code works perfectly, however it displays static diff message:
import org.javers.core.Javers
import org.javers.core.JaversBuilder
import org.javers.core.diff.Diff
import org.javers.core.diff.changetype.ValueChange
import spock.lang.Specification
class LolSpec extends Specification implements DiffTrait {
def 'lol'() {
expect:
def whatIGot = new Lol(l: 'a')
def whatIExpected = new Lol(l: 'b')
assert result(whatIGot) == expected(whatIExpected), 'diff'
}
}
trait DiffTrait {
Javers javers = JaversBuilder.javers().build()
def result
def expected
String diff() {
diff(result, expected)
}
String diff(result, expected) {
Diff diff = javers.compare(result, expected);
def valueChanges = diff.getChangesByType(ValueChange)
String message = ""
valueChanges.each { message += "\n$it.propertyName = $it.left instead of expected: $it.right" }
return message
}
def result(result) {
this.result = result
return result
}
def expected(expected) {
this.expected = expected
return expected
}
}
class Lol {
String l
}
You need to pass the arguments twice or change the implementation, e.g.:
import groovy.transform.EqualsAndHashCode
import org.javers.core.Javers
import org.javers.core.JaversBuilder
import org.javers.core.diff.changetype.ValueChange
import spock.lang.Specification
class LolSpec extends Specification {
def 'lol'() {
expect:
def whatIGot = new Lol(l: 'a')
def whatIExpected = new Lol(l: 'b')
def diff = new Diff(result: whatIGot, expected: whatIExpected)
assert diff.check(), diff.message()
}
}
class Diff {
Javers javers = JaversBuilder.javers().build()
def result
def expected
String message() {
def diff = javers.compare(result, expected);
def valueChanges = diff.getChangesByType(ValueChange)
String message = ""
valueChanges.each { message += "\n$it.propertyName = $it.left instead of expected: $it.right" }
return message
}
boolean check() {
result.equals(expected)
}
}
#EqualsAndHashCode
class Lol {
String l
}

Inline Conditional Map Literal in Groovy

Working on some translation / mapping functionality using Maps/JsonBuilder in Groovy.
Is is possible (without creating extra code outside of the map literal creation) .. to conditionally include/exclude certain key/value pairs ? Some thing along the lines of the following ..
def someConditional = true
def mapResult =
[
"id":123,
"somethingElse":[],
if(someConditional){ return ["onlyIfConditionalTrue":true]}
]
Expected results:
If someConditional if false, only 2 key/value pairs will exist in mapResult.
If someConditional if true, all 3 key/value pairs will exist.
Note that I'm sure it could be done if I create methods / and split things up.. for to keep things concise I would want to keep things inside of the map creation.
You can help yourself with with:
[a:1, b:2].with{
if (false) {
c = 1
}
it
}
With a small helper:
Map newMap(m=[:], Closure c) {
m.with c
m
}
E.g.:
def m = newMap {
a = 1
b = 1
if (true) {
c = 1
}
if (false) {
d = 1
}
}
assert m.a == 1
assert m.b == 1
assert m.c == 1
assert !m.containsKey('d')
Or pass an initial map:
newMap(a:1, b:2) {
if (true) {
c = 1
}
if (false) {
d = 1
}
}
edit
Since Groovy 2.5, there is an alternative for with called tap. It
works like with but does not return the return value from the closure,
but the delegate. So this can be written as:
[a:1, b:2].tap{
if (false) {
c = 1
}
}
You could potentially map all false conditions to a common key (e.g. "/dev/null", "", etc) and then remove that key afterwards as part of a contract. Consider the following:
def condA = true
def condB = false
def condC = false
def mapResult =
[
"id":123,
"somethingElse":[],
(condA ? "condA" : "") : "hello",
(condB ? "condB" : "") : "abc",
(condB ? "condC" : "") : "ijk",
]
// mandatory, arguably reasonable
mapResult.remove("")
assert 3 == mapResult.keySet().size()
assert 123 == mapResult["id"]
assert [] == mapResult["somethingElse"]
assert "hello" == mapResult["condA"]
There is no such syntax, the best you can do is
def someConditional = true
def mapResult = [
"id":123,
"somethingElse":[]
]
if (someConditional) {
mapResult.onlyIfConditionalTrue = true
}
I agree with Donal, without code outside of map creation it is difficult.
At least you would have to implement your own ConditionalMap, it is a little work but perfectly doable.
Each element could have it's own condition like
map["a"] = "A"
map["b"] = "B"
map.put("c","C", true)
map.put("d","D", { myCondition })
etc...
Here an incomplete example (I did only put, get, keySet, values and size to illustrate, and not typed - but you probably don't need types here?), you will probably have to implement few others (isEmpty, containsKey etc...).
class ConditionalMap extends HashMap {
/** Default condition can be a closure */
def defaultCondition = true
/** Put an elemtn with default condition */
def put(key, value) {
super.put(key, new Tuple(defaultCondition, value))
}
/** Put an elemetn with specific condition */
def put(key, value, condition) {
super.put(key, new Tuple(condition, value))
}
/** Get visible element only */
def get(key) {
def tuple = super.get(key)
tuple[0] == true ? tuple[1] : null
}
/** Not part of Map , just to know the real size*/
def int realSize() {
super.keySet().size()
}
/** Includes only the "visible" elements keys */
def Set keySet() {
super.keySet().inject(new HashSet(),
{ result, key
->
def tuple = super.get(key)
if (tuple[0])
result.add(key)
result
})
}
/** Includes only the "visible" elements keys */
def Collection values() {
this.keySet().asCollection().collect({ k -> this[k] })
}
/** Includes only the "visible" elements keys */
def int size() {
this.keySet().size()
}
}
/** default condition that do not accept elements */
def map = new ConditionalMap(defaultCondition: false)
/** condition can be a closure too */
// def map = new ConditionalMap(defaultCondition : {-> true == false })
map["a"] = "A"
map["b"] = "B"
map.put("c","C", true)
map.put("d","D", false)
assert map.size() == 1
assert map.realSize() == 4
println map["a"]
println map["b"]
println map["c"]
println map["d"]
println "size: ${map.size()}"
println "realSize: ${map.realSize()}"
println "keySet: ${map.keySet()}"
println "values: ${map.values()}"
/** end of script */
You can use the spread operator to do this for for both maps and lists:
def t = true
def map = [
a:5,
*:(t ? [b:6] : [:])
]
println(map)
[a:5, b:6]
This works in v3, haven't tried in prior versions.

Call method multiple times with each member of a collection as parameter

Let's say that I have a collection of parameters
def params = ['a','b','c']
Is there a short way to run a method that accepts a single parameter once for every element of a collection to replace this:
params.each {
foo(it)
}
with something more declarative (like a "reverse" spread operator)?
You can use collect:
def params = ['a','b','c']
def foo(param) {
'foo-' + param
}
assert ['foo-a', 'foo-b', 'foo-c'] == params.collect { foo(it) }
Or just a closure
def foo = { a -> a + 2 }
def modified = list.collect foo
You can use method pointer:
def l = [1,2,3]
l.each(new A().&lol)
class A {
def lol(l) {
println l
}
}
Or add a method that will do the task you need:
def l = [1,2,3]
List.metaClass.all = { c ->
delegate.collect(c)
}
l.all(new A().&lol)
class A {
def lol(l) {
println l
return l+2
}
}

Assigning values dynamically from a groovy config file

As I am reading values from a file in my Groovy code, I want to assign these values to the equivalent properties in my object as i am iterating through the map values!
code:
new ConfigSlurper().parse(new File(configManager.config.myFile.filepath)
.toURI().toURL()).each { k,v ->
if (k == 'something') {
v.each {
myObject.$it =v.$it
// so here i want this dynamic assignment to occur
}
}
}
You code there would already work like this, if you would use the form:
myObject."$it.key" = it.value
Here is a slightly more protective version:
class MyObject {
Long x,y
}
def obj = new MyObject()
def cfg = new ConfigSlurper().parse('''\
a {
x = 42
y = 666
}
b {
x = 93
y = 23
}''')
cfg.b.findAll{ obj.hasProperty(it.key) }.each{
obj.setProperty(it.key,it.value)
}
assert obj.x==93 && obj.y==23

Is it possible to get the caller object of a closure in groovy?

Is it possible to get a reference of the Object that invoked a Closure in the Closure's execution context?
For example:
public class Example {
public Example(){
def a = {return this};
def b = [];
b.metaClass.a = a;
println b.a();
}
}
I want this execution to return b instead of an instance of Example.
The object that the closure is invoked on can be referenced as delegate. Example:
def a = { return delegate }
def b = []
b.metaClass.a = a
assert b.a() == b

Resources