Der Aktienwecker in Aktion
Noch ein weiterer Vorteil der lockeren Bindung durch Any Event: Das Modul »WatchQuotes.pm« lässt sich unabhängig von Pidgin testen, etwa mit dem Skript in Listing 2. Es erzeugt eine Instanz der Klasse »WatchQuotes« und ruft die Methode »init()« auf, mit der »WatchQuotes« die vom User definierte Konfigurationsdatei »~/.pidgin-stockwatch.yml« einliest und intern in eine Perl-Datenstruktur umformt (Abbildung 3).
|
Listing 2: |
|---|
01 #!/usr/bin/perl -w
02 use strict;
03 use lib local::lib;
04 use AnyEvent;
05 use WatchQuotes;
06
07 my $watcher = WatchQuotes->new();
08 $watcher->init();
09 $watcher->watch( &callback );
10
11 my $quit_program = AnyEvent->condvar;
12 $quit_program->recv;
13
14 ###########################################
15 sub callback {
16 ###########################################
17 print "$_[0]n";
18 }
|
Als Beispiel einer Eventloop bringt Any Event eine in Perl implementierte Schleife mit, die das Testskript benutzt. Listing 2 definiert mit »condvar()« eine Bedingungsvariable, welcher der Any-Event-Kernel Nachrichten schicken kann. Zeile 12 wartet mit »recv()« auf eine solche Nachricht - die aber nie kommt - und lässt derweil den Any-Event-Kernel Events von Modulen wie »WatchQuotes« bearbeiten.
Das aufgerufene Testskript holt also in regelmäßigen Zeitabständen Börsenkurse ein und ruft den ab Zeile 15 definierten Callback mit einer Tabelle formatierter Stockquotes auf, falls der Kurs einer der überwachten Aktien die in der Konfigurationsdatei festgelegten Grenzwerte überschreitet. Die Funktion »print()« in Zeile 17 gibt die Nachricht jeweils auf Stdout aus. Das Skript läuft weiter, bis der User es mit [Ctrl]+[C] abbricht.
Das Testskript hilft etwaige Fehler in Ruhe zu lokalisieren, bevor das Skript in die feindliche Pidgin-Umgebung gerät, wo das Debuggen schwer ist, ganz besonders wenn Pidgin das Plugin aus irgendwelchen Gründen nicht korrekt lädt (siehe Kasten "RTFM, falls existent"). Das Modul »WatchQuotes.pm« selbst definiert in Listing 3 zunächst Perl-typisch einen Konstruktor »new()«, der lediglich das Homedirectory des Aktienspezies herausfindet und im Objekthash einige Standardwerte wie den Namen der Konfigurationsdatei festlegt.
| RTFM, falls existent |
|---|
| Dokumentation über das Schreiben von Pidgin-Plugins ist rar. Zwar hat Pidgin-Chefentwicker Sean Egan ein Buch zum Thema "Building and Extending Gaim" [5] geschrieben, doch nicht nur der Name des Projekts hat sich seither geändert (Pidgin statt Gaim), sondern auch so ziemlich jeder Funktionsaufruf und jede Datenstruktur. Das an sich sehr gut geschriebene Werk taugt daher allenfalls zum Studium der Pidgin-Architektur. Auch die Aktualität der Online-Dokumentation ([6], [7]) lässt schwer zu wünschen übrig. Die automatisch generierte Doxygen-Dokumentation ist, wie so oft, lieblos zusammengeschustert, unvollständig und damit für die Katz. Die effektivste Methode, um in der aktuellen Pidgin-Version einen Parameter zu einem Funktionsaufruf zu finden, ist das Studium real existierenden Plugins neuerer Bauart, zum Beispiel [8]. |
|
Listing 3: |
|---|
001 ###########################################
002 package WatchQuotes;
003 # Mike Schilli, 2010 (m@perlmeister.com)
004 ###########################################
005 use strict;
006 use warnings;
007 use AnyEvent;
008 use AnyEvent::HTTP;
009 use YAML qw(LoadFile);
010
011 ###########################################
012 sub new {
013 ###########################################
014 my($class, %options) = @_;
015
016 my ($home) = glob "~";
017
018 my $self = {
019 watcher => undef,
020 data => {},
021 refdata => {},
022 conf_file =>
023 "$home/.pidgin-stockwatch.yml",
024 conf => {},
025 %options,
026 };
027
028 bless $self, $class;
029 }
030
031 ###########################################
032 sub init {
033 ###########################################
034 my($self) = @_;
035
036 my $yml = LoadFile( $self->{conf_file} );
037
038 for my $e ( @$yml ) {
039 if( ref $e eq "HASH") {
040 my($key, $val) = %$e;
041 $val =~ s/%//g;
042 $self->{conf}->{ $key } = $val;
043 } else {
044 # 2% by default
045 $self->{conf}->{ $e } = 2;
046 }
047 }
048 }
049
050 ###########################################
051 sub watch {
052 ###########################################
053 my($self, $cb) = @_;
054
055 $self->{watcher} = AnyEvent->timer (
056 after => 10,
057 interval => 300,
058 cb => sub {
059 $self->fetch( $cb );
060 },
061 );
062 }
063
064 ###########################################
065 sub fetch {
066 ###########################################
067 my($self, $cb) = @_;
068
069 my $url = "http://" .
070 "download.finance.yahoo.com/d/" .
071 "quotes.csvr?e=.csv" .
072 "&f=spl1p2&s=" .
073 join('+', sort keys %{ $self->{conf} });
074
075 http_get($url, sub {
076 $self->parse_csv( $_[0] );
077 $self->check( $cb );
078 });
079 }
080
081 ###########################################
082 sub parse_csv {
083 ###########################################
084 my($self, $csv) = @_;
085
086 for my $line ( split /n/, $csv ) {
087
088 my($symbol, $prev, $last, $change) =
089 map { s/[^w.-]//g; $_ }
090 split /,/, $line;
091
092 next unless defined $symbol;
093
094 $symbol = lc $symbol;
095
096 $self->{data}->{$symbol} =
097 [ $prev, $last, $change ];
098 }
099 }
100
101 ###########################################
102 sub check {
103 ###########################################
104 my($self, $cb) = @_;
105
106 if(!scalar keys %{ $self->{refdata} }) {
107 $self->{refdata} = {%{$self->{data}}};
108 }
109
110 for my $stock (keys %{ $self->{data}} ) {
111 if( $self->noteworthy( $stock ) ) {
112 $self->{refdata} = {
113 %{$self->{data}} };
114
115 # reset 'prev'
116 for my $s (
117 keys %{$self->{refdata}} ) {
118 $self->{refdata}->{$s}->[0] =
119 $self->{data}->{$s}->[1];
120 }
121
122 $cb->( $self->message );
123 last;
124 }
125 }
126 }
127
128 ###########################################
129 sub message {
130 ###########################################
131 my($self) = @_;
132
133 my $msg = "n";
134
135 for my $stock (keys %{ $self->{data} }) {
136 my($prev, $last, $change) =
137 @{ $self->{data}->{$stock} };
138 $msg .= "$stock: $last $change%n";
139 }
140
141 return $msg;
142 }
143
144 ###########################################
145 sub noteworthy {
146 ###########################################
147 my($self, $stock) = @_;
148
149 my $price_ref =
150 $self->{refdata}->{$stock}->[0];
151
152 my $price_now =
153 $self->{data}->{$stock}->[1];
154
155 my $change_percent = abs(
156 ($price_now - $price_ref))/
157 $price_ref*100;
158
159 return($change_percent >
160 $self->{conf}->{$stock});
161 }
162
163 1;
|
Yaml für Mensch und Maschine
Die ab Zeile 32 definierte Methode »init()« liest die Konfiguration mit der Funktion »LoadFile()« ein, die aus dem Yaml-Modul vom CPAN stammt. Das Yaml-Format hat den Vorteil, dass es sowohl für Menschen wie Maschinen gleichermaßen leicht lesbar ist (Abbildung 2).
Die Applikation erlaubt sowohl einfache Array-Einträge der Art »- amzn« genauso wie kleine Hashes nach dem Muster »- goog: 2%«, die Yaml als Referenz auf einen Hash unter dem Array-Eintrag speichert (»"goog" => "2%"«). Zeile 39 prüft, ob Perls »ref«-Funktion das Wort »HASH« zurückliefert, was auf eine Hashreferenz hindeutet.
Kommt aber der Leerstring zurück, handelt es sich um einen einfachen Skalar und Zeile 45 setzt einen Standard-Schwellenwert von 2 Prozent an. Die überwachten Aktienkürzel speichert das Modul unter dem Eintrag »conf« im Objekthash und ordnet ihnen die konfigurierten prozentualen Triggerwerte zu.
Die Methode »watch« ab Zeile 51 erzeugt einen periodischen Timer. Der Parameter »after => 10« bestimmt, dass der Timer den unter »cb« definierten Callback genau 10 Sekunden nach dem Start aufruft. Die Verzögerung ist gewollt und nötig, falls das Plugin schon startet, bevor der User im Instant-Messaging-Netzwerk ansprechbar ist. Ohne diese Denkpause würden frühe Nachrichten in die ewigen Jagdgründe gehen statt zum Stockholder. Der Parameter »interval« gibt mit dem Wert 300 an, dass der Timer den Callback nach dem ersten Aufruf alle 5 Minuten erneut anspringt, um die neuesten Börsenkurse vom Yahoo-Server einzuholen und die Werte auf Ausreißer hin zu untersuchen.
Diesen Artikel als PDF kaufen
Express-Kauf als PDF
Umfang: 6 Heftseiten
Preis € 0,99
(inkl. 19% MwSt.)
Als digitales Abo
Weitere Produkte im Medialinx Shop »
Versandartikel
Onlineartikel
Alle Rezensionen aus dem Linux-Magazin
- Buecher/07 Bücher über 3-D-Programmierung sowie die Sprache Dart
- Buecher/06 Bücher über Map-Reduce und über die Sprache Erlang
- Buecher/05 Bücher über Scala und über Suchmaschinen-Optimierung
- Buecher/04 Bücher über Metasploit sowie über Erlang/OTP
- Buecher/03 Bücher über die LPI-Level-2-Zertifizierung
- Buecher/02 Bücher über Node.js und über nebenläufige Programmierung
- Buecher/01 Bücher über Linux-HA sowie über PHP-Webprogrammierung
- Buecher/12 Bücher über HTML-5-Apps sowie Computer Vision mit Python
- Buecher/11 Bücher über Statistik sowie über C++-Metaprogrammierung
- Buecher/10 Bücher zu PHP-Webbots sowie zur Emacs-Programmierung
Insecurity Bulletin
Im Insecurity Bulletin widmet sich Mark Vogelsberger aktuellen Sicherheitslücken sowie Hintergründen und Security-Grundlagen. mehr...





