Able to subclass domain objects in Gradle Plugin DSL?


(ambcloses) #1

Hello,

I am working on a Gradle Plugin DSL. I would like to know if I there is a way to use subclasses of domain objects in my DSL. For example, I would like my DSL to do conceptually something like:

jobs {
  BaseJob 'job1' {
    propertyA = "a"
  }
  ExtendedJob 'job2' {
    propertyA = "a"
    propertyB = "b"
  }
}

where in my plugin, I have something like:

class MyJobPlugin implements Plugin<Project> {
  void apply(Project project) {
    def jobs = project.container(BaseJob)
    project.extensions.jobs = jobs
  }
}
  // and my classes look like:
  class BaseJob {
  String name;
  String propertyA;
}
  class ExtendedJob extends BaseJob {
  String propertyB;
}

I think I can do something like this by using the NamedDomainObjectContainer.create() method that accepts a NamedDomainObjectFactory, by doing something hacky like naming my objects with a prefix name that is the class to be created, like “BaseJob-job1” and “ExtendedJob-job2” and then parsing out the class name. Obviously, this is less desirable than some way to specify exactly what class to create in the DSL itself.

Thanks, Alex


(Peter Niederwieser) #2

That’s what ‘PolymorphicDomainObjectContainer’ is for, but there isn’t currently a public API for creating such a container.


(ambcloses) #3

I’m open to suggestions! Just FYI, I am a software engineer at LinkedIn - I’ll see if Szczepan has any ideas.


(ambcloses) #4

Just to tie off on this - instead of using containers, I wrote an extension class. My extension classes look like:

class MyJobPlugin implements Plugin<Project> {
  void apply(Project project) {
    project.extensions.add("jobs", new JobsExtension(project))
  }
}
  class JobsExtension {
  final List<BaseJob> jobs;
  final Project project;
    JobsExtension(Project project) {
    this.jobs = new ArrayList<BaseJob>();
    this.project = project;
  }
    BaseJob configureAndAdd(BaseJob job, Closure configure) {
    project.configure(job, configure);
    jobs.add(job);
    return job;
  }
  BaseJob baseJob(String name, Closure configure) {
    return configureAndAdd(new BaseJob(name), configure);
  }
  ExtendedJob extendedJob(Closure configure) {
    return configureAndAdd(new ExtendedJob(), configure);
  }
}

and then my DSL looks like:

jobs {
  baseJob('job1') {
    propertyA = "a"
  }
  extendedJob('job2') {
    propertyA = "a"
    propertyB = "b"
  }
}

With this solution, the DSL is still pretty nice and the extension classes are straightforward (no containers, no internal API, etc).