Написание плагина gradle с вложенными объектами расширения

Я работаю над плагином gradle (я не знаю groovy и просто начал использовать gradle), который интегрируется с Asgard (из netflix). Я хочу, чтобы мой объект расширения выглядел следующим образом:

asgard {
  url = "http://asgard"
  regions {
    "us-east-1" {
      autoScaling {
        {
          devPhase = "test"
          min = 3
          max = 6
          availabilityZones = ["us-east-1a", "us-east-1b", "us-east-1c"]
          ami = "Base AMI 2013-07-11"
          instanceType = "m3.xlarge"
          securityGroups = ["base", "test-application"]
        }
      }
    }
  }
}

Или что-то близкое к этому. Мне удалось приблизиться к этому, сделав autoScaling List, но когда я пытаюсь получить свойство из этого класса, он, похоже, возвращает динамическое свойство и никогда не имеет значения. Вот приведенный ниже плагин для начинающих:

import org.gradle.internal.reflect.Instantiator

class AsgardPlugin implements Plugin<Project> {
  void apply(Project project) {
    project.extensions.create("asgard", AsgardPluginExtension, project)
    project.asgard.extensions.regions = project.container(AsgardRegion) {String name ->
      AsgardRegion region = project.gradle.services.get(Instantiator).newInstance(AsgardRegion, name)
      assert region instanceof ExtensionAware

      region.extensions.add("autoScaling", project.container(AsgardAutoScaling))

      return region
    }


    project.task('displayConfigs') << {
      if(project.asgard.applicationName == null) project.asgard.applicationName = project.name
      println "Asgard URL $project.asgard.url"
      println "Application name $project.asgard.applicationName"
      println "Runs on regions..."
      project.asgard.regions.each() {region ->
        println "\tRegion $region.name"
        println "\tAutoScaling groups..."
        region.autoScaling.each() {asg ->
          println "\t\tdevPhase $asg"
          println "\t\tdevPhase $asg.devPhase"
          println "\t\tdevPhase $asg.get('devPhase')"
        }
      }
    }
  }
}

class AsgardPluginExtension {
  String url = "http://asgard.sisa.samsung.com"
  String applicationName

  AsgardPluginExtension(Project project) {
    applicationName = project.name
  }
}

class AsgardRegion {
  def String name
  List<AsgardAutoScaling> autoScaling
  AsgardRegion(String name) { this.name = name }
}

class AsgardAutoScaling {
  String devPhase
  int min
  int max
  List<String> availabilityZones
  String ami
  String instanceType
  String sshKey
  List<String> securityGroups
}

Когда я запускаю задачу, это то, что я вижу в журналах:

$  ./gradlew displayConfigs
:displayConfigs
Asgard URL http://asgard
Application name gradle-asgard-plugin
Runs on regions...
        Region us-east-1
        AutoScaling groups...
Creating properties on demand (a.k.a. dynamic properties) has been deprecated and is scheduled to be removed in Gradle 2.0. Please read http://gradle.org/docs/current/dsl/org.gradle.api.plugins.ExtraPropertiesExtension.html for information on the replacement for dynamic properties.
Deprecated dynamic property: "devPhase" on "[email protected]", value: "test".
Deprecated dynamic property: "min" on "[email protected]", value: "3".
Deprecated dynamic property: "max" on "[email protected]", value: "6".
Deprecated dynamic property: "availabilityZones" on "[email protected]", value: "[us-east-1a, us-east-1...".
Deprecated dynamic property: "ami" on "[email protected]", value: "Base AMI 2013-07-11".
Deprecated dynamic property: "instanceType" on "[email protected]", value: "m3.xlarge".
Deprecated dynamic property: "securityGroups" on "[email protected]", value: "[base, test-application]".
                devPhase
                devPhase test
                devPhase [email protected]('devPhase')

BUILD SUCCESSFUL

Total time: 1.996 secs

Неужели я все это делаю неправильно? Если нет, то как я могу получить значение из динамического свойства (попытался получить и значение, но они, похоже, не работают).

Спасибо, что прочитали это время.

Отредактировано EDIT @Обратная связь peter-niederwieser

Спасибо peter-niederwieser за ваши намеки. После принятия вашего совета и рассмотрения того, как работают закрытие, я думаю, что у меня есть лучшее понимание того, как сделать то, что я хотел попробовать.

Вот синтаксис плагина:

asgard {
  url = "http://asgard"
  regions {
    "us-east-1" {
      autoScaling {
          devPhase = "test"
          min = 3
          max = 6
          availabilityZones = ["a", "b", "c"]
          ami = "Base AMI 2013-07-11"
          instanceType = "m3.xlarge"
          securityGroups = ["base", "test-application"]
      }
      autoScaling {
        devPhase = "stage"
      }
    }

    "us-west-1" {
      autoScaling {
        devPhase = "test"
      }
      autoScaling {
        devPhase = "stage"
      }
    }

  }
} 

Код для поддержки этого можно найти здесь:

import org.gradle.internal.reflect.Instantiator

class AsgardPlugin implements Plugin<Project> {
  void apply(Project project) {
    project.extensions.create("asgard", AsgardPluginExtension, project)
    project.asgard.extensions.regions = project.container(AsgardRegion) {String name ->
      AsgardRegion region = project.gradle.services.get(Instantiator).newInstance(AsgardRegion, name, project)
      assert region instanceof ExtensionAware

      return region
    }


    project.task('displayConfigs') << {
      if(project.asgard.applicationName == null) project.asgard.applicationName = project.name
      println "Asgard URL $project.asgard.url"
      println "Application name $project.asgard.applicationName"
      println "Runs on regions..."
      project.asgard.regions.each() {region ->
        println "\tRegion $region.name"
        println "\tAutoScaling groups..."
        region.autoScaling.each() {asg ->
          println "\t\tdevPhase $asg.devPhase"
        }
      }
    }
  }
}

class AsgardPluginExtension {
  String urlgg
  String applicationName

  AsgardPluginExtension(Project project) {
    applicationName = project.name
  }
}

class AsgardRegion {
  String name
  Project project
  List<AsgardAutoScaling> autoScaling = []

  AsgardRegion(String name, Project project) {
    this.name = name
    this.project = project
  }

  void autoScaling(Closure closure) {
    def asg = new AsgardAutoScaling()
    project.configure(asg, closure)
    autoScaling.add(asg)
  }
}

class AsgardAutoScaling {
  String devPhase
  int min
  int max
  List<String> availabilityZones
  String ami
  String instanceType
  String sshKey
  List<String> securityGroups
}

Вот результат, который я вижу при запуске:

$  ./gradlew displayConfigs
:displayConfigs
Asgard URL http://asgard
Application name gradle-asgard-plugin
Runs on regions...
        Region us-east-1
        AutoScaling groups...
                devPhase test
                devPhase stage
        Region us-west-1
        AutoScaling groups...
                devPhase test
                devPhase stage

BUILD SUCCESSFUL

Total time: 1.929 secs

Ответы

Ответ 1

Некоторые подсказки:

  • Нет необходимости добавлять расширение autoScaling к region, потому что класс AsgardRegion уже имеет свойство autoScaling. Вам просто нужно инициализировать это свойство и, возможно, добавить метод void autoScaling(Closure closure) { project.configure(autoScaling, closure) } для поддержки синтаксиса autoScaling { ... }.

  • project.container создает NamedDomainObjectContainer. Литеральный синтаксис для этого контейнера someName { ... }; otherName { ... }. Синтаксиса "простого списка" нет.

  • Тестовый код устанавливает devPhase и т.д. для контейнера autoScaling, который не имеет этих свойств. Вот почему вы получаете предупреждения о динамических свойствах. Самый внутренний { ... } является блоком (а не замыканием) и является избыточным.

Чтобы решить проблему с списком, определите свойство name для AsgardAutoScaling и используйте регулярный синтаксис литерала NamedDomainObjectContainer или не используйте NamedDomainObjectContainer и реализуйте свой собственный синтаксис. Например, вы можете объявить метод autoScaling на AsgardRegion, который принимает закрытие, создает экземпляр AsgardAutoScaling и добавляет его в список.

Ответ 2

Предыдущий ответ помог мне решить мою проблему. Я хотел добавить вложенные объекты расширения в файл build.gradle. Как этот

family {
   father {
       firstName = "Ivan"
       lastName = "Karamazon"
   }
   children {
       son {
           firstName = "Alesha"
           lastName = "Rakitka"
       }
   }
}

Наконец, я смог сделать это и создал образец проекта. Однако требования были несколько разными, поэтому я рассказываю об этом здесь. Подробности см. В разделе "Ответ" fooobar.com/info/378322/... и проект Github https://github.com/andriipanasiuk/family-gradle-plugin