Spock reusing generic closure - groovy

I have this code in Spock:
then:
1 * dao.getByValue(Something.ONE, _ as String) >> {Something smth, String value ->
return createSomething(smth).withValue(value).build()
}
It doesn't look exactly like that, but you get the point. I want to return an object based on arguments passed to the method, in the real version this object is loaded from database.
The point is that I have this call in a lot of places and it looks exactly the same everywhere. Could I somehow extract this closure and use it everywhere, like this:
then:
1 * dao.getByValue(Something.ONE, _ as String) >> Closures.makeSomething
I tried using Intellij extract feature, but it kinda went crazy there with types, after I edited the types manually I had weird errors:
public static final Closure<Optional<Something>> makeSomething = { Something smth, String value ->
return createSomething(smth).withValue(value).build()
}
...
1 * dao.getByValue(Something.ONE, _ as String) >> makeSomething
org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object 'mypackage.MySpec$__clinit__closure1#1757cd72' with class 'mypackage.MySpec$__clinit__closure1' to class 'java.util.Optional'
Even that one didn't work, and I thought it would:
public static final Closure<Optional<Something>> makeSomething = { Something smth, String value ->
return createSomething(smth).withValue(value).build()
}
...
1 * dao.getByValue(Something.ONE, _ as String) >> {args -> makeSomething.call(args[0], args[1]) }
groovy.lang.MissingMethodException: No signature of method: mypackage.MySpec$__clinit__closure2.call() is applicable for argument types: (java.util.Arrays$ArrayList) values: [[mypackage.Something$$Lambda$6/1105423942#6f45df59, ...]]
I'm not good at Groovy or Spock in general, I'm just trying this out for now.
Edit:
Working code after #tim_yates suggestion (whole interaction is in the helper method):
then:
interaction {
somethingCall(2, Something.TWO)
somethingCall(3, Something.ONE)
}
}
private void somethingCall(int times, Something something) {
times * dao.getByValue(something, _ as String) >> { Something smth, String value ->
return createSomething(smth).withValue(value).build()
}
}
Not working code that I'd like (only the return value is in the helper method):
then:
2 * dao.getByValue(Something.TWO, _ as String) >> makeSomething
3 * dao.getByValue(Something.ONE, _ as String) >> makeSomething
}
public static final Closure<Optional<Something>> makeSomething = { Something smth, String value ->
return createSomething(smth).withValue(value).build()
}
If I simply inline each >> makeSomething and write there it's body instead, then it works.

You have a conceptual problem here. You cannot just split the closure from the preceding code because if you look at it
dao.getByValue(something, _ as String) >> { Something smth, String value ->
return createSomething(smth).withValue(value).build()
}
you might notice that smth and value inside the closure get their values from getByValue(something, _ as String).
Now if you factor out the in-line closure part into a stand-alone closure instance, you lose that connection. First of all, >> makeSomething has no parameters, secondly you do not evaluate the makeSomething closure, i.e. on the right hand side you do not get your Optional instance but a Closure instance. In order to evaluate the closure you have to call it with parameters, i.e. something like >> makeSomething(something, "dummy") would work. But this way you have to repeat the first getByValue parameter and make up a dummy for the second, unspecified one because you have no easy way to refer to it other than introducing yet another closure like >> { Something smth, String value -> makeSomething(smth, value) }. But then you are not saving a lot of code.
It is your decision if this is nicer than somethingCall(2, Something.TWO) (I like it, actually) or if you go for my contrived construct. What I cannot do for you is change Groovy or Spock DSL syntax just because you prefer it to look different.

Related

Subtracting Enum elements in groovy

Let's assume we have an enum:
enum MyEnum
{
ONE, TWO
}
we can easily subtract MyEnum values from each other by using:
def a = MyEnum.values()
println (a - MyEnum.values()) //results: []
but if we try to use strong typing, we don't receive an empty list:
Collection<MyEnum> a = MyEnum.values()
println (a - MyEnum.values()) //results: [ONE, TWO]
What type should we use to properly subtract MyEnum values and why?
Given that the return type of E.values() is E[] the obvious answer
would be
MyEnum[] a = MyEnum.values()
The way how two arrays are "subtracted" is defined in
org.codehaus.groovy.runtime.DefaultGroovyMethods:
public static <T> T[] minus(T[] self, Object[] removeMe) {
But if you are using something "collection-y" this is used (e.g. for a Set here):
public static <T> Set<T> minus(Set<T> self, Object removeMe)
(Only the given element is about to be removed - not each element).
So if you want something "collection-y", then you also have to turn the
array into something iterable or "collection-y". E.g. this works also:
Set<MyEnum> a = MyEnum.values()
println (a - MyEnum.values().toList())
// → []
And if you want to be explicit you might as well use a function like
removeAll.

String.equals(StringBuilder) and StringBuilder.equals(String) Confusion

So I am given this piece of code and asked the question: What will be the result of compiling and executing Test class.
package com.foo.bar;
public class Test {
public static void main(String[] args) {
String str = "java";
StringBuilder sb = new StringBuilder("java");
System.out.println(str.equals(sb) + ":" + sb.equals(str));
}
}
The result according to them and the program when run through Eclipse is
false:false
I do not understand why it gives the above output. I thought String class overrides .equals so that it converts the values into strings and compares them. i.e. :
Object b, Object c ->
b.equals(c) ->
b.toString.equals(c.toString()) //b is a String
It is running the String.equals(). If you have str -> "java" and sb -> "java" which both override toString.
str.equals(sb) -> str.toString().equals(b.toString) -> true
No, String.equals does not convert the other argument to a String then compare the characters.
Compares this string to the specified object. The result is true if and only if the argument is not null and is a String object that represents the same sequence of characters as this object.
If the other object is not a String, e.g. a StringBuilder, then it will always return false. It will not convert the object to a String.
Like any well-formed equals method, it will test if the given object is the same class (ensuring it's not null first), and if it is, cast it to a String, but it won't call toString or otherwise convert the given object to a String.

Groovy: Constructor hash collision

I have the following groovy code:
def script
String credentials_id
String repository_path
String relative_directory
String repository_url
CredentialsWrapper(script, credentials_id, repository_name, repository_group, relative_directory=null) {
this(script, credentials_id, 'git#gitlab.foo.com:' + repository_group +'/' + repository_name + '.git', relative_directory);
}
CredentialsWrapper(script, credentials_id, repository_url, relative_directory=null) {
this.script = script;
this.credentials_id = credentials_id;
this.repository_url = repository_url;
if (null == relative_directory) {
int lastSeparatorIndex = repository_url.lastIndexOf("/");
int indexOfExt = repository_url.indexOf(".git");
this.relative_directory = repository_url.substring(lastSeparatorIndex+1, indexOfExt);
}
}
Jenkins gives me the following:
Unable to compile class com.foo.CredentialsWrapper due to hash collision in constructors # line 30, column 7.
I do not understand why, the constructors are different, they do not have the same number of arguments.
Also, "script" is an instance from "WorkflowScript", but I do not know what I should import to access this class, which would allow me to declare script explicitly instead of using "def"
Any idea ?
When you call the Constructor with four parameters, would you like to call the first or the second one?
If you write an constructor/method with default values, groovy will actually generate two or more versions.
So
Test(String x, String y ="test")
will result in
Test(String x, String y) {...}
and
Test(String x) {new Test(x, "test")}
So your code would like to compile to 4 constructors, but it contains the constructor with the signature
CredentialsWrapper(def, def, def, def)
two times.
If I understand your code correctly, you can omit one or both of the =null. The result will be the same, but you will get only two or three signatures. Then you can choose between both versions by calling calling them with the right parameter count.

Problems overloading Groovy comparison operators

I am building an analytic application using Groovy and require very forgiving math operators regardless of data format. I achieve this through operator overloading, in many cases improving (in my case) on the default Groovy type flexibility. As an example, I need 123.45f + "05" to equal 128.45f. By default Groovy downgrades to String and I get 123.4505.
In most cases my overloading works very well, but not for comparison operators. I've followed a couple of discussions on this, but I'm not getting to an answer and I'm looking for ideas. I recognize that the goal is to overload compareTo() (vs. something like lessThan), but Groovy seems to ignore this and instead attempts its own smart comparison - e.g. DefaultTypeTransformation.compareTo(Object left, Object right), which fails on mixed types.
Unfortunately this is a must have for me, because improperly comparing two values compromises the whole solution and I don't have control over some of the data types being analyzed (e.g. vendor data structures).
For example, I need the following to work:
Float f = 123.45f;
String s = "0300";
Assert.assertTrue( f < s );
I have many permutations of these, but my attempt to overload includes (let's just assume my JavaTypeUtil does what I need if I can get Groovy to call it):
// overloads on startup, trying to catch all cases
Float.metaClass.compareTo = {
Object o -> JavaTypeUtil.compareTo(delegate, o) }
Float.metaClass.compareTo = {
String s -> JavaTypeUtil.compareTo(delegate, s) }
Object.metaClass.compareTo = {
String s -> JavaTypeUtil.compareTo(delegate, s) }
Object.metaClass.compareTo = {
Object o -> JavaTypeUtil.compareTo(delegate, o) }
When I try the above test, none of these are called and instead I get:
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Float
at java.lang.Float.compareTo(Float.java:50)
at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.compareToWithEqualityCheck(DefaultTypeTransformation.java:585)
at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.compareTo(DefaultTypeTransformation.java:540)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.compareTo(ScriptBytecodeAdapter.java:690)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.compareLessThan(ScriptBytecodeAdapter.java:710)
at com.modelshop.datacore.generator.GroovyMathTests.testMath(GroovyMathTests.groovy:32)
Debugging through I see that the < operator goes right to ScriptBytecodeAdapter.compareLessThan(), and the implementation of that seems to ignore the overloaded compareTo() here:
In DefaultTypeTransformations.java:584 (2.4.3)
if (!equalityCheckOnly || left.getClass().isAssignableFrom(right.getClass())
|| (right.getClass() != Object.class && right.getClass().isAssignableFrom(left.getClass())) //GROOVY-4046
|| (left instanceof GString && right instanceof String)) {
Comparable comparable = (Comparable) left;
return comparable.compareTo(right); // <--- ***
}
In a desperate attempt, I've also tried to overload compareLessThan, but I'm grasping now, I don't know that there's any way to jump in front of the < mapping in Groovy.
Float.metaClass.compareLessThan << {
Object right -> JavaTypeUtil.compareTo(delegate, right) < 0 }
Float.metaClass.compareLessThan << {
String right -> JavaTypeUtil.compareTo(delegate, right) < 0 }
Any thoughts on a work-around? Thanks!
Part of it is you need to include static, like this
Float.metaClass.static.compareTo = { String s -> 0 }
This makes f.compareTo(s) work, but the < operator still won't work. This is a known limitation. The only operators that can be overloaded are mentioned in the documentation. Possibly you could do a custom AST to change all those operators to a compareTo().
But this isn't the whole story. f <=> s also doesn't work, despite <=> delegating to compareTo(). I believe this is because Float doesn't implement Comparable<Object> or Comparable<String>, only Comparable<Float>. Although I'm not sure where exactly in the chain Groovy makes the decision not to use that method, you can see it's not limited to Groovy's math classes. This also doesn't work
Foo.metaClass.compareTo = { String s -> 99 }
new Foo() <=> ''
class Foo implements Comparable<Foo> {
int compareTo(Foo o) {
0
}
}
I think Groovy is doing some pre-parsing validation that's preventing the metaclass stuff from working. Whatever validation it's doing definitely inspects the interfaces implemented, because this fails for a different reason
Foo.metaClass.compareTo = { String s -> 99 }
new Foo() <=> ''
class Foo {
int compareTo(Foo o) {
0
}
}
In both of these examples, replacing <=> with compareTo() works.
This question has been asked a couple times before, but I haven't seen a good explanation for why. You might try asking on the user mailing list. I'm sure Jochen or Cedric would be able to explain why.
I guess the point is that your compareTo closure is expecting an Object; so when you invoke compareTo with a String, your closure doesn't get called at all.
I can only think of the following; being precise when specifying closure input parameter type:
Float.metaClass.compareTo = { Integer n -> aStaticHelperMethod(n) }
Float.metaClass.compareTo = { String s -> aStaticHelperMethod(s) }
Float.metaClass.compareTo = { SomeOtherType o -> aStaticHelperMethod(o) }

Groovy named and default arguments

Groovy supports both default, and named arguments. I just dont see them working together.
I need some classes to support construction using simple non named arguments, and using named arguments like below:
def a1 = new A(2)
def a2 = new A(a: 200, b: "non default")
class A extends SomeBase {
def props
A(a=1, b="str") {
_init(a, b)
}
A(args) {
// use the values in the args map:
_init(args.a, args.b)
props = args
}
private _init(a, b) {
}
}
Is it generally good practice to support both at the same time? Is the above code the only way to it?
The given code will cause some problems. In particular, it'll generate two constructors with a single Object parameter. The first constructor generates bytecode equivalent to:
A() // a,b both default
A(Object) // a set, b default
A(Object, Object) // pass in both
The second generates this:
A(Object) // accepts any object
You can get around this problem by adding some types. Even though groovy has dynamic typing, the type declarations in methods and constructors still matter. For example:
A(int a = 1, String b = "str") { ... }
A(Map args) { ... }
As for good practices, I'd simply use one of the groovy.transform.Canonical or groovy.transform.TupleConstructor annotations. They will provide correct property map and positional parameter constructors automatically. TupleConstructor provides the constructors only, Canonical applies some other best practices with regards to equals, hashCode, and toString.

Resources