Rails Plugin amount_field

07. Aug 2009 | 0 comments
Ich habe heute mein Rails Plugin amount_field veröffentlicht. Es ermöglicht die Angabe von Beträgen im deutschen und im amerikanischen Format in Textfeldern eines Formulars, zum Beispiel:

Bisher habe ich das für den Einzelfall speziell gelöst. In einen aktuellen Projekt habe ich jedoch ein Formular mit vielen Beträgen, die im deutschen Format angezeigt und akzeptiert werden sollen. Daher war es an der Zeit, eine generelle Lösung zu finden.

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 ?
Folgende Anforderungen und Überlegungen führten zur Lösung:
  1. 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.

  2. 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.

  3. 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
    
  4. 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
    
  5. 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.

  6. 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 => '.' }

  7. Fehlermeldungen bei einen falschen Format sollten internationalisierbar sein

  8. 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.

class Product < ActiveRecord::Base
  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.

Post your comment