2012-03-13 20 views
10

Así que tengo un formulario donde los usuarios pueden ingresar un precio. Estoy tratando de hacer una validación previa que normalice los datos, recorte $ si el usuario lo pone.Convertir la entrada del usuario al número entero

before_validation do 
unless self.price.blank? then self.price= self.price.to_s.gsub(/\D/, '').to_i end 
end 

Si las entradas del usuario $ 50 Este código es que me da 0. Si las entradas del usuario 50 $ este código me da 50. Creo que desde el tipo de datos es entero que los carriles se está ejecutando .to_i antes de mi before_validation y el recorte de todo después de $. Este mismo código funciona bien si el tipo de datos es una cadena.

¿Alguien tiene una solución que me permita mantener el tipo de datos entero?

Respuesta

20

Una forma es para anular el mecanismo en el modelo que establece el precio, así:

def price=(val) 
    write_attribute :price, val.to_s.gsub(/\D/, '').to_i 
end 

Así que cuando lo hace @model.price = whatever, que se destinará a este método en lugar del escritor atributo carriles por defecto. Luego puede convertir el número y usar write_attribute para hacer la escritura real (debe hacerlo de esta manera porque el estándar price= es ahora este método).

Me gusta mejor este método, pero para referencia, otra forma de hacerlo es en su controlador antes de asignarlo al modelo. El parámetro viene como una cadena, pero el modelo está convirtiendo esa cadena en un número, así que trabaje con el parámetro directamente. Algo como esto (solo adaptarlo a su código de controlador):

def create 
    @model = Model.new(params[:model]) 
    @model.price = params[:model][:price].gsub(/\D/, '').to_i 
    @model.save 
end 

Para cualquiera de las soluciones, eliminar ese before_validation.

+0

Gracias. Siempre pensé que usar una before_validation es bastante torpe. Esto es definitivamente más elegante. –

3

Yo definiría un atributo virtual y hacer mi manipulación no lo que le permite dar formato y modificar tanto el getter y setter a voluntad:

class Model < ActiveRecord::Base 

    def foo_price=(price) 
    self.price = price... #=> Mods to string here 
    end 

    def foo_price 
    "$#{price}" 
    end 

También puede ser que desee tener en cuenta que:

"$50.00".gsub(/\D/, '').to_i #=> 5000 
+0

Gracias, voy a probar esto también. Di otra respuesta porque primero probé su comentario. Además, gracias por la captura de .00, actualizaré mi expresión regular. – Robert

+0

Np. Sugerí ese enfoque porque no soy partidario de una lógica adicional en el controlador y hay claridad, ya que no está anulando un campo que a primera vista debería coincidir con el campo db y supongo que una cadena no es del tipo de datos correcto:) – offbyjuan

+0

Crearía una clase de presentador adicional para la lógica de formateo aquí –

0

Mi soluction Colum precio decimal

t.decimal :price, precision: 12, scale: 6 

# app/concern/sanitize_fields.rb 
    module SanitizeFields 
     extend ActiveSupport::Concern 

     def clear_decimal(field) 
     return (field.to_s.gsub(/[^\d]/, '').to_d/100.to_d) unless field.blank? 

     end 

     def clear_integer(field) 
     field.to_s.strip.gsub(/[^\d]/, '') unless field.blank? 
     end 

     # module ClassMethods 
     # def filter(filtering_params) 
     #  results = self.where(nil) 
     #  filtering_params.each do |key, value| 
     #  results = results.public_send(key, value) if value.present? 
     #  end 
     #  results 
     # end 
     # 
     # #use 
     # #def index 
     # # @products = Product.filter(params.slice(:status, :location, :starts_with)) 
     # #end 
     # 
     # end 

    end 

#app/controllers/products_controller.rb 

include SanitizeFields 

params[:product][:price] = clear_decimal(params[:product][:price]) 
Cuestiones relacionadas