使用动态对象组配置 gradle 插件扩展的正确方法
Correct way to configure gradle plugin extensions with groups of dynamic objects
我正在尝试编写自己的 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)
这工作正常,但这是一种非常丑陋的配置风格,不是典型的 gradle DSL 的风格。
如何将我的插件配置 DSL 更改为如下所示:
teregrin {
terraformVersion = '0.6.6'
roots {
dev {
accessKey = "flobble"
}
prd {
}
}
}
实现这种 DSL 的 gradle 方法是使用 extensions and 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
}
}
虽然 groovy 实现此类 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
每个参数的大小和类型都需要验证 - 为了简洁起见,它被省略了。
当然没有必要为每个配置创建一个单独的 class(我的意思是 dev
、prod
等)。创建一个 class 来保存配置并将它们全部存储在 Map
中,其中键是配置名称。
您可以找到演示 here。
我正在尝试编写自己的 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)
这工作正常,但这是一种非常丑陋的配置风格,不是典型的 gradle DSL 的风格。
如何将我的插件配置 DSL 更改为如下所示:
teregrin {
terraformVersion = '0.6.6'
roots {
dev {
accessKey = "flobble"
}
prd {
}
}
}
实现这种 DSL 的 gradle 方法是使用 extensions and 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
}
}
虽然 groovy 实现此类 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
每个参数的大小和类型都需要验证 - 为了简洁起见,它被省略了。
当然没有必要为每个配置创建一个单独的 class(我的意思是 dev
、prod
等)。创建一个 class 来保存配置并将它们全部存储在 Map
中,其中键是配置名称。
您可以找到演示 here。