At the moment contrib packages are in trouble if they need to initialise. There needs to be an initialiser, e.g.
package TWiki::Contrib::SexySkin;
use vars qw($VERSION $RELEASE);
$VERSION = '$Rev$';
$RELEASE = 'TWiki-4';
BEGIN {
... do stuff ...
}
1;
It goes without saying that setup should be done in
configure if possible, but if not then this function provides an opportunity.
Of course extensions that require this function will
not be compatible with TWiki < 4.2
--
Contributors: CrawfordCurrie,
SvenDowideit
Discussion
I can feel the advantage of this, but a concrete example would be good.
--
RafaelAlvarez - 30 Apr 2007
Incorporated Sven's perspicacious BEGIN observation.
Raf, consider the simplest possible cases;
- I want to list the version numbers of all installed contribs.
- I want to load an extension to a standard TWiki module
Let's say I'm writing a compatability contrib for an old rev of TWiki. I want to add a
splurge function to
TWiki::Func,
and I want to add a new system tag called %CONTRIBVERSIONS%.
package TWiki::Contrib::BlahContrib;
use vars qw($VERSION $RELEASE);
$VERSION = '$Rev$';
$RELEASE = 'TWiki-4';
BEGIN {
# Note that this is another approach to an issue addressed by Meredith in her TWikiFns work.
TWiki::registerTagHandler(\&CONTRIBVERSIONS);
};
sub CONTRIBVERSIONS {
#my ($session, \%params, $web, $topic ) = @_;
return join(' ',
map {
$_.':'.($TWiki::cfg{Contribs}{$_}{Enabled} ? eval '$'.$TWiki::cfg{Contribs}{$_}.'::VERSION' : '(disabled)');
} keys(%{$TWiki::cfg{Contribs}});
}
# Extend TWiki::Func
package TWiki::Func;
sub splurge {
# implement the splurge function
}
1;
All that's required is the same sort of discover/enable mechanism as exists for plugins in
configure, and to add this to the end of the
BEGIN block in TWiki.pm:
foreach my $contrib ( keys(%{$TWiki::cfg{Contribs}} ) {
next unless $TWiki::cfg{Contribs}{$contrib}{Enabled};
eval "use TWiki::Contrib::$contrib";
die "Error loading $module: $@" if $@;
}
i.e. a very low code burden.
--
CrawfordCurrie - 01 May 2007
BEGIN blocks are executed only once in perl accelerators and initializers might need to initialize based on the current request.
Why not making such a contrib a plugin where the needed callback infrastructure exists?
(just asking to test your good idea)
--
MichaelDaum - 01 May 2007
I do not think there is a problem for contribs which need to
initialise. The problem arises if they need to register a callback. For that they need to be
compiled first, but in contrast to a plugin there is no canonical place where to put the
use statement for a contrib. Whenever they
are compiled, they can do their initialisation on the way.
This is what you get with an
{Enabled} key: Control
whether and
when the contrib will be compiled, and therefore when its global initialisation is being called. You need it if you want to register a tag handler
without having to write a plugin, i.e. without having to deliver an
initPlugin per-request initialisation handler.
But this suggestion comes at a price:
If
TWiki.pm compiles all "enabled" contribs, regardless of whether they are needed for a particular request, then this will slow down things in a
non-persistent interpreter, unless authors introduce an extra stub to allow for dynamic compilation within their contrib. So if there is a
%CONTRIBVERSIONS% tag, which is pretty unlikely to appear in many topics, it might better do such a discovery itself.
BTW: A
BEGIN block is not needed in the given situation. The statements are executed when the module is compiled without the block, and exactly once with persistent interpreters for the same reason. You'd only need a
BEGIN block if the rest of the
module would need the result of the block
at compile time.
--
HaraldJoerg - 01 May 2007
You can think a contrib as a module that
by definition is always compiled (if enabled). Most contribs (e.g. skins) will have very light compilation loads, because all they do is define a couple of variables. Others will have a heavy load, for example to install some fancy cache, or replace some big chunk of TWiki code. I think it's perfectly reasonable that well written heavyweight contribs should lazy compile as you describe. I don't really see the 'price' as something anyone can avoid paying.
(for an example of how awkward it currently is to inject contrib code, look at how the
JSCalendarContrib is used in Form.pm)
--
CrawfordCurrie - 06 May 2007
No good example: The way how JSCalendarContrib is used in
Form.pm is not awkward, it is
broken. A
require will simply
die if the module in question isn't available, The
require in
Form.pm is not wrapped in
eval, so examining
$@ is pretty useless.
But let's nevertheless examine the situation in
Form.pm: A core module finds an item (in this case: a 'date' field) which it can not handle itself. The current implementation just
knows that it should try to fire up
JSCalendarContrib.
How would initialising or "enabling" the Contrib help? Well - the code could get rid of the
require statement in
Form.pm. And simply call the method, praying that the module has been compiled elsewhere. It still would need to know
which method to call. Not a big progress.
More flexibility can only be achieved if
Form.pm would, for example, throw a well-named exception describing its pain "I have a data type I don't know - Help!". A routine catching this exception could then check whether there's a handler for this type of error. But again, it would not need a precompiled module to find out, a simple entry in
%TWiki::cfg could do. These entries are created by
configure, which already
does discovery (and performance does
not matter). So only
if the situation occurs
and there's a handler for it
and the Contrib is enabled it would be compiled.
I haven't looked thoroughly through the skins, but those I have looked at either have no need for compilation at all (e.g. Dandruff) or are good enough as plugins (e.g. Nat).
So what remains to look at is Contribs which override TWiki code. These need to be explicitly compiled, otherwise TWiki would ignore the Contrib and handle stuff as it always did. Would there be any problems if such "Contribs" would simply be implemented as Plugins and use the existing mechanisms?
--
HaraldJoerg - 06 May 2007
The JSCalendarContrib is the only example I have, because it's an example where a contrib has been pulled into the core to avoid the overhead of plugin discovery (the
DateFieldPlugin used to do this, but was obsoleted).
OK, in the absence of a strong counter-argument I have to agree with you; the only possible requirement is for %CONTRIBVERSIONS and that's really not a strong enough requirement to justify unconditional compiles. Let's just address these case-by-case. I have re-used
Bugs:Item3997
for a couple of the issues raised in the discussion above.
Good debate! Thanks!
--
CrawfordCurrie - 08 May 2007