Percursor to #2625696: Make migrations themselves plugins instead of config entities

Problem/Motivation

#2625696: Make migrations themselves plugins instead of config entities wants to move migrations to plugins. In order to do this it wants to have a YAML discovery where a single plugin resides in a file. Other projects with complex plugins have asked for something similar - specifically https://www.drupal.org/project/skinr

Proposed resolution

Add a new YamlDirectoryDiscovery that can scan directories for YAML files and key them by an arbitrary value from the data.

Remaining tasks

User interface changes

None

API changes

New discovery class to support this.

Data model changes

None

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

alexpott created an issue. See original summary.

alexpott’s picture

Issue summary: View changes
Status: Active » Needs review
FileSize
10.67 KB

The patch attached adds the discovery part of this. Now just need to do the plugin discovery part of this.

dawehner’s picture

  1. +++ b/core/lib/Drupal/Component/Discovery/YamlDirectoryDiscovery.php
    @@ -0,0 +1,138 @@
    +   *   Defaults to 'id'.
    

    Let's skip that, its pointless IMHO

  2. +++ b/core/lib/Drupal/Component/Discovery/YamlDirectoryDiscovery.php
    @@ -0,0 +1,138 @@
    +        catch (InvalidDataTypeException $e) {
    +          throw new DiscoveryException("The $file contains invalid YAML");
    +        }
    

    Can we add the exception onto the other exception as previous exception?

  3. +++ b/core/lib/Drupal/Component/Discovery/YamlDirectoryDiscovery.php
    @@ -0,0 +1,138 @@
    +    foreach ($this->directories as $provider => $directories) {
    +      $directories = (array) $directories;
    +      foreach ($directories as $directory) {
    +        if (is_dir($directory)) {
    +          foreach (scandir($directory) as $file) {
    

    What about using a iterator?

  4. +++ b/core/tests/Drupal/Tests/Component/Discovery/YamlDirectoryDiscoveryTest.php
    @@ -0,0 +1,149 @@
    +    $this->assertEquals(1, count($data['test_2']), 'test_4 provides 1 item');
    ...
    +    $this->assertEquals(1, count($data['test_1']), 'test_1 provides 1 item');
    

    Let's use $this->assertCount()

  5. +++ b/core/tests/Drupal/Tests/Component/Discovery/YamlDirectoryDiscoveryTest.php
    @@ -0,0 +1,149 @@
    +   *
    +   * @covers ::findAll
    +   */
    +  public function testDiscoveryNoIdException() {
    +    $this->setExpectedException(DiscoveryException::class, 'The vfs://modules/test_1/item_1.test.yml contains no data in the identifier key \'id\'');
    

    We could put @covers ::getIdentifier

alexpott’s picture

Thanks for the review @dawehner

  1. I'm not sure - I think it is useful because it documents that the 'id' key is used by default - and there is no other way of seeing this form documentation alone. And atm it is the standard and there are issues out there to add this everywhere.
  2. Fixed
  3. Fixed
  4. Fixed
  5. Fixed

Also added proper plugin discovery and tests for it.

dawehner’s picture

/me waits for damian to appear on this issue.

benjy’s picture

All looks pretty good to me, just wondering do we need this cast?

+++ b/core/lib/Drupal/Component/Discovery/YamlDirectoryDiscovery.php
@@ -0,0 +1,138 @@
+      $directories = (array) $directories;
+      foreach ($directories as $directory) {
damiankloip’s picture

Yes, I spoke to Daniel briefly about suggesting an iterator instead. Looks like you've already done that - nice!

If we're already using the FileSystem iterator, IMO it would be nice to have the logic encapsulated in an iterator of its own. This similar pattern must be used at least a couple of times (maybe more) in core alone? So having one goto way to get and iterate YAML files in a directly would be nice.

Something like this maybe.. Thoughts?

EDIT: Sorry, should have given this file a .txt extension!

damiankloip’s picture

+++ b/core/tests/Drupal/Tests/Component/Discovery/YamlDirectoryDiscoveryTest.php
@@ -0,0 +1,157 @@
+    $this->setExpectedException(DiscoveryException::class, 'The vfs://modules/test_1/item_1.test.yml contains invalid YAML');

Nit: Is this the preferred way to set expected exceptions now? I prefer using the annotation myself. Just seems easier to spot when scanning the test.

alexpott’s picture

@damiankloip I'm not sure that we need to generalise that here. Maybe we can open a separate issue to look at generalising file iteration. I'm not sure that Discovery is the right place for this.

damiankloip’s picture

Sure, follow up is OK. That was not a finished product, just a suggestion.

damiankloip’s picture

alexpott’s picture

@damiankloip so just to make you lol I realised that your iterator was also making sure we are dealing with a file - which seems eminently sensible - so perhaps it is right to add this here.

alexpott’s picture

So now this is blocked on #2671708: Add a RegexDirectoryIterator...

dawehner’s picture

Nit: Is this the preferred way to set expected exceptions now? I prefer using the annotation myself. Just seems easier to spot when scanning the test.

https://thephp.cc/news/2016/02/questioning-phpunit-best-practices

moonray’s picture

Skinr module would actually need to be able to find .yml files recursively. e.g. /modules/skinr/skins/plugin1/plugin1.yml and /modules/skinr/skins/plugin2/plugin2.yml
Layout module actually goes a step further and allows arbitrary recursion depth. So the following should also be a valid path: /modules/layout/layouts/static/one-col/one-col.yml

Talking with @dawehner it seems we should probably allow specifying the iterator so you could use a recursive iterator or a more basic one.

moonray’s picture

I'm not sure this is useful for core or other modules, but I need to be able to find out the .yml file's path. The YamlDirectoryDiscovery component doesn't include this with the data returned, only the provider (module or theme name) is returned.
The reason I would need this info is that the .yml plugin includes paths to css and js files, which are relative to the plugin's directory. Requiring paths in the .yml file to be relative to the module's or theme's root (instead of the plugin's root) would quickly become tedious and error prone.

alexpott’s picture

@moonray - I think you are right- we should add the plugin path to the information returned

alexpott’s picture

FileSize
5.54 KB

Patch uses #2671708: Add a RegexDirectoryIterator and adds a the discovery location to the plugin array of data. I think this makes sense since even without recursive plugin finding it is possible the ID and the filename don't match up so this will help to work out what is coming from where.

alexpott’s picture

Patch was missing for some reason...

alexpott’s picture

After discussing with @dawehner in IRC - trying to make @moonray's life a bit easier by making the iterator easily replaceable with one that can be recursive.

dawehner’s picture

+++ b/core/lib/Drupal/Component/Discovery/YamlDirectoryDiscovery.php
@@ -142,4 +139,22 @@ protected function findFiles() {
+   * @param $directory

Nitpick: should be typehinted with string

  1. +++ b/core/lib/Drupal/Component/Discovery/YamlDirectoryDiscovery.php
    @@ -0,0 +1,160 @@
    +        $data[static::DISCOVERY_PATH] = $file;
    

    This is a bit confusing. Don't we want to do something like $data['plugin_definition_file'] = $file or static::DISCOVERY_PATH?

  2. +++ b/core/lib/Drupal/Component/Discovery/YamlDirectoryDiscovery.php
    @@ -0,0 +1,160 @@
    +  protected function getIdentifier($file, array $data) {
    

    missing docs on parameter $file

alexpott’s picture

Thanks @dawehner I've fixed the missing typehints. The reason for static::DISCOVERY_PATH was to allow things that extend the discovery to use a different key if they choose and also to not have hardcoded strings everywhere. I've made some changes to make it more obvious. We can't use 'plugin_definition_file' because that is about plugins and discovery is supposed to be decoupled from that.

dawehner’s picture

Status: Needs review » Reviewed & tested by the community

Thank you alex, this clarifies it.

damiankloip’s picture

+1 Just looked through the most recent patch again - looks grrrrreat!

moonray’s picture

Would it make sense to use 'pathname' like the Extension class to keep things consistent, instead of '_discovered_file_path'? Of course, Extension is an object and only exposes the getPathname() function, not the actual pathname variable. And there is a chance to conflict with a 'pathname' key in the .yml file. But I thought I'd throw this out there.

dawehner’s picture

@moonray Well the point alex made earlier is: we want to not expose the information by default, at least not super visible. Most plugins should really not care about their location.

catch’s picture

Status: Reviewed & tested by the community » Needs work

Overall this looks good but a lot of inconsistencies/typos in the comments.

  1. +++ b/core/lib/Drupal/Component/Discovery/YamlDirectoryDiscovery.php
    @@ -0,0 +1,162 @@
    +   * The suffix the file cache key.
    

    The suffix for the file cache key.

  2. +++ b/core/lib/Drupal/Component/Discovery/YamlDirectoryDiscovery.php
    @@ -0,0 +1,162 @@
    +   * The key which contains the value to key the data by.
    

    This is very confusing - could we replace it with something closer to the @param docs?

  3. +++ b/core/lib/Drupal/Component/Discovery/YamlDirectoryDiscovery.php
    @@ -0,0 +1,162 @@
    +   *   An array of directories to scan, keyed by the provider.
    

    This doesn't match the property documentation.

  4. +++ b/core/lib/Drupal/Component/Discovery/YamlDirectoryDiscovery.php
    @@ -0,0 +1,162 @@
    +   * This method is exists so that it is easy to replace this functionality in
    

    This method is exists.

  5. +++ b/core/lib/Drupal/Core/Plugin/Discovery/YamlDirectoryDiscovery.php
    @@ -0,0 +1,35 @@
    + * Allows directories that contain YAML files to define plugin definitions.
    

    "Allows directories containing YAML files to define plugin definitions."

    The directories themselves don't define the plugin definitions.

alexpott’s picture

Status: Needs work » Needs review
FileSize
3.01 KB
17.01 KB

@catch thanks for the review.

Patch attached addresses everything - I went in a slightly different direction for point 5.

alexpott’s picture

Status: Needs review » Reviewed & tested by the community

Given the fixes were only docs fixes setting back to rtbc.

  • catch committed e559eaa on 8.1.x
    Issue #2671034 by alexpott, damiankloip: Create plugin per file YAML...
catch’s picture

Status: Reviewed & tested by the community » Fixed

Committed/pushed to 8.1.x, thanks!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.