Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Task assembly #284

Merged
merged 34 commits into from Mar 19, 2016
Merged

Task assembly #284

merged 34 commits into from Mar 19, 2016

Conversation

greg-1-anderson
Copy link
Member

This PR introduces the concept of task assembly, which is done by way of the TaskAssembler, a helper object that helps do dependency injection on tasks that are created at runtime (as opposed to during container initialization, where the DI container can be used for this purpose).

The old way to build tasks looks like this:

    protected function taskExtract($filename)
    {
        return new Extract($filename);
    }

With task assembly, it now looks like this:

    protected function taskExtract($filename)
    {
        return $this->taskAssembler()->assemble(
            '\Robo\Task\Archive\Extract',
            [$filename]
        );
    }

The benefit of using the task assembler is threefold:

  1. It will call setLogger & c. as necessary on the task, after it is constructed, reducing coupling between the different areas of the code.
  2. It recognizes tasks that implement CompletionInterface, and ensures their complete() method will be called at shutdown time, if the task is run().
  3. It also supports 'simulated' tasks, which print out what they would do when run() without actually making any persistent changes.

This PR does not break backwards compatibility with existing external Robo tasks. Existing tasks that continue to use the old builder pattern will still work; however, they will silently ignore the --simulate flag, and execute instead. To break backwards compatibility and force old tasks to update, all that needs to be done is remove one of the last references to the Config class, and then all task printing operations will fail for tasks that have not updated to the new mechanism.

… us to reduce internal coupling, and also support temporary and simulated tasks in a uniform way.
@@ -36,7 +45,10 @@ protected function taskSemVer($pathToSemVer = '.semver')
*/
protected function taskServer($port = 8000)
{
return new PhpServer($port);
return $this->taskAssembler()->assemble(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line indented incorrectly; expected at least 12 spaces, found 8

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see what's wrong about the formatting of this line. Looks the same as all of the others like it. No tabs that I can see. Odd.

@greg-1-anderson
Copy link
Member Author

The accessor methods added here were showing up as commands, so I added a filter to automatically remove accessors from command lists. It would also be possible to add an annotation to opt-out of command selection.

@greg-1-anderson
Copy link
Member Author

It also seems that we could replace task assemblers with direct use of a DI container. This would be particularly convenient if we used a container that supported inflectors, such as League\Container.

Doing this would substantially change the syntax of Robo, though; $this->taskExec() would become $container->get('taskExec'). I don't know if that would be a definite improvement, but I thought I'd mention it as a possibility. Maybe defer until later, and keep the proposed implementation in this PR to maintain compatibility? I could go either way.

UPDATE: I think I'd prefer to avoid tying this part of the code to the specific container implementation. It will reduce coupling between the code to rely only on constructor / setter injection.

@greg-1-anderson
Copy link
Member Author

See the discussion in the Drush issue queue on the thought process I went through to select a proposed DI container for Drush. Aurora DI could work; it's syntax is fine, but I didn't really like the names of its methods. I like league/container quite a bit more than Symfony DI.

{
$this->logger = $logger;
}
use LoggerAwareTrait;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line indented incorrectly; expected at least 4 spaces, found 3

@greg-1-anderson
Copy link
Member Author

Switched to league/container for DI. The inflector feature allows us to automatically inject a logger into any object (managed by the DI container) simply by having it implement LoggerAwareInterface and use LoggerAwareTrait. This gives us the convenience of auto-wiring, but in a more controlled, declarative way.

@@ -21,7 +21,7 @@
"symfony/console": "~2.5|~3.0",
"symfony/process": "~2.5|~3.0",
"symfony/filesystem": "~2.5|~3.0",
"symfony/dependency-injection": "~2.5|~3.0"
"league/container": "~2"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good choice )

@DavertMik
Copy link
Member

I really like your choice of container

But I think If we choose it, maybe we can use it in full and not include task assembler class. The overall concept seems like factory method, so it can be moved to the container.

Doing this would substantially change the syntax of Robo, though; $this->taskExec() would become $container->get('taskExec'). I don't know if that would be a definite improvement, but I thought I'd mention it as a possibility.

Yes, this will be much cleaner if container would manage creation of tasks, and inject everything. Probably you can use http://container.thephpleague.com/factory-closures/ to implement simulated behaviors.

Also I'd ask you to use ::class constant instead of strings in class names. They really save ass, in refactorings, and they really look nicer.

And thanks for your work. I really like the way it is moving, but I'm still desore to find a way to keep things simpler )

@@ -14,6 +16,25 @@
*/
trait TaskIO
{
use LoggerAwareTrait;

static $gaveDeprecationWarning = false;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Visibility must be declared on property $gaveDeprecationWarning


if (is_dir($tmpPath)) {
$this->say("Created a temporary directory at $tmpPath");
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expected 1 space after closing brace; newline found

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expected 1 space after closing brace; newline found

@greg-1-anderson
Copy link
Member Author

The latest technique does not quite work due to a limitation in League/Container. I have submitted a PR to rectify the situation. I suspect the maintainer might want to see the strictly backwards-compatible implementation of this.

@greg-1-anderson
Copy link
Member Author

Submitted a backwards compatible version: thephpleague/container#94

@philipobenito
Copy link

Hope this is agreeable thephpleague/container#94 (comment)

@greg-1-anderson
Copy link
Member Author

Thanks, @philipobenito! Works great & is totally awesome. This will be great for both Robo and Drush.

@philipobenito
Copy link

Brilliant stuff, I'll get you a tag sorted in the next few mins.

@greg-1-anderson
Copy link
Member Author

@DavertMik this is now using the tagged release of league/container. I think this should now meet all of your expectations per our last chat -- and, against all odds, even maintains bc (although --simulate won't work for folks who keep using the deprecated bc loadTasks conventions). Take a look and let me know. Excited to see this merged soon!

@greg-1-anderson
Copy link
Member Author

Not captured in the discussion above (from chat): we want to preserve loadTasks for parameter type hinting, and autocomplete in IDEs. However, now that we have combined the service provider into the loadTasks file, it isn't so bad to have both. The pattern for making new tasks is pretty apparent from the existing examples; I'll also update the documentation on this.

@DavertMik
Copy link
Member

Awesome ❤️ ❤️ ❤️

DavertMik added a commit that referenced this pull request Mar 19, 2016
@DavertMik DavertMik merged commit ccda129 into tasklib-2 Mar 19, 2016
@DavertMik DavertMik deleted the task-assembly branch March 19, 2016 21:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants