On 12/20/05, Mark Stosberg <[EMAIL PROTECTED]> wrote:
> The role of CGI::App should to make it easy to include this 'best
> practice' in your workflow. We can do that providing a standard,
> minimal logging API, and let plugins do the heavy lifting.

Hi Mark,

I guess my last message didn't come across as intended, as I am not
disagreeing with you on the need for this standardization.  I think it
is a very important step to take.  But it seems to me you are
defending a specific implementation of this problem and making it look
like it is the only solution (ie adding a stub method to the core). 
All I am saying is that adding a stub into the core is not the only
way of solving this particular problem, and it would be worthwhile
looking at alternative implementations as well.

What I am afraid of is that we will end up with a whole bunch of
methods in the core that are nothing more than placeholders.  This
would further complicate the docs, and add methods that do absolutely
nothing without extra modules/plugins.  I know that there are already
lots of empty stub methods in the core, but they are intended to be
subclassed and overridden, and are never called directly, they are
only called by CGI::Application internally.  So I believe there is a
difference here.

So let me suggest one possible alternative.

>From what I can see, there are only two main requirements.  If we look
at the config example, this is what I think is required:

1. common interface that all config plugins should adhere to
2. allow other plugins to painlessly call this common interface (even
if the application is not actually using a config plugin)

I think that it is requirement number 2 that is pushing you to put a
config stub in the core.  However, there is an alternative that would
allow us to have the config stub in a plugin
(CGI::Application::Plugin::Config) and still match the requirements.

The first requirement is easily met with a plugin, since it will
define a standard API that all Config plugins should adhere to.  The
actual CAP::Config plugin will only provide stub methods that a real
plugin can link into (perhaps by registering themselves with
CAP::Config when they are loaded)

package CGI::Application::Plugin::Config::FancyXML;

use CGI::Application::Plugin::Config;
CGI::Application::Plugin::Config->register('FancyXML');
### more stuff here to fully implement whatever api there is


So that will cover requirement 1.  For requirement 2, what we would
need is for a plugin to be able to load other plugins on demand.  So
the CAP::Session plugin could then load the CAP::Config plugin at
startup so that it knows the Config interface will be available later
on when it needs it (note that I didn't say load the Config::FancyXML
plugin, as it only really needs the stubs to be in place which are
provided by CAP::Config).

package CGI::Application::Plugin::Session;

sub import {
  my $class = shift;
  my $app = caller;
  $app->load_plugin('Config');
}

What this will do is make sure the that CAP::Config stub methods are
loaded for the Session plugin to use later on.  If the user has also
loaded the Config::FancyXML plugin somewhere else, then the Session
plugin may very well get some valid configuration data when it calls
$self->config.  If however the user is not using a Config plugin, then
the stub will return undef for all calls to $self->config and the
Session plugin will just need to look elsewhere for it's
configuration.

my $session_options = $self->config('Session_options')
                                      || $self->session_config()
                                      || $self->session_defaults;

That should be sufficient to deal with requirement 2.  It means adding
an extra line to your plugins to tell it what plugins it depends on,
but that shouldn't be that onerous.

So all that is required for us to be able to pull the config stubs out
of the core and into a plugin is the ability for a plugin to load
another plugin.  And as a side-effect, users can also load their
plugins the same way.

package My::App;

use base qw(CGI::Application);
use CGI::Application::Plugin;

My::App->load_plugin(
    'My::Custom::Plugin',
    ['Config::FancyXML' => '/etc/myapp.conf'],
    'TT',
    'Session',
);

And once we have a load_plugin method that actually loads the plugins,
we can also do some more tricks like calling a standard initialization
method in the plugin when it is loaded (this would take the place of
the current 'import' method):

package CAP::Session;

sub plugin_init {
  my $class = shift;
  $class->load_plugin('Config');
  $class->export_methods(qw/session session_cookie .../);
  return 1;
}


And since you probably saw me sneak in the 'export_methods' call in
there, we can do some sanity checks on the methods we are importing
into the application namespace to check for method name conflicts. 
Now that I think about it, it actually might be easier to do the
method exporting with global variables so that it is similar to the
way Exporter does it:

package CAP::Session;

our @EXPORT       = qw/session/;
our @EXPORT_OK = qw/session_cookie session_config/;

sub plugin_init {
  my $class = shift;
  $class->load_plugin('Config');
  return 1;
}

Then the load_plugin method can look at these variables (whatever we
decide to call them) and decide what methods need to be exported. 
Optional methods can then be specified directly in the call to
'load_plugin'.  You could even go so far as to allow method attributes
as well.

sub session : RequiredExport { ... }
sub session_cookie : OptionalExport { ... }



This message looks like it is once again longer than I intended (which
seems to be the norm with me...), and it may seem like a much more
complex solution than just putting a stub method in the core.  But I
think what it does is solve an underlying deficiency in the way
plugins are currently built.  That is, knowing what other plugins are
loaded and being able to load plugins from anywhere when they are
needed.  And as a side effect gives us a simple method for loading
plugins, and managing namespace conflicts.  Also, all of this can be
implemented outside of the core which keeps CGI::Application itself
small and simple for the purists.

Hopefully this won't draw people's attention away from the Config
discussion, since that is the ultimate goal, but I thought I would
throw out an alternate way of dealing with the actual implementation.

OK, I'm ready ---- everyone get out your flame-throwers ;-D

Cheers,

Cees

ps I wrote this last night, but decided to wait until today to send
it.  During that time it seems there is now a new plugin called
CGI::Application::Pluggable...

---------------------------------------------------------------------
Web Archive:  http://www.mail-archive.com/[email protected]/
              http://marc.theaimsgroup.com/?l=cgiapp&r=1&w=2
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to