http://robots.thoughtbot.com/how-rails-works-type-casting I found this link very helpful.
I was having an issue validating a date. I needed the date to either match my format or be empty.
Here's what my validation looked like:
validate :expiration_date, format:{with: /(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])/}, allow_nil: true
For some reason, though, when the date was not in the right format, instead of kicking back an error it validated as if it was nil.
After some troubleshooting, I came to realize that was because the date WAS nil.
Turns out, Rails automagically casts values to match the attribute type when they are assigned to attribute (like record.id = "100"
, "100" (string) will be turned into an integer by rails).
For Date types, if the value doesn't match the required attribute formatting for casting, Rails lets it fail silently and returned nil.
I needed to capture what the value that was submitted, not what was cast, and thanks to the handy link above I learned you can use record.attribute_before_type_cast
.
So in my case I had to make a custom validation method:
validate :validate_expiration_date
def validate_expiration_date
if expiration_date_before_type_cast.present?
unless expiration_date_before_type_cast.match(/(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])/).present?\
errors.add(:expiration_date, " date format must be YYYY-MM-DD.")
end
end
end