Archiv der Kategorie: Ruby On Rails

Projekt P: Benutzerverwaltung

Auf der Seite wird man sich als Benutzer registrieren können. Also brauche ich eine Benutzerverwaltung (Registrierung, Login, etc.).

Auch hier bin ich faul. Ich nutze einfach acts_as_authenticated.

acts_as_authenticated ist ein Generator der mir die grundlegenden Funktionen generiert. Wie schon bei der Newsletter wird es auch hier einen Registrierungscode geben, den man per Mail zugeschickt bekommt.

Das vorgegebene User-Datenmodell erweiter ich um einige Funktionen. Unter anderem kann man mehrere Adressen hinterlegen.
Also gibt es ein neues Modell namens „address“. Das Usermodell erweiter ich um den Befehl
has_many :addresses
Entsprechend kommt an das Addressenmodell:
belongs_to :user
Und schon habe ich eine saubere 1:N Beziehung definiert.
… Fast. Denn wenn ein User gelöscht wird, brauchen wir die Adressen natürlich nicht mehr. also
sieht es im Usermodell tatschlich so aus:
has_many :addressed :dependent => :destroy
Jetzt sieht es gut aus. Man könnte auch destroy_all schreiben, dann werden die Objekte sofort gelöscht. So wird vorher noch before_destroy und after_destroy für jedes Objekt aufgerufen. Es ist also langsamer. Aber die Benutzer sollen sich schließlich anmelden und nicht alle wieder löschen…

Jetzt kommt die Kür. Jeder User hat viele Adressen, aber eine Hauptadresse. Die Hauptadresse will ich einfacher finden und erweiter mein Usermodell so:
has_one :mainaddress, :class_name=>'Address', :conditions =>'addresses.primaereadresse=true'

Das Ergebnis: Auf die Hauptadresse kann ich nun in meinem Programm über User.mainaddress problemlos zugreifen. Schick, oder?

Zeitaufwand: ca. 2 Stunden. Allerdings habe ich das Design noch nicht optimiert. Aber das kommt später.

Projekt P Tag 2: Newsletter

Bei der Entwicklung der Seite werde ich die wichtigsten Funktionen zuerst entwickeln. Sollte irgendetwas gründlich schief laufen, sollen sich die Kunden zumindest schon mal als User registrieren können und eine Newsletter empfangen können.

User und Newsletter-Empfänger trenne ich dabei. Die Newsletter wird – klar – durch ein DoubleOptIn Verfahren abonniert werden. Das heißt, dass man sich auf der Seite anmeldet und anschließend einen Bestätigungslink zugeschickt bekommt. Ich brauche allerdings noch ein wenig mehr. Ich möchte neben der Mailadresse auch Name und Anschrift des Abonnenten haben (natürlich freiwillig).

Also sieht mein Prozess so aus:
1. Kunde trägt seine Mailadresse auf der Seite ein
2. Kunde bekommt eine Mail mti einem Bestätigungslink
3. Kunde ruft den Link auf und gibt anschließend weitere Daten ein
4. Datensatz wird aktiviert.

Ok, wir brauchen also zuerst eine Tabelle um die Daten abzulegen.
script/generate modell subscriber

Jetzt schnell die Attribute definiert:
CreateSubscribers < ActiveRecord::Migration def self.up create_table :subscribers do |t| t.column :name, :string t.column :vorname, :string t.column :strasse, :string t.column :plz:string t.column :ort:string t.column :mail, :string t.column :created_at, :date t.column :updated_at, :date t.column :activation_code, :string, :limit => 40
t.column :activated_at, :datetime
end
end

def self.down
drop_table :subscribers
end
end

Und die Tabelle anlegen:
rake db:migrate

created_at und updated_at sind besondere Fehler. Rails wir diese Felder selbstständig füllen.

Um sein Abo zu aktivieren, braucht der Kunde einen Aktivierungslink. Wir brauchen also einen Schlüssel, den wir in den Link einbauen. Das machen wir in der Klasse Subscriber so:
class Subscriber < ActiveRecord::Base
before_create :make_activation_code

def make_activation_code

self.activation_code = Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join )
end
(...)
end

So, jetzt kann ich den controller erzeugen und mir die notwendigen Methoden bauen. Diese sind:
– activate (hier frage ich den weiteren Kundendaten)
– activate_step2 (hier speichern wir die Daten)

Und schon bin ich fertig.

Ach, mist. Da habe ich doch glatt etwas vergessen. Ich möchte dem Abonnenten die Möglichkeit geben, sich durch einen einfachen Link wieder abzumelden. Dazu muss ich aber eine Schlüssel speichern aus dem ich den Link erzeuge (also wie der activation_code, den ich dem Kunden schicke. Unter Ruby on Rails ist das aber kein Ding:
script/generate migration subscriber_deactivationcode
erzeugt mir eine neue Migrationsdatei.

Die nur schnell ausfüllen:
class Subscribers3Deactivationcode < ActiveRecord::Migration def self.up add_column :subscribers, :deactivation_code, :string, :limit => 40
end
def self.down
remove_column :subscribers, :deactivation_code
end
end

Ok, eigentlich müsste die die vorhandenen Datensätze updaten. Aber ich habe ja nur Spieldaten, also was soll’s. Jetzt noch schnell make_activation_code erweitern:
self.activation_code = Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join )

Fertig!

Ich habe natürlich nicht alles beschrieben. Der Kunde bekommt natürlich einige Mails zugeschickt. Das ist aber nicht sonderlich spektakulär. Außerdem habe ich einige Tests geschrieben, um meine Entwicklung zu überprüfen.

Zeitaufwand für das Feature? 1/2 Tag.
Geschwindigkeitsvorteil gegenüber normaler (J2EE) Entwicklung: Gering, in Jave würde man es ähnlich machen. Allerdings musste ich nicht ständig meine Arbeit übersetzen und auf einen Testserver deployen, sondern konnte direkt aus der Entwicklungsumgebung (netbeans 6) „run file“ aufrufen und habe dadurch sehr schnell meine Entwicklung testen können.

Projekt P Tag 1: Design und HTML

Auch nach vielen Jahren bei verschiedenen Web-Agenturen haben sich meine zeichnerischen Fähigkeiten nicht wirklich verbessert. Ich hatte schon in der Schule eine 3- in Kunst. Bilde mir zwar ein, Design beurteilen zu können. Aber damit muss ich es sja noch lange nicht selber können.

Also gebe ich das Design an eine externe Agentur. Auf der erstellten Grafiken werden dann die HTML Template gebaut. Das könnte ich zwar eigentlich einigermaßen selber. Aber die Zeit drängt. Daher wird die initiale Erstellung durch einen externen HTML’er durchgeführt. Dies nutze ich dann zusammen mit masterview als Basis meiner Entwicklung.

Das führt uns zu der Frage: wann delegieren, wann selber machen? Viele Menschen haben Probleme, Aufgaben zu delegieren. Das hat einen einfachen Grund: Entweder man kann die Aufgabe selber nicht durchführen. In dem Fall kann man auch die Qualität der Arbeit schlecht bewerten. Man kann das Ergebnis zwar beurteilen. Aber vielleicht hätte man es noch besser oder schneller machen können. Leider kann man es nicht beurteilen.
Oder man ist bei dem Thema selber gut. In dem Fall geht viele Leute ihren Mitarbeitern auf die Nerven, weil diese die Aufgabe scheinbar nicht gut genug erfüllen. Wie auch, wenn der Chef ihnen das immer wieder weg nimmt …
Gerne hört man das Sprüche wie „Das kann sonst niemand richtig“, „Wenn ich es nicht selber mache, herrscht hier das Chaos“ usw. usw.

Es gibt auf der anderen Seite z.B. die Regel, dass Leiter von Entwicklungsteams ab einer Teamgröße von 7 Personen nicht mehr selber programmieren sollten. Auch das halte ich mittlerweile für zu einfach formuliert. Warum sollte man nicht selber in die Tasten greifen, solange das Team nicht auseinanderfällt?

In meinem aktuellen Fall gibt es zwei einfache Parameter: Zeit und Geld. Ich habe ein sehr geringes Budet (vor allem ist es mein eigenes Geld …) und ich habe feste Zeitvorgaben. Daraus will ich eine möglichst hohe Qualität holen – also ein Aufgabe aus dem Bereich der linearen Optimierung :-)

Allgemein würde ich folgende Regel aufstellen: Es ist einfacher, Aufgaben nachträglich wieder an sich zu ziehen als nachträglich abzugeben. Also sollte so viel wie möglich abgeben und sich auf die Qualitätssicherung konzentrieren.

Projekt P: Los geht’s

Es wird mal wieder Zeit für ein neues Projekt. Diesmal gibt es einige Besonderheiten. Normalerweile arbeite ich ja als freier Berater meistens als Projektleiter beim Kunden vor Ort. Diesmal erstelle ich für den Kunden die komplette Applikation (natürlich in Ruby on Rails) selber und arbeite im Home Office. Damit bin ich zum ersten Mal seit über 7 Jahren fast täglich zuhause.

Nach der Web-Lernbox ist das damit mein zweites RoR Projekt. Diesmal allerdings in groß …

Würde ich das Projekt klassisch kalkulieren (z.B. als J2EE Anwendung mit einem entsprechenden Team), käme ich auf Kosten von ca. 150.000 EUR Kosten. Mein Ziel ist es, das ganze für ca. 50.000 EUR zu schaffen (dabei setze ich meinen normalen Tagespreis an). Allerdings bekommen ich das Geld nicht, sondern werde mich später am Ergebnis beteiligen – aber das ist eine andere Geschichte und die soll ein anderes Mal erzählt werden.

In den nächsten Wochen wird es hier also in lockerer Folge meine Erfahrungen bei der Entwicklung einer größeren Anwendung mit Ruby On Rails geben.

Möge die Übung gelingen.

Ruby on Rails Entwicklungsumgebungen

Ich habe mir in letzter Zeit ein paar Entwicklungsumgebungen für RoR (Ruby On Rails) angeschaut. Hier mein subjektives Fazit:

1. Der reine Editor: JEdit
JEdit ist ein wenig der Emacs des 21. Jahrhunderts – der kann alles.
Das Ruby-Plugin kennt CodeCompletion, kann (einigermaßen gut) Klassen erkennen und die entsprechenden Mathoden anbieten und kennt natürlich Syntax-Highlightning.

2. RadRails / Aptana
RadRails basiert auf Eclipse . Zuerst die gute Nachricht: Wenn man InstantRails unter Windows einsetzt, kann man den Arbeitsbereich auf das Verzeichnis legen. Weitere Konfigurationen sind nicht notwendig. Das war aber auch schon alles, was mir gefällt. Die Code-Vervollständigung ist gefühlt schlechter als bei JEdit. Dafür kann man einigermaßen konfortabel Gems installieren. Aber alles in allem hat es mich nicht umgehauen.

3. 3rd Rail
Ich habe meine erstes Geld als Softwareentwickler mit Turbo-Pascal verdient (oder Clipper? Ach, egal). Der Anbieter, Borland, hatte immer einen guten Ruf. Als ich das erste Mal „Delphi 1.0“ sah, war ich schlicht und einfach überwältigt. So musste Softwareentwicklung aussehen (ok, die dunklen Seiten habe ich auch recht schnell kennen gelernt). Trotzdem noch heute hängt an meiner Pinnwand die Eintrittskarte zum „Borland European Software Festival“ – das muss so 1991 gewesen sein. Mittlerweile nennt sich die Sparte welche Entwicklungstools anbietet Codegear. Als ich hörte, dass Codegear ein RoR war ist sehr, sehr gespannt.
Tja, aber was soll man sagen 3rd Rails basiert wie RadRails auf Eclipse. Ich habe kein Feature gefunden, was mich auch nur ansatzweise umgehauen hätte. Also vergessen wir das schnell wieder.

3. Netbeans 6.0 (Beta)
Netbeans wurde von SUN zusammen mit Java auf den Markt geworfen. Es ist eine in Java geschriebene Java-Entwicklungsumgebung. Die ersten Versionen waren unglaublich schlecht. Langsam, buggy, seltsam zu bedienen. Alles in allem eher eine Warnung als eine Werbung für Java.
Netbeans 6 kann nun (neben Java, UML und anderen Dingen) auch Rails.

Und damit kommen wir auch zum absoluten Siegern: Netbeans 6 rockt.

Die Code-Vervollständigung ist super, analysiert den Code sauber und liefert die richtigen Methoden. Rails wird vorbildlich unterstützt. Ein Beispiel: Wie migriert man die Datenbank unter 3rdRail? Man muss auf die Console gehen, und dort per Hand „rake db:migrate“ eingeben. Wow, dafür brauche ich keine Entwicklungsumgebung.
Netbeans? Mit dem Menüpunkt „Migrate Database“. Anschließend werden einem die möglichen Versionen zur Auswahl angeboten. Neue Generatoren, neue Gems? Alles kein Problem.
Mal eben eine Funktion ausprobieren? Methode im Editor auswählen, rechte Maustaste „run File“ – fertig.
Und das Beste: Der Support von Subversion (Versionierungstool) ist super.

Rails & Dreamweaver & Masterview

So schön Sprachen wie RubyOnRails auch sind, es gibt einen kleinen Nachteil: Das Design der Oberfläche ist eher suboptimal. Eigentlich würde man ja gerne „mal eben“ mit einem WYSIWYG-Tool das Benutzerinterface malen, das klappt aber leider nicht.

In Rails definiert man ein Eingabefeld z.B. so :
< %= text_field("user","name") %>

Rails baut daraus anschließend die HTML-Sicht. Das ist zwar alles schon und praktisch, sieht nur in einem HTML-Editor doof aus. Jetz mag man zwar einwenden, dass echte Programmierer keine WYSIWYG-Editor benutzen, aber echte Programmierer würden eh nicht Rails oder HTML programmieren.

Die Lösung lautet: Masterview . Masterview ist quasi der Leim zwischen Dreamweaver (oder nvu oder was für ein HTML Editor auch immer) und Rails. Kurz ausgedrückt versteckt Masterview die Rails-Kommandos in XML Tags.

Unser obiges Beispiel sieht dann so aus:
input name=“name“ type=“text“ mv:text_field=“ ‚user‘,’name'“
Ergebnis: Das lässt sich auch ohne Rails in jedem Browser darstellen und in einem Editor bearbeiten.

Aber Masterview kann noch mehr.
Rails kennt ja die Möglichkeit, Teile einer Seite in Layouts zu lagern. Das ist extrem praktisch, da man wiederkehrende Elemente nur einmal definieren muss. Dumm nur, dass man in so einem Fall mindestens zwei Dateien hat, nämlich die Layout Datei und die „eigentliche“ Datei. Wenn man jetzt noch Teile seiner Seite mit Ajax dynamisch aktualisieren will, hat man eine dritte Datei (welche mit render :partial eingebunden wird).
Unter Masterview packt man alles in eine einzige HTML-Datei.
Mit den Befehlen mv:generate und mv:gen_partial kann man dort die Generierung der benötigten Dateien anstoßen.

Man kann Masterview problemlos ausprobieren. Da es (auf Wunsch) *.rhtml Dateien erzeugt, ist der Wechsel zurück zur „normalen“ Entwicklung kein Problem.

Status Web-Lernbox

Hier mal ein kurzer Zwischenstatus meiner Internet Lernkartei (http://www.web-lernbox.de):

Mittlerweile werden rund 10.000 Lernkarten damit verwaltet. Für eine Testversion, nicht schlecht. Was ich auch sehr nett finde: Einige Lehrer haben ihre Schüler offensichtlich dazu „verdonnert“, die Lernbox zu nutzen – sorry Leute :-)

Leider war ich mit meinem RubyOnRails-Hoster nicht 100% zufrieden. Daher ist der Auftritt auf einen Server umgezogen, den ich nun komplett selber verwalte. Nach einigen Kinderkrankheiten läuft der Auftritt auch stabil und schnell. Es hat allerdings ein wenig gedauert, wenn der Server so lief, wie ich wollte.

Leider sind durch diesen Umzug die anstehenden neuen Entwicklungen ein wenig aus dem Zeitplan geraten. Ich habe aber alle Anregungen der Nutzer notiert und werde mich in den nächsten Tagen an die nächsten Features machen.

Ruby On Rails: Ajax Paginate und Suche

Auf meiner Internet-Lernkartei namens Web-Lernbox kann man ja per Ajax nach Lernkarten suchen und in der Liste blättern (dies nennt man Pagination). Das Ergebnis sieht (zumindest in meinem Augen) recht ansprechend aus.

Hier die Details, wie man so einen Effekt recht einfach in Rails programmiert.
Web-Lernbox Suchmaske

Phase 1: Das Suchfenster
Wir brauchen ein Textfeld. Wenn nun der Benutzer eine Eingabe tätigt, soll eine Aktion auf dem Server ausgeführt werden. Klingt schwierig, ist es aber nicht. So sieht der entsprechende Teil in der View-Datei aus:

(...)
< %= text_field_tag :filter %>
< %= observe_field(:filter, :frequency => 0.2,
:update => "cardlist",
:url => {:action => :filter_elements, :id => @box}) %>

Der erste Befehl ist klar. text_field_tag definiert ein Inputfeld. Anschließend definieren wir ein Observer_field. Der erste Parameter definiert das Feld, welches beobachtet werden soll. :frequency=>0.2 sagt, dass alls 0,2 Sekunden auf eine Änderung geprüft werden soll. Gibt der Benutzer etwas sein, wird die Methode „filter_elements“ aufgerufen. Diese Methode bekommt den Paramenter id mit, damit wir wissen, welche Lernbox eigentlich gefiltert werden soll. Schließlich haben wir noch das Attribut :update=>“cardlist“. Das sagt, welcher Bereich eigentlich nach dem Aufruf neu beschrieben wird (wir wollen ja nicht die komplette Seite neu zum Client schicken, sondern nur das Ergebnis).

In dem View gibt es also einen Bereich „cardlist“ den habe ich in eine zweite Datei (_cardlist.rhtml) gepackt und rufe ihn im View über
< %= render( :partial =>'edit/cardlist' ) %>
auf.
Wichtig ist natürlich, dass in der Datei cardlist ein Bereich mit der id „cardlist“ definiert ist:
Datei _cardlist.rtml:
<div id="cardlist">
<table cellpadding="5" class="edit_list_table" width="100%">
(... Anzeige der Elemente... )
</table>
</div>

Das war bislang einfach, oder? Also weiter.

Phase 2: Die Ajax-Suche
In meinem Controller sieht die Suchmethode nun so aus:
def filter_elements
filter_name = request.raw_post
box_id = current_user.Boxes.find(params[:id]).id;
@cards_pages, @cards = paginate(:cards, :conditions => ['(frage like ? or antwort like ?) and box_id = ?',
filter_name, filter_name, box_id])
render :partial => 'cardlist'
end

Kleine Anmerkung: Ok ok, in Wirklichkeit sieht die Funktion bei der echten Lernbox ein wenig anders aus, aber es geht ja ums Prinzip. Es passiert hier also folgendes. Für Für jeden Filteraufruf wird der Datenbestand neu gefiltert und mit dem Befehl paginate in mehrere Seiten geteilt.

Phase 3: Blättern mit Ajax
Jetzt gibt es aber noch ein Problem. Die Paginate Funktion existiert schon in Rails und kann mit dem Befehl „pagination_links“ im View auf die entsprechenden Navigationselemente erzeugen. Dann würde aber die komplette Seite neu aufgerufen und nciht per Ajax nur der notwendige Bereich neu geschrieben. Also musste ich mir einen kleinen Helfer schreiben. In die _cardlist.rhtml kommt:
< %= draw_paginator(@cards_pages) %>

Jetzt muss man nur noch der Helper entsprechend erweitern:

def my_pagination_links(paginator,action)
if paginator then
pagination_links_each(paginator,{:link_to_current=>false}) do |n|
link_to_remote(n.to_s, { :url=>{:action=>action, :page =>n.to_s}, :update=>'cardlist' })
end
else
return ''
end
end

def draw_left_paginator(paginator,action)
if paginator.current.previous then
link_to_remote('Zurück',
:url=>{:action=>action, :page =>paginator.current.previous}, :update=>'kart_list')
else
return ''
end
end

def draw_right_paginator(paginator,action)
if paginator.current.next then
link_to_remote('Nächste Seite', :url=>{:action=>action,
:page =>paginator.current.next}, :update=>'kart_list')
else
return ''
end
end

def draw_paginator(paginator,action='list')
if paginator then
return draw_left_paginator(paginator,action) + my_pagination_links(paginator,action) + draw_right_paginator(paginator,action)
else
return ''
end
end

(Auch hier: der echte Code sieht anders aus)

Im Prinzip habe ich die ursprünglichen Pagination Sourcen genommen und verändert. Klar, man hätte auch einfach die entsprechende Methode überschreiben können. Aber so fand ich es sinniger.

Das war es auch schon. Der Aufwand für das Ajax-Frontend ist faktisch genauso hoch, wie bei einer normalen Seite, sieht aber viel hübscher aus.

Eigentlich …

Also eigentlich wollte ich jezt stolz über die neuesten Fortschritte der Web-Lernbox berichten.

  • Man kann nun Boxen so freigeben, dass Nutzern sie nicht bearbeiten dürfen.
  • Es gibt einen neuen Lernmode bei dem die Antwort bei der Eingabe schon geprüft wird
  • Viele Fehler wurden behoben
  • Bei Aufruf der Seite bekommt das Eingabefeld den Focus

Allerdings muss ich gerade zu meinem Schrecken feststellen, dass die komplette Seite nicht funktioniert, da der Zugriff auf das public Verzeichnis gesperrt ist.

Das ist schon das 2. Problem binne weniger Wochen.

Grummel, kennt jemand einen guten Hoster für Rails?

Update:
So, jetzt geht es wieder. Der Hoster macht viele Fehler, meldet sich aber immer schnell …
Der neue Lernmode ist bei den 1X1 Karten bereits eingerichtet. Probiert es mal aus.

Die nächsten Meilensteine:

  • Lernkarten drucken
  • Lehrermodus mit Auswertung des Lernfortschritts der Schüler
  • (…) Ich bin für Vorschläge immer offen

www.web-lernbox.de

Wie der ein oder andere Leser vermutet haben mag, habe ich die letzten Tage mit Ruby On Rails gespielt. Hier ist das erste Ergebnis, die

Web Lernbox:
http://www.web-lernbox.de

Dabei handelt es sich um die Online-Version einer Lernkartei.

Eine Lernkartei besteht aus (meistens) 5 Fächern und hilft dabei, z.B. Vokabeln zu lernen. Man nimmt eine Karteikarte und schreibt auf eine Seite die Frage, auf Rückseite die Antwort. Anschließend legt man die Karten in Fach 1 der Kartei. Wenn man nun lernt, fängt man vorne in der Kartei an und arbeitet sich durch: Kann man die Frage auf der Karte bearbeitet, wandert die Karte ein Fach weiter. Macht man einen Fehler, landet die Karte in Fach 1. Ist eine Karte irgendwann in Fach 5 angekommen (man hat also 5x hintereinander die Antwort gewusst), hat sich der Stoff im Langzeitgedächtnis verankert. 10-15 Minuten Training pro Tag reichen dabei völlig aus.

Meine Online Lernbox erweitert das Prinzip noch ein wenig. Karten werden nur einmal täglich erfragt. Außerdem werden Karten in den hinteren Fächern erst mit einigen Tagen Verzögerung abgefragt.

Angelegte Lernboxen können für andere Benutzer freigegeben werden.

Die aktuelle Version bietet noch nicht alle geplanten Funktionen, Erweiterungen sind aber schon in Planung. So wird es verschiedene Lernmodi sowie eine Druckfunktion geben. Darüber hinaus kann bestimmen, ob eine freigegebene Lernkartei durch andere Benutzer verändert werden kann.

Wer Lust hat, kann gerne einen Blick darauf werfen. Einige Lernboxen sind bereits freigeschaltet (u.a. Austragungsorte der Olympischen Sommerspiele sowie Deutsche Meister im Fußball – braucht man ja spätestens, wenn man sich bei Wer Wird Millionär anmeldet)

Ich würde mich über Feedback, Anregungen oder Fehlermeldungen sehr freuen.