2010-12-03 10 views
5

Je champs de formulaire où l'utilisateur entre dans:Rails Way: Formatage de la valeur avant de le définir dans le modèle?

  • : 50.5%
  • Percents
  • argent: $144.99
  • dates: Wednesday, Jan 12th, 2010

...

Le percent et money les attributs de type sont enregistrés sous le champ decimal s avec ActiveRecord, et les dates sont les champs datetime ou date.

Il est facile de convertir entre les formats en javascript, et vous pouvez théoriquement les convertir au format acceptableecommandecord onsubmit, mais ce n'est pas une solution décente.

Je voudrais faire quelque chose remplacer les accesseurs dans ActiveRecord ainsi quand ils sont définis il les convertit de n'importe quelle chaîne au format approprié, mais ce n'est pas le meilleur non plus.

Ce que je ne veux pas est d'avoir à les exécuter à travers un objet processeur séparé, ce qui nécessiterait quelque chose comme ceci dans un contrôleur:

def create 
    # params == {:product => {:price => "$144.99", :date => "Wednesday, Jan 12, 2011", :percent => "12.9%"}} 
    formatted_params = Product.format_params(params[:product]) 
    # format_params == {:product => {:price => 144.99, :date => Wed, 12 Jan 2011, :percent => 12.90}} 
    @product = Product.new(format_params) 
    @product.save 
    # ... 
end 

Je voudrais que ce soit complètement transparent. Où est le crochet dans ActiveRecord donc je peux le faire le Rails Way?

Mise à jour

que je fais tout ce pour l'instant: https://gist.github.com/727494

class Product < ActiveRecord::Base 
    format :price, :except => /\$/ 
end 

product = Product.new(:price => "$199.99") 
product.price #=> #<BigDecimal:10b001ef8,'0.19999E3',18(18)> 
+1

Il est petit bijou nommé « attribute_normalizer » qui fait ce travail –

Répondre

10

Vous pouvez utiliser un avant crochet validation pour normaliser vos params tels que before_validation

class Product < ActiveRecord::Base 

    before_validation :format_params 


     ..... 


    def format_params 
     self.price = price.gsub(/[^0-9\.]/, "") 
     .... 
    end 
+2

Si vous modifiez cette valeur à nouveau dans votre application, vous aurez besoin de rendre ce processus bidirectionnel par exemple tout processus que vous effectuez dans la before_validation vous devez inverser en remplaçant le getter, voir la réponse de iain à cette question pour un exemple –

+1

En supposant que le prix est un nombre dans la base de données, comme écrit ci-dessus, il se produira après le cast de type. Ce qui signifie que l'entrée utilisateur de "1ab23cd45" obtiendrait to_f traitement à 1 avant de voir format_params. Après une longue journée, je sais maintenant que cela devrait être self.price = price_before_type_cast.gsub ([/ [^ 0-9 \.] /, '') L'utilisation de l'accesseur * _before_type_cast généré automatiquement est un secret crucial. – elc

+1

autre addendum: Vous avez réellement besoin de price_before_type_cast.to_s.gsub (...) parce que sinon vous frapperez les erreurs de type 'méthode inconnue gsub pour Fixnum', par exemple lorsqu'une usine attribue un prix au prix. – elc

29

Vous pourrait remplacer le setter ou le getter.

Outrepasser le poseur:

class Product < ActiveRecord::Base 
    def price=(price) 
    self[:price] = price.to_s.gsub(/[^0-9\.]/, '') 
    end 
end 

Outrepasser le getter:

class Product < ActiveRecord::Base 
    def price 
    self[:price].to_s.gsub(/[^0-9\.]/, '')) 
    end 
end 

La différence est que cette dernière méthode stocke encore ce que l'utilisateur est entré, mais récupère formaté, alors que le premier, stocke la version formatée.

Ces méthodes seront utilisées lorsque vous appelez Product.new(...) ou update_attributes, etc ...

+0

Ceci devrait être la réponse acceptée. – Erowlin