br_validators 0.3.0 → 0.3.1
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/CHANGELOG.md +23 -0
- data/README.md +5 -2
- data/lib/br_validators/boleto.rb +20 -20
- data/lib/br_validators/cep.rb +9 -4
- data/lib/br_validators/chave_acesso.rb +14 -13
- data/lib/br_validators/cnpj.rb +17 -20
- data/lib/br_validators/cns.rb +17 -12
- data/lib/br_validators/consulta.rb +3 -2
- data/lib/br_validators/cpf.rb +18 -13
- data/lib/br_validators/digitos.rb +27 -0
- data/lib/br_validators/documentos.rb +5 -0
- data/lib/br_validators/placa.rb +17 -10
- data/lib/br_validators/processo_cnj.rb +16 -9
- data/lib/br_validators/renavam.rb +13 -13
- data/lib/br_validators/telefone.rb +16 -12
- data/lib/br_validators/titulo_eleitor.rb +22 -15
- data/lib/br_validators/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7c36775e47d48c4dadc9fae27db78abb372bacdd85cf58ac586d3c005ddc29ae
|
|
4
|
+
data.tar.gz: 5b0a94be147182c7773dd201194041810b06cc4c0a6e784392dee72233dc4797
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b88cefcb1d52ba0f54b77f6a808b9f962449f4fbc636c3491e9502c48747e1dd205e23887bcc126c7661bc48096beabc13ccab9d75695ec1b0bb033f091ceec1
|
|
7
|
+
data.tar.gz: c04d5712613468e91dfe37ad9669634c5ec6991a4e2c36e9eaff43b8b3d21958612cac46cfd93bc8b6eccffbec885511132a601ec91c878bb9c7931542764a8e
|
data/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,29 @@
|
|
|
3
3
|
As mudanças de cada versão ficam aqui. O projeto segue o
|
|
4
4
|
[versionamento semântico](https://semver.org/lang/pt-BR/).
|
|
5
5
|
|
|
6
|
+
## [0.3.1] - 2026-10-31
|
|
7
|
+
|
|
8
|
+
### Corrigido
|
|
9
|
+
|
|
10
|
+
- `Consulta.banco` e `Consulta.ddd` aceitam número e texto em qualquer encoding:
|
|
11
|
+
`Consulta.banco("341".encode("UTF-16LE"))` levantava `Encoding::CompatibilityError`.
|
|
12
|
+
|
|
13
|
+
### Mudado
|
|
14
|
+
|
|
15
|
+
- Mais rápido, com os mesmos resultados (conferido em 72 mil entradas contra a 0.3.0):
|
|
16
|
+
`format`, `uf`, `ddd`, `segmento` e `modelo` tiram a máscara uma vez só, e as contas
|
|
17
|
+
de dígito verificador não criam uma lista por caractere. `CPF.format` ficou cerca de
|
|
18
|
+
3 vezes mais rápido; a chave de acesso e o boleto, cerca de 2 vezes.
|
|
19
|
+
- A conta de módulo 11 com pesos de 2 a 9, que estava repetida no CNPJ, no RENAVAM, na
|
|
20
|
+
chave de acesso e no boleto, ficou num lugar só (`digitos.rb`, interno da gem).
|
|
21
|
+
|
|
22
|
+
### Outros
|
|
23
|
+
|
|
24
|
+
- Página de teste com visual novo, de documento: fonte datilografada, a trama dos papéis
|
|
25
|
+
de segurança no topo, o formulário como uma ficha e o resultado como um carimbo. O
|
|
26
|
+
campo mostra o modelo do formato, o resultado aparece na tela no celular e a página
|
|
27
|
+
tem imagem para quando o link é compartilhado.
|
|
28
|
+
|
|
6
29
|
## [0.3.0] - 2026-10-31
|
|
7
30
|
|
|
8
31
|
### Novo
|
data/README.md
CHANGED
|
@@ -189,8 +189,11 @@ pode abrir direto num documento: `?documento=placa`, `?documento=boleto`... A p
|
|
|
189
189
|
mostra o resultado, explica por que um número é inválido e monta o trecho de código
|
|
190
190
|
Ruby que faz o mesmo.
|
|
191
191
|
|
|
192
|
-
A página (`public/`) é HTML e JavaScript puro e chama uma função Ruby
|
|
193
|
-
(`api/validar.rb`) que usa a própria gem, então as regras ficam num lugar s
|
|
192
|
+
A página (`public/`) é HTML, CSS e JavaScript puro, sem build, e chama uma função Ruby
|
|
193
|
+
da Vercel (`api/validar.rb`) que usa a própria gem, então as regras ficam num lugar só.
|
|
194
|
+
O visual imita um documento brasileiro; a fonte datilografada é a
|
|
195
|
+
[Courier Prime](https://github.com/quoteunquoteapps/CourierPrime) (licença OFL, em
|
|
196
|
+
`public/fontes`).
|
|
194
197
|
|
|
195
198
|
```
|
|
196
199
|
GET /api/validar?documento=cnpj&valor=12.ABC.345/01DE-35
|
data/lib/br_validators/boleto.rb
CHANGED
|
@@ -30,11 +30,7 @@ module BrValidators
|
|
|
30
30
|
|
|
31
31
|
# :cobranca, :arrecadacao ou nil se a linha for inválida.
|
|
32
32
|
def self.type(value)
|
|
33
|
-
|
|
34
|
-
return :cobranca if line.match?(/\A\d{47}\z/) && valid_cobranca?(line)
|
|
35
|
-
return :arrecadacao if line.match?(/\A8\d{47}\z/) && valid_arrecadacao?(line)
|
|
36
|
-
|
|
37
|
-
nil
|
|
33
|
+
type_stripped(strip(value))
|
|
38
34
|
end
|
|
39
35
|
|
|
40
36
|
def self.valid?(value)
|
|
@@ -46,12 +42,19 @@ module BrValidators
|
|
|
46
42
|
# arrecadação: "82670000001-9 00000000000-0 00000000000-0 00000000000-0"
|
|
47
43
|
def self.format(value)
|
|
48
44
|
line = strip(value)
|
|
49
|
-
case
|
|
45
|
+
case type_stripped(line)
|
|
50
46
|
when :cobranca then format_cobranca(line)
|
|
51
47
|
when :arrecadacao then line.scan(/\d{12}/).map { |block| "#{block[0, 11]}-#{block[11]}" }.join(" ")
|
|
52
48
|
end
|
|
53
49
|
end
|
|
54
50
|
|
|
51
|
+
# A regra, com a linha já sem máscara.
|
|
52
|
+
def self.type_stripped(line)
|
|
53
|
+
return :cobranca if line.match?(/\A\d{47}\z/) && valid_cobranca?(line)
|
|
54
|
+
|
|
55
|
+
:arrecadacao if line.match?(/\A8\d{47}\z/) && valid_arrecadacao?(line)
|
|
56
|
+
end
|
|
57
|
+
|
|
55
58
|
def self.format_cobranca(line)
|
|
56
59
|
"#{line[0, 5]}.#{line[5, 5]} #{line[10, 5]}.#{line[15, 6]} " \
|
|
57
60
|
"#{line[21, 5]}.#{line[26, 6]} #{line[32]} #{line[33, 14]}"
|
|
@@ -102,33 +105,30 @@ module BrValidators
|
|
|
102
105
|
|
|
103
106
|
# Pesos 2, 1, 2, 1... da direita para a esquerda; produtos com dois algarismos
|
|
104
107
|
# entram somados (12 vira 1 + 2). O dígito é o que falta para a próxima dezena.
|
|
108
|
+
# getbyte - 48 é o valor do dígito (explicado em digitos.rb).
|
|
105
109
|
def self.mod10(digits)
|
|
106
|
-
|
|
107
|
-
|
|
110
|
+
last = digits.size - 1
|
|
111
|
+
sum = (0..last).sum do |index|
|
|
112
|
+
product = (digits.getbyte(last - index) - 48) * (index.even? ? 2 : 1)
|
|
108
113
|
(product / 10) + (product % 10)
|
|
109
114
|
end
|
|
110
115
|
(10 - (sum % 10)) % 10
|
|
111
116
|
end
|
|
112
117
|
|
|
113
|
-
#
|
|
114
|
-
|
|
115
|
-
digits.reverse.chars.each_with_index.sum { |digit, index| digit.to_i * ((index % 8) + 2) }
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
# Na cobrança, quando a conta dá 0, 10 ou 11, o dígito vira 1 (nunca é 0).
|
|
118
|
+
# Na cobrança, a soma é a do módulo 11 com pesos de 2 a 9 (Digitos, em
|
|
119
|
+
# digitos.rb), mas quando a conta dá 0, 10 ou 11 o dígito vira 1 (nunca é 0).
|
|
119
120
|
def self.mod11_cobranca(digits)
|
|
120
|
-
digit = 11 - (
|
|
121
|
+
digit = 11 - (Digitos.soma_modulo11(digits) % 11)
|
|
121
122
|
[0, 10, 11].include?(digit) ? 1 : digit
|
|
122
123
|
end
|
|
123
124
|
|
|
124
|
-
# Na arrecadação, resto 0 ou 1 vira dígito 0.
|
|
125
|
+
# Na arrecadação, é o módulo 11 comum: resto 0 ou 1 vira dígito 0.
|
|
125
126
|
def self.mod11_arrecadacao(digits)
|
|
126
|
-
|
|
127
|
-
rest < 2 ? 0 : 11 - rest
|
|
127
|
+
Digitos.modulo11(digits)
|
|
128
128
|
end
|
|
129
129
|
|
|
130
|
-
private_class_method :format_cobranca, :valid_cobranca?, :cobranca_barcode,
|
|
131
|
-
:arrecadacao_check, :check_digit_ok?, :without, :mod10,
|
|
130
|
+
private_class_method :type_stripped, :format_cobranca, :valid_cobranca?, :cobranca_barcode,
|
|
131
|
+
:valid_arrecadacao?, :arrecadacao_check, :check_digit_ok?, :without, :mod10,
|
|
132
132
|
:mod11_cobranca, :mod11_arrecadacao
|
|
133
133
|
end
|
|
134
134
|
end
|
data/lib/br_validators/cep.rb
CHANGED
|
@@ -24,16 +24,21 @@ module BrValidators
|
|
|
24
24
|
# 8 dígitos. "00000-000" é o CEP que sistemas usam como "não informado";
|
|
25
25
|
# nenhuma região dos Correios começa com 00000.
|
|
26
26
|
def self.valid?(value)
|
|
27
|
-
|
|
28
|
-
cep.match?(/\A\d{8}\z/) && cep != "00000000"
|
|
27
|
+
valid_stripped?(strip(value))
|
|
29
28
|
end
|
|
30
29
|
|
|
31
30
|
# Devolve "01001-000", ou nil se o CEP for inválido.
|
|
32
31
|
def self.format(value)
|
|
33
|
-
return nil unless valid?(value)
|
|
34
|
-
|
|
35
32
|
cep = strip(value)
|
|
33
|
+
return nil unless valid_stripped?(cep)
|
|
34
|
+
|
|
36
35
|
"#{cep[0, 5]}-#{cep[5, 3]}"
|
|
37
36
|
end
|
|
37
|
+
|
|
38
|
+
def self.valid_stripped?(cep)
|
|
39
|
+
cep.match?(/\A\d{8}\z/) && cep != "00000000"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private_class_method :valid_stripped?
|
|
38
43
|
end
|
|
39
44
|
end
|
|
@@ -48,31 +48,32 @@ module BrValidators
|
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
def self.valid?(value)
|
|
51
|
-
|
|
52
|
-
return false unless chave.match?(PATTERN)
|
|
53
|
-
return false unless UFS_IBGE.include?(chave[0, 2]) && chave[4, 2].between?("01", "12")
|
|
54
|
-
|
|
55
|
-
chave[43] == check_digit(chave[0, 43])
|
|
51
|
+
valid_stripped?(strip(value))
|
|
56
52
|
end
|
|
57
53
|
|
|
58
54
|
# Devolve a chave em 11 grupos de 4 ("3526 1011 2223 ..."), ou nil se for inválida.
|
|
59
55
|
def self.format(value)
|
|
60
|
-
|
|
56
|
+
chave = strip(value)
|
|
57
|
+
return nil unless valid_stripped?(chave)
|
|
61
58
|
|
|
62
|
-
|
|
59
|
+
chave.scan(/.{4}/).join(" ")
|
|
63
60
|
end
|
|
64
61
|
|
|
65
62
|
# O código do modelo ("55" para NF-e, "65" para NFC-e...), ou nil se for inválida.
|
|
66
63
|
def self.modelo(value)
|
|
67
|
-
|
|
64
|
+
chave = strip(value)
|
|
65
|
+
valid_stripped?(chave) ? chave[20, 2] : nil
|
|
68
66
|
end
|
|
69
67
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
68
|
+
# A regra, com a chave já sem máscara. O dígito é o módulo 11 com pesos de 2 a 9
|
|
69
|
+
# (Digitos, em digitos.rb), com as letras do CNPJ valendo ASCII menos 48.
|
|
70
|
+
def self.valid_stripped?(chave)
|
|
71
|
+
return false unless chave.match?(PATTERN)
|
|
72
|
+
return false unless UFS_IBGE.include?(chave[0, 2]) && chave[4, 2].between?("01", "12")
|
|
73
|
+
|
|
74
|
+
chave[43] == Digitos.modulo11(chave[0, 43]).to_s
|
|
74
75
|
end
|
|
75
76
|
|
|
76
|
-
private_class_method :
|
|
77
|
+
private_class_method :valid_stripped?
|
|
77
78
|
end
|
|
78
79
|
end
|
data/lib/br_validators/cnpj.rb
CHANGED
|
@@ -33,7 +33,19 @@ module BrValidators
|
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
def self.valid?(value)
|
|
36
|
+
valid_stripped?(strip(value))
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Devolve "12.ABC.345/01DE-35", ou nil se o CNPJ for inválido.
|
|
40
|
+
def self.format(value)
|
|
36
41
|
cnpj = strip(value)
|
|
42
|
+
return nil unless valid_stripped?(cnpj)
|
|
43
|
+
|
|
44
|
+
"#{cnpj[0, 2]}.#{cnpj[2, 3]}.#{cnpj[5, 3]}/#{cnpj[8, 4]}-#{cnpj[12, 2]}"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# A regra, com o CNPJ já sem máscara e em maiúsculas.
|
|
48
|
+
def self.valid_stripped?(cnpj)
|
|
37
49
|
return false unless cnpj.match?(PATTERN)
|
|
38
50
|
|
|
39
51
|
# 00.000.000/0000-00 e parecidos passam na conta, mas não existem.
|
|
@@ -42,29 +54,14 @@ module BrValidators
|
|
|
42
54
|
cnpj[12, 2] == check_digits(cnpj[0, 12])
|
|
43
55
|
end
|
|
44
56
|
|
|
45
|
-
#
|
|
46
|
-
|
|
47
|
-
return nil unless valid?(value)
|
|
48
|
-
|
|
49
|
-
cnpj = strip(value)
|
|
50
|
-
"#{cnpj[0, 2]}.#{cnpj[2, 3]}.#{cnpj[5, 3]}/#{cnpj[8, 4]}-#{cnpj[12, 2]}"
|
|
51
|
-
end
|
|
52
|
-
|
|
57
|
+
# Cada dígito é o módulo 11 com pesos de 2 a 9 (Digitos, em digitos.rb) dos
|
|
58
|
+
# caracteres anteriores; o segundo conta também o primeiro.
|
|
53
59
|
def self.check_digits(base)
|
|
54
|
-
first =
|
|
55
|
-
second =
|
|
60
|
+
first = Digitos.modulo11(base)
|
|
61
|
+
second = Digitos.modulo11("#{base}#{first}")
|
|
56
62
|
"#{first}#{second}"
|
|
57
63
|
end
|
|
58
64
|
|
|
59
|
-
|
|
60
|
-
# Pesos 2, 3, ..., 9, 2, 3... a partir do último caractere.
|
|
61
|
-
sum = chars.reverse.chars.each_with_index.sum do |char, index|
|
|
62
|
-
(char.ord - 48) * ((index % 8) + 2)
|
|
63
|
-
end
|
|
64
|
-
rest = sum % 11
|
|
65
|
-
(rest < 2 ? 0 : 11 - rest).to_s
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
private_class_method :check_digits, :check_digit
|
|
65
|
+
private_class_method :valid_stripped?, :check_digits
|
|
69
66
|
end
|
|
70
67
|
end
|
data/lib/br_validators/cns.rb
CHANGED
|
@@ -28,7 +28,20 @@ module BrValidators
|
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
def self.valid?(value)
|
|
31
|
+
valid_stripped?(strip(value))
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Devolve o CNS em grupos de 3, 4, 4 e 4 dígitos, como aparece impresso no
|
|
35
|
+
# cartão ("123 4567 8901 0000"), ou nil se for inválido.
|
|
36
|
+
def self.format(value)
|
|
31
37
|
cns = strip(value)
|
|
38
|
+
return nil unless valid_stripped?(cns)
|
|
39
|
+
|
|
40
|
+
"#{cns[0, 3]} #{cns[3, 4]} #{cns[7, 4]} #{cns[11, 4]}"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# A regra, com o CNS já sem máscara.
|
|
44
|
+
def self.valid_stripped?(cns)
|
|
32
45
|
return false unless cns.match?(/\A\d{15}\z/)
|
|
33
46
|
|
|
34
47
|
case cns[0]
|
|
@@ -38,15 +51,6 @@ module BrValidators
|
|
|
38
51
|
end
|
|
39
52
|
end
|
|
40
53
|
|
|
41
|
-
# Devolve o CNS em grupos de 3, 4, 4 e 4 dígitos, como aparece impresso no
|
|
42
|
-
# cartão ("123 4567 8901 0000"), ou nil se for inválido.
|
|
43
|
-
def self.format(value)
|
|
44
|
-
return nil unless valid?(value)
|
|
45
|
-
|
|
46
|
-
cns = strip(value)
|
|
47
|
-
"#{cns[0, 3]} #{cns[3, 4]} #{cns[7, 4]} #{cns[11, 4]}"
|
|
48
|
-
end
|
|
49
|
-
|
|
50
54
|
# Monta o CNS esperado para um PIS, como na rotina do Ministério da Saúde.
|
|
51
55
|
def self.from_pis(pis)
|
|
52
56
|
sum = weighted_sum(pis)
|
|
@@ -59,11 +63,12 @@ module BrValidators
|
|
|
59
63
|
"#{pis}001#{digit}"
|
|
60
64
|
end
|
|
61
65
|
|
|
62
|
-
# Pesos começando em 15 e diminuindo de 1 em 1 a cada dígito.
|
|
66
|
+
# Pesos começando em 15 e diminuindo de 1 em 1 a cada dígito. getbyte - 48 é o
|
|
67
|
+
# valor do dígito (explicado em digitos.rb).
|
|
63
68
|
def self.weighted_sum(digits)
|
|
64
|
-
digits.
|
|
69
|
+
(0...digits.size).sum { |index| (digits.getbyte(index) - 48) * (15 - index) }
|
|
65
70
|
end
|
|
66
71
|
|
|
67
|
-
private_class_method :from_pis, :weighted_sum
|
|
72
|
+
private_class_method :valid_stripped?, :from_pis, :weighted_sum
|
|
68
73
|
end
|
|
69
74
|
end
|
|
@@ -61,7 +61,8 @@ module BrValidators
|
|
|
61
61
|
|
|
62
62
|
# Banco pelo código de compensação ("001", "237", 341): code, name, fullName e ispb.
|
|
63
63
|
def self.banco(codigo)
|
|
64
|
-
|
|
64
|
+
# BrValidators.texto: aceita número e texto em qualquer encoding (UTF-16 dava erro).
|
|
65
|
+
codigo = BrValidators.texto(codigo).strip
|
|
65
66
|
return nil unless codigo.match?(/\A\d{1,3}\z/)
|
|
66
67
|
|
|
67
68
|
buscar("/banks/v1/#{codigo.to_i}")
|
|
@@ -69,7 +70,7 @@ module BrValidators
|
|
|
69
70
|
|
|
70
71
|
# UF e cidades de um DDD: state e cities.
|
|
71
72
|
def self.ddd(valor)
|
|
72
|
-
ddd = valor.
|
|
73
|
+
ddd = BrValidators.texto(valor).strip
|
|
73
74
|
return nil unless Telefone::DDDS.include?(ddd)
|
|
74
75
|
|
|
75
76
|
buscar("/ddd/v1/#{ddd}")
|
data/lib/br_validators/cpf.rb
CHANGED
|
@@ -24,24 +24,28 @@ module BrValidators
|
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def self.valid?(value)
|
|
27
|
-
|
|
28
|
-
return false unless digits.match?(/\A\d{11}\z/)
|
|
29
|
-
|
|
30
|
-
# 111.111.111-11, 222.222.222-22... passam na conta, mas não são CPFs reais.
|
|
31
|
-
return false if digits.squeeze.size == 1
|
|
32
|
-
|
|
33
|
-
digits[9, 2] == check_digits(digits[0, 9])
|
|
27
|
+
valid_stripped?(strip(value))
|
|
34
28
|
end
|
|
35
29
|
|
|
36
30
|
# Devolve "123.456.789-09", ou nil se o CPF for inválido
|
|
37
31
|
# (formatar um número errado esconderia o erro de quem chamou).
|
|
38
32
|
def self.format(value)
|
|
39
|
-
return nil unless valid?(value)
|
|
40
|
-
|
|
41
33
|
digits = strip(value)
|
|
34
|
+
return nil unless valid_stripped?(digits)
|
|
35
|
+
|
|
42
36
|
"#{digits[0, 3]}.#{digits[3, 3]}.#{digits[6, 3]}-#{digits[9, 2]}"
|
|
43
37
|
end
|
|
44
38
|
|
|
39
|
+
# A regra, com o CPF já sem máscara.
|
|
40
|
+
def self.valid_stripped?(digits)
|
|
41
|
+
return false unless digits.match?(/\A\d{11}\z/)
|
|
42
|
+
|
|
43
|
+
# 111.111.111-11, 222.222.222-22... passam na conta, mas não são CPFs reais.
|
|
44
|
+
return false if digits.squeeze.size == 1
|
|
45
|
+
|
|
46
|
+
digits[9, 2] == check_digits(digits[0, 9])
|
|
47
|
+
end
|
|
48
|
+
|
|
45
49
|
def self.check_digits(base)
|
|
46
50
|
first = check_digit(base)
|
|
47
51
|
second = check_digit("#{base}#{first}")
|
|
@@ -49,14 +53,15 @@ module BrValidators
|
|
|
49
53
|
end
|
|
50
54
|
|
|
51
55
|
# Pesos decrescentes até 2, começando por (tamanho + 1): 10..2 para 9 dígitos,
|
|
52
|
-
# 11..2 para 10 dígitos.
|
|
56
|
+
# 11..2 para 10 dígitos. getbyte devolve o código ASCII do dígito; menos 48 dá
|
|
57
|
+
# o valor dele ("0" é 48), sem criar uma string por dígito.
|
|
53
58
|
def self.check_digit(digits)
|
|
54
|
-
|
|
55
|
-
sum = digits.
|
|
59
|
+
first_weight = digits.size + 1
|
|
60
|
+
sum = (0...digits.size).sum { |index| (digits.getbyte(index) - 48) * (first_weight - index) }
|
|
56
61
|
rest = sum % 11
|
|
57
62
|
(rest < 2 ? 0 : 11 - rest).to_s
|
|
58
63
|
end
|
|
59
64
|
|
|
60
|
-
private_class_method :check_digits, :check_digit
|
|
65
|
+
private_class_method :valid_stripped?, :check_digits, :check_digit
|
|
61
66
|
end
|
|
62
67
|
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BrValidators
|
|
4
|
+
# Contas de dígito verificador usadas por mais de um documento. É interno da gem
|
|
5
|
+
# (private_constant no fim): quem usa a gem não precisa dele.
|
|
6
|
+
#
|
|
7
|
+
# O valor de cada caractere é o código ASCII dele menos 48: "0" vale 0, "9" vale
|
|
8
|
+
# 9 e "A" vale 17, como manda a regra do CNPJ alfanumérico. Para dígitos, dá o
|
|
9
|
+
# mesmo que to_i, sem criar uma string por caractere (getbyte devolve o código).
|
|
10
|
+
module Digitos
|
|
11
|
+
# Soma do "módulo 11 com pesos de 2 a 9": da direita para a esquerda, cada
|
|
12
|
+
# caractere é multiplicado por 2, 3, ..., 9, e o peso volta ao 2 depois do 9.
|
|
13
|
+
# Usada no CNPJ, no RENAVAM, na chave de acesso e no boleto.
|
|
14
|
+
def self.soma_modulo11(texto)
|
|
15
|
+
ultimo = texto.size - 1
|
|
16
|
+
(0..ultimo).sum { |posicao| (texto.getbyte(ultimo - posicao) - 48) * ((posicao % 8) + 2) }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# O dígito mais comum do módulo 11: resto 0 ou 1 vira 0; senão, 11 menos o resto.
|
|
20
|
+
def self.modulo11(texto)
|
|
21
|
+
resto = soma_modulo11(texto) % 11
|
|
22
|
+
resto < 2 ? 0 : 11 - resto
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private_constant :Digitos
|
|
27
|
+
end
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "version"
|
|
4
4
|
require_relative "texto"
|
|
5
|
+
require_relative "digitos"
|
|
5
6
|
require_relative "cpf"
|
|
6
7
|
require_relative "cnpj"
|
|
7
8
|
require_relative "cns"
|
|
@@ -27,6 +28,10 @@ require_relative "chave_acesso"
|
|
|
27
28
|
# Alguns têm métodos a mais: type (Pix, Boleto, Telefone, Placa), uf (TituloEleitor),
|
|
28
29
|
# segmento (ProcessoCNJ), modelo (ChaveAcesso), ddd (Telefone), mercosul e antiga (Placa).
|
|
29
30
|
#
|
|
31
|
+
# Dentro de cada módulo, os métodos públicos tiram a máscara uma vez só e passam
|
|
32
|
+
# o texto limpo para um método privado com a regra (valid_stripped? ou
|
|
33
|
+
# type_stripped). Assim format, uf, modelo... não refazem o mesmo trabalho.
|
|
34
|
+
#
|
|
30
35
|
# Este arquivo carrega só os documentos, sem os validadores do Rails. Serve para o
|
|
31
36
|
# app que já tem um CpfValidator (ou outro com o mesmo nome) e quer continuar com o
|
|
32
37
|
# dele: no Gemfile, gem "br_validators", require: "br_validators/documentos".
|
data/lib/br_validators/placa.rb
CHANGED
|
@@ -28,11 +28,7 @@ module BrValidators
|
|
|
28
28
|
|
|
29
29
|
# :antiga, :mercosul ou nil se a placa for inválida.
|
|
30
30
|
def self.type(value)
|
|
31
|
-
|
|
32
|
-
return :antiga if placa.match?(ANTIGA)
|
|
33
|
-
return :mercosul if placa.match?(MERCOSUL)
|
|
34
|
-
|
|
35
|
-
nil
|
|
31
|
+
type_stripped(strip(value))
|
|
36
32
|
end
|
|
37
33
|
|
|
38
34
|
def self.valid?(value)
|
|
@@ -42,7 +38,7 @@ module BrValidators
|
|
|
42
38
|
# Devolve "ABC-1234" (antiga) ou "ABC1D23" (Mercosul), ou nil se for inválida.
|
|
43
39
|
def self.format(value)
|
|
44
40
|
placa = strip(value)
|
|
45
|
-
case
|
|
41
|
+
case type_stripped(placa)
|
|
46
42
|
when :antiga then "#{placa[0, 3]}-#{placa[3, 4]}"
|
|
47
43
|
when :mercosul then placa
|
|
48
44
|
end
|
|
@@ -52,7 +48,7 @@ module BrValidators
|
|
|
52
48
|
# mercosul("ABC-1234") # => "ABC1C34"
|
|
53
49
|
def self.mercosul(value)
|
|
54
50
|
placa = strip(value)
|
|
55
|
-
case
|
|
51
|
+
case type_stripped(placa)
|
|
56
52
|
when :antiga then placa[0, 4] + ("A".ord + placa[4].to_i).chr + placa[5, 2]
|
|
57
53
|
when :mercosul then placa
|
|
58
54
|
end
|
|
@@ -64,10 +60,21 @@ module BrValidators
|
|
|
64
60
|
# antiga("ABC1C34") # => "ABC1234"
|
|
65
61
|
def self.antiga(value)
|
|
66
62
|
placa = strip(value)
|
|
67
|
-
|
|
68
|
-
|
|
63
|
+
case type_stripped(placa)
|
|
64
|
+
when :antiga then placa
|
|
65
|
+
when :mercosul
|
|
66
|
+
# Só as letras de A a J na quinta posição vieram da conversão.
|
|
67
|
+
placa[0, 4] + (placa[4].ord - "A".ord).to_s + placa[5, 2] if placa[4].between?("A", "J")
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# A regra, com a placa já sem máscara e em maiúsculas.
|
|
72
|
+
def self.type_stripped(placa)
|
|
73
|
+
return :antiga if placa.match?(ANTIGA)
|
|
69
74
|
|
|
70
|
-
|
|
75
|
+
:mercosul if placa.match?(MERCOSUL)
|
|
71
76
|
end
|
|
77
|
+
|
|
78
|
+
private_class_method :type_stripped
|
|
72
79
|
end
|
|
73
80
|
end
|
|
@@ -38,26 +38,33 @@ module BrValidators
|
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
def self.valid?(value)
|
|
41
|
-
|
|
42
|
-
return false unless numero.match?(/\A\d{20}\z/)
|
|
43
|
-
return false unless SEGMENTOS.key?(numero[13])
|
|
44
|
-
|
|
45
|
-
# NNNNNNN + AAAA J TR OOOO + DD
|
|
46
|
-
(numero[0, 7] + numero[9, 11] + numero[7, 2]).to_i % 97 == 1
|
|
41
|
+
valid_stripped?(strip(value))
|
|
47
42
|
end
|
|
48
43
|
|
|
49
44
|
# Devolve "0000123-45.2024.8.26.0100", ou nil se o número for inválido.
|
|
50
45
|
def self.format(value)
|
|
51
|
-
return nil unless valid?(value)
|
|
52
|
-
|
|
53
46
|
numero = strip(value)
|
|
47
|
+
return nil unless valid_stripped?(numero)
|
|
48
|
+
|
|
54
49
|
"#{numero[0, 7]}-#{numero[7, 2]}.#{numero[9, 4]}.#{numero[13]}.#{numero[14, 2]}.#{numero[16, 4]}"
|
|
55
50
|
end
|
|
56
51
|
|
|
57
52
|
# O nome do segmento do Judiciário ("Justiça do Trabalho"), ou nil se o número
|
|
58
53
|
# for inválido.
|
|
59
54
|
def self.segmento(value)
|
|
60
|
-
|
|
55
|
+
numero = strip(value)
|
|
56
|
+
valid_stripped?(numero) ? SEGMENTOS[numero[13]] : nil
|
|
61
57
|
end
|
|
58
|
+
|
|
59
|
+
# A regra, com o número já sem máscara.
|
|
60
|
+
def self.valid_stripped?(numero)
|
|
61
|
+
return false unless numero.match?(/\A\d{20}\z/)
|
|
62
|
+
return false unless SEGMENTOS.key?(numero[13])
|
|
63
|
+
|
|
64
|
+
# NNNNNNN + AAAA J TR OOOO + DD
|
|
65
|
+
(numero[0, 7] + numero[9, 11] + numero[7, 2]).to_i % 97 == 1
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private_class_method :valid_stripped?
|
|
62
69
|
end
|
|
63
70
|
end
|
|
@@ -23,26 +23,26 @@ module BrValidators
|
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def self.valid?(value)
|
|
26
|
-
|
|
27
|
-
return false unless renavam.match?(/\A\d{11}\z/)
|
|
28
|
-
|
|
29
|
-
# 00000000000, 11111111111... não são códigos reais.
|
|
30
|
-
return false if renavam.squeeze.size == 1
|
|
31
|
-
|
|
32
|
-
renavam[10] == check_digit(renavam[0, 10])
|
|
26
|
+
valid_stripped?(strip(value))
|
|
33
27
|
end
|
|
34
28
|
|
|
35
29
|
# O RENAVAM não tem máscara: devolve os 11 dígitos, ou nil se for inválido.
|
|
36
30
|
def self.format(value)
|
|
37
|
-
|
|
31
|
+
renavam = strip(value)
|
|
32
|
+
valid_stripped?(renavam) ? renavam : nil
|
|
38
33
|
end
|
|
39
34
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
35
|
+
# A regra, com o RENAVAM já sem máscara. O dígito é o módulo 11 com pesos de
|
|
36
|
+
# 2 a 9 (Digitos, em digitos.rb).
|
|
37
|
+
def self.valid_stripped?(renavam)
|
|
38
|
+
return false unless renavam.match?(/\A\d{11}\z/)
|
|
39
|
+
|
|
40
|
+
# 00000000000, 11111111111... não são códigos reais.
|
|
41
|
+
return false if renavam.squeeze.size == 1
|
|
42
|
+
|
|
43
|
+
renavam[10] == Digitos.modulo11(renavam[0, 10]).to_s
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
-
private_class_method :
|
|
46
|
+
private_class_method :valid_stripped?
|
|
47
47
|
end
|
|
48
48
|
end
|
|
@@ -47,14 +47,7 @@ module BrValidators
|
|
|
47
47
|
|
|
48
48
|
# :celular, :fixo ou nil se o número for inválido.
|
|
49
49
|
def self.type(value)
|
|
50
|
-
|
|
51
|
-
return nil unless numero && DDDS.include?(numero[0, 2])
|
|
52
|
-
|
|
53
|
-
assinante = numero[2..]
|
|
54
|
-
return :celular if assinante.match?(CELULAR)
|
|
55
|
-
return :fixo if assinante.match?(FIXO)
|
|
56
|
-
|
|
57
|
-
nil
|
|
50
|
+
type_nacional(nacional(value))
|
|
58
51
|
end
|
|
59
52
|
|
|
60
53
|
def self.valid?(value)
|
|
@@ -63,15 +56,26 @@ module BrValidators
|
|
|
63
56
|
|
|
64
57
|
# Devolve "(11) 98765-4321" ou "(11) 3456-7890", ou nil se for inválido.
|
|
65
58
|
def self.format(value)
|
|
66
|
-
return nil unless valid?(value)
|
|
67
|
-
|
|
68
59
|
numero = nacional(value)
|
|
60
|
+
return nil unless type_nacional(numero)
|
|
61
|
+
|
|
69
62
|
"(#{numero[0, 2]}) #{numero[2...-4]}-#{numero[-4..]}"
|
|
70
63
|
end
|
|
71
64
|
|
|
72
65
|
# O DDD ("11"), ou nil se o número for inválido.
|
|
73
66
|
def self.ddd(value)
|
|
74
|
-
|
|
67
|
+
numero = nacional(value)
|
|
68
|
+
type_nacional(numero) ? numero[0, 2] : nil
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# A regra, com o número já como devolvido por nacional (ou nil).
|
|
72
|
+
def self.type_nacional(numero)
|
|
73
|
+
return nil unless numero && DDDS.include?(numero[0, 2])
|
|
74
|
+
|
|
75
|
+
assinante = numero[2..]
|
|
76
|
+
return :celular if assinante.match?(CELULAR)
|
|
77
|
+
|
|
78
|
+
:fixo if assinante.match?(FIXO)
|
|
75
79
|
end
|
|
76
80
|
|
|
77
81
|
# O número só com DDD e assinante (10 ou 11 dígitos), sem o código do país
|
|
@@ -88,6 +92,6 @@ module BrValidators
|
|
|
88
92
|
digits.size.between?(10, 11) ? digits : nil
|
|
89
93
|
end
|
|
90
94
|
|
|
91
|
-
private_class_method :nacional
|
|
95
|
+
private_class_method :type_nacional, :nacional
|
|
92
96
|
end
|
|
93
97
|
end
|
|
@@ -36,32 +36,39 @@ module BrValidators
|
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
def self.valid?(value)
|
|
39
|
-
|
|
40
|
-
return false unless titulo.match?(/\A\d{12}\z/)
|
|
41
|
-
|
|
42
|
-
uf = titulo[8, 2]
|
|
43
|
-
return false unless UFS.key?(uf)
|
|
44
|
-
|
|
45
|
-
first = check_digit(weighted_sum(titulo[0, 8], 2..9), uf)
|
|
46
|
-
second = check_digit(weighted_sum("#{uf}#{first}", 7..9), uf)
|
|
47
|
-
titulo[10, 2] == "#{first}#{second}"
|
|
39
|
+
valid_stripped?(strip(value))
|
|
48
40
|
end
|
|
49
41
|
|
|
50
42
|
# Devolve "1023 8501 0671", ou nil se o título for inválido.
|
|
51
43
|
def self.format(value)
|
|
52
|
-
return nil unless valid?(value)
|
|
53
|
-
|
|
54
44
|
titulo = strip(value)
|
|
45
|
+
return nil unless valid_stripped?(titulo)
|
|
46
|
+
|
|
55
47
|
"#{titulo[0, 4]} #{titulo[4, 4]} #{titulo[8, 4]}"
|
|
56
48
|
end
|
|
57
49
|
|
|
58
50
|
# A sigla da UF onde o título foi emitido ("SP", "ZZ" para o exterior), ou nil.
|
|
59
51
|
def self.uf(value)
|
|
60
|
-
|
|
52
|
+
titulo = strip(value)
|
|
53
|
+
valid_stripped?(titulo) ? UFS[titulo[8, 2]] : nil
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# A regra, com o título já sem máscara.
|
|
57
|
+
def self.valid_stripped?(titulo)
|
|
58
|
+
return false unless titulo.match?(/\A\d{12}\z/)
|
|
59
|
+
|
|
60
|
+
uf = titulo[8, 2]
|
|
61
|
+
return false unless UFS.key?(uf)
|
|
62
|
+
|
|
63
|
+
first = check_digit(weighted_sum(titulo[0, 8], 2), uf)
|
|
64
|
+
second = check_digit(weighted_sum("#{uf}#{first}", 7), uf)
|
|
65
|
+
titulo[10, 2] == "#{first}#{second}"
|
|
61
66
|
end
|
|
62
67
|
|
|
63
|
-
|
|
64
|
-
|
|
68
|
+
# Pesos crescentes da esquerda para a direita, a partir de first_weight (2 no
|
|
69
|
+
# primeiro dígito, 7 no segundo). getbyte - 48 é o valor do dígito (digitos.rb).
|
|
70
|
+
def self.weighted_sum(digits, first_weight)
|
|
71
|
+
(0...digits.size).sum { |index| (digits.getbyte(index) - 48) * (first_weight + index) }
|
|
65
72
|
end
|
|
66
73
|
|
|
67
74
|
# codigo_uf: "01" (SP) e "02" (MG) têm a regra especial do resto 0.
|
|
@@ -72,6 +79,6 @@ module BrValidators
|
|
|
72
79
|
rest == 10 ? 0 : rest
|
|
73
80
|
end
|
|
74
81
|
|
|
75
|
-
private_class_method :weighted_sum, :check_digit
|
|
82
|
+
private_class_method :valid_stripped?, :weighted_sum, :check_digit
|
|
76
83
|
end
|
|
77
84
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: br_validators
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Pedro Pedrassolli
|
|
@@ -31,6 +31,7 @@ files:
|
|
|
31
31
|
- lib/br_validators/cns.rb
|
|
32
32
|
- lib/br_validators/consulta.rb
|
|
33
33
|
- lib/br_validators/cpf.rb
|
|
34
|
+
- lib/br_validators/digitos.rb
|
|
34
35
|
- lib/br_validators/documentos.rb
|
|
35
36
|
- lib/br_validators/locale/en.yml
|
|
36
37
|
- lib/br_validators/locale/pt-BR.yml
|