Adding object instance to gradle plugin extension

2019-04-08 04:28发布

问题:

I have something like the plugin below where I have one outer namespace and within it there is a single 'concrete' instance (mother) of an object plus another collection (children).

family {
  mother {
      firstname = 'John'
      lastname  = 'Cleese'
  }
  children {
      son {
        firstName = 'John'
        lastName  = 'Cleese'
      }
      daughter {
        firstName = 'Jane'
        lastName  = 'Cleese'
      }
  }
}

I am able to add the collection object and read the variables based on various examples I've seen but not sure how I add the concrete instance in addition.
How do I define it on the extension object?

Code which shows the issue - I would like to add mother as a single instance withing the plugin.

import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.*

class Person
{
  final String name
  String firstName
  String lastName
  Person(String name) { this.name = name }
}

class FamilyExtension {

  final NamedDomainObjectContainer<Person> children
  Person mother
  Person father

  FamilyExtension(children) {
    this.children = children
  }

  def children(Closure closure) {
    children.configure(closure)
  }
}

class FamilyPlugin implements Plugin<Project> {

  void apply(Project project) {
    project.task('sayHello', type: DefaultTask) << { 
      println(
        "Hello ${project.family.children["son"].firstName} " +
        "and hello ${project.family.children["daughter"].firstName} ")
    }

    def children  = project.container(Person)

    project.extensions.create("family", FamilyExtension, children)
  }
}
apply plugin: FamilyPlugin

family {
  // How do I add support for this?
  // mother {
  //     firstname = 'John'
  //     lastname  = 'Cleese'
  // }
  children {
      son {
        firstName = 'John'
        lastName  = 'Cleese'
      }
      daughter {
        firstName = 'Jane'
        lastName  = 'Cleese'
      }
  }
}

回答1:

I was having a similar problem to yours, but I figured out how to create the "mother" on my case, but not the "children". The way that I accomplished this is with a nested extension object.

Here is how I did this (mine is in pure java, so you may need to make some changes to do it in Groovy - I'm very new to Groovy, so I'm not sure exactly what needs to be changed):

When you add your extension in the plugin, the class will automatically support the ExtensionAware interface, and thus can have extensions added to it. In your FamilyExtension constructor add a new extension, casting the class to ExtensionAware first:

public FamilyExtension(NamedDomainObjectContainer<Person> children) {
    ((ExtensionAware) this).getExtensions().create("mother",Person.class);
    this.children = children;
}

This will only allow a single mother declared exactly as you have. Note, however, that it won't enforce only a single one, as you can add as many mothers as you want, but any later declarations will have the result of changing the value on that single instance, so there really is only one. You may also get a null value if the mother had not been declared, so will have to test for that (some demos say that this can happen, but in my own experiments it doesn't seem to).

To get your mother object, just cast your extension to ExtensionAware and retrieve the Person class off from that like you would get the family extension from the project

FamilyExtension fext = project.getExtensions().findByType(Person.class)
Person mother = (Person) ((ExtensionAware) fext).getExtensions().findByName("mother")

Edit: An alternative way would be just to define a function on your extension object which accepts a closure and have the closure delegate to a new mother object. Then you could use a Boolean flag to enforce the requirement of only one mother.

private boolean mother_defined = false;
private Person mother_value = null;  // or Person mother = new Person()

public void mother(Closure<?> motherdef) {
    if (mother_defined) {throw new Exception("Only one mother allowed");}
    mother_value = new Person();
    motherdef.setDelegate(mother);
    motherdef.call();
    mother_defined = true;
}


回答2:

Well, to add mother closure to the family you add the following code to the FamilyExtension class

Person mother(Closure closure) {
   mother = new Person()
   project.configure(mother, closure)
   return mother
}

If, at the same time you want to have children extension inside family one you add the following code into the FamilyPlugin.apply method

project.extensions.create("family", FamilyExtension, instantiator, project)
project.family.extensions.create("children", FamilyExtension.Children, project)

And then you add

void son(Closure closure) {
    Person son = new Person()
    project.configure(son, closure)
    children.add(son)
}

to be able to add son closure inside the children one.

To see the whole project and explanation with more details please visit Github project https://github.com/andriipanasiuk/family-gradle-plugin



标签: gradle