2009-12-02 18 views
5

我有几个模型具有日期属性,对于每个模型,我想根据给定范围验证这些日期。一个基本的例子是:如何根据特定范围验证模型的日期属性(在运行时评估)

validates_inclusion_of :dated_on, :in => Date.new(2000,1,1)..Date(2020,1,1) 

理想情况下,我想,以评估在运行时的日期范围,使用类似的方法为named_scope使用,如:

validates_inclusion_of :dated_on, :in => lambda {{ (Date.today - 2.years)..(Date.today + 2.years)}} 

以上不工作的当然,那么达到相同结果的最佳方式是什么?

回答

6

如果验证为每个类相同,答案很简单:把一个验证方法的模块中,并在每个模型混合,然后用validate添加验证:

# in lib/validates_dated_on_around_now 
module ValidatesDatedOnAroundNow 
    protected 

    def validate_dated_around_now 
    # make sure dated_on isn't more than five years in the past or future 
    self.errors.add(:dated_on, "is not valid") unless ((5.years.ago)..(5.years.from_now)).include?(self.dated_on) 
    end 
end 

class FirstModel 
    include ValidatesDatedOnAroundNow 
    validate :validate_dated_around_now 
end 

class SecondModel 
    include ValidatesDatedOnAroundNow 
    validate :validate_dated_around_now 
end 

如果你想为每个模型不同的范围,你可能想更多的东西是这样的:

module ValidatesDateOnWithin 
    def validates_dated_on_within(&range_lambda) 
    validates_each :dated_on do |record, attr, value| 
     range = range_lambda.call 
     record.errors.add(attr_name, :inclusion, :value => value) unless range.include?(value) 
    end 
    end 
end 

class FirstModel 
    extend ValidatesDatedOnWithin 
    validates_dated_on_within { ((5.years.ago)..(5.years.from_now)) } 
end 

class SecondModel 
    extend ValidatesDatedOnWithin 
    validates_dated_on_within { ((2.years.ago)..(2.years.from_now)) } 
end 
+0

谢谢,这是有帮助的,但我刚编辑我的问题,以改善解释。我理想的是寻找一种更通用的解决方案,我可以跨模型和任何给定的日期字段使用该解决方案。 – Olly

+0

我更新了这个解决方案以更接近地匹配更新的用例,但我更喜欢我的其他解决方案。 –

1

不同的解决方案是依靠事实validates_inclusion_of只需要一个:in对象响应include?。构建延迟评估范围如下:

class DelayedEvalRange 
    def initialize(&range_block) 
    @range_block = range_block 
    end 
    def include?(x) 
    @range_block.call.include?(x) 
    end 
end 

class FirstModel 
    validates_inclusion_of :dated_on, :in => (DelayedEvalRange.new() { ((5.years.ago)..(5.years.from_now)) }) 
end 
13

可以使用date validator

validates :dated_on, :date => {:after => Proc.new { Time.now + 2.years }, 
            :before => Proc.new { Time.now - 2.years } } 
0

问问自己,如果你需要在运行时评估的范围内。如果价格很复杂,一个好的开发者并不总是要求正确,imho。

我想明智地检查一下日期是否合理,即在几年之后。如果这个数字在今天或两周前的几年内变化并不重要,而且我的服务器进程在几年内可能还不会运行。最后,我决定更简单的解决方案(只是一个简单的包含:in => reasonable_date_range)比正确但更复杂的解决方案更可取。

这就是说,我喜欢James A. Rosen的DelayedEvalRange解决方案。如果你的范围仅仅是几天,或者正确性因为其他原因而重要,我会去做。

3

validates :future_date, inclusion: { in: ->(g){ (Date.tomorrow..Float::INFINITY) }

0

对我来说,最简单的解决办法是:

validate :validate_dob 

def validate_dob 
    unless age_range.cover?(dob.present? && dob.to_date) 
    errors.add(:dob, "must be between %s and %s" % age_range.minmax) 
    end 
end 

不过,如果你想删除重复的话,建议在your own validator移动它。

是的,我想运行时执行,因为我的代码将在运行几年,并且一年多后我没有部署,我当然不想再次提出项目。

0

最简单和有效的解决方案是使用Rails的内置验证。只是验证它这样的:

validates :dated_on, inclusion: { in: (Date.new(2000,1,1)..Date(2020,1,1)) } 

,如果你需要验证的存在也只是增加一个验证:

validates :occurred_at, presence: true, inclusion: { in: (Date.new(2000,1,1)..Date(2020,1,1)) } 

请记住,你可以随时使用助手像1.day.ago1.year.from_now定义范围。

相关问题