-
Notifications
You must be signed in to change notification settings - Fork 37
Conversation
Have an named extension in the command extension to support DSL like scripting for inputs. Put them into a group named `add script`
Also move a method in the spec to make more sense.
private configureInputScripts(AndroidCommandPluginExtension extension, Project project) { | ||
extension.scripts.all { input -> | ||
project.tasks.create(input.name, Input) { | ||
group = 'adb script' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I decided to have a brand new group
called adb script
. Suggestions are welcome.
@@ -83,6 +86,14 @@ public class AndroidCommandPluginExtension { | |||
monkey | |||
} | |||
|
|||
void script(Action<NamedDomainObjectContainer<InputSpec>> action) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused by this method: why do you need it? I'd expect you would expose the container itself (look at BPP for instance). This way the user will be able to create all the scripts he needs and then you can (lazily) parse all the InputSpec
in there to create the Input
tasks you need.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That line in BPP adds an extension to the project, right? I want to add the same to the command
extension we already have.
Should I do something like project.android.command.extensions.add()
?
And I didn't understand how it will help being lazy. All the code in the extension closure runs immediately with any gradle task in the configuration phase, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We might want to take a look at this tomorrow together, but beware the code executed in each entry of the container will be affecting only the InputSpec
that is perfectly fine, because it's just about collecting the commands to be used later in the Input
task
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep thats perfectly fine. I think it is 2 different ways of achieving the same thing. Let's discuss tomorrow and decide on a solution.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, and about adding the container you don't need to do anything special, just make the scripts
field public in the extension (in BPP we want to add it to the project
we have no extension there)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not really happy with evaluating the scripts early. That limits their use quite a bit since we lose all kinds of runtime evaluations. Or am I missing something?
def command = ["shell", "input"] | ||
command.addAll(values) | ||
runCommand(command) | ||
@Deprecated |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method has been just added and already deprecated: what's the point of it? 😕
If you want to introduce this change without breaking any public API you should keep all the public methods deleted, mark them as @Deprecated
and delegate to the InputSpec
instead. Other choice will be to annotate the script
field as @Delegate
and let Groovy do its magic, but then the users won't have any notion of deprecation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't want to deprecate the field script
because now it is a proper input that I use. It is now used when the task is created in the plugin.
Before we had Closure script
. With gradle magic, when the user creates a new task with type Input, they could use script
like below (which looks like a method call)
This method is now introduced to support that the old behavior.
task autoLogin(type: com.novoda.gradle.command.Input) {
script {
}
}
I didn't want to keep all the methods because that would cause a lot of duplication. I know that it is not ideal but with this we are 100% backward compatible.
I will look into @Delegate
. I guess with that I can just remove this method. I would have to remove deprecation but it is not really super visible anyways.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're talking from the perspective of the sample we provide, not the real public API: if you remove public methods you're introducing breaking changes, right? Duplication in this case is not correct, because you will not duplicate the implementation, will just be tedious to make those method to use delegation instead. I feel we can bare with that as long as we have a proper strategy to move forward :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's very true :)
Then Delegate seems to be a logical way to do it.
@@ -83,6 +86,14 @@ public class AndroidCommandPluginExtension { | |||
monkey | |||
} | |||
|
|||
void script(Action<NamedDomainObjectContainer<InputSpec>> action) { | |||
action.execute(scripts) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't because of this that you can't have lazy initialisation of commands?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess so. But I kinda need to execute the action to retrieve the name to create the task.
In the following example, I need to get the name autoLogin
in advance to create the task with that name but still need to run the rest lazily. How would I do that?
By doing action.execute
, the script container gets an InputSpec
object with a name.
autoLogin {
2.times {
text 'bob'
enter()
}
enter()
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can postpone the creation of the task waiting for the end of the configuration phase, after which the scripts
container will be filled with all the entries. we do something similar in the other plugin :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense. 👍 Should totally do that.
Keep the input scripting evaluation lazy by introducing another closure in the middle.
5a06e97
to
6ee04a7
Compare
* https://github.com/novoda/gradle-android-command-plugin#input-scripting | ||
*/ | ||
@Deprecated | ||
void setScript(Closure script) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
README.md
Outdated
```groovy | ||
inputScripts { | ||
autoLogin { | ||
inputScript { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is how the new NamedDomainObjectContainer
is used. Unfortunately I had to create another inputScript
property in the middle to make the execution of the Closure lazy.
When I remove it and have the closure in one level up (like below), configure
method of NamedDomainObjectContainer
just executes the whole closure.
inputScripts {
autoLogin {
2.times {
text 'bob'
enter()
}
enter()
}
}
Since there are no behavior changes with this change, I will go ahead and merge this. |
Problem
Currently, user has to create a new task extending from Input to have input scripting. It doesn't really look good and we don't have much control over that task since we don't create it ourselves.
Solution
This is a proposal of a possible solution. I would like to get opinions and structure possible follow-ups for other tasks.
A
NamedDomainObjectContainer
is introduced in the extension. This is whatproductFlavors
use in Android. This will allow users to have scripts like belowThis will then create an
InputSpec
object which will holds aClosure
to be lazily executed in runtime of the task.For backward compatibility,
Closure script
inInput
task is not removed. But instead it now usesInputSpec
as an inner field.script
is converted towrite-only
property to keep backward compatibility. When called, prints a warning with a link to the appropriate section in README.Deprecation is even visible to users in their
build.gradle
files