This message was deleted.
# plugin-development
s
This message was deleted.
n
My use-case is as follows: I'm developing a plugin that will compare 2 resources, both of these resources can either be a local resource (aka RegularFileProperty), or a remote resource (a file to be downloaded, and optionally extracted from a zip/tar/war/...). The DSL currently looks like this:
Copy code
diffExtension {
    oldSchema {         // SchemaResource
        schemaUrl           // Property<String>
        username            // Property<String>
        password            // Property<String>
        requiresExtraction  // Property<Boolean>
        schemaFile          // Property<String>
        schema              // RegularFileProperty
    }
    newSchema {         // SchemaResource
        schemaUrl           // Property<String>
        username            // Property<String>
        password            // Property<String>
        requiresExtraction  // Property<Boolean>
        schemaFile          // Property<String>
        schema              // RegularFileProperty
    }
    outputDir               // DirectoryProperty
}
you either define the
schema
property, or all the others, depending on where the file should be coming from. This is rather confusing in my opinion, so I would like it to look something like this instead:
Copy code
diffExtension {
    oldSchema {         // RemoteSchemaResource --> somehow have a way to say to gradle that this is remote in this case
        schemaUrl           // Property<String>
        username            // Property<String>
        password            // Property<String>
        requiresExtraction  // Property<Boolean>
        schemaFile          // Property<String>
    }
    newSchema {         // LocalSchemaResource --> somehow have a way to say to gradle that this is local in this case
        schema              // RegularFileProperty
    }
    outputDir               // DirectoryProperty
}
Note that
oldSchema
and
newSchema
could take on the
RemoteSchemaResource
or
LocalSchemaResource
interchangeably.
v
Does it have to be that DSL-like? Otherwise an option could for example be
Copy code
diffExtension {
    oldSchema.set(RemoteSchemaResource {
        ...
    }
    newSchema.set(LocalSchemaResource {
        ...
    }
    outputDir               // DirectoryProperty
}
Or in the future when the compiler plugin is done:
Copy code
diffExtension {
    oldSchema = RemoteSchemaResource {
        ...
    }
    newSchema = LocalSchemaResource {
        ...
    }
    outputDir               // DirectoryProperty
}
Or maybe with convenience functions instead of constructors
Copy code
diffExtension {
    oldSchema = remote {
        ...
    }
    newSchema = local {
        ...
    }
    outputDir               // DirectoryProperty
}
remote
and
local
have a parameter
Action<RemoteSchemaResource>
and
Action<LocalSchemaResource>
respectively and their body will create an instance, run the action against it and then return it.
For Kotlin DSL consumers you could maybe even do - if you really want to - define infix functions, so that it is like
Copy code
diffExtension {
    oldSchema remote {
        ...
    }
    newSchema local {
        ...
    }
    outputDir               // DirectoryProperty
}
n
I was thinking about that
Action<RemoteSchemaResource>
way that you proposed. I'm just struggling to translate that to my extension object, which currently looks like this:
Copy code
public interface DiffExtension {

	@Nested
	SchemaResource getOldSchema();

	default void oldSchema(Action<? super SchemaResource> action) {
		action.execute(getOldSchema());
	}

	@Nested
	SchemaResource getNewSchema();

	default void newSchema(Action<? super SchemaResource> action) {
		action.execute(getNewSchema());
	}

	DirectoryProperty getOutputDir();

}
what would the implementation of those configure Action methods be? Or do I define those on the
SchemaResource
Object itself instead of on the Extension?
v
Would have to think about it, but probably something like
Copy code
public interface DiffExtension {

	@Nested
	Property<SchemaResource> getOldSchema();

	@Nested
	Property<SchemaResource> getNewSchema();

    default SchemaResource remote(Action<? super RemoteSchemaResource> action) {
        RemoteSchemaResource result = new RemoteSchemaResource();
        action.execute(result);
        return result;
    }

    default SchemaResource local(Action<? super LocalSchemaResource> action) {
        LocalSchemaResource result = new LocalSchemaResource();
        action.execute(result);
        return result;
    }

	DirectoryProperty getOutputDir();

}
n
alright, that looks like something I could try out. Thanks for the pointers 👍
👌 1
e
might not be what you want, but also possible:
Copy code
// buildSrc/src/main/java/Schema.java
public interface Schema extends Named {
}

// buildSrc/src/main/java/LocalSchema.java
public interface LocalSchema extends Schema {
  RegularFileProperty getSchema();
}

// buildSrc/src/main/java/RemoteSchema.java
public interface RemoteSchema extends Schema {
  Property<URI> getSchemaUri();
}

// buildSrc/src/main/java/DiffPlugin.java
public class DiffPlugin implements Plugin<Project> {
  @Override
  public void apply(Project project) {
    ExtensiblePolymorphicDomainObjectContainer<Schema> diffExtension = project.getObjects().polymorphicDomainObjectContainer(Schema.class);
    diffExtension.registerBinding(LocalSchema.class, LocalSchema.class);
    diffExtension.registerBinding(RemoteSchema.class, RemoteSchema.class);
    project.getExtensions().add(PolymorphicDomainObjectContainer.class, "diffExtension", diffExtension);
  }
}

// build.gradle
diffExtension {
  oldSchema(RemoteSchema) {
    schemaUrl = uri('<https://example.com/schema>')
  }

  newSchema(LocalSchema) {
    schema = file('example.schema')
  }
}
if you don't want a
DomainObjectContainer
but only
oldSchema
and
newSchema
then you could still create methods like
Copy code
// buildSrc/src/main/java/DiffExtension.java
public interface DiffExtension {
  Property<Schema> getOldSchema();
  Property<Schema> getNewSchema();

  <T extends Schema> T oldSchema(Class<T> type, Action<? super T> configuration);
  <T extends Schema> T newSchema(Class<T> type, Action<? super T> configuration);
}

// buildSrc/src/main/java/DefaultDiffExtension.java
public abstract class DefaultDiffExtension implements DiffExtension {
  private final ObjectFactory objects;

  public DefaultDiffExtension(ObjectFactory objects) {
    this.objects = objects;
  }

  @Override
  public <T extends Schema> T oldSchema(Class<T> type, Action<? super T> configuration) {
    T schema = objects.newInstance(type);
    getOldSchema().set(schema);
    configuration.execute(schema);
    return schema;
  }

  @Override
  public <T extends Schema> T newSchema(Class<T> type, Action<? super T> configuration) {
    T schema = objects.newInstance(type);
    getNewSchema().set(schema);
    configuration.execute(schema);
    return schema;
  }
}

// buildSrc/src/main/java/DiffPlugin.java
public class DiffPlugin implements Plugin<Project> {
  @Override
  public void apply(Project project) {
    project.getExtensions().create(DiffExtension.class, "diffExtension", DefaultDiffExtension.class);
  }
}
to make it work in a similar way
👀 1
v
I thought about recommending a container, but thought it is not appropriate here, as he needs exactly those two instances, not more, not less, not differently named. But the latter is also a good idea, giving the type like that, so it would be
Copy code
diffExtension {
    oldSchema(RemoteSchemaSource::class) {
        ...
    }
    newSchema(LocalSchemaSource::class) {
        ...
    }
    outputDir               // DirectoryProperty
}
Or maybe it could also be made
Copy code
diffExtension {
    oldSchema<RemoteSchemaSource> {
        ...
    }
    newSchema<LocalSchemaSource> {
        ...
    }
    outputDir               // DirectoryProperty
}
Or another option would be 4 methods
Copy code
diffExtension {
    remoteOldSchema {
        ...
    }
    localNewSchema {
        ...
    }
    outputDir               // DirectoryProperty
}
and the remote and local of the same target exclude each other.
e
I gave it a shot and Gradle didn't auto-generate the
s/Class<T>/KClass<T>/
accessors for the extension like it does for other methods in the Gradle API 😞 so you'd have to write it in Kotlin to get 1 or 2 there
that's one thing that would work out of the box with the container, but yeah it does not do exactly what was asked for
writing your own functions for the
{remote,local}{Old,New}Schema
permutations is doable with this small number I guess
n
I was hoping it would do the
oldSchema(RemoteSchemaResource::class) { }
. I'm in the process or trying out this approach as we speak. It seems like the dsl now looks like the following:
Copy code
schemaDiff {
    oldSchema(RemoteSchemaResource::class.java, {
        schemaUrl.set("<https://some.uri>")
        username.set("myUsername")
        password.set("myPassword")
        requiresExtraction.set(true)
        schemaFile.set("filename.to.extract")
    })
    newSchema(LocalSchemaResource::class.java, {
        schema.set(schemaPath)
    })
}
it's not horrible, but I can't say it looks amazing either 🤔
I'll try with this suggestion as well, because I like that form a bit better: https://gradle-community.slack.com/archives/CA745PZHN/p1666625683465489?thread_ts=1666625132.925799&amp;cid=CA745PZHN
e
as always, you can move the lambda out of the parentheses
Copy code
schemaDiff {
    oldSchema(RemoteSchemaResource::class.java) {
        schemaUrl.set("<https://some.uri>")
you can also write write the extension in Kotlin to add
KClass
overloads if you want (gradle-kotlin-dsl has a bunch generated for the Gradle API but I haven't figured out how to get it to do that for custom types)
with the other form, you'll end up with
Copy code
diffExtension {
    oldSchema.set(remote {
        ...
    })
in Kotlin which I find more annoying, but YMMV
v
Currently, yes. I hope they get it managed to write that Kotlin compiler plugin they are working on, so that the assignment form is possible. 🙂
n
having tried both, I'll stick with the typed action with the lambda outside of the parentheses. Looks nicest indeed. Looking forward to that kotlin compiler plugin 🤞
In any case, thanks for all the help and insights, much appreciated 🙂
👍 1