From: Aaron Trevena Date: Fri, 8 Jul 2005 19:21:23 +0000 (+0000) Subject: added lib/Maypole/Manual/Plugins.pod X-Git-Tag: 2.10~12 X-Git-Url: https://git.decadent.org.uk/gitweb/?p=maypole.git;a=commitdiff_plain;h=8697b8b5b7f1a45960ef15dd31b9509d31f93d48 added lib/Maypole/Manual/Plugins.pod git-svn-id: http://svn.maypole.perl.org/Maypole/trunk@352 48953598-375a-da11-a14b-00016c27c3ee --- diff --git a/lib/Maypole/Manual/Plugins.pod b/lib/Maypole/Manual/Plugins.pod new file mode 100644 index 0000000..f0582c6 --- /dev/null +++ b/lib/Maypole/Manual/Plugins.pod @@ -0,0 +1,258 @@ +=head1 NAME + +Maypole::Manual::Plugins - the Maypole Plugin API + +=head1 VERSION + +This version written for Maypole 2.10 + +=head1 LOADING PLUGINS + +Plugins occupy the C namespace on CPAN. At time of writing, there are 16 plugin modules +available - see http://search.cpan.org/search?query=Maypole%3A%3APlugin&mode=module + +Plugins are loaded into a Maypole application by L. For instance, to add L +support to the BeerDB example application: + + package BeerDB; + use strict; + use warnings; + + use Maypole::Application( 'QuickTable' ); + +Note that the leading C is omitted. + +For some plugins, that's it. You probably have a bunch of new methods available on your Maypole request objects - +see the documentation for the plugin. + +For others, you will need to set configuration variables or customise other +parts of the application. For instance, to add sessions to your application, you can use L: + + package BeerDB; + use strict; + use warnings; + + use Maypole::Application( 'Session' ); + +That's all, if you're willing to stick with the defaults (L backend, session and lock files in C and C). Otherwise, you need to supply some configuration: + + __PACKAGE__->config->session( { class => "Apache::Session::Flex", + args => { + Store => 'DB_File', + Lock => 'Null', + Generate => 'MD5', + Serialize => 'Storable' + } + } ); + +The plugin module is responsible for adding slots to L, in this case, the C accessor. + +=head1 WRITING PLUGINS + +=head2 Modifying the Maypole request object + +Plugins are inserted into the C<@ISA> of the Maypole request object. So method calls on the request object will +first search the plugin classes, before looking in L. Methods defined in the plugin are +therefore directly available on the request. That also goes for methods inherited by the plugin. I'm not aware +of any plugins that currently inherit from another package, but there's no reason not to. + +Note that if you need simple accessor methods on the request, you can add them by saying + + Maypole->mk_accessors( qw/ fee fi fo / ); + +at the start of your plugin. Under mod_perl, you've just added these accessors to B Maypole applications +on the server, even ones that do not use this plugin. You could instead make the call inside the C method: + + $r->mk_accessors( qw/ fee fi fo / ); + +Now the accessors are only added to applications that use this plugin. + +=head2 Initialisation with C + +After loading plugins via L, setting configuration variables in calls to +C<< __PACKAGE__->config->foo( 'bar' ) >>, and optionally defining custom request methods, your +application should call its C method, generally including arguments for the database connection: + + __PACKAGE__->setup( $dsn, $user, $pass, @more_args ); + +All of these arguments will be passed to the C method of the model class. + +C is responsible for loading the model class, calling the C method +on the model class, and making each table class in the application inherit from the model. It is therefore +recommended that you call C B setting up all your configuration options. + +Plugins can intercept the call to C to carry out their own initialisation, as long as they propagate +the call up through the hierarchy. A common idiom for this is: + + Maypole::Plugin::Foo; + use strict; + use warnings; + + use NEXT; + + sub setup + { + my $r = shift; + + $r->NEXT::DISTINCT::setup(@_); + + # Foo initialisation goes here + my $option = $r->config->foo_option; + + # do something with $option + } + +L is a replacement for the built-in C syntax. C dispatches a call to the superclass +of the current package - B it determines the superclass at compile time. At that time, the superclass +is something like C. L does the superclass lookup at runtime, after L has +inserted the plugin into the request class's inheritance chain. + +The C modifier ensures each plugin's C method is only called once, and protects against +diamond inheritance. This may or may not be an issue in your app - and if you always use the C +syntax, it won't be. + +Notice that the C call is re-dispatched before running the plugin's own initialisation code. This +allows C to set up the database, model, and table classes, before your plugin starts tweaking +things. + +You can use the C method to load modules into the request class namespace. L has: + + sub setup { + my $r = shift; + $r->NEXT::DISTINCT::setup(@_); + require Locale::Maketext::Simple; + import Locale::Maketext::Simple + Class => $r, + Export => '_loc', + Path => $r->config->lexicon; +} + +Now the application namespace has a C<_loc> function (exported by L), (plus C and +C methods inherited from L). + +=head3 More initialisation with C + +L also defines an C method. It +pulls the name of the view class from the config, loads it, instantiates an object in the view class, and +sets this in the C config slot. + +In CGI applications, C is called at the start of every request. + +Under mod_perl, this method will only ever be called once per server child, at the start of the first request after +server startup. If instead, you call this method in your application module (after the call to C), +then the code loaded by this call will be shared by all child servers. + +See B for a plugin that uses C. + +=head2 Adding configuration + +The configuration object can be retrieved from the Maypole request object (C<< $r->config >>) or as a class method +on the application (e.g. C<< BeerDB->config >>). + +If your plugin needs some custom configuration settings, you can add methods to the config object by +saying + + Maypole::Config->mk_accessors( qw/ foo bar baz / ); + +at the start of your plugin. In the application, after the C call, these methods will +be available on the config object. + +=head2 Modifying the Maypole model + +=over 4 + +=item Replacing the model + +To load a different model, set C<< __PACKAGE__->config->model( 'Custom::Model' ) >> in the application +before calling C. You could instead set C<< $r->config->model >> before re-dispatching the C call, +but this is going to confuse and annoy your users. + +=item Hacking the model + +B: the way I do this just seems dirty, so there must be a Better Way. + +L (part of the L distribution), in its C method, +loads a custom pager class into the model by saying + + eval "package $model; use $pager"; + +Yuk. Note that under mod_perl, you have just forced B application using C<$model> to also use C<$pager>. + +C loads an extra method into the model by saying + + no strict 'refs'; + *{"$model\::auto_untaint"} = \&Class::DBI::Plugin::AutoUntaint::auto_untaint; + +Yuk again. And again, under mod_perl, now every application using C<$model> has an C method +added to its model. + +Same plugin, next line has + + eval "package $model; use Class::DBI::Plugin::Type"; + +Same yuk, same mod_perl caveat. + + + + +=back + + +=head2 Modifying the Maypole view + +=over 4 + +=item Replacing the view + +Again, just specify a different view in the application configuration. + +=item Hacking the view + +L intercepts the C call to override the C method in the view class. +First it re-dispatches the C call, which will set up either a default view class and object, or those +configured in the application. Then it builds a new view class on-the-fly, and makes this new class inherit from +L and from the original view class. Finally it replaces the C and C +in the application's config object. + + sub init + { + my ( $class ) = @_; + + my $config = $class->config; + + $class->NEXT::DISTINCT::init; + + my $old_view = $class->config->view || + die "Please configure a view in $class before calling init()"; + + my $virtual_view = "$class\::__::View"; + + eval <view( $virtual_view ); + + $class->view_object( $virtual_view->new ); + } + + +There really must be a Better Way. + +=back + +=head1 AUTHOR + +David Baird, C<< >> + +=head1 COPYRIGHT & LICENSE + +Copyright 2005 David Baird, All Rights Reserved. + +This text is free documentation; you can redistribute it and/or modify it +under the same terms as the Perl documentation itself. + +=cut