Schandfleck
vom 19. November 2009Ok, jetzt ist es offiziell: Meine Wohnung ist ein Schandfleck der Gesellschaft und muss schleunigst entfernt werden. Bürger von Heilbronn werden zur Mithilfe aufgefordert.
4 Kommentare,
del.icio.us,
Ok, jetzt ist es offiziell: Meine Wohnung ist ein Schandfleck der Gesellschaft und muss schleunigst entfernt werden. Bürger von Heilbronn werden zur Mithilfe aufgefordert.
), bin ich natürlich immer wieder auf der Suche nach Vorbildern.
Deshalb dachte ich mir, wieso nicht mal den Quellcode von TeX (einem Textsatzsystem von Herrn Ph. D. Knuth persönlich) anschauen. Und tatsächlich: Im ersten Moment dachte ich, der Code sei nur die Dokumentation zu TeX. Er liest sich wie eine Definition oder ein Informatikbuch. Hier mal ein kleiner Auszug:
@ Now comes the harder part: At this point in the program, |cur_val| is a
nonnegative integer and $f/2^{16}$ is a nonnegative fraction less than 1;
we want to multiply the sum of these two quantities by the appropriate
factor, based on the specified units, in order to produce a |scaled|
result, and we want to do the calculation with fixed point arithmetic that
does not overflow.
@<Scan units and set |cur_val| to $x\cdot(|cur_val|+f/2^{16})$...@>=
if inf then @<Scan for \(f)\.{fil} units; |goto attach_fraction| if found@>;
@<Scan for \(u)units that are internal dimensions;
|goto attach_sign| with |cur_val| set if found@>;
if mu then @<Scan for \(m)\.{mu} units and |goto attach_fraction|@>;
if scan_keyword("true") then @<Adjust \(f)for the magnification ratio@>;
@.true@>
if scan_keyword("pt") then goto attach_fraction; {the easy case}
@.pt@>
@<Scan for \(a)all other units and adjust |cur_val| and |f| accordingly;
|goto done| in the case of scaled points@>;
attach_fraction: if cur_val>=@'40000 then arith_error:=true
else cur_val:=cur_val*unity+f;
done:
Abgesehen von der gewöhnungsbedürftigen Syntax ist das fast schon lyrik, was er da an den Tag legt. Leider sind nicht alle Programmiersprachen so biegsam, aber Ruby geht hier schon sehr in die richtige Richtung. Also hier ein Aufruf an alle: Doku + Code = Awesome! :)
6 Kommentare,
del.icio.us,
[Donald E. Knuth][1] ist *die* lebende Legende im Softwareumfeld. Er widmet schon über Jahrzehnte hinweg sein Leben der Forschung und der formalen Beschreibung von Algorithmen. Sein \"still in progress\" Band *Art of Computer Programming* ist wahrlich ei
Load Balancing für Ruby
vom 28. July 2009Web-Anwendungen werden immer anspruchsvoller und komplexer. Sobald der erste Server in die Knie geht, muss man sich Gedanken über Performance und Lastverteilung machen. Gerade bei Anwendungen, die mit Ruby oder Python geschrieben wurden, stößt man häufig schneller an die Grenzen als mit anderen Skriptsprachen wie PHP oder Perl.
Für Ruby gibt es mehrere Ansätze, wie man die Last auf mehrere Server verteilen kann. Ich möchte hier nun eine der vielen Möglichkeit vorstellen. Für dieses Beispiel verwende ich Lighttpd als Webserver, Thin als Dispatcher, Ramaze als Web-Framework und MySQL zur Datenhaltung.

Fangen wir von vorn an: Wenn ein Request eintritt, wird zuerst entschieden, ob das anzufragende Objekt erst noch generiert werden muss. Wird ein Bild, eine CSS-Datei oder eine andere statische Datei angefragt, kann diese direkt zurückgeliefert werden. Anders sieht es bei Webseiten aus, die zuerst durch Ruby gerendert werden müssen. Hier kommt die Lastverteilung mit ins Spiel. Wir konfigurieren Lighttpd so, dass er die Last auf zwei andere Webserver umleitet. Hier die nötige Konfiguration:
$HTTP["host"] =~ "example\.com" {
server.document-root = "/var/www/"
$HTTP["url"] !~ "^/(css|images|js|libs)" {
proxy.balance = "fair"
proxy.server = ( "/" => (
("host" => "192.168.0.50", "port" => 7000),
("host" => "192.168.0.51", "port" => 7000)
))
}
}
Thin selbst muss natürlich auf den beiden Hosts 192.168.0.50 und 192.168.0.51 gestartet und konfiguriert werden. Das nimmt uns Ramaze ab, in dem wir in der app.rb (der "Startdatei") folgendes hinterlegen:
Ramaze.start( :adapter => :thin, :port => 7000, :file => __FILE__ )
Mit dieser Konfiguration starten wir die Anwendung auf beiden Hosts. Thin kann auch von Hand gestartet werden, der Befehl dazu ist, wer hätte es anders erwartet: "thin". Wenn die Performance in die Knie geht, kann man so einfach eine neue Thin-Instanz auf einem separaten Server starten und einbinden.
Natürlich lässt sich jetzt noch die Datenbank auf mehrere Server verteilen. Hier sollte man allerdings darauf achten, dass der Server mit dem Anwendungscode und der Datenbankserver physikalisch im selben LAN bzw. im selben Rechenzentrum untergebracht sind, denn sonst ist zwar die Last verteilt, aber die Performance geht in den Keller, da mehr Zeit zum Übertragen der Datensätze verbraten wird als das eigentliche berechnen.
1 Kommentar,
del.icio.us,
Web-Anwendungen werden immer anspruchsvoller und komplexer. Sobald der erste Server in die Knie geht, muss man sich Gedanken über Performance und Lastverteilung machen. Gerade bei Anwendungen, die mit Ruby oder Python geschrieben wurden, stößt man häu
Active Browsing, Teil 2
vom 23. July 2009Im letzten Beitrag habe ich über Active Browsing geschrieben und der Mächtigkeit, die jeder User im Grunde hat, diese aber nicht nutzt. Wir wollen uns nun das Leben mit ein paar Zeilen Code vereinfachen.
Installation der Komponenten
Um ruby-libnotify und mechanize nutzen zu können, müssen sie zuerst installiert werden. Bei Mechanize ist das kein Problem, da es schon als rubygem vorliegt. Ein simples gem install mechanize installiert das Paket, inklusive nokogiri für das Parsen der Webseiten. Bei ruby-libnotify muss man selbst Hand anlegen und mit dem Dreischritt ruby extconf.rb && make && make install das Paket zusammenbauen und installieren. Wer das Paket libopenssl-ruby noch nicht installiert hat, sollte das zuvor erledigen.
Login und Parsing
Der Erste Schritt ist nun, sich auf der Webseite anzumelden und die Titel aller Jobaufträge aus der HTML Datei herauszusuchen. Wir binden zuerst die Pakete mechanize und nokogiri ein, um damit arbeiten zu können.
require 'mechanize' require 'nokogiri'
Als nächstes kümmern wir uns um den Login. Hier kommt uns mechanize sehr entgegen, denn es bietet ein simples Interface zur HTML-Seite. Machen wir zuerst Gebrauch von den Methoden get und submit:
class TextBroker
def initialize(mail, pass)
@agent = WWW::Mechanize.new
@mail, @pass = mail, pass
login
end
private
def login
page = @agent.get('http://www.textbroker.de/')
loginForm = page.form('login-autoren')
loginForm.author_mail = @mail
loginForm.pass = @pass
@agent.submit(loginForm, loginForm.buttons.first)
end
end
Unter @agent ist die mechanize-Instanz abgelegt, die wir im Konstruktor initialisieren. Die private Methode login läd zuerst die Startseite mit get herunter und greift sich das Login Formular der Autoren anhand der ID. Die Attribute author_mail und pass sind Formularfelder, die sich so befüllen und auslesen lassen. mit submit wird das Login-Formular abgeschickt. In der mechanize Instanz wird nun der Cookie gesetzt, um dauerhaft eingeloggt zu sein. Ab jetzt können wir GET/POST-Anfragen innerhalb des passwortgeschützten Bereichs starten ohne sich erneut einzuloggen.
Sammeln wir nun die Jobs ein, die auf der Seite verfügbar sind. Ich habe die Methode fetch genannt, und in die TextBroker Klasse integriert. (In Ruby sind alle Klassen offen und können so erweitert werden, hier wird also keine neue Klasse angelegt, sondern die bestehende erweitert)
class TextBroker
def fetch
page = @agent.get('http://www.textbroker.de/a/search.php')
links = page.links.select {|l| !l.href.nil? and l.href.include?("search.php?hdl_id=") }
links.map {|l| l.text }
end
end
Wir stellen einen neuen GET-Request, um uns die HTML-Seite mit den Jobangeboten zu besorgen. über das Attribut links erhalten wir ein Array von allen Links, die auf der Seite verfügbar sind. Daraus selektieren wir uns alle, die im href Attribut den String "search.php?hdl_id=" enthalten haben. Da wir nur am Linktext interessiert sind, erstellen wir mit map ein Array der Linktexte und geben diese zurück.
Damit sind wir schon mit dem ersten Teil fertig und können uns eine Liste der Aufträge holen.
wwwAccess = TextBroker.new('mail@aaron-mueller.de', 'xxx')
pp wwwAccess.fetch #=> ["...", "...", ...]
Im nächsten Schritt werden wir diese Information auswerten und grafisch aufbereiten, so dass der User nur das sieht, was er wirklich sehen will: Die neu hinzugekommenen Jobaufträge.
Darstellung
Da ich unter GNOME arbeite, habe ich mich für das GTK Binding entschieden. Zusammen mit ruby-libnotify ist das Zusammenstecken einer GUI ganz einfach. Zuerst einmal binden wir wieder die benötigten Bibliotheken ein:
require 'gtk2' require 'RNotify'
Da wir unser Programm in der Notification Area positionieren wollen, brauchen wir grundlegend nur ein Gtk::StatusIcon und die Notification selbst.
statusIcon = Gtk::StatusIcon.new
statusIcon.stock = Gtk::Stock::DND_MULTIPLE
statusIcon.tooltip = "Textbroker.de Benachrichtigung"
notify = Notify::Notification.new("Header", "Content", nil, statusIcon)
notify.timeout = 5000
Um nun in regelmäßigen Abständen nach neuen Aufträgen zu suchen, und um diesen Vorgang starten und stoppen zu können, verbinden wir das Signal "activate" (was für einen Linksklick steht) mit einem Stück Code:
wwwAccess = TextBroker.new('mail@aaron-mueller.de', 'xxx')
fetchThread = nil
statusIcon.signal_connect("activate") {|widget, event|
if fetchThread.instance_of? Thread
fetchThread.terminate
fetchThread = nil
statusIcon.stock = Gtk::Stock::DND_MULTIPLE
else
statusIcon.stock = Gtk::Stock::JUMP_TO
fetchThread = Thread.new do
oldJobs = []
loop do
newJobs = (jobs = wwwAccess.fetch) - oldJobs
oldJobs = jobs
if !newJobs.empty?
content = "\n\n"
newJobs.each {|job| content += job + "\n" }
notify.update("Neue Schreibaufträge vorhanden", content, nil)
notify.show
end
sleep 60
end
end
end
}
Dies bedarf einer Erklärung: Jedes mal, wenn das Icon gedrückt wird, soll es entweder den fetchThread starten und in regelmäßigen Abständen nach neuen Jobs suchen, oder den aktuellen Thread beenden. Um dies zu visualisieren, ändern wir das Icon bei jeden Klick. Wird der Thread gestartet, läuft er in eine Endlosschleife, die alle 60 Sekunden unsere fetch Methode aus der TextBroker Klasse aufruft und dann mit dem letzten Stand überprüft. Gibt es Veränderungen (ist also das newJobs Array nicht leer), rufen wir die Methode update vom Notification Objekt auf und zeigen diese an.
Damit man das Programm auch anständig beenden kann, geben wir dem Icon noch ein Kontextmenü mit einem Beenden Eintrag.
menu = Gtk::Menu.new
menuQuit = Gtk::ImageMenuItem.new(Gtk::Stock::QUIT)
menuQuit.signal_connect("activate") {|widget, event| Gtk.main_quit }
menu.append(menuQuit)
statusIcon.signal_connect("popup-menu") {|widget, button, time|
if button == 3
menu.show_all
menu.popup(nil, nil, button, time)
end
}
Am Ende müssen wir nur noch die Notification initialisieren und in den Event-Loop von GTK hineinspringen. Danach übernimmt GTK die Ablaufkontrolle.
Notify.init("textbroker-notification")
Gtk.main
Zusammengezählt sind das nun ca. 80 Zeilen Code, nicht viel für eine solche Aufgabe. Dieses Vorgehen lässt sich auf beliebige Resourcen anwenden und erleichtert den Alltag ungemein. Allerdings kann man nicht oft genug darauf hinweisen, dass man hier Funktionalität erstellt, die vom Autor der Webseite (oder einer anderen Resource) so nicht vorgesehen wurden, entweder aus Faulheit oder aber aus gutem Grund.
Habt ihr Ideen, wo man eine solche Technik einsetzen kann? Oder habt ihr selbst schon das ein oder andere Tool geschrieben? Soll ich öfters solche "Beispielprojekte" vorstellen? Lasst es mich in den Kommentaren wissen. :)
2 Kommentare,
del.icio.us,
Im [letzten Beitrag][1] habe ich über Active Browsing geschrieben und der Mächtigkeit, die jeder User im Grunde hat, diese aber nicht nutzt. Wir wollen uns nun das Leben mit ein paar Zeilen Code vereinfachen.
## Installation der Komponenten
Um [ruby
Active Browsing, Teil 1
vom 23. July 2009Wer bei meinem Vortrag mit dem Titel "Fix the web with Greasemonkey" dabei war, weiß was gemeint ist. Für alle anderen ein kleiner Abriss: Das Web liefert uns aufbereitete Daten in einer Form, die der Ersteller festlegt. Oft ist es aber so, dass man nur an einer bestimmten Information interessiert ist und alles andere am Liebsten ausblenden würde oder auf eine andere Art und Weise dargestellt bekommen möchte. Mechanismen, die dazu beitragen, die Informationen so darzustellen, wie es einem selbst am besten gefällt, nennt man Active Browsing. Mittlerweile gibt es einige Tools, um sich das Surfen etwas zu personalisieren und nach den eigenen Wünschen zu formen.
Mit dem schon erwähnten Greasemonkey - einem Firefox-Addon - kann man durch einfache Javascript-Dateien die Darstellung umgestalten, in dem man in den DOM-Baum eingreift. Ein anderes Beispiel ist Readability, welches durch ein Bookmarklet nur den eigentlichen Content einer Seite anzeigt.
Diese Tools vereinfachen das Surfen ungemein, aber es gibt oft genug Situationen, wo man regelmäßig eine bestimmte Information auf einer Webseite abfragen will, bspw. den Flottenstatus bei einem Onlinespiel oder ob jemand neue Artikel bei eBay eingestellt hat. Für gewöhnlich geht man hier alle paar Stunden auf die Seite, loggt sich ein, hangelt sich durch ein paar Menüs und sucht die Information, die meistens nur aus einer Zahl oder einer Liste bestehen, heraus. Wie praktisch wäre es doch, wenn solche Seiten einen RSS-Feed oder eine andere API anbieten würden, damit man immer zur richtigen Zeit mit den entsprechenden Informationen versorgt werden würde. Dies ist allerdings in den seltensten Fällen so. Eine bessere Möglichkeit muss also her!
Hinweis: Da ich nach der Springerlink Aktion (eingeweihte wissen Bescheid) etwas vorsichtig geworden bin was "Web-Automatismus" angeht, soll das folgende Beispiel nur das Konzept verdeutlichen und dient NICHT zur eins zu eins Nachahmung. Deshalb stelle ich auch nicht den kompletten Code online, sondern nur die einzelnen Stücke separat. Zudem wird das Verfahren oft missbräuchlich verwendet. Grundsätzlich ist jeder selbst für sein Handeln verantwortlich und sollte mit gesundem Menschenverstand und Rücksicht auf den Webseitenbetreiber vorgehen, denn dieser stellt schließlich den Service bereit und trägt den Traffic.
Um mit dem Nachfolgenden etwas anfangen zu können, muss man kein Ruby können und auch kein Linux verwenden, es soll nur das Konzept beschrieben werden. Ähnliche Tools und Bibliotheken gibt es in fast jeder Sprache.
So, nun also zum Thema! Weiter oben habe ich die Problematik mit den immer wiederkehrenden Schritten, um an eine bestimmte Information zu kommen, schon angesprochen. Diesen Umstand wollen wir vereinfachen. Als Beispiel nehme ich das Portal textbroker.com, auf dem Autoren Texte für Geld schreiben können und Kunden Anfragen für Texte stellen können. Leider gibt es mehr Autoren als Kunden, so sind die meisten Aufträge schon nach ein paar Minuten nach der Einstellung vergeben. Sprich, man muss entweder Glück haben und im richtigen Moment die Stellenbeschreibung anschauen oder eben alle paar Minuten das folgende Szenario abspulen:
Auf die Startseite gehen und sich anmelden. Zum Glück speichert Firefox nach dem ersten Mal ein Cookie und dieser Schritt muss nur einmal am Tag gemacht werden. Dann auf "Aufträge Zeigen" und anschließend auf den Button "Suche starten" klicken. Nun bekommt man alle verfügbaren Aufträge angezeigt. Da man meist nur an bestimmten Aufträgen interessiert ist, und die Liste alle verfügbaren Aufträge anzeigt, muss man diese nach Änderungen durchsuchen und kann sich dann entscheiden, ob man den Auftrag annehmen will oder nicht.
Diesen Vorgang wollen wir automatisieren, aber nicht im Browser, sondern mit einem externen Programm. Die Idee ist, dass sich das Programm selbständig in regelmäßigen Abständen auf der Webseite einloggt, die Liste der verfügbaren Aufträge holt und diese dem User über die Notification Area der Schnellstartleiste (unter Windows wird das meines Wissens SysTray genannt), als Notification (Sprechblase) anzeigt. Das Ergebniss soll so aussehen:

Um dies ohne händisches Parsen von XML Dateien oder umständliche wget/curl Aufrufe zu lösen, gibt es im Ruby Umfeld einige interessante Projekte. Eines davon nennt sich mechanize, das die Interaktion mit Webseiten um einiges vereinfacht. So wird dem Programmierer beispielsweise das Cookie-Handling komplett abgenommen und auch das Parsen der HTML-Seiten ist erschreckend simpel. Ein weiteres tolles Projekt ist ruby-libnotify, welches ein Wrapper für libnotify implementiert, um Betriebssystem Notifications darzustellen.
Mit diesen beiden Helfern wollen wir ein Programm zusammennageln, das uns immer die neusten Jobangebote anzeigt. Wie dies funktioniert, erfahrt ihr im nächsten Blogeintrag :)
Kommentar schreiben,
del.icio.us,
Wer bei meinem Vortrag mit dem Titel \"[Fix the web with Greasemonkey][1]\" dabei war, weiß was gemeint ist. Für alle anderen ein kleiner Abriss: Das Web liefert uns aufbereitete Daten in einer Form, die der Ersteller festlegt. Oft ist es aber so, dass
Mit GIT und Ramaze zum Ziel
vom 11. June 2009Wer mich kennt, weiß dass Ruby seit ein paar Jahren meine Lieblingsprogrammiersprache ist. Da ich zur Zeit durch das Praxissemester bei IBM täglich mit PHP und dem Zend-Framework arbeiten muss, tut es richtig gut, privat mit Ruby zu programmieren. Im Webbereich ist RubyOnRails ja mittlerweile zu einem quasi-Industriestandard geworden. Mir ist das Framework aber zu groß, zu aufgebläht, zu schwerfällig und komplex. Deshalb hab ich mich - mal wieder - auf die Suche nach einem für mich besseren Web-Framework gemacht und bin auf Ramaze gestoßen. Ideal für mein aktuelles Projekt (von dem ich bald berichten werde).
Ramaze ist wirklich nur ein Framework zur Entwicklung, nichts anderes. Es steht jedem Entwickler frei, welchen Server, welche DB-Schnittstelle und welche Template Engine man verwenden möchte. Der SourceCode ist super dokumentiert und durchgetestet. Aber das Beste an diesem Framework ist die Community und die Entwickler selbst. Ich hatte zu Beginn ein paar Anlaufschwierigkeiten, die ich im IRC innerhalb von ein paar Minuten gelöst bekam.
Beim Programmieren habe ich einen kleinen Bug gefunden, der eigentlich schnell behoben war. Auf der Seite fand ich leider kein Bug-Tracker o.ä., also beschloss ich mit der Anleitung ein Patchfile zu generieren. Nachdem ich den Patch an manveru im IRC geschickt hatte, war er 10 Minuten später im trunk eingeflochten. So macht das Spaß!
Bisher bin ich mit Subversion als Versionsverwaltung immer gut gefahren, auch wenn viele nicht damit zurechtkommen oder darüber fluchen. Doch GIT wird mir von Tag zu Tag sympathischer.
Kommentar schreiben,
del.icio.us,
Wer mich kennt, weiß dass Ruby seit ein paar Jahren meine Lieblingsprogrammiersprache ist. Da ich zur Zeit durch das Praxissemester bei IBM täglich mit PHP und dem [Zend-Framework][1] arbeiten muss, tut es richtig gut, privat mit Ruby zu programmieren.
Geocaching
vom 11. June 2009Allein in Baden-Württemberg sind über 14 Tausend Schätze versteckt, von denen die meisten gar nichts wissen, und die, die von den Schätze wissen, geben alles daran, dass sie nicht beim Finden erwischt werden. Ich hab schon des Öfteren von Geocaching gehört, aber mich nie richtig damit beschäftigt, da ich kein tragbares GPS-Gerät habe. Heute haben Bea und ich uns auf den Weg gemacht und gleich mal den ersten Cache direkt vor meiner Wohnung gehoben. Zur Navigation haben wir einen BlackBerry Bold 9000 mit der Black Star Software verwendet.
Weil wir beim ersten schnell fündig wurden, haben wir uns natürlich danach was komplexeres herausgesucht. Der dreiteilige Multi-Cache SIDK war schon ziemlich herausfordernd für den Anfang. Beim ersten GPS-Punkt angekommen, fanden wir nach 5 Minuten suchen weitere GPS-Koordinaten, die uns mitten in die Stadt führten. Als wir dann da ganz unauffällig nach dem nächsten Hinweis suchten, fragte uns plötzlich einer "Habt ihr auch euren Schlüssel verloren?" und grinste :) Es stellte sich dann heraus, dass er auch auf der Suche war. Gemeinsam haben wir es dann gefunden.
Den dritten Teil mit Nacht-Cache Finale haben wir nicht hinbekommen, aber das wird noch!
4 Kommentare,
del.icio.us,
Allein in Baden-Württemberg sind über [14 Tausend Schätze][0] versteckt, von denen die meisten gar nichts wissen, und die, die von den Schätze wissen, geben alles daran, dass sie nicht beim Finden erwischt werden. Ich hab schon des Öfteren von [Geoca
Syntax Dokumentation
vom 15. May 2009Nach langer Abstinenz wieder mal ein kleiner Beitrag zum Thema Dokumentation. Neulich schaute ich mir die Syntax zu SQLite an und war begeistert! Eine solch hervorragende Dokumentation habe ich schon lange nicht mehr gesehen.
Normalerweise sehen Syntax-Definitionen so aus:

Seit ich programmiere, wird die Syntax so oder in abgeänderter Form dargestellt. Alles was in eckigen Klammern ist ist optional. Das Pipe-Symbol stellt ein "oder" dar. Die türkisen Hervorhebungen stellen Unterausdrücke dar, die nochmal separat beachtet werden müssen.
In der SQLite Dokumentation wurde der selbe Sachverhalt in einem "Flussdiagramm" bzw. einem Automaten dargestellt:

Welch angenehme Überraschung! Die Syntax lässt sich um einiges leichter lesen und verstehen. Wieso wird diese Darstellungsweise nicht immer verwendet? Weil es früher in ASCII unmöglich war? Oder weil es zu umständlich ist? Ne. Weil es nicht parsbar ist? Kann nicht sein, dafür gibts die BNF. Wenn mir jemand einen triftigen Grund nennen kann, bitte ich dies in den Kommentaren zu tun :)
Auch toll gemacht ist in diesem Zusammenhang ist strfriend.com. Hier werden reguläre Ausdrücke durch eine ähnliche Darstellung visualisiert. Leider unterstützt dieses Tool nur einen Bruchteil vom PCRE Quasi-Standard und man muss sich auf einfache Ausdrücke beschränken.
Und wenn wir schon beim Graphen malen sind: Wer "mal schnell" ein UML-Diagramm zeichnen möchte, aber dafür kein Zeichenwerkzeug bemühen will, kann sich mal yuml.de anschauen. Damit lassen sich über die URL Diagramme generieren!
5 Kommentare,
del.icio.us,
Nach langer Abstinenz wieder mal ein kleiner Beitrag zum Thema Dokumentation. Neulich schaute ich mir die [Syntax zu SQLite][1] an und war begeistert! Eine solch hervorragende Dokumentation habe ich schon lange nicht mehr gesehen.
[Normalerweise][9] se
Auflösung des zweiten Programmierwettbewerbs
vom 8. March 2009Zuerst einmal möchte ich mich in aller Form dafür entschuldigen, dass ich so lange nichts von mir hören lassen habe und - noch schlimmer - den Programmierwettbewerb noch nicht aufgelöst habe. Seit knapp zwei Monaten liegen nun schon die Abgaben auf meinem Desktop und ich habe mir geschworen, erst die Auswertung zu machen, bevor ich irgend etwas anderes schreibe. Ich tat mich so schwer dabei, weil die Einsendungen mich so überrascht haben und so vielseitig waren, dass ich mir lange Zeit Gedanken gemacht habe, wie ich diese tollen Arbeiten entsprechend würdigen und bewerten könnte.
Das Problem war, dass die Einsendungen so grundlegend verschieden waren, dass man sie fast nicht miteinander vergleichen konnte. In allen abgegebenen Projekten steckt zudem so viel Arbeit, Mühe und Detailverliebtheit, dass ich es kaum geschafft habe, einen zum Sieger zu erklären und die anderen leer ausgehen zu lassen. Aus diesem Grunde erhalten alle Teilnehmer für ihre tolle Abgabe ein Buch und der Sieger ein zusätzliches Dankeschön.

Ganz herzlich bedanken möchte ich mich natürlich bei allen, die mitgemacht haben. Das wären Andreas Maier, Florian Eitel, Ruben Müller und Uwe Klawitter. Ich hoffe, ihr habt jetzt nicht die Lust verloren, weil es so lange gedauert hat. Ich verspreche hiermit feierlich, dass die nächste Auswertung eines kommenden Programmierwettbewerbs schneller geht.
So, nun zu den Einsendungen: Ich habe versucht, die relevanten Punkte herauszuschreiben und den Code und die gesamte Anwendung nach den vier zuvor angegebenen Gesichtspunkten Features, Codedesign, Nutzung der Sprachfeatures und Usability zu bewerten. Wenn ich bei einem Punkt komplett falsch liege, würde ich mich sehr über einen Kommentar an der Seite freuen.
Ruben Müller
Sprache: ActionScript
Features: 1/5
Codedesign: 3/5
Nutzung der Sprachfeatures: 3/5
Usability: 4/5
(Download)
Features
Dadurch, dass der Chat-Client in ActionScript und Flash programmiert ist, läuft er auf jedem Rechner, der Flash installiert hat.
Positiv
Exzessive Nutzung vom Delegate-Pattern, um beim Datenempfang und beim Verbinden vom Socket auf die entsprachenden Methoden zu delegieren. Hier wurde strikt zwischen Darstellung und Logik getrennt.
Farbliche Markierung von Systemnachrichten, Benutzernamen und normalen Nachrichten.
Der Name: "Macc: Most awesome chat client" :-)
Negativ
Wenig Dokumentation
Das Einfärben der Nachrichten funktioniert in manchen Fällen nicht richtig, wenn im Benutzernamen ein "*" vorkommt oder wenn die geschickte Nachricht in einem "falschen" Zeichensatz Nachrichten schickt.
Umlaute werden nur unter OSX dargestellt. Unter Linux gibt es Darstellungsprobleme. Unter Windows hab ich es nicht getestet.
Das Senden und Empfangen von Nachrichten findet in unterschiedlichen Klassen statt, hier hätte man die beiden Funktionalitäten unter einen Hut bringen können.
Geilste Stelle im Code
if (success) {
Debug.trace( "macc.as :: Connection succeeded, yay for the cheeseburgers! :D", 3 );
}
Andreas Maier
Sprache: F#, WPS(XAML), C#
Features: 5/5
Codedesign: 5/5
Nutzung der Sprachfeatures: 5/5
Usability: 4/5
(Download)
Features
GUI und Kommunikationslogik sind hier komplett voneinander getrennt. Und nicht nur das, sie wurden auch in unterschiedlichen Programmiersprachen realisiert. Die GUI ist ein WPF-Anwendung mit C# und die restliche Logik wurde in F# geschrieben. Eine sehr interessante Kombination. Auch hier wurde die Trennung von GUI und Logik strikt eingehalten; Interfaces und Factories sorgten hier für die lose Kopplung.
Die GUI bietet alles, was man aus dem Server rausholen hätte können. Ein Chatfenster zeigt den Chat an, Nachrichten vom Server sind speziell markiert. Rechts befindet sich eine Userliste, die immer die aktuell im Chat befindlichen User anzeigt. Den Nicknamen kann man auch bequem über die GUI ändern.
Positiv
Verwendung meherer Sprachen in einem "Projekt". Die Wahl der Sprache F# war äußerst mutig und hat sicher viel Zeit in Anspruch genommen. Dies hat sich aber ausgezahlt, der Code ist sehr schlank und auch wenn man F# nicht komplett verstanden hat, versteht man, was passiert.
Eine Schicke GUI mit tollen Extrafeatures
Es lag noch eine Dokumentation bei, die den Code und das Vorgehen beschreibt. Das hat mir sehr geholfen zu verstehen, wie was zusammenwirkt.
Negativ
Ich habe alles Mögliche versucht, das Programm zum Laufen zu bringen, doch ich bin kläglich gescheitert. Selbst im Windows-Pool konnte ich das Programm nicht compillieren bzw. ausführen. Vermutlich fehlten mir einfach die Kenntnisse zu Visual Studio und Windows. Da in der Aufgabenbeschreibung stand, dass das Programm Plattformunabhängig sein soll, muss ich hier bei der Usability einen Punkt abziehen.
Geilste Stelle im Code
fun dataEvArgs ->
let dat = dataEvArgs.Data;
if x.IsGetUsersMessage(dat) then
(x.usersAvailableFunc.Invoke(dat.Split([|'\n'|])
// The pipeline operator rocks :-)!!
|> Seq.filter (fun element -> element <> null
&& element.Trim().Length >= 2)
|> Seq.map (fun element ->
(let mutable nickName = element.Trim().Substring(1, element.Trim().Length-1).Trim()
while nickName.StartsWith("*") do
nickName <- nickName.Substring(1, nickName.Length-1).Trim()
new User(nickName)))
|> List.of_seq))
else
())
Florian Eitel
Sprache: Ruby, Bash
Features: 3/5
Codedesign: 5/5
Nutzung der Sprachfeatures: 5/5
Usability: 3/5
(Download)
Features
Florian ging einen komplett anderen Weg. Er reduzierte sein Programm auf das mindeste Minimum, in dem er Features des Linux-Systems nutzte. Inspiriert durch suckless.org kommuniziert der Client mit Named Pipes. Das Chat-Programm kontrolliert nur die Named Pipes "in" und "out", die dann mit anderen Programmen oder einem simplen "echo" und "tail" beschrieben und ausgelesen werden können. Über Hooks kann dann auf Events reagiert werden.
Um die Kommunikation mit dem Server zu vereinfachen, lag ein kleines Shell-Skript bei, das alle Eingaben in die "in"-Pipe schreibt. Für die farbige Ausgabe wurde das Ruby gem "term/ansicolor" verwendet. Zudem konnte das System mit dmenu (aus dem dwm Windowmanager) betrieben werden.
Positiv
Die Einfachheit des Programms hat mich begeistert. Dadurch, dass simple Named Pipes verwendet wurden, wurde hier ebenfalls die Logik von der Darstellung getrennt.
Eine README beschrieb das komplette System und deren Aufbau, was sehr hilfreich war.
Negativ
Es gab keinerlei Möglichkeit, sich eine Benutzerliste anzuzeigen, was allerdings etwas umständlich gewesen wäre, dies auch mit Named Pipes zu lösen.
Die Benutzung des Programms erfordert einiges an Linux-Kenntnis, um es "zum Laufen zu bringen".
Geilste Stelle im Code
def def_hook name, *aliases
# Workaround da define_method vor ruby 1.9 keine Blocks als Parameter unterstützt
eval_methods name, "
def #{name}(&block)
( @#{name} ||= Array.new ) << block if block_given?
return @#{name}
end", aliases
end
Uwe Klawitter
Sprache: Java
Features: 1/5
Codedesign: 4/5
Nutzung der Sprachfeatures: 5/5
Usability: 4/5
(Download)
Features
Uwe ging den "traditionellen" Weg über Java. Trotz Java hat er es geschafft mit relativ wenig und übersichtlichem Code die Kernaufgaben zu erfüllen. Selbst der Code zur Swing-GUI ist anschaulich und übersichtlich. Im Code selbst wurden die Features von Java voll ausgeschöpft; eine Queue-Objekt sammelt alle Nachrichten zur weiterverarbeitung und der komplette Code kommt ohne Telnet-Wrapper o.ä. aus. Alles in allem macht der Code einen sehr stabilen Eindruck.
Positiv
"java -jar AChat.jar" und läuft :)
Übersichtlicher Code trotz Java.
Negativ
Leider biete die GUI nicht all zu viel Möglichkeiten. Hier hätte man noch einiges rausholen können. Beispielsweise eine Userliste oder einen Button zum Ändern des Benutzernamens. Deshalb hier auch ein Punkt Abzug bei Usability.
Geilste Stelle im Code
package cowabunga;
Aaron Müller
Sprache: Ruby, (XML)
(Download)
Ich habe natürlich auch eine "Referenzimplementierung" in Ruby gemacht :-) Verwendet habe ich dazu libgnome2 und glade für die GUI.
Der Server
Der Server selbst - gegen den ihr alle programmiert habt - ist in Ruby geschrieben und nutzt den "gserver" (GenericServer) von Ruby. Wer ihn sich mal anschauen möchte, kann ihn hier herunterladen.
Bewertung
Wie schon anfangs geschrieben, ist es mir sehr schwer gefallen, einen Gewinner zu ermitteln, weil ich alle Einsendungen toll fand und sich die einzelnen Einsendungen sehr schwer direkt vergleichen lassen. Ich habe mich dazu entschlossen, euch zu entscheiden, wer den Hauptpreis bekommen soll. Gebt einfach am linken Rand eure Stimme ab (am Besten noch mit einer kurzen Begründung). Natürlich bekommen alle, die mitgemacht haben ein Buch von mir. Schickt mir einfach eure Adresse per E-Mail.
Noch einmal vielen Dank fürs Mitmachen! Ich hoffe, es gibt wieder solch tolle Einsendungen bei kommenden Programmierwettbewerben. Mir hat es sehr viel Spaß gemacht, ich hoffe euch auch.
2 Kommentare,
del.icio.us,
Zuerst einmal möchte ich mich in aller Form dafür entschuldigen, dass ich so lange nichts von mir hören lassen habe und - noch schlimmer - den Programmierwettbewerb noch nicht aufgelöst habe. Seit knapp zwei Monaten liegen nun schon die Abgaben auf me
Alte Schinken
vom 2. January 2009
Neulich laß ich einen ziemlich alten Schinken. Ein Science Fiction Buch vom Jahre 1948 mit dem Titel Weltraumkadetten, das ich irgend wann mal auf einem Bücherflohmarkt für 50 Cent gekauft hatte.
Es spielt im Jahre 2075, die Menschen besiedeln schon lange nicht mehr nur die Erde. Nach einem verherenden Krieg wurde eine Raumpatrouille gebildet, die für Frieden im Universum sorgen soll. Ein paar Jungs wollen dieser Organisation beitreten und lernen alles über die Patrouille. Gegen Ende (auf den letzten 100 Seiten) wird es dann endlich spannend, und die Jungs stranden auf einem Planeten, der von eigenartigen Kreaturen bewohnt wird.
Alles in allem ein recht interessantes Buch, an dem noch Spuren des zweiten Weltkriegs zu spüren sind. Amüsant wird es an den Stellen, an denen der Autor technische Geräte beschreibt, die es in unserem Alltag schon längst gibt. Beispielsweise ein "Mobiles Telefon", das in geschlossenen Räumen nicht funktioniert, oder "Bänder" als das Speichermedium für Raumdaten und Aufzeichnungen.
Eine Stelle hat mich irgend wie beschäftigt :-)
Sie aßen; anschließend lehnte Matt sich zurück, rülpste nachdenklich und sagte: "Wißt ihr, ich könnte zwar etwas Orangensaft und Kaffee gebrauchen, aber übel ist das Zeug nicht."
Mir war bis zu diesem Zeitpunkt nicht klar, dass man beim Aufstoßen nachdenklich wirken kann.
Wem altdeutsche Begriffe wie "unken" oder "Stube" (für Schlafkabiene auf einer Raumstation) nichts ausmacht und ab Seite 230 anfängt zu lesen, der findet sicher Gefallen an dem Buch.
Kommentar schreiben,
del.icio.us,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18




