Advanced content: for Plugins authors only
Every so often we find that there are code or data that are shipped with several plugins, addons or skins. This creates several problems:
- conflicts can arise between plugins that try to install the same data or code,
- multiple versions of the same code can be installed simultaneously in the TWiki installation,
- versions can get out of step
This topic describes how to package libraries of common code and data so that they can be shared between different TWiki plugins, addons and skins. It also describes how plugins authors can use the facilities of the
TWiki::Func
module to check dependencies in their plugins.
Modules packaged in this way are called
Contrib modules. While the mechanism described here is mainly intended for packaging code, it could also be used for shipping other contributed modules that have dependencies, such as content or templates.
Note: it would have been nice to re-use one of the standard packaging and distribution mechanisms, such as RPM or
CPAN. Unfortunately these make assumptions about the target installation environment that would be incompatible with the way that TWiki is distributed.
Packaging shared code
Shared code modules are shipped in the same way as plugins and addons
i.e. in a zip attached to a documentation topic on twiki.org. The zip is constructed so that a simple "unzip" command at the root of the TWiki installation will install the contributed module.
All shared code modules must have a
Perl Module (stub), whether they are Perl code or not, and a
documentation topic. The Perl module is requires so we can define the dependencies for shared code in a consistent and portable manner.
Step 1: Perl Module (Stub)
This module is required for dependency checking. It will always exist in the
TWiki::Contrib
namespace. The simplest possible module contains just a version number. For example, for the
JSCalendarContrib
module (which is principally Javascript) has the following stub:
package TWiki::Contrib::JSCalendar;
use vars qw( $VERSION );
$VERSION = 0.96;
1;
- Note that multiple versions of the same code in a single TWiki installation is not supported.
- Note that in order to support dependency checking, $VERSION should always be a valid number.
Step 2: Documentation Topic
There is always a documentation topic that is installed in the TWiki web alongside plugins and addons, as well as in the Plugins web on twiki.org. This topic is named
xxxxContrib
(where
xxxx
is the name of the contribution), and contains the documentation for the module.
Documentation for Perl modules can be hand-authored, or automatically extracted from the code via POD by the
BuildContrib
module. For other languages it may be hand-authored, or consist of links to documentation topics shipped in the
pub
directory (e.g. javadoc). In all cases it should follow the template shown in
NewContribTemplate.
NewContribTemplate
includes the definition of a variable called
STUB
, that must be set to the name of the Perl module for the code.
Note that the standard search summary is used as the code library equivalent of "SHORTDESCRIPTION".
When uploading the documentation topic to twiki.org, please ensure that the form type is
PackageForm
and the
TopicClassification
in the form is set to 'ContribPackage'.
Step 3: Everything else
With the topic and stub module in place, we still have to establish where to put the actual code. The rules are very simple:
- Installation should follow the "unzip at the top level" methodology, same as plugins
- Shared perl code should be in the TWiki::Contrib namespace, so should ship in
$twikiroot/lib/TWiki/Contrib...
.
- Web components that need to be publically accessible via a URL (for example, Javascript) should ship in the pub directory for the contrib topic e.g.
%TWIKIWEB/JSCalendarContrib/JScalendar...
-
bin
scripts should ship in bin
- Other components that must not be accessible through the web, such as:
- code only run once on installation,
- sources for statically-built modules e.g. Java,
- other code that must be shipped (e.g. to fulfil license conditions), should ship in a top level directory named
contrib
e.g.
$twikiroot/contrib/GNURegexp/src/gnu/regexp/RE.java --- used by several plugins
$twikiroot/contrib/twiki_dav/dav_twiki.c --- WebDAVPlugin apache module source
Note: CPAN modules should
not be repackaged as Contrib modules. It is much better to document the requirement to install them for the end-user to resolve in a way that is appropriate for their installation.
Checking dependencies
Because all contributed modules have an associated Perl module that includes the version number, the
TWiki::Func
function
checkDependencies
can be used to check dependencies at run-time. For example, your
initPlugin
might contain:
if( $TWiki::Plugins::VERSION >= 1.025 ) {
my @deps = (
{ package => 'TWiki::Contrib::JSCalendar', constraint => '>= 0.96' },
{ package => 'TWiki::Contrib::Attrs', constraint => '>= 1.00' },
);
my $err = TWiki::Func::checkDependencies( $pluginName, \@deps );
if( $err ) {
TWiki::Func::writeWarning( $err );
print STDERR $err; # print to webserver log file
return 0; # plugin initialisation failed
}
}
Note the use of a print to
STDERR
to output error messages. This will print a message to the Apache log file if the installation is using Apache. This is in addition to writing to
warning.txt
.
Tip: If you use
lazy loading to delay loading modules until they are actually required, you may also have to make the checkDependencies call lazy, as it internally calls
use
on each dependency.
Putting it all together
What goes in a module?
One key decision you will have to make is what to bundle together, and what to ship as separate modules. To make this decision, consider the following factors:
- The pieces of a module should fit together logically, and fit with the module name. Don't package a search routine in a module called "SortRoutines".
- If the different submodules of the common code have tight interdependencies, then ship them together.
- Each common code module has to be separately installed by the end user; more modules means more work for them.
- Don't be afraid to package very small pieces of reusable code separately if they don't fit perfectly within a bigger module, but are very reusable.
To SVN or not to SVN
You are
strongly recommended to use the twiki plugins SVN repository for managing your code. If you can't get to this repository, then you are
strongly recommended to use a local repoaitory.
Build support
A
BuildContrib module has been developed to help you ship shared code, plugins, skins and add-ons. This module is described in the
BuildContrib topic. The Build module supports testing using
Test::Unit
, automatic extraction of documentation using POD, manifest based packaging, and automatic upload mechanisms,
--
CrawfordCurrie - 08 Aug 2004