Если вы пользовались системой сборки Gradle, то, вероятно, уже применяли и извлекали пользу из DSL-возможностей языка Groovy. В этой статье давайте рассмотрим такой пример.

Возьмем следующий фрагмент DSL из конфигурации Gradle.

plugins {
    id 'java'
}

В разделе plugins настраиваются плагины Gradle, используемые в вашей сборке, и упоминаются все плагины, которые вы хотите применить. Давайте разберемся, что происходит под капотом, чтобы добиться работоспособности, с помощью не очень сложного примера.

def plugins (def pluginConfig) {
    println "Plugins called"
}

//--------------- DSL
plugins {
    id 'java'
}

Когда мы запускаем приведенный выше код, то видим, что в консоли выводится "Plugins called" (вызываемые плагины). Мы понимаем, что plugins — это метод. Посмотрите на альтернативный способ вызова метода plugins.

plugins({
    id 'java'
})

Теперь понятно, что содержимое внутри '{}' передается в качестве аргумента методу plugins. Такие блоки кода в Groovy называются closures. Если последний аргумент вашего метода (он может быть и единственным) является замыканием (closure), Groovy позволяет писать их вне круглых скобок. Таким образом, приведенный выше код можно записать следующим способом.

plugins() {
    id 'java'
}

Кроме того, Groovy позволяет опускать круглые скобки, если метод имеет хотя бы один аргумент, что и произошло в данном случае. Таким образом, мы получаем оригинальный код, используемый в нашем DSL.

plugins {
    id 'java'
}

Поскольку мы хотим добавить плагины в проект, давайте создадим структуру для хранения информации о нем.

class Project {
    List<Plugin> plugins = []
}

import groovy.transform.ToString

@ToString(includePackage = false)
class Plugin {
    String id
}

Теперь нам осталось создать экземпляр класса Plugin и добавить его в plugins Project. Как вы уже догадались, id 'java' означает, что мы вызываем метод идентификации с 'java' в качестве аргумента. Давайте добавим этот метод в класс Project, поскольку он кажется подходящим местом для его размещения.

class Project {
    List<Plugin> plugins = []

    def id(String pluginId) {
        println "Adding plugin $pluginId"
        plugins << new Plugin(id: pluginId)
    }
}

Давайте обновим наш метод плагинов, чтобы вызвать замыкание на инстансе project.

def plugins(def pluginConfig) {
    Project project = new Project()
    println "Plugins called"
    project.with pluginConfig
}

В консоли должно появиться сообщение "Adding plugin java" (Добавление плагина java). Здесь мы используем метод with, чтобы обеспечить замыкание, которое мы получили в качестве аргумента, для объекта 'project' (а не для инстанса этого скрипта).

Давайте добавим еще один плагин в наш DSL и убедимся, что он работает так, как ожидалось. Заодно отобразим все плагины из проекта.

def plugins(def pluginConfig) {
    Project project = new Project()
    println "Plugins called"
    project.with pluginConfig
    println project.plugins
}

//---------------
plugins{
    id 'java'
    id 'groovy'
}

На консоли вы должны увидеть следующее:

Plugins called
Adding plugin java
Adding plugin groovy
[Plugin(java), Plugin(groovy)]

Конечно, полноценный DSL будет более сложным. Здесь я попытался передать суть идеи с помощью максимально простых примеров.


В заключение приглашаем всех желающих на открытое занятие «Shared Libraries в Jenkins», которое пройдет завтра в 20:00. На этом уроке посмотрим, как расширять пайплайны в Jenkins с помощью внешних библиотек и научимся их писать. Урок будет особенно полезен DevOps-инженерам и Java-разработчикам, которые хотят научиться создавать и настраивать сборки Java Backend проектов.

Комментарии (1)


  1. UbuRus
    26.06.2023 16:49
    +2

    Тем временем gradle перешел на Kotlin DSL по-умолчанию, так что закапывайте стюардессу)