On 15 January 2013 14:45, Adam Murdoch <[email protected]> wrote:
>
> On 15/01/2013, at 10:27 PM, Luke Daley wrote:
>
> Wanted to get a thread started on ideas for how this might work.
>
> The problem generally is that our containers internalise construction, and
> offer no way to parameterise it.
>
> sourceSets {
> custom {}
> }
>
> The use has no way of influencing what kind of thing “custom” is. The
> immediate need for this kind of thing is with the publications container.
> The user needs to be able to add arbitrary maven, ivy etc. publication
> objects.
>
> Options:
>
> 1. Add via concrete types, with rigid constructor contract
>
> To add something of a certain type, you specify the actual type, which
> must have a 1 string arg constructor (the item name)…
>
> publications {
> custom(DefaultMavenPublication)
> }
>
> class DefaultMavenPublication implements MavenPublication {
> DefaultMavenPublication(String name) { … }
> }
>
> 2. Add via concrete types, allowing constructor params
>
> publications {
> custom(DefaultMavenPublication, arg1, arg2)
> }
>
> class DefaultMavenPublication implements MavenPublication {
> DefaultMavenPublication(String name, Object arg1, Object arg2) { … } //
> arg1 & arg2 could be typed of course
> }
>
> 3. Add via contract types, using rigid factories…
>
> publications.registerTypeFactory(new MavenPublicationFactory())
> publications {
> custom(MavenPublication) {}
> }
>
> class MavenPublicationFactory implements
> DomainObjectFactory<MavenPublication> {
> MavenPublication createDomainObject(String name) {
> instantiator.newInstance(DefaultMavenPublication, name)
> }
> }
>
> (not suggesting that as the DomainObjectFactory API, just illustrative)
>
> 4. Add via contract types, allowing construction params…
>
> publications.registerTypeFactory(new MavenPublicationFactory())
> publications {
> custom(MavenPublication) {}
> }
>
> class MavenPublicationFactory implements
> DomainObjectFactory<MavenPublication> {
> MavenPublication createDomainObject(String name, Object...
> constructionArgs) {
> // constructionArgs doesn't necessarily need to be passed to the
> constructor
> instantiator.newInstance(DefaultMavenPublication, name,
> *constructionArgs)
> }
> }
>
> 5. Add via contract types, using typed builders to parameterise
>
> publications.registerTypeBuilder(MavenPublicationBuilder)
> publications {
> custom(MavenPublication, «configure the builder by map») {
> someMethodOnMavenPublication()
> }
> }
>
> publications.registerTypeBuilder(MavenPublicationBuilder)
> publications {
> custom MavenPublication), {
> « configure builder by closure »
> }, {
> someMethodOnMavenPublication()
> }
> }
> class MavenPublicationBuilder implements
> DomainObjectBuilder<MavenPublication> {
>
> MavenPublicationBuilder(String domainObjectName, Instantiator
> instantiator) { … }
>
> MavenPublication build() {
> instantiator.newInstance(DefaultMavenPublication, name)
> }
> }
>
>
> I think option 3 is the most desirable, if we can avoid the need to
> parameterise what gets created at construction time. 5 offers the most
> flexibility, but I can't think of a way to avoid making the DSL so
> cumbersome and to avoid the complexity.
>
> Perhaps the two aren't mutually exclusive. We could start with 3, and then
> add something like 5 later if it turns out to be necessary.
>
> 1 and 2 are non starters for me. There needs to be indirection between the
> requested and concrete type.
>
> I don't like 4 because it's so loose. There's no compile time typing and
> no good way to specify the contract.
>
>
> Given those options (feel free to propose others), I'd vote for starting
> with 3 I think.
>
>
> I agree. It's actually what Daz ended up implementing, too, so that works
> out well.
>
One nice thing about option 3. is that it completely separates the plugin
author's (producer) side of the DSL from the build author's view
(consumer). I put some work into making the DSL nice for build authors, but
very work little into the plugin (factory registration) side. This is still
an internal API, while the consumer API is public (incubating).
Daz
>
> But I don't think it's quite right yet, from a plugin author's point of
> view. There are a few things we still need to sort out, which influence how
> well these options work:
>
> 1. A public way to do dependency injection into these domain objects. This
> to me is a good option for avoiding construction parameters.
>
> 2. A public way to do DSL injection. Every object that ends up in these
> containers needs to be DSL-aware.
>
> 3. We want the code to be as declarative as possible. When things are
> statically declared we can infer things like the dependencies between
> things, we can include interesting information in the DSL reference, and we
> can infer which plugins provide which types of things for implicit plugin
> application, and so on. When opaque factories are used, we cannot do this
> useful stuff.
>
> 4. We need to open up some of the domain object types for extension. For
> example, we want people to be able to provide their own component
> implementations, report implementations, and so on. I think the approach
> we've used for tasks actually works pretty well in practise, as far as
> extensibility goes (assuming we've moved the TaskInternal stuff off
> DefaultTask), so I would suggest generalising this approach somewhat:
>
> - An abstract contract type (eg Task)
> - One or more public implementations (eg DefaultTask) open for extension.
> - The public implementations have a zero-args constructor and rely on
> (field) dependency injection to receive the internal services they require.
> - Instances must be managed by Gradle - you can't use new Thing().
> - Class decoration injects whatever internal stuff is needed (eg
> TaskInternal)
> - When a type is not specified at construction time, or when a contract
> type is specified, then an appropriate implementation type is selected.
>
> I think generally we want using a factory to be a last resort for the
> plugin author. Instead we want some way for a plugin author to say 'I
> provide instances of this implementation type' and separately for a
> consumer (possibly the plugin author) to say 'please give me an instance of
> this type' and for Gradle to take care of the rest.
>
>
> --
> Adam Murdoch
> Gradle Co-founder
> http://www.gradle.org
> VP of Engineering, Gradleware Inc. - Gradle Training, Support, Consulting
> http://www.gradleware.com
>
>
--
Darrell (Daz) DeBoer
Principal Engineer, Gradleware
http://www.gradleware.com