5

我正在尝试编写自己的 gradle 插件,它需要能够配置一组对象 - 这些对象中有多少以及它们被称为什么取决于用户。

用于创建具有高级可定制性的自定义 gradle 插件的文档非常差。它提到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 {
    }
  }
}
4

1 回答 1

8

实现这种 DSL 的渐变方式是使用扩展容器

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其中键是配置名称的地方。

你可以在这里找到一个演示。

于 2015-11-15T10:12:23.263 回答