0

Here is a minimal example of a builder-style Groovy DSL for declaring e-mails.

email {
    from("pepa@depo.cz")
    to("lojza@depo.cz")
    body {
        html("Ahoj")
        // lots of other fields here
    }
}

For example, I want to extract the body part

    body {
        html("Ahoj")
        // lots of other fields here
    }

and reuse it in multiple e-mails. Probably it should look like this

email {
    from("pepa@depo.cz")
    to("lojza@depo.cz")
    myBody("Ahoj1")
}

email {
    from("pepa@depo.cz")
    to("lojza@depo.cz")
    myBody("Ahoj2")
}

I want to preserve IDE autocompletion in the myBody function.

/// This is the (abbreviated) DSL example from
///  http://docs.groovy-lang.org/docs/latest/html/documentation/core-domain-specific-languages.html#TheDelegatesToannotation-DelegatesTo

def email(@DelegatesTo(strategy = Closure.DELEGATE_ONLY, value = EmailSpec) Closure cl) {
    def email = new EmailSpec()
    def code = cl.rehydrate(email, this, this)
    code.resolveStrategy = Closure.DELEGATE_ONLY
    code()
}

class EmailSpec {
    void from(String from) { println "From: $from" }
    void to(String... to) { println "To: $to" }

    void body(@DelegatesTo(strategy = Closure.DELEGATE_ONLY, value = BodySpec) Closure body) {
        def bodySpec = new BodySpec()
        def code = body.rehydrate(bodySpec, this, this)
        code.resolveStrategy = Closure.DELEGATE_ONLY
        code()
    }
}

class BodySpec {
    void html(String html) { println "Body (html): $html" }
}

I am using the above DSL to make the question self-contained. In fact, I am interested in doing the same with the Jenkins Job DSL.

user7610
  • 25,267
  • 15
  • 124
  • 150
  • Here's one suggestion. Do not use the DSL, use a Java-like API when that is available. The advice is for Gradle Groovy DSL, https://discuss.gradle.org/t/how-to-refactor-common-configuration-code/5050/2. – user7610 Aug 05 '23 at 10:11

1 Answers1

0

Create a helper function

static <V> Closure<V> closureCtx(@DelegatesTo.Target context, @DelegatesTo Closure<V> closure) {
    return closure
}

then use it to set context for the the extracted DSL fragment

Closure<Void> myBody(String text) {
    return closureCtx(EmailSpec) { ->
        body {
            html(text)
            // lots of other fields here
        }
    }
}

This is sufficient to get the IDE autocompletions to work correctly when editing the fragment.

user7610
  • 25,267
  • 15
  • 124
  • 150