Ruby
Für ein Nebenprojekt in Rails3 benötige ich wieder mein Rails Gem amount_field. Daher bin ich endlich dazu gekommen, es für Rails3 zu aktualisieren, wenn auch intern eher im Rails2-Style.
Infos unter GitHub.
Irgendwann, vermutlich nach einem Update von Ruby und/oder Rails, stand die History meiner Rails Console nicht mehr zur Verfügung.
Nachdem ich damit eine gewisse Zeit leben konnte, wollte ich heute dem Grund auf die Schliche kommen.
Nach einigem Probieren kam ich darauf, dass die Konsole und deren History unter ~/.irb_history schon funktioniert. Sie wird allerdings durch die Datei ~/.rdebug_hist erweitert.
Ein Durchlaufen der einzelnen Einträge durch "Pfeil-Taste hoch" lief erst durch die Einträge des Debuggers und fand dann irgendwann auch die Einträge aus ~/.irb_history. Man musste nur oft genug Tippen :).
Ein Löschen der Datei ~/.rdebug_hist führte dazu, dass die letzten Einträge in der Konsole beim nächsten Aufruf sofort zur Verfügung standen.
Die Datei ~/.rdebug_hist wird durch ruby-debug (genauer ruby-debug-0.10.3/cli/ruby-debug/interface.rb) eingelesen und geschrieben. Ich habe den Code noch nicht ganz durchschaut, aber die Datei wurde trotz des Einsatzes des Debuggers bisher nicht wieder erzeugt. Sicherheitshalber habe ich die Datei selbst schreibgeschützt angelegt, um eine erneute Erzeugung zu vermeiden.
touch ~/.rdebug_hist
chmod 400 ~/.rdebug_hist
Logging in der Rails-Console reaktiviert
Darüber hinaus habe ich das Aktivieren und Deaktivieren der Logausgaben
in der Rails-Konsole wieder hinbekommen. Meine ~/.irbrc sieht wie folgt aus:
1 2 3 4 5 Wirble.init 6 Wirble.colorize 7 8 9 system(%{ri }) 10 end 11 12 if ENV.include?('RAILS_ENV') 13 14 15 ActiveRecord::Base.connection.select_all(query) 16 end 17 18 19 set_logger Logger.new(STDOUT) 20 end 21 22 23 set_logger @logger 24 end 25 26 27 ActiveRecord::Base.connection.instance_variable_get("@logger") 28 end 29 30 31 @logger ||= get_logger 32 ActiveRecord::Base.connection.instance_variable_set("@logger", logger) 33 end 34 35 end 36
Update: Ich nutze Rails 2.3.4 und Ruby ruby 1.8.7
Ein häufiger Denkfehler beim Einbinden eines Moduls per include in eine Klasse ist, dass die Methoden aus dem Modul tatsächlich in die Klasse eingefügt werden. Dem ist nicht so. Hier ein Beispiel:
"M1#foo"
end
end
include M1
end
Der Aufruf von include M1 führt intern nicht zu folgendem Code:
"M1#foo"
end
end
Tatsächlich wird durch include eine anonyme Proxyklasse erzeugt, die auf das Modul verweist und die Klasse A erhält eine Referenz auf diese Proxy-Klasse:
-> M1
end
-> proxy
end
a = A.new
a.foo
Im obigen Beispiel findet Ruby die Methode foo also nicht in der Klasse A, sondern (über die Proxy-Klasse) im Modul M1.
Suche entlang der Vererbungshierachie
Grundsätzlich gilt: Um eine Methode auszuführen, muss Ruby die Definition der Methode finden und sucht die Methode dazu entlang der Vererbungshierachie. Ein Beispiel:
include M1
include M2
end
a = A.new
a.foo
Damit ergibt sich diese Suchreihenfolge:
1) A 2) M2 (über proxy) 3) M1 (über proxy) 4) B
Die Vererbungshierachie sieht wie folgt aus:
A -> (proxy->M2) -> (proxy->M1) -> B
Es wird bei der Klasse A begonnen. Wird die Methode nicht gefunden, wird im Modul M2 gesucht. Ist die Methode hier nicht vorhanden, wird im Modul M1 gesucht und existiert die Methode hier nicht, wird in der Superklasse B nachgeschaut. Dort wiederholt sich die Suche.
Daher hat eine in der Klasse A definierte Methode immer Vorrang vor einer gleichnamigen aus einem inkludierten Modul:
"M1#foo"
end
end
-> proxy -> M1
"A#foo"
end
end
a = A.new
a.foo # => "A#foo"
Ruby sucht die Methode entlang der Vererbungshierachie und findet die Methode in der Klasse A bevor es im Modul M1 sucht.
Modulmethode über super aufrufen
Es ist sicher bekannt, dass die Methode super dazu dient, eine gleichnamige Methode in der Superklasse aufzurufen. Beispiel:
"SuperClass#foo"
end
end
"A#foo: "
end
end
a = A.new
a.foo # => "A#foo: SuperClass#foo"
Was vielleicht nicht sofort einleuchtet ist, dass auch die Modulmethode über super aufgerufen werden kann. Das Modul befindet sich (wie die Superklasse auch) in der Vererbungshierachie und daher kann sowohl die Superklassenmethode, als auch die Modulmethode über super aufgerufen werden:
"M1#foo"
end
end
include M1
"A#foo: "
end
end
a = A.new
a.foo # => "A#foo: M1#foo"
Vorrang beim Einbinden von Modulen
Werden zwei Modul eingebunden und erhalten diese dieselbe Methode, so wird die Methode des zuletzt inkludierten Moduls verwendet. Im Beispiel M2:
"M1#foo"
end
end
"M2#foo"
end
end
include M1
include M2
end
a = A.new
a.foo # => M2#foo
Einschränkung der Sichtbarkeit
Mit dem Wissen um die Wirkung von include, wird auch klar, warum die Einschränkung der Sichtbarkeit wie folgt nicht möglich ist:
"mod_method1"
end
end
"mod_method2"
end
end
include M1
private
include M2 # method mod_method2 is still public
end
A.new.mod_method1 # => "mod_method1"
A.new.mod_method2 # => "mod_method2"
Die Methoden aus dem Modul M2 sind öffentlich (public), obwohl das Modul nach dem Schlüsselwort private eingebunden wird. Und zwar, weil die Methode aus dem Modul nicht in die Klasse eingebunden werden, sondern die Klasse auf das Modul verweist (über den Proxy) und mod_method2 in M2 öffentlich ist.
Korrekt geht es wie folgt:
private
"mod_method2"
end
end
include M1
include M2
mod_method2
end
end
A.new.foo # => "mod_method2"
A.new.mod_method1 # => "mod_method1"
A.new.mod_method2 # => NoMethodError: private method ‘mod_method2’ called
for #<A:0x108d8>
Wenn eine Modulmethode nur für interne Implementierung dient, sollte die Methode auf jeden Fall als private deklariert werden.
Proxy-Klasse
Wozu dient eigentlich die Proxy-Klasse? Warum wird nicht direkt eine Referenz auf das Modul in die Klasse eingefügt? Wozu der Umweg über die Proxy-Klasse?
Die Proxy-Klasse ist notwendig, da von Modulen keine Instanzen erzeugt werden können. Dennoch möchte man eventuell Instanzvariablen von Modulen nutzen.
Inkludieren die Klassen A und B das Modul M1, so darf die Änderung einer Instanzvariablen in der Klasse A nicht den Wert in B beeinflussen. Daher gibt für A und B jeweils eine Proxy-Klasse und die die Werte hält.
Alle Proxy-Klasse verweisen auf dasselbe Modul, daher werden Änderung an Methoden des Moduls in alle Klassen sichtbar:
@foo = v
end
"M#foo: "
end
end
include M
self.foo = v
end
end
include M
self.foo = v
end
end
a = A.new(47)
p a.foo # => "M#foo 47"
b = B.new(11)
p b.foo # => "M#foo 11"
"modified M#foo "
end
end
p a.foo # => "modified M#foo 47"
p b.foo # => "modified M#foo 11"
Hoffe, ich habe für mehr Klarheit sorgen können und nicht mehr verwirrt :)
to be continued ...
Ist schon wieder zwei Wochen her, dass wir die 4. Rails-Konferenz hinter uns gebracht haben.
Diesmal fand die Konferenz im ACHAT Plaza Frankfurt/Offenbach statt. Das Hotel machte einen durchweg guten Eindruck, liegt allerdings verkehrstechnisch nicht besonders günstig. Auch der Vorraum für Pausen ist für 100+ Personen einfach viel zu klein. Die Zimmer waren sauber und geräumig und das Essen gut. Für 2010 werden wir uns aber trotzdem nach einer Alternative umschauen. Wer Tipps hat, bitte melden.
Den Gesprächen und Feedback-Bögen zur Folge, ist die Konferenz auch diese Jahr wieder sehr gut angekommen. Die Workshops, Vorträge und das Socializing zwischendurch ergaben ein rundes Bild.
Nur der Ort des Vorabend-Events kam nicht so gut an, weil die Teilnehmer vom Hotel in Offenbach erst in die Innenstadt von Frankfurt mussten. Das ACHAT-Hotel selbst wäre ein guter Ort gewesen, kostete aber zu viel Geld. Die Lounge im Fleming's Club kam bei den Anwesenden dann aber gut an. Danke auf diesem Wege nochmals an XING für das Sponsoring und die Vorträge!
Danke auch an alle Sponsoren, Speaker und Teilnehmer!!!
Dem Feedback nach zu urteilen, werden auch 2010 wieder genügend Teilnehmer kommen. In diesem Sinne: bis 2010!.
Na, dann mal schnell zuschlagen :)
Vielen Dank an Kaan für die Unterstützung!
Da ich nichts adäquates an Gems oder Plugins gefunden habe, habe ich mich selbst daran gemacht. Und das war anfangs gar nicht so einfach :)
Es ist die Eingabe (die Übertragung aus dem Formular an die Rails-Anwendung) und die korrekt formatierte Ausgabe (Anzeige im View/Formular) zu betrachten.
Eingabe
Vorweg folgende Definition:- Formatvalidierung: z.B. ist "1.234,56" ein gültiges Format?
- Wertvalidierung: z.B. ist 1.234,56 > 1250 ?
- Damit sichergestellt werden kann, dass der eingegebene Wert wirklich ein gültiges Format hat (z.B. 1.234,56), muss zunächst eine Formatvalidierung erfolgen und danach die (sichere) Konvertierung in eine Zahl 1234.56. Im Anschluss kann optional die Wertvalidierung erfolgen.
Ohne die Formatvalidierung würde Rails aus 1.234,56 den Wert 1.23 machen, ohne dass der Anwender das gegebenenfalls merkt. Und das ist schlecht.
-
Die Formatierung (Ein- und Ausgabe) hat eigentlich nichts mit dem Attribut oder dem ActiveRecord-Modell zu tun. Es handelt sich ja mehr um eine bestimmte Repräsentation eines Betrags und dafür ist das Modell nicht zuständig. Zur formatierten Anzeige werden u.a. die View-Helper von Rails genutzt.
Die Konvertierung von "1.234,56" in 1234.56 kann aber nicht auf Controller/View-Ebene erfolgen, da die Validierung der Attribute in ActiveRecord geschieht. Und die Formatvalidierung ist eben eine solche Validierung. Daher bleibt doch nur das Modell.
- Die Zuweisung als Ruby-Zahl an den Setter (an das Attribute) muss weiterhin wie gewohnt möglich sein:
p = Product.new p.price = '12.345,67' # schlecht p.price = 12345.67 # ok - Der Getter des Attributs muss den Wert als (Ruby-)Zahl liefern, damit Berechnungen weiterhin wie gewohnt möglich sind:
p = Product.new(:price => 1.23) p.price # => 1.23 damit Rechnen möglich tax = p.price * 19.0 / 100.0 - Eine clientseitige Formatvalidierung (und Formatierung) durch JavaScript ist hilfreich, verhindet aber nicht unbedingt die Übertragung falsch formatierter Beträge und daher ist eine serverseitige Formatvalidierung immer notwendig.
- Die Formatierung eines Betrags besteht immer aus den drei Werten:
- Separator (Tausendseparator)
- Delimiter (Trennung Ganzzahl und Nachkommastellen)
- Precision (Anzahl der Nachkommastellen)
Die drei Werte bilden die Formatkonfiguration:
deutsch:{ :precision => 2, :delimiter => '.', :separator => ',' }
amerikanisch:{ :precision => 2, :delimiter => ',', :separator => '.' } - Fehlermeldungen bei einen falschen Format sollten internationalisierbar sein
- Hinweis: Active Record speichert den übergebenen Wert “1.234,56” in dem Attribute "price". Erst beim Lesen des Wertes wird dieser konvertiert zurückgeliefert. Daher liefert "price_before_type_cast" den Wert “1.234,56” und "price" den Wert 1.234 zurück. Die Wertvalidierung erfolgt bei Active Record auf Basis von XXX_before_type_cast.
Als Lösung definiere ich einen eigenen View-Helper "amount_field" als Ersatz für "text_field". Das HTML sieht wie folgt aus:
<input name="product[amount_field_price]" class=" amount_field"... />
Der eingegebene Wert (z.B. "1.234,56") wird dadurch über den Parameter amount_field_price und nicht (wie normalerweise) über den Parameter price übertragen.
Zusätzlich existiert das Validierungsmakro "validates_amount_format_of", dass für jedes angegebene Attribut eine spezielle Setter-Methode (z.B. amount_field_price=(value) definiert, die den Parameter amount_field_price annimmt und Werte im Format "1.234,56" akzeptiert.
validates_amount_format_of :price
end
Nach der Validierung und Konvertierung (z.B. "1.234,56" in 1234.56) wird der Wert an das Originalattribut "price" zugewiesen und kann von weiteren Validierungsmakro geprüft werden (z.B: validates_numericality_of :price).
Ausgabe
Damit die Ausgabe des Wertes 1234.56 formatiert erfolgt (z.B. "1.234,56"), verwendet der View-Helper amount_field intern den View-Helper number_with_precision mit der global definierten Formatkonfiguration. Im Fehlerfall wird der Wert nicht formatiert, sondern wie eingegeben angezeigt.
Das Gem (oder Plugin) inklusive Dokumentation ist unter GitHub zu finden.
Freue mich über Feedback, Fragen oder Fehlermeldungen.
In der am Montag, dem 20. April erscheinenden dritten Ausgabe des RailsWay-Magazins habe ich wieder einen Artikel beigesteuert. Diesmal geht es um das Thema, wie man ein eigenes Ruby-Gem und Rails-Plugin erstellt.
Weitere spannende Artikel und ein Interview mit David Heinemeier Hansson sind enthalten.
Viel Spaß mit dem neuen Magazin!
Download: MP3 (50:12 Min, 16.7 MB)
Podcast-Abo: RSS mit Enclosures
Weitere Links:
Ruby on Rails Security Project
PDF: Ruby on Rails Security
Ruby On Rails Security Guide
Session auf der Rails-Konferenz 2008
Cross Site Request forgery (csrf)
Newsletter zur Rails-Sicherheit
Newsletter Sicherheitslücken in Ruby
bauland42
Heiko Webers ist Gründer und Geschäftsführer von bauland42. Das Softwarebüro bauland42 erstellt sichere, effiziente und einfach zu bedienende Webanwendungen mit Ruby on Rails. Außerdem bietet bauland42 Sicherheitsberatung, Code-Audits und Training für Ruby on Rails. bauland42 informiert mit dem Ruby On Rails Security Project regelmäßig über neue Entwicklungen im Bereich der Sicherheit von Rails.
Der Podcast ist etwas länger geworden, als geplant, aber das Thema Sicherheits ist nicht mal eben abzuhandeln. Und wir haben sicher noch lange nicht alle Punkte angesprochen :). An der Qualität der Aufnahmen muss ich noch etwas pfeilen, aber besser so, als gar nicht.
Danke an Heiko!
Gute Neuigkeiten für alle Rails-Interessierten:
Die Rails-Konferenz findet auch dieses Jahr wieder statt!
Termin & Ort
1. und 2. September 2009
ACHAT Plaza Frankfurt/Offenbach
Ernst-Griesheimer-Platz 7
D - 63071 Offenbach
ACHAT Plaza Frankfurt/Offenbach
Auch dieses Jahr freuen wir uns wieder auf spannende Vorträge, interessante Diskussionen, den Erfahrungsaustausch in gemütlicher Atmosphäre und vieles mehr!
Seit dabei, es lohnt sich!Alle Informationen unter Rails-Konferenz.de!
Download: MP3 (32:35 Min, 20 MB)
Podcast-Abo: RSS mit Enclosures
Weitere Links:
Maik Schmidt
Enterprise Recipes with Ruby and Rails
Enterprise Integration with Ruby
Messaging in Ruby (Video von Maik Schmidt auf der
Rails-Konferenz 2008)
Review Markus Jais
Maik Schmidt hat sein neues Buch "Enterprise Recipes with Ruby and Rails" veröffentlicht. Eine willkommene Gelegenheit einen Podcast zu probieren und mit Maik ein paar Fragen zum Thema und zum Buch zu klären.
Deutschsprachige Podcasts zum Thema Ruby und Rails sind noch nicht so verbreitet. Es gibt den einen oder anderen Versuch, so auch hier. Neben dem gerade gestarteten Rails-Magazin RailsWay stellt es ein weiteres Medium dar, um Ruby und Rails im deutschsprachigen Raum weiter zu verbreiten.
Maik ist wie immer souverän, ich noch etwas unkonzentriert. Wird sicher im Laufe der Zeit besser. Insgesamt aber auf jeden Fall hörenswert.
Danke an Maik!
Am 17. Dezember 2008 kommt das weltweit erste Rails-Magazin auf den Markt. Es wird vom Software & Support Verlag herausgegeben und beglückt erstmal nur den deutschsprachigen Markt. Um das Magazin vor Weihnachten zu veröffentlichen, haben einige Autoren ein paar Überstunden gemacht (unter anderem ich :)). Wir hoffen mit dem Magazin für eine weitere Verbreitung von Ruby on Rails in Deutschland bzw. im deutschsprachigen Raum zu sorgen.
Ein Dankeschön an den Initiator und Chefredakteur Michael Johann und den Software & Support Verlag.
Weitere Links:
Pressemitteilung
weblog.rubyonrails.org
Wieder ein gelungenes Buch, wie ich finde. Habe daher gleich eine Rezension bei Amazon geschrieben.
Fazit: Das Buch sollte in keiner Ruby/Rails-Bibliothek fehlen.
