|Think about Loose Coupling|
Request For Comment: Web Application Plugin Managerby andreychek (Parson)
|on Oct 01, 2002 at 17:25 UTC||Need Help??|
I'm currently working on a module called OpenPlugin. It's a plugin manager for web applications. It allows you to make use of a number of plugins, each which can have any number of drivers. For example, the Log plugin has drivers for logging to STDERR, Files, Syslog, etc. The Session plugin has drivers for storing sessions in Files, DBI, etc. Changing drivers is easy, you just change the driver name in a config file.
OpenPlugin even has plugins for params, cookies, httpheaders, and uploads, which have drivers for Apache and CGI. These plugins abstract Apache::Request and CGI.pm, allowing you to build applications that can work seamlessly under mod_perl or CGI. If you want to move your application from one environment to another, you again can just change the driver being used in the config file.
Also in this config file, you can define whether a plugin loads at startup time, or only on demand.
My question though, it's regarding how you feel about it's interface. Currently, to access plugins under OpenPlugin, you use the syntax OpenPluginObject->PluginName->method. For example:
This Obj->Plugin->method syntax was used for several reasons:
The above syntax prevents us from saying, every time we want to use a plugin method for the first time:
Instead of all that, we just need to say:
It's then OpenPlugin's job to make sure the plugin and it's driver are properly loaded, and to return an object of that particular plugin. fetch() is then called on that object.
If you're just opposed to using the arrow operator twice on one line, you could always say:
As I mentioned, some plugins are not loaded at startup time, they are only loaded when they are accessed. If we know we won't be using 5 out of the currently 10 plugins, why bother loading them?
With the above Obj->Plugin->method syntax, OpenPlugin receives a method call for Plugin. If Plugin doesn't exist, OpenPlugin can now do everything it wants/needs to in order to load that plugin, and make it available to the calling application.
Are there other ways to implement this? Sure, we could have the application author load OpenPlugin::Session first, and then call it's new() constructor. I find this undesirable, I don't think the application author should have to worry about if and when to load a plugin. Another way to do it is to make use of Class::Factory (which OpenPlugin already does internally). Class::Factory would allow us to instanciate a new plugin by saying:
Class::Factory handles loading the plugin. Much better. But it's still an extra line of code than the existing $OP->session->fetch() offers us.
Just a little more nitty gritty.
What happens when I call $OP->session->fetch for the first time? Well, if session wasn't loaded at startup, OpenPlugin has no 'session' method. In fact, OpenPlugin knows nothing about it's plugins, aside from a config file it is given with some configuration information in it.
So, the first time $OP->session is called, AUTOLOAD ends up being called. AUTOLOAD invokes a method to look up the plugin name (session, in this case) in the config file, determine which driver to load, and then load it. OpenPlugin calls the plugin's constructor, and saves that plugin's object internally in a hash. Lastly, it creates a method called "session" in OpenPlugin's namespace. From here on out, whenever an application calls the "session" method, OpenPlugin returns the session object that we stored.
The relationship of the various plugins and modules is like so:
Notice that OpenPlugin.pm is not in this heirarchy. OpenPlugin is a standalone class, it had no parents and no children. OpenPlugin simply offers a means of accessing plugins.
I bring all this up because I'd like opinions on this interface. I had spoken at length with some folks in the chatterbox yesterday, who felt there might be a better way of doing the above. So, I wanted to explain as best I could what all was going on, and to see what the opinions are on this particular interface. I would very much like to make this available on CPAN, so I wanted to straighten this out first :-)
For those who are interested, my scratchpad contains the full OpenPlugin.pm file, sample config file entries, and sample usage. Also, if you're interested in trying it out on your system, feel free to msg me and I'd be happy to send you an up to date copy of the full distribution.
I look forward to any comments, ideas, and constructive criticism. Even though it seems longer, would you still prefer to call a new() method on each plugin? Is there something I didn't mention that you would like even better? And if you like it as is, please let me know that too :-) Thanks!
Lucy: "What happens if you practice the piano for 20 years and then end up not being rich and famous?"
Schroeder: "The joy is in the playing."
edited: Wed Oct 2 01:54:38 2002 by jeffa - added <readmore> tag