Hi,

TL;TR. I would like to have a way to inform IDE that a passed in an
annotation Closure's execution is in fact delegated to a specific object
(type). E.g. "@Requires({ jvm.java11 })" should allow to write "jv" and
see jvm in IDE (once a delegate has that field/getter).


More detailed version.

One of the nice features of Spock is an ability to define conditions in
Closure when test(s) should (not) be executes with @Required/@IgnoreIf.

@Requires({ isSpecialConditionFulfilled() })
def "some test"() {}

The annotation itself is defined as:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@ExtensionAnnotation(RequiresExtension.class)
public @interface Requires {
  Class<? extends Closure> value();
}

Down the line the execution is delegated to PreconditionContext which
provides convenient methods/objects such as os.linux, jvm.java11, env, etc.

@Requires({ jvm.java11 && os.linux })

Unfortunately, there is no code completion as IDE doesn't know about
that delegation (and jvm, os fields/methods). Groovy 2 introduced
@DelegatesTo, however it cannot be used with other annotations or methods.

It can be tricked by creating a static final instance of
PreconditionContext (it's stateless) somewhere (e.g. in the
Specification super class), but people has to know to refer it, e.g:

@Requires({ CTX.jvm.java11 })

Alternatively, I was thinking about a method in the base Spock class:

    protected static def ctx(@DelegatesTo(PreconditionContext) Closure
closure) {
        closure()
    }

which could be referenced as:

    @Requires({ ctx { jvm.java10Compatible } })

It works, but again "ctx" has to be referenced on demand.

The best long-term solution would be to allow to use @DelegatesTo at the
method level:

public @interface MyRequires {
  @DelegatesTo(PreconditionContext.class) Class<? extends Closure> value();
}

It would provide code completion out of the box (once IDEs have support
for that :) ). The second main drawback I see is being not very
intuitive declaration and limited usage.


Having Groovy 3.0 on the horizon (with breaking changes postponed to
Groovy 4) I wonder do you see any elegant solution to deal with the
aforementioned case in Spock itself (Spock 2 could potentially break
compatibility if needed) or in Groovy (that potentially could be
implemented before 3.0-final)?

Marcin

-- 
https://blog.solidsoft.info/ - Working code is not enough

Reply via email to