Sonntag, 28. August 2011

Rails 3.1 für Ubuntu 8.04 LTS (Hardy) - RVM bringt's

Unsere produktiven Server laufen zurzeit alle unter Ubuntu 8.04 LTS (Hardy). Leider gibt es für Ubuntu 8.04 LTS aber kein Ruby 1.8.7, was jedoch für Rails 3 Voraussetzung ist.

Eine Möglichkeit, Ruby in Version 1.8.7 auf Hardy zu installieren ist im Wiki von brightbox beschrieben.

Das hat soweit funktioniert, nur leider bin ich anschließend bei der Installation der diversen gems wahnsinnig geworden. Das fängt schon damit an, dass für Rails 2.3.x-Anwendungen RubyGems maximal in Version 1.6 verwendet werden darf.

Bei meinen Recherchen bin ich immer wieder über den Ruby Version Manager RVM gestolpert (https://rvm.beginrescueend.com/), also hab' ich den mal ausprobiert.

Nach der Installation (RVM wird per Script aus dem git-Repository geholt) installiert man die gewünschte Ruby-Version mit

rvm install 1.8.7

Anschließend gibt man an, welche Ruby-Version man haben will (hier gleich mit Option, dass dies die Standardeinstellung ist):

rvm --default use 1.8.7

Anschließend die gewünschten Gems installiert, also z. B.

gem install rails
gem install passenger
...


und in der Apache-Konfiguration für den entsprechenden Vhost noch dem Passenger mitgeteilt, welches Ruby er verwenden soll:

PassengerRuby /srv/www/rails/passenger/.rvm/bin/passenger_ruby

Nun steht dem Deployment meiner Rails3-Apps (fast) nichts mehr im Wege.....

Donnerstag, 21. Juli 2011

Bei Rails: *kein* require

Kleiner, aber nerviger Fallstrick:

In normalem Ruby schreibt man am Anfang einer Source-Datei
require 'my_class'
um Ruby-Code aus anderen Quellcode-Dateien einzubinden.

In einer Rails-Applikation ist diese Deklaration nicht nötig, um andere Klassen der Applikation zu finden, denn Rails benutzt für das Auffinden der Applikationsklassen einen eigenen Loader.  Dadurch kann z.B. eine Controller-Klasse alle Models der Applikation ansprechen, ohne dass dafür requires gebraucht werden.

Soweit ist die Sache Rails-Entwicklern wohlbekannt. Was vielleicht nicht so bekannt ist: Eine unnötige "requires"-Anweisung zwischen den Klassen einer Rails-Applikation schadet sogar, weil sie das automatische Neuladen der Klassen im Development-Modus verhindert.

Rails löst die Abhängigkeiten zu anderen Quellcode-Dateien automatisch auf, wenn eine Klasse von Ruby nicht gefunden wird. Dabei überprüft es auch, ob die Klasse neu geladen werden muss (z.B. für den Development-Modus.)

Wenn man die Source-Datei dagegen von Hand per require einbindet, wird sie von Ruby statisch geladen, und Rails bekommt nicht mehr die Kontrolle in die Hand. Damit muss man Rails  bzw. den genutzten Webserver, nach jeder Source-Code-Änderung dieser Datei neu starten.

Montag, 21. Februar 2011

Praktische Datums-Helferlein

Spätestens seit dem weitgehend ausgefallenen Jahr-2000-Problem und dem dann überraschend eingetretenen Jahr-2010-Problem weiß es jeder: Das Hantieren mit Datum-Werten in Software ist widerlich und extrem fehleranfällig(*). Der tiefere Grund liegt im Datums- und Zeit-Format: Das ist aus Software-Entwickler-Sicht geradezu schwachsinnig un-orthogonal und schrecklich. Die Einheiten (Tage, Wochen, Monate, Jahre) hängen nicht wohl-definiert und dezimal zusammen, wie das praktisch wäre. Für Monate (28,29,30 oder 31 Tage) und Jahre (365 und 366 Tage, und wann nochmal gleich?) gibt es noch nicht mal feste Bezugspunkte. Haufenweise Gelegenheiten also, Fehler zu machen. Nun werden wir am grundsätzlichen Problem so schnell nichts geändert kriegen. Der Mensch als Erdenbewohner hat es nunmal gern wenn sich seine Zeitmaße an den unrunden Zeiträumen seiner natürlichen Umgebung ausrichten (Tages- und Jahreszeiten). Daher bleibt uns nur, auf Software zu hoffen, die uns bei dieser undankbaren Aufgabe hilft.

Daher ist es recht praktisch, dass uns Rails mit einer extrem wohl sortierten Sammlung von Helferlein bei diesem unangehmen Thema unter die Arme greift. Das tut es wie üblich per Erweiterung (Monkey-Patching) der Core-Classen Date, DateTime und Time (der Code ist in der Klasse ActiveSupport::CoreExtensions::DateTime::Calculations). Am besten eignet sich die Rails Console, um sich einen Überblick zu verschaffen. Ein paar Beispiele für schwierige Dinge, die damit einfach gehen:


andi@xwing:~/lc-search-ana$ ./script/console
Loading development environment (Rails 2.3.10)

# Heute:
>> Date.today
=> Mon, 21 Feb 2011

# Gut das ist noch nicht so spannend. Anfang dieser Woche:
>> Date.today.end_of_week
=> Sun, 27 Feb 2011

# Montag in 4 Wochen:
>> Date.today.beginning_of_week.advance(:weeks => 4)
=> Mon, 21 Mar 2011

# Ende des Monats Januar
>> Date.today.beginning_of_year.end_of_month
=> Mon, 31 Jan 2011

# Ein Monat später als 31. Januar ist *28*. Februar
>> Date.today.beginning_of_year.end_of_month.advance(:months => 1)
=> Mon, 28 Feb 2011



Besonders gut gefällt mir dabei, wie leserlich die Operationen hier aneinander reihen lassen. Wer die Java-Calendar-Classen kennt, weiß das diese ähnliche Operationen bieten. Trotzdem vertut man sich dort aufgrund des komplizierten Syntax immer mal wieder. Hier lässt sich die Konstruktion des Datums als englischer Text ablesen. Fein!



* Und wir lassen jetzt die Komplexität der Tageszeiten mit ihren unterschiedlichen Zeitzonen mal außen vor!

Samstag, 25. September 2010

Authentifizierung leicht gemacht mit Devise/Warden

In jeder Anwendung hat man immer wieder das gleiche Problem: Die Nutzer sollen verschiedene Rechte in der Anwendung haben.

Voraussetzung dafür ist zunächst einmal die Registrierung von Nutzern, am besten noch über eine Bestätigungsmail und die Anmeldung der Nutzer über eine Login-Seite.

Bisher hatte ich dafür Authlogic verwendet. Allerdings wurde das zuletzt vor anderthalb Jahren aktualisiert.

Also machte ich mich auf die Suche nach etwas "Modernerem" und stieß auf Devise.

Auf den ersten Blick machte das einen sehr guten Eindruck, obwohl hier anscheinend mit Codegenerierung gearbeitet wird, was Upgrades prinzipiell erschwert.

Allerdings sind die Features so überzeugend, dass ich es einmal ausprobieren wollte. Dazu gehört die Möglichkeit, mehrere Models an die Authentisierung zu koppeln und damit verschiedene Benutzerrollen in der Anwendung verwenden zu können.

Die aktuelle Version 1.1 funktioniert nur mit Rails 3. Da meine Anwendung noch Rails 2.3.8 verwendet, installierte ich also das Gem mit der aktuellen 1.0 Version von Devise:

gem install devise --version 1.0.8

Als nächstes muss Devise in das Rails-Projekt eingebunden werden:

script/generate devise_install

Devise gibt daraufhin ein paar Hinweise aus, was auf jeden Fall manuell angepasst werden muss:
  • Der ActionMailer braucht default_url_options, um die Bestätigungsmais korrekt erzeugen zu können

  • Es muss einen root-Controller geben

  • Im (default-) Layout müssen die Flashes für :notice und :alert vorhanden sein

Damit kann das erste Model für unser Login generiert werden:

thomas$ script/generate devise User
exists app/models/
create app/models/user.rb
exists db/migrate
create db/migrate/20100925163708_devise_create_users.rb
route map.devise_for :users


In der erzeugten Migration und in der User-Klasse kann man nun erst mal die verschiedenen Module aktivieren bzw. deaktivieren und kann dabei auch noch weitere Attribute für das User-Model aufnehmen. Anschließend muss man natürlich die Datenbank per rake db:migrate aktualisieren.

In der User-Klasse sehen die Devise-Module dann so aus:

# Include default devise modules. Others available are:
# :http_authenticatable, :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :activatable
devise :registerable, :database_authenticatable, :recoverable,
:rememberable, :trackable, :validatable


Nun noch in den ApplicationController die Zeile

before_filter :authenticate_user!

einfügen, und schon bekomme ich eine Login-Seite mit der Möglichkeit der Neu-Registrierung und der Zusendung eines vergessenen Passworts an die Mail-Adresse (die im übrigen der default-Login ist).

Die verwendeten Views kommen direkt aus dem gem. Um sie anzupassen, kann man sie ganz einfach in die eigene Anwendung kopieren:

thomas$ script/generate devise_views
exists app/views
create app/views/confirmations
create app/views/confirmations/new.html.erb
create app/views/devise_mailer
create app/views/devise_mailer/confirmation_instructions.html.erb
exists app/views/devise_mailer
create app/views/devise_mailer/reset_password_instructions.html.erb
exists app/views/devise_mailer
create app/views/devise_mailer/unlock_instructions.html.erb
create app/views/passwords
create app/views/passwords/edit.html.erb
exists app/views/passwords
create app/views/passwords/new.html.erb
create app/views/registrations
create app/views/registrations/edit.html.erb
exists app/views/registrations
create app/views/registrations/new.html.erb
create app/views/sessions
create app/views/sessions/new.html.erb
exists app/views/shared
create app/views/shared/_devise_links.erb
create app/views/unlocks
create app/views/unlocks/new.html.erb


Nach dem Einfügen von ein paar CSS-Anweisungen sieht meine Benutzeranmeldung nun schon sehr professionell aus!

Als nächstes will ich ein zweites Model für die Administratoren einführen. Doch das kommt in einem neuen Post....

Samstag, 22. Mai 2010

Deployment: Passenger!

So viel Spaß wie das Entwickeln von Software mit Rails macht, so wenig unterhaltsam war es früher, die Sache dann auch skalierbar an den Mann bzw. auf den Server zu bringen. Rails ist zu heavy für klassisches CGI, FastCGI klemmt dank Schwächen in der Implementierung und der Spezifikation immer an irgendeiner Stelle, mod_ruby taugt nicht für Rails, und die Python-basierten Entwicklungswebserver Webbrick und Mongrel sind nicht wirklich für den Produktiveinsatz gedacht.

Trotzdem galt bis dato ein Reverse Proxy-Setup mit Mongrel noch als das kleinste Übel. Auch dafür ist aber ein ganzer Haufen an Apache-Direktiven notwendig, und das manuelle Einrichten und Kontrollieren der System-Infrastruktur für den Mongrel (Init-Skript, Benutzer etc.pp). Alles keine wirkliche Gaudi, und schnell war das Ergebnis dann auch nicht im eigentlichen Sinne. Richtig pricklig wurde es dann, wenn mehrere Rails-Applikationen auf einem virtuellen Host laufen sollten.

Dagegen ist das Deployment mit mod_passenger die reinste Wohltat. Meine Installation eines Staging-Servers mit zwei Rails-Applikationen auf Ubuntu 8.04 Hary sah im Detail so aus:

  1. Modul installieren: Hardy bringt (im Gegensatz zu Ubuntu 10.04 Lucid und Debian Lenny) selbst noch kein Passenger-Modul mit. Hier gibt es aber freundlicherweise eine Paketquelle mit einem Hardy-Modul, das für mich gut funktioniert hat.
  2. Verzeichnis des Rails-App-Ordners in das Apache-Vhostverzeichnis linken. In meinem Fall dann ln -s /srv/rails/rails-app/public /srv/www/vhost/rails-app.
  3. PassengerRoot /rails-app in die Apache-Vhost-Konfiguration schreiben
    Diesen Schritt kann man sich sogar sparen, wenn die Rails-Applikation direkt im Root eines Virtuellen Hosts laufen soll. Passenger erkennt Rails dann automatisch und richtet alles ein.

  4. fertig! Passenger startet und managet automatisch einen Pool von Rails-Servern, je nach Serverlast, und loadbalanct die Requests.


Auch die Nebenwertungen fallen durchweg positiv aus: Die Fehlermeldungen sind aussagekräftig, inklusiver sinnvoller Tipps zum Lösen von Deploymentproblemen auf den Reporting-Seiten, die mod_passenger anzeigt. Und auch die Dokumentation ist hervorrangend.

Einziger Wermutstropfen momentan: Die Latenz des Staging-Servers liegt bisher mit 250ms deutlich zu hoch, und ein ganzes Stück über unseren sorgsam optimierten Mongrel-Setups. Ich werde drüber berichten, wenn wir da Fortschritte machen. Angeblich ist Performanz-mäßig dieser Tage ja JRuby und ein Java-Applikationserver das gelbe vom Ei...

Also, nicht von der hässlichen Webseite abschrecken lassen, Passenger nehmen und Rails ohne Ärger deployen!

Donnerstag, 20. Mai 2010

Seelenfrieden und Fingerschonung beim HTML-Schreiben: Zen-Coding

Nicht eigentlich ein Rails-Tipp, aber sollte jeder kennen, der gelegentlich die (gemeinhin beschränkte) Freude hat, HTML-Seiten zu editieren: Zen Coding.

Das ist ein ultra-praktische Template-Plugin, das einem beim Schreiben von HTML unglaublich viel Tipparbeit abnimmt. Die Grundidee ist, dass man statt HTML-Tags CSS-Selektoren hinschreibt (also z.B. div.MeineKlasse statt <div class="MeineKlasse"></div>) und dies dann vom Plugin in die eigentlichen Tags expandieren lässt, die man dann nur noch mit Inhalt füllen muss.

Ein paar kleine Beispiele:

div.InfoBereich>span#firstWarning<HOTKEY>

... gibt ...

<div class="InfoBereich">
<span id="firstWarning"></span>
</div>


Das ganze kann man auch weiter treiben, und z.B. Elemente vervielfachen:


div#page>div.logo+ul#navigation>li*5>a<HOTKEY>

.... gibt...

<div id="page">
<div class="logo"></div>
<ul id="navigation">
<li><a href=""></a></li>
<li><a href=""></a></li>
<li><a href=""></a></li>
<li><a href=""></a></li>
<li><a href=""></a></li>
</ul>
</div>


Außderdem gibts noch praktische Abkürzungen für typische Aufgaben wie "Scheib einen HTML-Header hin".


html:xt

... gibt ...

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
<title></title>
</head>
<body>

</body>
</html>

Für wen gibts das? Praktischerweise für die allermeisten populären Editoren (inklusive vim, textmate, Eclipse, Netbeans).

Ressourcen:

Mittwoch, 19. Mai 2010

Sichere Zufallszahlen mit ActiveSupport::SecureRandom

Auch wenn es manchmal anders scheint: Kaum etwas ist für eine deterministischen Maschine (also einen Computer) so schwierig, wie ein echt "zufälliges" Ergebnis zu erzeugen. Die gleiche Berechnungsvorschrift mit den gleichen Parametern ergibt nun mal per se immer das gleiche Ergebnis.

Weil man häufig mal beim Programmieren aber ein zufälliges Ergebnis braucht, behilft man sich typischerweise mit "Pseudo"-Zufallsgeneratoren. Die basieren auf komplizierten mathematischen Formeln, die ausgehend von einem Startwert deterministisch eine zufällig erscheinende Zahlenfolge erzeugen. Dieser Startwert wird dann beim Programmstart intialisiert, z.B. auf die aktuelle Systemzeit. Dieser "Zufall" ist wohl gut genug, um in iTunes für die zufällige Anordnung einer Playlist zu sorgen, aber es handelt trotzdem um eine deterministische Berechnungsreihe, und zwar eine, deren Startwert relativ leicht erratbar ist (die Systemzeit), das heißt mit ein bisschen Mühe ist Ihr Ergebnis vorhersagbar.

Deswegen sind solche Pseudo-Zufallsgeneratoren für alle sicherheitskritischen Anwendungsfälle absolut ungeeignet. Was also tun? Moderne Betriebssysteme bieten häufig Funktionen an, die "gute" Zufallswerte liefern. Dazu nutzen sie externe Ereignisse als Quelle von "Zufall" -- z.B. Tastendrücke, Netzwerkpakete, Interrupts, die geschickt den generierten Psuedozufallszahlen beigemengt werden. Weil es schwierig ist, alle diese externen Signale genau genug zu beobachten, vorherzusagen, oder nachzuspielen, sinkt damit die Chance rapide, dass ein Angreifer die Zufallszahlen vorhersagen kann.

Leider sind diese modernen Mechanismen aber eben vom konkreten Betriebssystem abhängig, und teilweise schwer zu verwenden. Deshalb ist es praktisch, dass uns Rails diesen Aufwand erspart, indem es uns eine hübsche Bibliothek zur Verfügung stellt: ActiveSupport::SecureRandom. Unter dem Strich lässt sich damit ein Zufallswert, z.B. ein Cookie für eine E-Mail-Bestätigung damit trivial erzeugen:




require 'securerandom'
p SecureRandom.hex(12)
#=> "52750b30ffbc7de3b362"

>> SecureRandom.base64(12)
=> "n7pKog8eliVZm8Pq"


Das ist einfacher und tausend mal besser, als eine SHA1-Summe eines Pseudo-Zufallswerts zu bilden. Also, vor dem Benutzen von Kernel.rand nachdenken, ob die "Zufalls"zahl in irgendeinerweise sicherheitsrelevant ist, und im Zweifelsfall lieber SecureRandom benutzen!