cpf_validator.rb 2.24 KB
Newer Older
Rafael Ravedutti's avatar
Rafael Ravedutti committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
# This file is part of Agendador.
#
# Agendador is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Agendador is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Agendador.  If not, see <https://www.gnu.org/licenses/>.

16
class CpfValidator < ActiveModel::EachValidator
Bruno Freitas Tissei's avatar
Bruno Freitas Tissei committed
17

18 19 20
  # regular expression to describe a valid cpf
  CPF_REGEX = /^[0-9]{11}$/

21
  # Validate cpf, if not valid send an error message to
22 23 24 25
  # the record.errors[attribute]
  # @param record [ApplicationRecord] the model which owns a cpf
  # @param attribute [Symbol] the attribute to be validated (cpf)
  # @param value [String] the value of the record's cpf
26
  def validate_each(record, attribute, value)
27 28 29
    unless CpfValidator.validate_cpf(value)
      # record.errors[attribute] << ("#{value} is invalid.")
      record.errors[attribute] << ("#{value} é inválido!")
30 31 32 33
    end
  end

  # @return [boolean] true if cpf is valid
34
  # @param cpf [String] the cpf to be validated
35
  def self.validate_cpf(cpf)
36 37 38 39 40 41

    # check if is in the right format
    unless CPF_REGEX.match(cpf)
      return false
    end

Bruno Freitas Tissei's avatar
Bruno Freitas Tissei committed
42 43 44 45 46 47
    # replace every non numeric char with blank
    if !cpf.nil?
      number = cpf.gsub(/[^0-9]/, '')
    end

    arr = number.to_s.chars.map(&:to_i)
48
    # cpf with all digits the same should not be valid, i.e. 11111111111
Bruno Freitas Tissei's avatar
Bruno Freitas Tissei committed
49
    if arr.count(arr[0]) == arr.size || arr.size != 11
50 51
      return false
    end
52

53
    # return true if the 10th and the 11th digit are valid
54 55 56
    return (check(10, arr) and check(11, arr))
  end

57
  private
58 59

  # @return [boolean] true if last two digits are valid
60 61
  # @param aux [Fixnum] the auh-th number to be validated (10 or 11)
  # @param arr [Array] the array with individual cpf numbers
62
  def self.check(aux, arr)
63 64
    sum = 0
    aux.downto(2).to_a.zip(arr.each) { |i, j| sum += j * i }
65
    return (((sum * 10) % 11) % 10 == arr[aux - 12])
66 67
  end
end