Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to validate a model's date attribute against a specific range (evaluated at run time)

I have several models with a date attribute and for each model I'd like to validate these dates against a given range. A basic example would be:

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

Ideally I'd like to evaluate the date range at runtime, using a similar approach as named_scope uses, e.g:

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

The above doesn't work of course, so what is the best way of achieving the same result?

like image 881
Olly Avatar asked Dec 02 '09 21:12

Olly


1 Answers

The simplest and working solution is to use the in-built validation from Rails. Just validates it like that:

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

if you need to validate the presence as well just add another validation:

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

Remember that you can always use helpers like 1.day.ago or 1.year.from_now to define ranges.

A special care should be taken when using relative dates. Consider the following example where the age should be in range 18..65:

validates :birthdate, 
          inclusion: { in: 65.years.ago..18.years.ago }

A person which is valid? # => true today, will suddenly become invalid once they turn 66.

This might lead to many unexpected cases, where your model cannot be updated anymore because this validation fails.

A clever way to handle this case is to validate the field only when it actually changes:

validates :birthdate, 
          inclusion: { in: 65.years.ago..18.years.ago }, 
          if: ->(model) { model.birthdate.present? && model.birthdate_changed? }
like image 85
coorasse Avatar answered Oct 27 '22 02:10

coorasse