If I read the current code correctly, the "plugins" tool will unconditionally load all plugins at least once (on initialization of the cache, or when listing the plugins without using the cache). In case my module sports a set of pluggable stuff that is implemented as classes, that would in some situations lead to PHP errors if ctools doesn't consider the correct include order that is required by the class hierarchy, like the thing that triggered the late Views 2 API change.
It would be neat if that use case was possible with ctools too, so that I can make use of that code instead of reimplementing it in my own module. (In case I missed something and it works already, please consider this a support request asking for a hint for implementing something like this with ctools.)
| Comment | File | Size | Author |
|---|---|---|---|
| #16 | 348323_ctools_class_plugins.patch | 3.06 KB | neclimdul |
| #15 | 348323_ctools_class_plugins.patch | 2.94 KB | neclimdul |
| #14 | 348323_ctools_class_plugins.patch | 2.89 KB | neclimdul |
| #3 | ctools-multiversion.diff | 2.88 KB | jpetso |
Comments
Comment #1
merlinofchaos commentedIn order for the plugins to work, the .inc file must be loaded to give information about the dependencies of the plugin. For a class dependency system to work, the classes should thus be kept in a file separate from the .inc -- this is how Views does its class dependencies (the classes are registered via the normal .views.inc)
Note that currently the .inc files are categories completely by directory name, but I have a TODO note in my own list to make it possible to categorize by filename, so that it could be possible to get files named modulename.views.inc or pluginname.views.inc and handle Views-like issues as well. The main .inc file would then need to include a class registry system.
It's entirely possible that this registry system could be a separate tool which can be easily invoked for plugins that want to use it. The Views code is currently a bit specialized toward Views but it might be possible to distill this down. That part makes sense to leave open as a feature request.
Comment #2
merlinofchaos commentedUpon reflection, I suppose that two separate plugin types could be combined to do this.
Plugin A is module specific (i.e, modulename.views.inc) and it registers the plugins, and then plugins have a separate .inc file. In that case, the plugin code would need some way to know that plugin .inc files must be registered before they are loaded, and this registration must contain an inheritance tree. This in itself is not too difficult, I think, since there is a hook to give the plugin system this information, but plugins would need to understand dependencies. Also, they wouldn't discover the files used in the normal manner, (i.e, looking for .inc files) but instead all files would be known by the registration system. That makes it a slightly different kind of plugin and much of plugin.inc isn't suitable for that since it is about 80% concerned with auto-discovering plugins.
Comment #3
jpetso commentedI notice that you solved part of this issue by adding a loader for $module.$api.inc files. The attached patch should make it possible to let modules support multiple API versions in different files - I'm not sure if you consider that a worthwhile idea, but it doesn't really come with additional costs so the only issue would probably be best practices in file naming.
Comment #4
merlinofchaos commentedOk, that's interesting! I'm very intrigued. I need to think about this a little bit more but I really like this.
Comment #5
jpetso commentedGoing back to the original issue topic, this is how the current code of my class-dependency aware extension loader looks like. I thought maybe you're interested in real-world usage outside of your own killer modules :) If some of that code could be replaced by stuff in ctools, that would be neat.
Generally, the code below first retrieves the [module].transformations.inc files, then gathers the plugin files and dependencies by calling a hook (which is supposed to reside in [module].transformation.inc), and then gathers additional information about the separate plugins from each plugin file - that way, plugin info and the corresponding class can live in the same place.
Comment #6
merlinofchaos commentedThe difficulty with this is that it's largely a module_invoke replacement, which is always a bit difficult if you might need references. Still, there's definitely a few things that we could probably abstract out of that in a satisfactory manner. We could assume, for example, that it's data collection only and that usually doesn't need references, for example. I'll ponder this, though it's not on my priority list just yet.
Comment #7
jpetso commentedOk. The current system works just fine for me, so whatever happens (or not) is ok with me :)
Comment #8
merlinofchaos commentedWould having a hook that looks something like this work well for you?
With that, we might be able to reduce a lot of that code to:
And
Now, one difference between my general philosophy and what you have is this:
I generally have that info as part of a registration function, so you get all the information via the _compile type hook, above, which would get a full list of all classes in the system. You could then use:
That would walk up the parent chain and include all the files. You'd still be responsible for invoking your own objects. This all mirrors how Views does things, of course, which is part of why I'm not completely sure if it'll work in your world, but I wanted to run this by you and see.
(Also, if you're interested in expanding this, I'll happily accept contributions. This could be quite useful to people).
Comment #9
merlinofchaos commentedAlso with these changes it might be necessary to separate the API stuff into its own .inc file; it isn't *really* related to the plugins and doesn't need the code, and additionally might be confusing since we're creating two very similar concepts and separating them might improve DX.
Comment #10
merlinofchaos commentedOh and I'm not sold on _compile as a good name. Maybe _collect.
Comment #11
neclimdulI'm looking for exactly this functionality as well. I went over some stuff with merlin today and ended up with something fairly simple that did this using the and a trivial patch to ctools. That clouds the issue a bit but it means that we might look at what can be share between plugins and this api model.
Very interested in where this goes.
Comment #12
merlinofchaos commentedAnd one more comment: I'm still dithering on how separate the API ideal should be from the plugin ideal.
Comment #13
jpetso commentedre #8: That should work great for me.
I understand the intention behind that, but in my case I wanted to move the information as near to the plugin itself as possible. The data that I'm querying from [module]_operation_[className]() is really stuff that would have belonged into a static $myoperation::info() method, but that's only usable from PHP 5.3 upwards. It's just label, description, "is abstract" (can I find this out programmatically) and an operation-specific property called "type options". I want to keep this local to the plugin, in order to ensure it's properly synchronized by the plugin author when the class changes.
The full list of classes is still contained in the [module].transformations.inc file, so the _compile hook actually does compile all the classes. I could add my custom stuff via ctools_plugin_api_alter(), so that should be fine.
Comment #14
neclimdulCaffeine frenzy; feedapi_eparser hacking; and a generous amount of leading from merlinofchaos lead to the following patch. I have only loosely tested it so there may be edge bugs, copy paste errors in docs etc. Serious review welcome!
Comment #15
neclimdulminor fixes from merlin's review on irc.
Comment #16
neclimdulmore awesome feed back. now with fewer notices and better docs!
Comment #17
merlinofchaos commentedNice to have this one in! Thanks!