2015-11-15 2 views
3

Я пытаюсь написать свой собственный плагин gradle, и он должен иметь возможность настраивать набор объектов - сколько из этих объектов и то, что они называются, вверх для пользователя.Правильный способ настройки расширений плагинов gradle с группами динамических объектов

Doco для создания пользовательских плагинов плагинов с расширенной настраиваемостью довольно беден. Он упоминает метод project.container() для этого, но я не мог понять, как заставить его работать в моей утилите.

Это пример конфигурации DSL моего плагина, как он стоит:

teregrin { 
    terraformVersion = '0.6.6' 

    root("dev"){ 
    accessKey = "flobble" 
    } 

    root("prd"){ 
    } 
} 

И это мой объект расширения плагин, который позволяет мне настроить:

class TeregrinPluginExtension { 
    boolean debug = false 
    boolean forceUnzip = false 
    String terraformVersion = null 

    Set<TeregrinRoot> roots = [] 

    def root(String name, Closure c){ 
    def newRoot = new TeregrinRoot(name) 
    c.setDelegate(newRoot) 
    c() 
    roots << newRoot 
    } 

} 

Расширения проводной в мой плагин стандартным образом:

project.extensions.create("teregrin", TeregrinPluginExtension) 

Это работает нормально, но это довольно уродливый стиль конфигурации, не совсем в стиле типичного DSL градации.

Как я могу изменить свой плагин конфигурации DSL, чтобы быть что-то вроде этого:

teregrin { 
    terraformVersion = '0.6.6' 

    roots { 
    dev { 
     accessKey = "flobble" 
    } 

    prd { 
    } 
    } 
} 

ответ

5

Gradle способ реализации такого DSL является использование extensions и containers:

apply plugin: SamplePlugin 

whatever { 
    whateverVersion = '0.6.6' 
    conf { 
    dev {} 
    qa {} 
    prod { 
     accessKey = 'prod' 
    } 
    } 
} 

task printWhatever << { 
    println whatever.whateverVersion 
    whatever.conf.each { c -> 
    println "$c.name -> $c.accessKey" 
    } 
} 

class SamplePlugin implements Plugin<Project> { 
    void apply(Project project) { 
    project.extensions.create('whatever', SampleWhatever) 
    project.whatever.extensions.conf = project.container(SampleConf) 
    project.whatever.conf.all { 
     accessKey = 'dev' 
    } 
    } 
} 

class SampleWhatever { 
    String whateverVersion 
} 

class SampleConf { 
    final String name 
    String accessKey 

    SampleConf(String name) { 
    this.name = name 
    } 
} 

в то время как заводной способ Выполнение такой DSL-мета-программирования - в этом конкретном случае вам необходимо реализовать methodMissing. Ниже приведен очень простой пример, который показывает, как это работает:

class SomeExtension { 
    def devConf = new SomeExtensionConf() 

    void methodMissing(String name, args) { 

    if ('dev'.equals(name)) { 
     def c = args[0] 
     c.resolveStrategy = Closure.DELEGATE_FIRST 
     c.delegate = devConf 
     c() 
    } else { 
     throw new MissingMethodException("Could not find $name method") 
    } 
    } 

    def getDev() { 
    devConf 
    } 
} 

class SomeExtensionConf { 
    def accessKey 
} 

project.extensions.create('some', SomeExtension) 

some { 
    dev { 
    accessKey = 'lol' 
    } 
} 

assert 'lol'.equals(some.dev.accessKey) 

Конечно, это не имеет никакого контроля ошибок - так args размер и тип каждого аргумента должны быть проверены - это опущено для краткости.

Конечно, нет необходимости создавать отдельный класс для каждой конфигурации (я имею в виду dev, prod и т. Д.). Создайте один класс, который содержит конфигурацию и сохранит их все в Map, где ключ - это имя конфигурации.

Вы можете найти демо-версию here.

+0

Относительно редактирования: Это часть, где я не понимаю, как использовать Project.container. Я хочу, чтобы элемент «roots» находился внутри элемента «teregrin». Или, действительно, даже лучше, в вашей DSL, как бы я работал с не повторяющимися группами, такими как «terraformVersion»? – Shorn

+1

Ahhhh - Я понял. Это был «project.whatever.extensions.conf = ...», который я не понял. Спасибо. – Shorn

Смежные вопросы