theirpuppet has asked for the wisdom of the Perl Monks concerning the following question:

I've never done anything with plugins before (except use them in applications), so I'm at a complete loss as to where to begin. I'm looking at potentially creating some new projects, which have been on my mind for quite some time, but implementation details get stuck on plugins and good OO design... I'd like to learn a little more about the theory and practice of plugins before actually trying to code an application around the concept.

Can someone please point out some good resources - hopefully some covering the basics like methodology, good practices, registers / interface design, etc - of plugin programming? I'd especially like to look at stuff that is either Perl specific or has Perl examples/references.

Thanks in advance.
:)

Replies are listed 'Best First'.
Re: Plugin Programming
by TedYoung (Deacon) on Feb 14, 2005 at 16:10 UTC

    I wish I could point you to some resources on the topic, but I can give you my experience with Perl and plugin development.

    Generally, each plugin should have a single point of interaction with your program. Normally, this is a package that implements an interface specified by the application. Then the plugin can have its own set of supporting libraries.

    So, you might have an interface that all plugins meet. The interface can be as simple as a list of methods that the plugin's implimentation class needs to support. However, you could provide a partial implimentation that can be subclassed by the plugin developer.

    Here is an example of how you might load plugins in your application. In this example, each plugin is a PM file located in a directory called plugins.

    sub LoadPlugins { my %plugins = (); for (<"plugins/*.pm">) { my ($pluginName) = /plugins/(\w+)\.pm/; require $_; # Create a new instance $plugins{$pluginName} = $_->new; } return %plugins; }

    Now, this is a very simple example, but should get you started.

    Ted Young

    ($$<<$$=>$$<=>$$<=$$>>$$) always returns 1. :-)
      Great advice!

      I realize the code above is just a demo and not a full implementation, but just a note on coding and error handling as appropriate here. I'd rewrite some of the above like this:

      eval { require $_; my $plugin = $_->new or die "failed to instantiate\n"; $plugins{$pluginName} = $plugin; } or logger("failed to load plugin: $@");

      Plugins can and will fail, so treat them with some skepticism :) -- this is actually a benefit of decoupling that you can afford to keep the main application running even if a plugin failed. In terms of development, if you expose a mechanism to rescan the plugin repository, you can reattempt to load a failing plugin without restarting your application. (Theoretically you could also implement plugin unload routines, but that's harder.)

      Plugins are a great opportunity to use either interfaces or traits. Getting compile-time (or at least, plugin load-time) verification that it conforms to the plugin interface is nice.

Re: Plugin Programming
by borisz (Canon) on Feb 14, 2005 at 16:04 UTC
Re: Plugin Programming
by kvale (Monsignor) on Feb 14, 2005 at 16:20 UTC
    I think Module::Pluggable is a good start as well. The general tasks that a plugin API must handle are
    • registration of all plugins upon program startup - the above can handle this
    • initialization of plugins - put any initialization code in constructor
    • activate plugins - need to have something like an activate() method in the plugin class
    • cleanup of plugins - More important for C than Perl, but plugins may need to write preference files, etc.
    Along with these basic tasks, you will need to figure out what sort of program data structures activate() has access to and what it should return. Always return error codes and check them. It makes debugging a lot easier.

    -Mark

Re: Plugin Programming
by dragonchild (Archbishop) on Feb 14, 2005 at 16:56 UTC
    I'm writing a distro that has plugin support all over it. The steps I've taken are:
    1. Define what can vary. This is harder than it looks and is the most important feature of all.
    2. Define what interface the varying bits are required the conform to. They may provide more than this, but they have to provide at least this much.
    3. Write a plugin loading scheme. Module::Pluggable is good. Mason and CGI::Application both have their own. Mason's is more defined as opposed to C::A's which is more Wild-West'ish.
    4. Sit back and let the users do what they need to do.

    Note: Plugin systems can get very complicated. Take a look at what DBI does, one day. That's complicated!

    Another plugin system of note is the XML::SAX stuff.

    Being right, does not endow the right to be rude; politeness costs nothing.
    Being unknowing, is not the same as being stupid.
    Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
    Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.

Re: Plugin Programming
by jdporter (Paladin) on Feb 14, 2005 at 16:05 UTC
    Well, there is no universal standard definition of "plug-in". It could be anything from a function (in languages where functions can be passed to other functions, such as Perl) to a hardware card or USB device. It would help if you would give some idea what kind of plug-in you're talking about. Source language-level classes? External code modules? Remotely addressable processes?
Re: Plugin Programming
by saintmike (Vicar) on Feb 14, 2005 at 19:00 UTC
    Here's a nice tutorial on the previously suggested Module::Pluggable.

    Also, here's an article on a real world application using it (this one is written in German, though).

Re: Plugin Programming
by cowboy (Friar) on Feb 14, 2005 at 16:47 UTC
    The latest version of Mason (1.29_01) includes plugin support.

    Digging through the code should give you some good practical examples.

    The main reason for plugins, in my mind, is to allow customization, without having to alter the main program.
Re: Plugin Programming
by dimar (Curate) on Feb 14, 2005 at 17:05 UTC

    Never implemented plugin support before, but just a thought: would not plugin support be analagous to designing a system for creating modules for a programming language? This comes to mind because some languages do this better than others, and that might also give some best-practices guidance (e.g., compare modules in perl with modules in PHP).

Re: Plugin Programming
by crenz (Priest) on Feb 15, 2005 at 22:21 UTC

    Another module that might be suitable for you is Module::Find. I wrote it for an application where I needed plug-ins myself. It's nowhere near as full-featured as Module::Pluggable, though.

Re: Plugin Programming
by nothingmuch (Priest) on Feb 20, 2005 at 10:01 UTC
    When I had more time I started a project which needed lots of modularity. Nearly all features would be optional, so every part of the program was designed as a plugin.

    The only "core" was the definition of the data plugins knew about, the MVC-esque "model" of the program.

    The program was then (in theory) reincarnated many times into byte sized scripts, that, for example, load the plugin-loader-plugin, which assumes plugins are Module::Pluggable like in their format, and then loads them up.

    The system I wrote to back this up was centered around totum-pole objects. The instances of the plugin objects were joined into one object, which pretended to be all those objects at the same time. Each plugin had it's own private space, it's own instance, and there was a clear api letting you talk to other plugins.

    The general design was that a plugin was either a sort of care taker for a certain group of plugins, which is responsible for arranging for proper dispatch of a method, or something having to do with how simple plugins work together, or it is a plugin that provided functionality, and is arranged by some other plugin.

    To pipeline an operation, for example, you need about 5 lines of code in a caretaker plugin, simple filter like plugins which get and return much of the same data. To do type or capability based dispatch, for example, to make the method "ring" work on both bells and piano forks, you define some sort of protocol, to find out which plugin likes what:

    package Plugin; ... sub process { my $self = shift; my $foo = shift; $foo->FooMethod(); ... } sub processes { return qw/Foo/; # this module's process method likes objects which + isa Foo. }
    and then the "caretaker" will have a plugin search order, and the first plugin that processes our object, gets it:
    sub process { my $self = shift; my $obj = shift; my @args = @_; DISPATCH: foreach my $plugin ($self->host->stack("process") { # th +e stack of plugins that ->process foreach my $class ($plugin->processes){ next unless $obj->isa($class); return $self->host->specific($plugin)->process($obj, $args +); } } }
    For such an idiom I'd probably implement a
    package Plugin; sub process : expects(FooClass) { my $self = shift; my $foo = shift; }
    With the "caretaker" reading the attrs for the method. However, since I have no time to work on that project anymore, it's not being actively used.

    Anyway, without further ado, I give you Object::Meta::Plugin.

    -nuffin
    zz zZ Z Z #!perl