使用动态对象组配置gradle插件扩展的正确方法

时间:2015-11-15 06:38:21

标签: groovy gradle gradle-plugin

我正在尝试编写自己的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 {
    }
  }
}

1 个答案:

答案 0 :(得分:6)

实施此类DSL的另一种方法是使用extensionscontainers

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的groovy方式是元编程 - 在这种特殊情况下你需要实现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大小和类型 - 为了简洁起见省略了它。

当然,不需要为每个配置创建单独的类(我的意思是devprod等)。创建一个包含配置的类,并将它们全部存储在Map中,其中key是配置名称。

您可以找到演示here