如何针对特定范围validation模型的日期属性(在运行时评估)

我有几个带有日期属性的模型,对于每个模型,我想根据给定的范围validation这些日期。 一个基本的例子是:

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)}} 

上述方法当然不起作用,那么实现相同结果的最佳方法是什么?

如果每个类的validation相同,答案很简单:将validation方法放入模块并将其混合到每个模型中,然后使用validate添加validation:

 # 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 

您可以使用日期validation器 :

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

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

另一种解决方案是依赖于validates_inclusion_of只需要:in响应include?对象中的事实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 

问问自己是否需要在运行时评估范围。 如果价格是复杂的,那么优秀的开发人员并不总是追求正确性。

我想要理智地检查一个日期是否合理,即从现在开始的几年内。 如果事实certificate在今天或两周前的几年范围内并不重要,我的服务器进程可能在几年内仍然无法运行。 最后,我认为更简单的解决方案(只是直接包含:in => rational_date_range)比正确但更复杂的解决方案更可取。

也就是说,我喜欢James A. Rosen的DelayedEvalRange解决方案。 如果你的范围只有几天,或者由于某些其他原因,正确性很重要,我会选择它。

对我来说最简单的解决方案是:

 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 

但是,如果您想删除重复,我建议您在自己的validation器中移动它。

是的,我想要运行时执行,因为我的代码在几年内运行,经过一年多的时间我没有部署,我肯定不想再次启动项目。

最简单和有效的解决方案是使用Rails的内置validation。 只需validation它:

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

如果您需要validation状态,只需添加另一个validation:

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

请记住,您始终可以使用1.day.ago1.year.from_now1.day.ago 1.year.from_now来定义范围。