Wie erstelle ich ein CPAN-konformes Modul ?
Diese Coding-Guidelines sind für die Programmierung von Perl-Modulen nach dem
CPAN-Standard gedacht.
Diese Module sollen objektorientiert und getestet sein.
Den ersten Schritt überlassen wir einem "externen" Programm - h2xs. Das dient
eigentlich der Einbindung von (C-)Header-Dateien in XS-Module, kann aber genauso
gut für die Erstellung eines Rumpfes für Module verwendet werden.
Ich benutze hier den "PerlCommunity"-Namensraum:
Modulrahmen erstellen:
~/EigeneModule 83> h2xs -b 5.6.1 -XA -n PerlCommunity::Testmodul
Writing PerlCommunity-Testmodul/lib/PerlCommunity/Testmodul.pm
Writing PerlCommunity-Testmodul/Makefile.PL
Writing PerlCommunity-Testmodul/README
Writing PerlCommunity-Testmodul/t/PerlCommunity-Testmodul.t
Writing PerlCommunity-Testmodul/Changes
Writing PerlCommunity-Testmodul/MANIFEST
Makefile.PL anpassen
PerlCommunity-Testmodul/Makefile.PL
Im Makefile.PL stehen verschiedene Angaben wie Abhängigkeiten und Infos zu Autor und
minimaler Perl-Version:
use 5.006001;
use ExtUtils::MakeMaker;
# See lib/ExtUtils/MakeMaker.pm for details of how to influence
# the contents of the Makefile that is written.
WriteMakefile(
NAME => 'PerlCommunity::Testmodul',
VERSION_FROM => 'lib/PerlCommunity/Testmodul.pm', # finds $VERSION
PREREQ_PM => {}, # e.g., Module::Name => 1.1
($] >= 5.005 ? ## Add these new keywords supported since 5.005
(ABSTRACT_FROM => 'lib/PerlCommunity/Testmodul.pm', # retrieve abstract from module
AUTHOR => 'Renee Baecker <reneeb@pharma.PerlCommunity.com>') : ()),
);
Abhängigkeiten (Modulname und minimale Modulversion) müssen bei PREREQ_PM eingetragen
werden, z.B.
PREREQ_PM => {Spreadsheet::SimpleExcel => 1.1},
Hier wird gesagt, dass unser neues Modul das Modul Spreadsheet::SimpleExcel braucht und
dass es eventuell noch installiert werden muss bevor
PerlCommunity::Tesmodul installiert werden
kann (passiert dann automatisch).
Gegebenenfalls müssen noch die Autorenangaben angepasst werden, ist bei einem rein
internen Gebrauch aber uninteressant.
Da das Makefile.PL ein Perl-Programm ist, können auch noch andere Sachen dort gemacht
werden, die als Vorbereitung zum Testen und Installieren dienen.
README
Diese README hat den gleichen Zweck wie in 99% aller Fälle in denen eine README existiert:
- Installationshinweise
- Allgemeine Hinweise
- evtl. ToDo?-List
Changes
In der Changes-Datei werden alle Veränderungen am Modul festgehalten. Diese Datei sollte
gut gepflegt werden, damit Veränderungen nachvollzogen werden können. Das kann die Fehlerfindung
beschleunigen ("in Version 1.0 hat's doch noch funktioniert").
Am Anfang sieht es noch so aus:
Revision history for Perl extension
PerlCommunity::Testmodul.
0.01 Mon Mar 27 09:30:57 2006
- original version; created by h2xs 1.23 with options
-b 5.6.1 -XA -n PerlCommunity::Testmodul
MANIFEST
Im MANIFEST sind alle Dateien festgehalten, die zu dem Modul-Paket gehören. Am Anfang sieht es so aus:
Changes
Makefile.PL
MANIFEST
README
t/PerlCommunity-Testmodul.t
lib/PerlCommunity/Testmodul.pm
Häufig kommt es vor, dass man intern noch weitere Module braucht, die von
PerlCommunity::Testmodul benutzt
werden. Z.B. ein
PerlCommunity::Testmodul::Untermodul. Das speichert man dann unter lib/PerlCommunity/Testmodul/Untermodul.pm.
Dieses Modul muss dann auch in die MANIFEST-Datei eingetragen werden:
Changes
Makefile.PL
MANIFEST
README
t/PerlCommunity-Testmodul.t
lib/PerlCommunity/Testmodul.pm
lib/PerlCommunity/Testmodul/Untermodul.pm
Auch neue Test-Skripte müssen hier eingetragen werden.
Testskripte
Tests sind übermäßig wichtig. Am Anfang sieht es aus wie Mehraufwand, aber wenn man nicht richtig
testet, geht am Ende viel Zeit durch Fehlersuche und Bugfixing verloren. Eventuell fällt der Fehler auch
erst nach einer gewissen Zeit im laufenden Betrieb auf und dann kostest es umso mehr Zeit und Nerven...
Auf die Testskripte gehe ich aber in einem
separaten Skript ein.
Das Modul
Endlich können wir uns dem Modul an sich widmen. h2xs schreibt schon einen Rumpf für das Modul - mit
einer Standard-POD. Dieses Modul muss man noch anpassen:
Standard-Pragmas
Als erstes wollen wir die "Standard"-Pragmas verwenden, die in keinem Skript und in keinem Modul
fehlen sollten:
- use strict;
- use warnings;
Zu use strict kann ich noch den
Online-Artikel hier im Wiki emfpehlen.
Exporter
Da wir das Modul Objektorientiert verwenden wollen, brauchen wir keinen Exporter. Damit fallen folgende
Zeilen aus dem Modul-Rumpf weg:
require Exporter;
our @ISA = qw(Exporter);
# Items to export into callers namespace by default. Note: do not export
# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.
# This allows declaration use PerlCommunity::Testmodul ':all';
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
# will save memory.
our %EXPORT_TAGS = ( 'all' => [ qw(
) ] );
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
our @EXPORT = qw(
);
Wer das Modul nicht Objektorientiert verwenden will und den Exporter benutzen möchte, sollte sich mal
perldoc Exporter anschauen.
Konstruktor
Im Gegensatz zu Java und anderen Sprachen ist kein Name für den Konstruktor vorgeschrieben, aber es ist
üblich auch in Perl den "Konstruktor" "new" zu nennen. Damit sieht dann später die Instanzierung der Klasse
so aus:
my $object = PerlCommunity::Testmodul->new();
Der Methode wird als erster Parameter der Name der Klasse (hier:
PerlCommunity::Testmodul) übergeben. Natürlich
kann man auch weitere Parameter übergeben.
Ein Objekt ist in Perl in der Regel eine "geblesste" (perldoc -f bless) Hashreferenz. Damit sieht ein
Standardkonstruktor so aus:
sub new{
my ($class) = @_;
my $self = {};
return bless $self,$class;
}
Methoden
Dann kann man seine eigenen Methoden schreiben. Da wir das Modul Objektorientiert verwenden wollen, muss
man berücksichtigen, dass beim Aufruf von
$object->methode('Welt');
das Objekt als erster Parameter übergeben wird.
Die Methode sieht dann so aus:
sub methode{
my ($self,$value) = @_; # $self ist das Objekt, $value ist hier "Welt"
print $value;
}
Die "berühmten" getter/setter-Methoden können einzeln oder auch in einer Methode zusammen geschrieben werden.
Nehmen wir an, wir wollen ein Attribut "Farbe" des Objekts erst setzen und danach auslesen:
Methoden getrennt:
Aufruf:
$object->set_farbe('blau');
print $object->get_farbe();
Implementierung im Modul:
sub set_farbe{
my ($self,$farbe) = @_;
$self->{FARBE} = $farbe if(defined $farbe);
}
sub get_farbe{
my ($self) = @_;
return $self->{FARBE};
}
Methoden in einer Methode:
Aufruf:
$object->farbe('blau');
print $object->farbe();
Implementierung im Modul:
sub farbe{
my ($self,$farbe) = @_;
$self->{FARBE} = $farbe if(defined $farbe);
return $self->{FARBE};
}
Abschluss
Ein Modul muss einen "wahren" Rückgabewert liefern. Üblich ist - und das wird von h2xs auch so gemacht -, eine
1 zurückzuliefern. Sonst kommt eine Meldung wie
PerlCommunity/Testmodul.pm did not return a true value
POD
Die Dokumentation im POD-Format ist innerhalb des Moduls gut aufgehoben. Man sollte darauf achten,
dass die Dokumentation möglichst vollständig ist. Es sollte die Verwendung (Synopsis) und die Methoden
nennen und möglichst die Rückgabewerte und Parameter sinnvoll benennen und beschreiben. Die
einzelnen POD-Anweisungen sind unter perldoc perlpod zu finden.
Test
Danach wird noch getestet, ob alles vorhanden ist und ob das Modul funktioniert. Man sollte dabei darauf achten,
dass die Testskripte aktuell sind und alle Features des Moduls abdecken...
Zum Testen startet man als erstes das Makefile.PL-Skript:
~/EigeneModule/PerlCommunity-Testmodul 104> perl Makefile.PL
Checking if your kit is complete...
Looks good
Writing Makefile for PerlCommunity::Testmodul
Hierbei wird das Makefile erstellt, das dann noch für make benutzt wird:
~/EigeneModule/PerlCommunity-Testmodul 105> make
cp lib/PerlCommunity/Testmodul.pm blib/lib/PerlCommunity/Testmodul.pm
Manifying blib/man3/PerlCommunity::Testmodul.3
Zum Abschluss dieses Schrittes werden noch die Testskripte aufgerufen. Damit wird überprüft, ob das Skript
überhaupt Fehlerfrei arbeitet:
~/EigeneModule/PerlCommunity-Testmodul 111> make test
/usr/bin/perl "-MExtUtils::Command::MM" "-e" "test_harness(0, 'blib/lib', 'blib/arch')" t/*.t
t/PerlCommunity-Testmodul....ok
All tests successful.
Files=1, Tests=2, 0 wallclock secs ( 0.06 cusr + 0.02 csys = 0.08 CPU)
So arbeitet das Modul fehlerfrei, aber häufig sieht das Ergebnis nicht ganz so gut aus (vor allem, wenn man
wirklich viele Testfälle schreibt). So sieht es aus, wenn Fehler auftauchen:
~/EigeneModule/PerlCommunity-Testmodul 114> make test
/usr/bin/perl "-MExtUtils::Command::MM" "-e" "test_harness(0, 'blib/lib', 'blib/arch')" t/*.t
t/PerlCommunity-Testmodul....
# Failed test in t/PerlCommunity-Testmodul.t at line 18.
# Looks like you failed 1 test of 2.
t/PerlCommunity-Testmodul....dubious
Test returned status 1 (wstat 256, 0x100)
DIED. FAILED test 2
Failed 1/2 tests, 50.00% okay
Failed Test Stat Wstat Total Fail Failed List of Failed
-------------------------------------------------------------------------------
t/PerlCommunity-Testmodul.t 1 256 2 1 50.00% 2
Failed 1/1 test scripts, 0.00% okay. 1/2 subtests failed, 50.00% okay.
make: *** [test_dynamic] Error 2
Hier wird auch gleich gesagt,
welche Tests fehlschlagen, so dass man die Fehlersuche eingrenzen kann.
Packen und los!
Nach dem Test kann man auch automatisch eine Distribution packen lassen, die fertig für den Upload in CPAN
oder für das Unternehmeneigene Repository ist:
~/EigeneModule/PerlCommunity-Testmodul 118> make tardist
rm -rf PerlCommunity-Testmodul-0.01
/tools/devel_tools/bin/perl "-MExtUtils::Manifest=manicopy,maniread" \
-e "manicopy(maniread(),'PerlCommunity-Testmodul-0.01', 'best');"
mkdir PerlCommunity-Testmodul-0.01
mkdir PerlCommunity-Testmodul-0.01/t
mkdir PerlCommunity-Testmodul-0.01/lib
mkdir PerlCommunity-Testmodul-0.01/lib/PerlCommunity
tar cvf PerlCommunity-Testmodul-0.01.tar PerlCommunity-Testmodul-0.01
a PerlCommunity-Testmodul-0.01/ 0K
a PerlCommunity-Testmodul-0.01/t/ 0K
a PerlCommunity-Testmodul-0.01/t/PerlCommunity-Testmodul.t 1K
a PerlCommunity-Testmodul-0.01/MANIFEST 1K
a PerlCommunity-Testmodul-0.01/META.yml 1K
a PerlCommunity-Testmodul-0.01/lib/ 0K
a PerlCommunity-Testmodul-0.01/lib/PerlCommunity/ 0K
a PerlCommunity-Testmodul-0.01/lib/PerlCommunity/Testmodul.pm 2K
a PerlCommunity-Testmodul-0.01/Changes 1K
a PerlCommunity-Testmodul-0.01/README 2K
a PerlCommunity-Testmodul-0.01/Makefile.PL 1K
rm -rf PerlCommunity-Testmodul-0.01
gzip --best PerlCommunity-Testmodul-0.01.tar
Siehe auch
Ergänzungen
Im Kopfteil des Perlmoduls wird eine Versionsnummer erstellt. Wenn man mit CVS oder RCS arbeitet, dann sollte man diese ändern, damit CVS bzw. RCS die Versionsnummer automatisch aktualisiert:
Die Zeile
our $VERSION = '0.01';
sollte man also ersetzen durch:
our $VERSION = do { my @r = (q$Revision: 1.9 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r };
Will man Konflickte mit einer gleichnamigen Variable $VERSION im Programm vermeiden, könnte man ggf. diese Zeile ergänzen:
$PerlCommunity-Testmodul::VERSION = do { my @r = (q$Revision: 1.9 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r };
--
ReneeBaecker - 26 Jun 2003 - Thema angelegt
--
WolfgangWiese - 22 Aug 2006 - Ergänzungshinweis zu CVS/RCS