Sprungziele
Um Vim dazu zu veranlassen, auch in Perl etwa zur Source-Datei von »LWP::UserAgent« zu springen, falls der Cursor irgendwo auf »LWP::UserAgent« steht, muss der Anwender zwei Dinge erledigen. Zuerst muss Vim verstehen, dass in Perl ein Schlüsselwort auch Doppelpunkte enthalten kann, das bewerkstelligt »:set iskeyword+= :«. Zweitens muss Vim noch eine Tags-Datei einlesen, die alle installierten Packages indiziert hat, zum Beispiel über »:set tags=/home/mschilli/.ptags.txt«. Dann springt die Kombination [Ctrl]+[]] in den Modul-Source, wenn der Cursor auf einem Modulnamen steht.
Alternativ kann man den Modulnamen auch im Kommando »:tag LWP::UserAgent« angeben. Einmal in der Bibliotheksdatei angelangt genügt ein [Ctrl]+[Shift]+[T], um wieder zurück zum Ausgangspunkt zu navigieren. Die ganze Magie steckt dabei in der Tags-Datei ».ptags.txt«, die das weiter unten vorgestellte Listing »ppitags« erzeugt.
Trotz Windowmanager ist es manchmal sinnvoll, zwei Dateien gleichzeitig in einem Fenster zu sehen. Welcher Vim-User greift schon freiwillig auf die Maus zurück, wenn die Hände auf der Tastatur bleiben können? Wer statt [Ctrl]+[]] die Kombination [Ctrl]+[W]+[]] tippt, während der Cursor auf einem Schlüsselwort steht, dessen Fenster teilt sich in zwei Hälften: In der unteren bleibt der Code der editierten Datei sichtbar, in der oberen erscheint der Code des referenzierten Moduls.
Zwischen den Fensterhälften springt man mit [Ctrl] und zweimal [W] hin und her. Das Kommando »:quit« im oberen Fenster schließt dieses. Alternativ schließt der Befehl »:only« im unteren Fenster das obere. Die Vim-Session in Abbildung 6 zeigt unten ein editiertes Testskript, das das Modul »LWP::UserAgent« verwendet, und oben die »new()«-Methode im Sourcecode des Moduls.

|
Abbildung 6: Vim mit gespaltenem Fenster: Unten ein Testskript, oben der Sourcecode des verwendeten Moduls »LWP::UserAgent«.
|
Wer den Namen eines gesuchten Moduls nicht genau weiß, gibt einen regulären Ausdruck an. Das Kommando »tselect« sucht nach allen passenden Tags und bietet eine Liste zur Auswahl: »:tselect /^LWP«. Der Benutzer muss dann nur den passenden Treffer per Nummernmenü auswählen.
Mach mir Tags
Für ein aktuelles »~/.ptags.txt«-File muss ein Skript regelmäßig alle Module der lokalen Perl-Installation durchforsten. Das Skript »ppitags« in Listing 1 klappert alle »@INC«-Pfade ab, merkt sich in »@dirs«, wo es bereits war, und durchläuft auch bei überlappenden Pfaden nicht zweimal denselben.
01 #!/usr/bin/perl -w
02
03 use strict;
04
05 use PPI::Document;
06 use File::Find;
07 use Sysadm::Install qw(:all);
08 use Log::Log4perl qw(:easy);
09
10 my $outfile = "$ENV{HOME}/.ptags.txt";
11 my %dirs = ();
12 my @found = ();
13
14 find &file_wanted, grep {$_ ne "."} @INC;
15
16 blurt join("n", sort @found), $outfile;
17
18 ###########################################
19 sub file_wanted {
20 ###########################################
21 my $abs = $File::Find::name;
22
23 # Avoid dupe dirs
24 $File::Find::prune = 1 if -d and
25 $dirs{$abs}++;
26
27 # Only Perl modules
28 return unless /.pm$/;
29
30 my $d = PPI::Document->load($abs);
31
32 unless($d) {
33 WARN "Cannot load $abs ($! $@)";
34 return;
35 }
36 # Find packages and
37 # all named subroutines
38 $d->find(&document_wanted);
39 }
40
41 ###########################################
42 sub document_wanted {
43 ###########################################
44 our $package;
45 my $tag;
46
47 if(ref($_[1]) eq
48 'PPI::Statement::Package') {
49 $tag = $_[1]->child(2)->content();
50 $package = $tag;
51
52 } elsif(ref($_[1]) eq
53 'PPI::Statement::Sub' and
54 $_[1]->name()) {
55 $tag = "$package::" .
56 $_[1]->name();
57 }
58
59 return 1 unless defined $tag;
60
61 push @found, $tag . "t" .
62 $File::Find::name . "t" .
63 regex_from_node($_[1]);
64
65 return 1;
66 }
67
68 ###########################################
69 sub regex_from_node {
70 ###########################################
71 my($node) = @_;
72
73 my $regex = $node->content();
74
75 $regex =~ s/n.*//gs;
76
77 while(my $prev =
78 $node->previous_sibling()) {
79 last if $prev =~ /n/;
80 $regex = $prev->content() .
81 $regex;
82 $node = $prev;
83 }
84
85 $regex =~ s#[/.*[]^$]#\$&#g;
86 return "/^$regex/";
87 }
|
Um Perl-Source zu analysieren, braucht man eigentlich Perl, denn Perl ist extrem schwierig zu parsen. Allerdings hat Adam Kennedy vor kurzem das Unmögliche gewagt und einen "Good enough"-Parser für Perl geschrieben, der wirklich erstaunlich gut arbeitet. Das »PPI«-Modul vom CPAN enthält »PPI::Document«, dessen Methode »load()« ein Perl-Modul einliest, in Tokens zerlegt und in einer Baumstruktur ablegt.
Das Skript »ppitags« nutzt »File::Find«, um alle Verzeichnisse in Perls globalem Array »@INC« zu durchlaufen. Für jeden gefundenen Eintrag springt »File::Find« die Funktion »file_wanted« an. Falls der gefundene Eintrag ein Verzeichnis und keine Datei ist, frischt Zeile 25 den Hash »%dirs« auf, um festzustellen, ob das Skript den Pfad schon besucht hat. Falls ja, setzt Zeile 24 die Variable »$File::Find::prune« auf »1«, um »File::Find« mitzuteilen, dass es sich den Rest des Verzeichnisses und alle Unterverzeichnisse sparen kann. Zeile 28 weist alles außer Perl-Modulen mit der Endung ».pm« zurück. Zeile 30 parst das gefundene Perl-Modul. Falls dabei Fehler auftreten, fängt Zeile 32 sie ab, gibt eine Warnung aus und lässt das problembehaftete Modul sausen.
Konnte das Modul erfolgreich eingelesen werden, ruft Zeile 38 die »find()«-Methode des »PPI::Document«-Objekts auf, das die Tokens der Perl-Source durchläuft und für jeden gefundenen die ab Zeile 42 definierte Funktion »document_wanted« aufruft. Diese prüft, ob es sich bei dem gefundenen Token um ein Objekt vom Typ »PPI::Statement::Package« oder »PPI::Statement::Sub« handelt, also eine »package«- oder »sub«-Definition im Perl-Code.
Eine »package«-Definition besteht aus einer Zeile wie »package LWP::UserAgent;«, das sind in der PPI-Welt vier Tokens: »package«, Leerzeichen, der Modulname und der abschließende Strichpunkt. Für die Belange von »ppitags« ist nur der Modulname interessant, also das dritte Kind des Knotens, der in »$_[1]« übergeben wurde. Die Methode »child()« mit dem ab 0 gezählten Kinder-Index fördert den String »"LWP::UserAgent"« zutage: »$_[1]->child(2)«.
| Whitepaper |
|
Daten Migration - Eine Publikation von Bloor Research
Datenmigrationsprojekte überschreiten häufig das Budget, neigen zu Verzögerung und werden unter Umständen komplett abgebrochen. Bloor Research ist eines der weltweit führenden IT-Forschungs-, Analyse- und Beratungsunternehmen und wird in dem vorliegenden White Paper die wichtigsten Aspekte dieser Problematik näher beleuchten. Ferner werden praktische Empfehlungen für erfolgreiche Migrationsprojekte gegeben, die Sie auf Ihr nächstes Projekt übertragen können.
Download PDF (Registrierung erforderlich)
|
|
Open Source Datenintegration in der Praxis: Fallstudien und Anwendungsbeispiele
Über die letzten Jahre hinweg haben sich Open Source Lösungen als fester Bestandteil des gesamten Datenintegrationsmarktes etabliert. Viele Unternehmen haben bereits das Open Source Modell für Ihre Datenintegrationsprojekte aufgegriffen. Das vorliegende White Paper illustriert anhand ausgewählter Fallstudien und Anwendungsbeispiele die Implementierung von Open Source Datenintegration in der Praxis und benennt die daraus resultierenden Vorteile.
Download PDF (Registrierung erforderlich)
|
Dieser Online-Artikel kann Links enthalten, die auf nicht mehr vorhandene Seiten verweisen. Wir ändern solche "broken links"
nur in wenigen Ausnahmefällen. Der Online-Artikel soll möglichst unverändert der gedrucken Fassung entsprechen.
|