2010-11-19 11 views
6

Tengo una aplicación de Rails 3 (usando Mongodb y Mongoid si eso hace la diferencia), y en uno de mis modelos tengo un campo definido como un tipo de fecha.Validación de fecha en Rails 3

class Participant 
    include Mongoid::Document 

    field :birth_date, :type => Date 
end 

Mi controlador utiliza la función de find_or_initialize_by mongo:

class ParticipantController 

    def create 
    @participant = Participant.find_or_initialize_by(params[:participant]) 
    if @participant.save 
     redirect_to participants_path 
    else 
     render :new 
    end 
    end 
end 

Todo esto se reduce a: cómo hago validación de fechas en ActiveModel con Mongoid y Rails 3?

Quiero asegurarme de que una entrada de "blah" como en un cuadro de texto no arroje una excepción cuando se asigna al campo .birth_date de mi modelo. Debería proporcionar un buen mensaje de error de validación sin usar el controlador para hacer la validación.

He aquí los requisitos básicos:

  • La vista debe ser un único cuadro de texto. Nada más. Se trata de un usuario requisito de que no podemos cambiar

  • La validación se debe hacer en el modelo , no en el controlador o en la vista
    (javascript o lo que sea)

  • No hay validaciones de formato de expresiones regulares (que no funcionan bien/local de apoyo, etc.)

el problema es que el valor de la caja de texto se asigna a la .birth_date antes validates_format_of y validates_presence_of se ejecutan. entonces ...

¿cómo puedo interceptar la asignación del valor, para poder validarlo antes de que se asigne? ¿Es posible hacer eso en el modelo, usando ActiveModel? o ¿esto me exige poner código en el controlador para hacer esto?

+0

¿por qué no está haciendo la validación de fecha en su controlador? Defina un método en su modelo que genere un 'ArgumentError' si la fecha no es válida y luego haga que su controlador use ese método como' raise 'Usted no especificó una fecha válida' 'a menos que @ participant.date_is_valid' – Alex

+0

Además, podría proporcionar un poco más detalles sobre lo que quieres decir con una fecha 'válida'? ¿Una cadena de fecha y hora estándar válida de RFC? La fecha se puede formatear de muchas maneras diferentes. Cualquier otra información sería útil – Alex

Respuesta

8

Este pequeño bloque de código puede hacer el truco

retVal = "it is a date" 
begin 
    y = Date.parse("rodman was here") 
rescue 
    retVal = "nope not a date" 
end 

puts retVal 
+2

tanto como no me gusta, esto es lo que terminamos usando. uno de estos días, voy a escribir un complemento de rieles para manejar esto de manera más elegante ... uno de estos días ... cuando tenga algo de tiempo libre ...: P –

+2

Recomiendo la joya validates_timeliness. –

2

me acaba de encontrar esto, aunque todavía no he jugado con él:

http://blog.codegram.com/2011/2/date-validation-with-rails-3

repositorio de Github es en https://github.com/codegram/date_validator

Citando del blog publicación:

"Usted puede usarlo ya sea en Rails 3 modelos o en cualquier clase personalizada (después de que requiere ActiveModel), de una manera muy fácil

También dan un ejemplo que hace que parezca que se debe trabajar para usted - está hecho en el modelo, en lugar de en el controlador.

validates expiration_date, 
    :date => { :after => Time.now, :before => Time.now + 1.year } 
+3

Tenga mucho cuidado con las marcas de tiempo en el contexto de clase. En la producción de Rails, esas fechas serán el momento en que se define la clase, que es cuando se inicia el servidor de la aplicación. Como el date_validator menciona, pasa la fecha en un 'Proc'. –

2

Debe utilizar el filtro before_validation para, p. Ej. en su modelo

class Participant 
    include Mongoid::Document 
    before_validation :make_a_date 
    field :birth_date, :type => Date 

    private 
    def make_a_date 
    #do all sorts of manipulation of the date here 
    end 
end 

otra alternativa sería la creación de su propio método de validación? Pero no creo que eso es lo que quiere ...

0

Una alternativa es crear una clase de validación que se extiende ActiveModel :: EachValidator, por ejemplo:

class TimeValidator < ActiveModel::EachValidator 
    def validate_each(record, attribute, value) 
    if value && !value.kind_of?(Time) 
     record.errors[attribute] << (options[:message] || "don't recognise time") 
    end 
    end 
end 

En el ejemplo anterior estoy acoplando este con un método setter en el modelo que maneja el análisis si se pasa una cadena. Quería admitir la coincidencia de fecha y hora difusa, así que estoy usando Chronic.

require 'chronic' 

def starts_at=(value) 
    if value.kind_of?(String) && parsed_time = Chronic.parse(value) 
    @starts_at = parsed_time.utc 
    else 
    @starts_at = value 
    end 
end 

y esto para configurar el validador en el modelo

validates :starts_at, :time => true 

que es agradable y convencional.

+0

Pero si la fecha es de la entrada del usuario 'kind_of? (Time)' siempre devolverá falso. '" 12/12/1912 ".kind_of? (Date) => false'. Entonces, ¿cómo funcionaría esto? – Mohamad

4

La gema validates_timeliness es muy dulce. Puede hacer algo como esto:

class Participant < ActiveRecord::Base 
    validates_date :birth_date 
end