cpf_cnpj_plus 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/CHANGELOG.md +45 -0
- data/README.md +60 -15
- data/exe/cnpj_plus +26 -0
- data/exe/cpf_plus +25 -0
- data/lib/cpf_cnpj_plus/active_model_validator.rb +38 -0
- data/lib/cpf_cnpj_plus/formatter/cnpj.rb +18 -0
- data/lib/cpf_cnpj_plus/formatter/cpf.rb +17 -0
- data/lib/cpf_cnpj_plus/formatter.rb +11 -0
- data/lib/cpf_cnpj_plus/generator/cnpj.rb +53 -0
- data/lib/cpf_cnpj_plus/generator/cpf.rb +29 -0
- data/lib/cpf_cnpj_plus/generator.rb +11 -0
- data/lib/cpf_cnpj_plus/validator/cnpj.rb +10 -13
- data/lib/cpf_cnpj_plus/validator/cpf.rb +10 -11
- data/lib/cpf_cnpj_plus/validator.rb +1 -1
- data/lib/cpf_cnpj_plus/version.rb +1 -1
- data/lib/cpf_cnpj_plus.rb +53 -10
- metadata +36 -13
- data/.byebug_history +0 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0be5c78bb01f594041dc6648af5e88c793a37909288d2e805ec4df415b1e80f3
|
|
4
|
+
data.tar.gz: c44eb35f3cd67072ba120f5c74960b295ba2abf8a902262743c2688f5ca8fad9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4437f396b413c092653cd6f1960d5686e392fdb1bc488bb9244b919443d3de9c992d4187ef88f29e88b939a8bc3a086f49d4f71ac53e95b3fa1d8711c97c8a00
|
|
7
|
+
data.tar.gz: 685071ce041e859639aa0cd3c6091133481c725234b419bd49f18e36761339bcd55455ce751ac01fdf12643f08e8bd7f44f9c3c3976dee0c5bb6986f298bab79
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.4.0] - 2026-08-01
|
|
4
|
+
|
|
5
|
+
- Adicionada API unificada que detecta CPF ou CNPJ pelo tamanho do
|
|
6
|
+
documento: `CpfCnpjPlus.valid?`, `CpfCnpjPlus.format` e
|
|
7
|
+
`CpfCnpjPlus.generate(:cpf | :cnpj, alphanumeric:)`.
|
|
8
|
+
- Adicionado `CpfCnpjPlus.alphanumeric_cnpj?`, que identifica se um CNPJ
|
|
9
|
+
válido já usa o novo padrão alfanumérico (útil para auditar bases de
|
|
10
|
+
dados antes da migração de julho de 2026).
|
|
11
|
+
- `format_cpf`/`format_cnpj` (e a nova `format`) agora aceitam a opção
|
|
12
|
+
`strict: true`, que retorna `nil` quando os dígitos verificadores ou a
|
|
13
|
+
estrutura do documento são inválidos, em vez de formatar um documento
|
|
14
|
+
incorreto silenciosamente.
|
|
15
|
+
- Adicionado validador opcional para ActiveModel/Rails
|
|
16
|
+
(`require "cpf_cnpj_plus/active_model_validator"`), permitindo
|
|
17
|
+
`validates :campo, cpf_cnpj: true` (ou `type: :cpf`/`type: :cnpj`) em
|
|
18
|
+
models. Não é carregado por padrão e não adiciona `activemodel` como
|
|
19
|
+
dependência de runtime.
|
|
20
|
+
- Testes adicionados para todas as features acima (100% de cobertura de
|
|
21
|
+
linhas).
|
|
22
|
+
|
|
23
|
+
## [0.3.1] - 2026-08-01
|
|
24
|
+
|
|
25
|
+
- Corrigido `exe/cnpj_plus --check`, que chamava um método inexistente
|
|
26
|
+
(`CpfCnpjPlus.valid?`) e quebrava com `NoMethodError`; agora usa
|
|
27
|
+
`valid_cnpj?`.
|
|
28
|
+
- Corrigida a rejeição de CNPJs alfanuméricos "conhecidamente inválidos":
|
|
29
|
+
antes só bases numéricas com todos os dígitos iguais eram rejeitadas;
|
|
30
|
+
agora qualquer base de 12 caracteres repetidos é rejeitada, incluindo
|
|
31
|
+
letras (ex.: `"AAAAAAAAAAAA45"`).
|
|
32
|
+
- Removida dependência morta de `byebug` nos specs do validator, que
|
|
33
|
+
quebrava `bundle exec rspec` em ambiente limpo (CI).
|
|
34
|
+
- Corrigido o workflow de CI, que disparava em push apenas na branch
|
|
35
|
+
`master` e nunca era executado em `main`.
|
|
36
|
+
- Corrigida a documentação do README, que citava um método
|
|
37
|
+
`CpfCnpjPlus.generate` inexistente (o correto é `generate_cpf`/`generate_cnpj`).
|
|
38
|
+
- Adicionados testes para os casos acima e para os executáveis
|
|
39
|
+
`cnpj_plus`/`cpf_plus`.
|
|
40
|
+
|
|
3
41
|
## [0.1.0] - 2025-07-06
|
|
4
42
|
|
|
5
43
|
- Initial release
|
|
@@ -9,5 +47,12 @@
|
|
|
9
47
|
|
|
10
48
|
- Adjustment to be able to create the two new generation and formatting methods
|
|
11
49
|
|
|
50
|
+
## [0.3.0] - 2026-04-27
|
|
51
|
+
|
|
52
|
+
- Added the `generate_cpf` method to generate valid CPF numbers.
|
|
53
|
+
- Added the `generate_cnpj` method to generate valid CNPJ numbers.
|
|
54
|
+
- Added the `format_cpf` method to format CPF numbers in the standard format (XXX.XXX.XXX-XX).
|
|
55
|
+
- Added the `format_cnpj` method to format CNPJ numbers in the standard format (XX.XXX.XXX/XXXX-XX).
|
|
56
|
+
- Updated the README.md file to include examples of the new generation and formatting methods.
|
|
12
57
|
|
|
13
58
|
|
data/README.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# CPF CNPJ Plus
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://rubygems.org/gems/cpf_cnpj_plus)
|
|
4
|
+
[](https://github.com/DougNeo/cpf_cnpj_plus/actions/workflows/main.yml)
|
|
5
|
+
[](LICENSE.txt)
|
|
6
|
+
|
|
7
|
+
**cpf_cnpj_plus** é uma biblioteca Ruby para **validar, gerar e formatar** números de CPF e CNPJ brasileiros. Funciona em **Ruby puro** (sem dependências de runtime), tem um **validador opcional para Rails/ActiveModel**, e oferece **ferramentas de linha de comando**.
|
|
4
8
|
|
|
5
9
|
---
|
|
6
10
|
|
|
@@ -40,22 +44,57 @@ A gem oferece uma API unificada para CPF e CNPJ. Exemplos:
|
|
|
40
44
|
```ruby
|
|
41
45
|
require "cpf_cnpj_plus"
|
|
42
46
|
|
|
43
|
-
CPF
|
|
47
|
+
# CPF
|
|
44
48
|
CpfCnpjPlus.valid_cpf?("123.456.789-09") # => true ou false
|
|
45
49
|
CpfCnpjPlus.format_cpf("12345678909") # => "123.456.789-09"
|
|
46
|
-
CpfCnpjPlus.
|
|
50
|
+
CpfCnpjPlus.generate_cpf # => Gera um CPF válido
|
|
47
51
|
|
|
48
|
-
CNPJ
|
|
49
|
-
CpfCnpjPlus.
|
|
52
|
+
# CNPJ
|
|
53
|
+
CpfCnpjPlus.valid_cnpj?("12.345.678/0001-95") # => true ou false
|
|
50
54
|
CpfCnpjPlus.format_cnpj("12345678000195") # => "12.345.678/0001-95"
|
|
51
|
-
CpfCnpjPlus.
|
|
55
|
+
CpfCnpjPlus.generate_cnpj # => Gera um CNPJ válido
|
|
56
|
+
|
|
57
|
+
# CNPJ Alfanumérico (novo formato)
|
|
58
|
+
CpfCnpjPlus.valid_cnpj?("12.ABC.345/01DE-35") # => true
|
|
59
|
+
CpfCnpjPlus.format_cnpj("12ABC34501DE35") # => "12.ABC.345/01DE-35"
|
|
60
|
+
CpfCnpjPlus.generate_cnpj(alphanumeric: true) # => Gera um CNPJ alfanumérico válido
|
|
61
|
+
CpfCnpjPlus.alphanumeric_cnpj?("12ABC34501DE35") # => true (identifica se já usa o novo padrão)
|
|
62
|
+
|
|
63
|
+
# API unificada (detecta CPF ou CNPJ pelo tamanho do documento)
|
|
64
|
+
CpfCnpjPlus.valid?("123.456.789-09") # => true ou false
|
|
65
|
+
CpfCnpjPlus.valid?("12.345.678/0001-95") # => true ou false
|
|
66
|
+
CpfCnpjPlus.format("12345678909") # => "123.456.789-09"
|
|
67
|
+
CpfCnpjPlus.format("12345678000195") # => "12.345.678/0001-95"
|
|
68
|
+
CpfCnpjPlus.generate(:cpf) # => Gera um CPF válido
|
|
69
|
+
CpfCnpjPlus.generate(:cnpj, alphanumeric: true) # => Gera um CNPJ alfanumérico válido
|
|
70
|
+
|
|
71
|
+
# Formatação estrita (retorna nil se os dígitos verificadores forem inválidos)
|
|
72
|
+
CpfCnpjPlus.format_cpf("12345678900", strict: true) # => nil (dígitos verificadores errados)
|
|
73
|
+
CpfCnpjPlus.format_cnpj("12345678000199", strict: true) # => nil (dígitos verificadores errados)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Validação em Rails/ActiveModel
|
|
77
|
+
|
|
78
|
+
A gem também traz um validador opcional para uso com `ActiveModel::Validations`
|
|
79
|
+
(inclusive em models Rails). Ele **não** é carregado junto com
|
|
80
|
+
`require "cpf_cnpj_plus"` — é preciso requerê-lo explicitamente, e `activemodel`
|
|
81
|
+
precisa estar disponível na sua aplicação:
|
|
82
|
+
|
|
83
|
+
```ruby
|
|
84
|
+
require "cpf_cnpj_plus/active_model_validator"
|
|
85
|
+
|
|
86
|
+
class Empresa
|
|
87
|
+
include ActiveModel::Model
|
|
88
|
+
attr_accessor :cnpj, :cpf
|
|
52
89
|
|
|
53
|
-
CNPJ
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
90
|
+
validates :cnpj, cpf_cnpj: { type: :cnpj } # aceita CNPJ numérico ou alfanumérico
|
|
91
|
+
validates :cpf, cpf_cnpj: { type: :cpf }
|
|
92
|
+
# ou, para detectar o tipo automaticamente pelo tamanho:
|
|
93
|
+
# validates :documento, cpf_cnpj: true
|
|
94
|
+
end
|
|
57
95
|
```
|
|
58
96
|
|
|
97
|
+
Suporta as opções padrão do Rails (`allow_nil`, `allow_blank`, `message`).
|
|
59
98
|
|
|
60
99
|
### Linha de comando
|
|
61
100
|
|
|
@@ -66,21 +105,27 @@ $ cpf_plus --check 123.456.789-09
|
|
|
66
105
|
$ cpf_plus --format 12345678909
|
|
67
106
|
$ cpf_plus --generate
|
|
68
107
|
$ cnpj_plus --check 12.345.678/0001-95
|
|
69
|
-
$ cnpj_plus --check
|
|
108
|
+
$ cnpj_plus --check 12.ABC.345/01DE-35
|
|
70
109
|
$ cnpj_plus --generate
|
|
71
110
|
```
|
|
72
111
|
|
|
73
112
|
## Compatibilidade
|
|
74
113
|
|
|
75
|
-
- Ruby >= 3.1.0
|
|
114
|
+
- Ruby >= 3.1.0 (testado em CI nas versões 3.1, 3.2, 3.3, 3.4 e 4.0).
|
|
115
|
+
- Sem dependências de runtime — funciona em qualquer projeto Ruby puro.
|
|
116
|
+
- Uso com Rails/ActiveModel é opcional: só é necessário se você requerer
|
|
117
|
+
`cpf_cnpj_plus/active_model_validator` (veja a seção "Validação em
|
|
118
|
+
Rails/ActiveModel" acima).
|
|
76
119
|
|
|
77
120
|
## Contribuição
|
|
78
121
|
|
|
79
122
|
1. Faça um fork do projeto
|
|
80
123
|
2. Crie sua branch (`git checkout -b minha-feature`)
|
|
81
|
-
3.
|
|
82
|
-
|
|
83
|
-
|
|
124
|
+
3. Rode `bundle install` e depois `bundle exec rake` (specs + rubocop) para
|
|
125
|
+
garantir que tudo continua passando
|
|
126
|
+
4. Commit suas alterações (`git commit -am 'Adiciona nova feature'`)
|
|
127
|
+
5. Faça push para a branch (`git push origin minha-feature`)
|
|
128
|
+
6. Abra um Pull Request
|
|
84
129
|
|
|
85
130
|
## Licença
|
|
86
131
|
|
data/exe/cnpj_plus
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "optparse"
|
|
5
|
+
require_relative "../lib/cpf_cnpj_plus"
|
|
6
|
+
|
|
7
|
+
options = {}
|
|
8
|
+
OptionParser.new do |opts|
|
|
9
|
+
opts.banner = "Uso: cnpj_plus [opções]"
|
|
10
|
+
opts.on("--check CNPJ", "Valida um CNPJ (numérico ou alfanumérico)") { |v| options[:check] = v }
|
|
11
|
+
opts.on("--format CNPJ", "Formata um CNPJ") { |v| options[:format] = v }
|
|
12
|
+
opts.on("--generate", "Gera um CNPJ numérico válido") { options[:generate] = true }
|
|
13
|
+
opts.on("--alphanumeric", "Gera um CNPJ alfanumérico (com --generate)") { options[:alphanumeric] = true }
|
|
14
|
+
end.parse!
|
|
15
|
+
|
|
16
|
+
if options[:check]
|
|
17
|
+
valid = CpfCnpjPlus.valid_cnpj?(options[:check])
|
|
18
|
+
puts valid ? "✅ CNPJ válido" : "❌ CNPJ inválido"
|
|
19
|
+
elsif options[:format]
|
|
20
|
+
result = CpfCnpjPlus.format_cnpj(options[:format])
|
|
21
|
+
puts result || "❌ CNPJ inválido para formatação"
|
|
22
|
+
elsif options[:generate]
|
|
23
|
+
puts CpfCnpjPlus.generate_cnpj(alphanumeric: options[:alphanumeric] || false)
|
|
24
|
+
else
|
|
25
|
+
puts "Use --help para ver as opções disponíveis."
|
|
26
|
+
end
|
data/exe/cpf_plus
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "optparse"
|
|
5
|
+
require_relative "../lib/cpf_cnpj_plus"
|
|
6
|
+
|
|
7
|
+
options = {}
|
|
8
|
+
OptionParser.new do |opts|
|
|
9
|
+
opts.banner = "Uso: cpf_plus [opções]"
|
|
10
|
+
opts.on("--check CPF", "Valida um CPF") { |v| options[:check] = v }
|
|
11
|
+
opts.on("--format CPF", "Formata um CPF") { |v| options[:format] = v }
|
|
12
|
+
opts.on("--generate", "Gera um CPF válido") { options[:generate] = true }
|
|
13
|
+
end.parse!
|
|
14
|
+
|
|
15
|
+
if options[:check]
|
|
16
|
+
valid = CpfCnpjPlus.valid_cpf?(options[:check])
|
|
17
|
+
puts valid ? "✅ CPF válido" : "❌ CPF inválido"
|
|
18
|
+
elsif options[:format]
|
|
19
|
+
result = CpfCnpjPlus.format_cpf(options[:format])
|
|
20
|
+
puts result || "❌ CPF inválido para formatação"
|
|
21
|
+
elsif options[:generate]
|
|
22
|
+
puts CpfCnpjPlus.generate_cpf
|
|
23
|
+
else
|
|
24
|
+
puts "Use --help para ver as opções disponíveis."
|
|
25
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_model"
|
|
4
|
+
require_relative "../cpf_cnpj_plus"
|
|
5
|
+
|
|
6
|
+
# Validador ActiveModel opcional. Não é carregado por padrão junto com a gem —
|
|
7
|
+
# quem quiser usar em um model Rails/ActiveModel deve fazer:
|
|
8
|
+
#
|
|
9
|
+
# require "cpf_cnpj_plus/active_model_validator"
|
|
10
|
+
#
|
|
11
|
+
# class Empresa
|
|
12
|
+
# include ActiveModel::Model
|
|
13
|
+
# attr_accessor :cnpj
|
|
14
|
+
#
|
|
15
|
+
# validates :cnpj, cpf_cnpj: true # detecta CPF ou CNPJ pelo tamanho
|
|
16
|
+
# validates :cnpj, cpf_cnpj: { type: :cnpj } # aceita só CNPJ (numérico ou alfanumérico)
|
|
17
|
+
# validates :cpf, cpf_cnpj: { type: :cpf } # aceita só CPF
|
|
18
|
+
# end
|
|
19
|
+
class CpfCnpjValidator < ActiveModel::EachValidator
|
|
20
|
+
def validate_each(record, attribute, value)
|
|
21
|
+
return if options[:allow_nil] && value.nil?
|
|
22
|
+
return if options[:allow_blank] && (value.nil? || value.to_s.strip.empty?)
|
|
23
|
+
|
|
24
|
+
record.errors.add(attribute, options.fetch(:message, :invalid)) unless valid_document?(value)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def valid_document?(value)
|
|
30
|
+
case options[:type]&.to_sym
|
|
31
|
+
when :cpf then CpfCnpjPlus.valid_cpf?(value)
|
|
32
|
+
when :cnpj then CpfCnpjPlus.valid_cnpj?(value)
|
|
33
|
+
when nil then CpfCnpjPlus.valid?(value)
|
|
34
|
+
else
|
|
35
|
+
raise ArgumentError, "type deve ser :cpf ou :cnpj (recebido: #{options[:type].inspect})"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CpfCnpjPlus
|
|
4
|
+
module Formatter
|
|
5
|
+
# Responsável por formatar números de CNPJ para exibição.
|
|
6
|
+
# Suporta o formato numérico tradicional e o novo formato alfanumérico,
|
|
7
|
+
# aplicando a máscara padrão XX.XXX.XXX/XXXX-XX.
|
|
8
|
+
class Cnpj
|
|
9
|
+
def self.format(cnpj, strict: false)
|
|
10
|
+
cleaned = cnpj.to_s.upcase.gsub(/[^A-Z0-9]/, "")
|
|
11
|
+
return nil unless cleaned.length == 14
|
|
12
|
+
return nil if strict && !Validator::Cnpj.valid?(cleaned)
|
|
13
|
+
|
|
14
|
+
"#{cleaned[0..1]}.#{cleaned[2..4]}.#{cleaned[5..7]}/#{cleaned[8..11]}-#{cleaned[12..13]}"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CpfCnpjPlus
|
|
4
|
+
module Formatter
|
|
5
|
+
# Responsável por formatar números de CPF para exibição.
|
|
6
|
+
# Aplica a máscara padrão XXX.XXX.XXX-XX a partir de uma string numérica.
|
|
7
|
+
class Cpf
|
|
8
|
+
def self.format(cpf, strict: false)
|
|
9
|
+
cleaned = cpf.to_s.gsub(/[^0-9]/, "")
|
|
10
|
+
return nil unless cleaned.length == 11
|
|
11
|
+
return nil if strict && !Validator::Cpf.valid?(cleaned)
|
|
12
|
+
|
|
13
|
+
"#{cleaned[0..2]}.#{cleaned[3..5]}.#{cleaned[6..8]}-#{cleaned[9..10]}"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CpfCnpjPlus
|
|
4
|
+
# Módulo responsável por agrupar formatadores de CPF e CNPJ.
|
|
5
|
+
# Fornece métodos e classes auxiliares para formatação de documentos brasileiros,
|
|
6
|
+
# incluindo suporte a formatos tradicionais e CNPJ alfanumérico.
|
|
7
|
+
module Formatter
|
|
8
|
+
require_relative "formatter/cnpj"
|
|
9
|
+
require_relative "formatter/cpf"
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CpfCnpjPlus
|
|
4
|
+
module Generator
|
|
5
|
+
# Responsável por gerar números de CNPJ válidos aleatoriamente.
|
|
6
|
+
# Suporta geração no formato numérico tradicional e no novo formato
|
|
7
|
+
# alfanumérico, calculando os dígitos verificadores conforme a Receita Federal.
|
|
8
|
+
class Cnpj
|
|
9
|
+
DIGITS = ("0".."9").to_a.freeze
|
|
10
|
+
LETTERS = ("A".."Z").to_a.freeze
|
|
11
|
+
ALPHANUMERIC = (DIGITS + LETTERS).freeze
|
|
12
|
+
|
|
13
|
+
WEIGHTS_1D = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2].freeze
|
|
14
|
+
WEIGHTS_2D = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2].freeze
|
|
15
|
+
|
|
16
|
+
def self.generate(alphanumeric: false)
|
|
17
|
+
loop do
|
|
18
|
+
base = build_base(alphanumeric)
|
|
19
|
+
cnpj = build_cnpj(base)
|
|
20
|
+
return cnpj unless Validator::Cnpj.cnpj_not_valid?(cnpj)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.build_base(alphanumeric)
|
|
25
|
+
if alphanumeric
|
|
26
|
+
# Garante ao menos uma letra para ser genuinamente alfanumérico
|
|
27
|
+
base = Array.new(12) { ALPHANUMERIC.sample }
|
|
28
|
+
base[rand(0..11)] = LETTERS.sample
|
|
29
|
+
base
|
|
30
|
+
else
|
|
31
|
+
Array.new(12) { DIGITS.sample }
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.build_cnpj(base)
|
|
36
|
+
values = base.map { |c| char_to_value(c) }
|
|
37
|
+
d1 = calculate_digit(values, WEIGHTS_1D)
|
|
38
|
+
d2 = calculate_digit(values + [d1], WEIGHTS_2D)
|
|
39
|
+
base.join + "#{d1}#{d2}"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.char_to_value(char)
|
|
43
|
+
char.ord - 48
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def self.calculate_digit(values, weights)
|
|
47
|
+
soma = weights.each_with_index.sum { |w, i| values[i] * w }
|
|
48
|
+
result = (soma * 10) % 11
|
|
49
|
+
result == 10 ? 0 : result
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CpfCnpjPlus
|
|
4
|
+
module Generator
|
|
5
|
+
# Responsável por gerar números de CPF válidos aleatoriamente.
|
|
6
|
+
# Calcula os dígitos verificadores conforme o algoritmo oficial da Receita Federal.
|
|
7
|
+
class Cpf
|
|
8
|
+
def self.generate
|
|
9
|
+
loop do
|
|
10
|
+
base = Array.new(9) { rand(0..9) }
|
|
11
|
+
cpf = build_cpf(base)
|
|
12
|
+
return cpf unless Validator::Cpf.cpf_not_valid?(cpf)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.build_cpf(base)
|
|
17
|
+
d1 = calculate_digit(base, (2..10).to_a.reverse)
|
|
18
|
+
d2 = calculate_digit(base + [d1], (2..11).to_a.reverse)
|
|
19
|
+
(base + [d1, d2]).join
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.calculate_digit(digits, weights)
|
|
23
|
+
soma = digits.each_with_index.sum { |d, i| d * weights[i] }
|
|
24
|
+
result = (soma * 10) % 11
|
|
25
|
+
result == 10 ? 0 : result
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CpfCnpjPlus
|
|
4
|
+
# Módulo responsável por agrupar geradores de CPF e CNPJ.
|
|
5
|
+
# Fornece métodos e classes auxiliares para geração de documentos brasileiros,
|
|
6
|
+
# incluindo suporte a formatos tradicionais e CNPJ alfanumérico.
|
|
7
|
+
module Generator
|
|
8
|
+
require_relative "generator/cnpj"
|
|
9
|
+
require_relative "generator/cpf"
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -8,7 +8,7 @@ module CpfCnpjPlus
|
|
|
8
8
|
def self.valid?(cnpj)
|
|
9
9
|
cnpj = normalize(cnpj)
|
|
10
10
|
return false unless valid_length?(cnpj)
|
|
11
|
-
return false if
|
|
11
|
+
return false if cnpj_not_valid?(cnpj)
|
|
12
12
|
return false unless valid_structure?(cnpj)
|
|
13
13
|
|
|
14
14
|
base = cnpj[0..11].chars.map { |c| char_to_value(c) }
|
|
@@ -30,13 +30,7 @@ module CpfCnpjPlus
|
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def self.char_to_value(char)
|
|
33
|
-
|
|
34
|
-
char.ord - 48
|
|
35
|
-
elsif ("A".."Z").include?(char)
|
|
36
|
-
char.ord - 48 # Ajuste para A=10, B=11, ..., Z=35
|
|
37
|
-
else
|
|
38
|
-
raise ArgumentError, "Caractere inválido"
|
|
39
|
-
end
|
|
33
|
+
char.ord - 48
|
|
40
34
|
end
|
|
41
35
|
|
|
42
36
|
def self.calculate_cnpj_digits(base)
|
|
@@ -63,11 +57,14 @@ module CpfCnpjPlus
|
|
|
63
57
|
digit
|
|
64
58
|
end
|
|
65
59
|
|
|
66
|
-
def self.
|
|
67
|
-
cnpj = cnpj
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
60
|
+
def self.alphanumeric?(cnpj)
|
|
61
|
+
cnpj = normalize(cnpj)
|
|
62
|
+
valid?(cnpj) && cnpj[0..11].match?(/[A-Z]/)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def self.cnpj_not_valid?(cnpj)
|
|
66
|
+
base = cnpj.to_s[0..11]
|
|
67
|
+
base.to_s.length == 12 && base.chars.uniq.length == 1
|
|
71
68
|
end
|
|
72
69
|
end
|
|
73
70
|
end
|
|
@@ -8,36 +8,35 @@ module CpfCnpjPlus
|
|
|
8
8
|
def self.valid?(cpf)
|
|
9
9
|
cpf = cpf.to_s.gsub(/[^0-9]/, "")
|
|
10
10
|
return false unless cpf.length == 11 && cpf =~ /^\d{11}$/
|
|
11
|
-
return false if
|
|
11
|
+
return false if cpf_not_valid?(cpf)
|
|
12
12
|
|
|
13
13
|
first_digit(cpf) && second_digit(cpf)
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def self.first_digit(cpf)
|
|
17
|
-
|
|
17
|
+
base_digits = cpf[0..8]
|
|
18
18
|
soma = 0
|
|
19
19
|
ponteiro = 10
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
soma += res
|
|
20
|
+
base_digits.chars.each do |char|
|
|
21
|
+
soma += char.to_i * ponteiro
|
|
23
22
|
ponteiro -= 1
|
|
24
23
|
end
|
|
25
|
-
soma * 10 % 11
|
|
24
|
+
result = soma * 10 % 11
|
|
25
|
+
result == cpf[9].to_i || (result == 10 && cpf[9].to_i.zero?)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def self.second_digit(cpf)
|
|
29
|
-
|
|
29
|
+
base_digits = cpf[0..9]
|
|
30
30
|
soma = 0
|
|
31
31
|
ponteiro = 11
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
soma += res
|
|
32
|
+
base_digits.chars.each do |char|
|
|
33
|
+
soma += char.to_i * ponteiro
|
|
35
34
|
ponteiro -= 1
|
|
36
35
|
end
|
|
37
36
|
soma * 10 % 11 == cpf[10].to_i || (soma * 10 % 11 == 10 && cpf[10].to_i.zero?)
|
|
38
37
|
end
|
|
39
38
|
|
|
40
|
-
def self.
|
|
39
|
+
def self.cpf_not_valid?(cpf)
|
|
41
40
|
cpf = cpf.to_s.gsub(/[^0-9]/, "")
|
|
42
41
|
%w[00000000000 11111111111 22222222222 33333333333
|
|
43
42
|
44444444444 55555555555 66666666666
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module CpfCnpjPlus
|
|
4
4
|
# Módulo responsável por agrupar validadores de CPF e CNPJ.
|
|
5
5
|
# Fornece métodos e classes auxiliares para validação de documentos brasileiros,
|
|
6
|
-
# incluindo suporte a formatos tradicionais e
|
|
6
|
+
# incluindo suporte a formatos tradicionais e CNPJ alfanumérico.
|
|
7
7
|
module Validator
|
|
8
8
|
require_relative "validator/cnpj"
|
|
9
9
|
require_relative "validator/cpf"
|
data/lib/cpf_cnpj_plus.rb
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "cpf_cnpj_plus/version"
|
|
4
4
|
require_relative "cpf_cnpj_plus/validator"
|
|
5
|
+
require_relative "cpf_cnpj_plus/formatter"
|
|
6
|
+
require_relative "cpf_cnpj_plus/generator"
|
|
5
7
|
|
|
6
8
|
# Módulo principal da gem cpf_cnpj_plus.
|
|
7
9
|
# Fornece métodos para validação, geração e formatação de CPFs e CNPJs,
|
|
@@ -9,27 +11,68 @@ require_relative "cpf_cnpj_plus/validator"
|
|
|
9
11
|
module CpfCnpjPlus
|
|
10
12
|
class Error < StandardError; end
|
|
11
13
|
|
|
14
|
+
# CPF
|
|
15
|
+
def self.valid_cpf?(cpf)
|
|
16
|
+
CpfCnpjPlus::Validator::Cpf.valid?(cpf)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.format_cpf(cpf, strict: false)
|
|
20
|
+
CpfCnpjPlus::Formatter::Cpf.format(cpf, strict: strict)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.generate_cpf
|
|
24
|
+
CpfCnpjPlus::Generator::Cpf.generate
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# CNPJ
|
|
28
|
+
|
|
12
29
|
def self.valid_cnpj?(cnpj)
|
|
13
30
|
CpfCnpjPlus::Validator::Cnpj.valid?(cnpj)
|
|
14
31
|
end
|
|
15
32
|
|
|
16
|
-
def self.
|
|
17
|
-
CpfCnpjPlus::
|
|
33
|
+
def self.format_cnpj(cnpj, strict: false)
|
|
34
|
+
CpfCnpjPlus::Formatter::Cnpj.format(cnpj, strict: strict)
|
|
18
35
|
end
|
|
19
36
|
|
|
20
|
-
def self.
|
|
21
|
-
CpfCnpjPlus::
|
|
37
|
+
def self.generate_cnpj(alphanumeric: false)
|
|
38
|
+
CpfCnpjPlus::Generator::Cnpj.generate(alphanumeric: alphanumeric)
|
|
22
39
|
end
|
|
23
40
|
|
|
24
|
-
def self.
|
|
25
|
-
CpfCnpjPlus::
|
|
41
|
+
def self.alphanumeric_cnpj?(cnpj)
|
|
42
|
+
CpfCnpjPlus::Validator::Cnpj.alphanumeric?(cnpj)
|
|
26
43
|
end
|
|
27
44
|
|
|
28
|
-
|
|
29
|
-
|
|
45
|
+
# API unificada (detecta CPF ou CNPJ pelo tamanho do documento)
|
|
46
|
+
|
|
47
|
+
def self.valid?(doc)
|
|
48
|
+
case document_type(doc)
|
|
49
|
+
when :cpf then valid_cpf?(doc)
|
|
50
|
+
when :cnpj then valid_cnpj?(doc)
|
|
51
|
+
else false
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def self.format(doc, strict: false)
|
|
56
|
+
case document_type(doc)
|
|
57
|
+
when :cpf then format_cpf(doc, strict: strict)
|
|
58
|
+
when :cnpj then format_cnpj(doc, strict: strict)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def self.generate(type, alphanumeric: false)
|
|
63
|
+
case type.to_s.downcase
|
|
64
|
+
when "cpf" then generate_cpf
|
|
65
|
+
when "cnpj" then generate_cnpj(alphanumeric: alphanumeric)
|
|
66
|
+
else
|
|
67
|
+
raise ArgumentError, "type deve ser :cpf ou :cnpj (recebido: #{type.inspect})"
|
|
68
|
+
end
|
|
30
69
|
end
|
|
31
70
|
|
|
32
|
-
def self.
|
|
33
|
-
|
|
71
|
+
def self.document_type(doc)
|
|
72
|
+
case doc.to_s.upcase.gsub(/[^A-Z0-9]/, "").length
|
|
73
|
+
when 11 then :cpf
|
|
74
|
+
when 14 then :cnpj
|
|
75
|
+
end
|
|
34
76
|
end
|
|
77
|
+
private_class_method :document_type
|
|
35
78
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,28 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: cpf_cnpj_plus
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- DougNeo
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: activemodel
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :development
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0'
|
|
12
26
|
- !ruby/object:Gem::Dependency
|
|
13
27
|
name: simplecov
|
|
14
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -23,17 +37,17 @@ dependencies:
|
|
|
23
37
|
- - ">="
|
|
24
38
|
- !ruby/object:Gem::Version
|
|
25
39
|
version: '0'
|
|
26
|
-
description:
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
governamentais e aplicações Ruby em geral.
|
|
40
|
+
description: Fornece métodos práticos para validar, gerar e formatar números de CPF
|
|
41
|
+
e CNPJ (incluindo o novo CNPJ alfanumérico), com API para Ruby puro, validador opcional
|
|
42
|
+
para Rails/ActiveModel e ferramentas de linha de comando.
|
|
30
43
|
email:
|
|
31
44
|
- doug.neo@gmail.com
|
|
32
|
-
executables:
|
|
45
|
+
executables:
|
|
46
|
+
- cnpj_plus
|
|
47
|
+
- cpf_plus
|
|
33
48
|
extensions: []
|
|
34
49
|
extra_rdoc_files: []
|
|
35
50
|
files:
|
|
36
|
-
- ".byebug_history"
|
|
37
51
|
- ".rspec"
|
|
38
52
|
- ".rubocop.yml"
|
|
39
53
|
- CHANGELOG.md
|
|
@@ -41,20 +55,29 @@ files:
|
|
|
41
55
|
- LICENSE.txt
|
|
42
56
|
- README.md
|
|
43
57
|
- Rakefile
|
|
58
|
+
- exe/cnpj_plus
|
|
59
|
+
- exe/cpf_plus
|
|
44
60
|
- lib/cpf_cnpj_plus.rb
|
|
61
|
+
- lib/cpf_cnpj_plus/active_model_validator.rb
|
|
62
|
+
- lib/cpf_cnpj_plus/formatter.rb
|
|
63
|
+
- lib/cpf_cnpj_plus/formatter/cnpj.rb
|
|
64
|
+
- lib/cpf_cnpj_plus/formatter/cpf.rb
|
|
65
|
+
- lib/cpf_cnpj_plus/generator.rb
|
|
66
|
+
- lib/cpf_cnpj_plus/generator/cnpj.rb
|
|
67
|
+
- lib/cpf_cnpj_plus/generator/cpf.rb
|
|
45
68
|
- lib/cpf_cnpj_plus/validator.rb
|
|
46
69
|
- lib/cpf_cnpj_plus/validator/cnpj.rb
|
|
47
70
|
- lib/cpf_cnpj_plus/validator/cpf.rb
|
|
48
71
|
- lib/cpf_cnpj_plus/version.rb
|
|
49
72
|
- sig/cpf_cnpj_plus.rbs
|
|
50
|
-
homepage: https://github.com/DougNeo/cpf_cnpj_plus
|
|
73
|
+
homepage: https://github.com/DougNeo/cpf_cnpj_plus
|
|
51
74
|
licenses:
|
|
52
75
|
- MIT
|
|
53
76
|
metadata:
|
|
54
77
|
allowed_push_host: https://rubygems.org
|
|
55
|
-
|
|
56
|
-
source_code_uri: https://github.com/DougNeo/cpf_cnpj_plus
|
|
57
|
-
changelog_uri: https://github.com/DougNeo/cpf_cnpj_plus
|
|
78
|
+
rubygems_mfa_required: 'true'
|
|
79
|
+
source_code_uri: https://github.com/DougNeo/cpf_cnpj_plus
|
|
80
|
+
changelog_uri: https://github.com/DougNeo/cpf_cnpj_plus/blob/main/CHANGELOG.md
|
|
58
81
|
rdoc_options: []
|
|
59
82
|
require_paths:
|
|
60
83
|
- lib
|
|
@@ -69,7 +92,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
69
92
|
- !ruby/object:Gem::Version
|
|
70
93
|
version: '0'
|
|
71
94
|
requirements: []
|
|
72
|
-
rubygems_version:
|
|
95
|
+
rubygems_version: 4.0.6
|
|
73
96
|
specification_version: 4
|
|
74
97
|
summary: Validação, geração e formatação de números de CPF e CNPJ para o Brasil.
|
|
75
98
|
test_files: []
|
data/.byebug_history
DELETED