Over in #1986988: Add a dedicated @CKEditorPlugin annotation, @tim.plunkett demonstrated an ability to shorten both the namespaces and directory structure of plugins discovered using AnnotatedClassDiscovery by extending the class and providing a custom namespace. In that particular example, he shortened the namespace and directory structure like this:
Before:
core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/ckeditor/plugin/Internal.php
After:
core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/CKEditorPlugin/Internal.php
Likewise, the namespace was also shortened:
-namespace Drupal\ckeditor\Plugin\ckeditor\plugin;
+namespace Drupal\ckeditor\Plugin\CKEditorPlugin;
Upsides
- Shorter directory structure and namespacing.
- Developers don't need to know which module (or Core) provides a particular plugin.
Downsides
- Requires each module providing a plugin create a custom discovery class extending AnnotatedClassDiscovery (see #6)
- Could result in namespaces conflicts. (see #16)
- Ambiguity around which module provides which plugin. (see #16)
Follow ups
#2055871: Introduce specific annotations for entity types
| Comment | File | Size | Author |
|---|---|---|---|
| #23 | discovery-structure-1987298-23.patch | 62.37 KB | tim.plunkett |
| #20 | discovery-structure-1987298-20.patch | 62.19 KB | tim.plunkett |
| #18 | discovery-structure-1987298-18.patch | 62.19 KB | tim.plunkett |
| #9 | shorten-discovery-structure-1987298-9.patch | 21.69 KB | tim.plunkett |
| #6 | plugin-annotation-1987298-6.patch | 21.71 KB | tim.plunkett |
Comments
Comment #1
quicksketchJust to attempt stretching this pattern across all core, here's what system/lib/Drupal would look like if this were extended to all plugins (left is before, right is after):
In this example, I *kept* the "Plugin" directory.
It results in some very useful redundancy removal, such as
block/blocksimply becomingBlock.Additionally it combines together locations that are separated for architectural reasons, but in practice are pretty much the same thing. Previously developers would need to distinguish between plugins provided by system module vs. Core, such as
system/imagetookitorCore/Archiver. Afterwards, both are simply moved to the root of /Plugin, such asImageToolkitandArchiver. Who provided the plugin is no longer required knowledge.On the flip side, these individually-created plugin namespaces introduce ambiguity. Does "ImageToolkit" come from image.module? It's no longer clear. For developers that aren't using IDEs, this could result in more directory structure hunting, but at the same time it makes scanning all plugins provided by a single module easier because there is less nesting.
Comment #2
webchickOh god yes, please. That would be amazing. "block/block" consistently causes glassy eyes in front of audiences during the module porting presentation.
Comment #3
webchickComment #4
chx commented> - Requires each module providing a plugin create a custom discovery class extending AnnotatedClassDiscovery
I am so much in support perhaps that could be used to fold derivatives into it.
Comment #5
chx commentedComment #6
tim.plunkettThis to me would be the first easy step, which would mean that if we don't care about nixing the /Plugin/ directory, you wouldn't need a custom discovery class.
Instead of specifying $owner and $type, which the Component AnnotatedClassDiscovery does not care about, we just pass the subdirectory "$owner/$type" as one variable. Then you can freely only pass "$owner$type" if you see fit.
Regardless of what we decide to do with core plugins, this makes contrib more flexible.
Comment #9
tim.plunkettNo need for DIRECTORY_SEPARATOR here.
Comment #10
quicksketchThat was my thought too. The new patch looks great IMO. We'd need followups to actually move directories around for each module of course, but the basic concept looks like a huge win. Plugins that require complex nested layouts can have nesting, and those that are straight-forward (like Block) can just use a single directory.
As much as I would like to eliminate the Plugin directory, as long as we have other non-plugin classes inside of /lib/Drupal/[module_name], I think mixing Plugin would cause confusion between what's a plugin and what's not. Keeping Plugin also reduces the number of places where we would have namespace conflicts.
Comment #11
quicksketchAlthough this isn't something we need to immediately worry about if we take the approach in #6, this is an opportunity to adjust our casing on our plugins. In my examples, I was changing "block/block" to "Block". Is that something we should pursue? It seems like long-term, everything is going to be CamelCase. This conversion could eliminate 2 lowercase-only locations if we chose to do that, leaving only the module name as lowercase.
Comment #12
tim.plunkettYes, I regret not making views plugins CamelCase when we first ported them.
IIRC, we only did it so that all of the views code that uses the plugin type names as string would match the directories (argument_validator, style, etc).
Comment #13
yched commentedI'm all for shortening what can be shortened, but I don't think we should remove any overall structure in the [module]/lib folder.
Modules will come with an awful lot of classes in D8: plugins for various subsystems, event listeners, page controllers, form controllers, entity types (almost plugins but not really) and associated entity controllers, native business or helper classes for the module, what else ?
Let's please not mix everything in the same.folder, or it's going to be impossible to answer those simple questions when e.g evaluating / reviewing a contrib module:
- what pages / forms does it provide ?
- what events does it react to ?
- what entity types does it provide ?
- what plugins, and for which system, does it provide ?
In other words, yay for simplifying what happens below the Plugins folder, but before we consider *removing* it, can we have a community discussion on how we're going to maitain human discoverability of what's in a module ?
Structureless bag of folders --
Comment #14
quicksketchThat's pretty much what this is right now. Tim's initial example removed the Plugins directory, but so far in this issue no one is advocating for removing the Plugin directory. Like you say, there are a lot of other "things" besides plugins, mixing plugins in with everything else would make things messy quickly. Are you concerned about the approach in #6, where the directory structure under "Plugin" gets to be defined by the module defining the plugin? And given this ability, do you think it makes sense to reduce things like "block/block" to just "Block"?
Comment #15
tim.plunkettHere are my proposals:
And leaving the rest alone for now.
Comment #16
eclipsegc commentedTo capture my own thoughts here.
Tim walked me through the highlights of this issue in IRC tonight, I expressed 2 basic principles I want to see maintained in the final outcome of this. Based upon Tim's responses, I think they have been maintained with his approach.
Principle 1.) We need at least 2 points of diversity when identifying plugins of a particular type. Previously that has been $owner/$type. The currently proposed resolution seems to be essentially $dir/$annotation_class. This continues to provide the two points of diversity amongst similarly named plugin types (or even identically named since we can use a different annotation class... though it'd be crazy to do that, it's technically possible).
Principle 2.) Drupal/my_module/Plugin/block/block at least attempts to communicate where I might find the managing class for plugins of this type. Switching away from that isn't problematic for a lot of plugin types that exist in modules, but it could be problematic for new users looking for anything that's defined in lib/Drupal/Core. msonnabaum and I have discussed this before, the annotation classes have been introduced since that time, and utilizing these should again lend a clue as to where the developer might find more information on plugins of this type. Both of these things are TOTALLY obtuse, but once a developer gets used to utilizing the plugin workflow it becomes a really important thing to know about, and as long as we maintain a way to do that, I am still quite happy. This ABSOLUTELY means every core plugin should ship with its own annotation class, but I think we intended on doing that anyway, so perhaps that should not give us pause.
Maintain these two principles and I'll be very happy.
Eclipse
Comment #17
quicksketchHey Eclipse, I'm not sure I'm following your first point. You're saying we need "two points of diversity". Previously that was $owner/$type. From the proposals above, we're essentially making things be $owner$type (represented as a single directory, no slash), or in the case the owner and type are the same thing, such as "block", then we don't do anything so dumb as BlockBlock. So we're *not* keeping two points of diversity in some situations, because the name of the thing is the same as the owner.
Additionally, in situations where Core or system.module is the owner, we also drop the owner, such as in Tim and my examples, we'd just have Entity and ImageToolkit instead of Core/Entity and system/ImageToolkit. It sounds like these suggestions are not in agreement with your first point. Is that the case?
Comment #18
tim.plunkett@quicksketch from my discussion with @EclipseGc, he means that we have both the $subdir ($owner$type) as well as the custom @SpecificPluginType annotation for identification.
So, this issue along with the continued efforts of #1966246: [meta] Introduce specific annotations for each plugin type should be fine.
Here is everything I outlined in #15, except Entity (since there are dozens of entity types and that's too much effort to make without 100% consensus)
Comment #20
tim.plunkettStupid macs, being case-insensitive. (At least, I hope that's the only thing wrong here)
This will need a core committer who doesn't use a Mac.
Comment #21
eclipsegc commented@quicksketch
Yeah, so using block as the example, we are currently $owner = block $type = block so can be found in dir Plugins/block/block. And this moves us to $dir = Block & $annotation class = @Block, so any @Block annotated class in Plugins/Block is a "block plugin".
To make another example of why I've been really hard-nosed on this issue:
Let's assume Drupal Core defines a "cache" plugin. Views also defines one, so does ctools, and probably a half dozen other modules. As things stand right now that will end in in Core/Cache, Views/Cache, CTools/Cache, etc, etc, but if we remove that second point of diversity, someone's going to want it to be just Cache and likely ViewsCache, and so on. That's fine, except core can't possibly squat all the relevant named things out there, so there will be collisions becomes someone somewhere is just going to name their plugin "cache", and want it in the cache dir. That's why the AnnotatedClassDiscovery forces $owner/$type on developers, but since we now have annotation class specification we can say "Drupal core's Cache plugins can be found in Plugins/Cache with an annotation class of @Cache." Then if there's every some conflict, the only thing that NEEDS to change is the annotation class on one of them, instead of having to refactor all the files around. This is much more likely in contrib where two modules might create identically named plugin types. If someone tries to make them work together on the same site, a.) hopefully their annotation classes were named differently (and we should define a spec for that) and b.) if they weren't named differently, the patch to fix one or both of them should be fairly minimal.
Hopefully that makes sense of what I'm trying to say.
Eclipse
Comment #22
quicksketchGotcha. So basically you just want some direct tie between a plugin and its parent module. Since the custom annotation type will be coming from a specific module, and that is easily traceable (since there is a "use" statement at the top of the file for that annotation type), this satisfies your requirement.
I'm fairly sure the plan is to convert everything to individual annotation types, so seems like we're all in agreement.
@tim.plunkett: your list of suggestions makes sense to me except for CKEditor. Although I doubt it will be common, you could have other things than just plugins for CKEditor, e.g. CKEditorSkin. And since we're defining "a CKEditor plugin" and not CKEditor itself, keeping the word plugin in the name would be preferred for me.
i.e.
ckeditor/plugin => CKEditorPlugin
Which doesn't shorten the name up any but still eliminates the nested directory.
Comment #23
tim.plunkettOkay, that makes sense.
If no one minds, I'd rather just do this here. I don't want to touch Core\Entity until the last possible moment (since this is scriptable but also will break a lot of patches).
Comment #24
eclipsegc commented@quicksketch,
yeah, I think I am on board with the proposed changes here. I'm just explaining why I'm on board since before the annotation class came into the picture I'd have fought this pretty strongly.
Eclipse
Comment #25
neclimdulI think types will still be useful but they are not broken with this and it looks nice and does simplify things for a group of plugins. Probably makes use outside of Drupal more sane in most cases too.
Comment #26
quicksketchThis looks great to me too. I applied it locally and everything still functions. Seems there are no special requirements for applying the patch despite what should be case-sensitivity conflicts (i.e. block/block being replaced by Block, git apply works perfectly to handle the renaming of the directory).
This does result in some interim jankiness while some plugins are being converted to custom annotations. For example CKEditor module now has Plugin/CKEditorPlugin/Internal.php (the new style), while at the same time still has Plugin/editor/editor/CKEditor.php (old-style). Of course after #1966246: [meta] Introduce specific annotations for each plugin type's child issues are all completed, this will no longer be an issue.
+1 RTBC. The flexibility and the decrease in wtf-'d-ness is a huge win.
Comment #26.0
quicksketchRemoving requirement of separate AnnotatedClassDiscovery implementation.
Comment #27
fubhy commentedI love this patch!
Will there be a follow-up for converting the rest? I would really like to see the CamelCase namespaces everywhere tbh. while we are at it!
Comment #28
quicksketch@fubhy: They'll likely all be done as they're converted to custom annotations, so the list at #1966246: [meta] Introduce specific annotations for each plugin type applies to everywhere else, except for Core/Entity, which seems like it will be its own followup.
Comment #29
xjmYes please. Thank you!
Let's file a postponed followup for the entity namespaces, tagged "Revisit before release." We'll want to go over all of those the week before code freeze.
Comment #30
alexpottCommitted 19e6c2c and pushed to 8.x. Thanks!
I'm sure that some change notice need updating...
Comment #31
quicksketchOddly I couldn't find change notices that mentioned the namespaces or directories of any of the changed plugins except for block, which I updated at Blocks are now Plugins.
Archiver, CKEditor, Condition, Layout, ImageToolkit, and PluginUI all don't seem to have any mentions by their previous plugin names.
Comment #32
tim.plunkettReopened for change notices:
#1950726: Convert hook_archiver_info into the New Shiny(TM)
#1664844: Convert image toolkits into plugins
Comment #33
tim.plunketthttps://drupal.org/node/1911646 is CKEditor's change notice
https://drupal.org/node/1813372 is for Layout
https://drupal.org/node/1961370 is for Condition
PluginUI never have one, would have been #1535868: Convert all blocks into plugins
Comment #34
quicksketchYes, sorry I should have been more clear. I found the original change notices for all of these things, but none of them have mentions of the namespaces or directory structures, so they didn't need updating.
Comment #35
tim.plunkettYes, but they should mention it :)
So the originals are incomplete.
Comment #36
yesct commentedSo what do we need here?
A general change notice for directory structure and namespacing changes,
*and* mentions in each of those other change notices, with links to the main change notice for this topic?
https://drupal.org/contributor-tasks/draft-change-notification
is instructions for drafting a change notice.
Anyone can try the first draft. :)
Just make a comment saying you are going to try, and ask questions if you have any.
Comment #37
shyamala commentedI am going to try the general change notice.
Comment #38
shyamala commented- Shorter directory structure and namespacing.
- Developers don't need to know which module (or Core) provides a particular plugin.
Plugin directory structure
Before:
After:
Likewise, the namespace was also shortened:
Previously what was
$owner/$type, from the proposals above, will be$owner$type(represented as a single directory, no slash) examples,ckeditor/plugin => CKEditorPluginCore or system.module is the owner, we also drop the owner, examples, we'd just haveEntityandImageToolkitinstead ofCore/Entityandsystem/ImageToolkitExamples of changes to core namespaces:
Comment #39
aspilicious commentedThis also needs examples how to pass the namespaces in the plugin manager.
Comment #40
shyamala commentedAdded examples to the change notice.
Comment #41
aspilicious commentedI mean examples of an actual plugin manager *defining* the new name. The part were you choose your anotation name. When reading your notice it "seems" that it magicly works when creating the anotation class.
Thats not true, the shortened directory structure is forced in each plugin manager. Look at the ckeditor plugin manager for an example.
Comment #42
shyamala commentedBusy at the moment to complete this task, unassigning myself.
Comment #43
vijaycs85here is the follow up #2055871: Introduce specific annotations for entity types as mentioned in #29
Comment #43.0
vijaycs85Updated issue summary
Comment #43.1
vijaycs85Updated issue summary with follow up #2055871
Comment #44
catch#2083415: [META] Write up all outstanding change notices before release.
Comment #44.0
catchUpdated issue summary.
Comment #45
yanniboi commentedI was thinking of having a go at taking over from @Shyamala on this, but I don't think I quite understand what @aspilicious wants an example of...
I had a look at CKEditorPluginManager.php, but I couldn't find anything that is "The part were you choose your anotation name. "
(unless they are talking about the use statement?)
Comment #46
chx commentedCKEditorPlugin is a particularly confusing example because of the repeated and different meaning of the word plugin -- ckeditor itself has plugins. Here are a few examples of the relevant call, here's where you choose your annotation name (in reality, annotation class):
parent::__construct("Plugin/migrate/$type", $namespaces, 'Drupal\Component\Annotation\PluginID');parent::__construct('Plugin/CKEditorPlugin', $namespaces, 'Drupal\ckeditor\Annotation\CKEditorPlugin');new AnnotatedClassDiscovery('Entity', $namespaces, 'Drupal\Core\Entity\Annotation\EntityType');"Plugin/migrate/$type"is the plugin directory-namespace. Previously you would have passed the defining module (migrate) and the plugin type ($type) and the system had Plugin/$module/$type hardwired. This is the new thing: you are no longer bound to this structure -- if you want it, you can use it as migrate does but you need to pass in the whole thing because nothing is hardwired now. For example, CKEditorPluginManager is using 'Plugin/CKEditorPlugin' and EntityManager simply uses 'Entity'. Bliss!$namespacesis just the module namepaces.Drupal\Component\Annotation\PluginIDis the annotation class. Right now, Migrate plugins only have an identifier, that's the simplest case there is.Drupal\Core\Entity\Annotation\EntityTypeis another annotation but it's probably one of the most complicated ones we have.Comment #47
xjmTagging "Missing change record", as completing the change record updates here blocks a beta: #2083415: [META] Write up all outstanding change notices before release
The patch for this issue was committed on May 7, 2013. This means that the needed change record updates for this issue have been outstanding for more than eight months. See the comments above for change records that could use simple updates. It would also be very valuable if someone who has deep understanding of the Plugin API could review @Shyamala's proposed change record and see if it is necessary, or if it should simply be incorporated into the main Plugin API change record. At this point documenting the D8 to D8 change is pretty unhelpful, because eight months.
We should also check https://drupal.org/node/2087839.
Comment #48
dawehnerhttps://drupal.org/node/2186931
Comment #49
chx commentedThe BAP change notice https://drupal.org/comment/7616615 now contains the right Block namespace in several examples. I do not think this even needs that draft, really. We don't write d8-d8 notices.
Comment #50
dawehner@chx
This is no longer true, seriously we need to write change notices for all changes.
Comment #51
chx commentedAccording to #1541298-56: Remove Node module dependency from Testing profile : nope.
Comment #52
dawehnerThank you for being so nice.
Comment #53
jessebeach commentedCN: https://drupal.org/node/2186931
I edited the original CN started by Shyamala. It succinctly explained the change. I just formatted the text with a little more vertical spacing. It must also be noted that Views plugins are an exception to the new pattern.
I added a link to the Annotations-based plugins documentation to address aspilicious' comment in #39, https://drupal.org/node/1882526. This change notice concerns the PSR-0 namespace shortening, not how to define a new type of annotation plugin, so I'd rather just link to existing docs. aspilicious, did I understand your comments correctly?
I also went through each Plugin Change Notice and either updated or added PSR-0 namespace information.
Archiver: https://drupal.org/node/2003376
ImageToolkit: https://drupal.org/node/1993056
CKEditorPlugin: https://drupal.org/node/1911646
Layout [REVERTED]: https://drupal.org/node/1813372
Condition: https://drupal.org/node/1961370
Block: https://drupal.org/node/1880620
FieldFormatter: https://drupal.org/node/1805846
FieldType: https://drupal.org/node/2064123
FieldWidget: https://drupal.org/node/1796000
Mail: https://drupal.org/node/2191201
DataType: https://drupal.org/node/1806650
Action: https://drupal.org/node/2020549
Filter: https://drupal.org/node/2015901
ImageEffect: https://drupal.org/node/2072699
I'm pretty sure this satisfies all of the critiques with the change notice. I'm going to mark this closed. Feel free to re-open if you disagree. Or just edit the CN to correct.