0

I'm using a custom validation to check if a card has expired (it has a month / year).

Here's what I've got:

    validate :card_not_expired

    def card_not_expired
      if exp_year > Time.now.year or 
        (exp_year == Time.now.year and exp_month >= Time.now.month)
        true
      end
    end

I'm getting a undefined method ">=" for nil:NilClass message, presumably because the exp_month and exp_year fields can't be accessed directly in the Model. I tried using the symbol form (:exp_month) but not surprisingly, that didn't work either.

How do I get it to work?

FloatingRock
  • 6,741
  • 6
  • 42
  • 75

2 Answers2

1
    def card_not_expired
      if exp_year.present? and (exp_year >= Time.now.year or 
        (exp_year == Time.now.year and exp_month >= Time.now.month))
        true
      end

      # Raise error for expired card
      errors.add(:exp_month, "can't be in the past")
    end

Validations are executed when you create new object, and in this moment you have not value for exp_year, in order to work you must use present? or make validation only executed before save

COPY PASTE FROM RailsValidations

There are two kinds of Active Record objects: those that correspond to a row inside your database and those that do not. When you create a fresh object, for example using the new method, that object does not belong to the database yet. Once you call save upon that object it will be saved into the appropriate database table. Active Record uses the new_record? instance method to determine whether an object is already in the database or not. Consider the following simple Active

FloatingRock
  • 6,741
  • 6
  • 42
  • 75
ppascualv
  • 1,137
  • 7
  • 21
  • I already validate the presence of `exp_year`. See `validates_presence_of :exp_month, :exp_year, :cvc, :number` – FloatingRock Dec 31 '14 at 12:37
  • You need tou use exp.year.present? because validations are running when you create a object – ppascualv Dec 31 '14 at 12:47
  • No error, but now it seems to skip the validation altogether.. it doesn't fail when I pass an expired card. – FloatingRock Dec 31 '14 at 12:52
  • I think that I made a fail in use of and and or, I editred with correct use of paretensis, check too that a integer >= Time.now.year works in your rails console – ppascualv Dec 31 '14 at 12:54
  • I was missing the `errors.add(:expiration_date, "can't be in the past")` at the end. Works now.. – FloatingRock Dec 31 '14 at 13:15
0

You can use this methods to check if the card has expired:

def card_expired?
 expiration = Time.utc(exp_year, exp_month, month_days, 23, 59, 59)
 Time.now.utc > expiration
end

def month_days
  mdays = [nil,31,28,31,30,31,30,31,31,30,31,30,31]
  mdays[2] = 29 if Date.leap?(year)
  mdays[exp_month]
end
Paulo Fidalgo
  • 21,709
  • 7
  • 99
  • 115