Exposing methods from plugin

What is the correct/idiomatic way for a plugin to expose a method such that it can be called in the build script that applies the plugin. So far I could only think of either method on an extension object or a closure on extra properties (ext namespace) and both of which are apparently discouraged

here

Extensions are meant as a way of extending the build script DSL.

and here

The ‘ext’ space is for adhoc storage. Plugins should never use this

All I want to be able to do in build.gradle is to call my.namespace.method(with, some, params). And, if at all there is no other way, which one of the two above is lesser evil :smiling_imp:

I think the preference is:

No method > method on custom extension > method on custom type > method (extra property) on existing extension > method (extra property) on existing type > method (extra property) on Project.

I say “No method” is the most preferred to mean that it can be too magical if someone using your plugin does something like this:

apply plugin: 'my-plugin'

myPlugin.setup("foo", 1, [ "x", "y", "z" ])

to mean “set the domainName to foo, the threshold to 1 and the list of widgets to x, y, z”.

Instead, you’d prefer to give better names to things and make it a little more explicit, e.g.,

apply plugin: 'my-plugin'

myPlugin {
   domainName = "foo"
   threshold = 1
   listOfWidgets = [ 'x', 'y', 'z' ]
}

Then as part of configuring tasks/other things, you can validate that everything is configured correctly and complain if it isn’t.

I think the only exception to this might be where you need a utility or builder method that creates a complicated object (this would be something like project.fileTree or project.file). I’d put a method like that on the extension to give it a namespace.

HTH

2 Likes

Wow. Thanks for such an elaborate explanation.

I’d totally agree that setting up domain model using DSL block is way better than calling a method. However the methods I am looking to expose are more action oriented utility methods which apply only context of our build.

To quote some example from Gradle API, I think project.copy() or project.delete() would be closest.

No worries, glad to help.

Something like project.copy() or project.delete() I would expose either as custom task types (if that makes sense), as a utility class (for someone to build their own custom task types/plugins on top of) or a method on the extension. Wrapping actions in a task are usually the way to go so you can make them incremental, but it would really depend on exactly what the action-like methods are doing.

I’m facing exactly this code design problem. I would like my binary plugin to provide the helper util method for manipulating RelativePaths, one to be used in many places of the build scripts. I can put it into my extension class, but this looks a bit wrong for me, as extensions seems to be thought to provide DSL constructs for modeling and not the general helper methods. So, maybe I should provide the utility class as suggested above and put my method into it - but how to make this utility class available for use in the build scripts? Please help. Code examples are welcome.