title: Writing gradle plugin with nested extension objects
body: <p>I am working on a gradle plugin (i dont know groovy, and just started using gradle) that integrates with Asgard (from netflix).  I want my extension object to look like this for the user:</p>
  
  
  
  <pre class="lang-groovy prettyprint-override"><code>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"]
          }
        }
      }
    }
  }
  </code></pre>
  
  <p>Or something close to that.  I have been able to get close to this by making autoScaling a List, but when ever i try to get a property from that class it seems to return a dynamic property and never the value.  Here is the below starter plugin:</p>
  
  <pre class="lang-groovy prettyprint-override"><code>import org.gradle.internal.reflect.Instantiator
  
  class AsgardPlugin implements Plugin&lt;Project&gt; {
    void apply(Project project) {
      project.extensions.create("asgard", AsgardPluginExtension, project)
      project.asgard.extensions.regions = project.container(AsgardRegion) {String name -&gt;
        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') &lt;&lt; {
        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 -&gt;
          println "\tRegion $region.name"
          println "\tAutoScaling groups..."
          region.autoScaling.each() {asg -&gt;
            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&lt;AsgardAutoScaling&gt; autoScaling
    AsgardRegion(String name) { this.name = name }
  }
  
  class AsgardAutoScaling {
    String devPhase
    int min
    int max
    List&lt;String&gt; availabilityZones
    String ami
    String instanceType
    String sshKey
    List&lt;String&gt; securityGroups
  }
  </code></pre>
  
  <p>When I run the task, this is what I see in the logs:</p>
  
  <pre class="lang-groovy prettyprint-override"><code>$  ./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 "AsgardRegion_Decorated@65087be0", value: "test".
  Deprecated dynamic property: "min" on "AsgardRegion_Decorated@65087be0", value: "3".
  Deprecated dynamic property: "max" on "AsgardRegion_Decorated@65087be0", value: "6".
  Deprecated dynamic property: "availabilityZones" on "AsgardRegion_Decorated@65087be0", value: "[us-east-1a, us-east-1...".
  Deprecated dynamic property: "ami" on "AsgardRegion_Decorated@65087be0", value: "Base AMI 2013-07-11".
  Deprecated dynamic property: "instanceType" on "AsgardRegion_Decorated@65087be0", value: "m3.xlarge".
  Deprecated dynamic property: "securityGroups" on "AsgardRegion_Decorated@65087be0", value: "[base, test-application]".
                  devPhase
                  devPhase test
                  devPhase AsgardRegion_Decorated@14860315('devPhase')
  
  BUILD SUCCESSFUL
  
  Total time: 1.996 secs
  </code></pre>
  
  <p>Am I going about this all wrong?  If not how can I get the value from the dynamic property (tried get and value, but those don't seem to work).</p>
  
  <p>Thanks for your time reading this.</p>
  
  <p>EDIT based off @peter-niederwieser feedback</p>
  
  <p>Thanks peter-niederwieser for your hints.  After taking your advice and looking at how the closures work I think i have a better understanding of how to set out to do what I wanted to try out.</p>
  
  <p>Here is the syntax of the plugin now:</p>
  
  <pre class="lang-groovy prettyprint-override"><code>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"
        }
      }
  
    }
  } 
  </code></pre>
  
  <p>The code to support this can be found here:</p>
  
  <pre class="lang-groovy prettyprint-override"><code>import org.gradle.internal.reflect.Instantiator
  
  class AsgardPlugin implements Plugin&lt;Project&gt; {
    void apply(Project project) {
      project.extensions.create("asgard", AsgardPluginExtension, project)
      project.asgard.extensions.regions = project.container(AsgardRegion) {String name -&gt;
        AsgardRegion region = project.gradle.services.get(Instantiator).newInstance(AsgardRegion, name, project)
        assert region instanceof ExtensionAware
  
        return region
      }
  
  
      project.task('displayConfigs') &lt;&lt; {
        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 -&gt;
          println "\tRegion $region.name"
          println "\tAutoScaling groups..."
          region.autoScaling.each() {asg -&gt;
            println "\t\tdevPhase $asg.devPhase"
          }
        }
      }
    }
  }
  
  class AsgardPluginExtension {
    String urlgg
    String applicationName
  
    AsgardPluginExtension(Project project) {
      applicationName = project.name
    }
  }
  
  class AsgardRegion {
    String name
    Project project
    List&lt;AsgardAutoScaling&gt; 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&lt;String&gt; availabilityZones
    String ami
    String instanceType
    String sshKey
    List&lt;String&gt; securityGroups
  }
  </code></pre>
  
  <p>Here is the output I see when I run:</p>
  
  <pre class="lang-none prettyprint-override"><code>$  ./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
  </code></pre>