2010-11-19 213 views
6

我有一個Rails 3應用程序(使用Mongodb和Mongoid,如果這有所作爲),並且在我的一個模型中,我有一個字段定義爲日期類型。在Rails 3中的日期驗證

class Participant 
    include Mongoid::Document 

    field :birth_date, :type => Date 
end 

我的控制器是採用蒙戈的find_or_initialize_by特點:

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 

這一切都歸結爲:我該怎麼辦日期驗證加載ActiveModel中與Mongoid和Rails 3?

我想確保在文本框中輸入「blah」在分配給我的模型的.birth_date字段時不會引發異常。它應該提供一個很好的驗證錯誤消息,而不使用控制器來進行驗證。

這裏的基本要求:

  • 的觀點必須是單個文本框。 沒有別的。這是一個用戶 要求,我們不能改變

  • 驗證應在 模型來完成,而不是在控制器或
    視圖(JavaScript或其他)

  • 沒有正則表達式格式驗證(他們不工作的權利/支持的語言環境等)

的問題是,從文本框中的值是validates_format_of之前分配給.birth_date和validates_presence_of中運行。所以...

我該如何截取值的賦值,以便在賦值之前驗證它?是否有可能在模型中使用ActiveModel?或者這是否需要我將代碼放入控制器才能執行此操作?

+0

你怎麼沒有在你的控制器中進行日期驗證?在你的模型中定義一個方法,如果日期無效,然後讓你的控制器使用像「raise」這樣的方法,則引發一個'ArgumentError'你沒有指定一個有效的日期「除非@ participant.date_is_valid' – Alex 2010-11-19 02:49:03

+0

另外,你能提供一個有關「有效」日期的含義的更多細節?一個有效的RFC標準日期時間字符串?日期可以用許多不同的方式進行格式化。任何更多的信息將是有幫助的 – Alex 2010-11-19 03:02:12

回答

8

這個代碼小塊可以做的伎倆

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

puts retVal 
+2

儘可能多的,因爲我不喜歡它,這是我們最終使用。其中一天,我打算寫一個rails插件來處理這個更優雅的問題....這些日子之一......當我有空閒的時候...:P – 2011-01-18 14:06:15

+2

我推薦validates_timeliness gem。 – 2012-08-06 18:51:39

2

我剛剛發現這一點,雖然我還沒有發揮它:

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

Github上庫是在https://github.com/codegram/date_validator

從博文中引用:

「你可以用它無論是在Rails 3個的模型或任何自定義類(需要加載ActiveModel後),在一個非常簡單的方式

他們也給這使得它看起來像它應該工作的範例你 - 它是在模型中完成的,而不是在控制器中完成的。

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

在類上下文中非常小心時間戳。在Rails生產中,這些日期將是該類定義的時間,即應用程序服務器啓動的時間。正如date_validator文檔中提到的那樣,在'Proc'中傳遞日期。 – 2012-02-27 16:04:57

2

您需要使用before_validation過濾器來處理例如 模型中的

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 

另一種選擇是創建自己的驗證方法是什麼?但我不認爲這就是你想要的...

0

另一種方法是創建一個擴展::加載ActiveModel一個EachValidator驗證類,如:

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 

在我這個連接上面的例子在模型中使用setter方法處理解析,如果傳遞字符串。我想支持模糊日期時間匹配,所以我使用的是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 

,這建立驗證的模型

validates :starts_at, :time => true 

這是很好的和傳統。

+0

但是如果日期來自用戶輸入'kind_of?(Time)'將始終返回false。 '「12/12/1912」.kind_of?(Date)=> false'。那麼這將如何工作? – Mohamad 2013-04-25 18:36:29

4

validates_timeliness寶石很漂亮。你可以這樣做:

class Participant < ActiveRecord::Base 
    validates_date :birth_date 
end