
=head1 NAME

 Plugins::Style1 - Generic plugins framework with linear config files

=head1 SYNOPSIS

 use Plugins::Style1;

 $plugins = Plugins::Style1->new([context => $context])
 $plugins->readconfig($config_file, self => $self)
 $plugins->initialize()

 $plugins->invoke($method, @args);

 $plugins->invoke_until($method, sub { scalar(@_) }, @args);

 my $iterator = $plugins->iterator();
 while (@results = &$iterator(@args)) {
 }

 for my $plugin ($plugins->plugins()) {
	$plugin->invoke($method);
 }

=head1 DESCRIPTION

Plugins::Style1 is a generic plugins framwork with a simple
linear-style configuration file that can support plugins of
plugins in the same configuration file.

It is based on L<Plugins>.

=head1 EXAMPLE CONFIG FILE (SIMPLE)

 sleeptime	1

 plugin SyslogScan::Daemon::BlacklistDetector as bld_

 bld_debug	0

 bld_plugin SyslogScan::Daemon::BlacklistDetector::Postfix
	 rx_ourIP	127\.0\.0\.1
	 logpath	/var/log/mail.log
	 debug		0

 bld_plugin SyslogScan::Daemon::BlacklistDetector::EmailNotify
	 debug		1
	 notify		"John <root@localhost>"
	 renotify_time	7200
	 forget_time	3600
	 sendfrom	root
	 clean_time	1800
	 maxkeep	100


=head1 CONSTRUCTION AND INITIALIAZATION

In addition to the parameters for C<new()> and
C<readconfig()> documented for L<Plugins>, the following
additional parameters may be given...

=head2 %args for new()

=over 4

=item parse_config_line =E<gt> \&func

The unknown line parser can be specified here or in
C<readconfig()>'s C<%args>.

=back

=head2 %args for readconfig()

=over 4

=item self =E<gt> $self

Set C<self> to let C<readconfig()> know who called it.  With that
information, C<readconfig()> can make callbacks for the other parameters.
Without a C<$self> C<readconfig()> uses C<caller()> to and uses class
methods instead of object methods.

=item config_prefix =E<gt> "prefix_"

Since the configuration file can support multiple plugins at the
same time, we need to distinquish what goes with what plugin.  This
is done by setting a configuration prefix here or when calling C<new()>.
Only config lines that start with the prefix
will be parsed: everything else will be ignored.
The root/parent program should have an empty string as its prefix.

If this is unspecified, C<$self-E<gt>config_prefix()> will be called.  If that
doesn't exist then an empty-string prefix will be assumed.

For configuration file readability, your prefix should probably end with an underscore (C<_>).

=item parse_config_line =E<gt> \&func

C<readconfig()> knows about lines that start C<${prefix}plugin>, but
for everything else it needs help.  The C<parse_config_line> parameter should be
a reference to a function to be called when there is a configuration line
other than C<${prefix}plugin>. 

If no C<parse_config_line> parameter is specified then C<$self-E<gt>parse_config_line()>
will be tried.  If that doesn't exist either then an unknown configuration
line is a fatal error.

The arguments passed to the C<&$parse_config_line()> function are:

=over 4

=item $self/$pkg

The object or package of the caller.

=item $config_prefix

The configuration prefix for the plugin being invoked.

=item $configfile

The filename of the configuration file

=item $line

The configuration line.  The prefix has not been removed.

=item $line_number

The line number of the line in the configuration file (for generating
nice error messages).

=item $seqno

An integer that increments each time the same configuration file
is read.

=back

=back

=head1 CONFIGURATION FILE FORMAT

The configuration file parser knows about four types of lines:

=over 4

=item Comments

The hash character (C<#>) denotes a comment and except for within
quotes a hash character will end parsing for that line.

=item Plugin requests

B<E<lt>prefixE<gt>plugin E<lt>/file/name<gt> [as E<lt>prefix_overrideE<gt>]>

B<E<lt>prefixE<gt>plugin E<lt>module_nameE<gt> [as E<lt>prefix_overrideE<gt>]>

 plugin Foobar::Baz	# defaults to prefix "fb_"
 plugin /some/file	# defaults to no prefix
 plugin a_file		# defaults to no prefix

 fb_plugin Foobar2::Baz as fb2_

The basic part of a configuation file is a line that starts with
the word C<plugin>.  What follows on the same line is the name of
a perl module.  The word C<plugin> may be preceeded by a prefix.  
The prefix disambiguates whose plugin it is when multiple plugins
are sharing a configuration file.

The prefix is normally given by the C<config_prefix()> class method
in the plugin module, but it can be overridden (in order) by 
(1) the config file; or (2) by an argument to readconfig 
(C<readconfig($configfile, config_prefix =E<gt> 'foobar_')>) or (3)
as by member data of the caller to readconfig():

 $self->{config_prefix} = "bar_";
 my $plugins = new Plugins;
 $plugins->readconfig($configfile, self => $self);

To override the prefix in the config file, add C<as prefix-name>
the end of a plugin line.

Plugins that are fully-fledged perl modules should be named as such.
Plugins that are not, (named as files) will be wrapped by L<Plugins>
into a perl module.  Plugins::Style1 dis-ambiguates between files and
modules by looking for the string C<::> in module names.

=item Plugin search directory directive

Plugins that are plain filenames that are not absolute
paths are searched for with their own search path.  The
The directive to add to that path is C<plugin_directory>. 

 plugin_directory /some/path
 plugin_directory /another/path

Directory names may not have whitespace in them.  Directories
that do not exist will be silently ignored.

=item Arguments to new()

Any indented lines that follow a plugin request will set what 
arguments are passed to the C<new()> when the module is initialized.

The indented lines a broken up on word boundries except that what
is between double-quotes or single-quotes counts as a single word.

 bld_plugin SyslogScan::Daemon::BlacklistDetector::EmailNotify
	notify		'"David Sharnoff" <muir@idiom.com>'
	from		root@my.poor.system

Will produce a call like:

 SyslogScan::Daemon::BlacklistDetector::EmailNotify->new(
	'notify', 
	'"David Sharnoff" <muir@idiom.com>', 
	'from', 
	'root@my.poor.system');

=item Freestanding configuration lines.

Lines that that begin in column one that aren't plugin requests,
are regular configuration lines.  By default they are parsed by
the C<parse_config_line()> method of the plugin that has
called C<readconfig()>.  The function called can be overridden
with the C<%args> to C<readconfig()>.  The function will be called
as a object method if a C<$self> was provided to C<readconfig()>.
It will be called as a class method otherwise.

Lines in the configuratin file that being with a plugin prefix
will be parsed by that plugin's C<parse_config_line()> method
(if it has one).  This will happen after C<new()> is called so
C<parse_config_line()> will be called as a class method.

The function to call for these lines defaults to C<parse_config_line()>
but it can be overridden by passing a function to the call to
readconfig: 

 my $plugins = new Plugins;
 $plugins->readconfig($configfile, parse_config_line => \&some_function);

Or it can be overridden with member data of the caller:
	
 $self->{parse_config_line} = \&some_parse_function;
 my $plugins = new Plugins;
 $plugins->readconfig($configfile, self => $self);

If no parse_config_line() function is provided, the parser will die
on any lines that aren't recognized.

=back

The same configuration file is potentially re-read for each plugin. 
Generally plugins that have plugins will need to be calling C<readconfig()>.

=head1 EXAMPLE CONFIG FILE (NOT SO SIMPLE)

 sleeptime	10

 plugin SyslogScan::Daemon::BlacklistDetector
	debug		1

 bld_plugin SyslogScan::Daemon::BlacklistDetector::Postfix
	debug		1
	rx_ourIP	216\.240\.47\.\d+
	logpath		/var/log/mail.log

 bld_plugin SyslogScan::Daemon::BlacklistDetector::EmailNotify
	debug		1
	notify		you@yourhost.
	renotify_time	7200
	forget_time	3600
	sendfrom	root	# comments are allowed here
	clean_time	1800
	maxkeep		100

 bld_plugin SyslogScan::Daemon::BlacklistDetector::KeepTrack
	# indented comments are allowed here
	debug		1
	dbi_dsn		DBI:mysql:database=quarentene;host=localhost 
	username	quarentene 
	password	bloorf 
	table_prefix	kt_
	# comments can go nearly anywhere
	postcommand	"(cd /etc/postfix; make)"
	postfile	/etc/postfix/outbound-problems
	pool		/etc/postfix/outbound-list
	latency		10
	decay_day	3600
	decay_rate	0.98
	decay_done	0.2

 # of course, comments are allowed in column 1.

 plugin SyslogScan::Daemon::BlacklistDetector as bld2_
	debug		1

 bld2_plugin SyslogScan::Daemon::BlacklistDetector::Postfix as x1_
	debug		1

 x1_rx_ourIP	216\.240\.47\.\d+
 x1_logpath	/tmp/mail.log

 bld2_plugin SyslogScan::Daemon::BlacklistDetector::EmailNotify as x2_
	debug		1

 x2_notify	you@yourhost
 x2_renotify_time	7200
 x2_forget_time	3600
 x2_sendfrom	root
 x2_clean_time	1800
 x2_maxkeep	100

 bld2_plugin SyslogScan::Daemon::BlacklistDetector::KeepTrack as x3_
	debug		1
	postcommand	''

 x3_dbi_dsn	DBI:mysql:database=quarentene;host=localhost 
 x3_username	quarentene 
 x3_password	''
 x3_table_prefix	kt2_
 x3_postfile	/etc/postfix/outbound-problems2
 x3_pool	/etc/postfix/outbound-list
 x3_latency	10
 x3_decay_day	3600
 x3_decay_rate	0.98
 x3_decay_done	0.2

=head1 WRITING PLUGINS

=head2 Plugin-defined methods

Plugins should be subclasses of C<Plugins::Style1>.

=over 4

=item config_prefix()

This method is used to determine the default configuration-line prefix
for the plugin.  Defining this method is manditory.

=item parse_config_line($prefix, $configfile, $line, $lineno, $seqno)

If your want to be able to have configuration directives other than what is
passed to C<new()>, then you'll need to define a C<parse_config_line()> method.  
Eg:

 my $bar = 7;

 sub config_prefix { 'foo_' }

 sub parse_config_line
 {
	my ($self, $prefix, $configfile, $line, $lineno, $seqno) = @_;
	if ($line =~ /^${prefix}bar\s*=\*(\d+)) { 
		$bar = $1;
	} else {
		die "illegal config at $configfile line $lineno\n";
	}
 }

One way to get a simple parser like this and also something to handle
arguments to C<new()> is to use L<Plugins::SimpleConfig>.

=back

=head2 Plugins with plugins

One subtle aspect of sharing configuration files is that plugin prefix may
be overridden.  This is done on a per-configuration file basis.  If for some
reason you want to un-override your configuration prefix then don't pass that
part of the C<$context>:

 $self->{plugins} = new Plugins context => $self->{context}, config_prefix => 'something_else_';

Since the I<child requestors>'s configuration file may get read twice, 
the C<parse_config_line> method may get called twice.  To avoid this, pass
in a null function for C<parse_config_line>:

 $self->{plugins} = new Plugins context => $self->{context}, parse_config_line => sub {}

Or

 $self->{plugins}->readconfig($config, self => $self, parse_config_line => sub {});

In most cases there probably isn't any harm in this double parsing.


=head1 SEE ALSO

L<Plugins>

=head1 THANK THE AUTHOR

If you find this module useful and wish to show your appreciation to the
author, please give me a Request-For-Quote on your next high-speed 
internet pipe order.  I have good pricing for T1s, T3s, OC3s etc.

=head1 LICENSE

Copyright (C) 2006-2007, David Muir Sharnoff <muir@idiom.com>. 
This module may be used and redistributed on the same terms as Perl
itself.
 
