Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvements and Corrections: separated validations for CPF and CNPJ, added dependencies, updated tests to Rspec3, make validation keep original value, value normalization after validation & some bug fixes. #9

Open
wants to merge 30 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
9283a0b
updates specs
Dec 1, 2014
f6ae9cb
makes validation non-destructive
Dec 1, 2014
19ce3a1
increases version number
Dec 1, 2014
a75198d
Merge pull request #1 from wendelscardua/dev
wendelscardua Dec 1, 2014
263559f
avoid changing original value in cnpj validation
joaohornburg May 13, 2015
b7def31
avoid changing original cpf in validation
joaohornburg May 13, 2015
62d1b49
fix(dependencies): add all dependencies
dhyegocalota Sep 18, 2015
cf65bad
fix(cpf, cnpj): validations must work isolatedly #6
dhyegocalota Sep 18, 2015
6e9fb61
chore(version): bump it
dhyegocalota Sep 18, 2015
a439086
CPF and CNPJ normalization
Phelipe-truckpad Oct 7, 2015
0f89a98
Normalize to single validation
Phelipe-truckpad Oct 7, 2015
ec103fd
Fixes
Phelipe-truckpad Oct 7, 2015
c39d3bc
Merge pull request #1 from truckpad/master
dhyegocalota Oct 8, 2015
a40fedd
chore(version): bump it
dhyegocalota Oct 8, 2015
f20fe94
Added validation for cnpj values with all equal characters.
MauricioMoraes Nov 11, 2015
ff1005b
Added the test for CNPJs with all equal chars.
MauricioMoraes Nov 11, 2015
eb5728c
Regexp correction. Missing slashes.
MauricioMoraes Nov 11, 2015
bd23e95
Some refactoring and added custom error message attribute (:message).
MauricioMoraes Nov 13, 2015
78432ca
Added :message option to validation. Added test.
MauricioMoraes Nov 13, 2015
cc8cc88
Validation is non-destructive: doesn't change original values of CPF …
MauricioMoraes Nov 13, 2015
ff61100
Corrected merge conflicts. Merely synthax.
MauricioMoraes Nov 13, 2015
cce65cb
Merge branch 'wendelscardua-master'
MauricioMoraes Nov 13, 2015
1d79e53
Merge with Patch1
MauricioMoraes Nov 13, 2015
cbea2b0
Merge branch 'joaohornburg-patch-1'
MauricioMoraes Nov 13, 2015
ec6a5c2
Resolving merge conflicts.
MauricioMoraes Nov 13, 2015
da6b35e
Merge branch 'dhyegofernando-master'
MauricioMoraes Nov 13, 2015
1b28565
Remove sqlite3 from gemfile. It is already on development dependencies.
MauricioMoraes Nov 13, 2015
3ea4f97
Corrected CPF and CNPJ formatting. Valid values are formatted on vali…
MauricioMoraes Nov 17, 2015
7f23ad3
Bump.
MauricioMoraes Nov 17, 2015
f253069
Added test for partially formatted value. Corrected some rspec3 synth…
MauricioMoraes Nov 24, 2015
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.rdoc
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ Regular validation options:
:if - Executes validation when :if evaluates true
:unless - Executes validation when :unless evaluates false
:on - Specifies validation context (e.g :save, :create or :update). Default is :save
:message - Sets a custom message to be passed to the attribute (default is 'is invalid')

CPF and CNPJ formatting:

When you run the validation on an attribute, if the value is a valid CPF or CNPJ, it will be formatted as so:
- valid CPF will be returned as 999.999.999-99
- valid CNPJ will be returned as 99.999.999/9999-99


== Contributing

Expand Down
83 changes: 72 additions & 11 deletions lib/validates_cpf_cnpj.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,88 @@ module Validations
class CpfOrCnpjValidator < ActiveModel::EachValidator
include ValidatesCpfCnpj

ELEVEN_DIGITS_REGEXP = /\A\d{11}\z/
FOURTEEN_DIGITS_REGEXP = /\A\d{14}\z/
CPF_FORMAT_REGEXP = /\A\d{3}\.\d{3}\.\d{3}\-\d{2}\z/
CNPJ_FORMAT_REGEXP = /\A\d{2}\.\d{3}\.\d{3}\/\d{4}\-\d{2}\z/
NON_DIGITS_REGEXP = /[^0-9]/

def validate_each(record, attr_name, value)
return if (options[:allow_nil] and value.nil?) or (options[:allow_blank] and value.blank?)
return if (options[:if] == false) or (options[:unless] == true)
return if (options[:on].to_s == 'create' and not record.new_record?) or (options[:on].to_s == 'update' and record.new_record?)

if value.to_s.gsub(/[^0-9]/, '').length <= 11
if (not value.to_s.match(/\A\d{11}\z/) and not value.to_s.match(/\A\d{3}\.\d{3}\.\d{3}\-\d{2}\z/)) or not Cpf.valid?(value)
record.errors.add(attr_name)
end
return if should_skip?(record, attr_name, value)

inside_cpf_length = value.to_s.gsub(NON_DIGITS_REGEXP, '').length <= 11

if inside_cpf_length
validate_only_cpf(record, attr_name, value)
else
validate_only_cnpj(record, attr_name, value)
end
end

protected

def should_skip?(record, attr_name, value)
return true if (options[:allow_nil] && value.nil?) || (options[:allow_blank] && value.blank?)
return true if (options[:if] == false) || (options[:unless] == true)
return true if (options[:on].to_s == 'create' && !record.new_record?) || (options[:on].to_s == 'update' && record.new_record?)
false
end

def validate_only_cpf(record, attr_name, value)
return if should_skip?(record, attr_name, value)
valid_cpf_format = value.to_s.match(ELEVEN_DIGITS_REGEXP) || value.to_s.match(CPF_FORMAT_REGEXP)

if (valid_cpf_format && Cpf.valid?(value))
format_cpf(record, attr_name, value)
else
add_error(record, attr_name)
end
end

def validate_only_cnpj(record, attr_name, value)
return if should_skip?(record, attr_name, value)
valid_cnpj_format = value.to_s.match(FOURTEEN_DIGITS_REGEXP) || value.to_s.match(CNPJ_FORMAT_REGEXP)

if (valid_cnpj_format && Cnpj.valid?(value))
format_cnpj(record, attr_name, value)
else
if (not value.to_s.match(/\A\d{14}\z/) and not value.to_s.match(/\A\d{2}\.\d{3}\.\d{3}\/\d{4}\-\d{2}\z/)) or not Cnpj.valid?(value)
record.errors.add(attr_name)
end
add_error(record, attr_name)
end
end

def add_error(record, attr_name)
if options[:message]
record.errors.add(attr_name, options[:message])
else
record.errors.add(attr_name)
end
end

def format_cpf(record, attr_name, value)
return unless value
value.gsub!(NON_DIGITS_REGEXP, '')
formatted_value = "#{value[0..2]}.#{value[3..5]}.#{value[6..8]}-#{value[9..10]}"
record.send("#{attr_name}=", formatted_value)
end

def format_cnpj(record, attr_name, value)
return unless value
value.gsub!(NON_DIGITS_REGEXP, '')
formatted_value = "#{value[0..1]}.#{value[2..4]}.#{value[5..7]}/#{value[8..11]}-#{value[12..13]}"
record.send("#{attr_name}=", formatted_value)
end
end

class CpfValidator < CpfOrCnpjValidator
def validate_each(record, attr_name, value)
validate_only_cpf(record, attr_name, value)
end
end

class CnpjValidator < CpfOrCnpjValidator
def validate_each(record, attr_name, value)
validate_only_cnpj(record, attr_name, value)
end
end

module HelperMethods
Expand Down
14 changes: 10 additions & 4 deletions lib/validates_cpf_cnpj/cnpj.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
module ValidatesCpfCnpj
module Cnpj
NON_DIGITS_REGEXP = /[^0-9]/
FOURTEEN_PLUS_EQUAL_CHARACTERS_REGEXP = /\A(\d)\1{13,}\z/

def self.valid?(value)
value.gsub!(/[^0-9]/, '')
digit = value.slice(-2, 2)
value_only_digits = value.gsub(NON_DIGITS_REGEXP, '')

return false if value_only_digits =~ FOURTEEN_PLUS_EQUAL_CHARACTERS_REGEXP

digit = value_only_digits.slice(-2, 2)
control = ''
if value.size == 14
if value_only_digits.size == 14
factor = 0
2.times do |i|
sum = 0;
12.times do |j|
sum += value.slice(j, 1).to_i * ((11 + i - j) % 8 + 2)
sum += value_only_digits.slice(j, 1).to_i * ((11 + i - j) % 8 + 2)
end
sum += factor * 2 if i == 1
factor = 11 - sum % 11
Expand Down
13 changes: 7 additions & 6 deletions lib/validates_cpf_cnpj/cpf.rb
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
module ValidatesCpfCnpj
module Cpf
@@invalid_cpfs = %w{12345678909 11111111111 22222222222 33333333333 44444444444 55555555555 66666666666 77777777777 88888888888 99999999999 00000000000}
NON_DIGITS_REGEXP = /[^0-9]/

def self.valid?(value)
value.gsub!(/[^0-9]/, '')
value_only_digits = value.gsub(NON_DIGITS_REGEXP, '')

return false if @@invalid_cpfs.member?(value)
digit = value.slice(-2, 2)
return false if @@invalid_cpfs.member?(value_only_digits)

digit = value_only_digits.slice(-2, 2)
control = ''
if value.size == 11
if value_only_digits.size == 11
factor = 0
2.times do |i|
sum = 0
9.times do |j|
sum += value.slice(j, 1).to_i * (10 + i - j)
sum += value_only_digits.slice(j, 1).to_i * (10 + i - j)
end
sum += (factor * 2) if i == 1
factor = (sum * 10) % 11
Expand Down
2 changes: 1 addition & 1 deletion lib/validates_cpf_cnpj/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module ValidatesCpfCnpj
VERSION = "0.2.0"
VERSION = "1.0.1"
end
Loading