Alternatives Syntax

Alternative syntax selects a child class through the generated method name rather than an explicit class argument to the adder method:

Given the schema:

(See: AlternativesDocumentaryTest#'uses alternative names derived from child class names'.)

@DSL
class Config {
    String name
    Map<String, Element> elements
}

@DSL
abstract class Element {
    @Key String name
}

@DSL
class SubElement extends Element {
    String role
}

@DSL
class ChildElement extends Element {
    String game
}

instead of

Config.Create.With {
  elements {
    element(SubElement, "bla") {}
    element(ChildElement, "bli") {}
  }
}

You could also write:

Config.Create.With {
  elements {
    subElement("bla") {}
    childElement("bli") {}
  }
}

Keep these constraints in mind:

Naming Strategies

KlumAST chooses the first applicable strategy below. Use the most local strategy that makes the Model readable: field names for one exceptional relationship, shortName for a Schema type's deliberate public DSL name, and stripSuffix for a repeated type-family convention.

Defining Explicit Names for a Field

Use @Field(alternatives = ...) to map method names explicitly to classes:

(See: AlternativesDocumentaryTest#'uses field-local alternative names for one endpoint relationship'.)

@DSL
class Config {
    String name
    @Field(alternatives = {[subby: SubElement, childy: ChildElement]})
    Map<String, Element> elements
}

The map must be inside a closure and contain only literal Strings and literal Classes.

Explicit Short Names for Subclasses

Set shortName on @DSL when a subtype has a deliberate public DSL name:

@DSL(shortName = 'subby')
class SubElement extends Element {

    String role
}

@DSL(shortName = 'child')
class ChildElement extends Element {

    String game
}

allows:

(See: AlternativesDocumentaryTest#'uses deliberate subtype short names for endpoint alternatives'.)

Config.Create.With {
  elements {
    subby("bla") {}
    child("bli") {}
  }
}

Strip Common Suffixes

@DSL(stripSuffix = "Element") on the common base strips the suffix from child class names when determining the alternative method name. An explicit shortName still takes precedence:

given: // Schema
@DSL
class Config {
    Map<String, Element> elements
}

@DSL(stripSuffix = "Element")
abstract class Element {
    @Key String name
}

@DSL
class ServiceElement extends Element {
}

@DSL
class JobElement extends Element {
}

when: // Model
def config = Config.Create.With {
    elements {
        service("api") {}
        job("cleanup") {}
    }
}

then: // Assertions
assert config.elements.api instanceof ServiceElement
assert config.elements.cleanup instanceof JobElement

The executable example is AlternativesSpec.groovy, feature uses stripped suffixes for alternative method names.

Default: Derived from the Class Name

In any other case, KlumAST lowercases the first character of the subclass name. The opening SubElement and ChildElement example therefore produces subElement and childElement.

The executable example is AlternativesDocumentaryTest.groovy, feature uses alternative names derived from child class names.

For more complex cases, custom Factory Classes can be used.