br_validators 0.1.1 → 0.2.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/CHANGELOG.md +22 -0
- data/README.md +80 -13
- data/lib/br_validators/active_model.rb +7 -0
- data/lib/br_validators/cep.rb +40 -0
- data/lib/br_validators/chave_acesso.rb +65 -0
- data/lib/br_validators/consulta.rb +111 -0
- data/lib/br_validators/locale/en.yml +7 -0
- data/lib/br_validators/locale/pt-BR.yml +7 -0
- data/lib/br_validators/placa.rb +73 -0
- data/lib/br_validators/processo_cnj.rb +64 -0
- data/lib/br_validators/renavam.rb +49 -0
- data/lib/br_validators/telefone.rb +94 -0
- data/lib/br_validators/titulo_eleitor.rb +78 -0
- data/lib/br_validators/validators/cep_validator.rb +7 -0
- data/lib/br_validators/validators/chave_acesso_validator.rb +7 -0
- data/lib/br_validators/validators/placa_validator.rb +7 -0
- data/lib/br_validators/validators/processo_cnj_validator.rb +7 -0
- data/lib/br_validators/validators/renavam_validator.rb +7 -0
- data/lib/br_validators/validators/telefone_validator.rb +7 -0
- data/lib/br_validators/validators/titulo_eleitor_validator.rb +7 -0
- data/lib/br_validators/version.rb +1 -1
- data/lib/br_validators.rb +10 -1
- metadata +19 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7297d8bd1acfbea15d3f7ef85f6bbb6cd5cc5555129c6a83bfade0e69358c14b
|
|
4
|
+
data.tar.gz: 9eab13d77a0a931e3c7a6dc34542dc9a0a069f67f5b9c72f38b9517f457e8336
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c8de53e6d4904f91915313b8f3f68ccfbe2b4e9269cff2bc0ea2f26c5cc87d2e13b3660cbd6130e4297a8d94d249758d4f1a5e5199b188882f16307413f37e7b
|
|
7
|
+
data.tar.gz: 6d948e9f10e7acd07d35c8e773f770931681afe19976fc447cbe29c6f37969a012629dac68ca187eb6b3f55fa8131cae69a0fd23d646eea250e2f8a719c085d7
|
data/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,28 @@
|
|
|
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.2.0] - 2026-10-26
|
|
7
|
+
|
|
8
|
+
### Novo
|
|
9
|
+
|
|
10
|
+
- `BrValidators::Consulta` (opcional, `require "br_validators/consulta"`): consultas online
|
|
11
|
+
pela BrasilAPI de CEP (endereço), CNPJ (dados da Receita), banco (código de compensação)
|
|
12
|
+
e DDD (UF e cidades). Só usa a biblioteca padrão do Ruby; valida o dado antes de
|
|
13
|
+
consultar, devolve `nil` quando não acha e levanta `BrValidators::Consulta::Erro` quando
|
|
14
|
+
a BrasilAPI não responde.
|
|
15
|
+
|
|
16
|
+
- `BrValidators::CEP`: formato do CEP.
|
|
17
|
+
- `BrValidators::Telefone`: fixo e celular, com os 67 DDDs da Anatel; `type`, `format` e `ddd`.
|
|
18
|
+
- `BrValidators::Placa`: placa antiga e Mercosul; `type`, `mercosul` e `antiga` para converter.
|
|
19
|
+
- `BrValidators::RENAVAM`: 11 dígitos com dígito verificador.
|
|
20
|
+
- `BrValidators::TituloEleitor`: 12 dígitos, dígitos verificadores e `uf`.
|
|
21
|
+
- `BrValidators::ProcessoCNJ`: numeração única do CNJ (módulo 97) e `segmento`.
|
|
22
|
+
- `BrValidators::ChaveAcesso`: chave de NF-e, NFC-e, CT-e e MDF-e, inclusive com CNPJ
|
|
23
|
+
alfanumérico, e `modelo`.
|
|
24
|
+
- Validadores do ActiveModel para todos eles (`cep: true`, `telefone: true`, `placa: true`,
|
|
25
|
+
`renavam: true`, `titulo_eleitor: true`, `processo_cnj: true`, `chave_acesso: true`),
|
|
26
|
+
com mensagens em pt-BR e en.
|
|
27
|
+
|
|
6
28
|
## [0.1.1] - 2026-10-25
|
|
7
29
|
|
|
8
30
|
### Corrigido
|
data/README.md
CHANGED
|
@@ -15,13 +15,25 @@ Ou direto: `gem install br_validators`. A gem está publicada no
|
|
|
15
15
|
|
|
16
16
|
## O que tem
|
|
17
17
|
|
|
18
|
-
| Documento | Observação |
|
|
19
|
-
| --- | --- |
|
|
20
|
-
| CPF | |
|
|
21
|
-
| CNPJ | numérico e alfanumérico (o formato novo da Receita Federal, a partir de julho de 2026) |
|
|
22
|
-
| CNS | Cartão Nacional de Saúde |
|
|
23
|
-
|
|
|
24
|
-
|
|
|
18
|
+
| Documento | Módulo | Observação |
|
|
19
|
+
| --- | --- | --- |
|
|
20
|
+
| CPF | `CPF` | |
|
|
21
|
+
| CNPJ | `CNPJ` | numérico e alfanumérico (o formato novo da Receita Federal, a partir de julho de 2026) |
|
|
22
|
+
| CNS | `CNS` | Cartão Nacional de Saúde |
|
|
23
|
+
| Título de eleitor | `TituloEleitor` | 12 dígitos, com a UF de emissão |
|
|
24
|
+
| Chave Pix | `Pix` | CPF, CNPJ, e-mail, celular e chave aleatória |
|
|
25
|
+
| Boleto | `Boleto` | linha digitável de cobrança e de arrecadação |
|
|
26
|
+
| CEP | `CEP` | só o formato (o CEP não tem dígito verificador) |
|
|
27
|
+
| Telefone | `Telefone` | fixo e celular, com os 67 DDDs da Anatel |
|
|
28
|
+
| Placa | `Placa` | antiga e Mercosul, com conversão entre as duas |
|
|
29
|
+
| RENAVAM | `RENAVAM` | 11 dígitos |
|
|
30
|
+
| Chave de acesso | `ChaveAcesso` | NF-e, NFC-e, CT-e e MDF-e, inclusive com CNPJ alfanumérico |
|
|
31
|
+
| Processo judicial | `ProcessoCNJ` | numeração única do CNJ |
|
|
32
|
+
|
|
33
|
+
A regra de cada um está no comentário do módulo, com o link da fonte oficial (Receita
|
|
34
|
+
Federal, Ministério da Saúde, Banco Central, FEBRABAN, Anatel, CONTRAN, CNJ...). O
|
|
35
|
+
título de eleitor é a exceção: o TSE não publica o cálculo, e a regra usada é a que a
|
|
36
|
+
OBMEP documenta.
|
|
25
37
|
|
|
26
38
|
Cada documento tem os mesmos três métodos:
|
|
27
39
|
|
|
@@ -59,6 +71,48 @@ BrValidators::Boleto.type("00190.00009 02345.678904 12345.678176 1 1000000001234
|
|
|
59
71
|
BrValidators::Boleto.valid?("826700000019000000000000000000000000000000000000") # => true
|
|
60
72
|
```
|
|
61
73
|
|
|
74
|
+
Os documentos novos seguem o mesmo jeito, com alguns métodos a mais:
|
|
75
|
+
|
|
76
|
+
```ruby
|
|
77
|
+
BrValidators::Telefone.type("+55 11 98765-4321") # => :celular
|
|
78
|
+
BrValidators::Telefone.format("1134567890") # => "(11) 3456-7890"
|
|
79
|
+
|
|
80
|
+
BrValidators::Placa.type("ABC1C34") # => :mercosul
|
|
81
|
+
BrValidators::Placa.mercosul("ABC-1234") # => "ABC1C34"
|
|
82
|
+
BrValidators::Placa.antiga("ABC1C34") # => "ABC1234"
|
|
83
|
+
|
|
84
|
+
BrValidators::TituloEleitor.uf("1023 8501 0671") # => "PR"
|
|
85
|
+
BrValidators::ProcessoCNJ.segmento("0000456-41.2023.5.02.9999") # => "Justiça do Trabalho"
|
|
86
|
+
BrValidators::ChaveAcesso.modelo("3526 1011 2223 3300 0181 5500 1000 0001 2311 2345 6783") # => "55" (NF-e)
|
|
87
|
+
|
|
88
|
+
BrValidators::CEP.format("01001000") # => "01001-000"
|
|
89
|
+
BrValidators::RENAVAM.valid?("00123456789") # => true
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Consultas online
|
|
93
|
+
|
|
94
|
+
Validar não diz se o documento existe. Para isso há consultas pela
|
|
95
|
+
[BrasilAPI](https://brasilapi.com.br), um serviço gratuito, de código aberto e sem chave.
|
|
96
|
+
Elas são opcionais (a validação continua funcionando sem internet):
|
|
97
|
+
|
|
98
|
+
```ruby
|
|
99
|
+
require "br_validators/consulta"
|
|
100
|
+
|
|
101
|
+
BrValidators::Consulta.cep("01001-000")
|
|
102
|
+
# => {"cep"=>"01001000", "state"=>"SP", "city"=>"São Paulo", "street"=>..., ...}
|
|
103
|
+
BrValidators::Consulta.cnpj("11.222.333/0001-81") # => dados da Receita Federal (razao_social, uf, ...)
|
|
104
|
+
BrValidators::Consulta.banco("001") # => {"code"=>1, "name"=>"BCO DO BRASIL S.A.", ...}
|
|
105
|
+
BrValidators::Consulta.ddd("11") # => {"state"=>"SP", "cities"=>[...]}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
- Dado inválido devolve `nil` sem chamar a internet; dado que a BrasilAPI não encontra
|
|
109
|
+
também devolve `nil`.
|
|
110
|
+
- Se a BrasilAPI não responder em 5 segundos ou der erro, levanta
|
|
111
|
+
`BrValidators::Consulta::Erro`.
|
|
112
|
+
- Para usar cache ou outro cliente HTTP, troque o `BrValidators::Consulta.cliente` por um
|
|
113
|
+
objeto com `get(uri)` que devolva `[status, corpo]`.
|
|
114
|
+
- A resposta é o JSON da BrasilAPI como `Hash`. Os campos são os dela e podem mudar.
|
|
115
|
+
|
|
62
116
|
## Rails e ActiveModel
|
|
63
117
|
|
|
64
118
|
Em projetos com ActiveModel (todo app Rails tem), os validadores entram sozinhos:
|
|
@@ -70,19 +124,32 @@ class Cliente < ApplicationRecord
|
|
|
70
124
|
validates :cartao_sus, cns: true
|
|
71
125
|
validates :chave_pix, pix: { message: "não parece uma chave Pix" }
|
|
72
126
|
validates :linha_digitavel, boleto: true
|
|
127
|
+
validates :cep, cep: true
|
|
128
|
+
validates :celular, telefone: true
|
|
129
|
+
validates :titulo, titulo_eleitor: true
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
class Veiculo < ApplicationRecord
|
|
133
|
+
validates :placa, placa: true
|
|
134
|
+
validates :renavam, renavam: true
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
class Nota < ApplicationRecord
|
|
138
|
+
validates :chave, chave_acesso: true
|
|
139
|
+
validates :processo, processo_cnj: true
|
|
73
140
|
end
|
|
74
141
|
```
|
|
75
142
|
|
|
76
143
|
As mensagens de erro vêm em português (`pt-BR`) e inglês (`en`). Para trocar o texto,
|
|
77
|
-
sobrescreva as chaves `errors.messages
|
|
78
|
-
`
|
|
144
|
+
sobrescreva as chaves `errors.messages.<documento>_invalid` (`cpf_invalid`, `cep_invalid`,
|
|
145
|
+
`titulo_eleitor_invalid`...) no seu arquivo de tradução.
|
|
79
146
|
|
|
80
|
-
Os validadores
|
|
81
|
-
`
|
|
147
|
+
Os validadores ficam no nível de cima (`CpfValidator`, `TelefoneValidator`,
|
|
148
|
+
`TituloEleitorValidator`...), porque é assim que o Rails encontra `cpf: true`.
|
|
82
149
|
|
|
83
150
|
Já existem boas gems para CPF e CNPJ. A ideia aqui é juntar num lugar só os documentos
|
|
84
|
-
que costumam aparecer em sistemas brasileiros (inclusive CNS, Pix
|
|
85
|
-
mensagens de erro em português e inglês.
|
|
151
|
+
que costumam aparecer em sistemas brasileiros (inclusive CNS, Pix, boleto, placa, NF-e e
|
|
152
|
+
processo judicial), com mensagens de erro em português e inglês.
|
|
86
153
|
|
|
87
154
|
## Página de teste
|
|
88
155
|
|
|
@@ -31,3 +31,10 @@ require_relative "validators/cnpj_validator"
|
|
|
31
31
|
require_relative "validators/cns_validator"
|
|
32
32
|
require_relative "validators/pix_validator"
|
|
33
33
|
require_relative "validators/boleto_validator"
|
|
34
|
+
require_relative "validators/cep_validator"
|
|
35
|
+
require_relative "validators/telefone_validator"
|
|
36
|
+
require_relative "validators/placa_validator"
|
|
37
|
+
require_relative "validators/renavam_validator"
|
|
38
|
+
require_relative "validators/titulo_eleitor_validator"
|
|
39
|
+
require_relative "validators/processo_cnj_validator"
|
|
40
|
+
require_relative "validators/chave_acesso_validator"
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BrValidators
|
|
4
|
+
# CEP (Código de Endereçamento Postal): 8 dígitos, escritos como 00000-000.
|
|
5
|
+
#
|
|
6
|
+
# Os 5 primeiros dígitos dizem a região, a sub-região, o setor, o subsetor e o
|
|
7
|
+
# divisor de subsetor; os 3 últimos (o sufixo) identificam a rua, a localidade
|
|
8
|
+
# ou a unidade dos Correios.
|
|
9
|
+
#
|
|
10
|
+
# O CEP não tem dígito verificador: aqui só dá para conferir o formato. Para
|
|
11
|
+
# saber se o CEP existe (e qual é o endereço), use a consulta online da gem
|
|
12
|
+
# (BrValidators::Consulta.cep).
|
|
13
|
+
#
|
|
14
|
+
# Fonte: "Tudo sobre CEP", dos Correios.
|
|
15
|
+
# https://www.correios.com.br/enviar/precisa-de-ajuda/imagens/tudo-sobre-cep
|
|
16
|
+
module CEP
|
|
17
|
+
# Pontos, hífens e espaços (inclusive os Unicode, como o não separável).
|
|
18
|
+
MASK = /[.\-[:space:]]/
|
|
19
|
+
|
|
20
|
+
# O scrub está explicado no CPF: texto corrompido vira inválido, sem erro.
|
|
21
|
+
def self.strip(value)
|
|
22
|
+
value.to_s.scrub.gsub(MASK, "")
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# 8 dígitos. "00000-000" é o CEP que sistemas usam como "não informado";
|
|
26
|
+
# nenhuma região dos Correios começa com 00000.
|
|
27
|
+
def self.valid?(value)
|
|
28
|
+
cep = strip(value)
|
|
29
|
+
cep.match?(/\A\d{8}\z/) && cep != "00000000"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Devolve "01001-000", ou nil se o CEP for inválido.
|
|
33
|
+
def self.format(value)
|
|
34
|
+
return nil unless valid?(value)
|
|
35
|
+
|
|
36
|
+
cep = strip(value)
|
|
37
|
+
"#{cep[0, 5]}-#{cep[5, 3]}"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BrValidators
|
|
4
|
+
# Chave de acesso dos documentos fiscais eletrônicos (NF-e, NFC-e, CT-e, MDF-e):
|
|
5
|
+
# 44 caracteres.
|
|
6
|
+
#
|
|
7
|
+
# cUF (2) código IBGE da UF do emitente
|
|
8
|
+
# AAMM (4) ano e mês de emissão
|
|
9
|
+
# CNPJ (14) CNPJ do emitente (ou CPF com zeros à esquerda)
|
|
10
|
+
# mod (2) modelo do documento: 55 NF-e, 65 NFC-e, 57 CT-e, 58 MDF-e...
|
|
11
|
+
# serie (3), nNF (9), tpEmis (1), cNF (8) e o dígito verificador (1)
|
|
12
|
+
#
|
|
13
|
+
# Com o CNPJ alfanumérico, os 12 primeiros caracteres do CNPJ podem ter letras;
|
|
14
|
+
# todo o resto continua só com números.
|
|
15
|
+
#
|
|
16
|
+
# O dígito verificador usa módulo 11 com pesos de 2 a 9, da direita para a
|
|
17
|
+
# esquerda, sobre os 43 primeiros caracteres. Cada caractere vale o código ASCII
|
|
18
|
+
# dele menos 48 (como no CNPJ alfanumérico: "0" vale 0, "A" vale 17). Se o resto
|
|
19
|
+
# for 0 ou 1, o dígito é 0; senão, é 11 menos o resto.
|
|
20
|
+
#
|
|
21
|
+
# Fontes: Manual de Orientação do Contribuinte da NF-e e Nota Técnica Conjunta
|
|
22
|
+
# 2025.001 (CNPJ alfanumérico nos documentos fiscais eletrônicos), publicados no
|
|
23
|
+
# Portal da NF-e. https://www.nfe.fazenda.gov.br/portal/
|
|
24
|
+
module ChaveAcesso
|
|
25
|
+
# 6 números (cUF e AAMM), 12 letras ou números (raiz e ordem do CNPJ) e 26
|
|
26
|
+
# números (dígitos do CNPJ e o resto da chave).
|
|
27
|
+
PATTERN = /\A\d{6}[0-9A-Z]{12}\d{26}\z/
|
|
28
|
+
|
|
29
|
+
# O DANFE imprime a chave em grupos de 4, separados por espaço.
|
|
30
|
+
MASK = /[.\-[:space:]]/
|
|
31
|
+
|
|
32
|
+
# Tira a máscara e passa as letras do CNPJ para maiúsculas.
|
|
33
|
+
# upcase(:ascii) e scrub estão explicados no CNPJ e no CPF.
|
|
34
|
+
def self.strip(value)
|
|
35
|
+
value.to_s.scrub.gsub(MASK, "").upcase(:ascii)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.valid?(value)
|
|
39
|
+
chave = strip(value)
|
|
40
|
+
return false unless chave.match?(PATTERN)
|
|
41
|
+
|
|
42
|
+
chave[43] == check_digit(chave[0, 43])
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Devolve a chave em 11 grupos de 4 ("3526 1011 2223 ..."), ou nil se for inválida.
|
|
46
|
+
def self.format(value)
|
|
47
|
+
return nil unless valid?(value)
|
|
48
|
+
|
|
49
|
+
strip(value).scan(/.{4}/).join(" ")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# O código do modelo ("55" para NF-e, "65" para NFC-e...), ou nil se for inválida.
|
|
53
|
+
def self.modelo(value)
|
|
54
|
+
valid?(value) ? strip(value)[20, 2] : nil
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def self.check_digit(chars)
|
|
58
|
+
sum = chars.reverse.chars.each_with_index.sum { |char, index| (char.ord - 48) * ((index % 8) + 2) }
|
|
59
|
+
rest = sum % 11
|
|
60
|
+
(rest < 2 ? 0 : 11 - rest).to_s
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private_class_method :check_digit
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "net/http"
|
|
5
|
+
require "openssl"
|
|
6
|
+
require_relative "../br_validators"
|
|
7
|
+
|
|
8
|
+
module BrValidators
|
|
9
|
+
# Consultas online pela BrasilAPI (https://brasilapi.com.br), um serviço gratuito,
|
|
10
|
+
# de código aberto e sem chave de acesso. É opcional: não vem com o
|
|
11
|
+
# require "br_validators" (a validação continua sem rede); carregue com
|
|
12
|
+
#
|
|
13
|
+
# require "br_validators/consulta"
|
|
14
|
+
#
|
|
15
|
+
# BrValidators::Consulta.cep("01001-000")
|
|
16
|
+
# # => {"cep"=>"01001000", "state"=>"SP", "city"=>"São Paulo", "street"=>..., ...}
|
|
17
|
+
# BrValidators::Consulta.cnpj("11.222.333/0001-81") # dados da Receita Federal
|
|
18
|
+
# BrValidators::Consulta.banco("001") # => {"code"=>1, "name"=>"BCO DO BRASIL S.A.", ...}
|
|
19
|
+
# BrValidators::Consulta.ddd("11") # => {"state"=>"SP", "cities"=>[...]}
|
|
20
|
+
#
|
|
21
|
+
# Cada método:
|
|
22
|
+
# - valida o dado antes (documento inválido devolve nil sem chamar a internet);
|
|
23
|
+
# - devolve o JSON da BrasilAPI como Hash (chaves em texto), ou nil se não achar;
|
|
24
|
+
# - levanta BrValidators::Consulta::Erro se a BrasilAPI não responder ou der erro.
|
|
25
|
+
module Consulta
|
|
26
|
+
BASE = "https://brasilapi.com.br/api"
|
|
27
|
+
|
|
28
|
+
# Segundos de espera para conectar e para receber a resposta.
|
|
29
|
+
TIMEOUT = 5
|
|
30
|
+
|
|
31
|
+
# A BrasilAPI não respondeu, demorou demais ou respondeu com erro.
|
|
32
|
+
class Erro < StandardError; end
|
|
33
|
+
|
|
34
|
+
class << self
|
|
35
|
+
# Quem faz a requisição: qualquer objeto com get(uri) que devolva
|
|
36
|
+
# [status, corpo]. O padrão é o HTTP abaixo; os testes trocam por um falso,
|
|
37
|
+
# e quem usa a gem pode trocar por um com cache, por exemplo.
|
|
38
|
+
attr_writer :cliente
|
|
39
|
+
|
|
40
|
+
def cliente
|
|
41
|
+
@cliente ||= HTTP
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Endereço do CEP: cep, state, city, neighborhood, street e service.
|
|
46
|
+
def self.cep(valor)
|
|
47
|
+
return nil unless CEP.valid?(valor)
|
|
48
|
+
|
|
49
|
+
buscar("/cep/v1/#{CEP.strip(valor)}")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Dados do CNPJ na Receita Federal (razao_social, nome_fantasia,
|
|
53
|
+
# descricao_situacao_cadastral, uf, municipio...). O CNPJ alfanumérico só é
|
|
54
|
+
# encontrado quando a BrasilAPI passar a aceitá-lo.
|
|
55
|
+
def self.cnpj(valor)
|
|
56
|
+
return nil unless CNPJ.valid?(valor)
|
|
57
|
+
|
|
58
|
+
buscar("/cnpj/v1/#{CNPJ.strip(valor)}")
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Banco pelo código de compensação ("001", "237", 341): code, name, fullName e ispb.
|
|
62
|
+
def self.banco(codigo)
|
|
63
|
+
codigo = codigo.to_s.strip
|
|
64
|
+
return nil unless codigo.match?(/\A\d{1,3}\z/)
|
|
65
|
+
|
|
66
|
+
buscar("/banks/v1/#{codigo.to_i}")
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# UF e cidades de um DDD: state e cities.
|
|
70
|
+
def self.ddd(valor)
|
|
71
|
+
ddd = valor.to_s.strip
|
|
72
|
+
return nil unless Telefone::DDDS.include?(ddd)
|
|
73
|
+
|
|
74
|
+
buscar("/ddd/v1/#{ddd}")
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# 200 vira Hash; 400 e 404 (não achou) viram nil; o resto é erro.
|
|
78
|
+
def self.buscar(caminho)
|
|
79
|
+
status, corpo = cliente.get(URI("#{BASE}#{caminho}"))
|
|
80
|
+
case status
|
|
81
|
+
when 200 then JSON.parse(corpo)
|
|
82
|
+
when 400, 404 then nil
|
|
83
|
+
else raise Erro, "A BrasilAPI respondeu #{status} em #{caminho}."
|
|
84
|
+
end
|
|
85
|
+
rescue JSON::ParserError
|
|
86
|
+
raise Erro, "A BrasilAPI respondeu algo que não é JSON em #{caminho}."
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
private_class_method :buscar
|
|
90
|
+
|
|
91
|
+
# Cliente padrão, só com a biblioteca padrão do Ruby (Net::HTTP).
|
|
92
|
+
module HTTP
|
|
93
|
+
HEADERS = { "Accept" => "application/json", "User-Agent" => "br_validators/#{VERSION}" }.freeze
|
|
94
|
+
|
|
95
|
+
# Problemas de rede: não conectou, a conexão caiu, demorou demais ou o
|
|
96
|
+
# certificado falhou. Viram Consulta::Erro, para quem chama tratar um erro só.
|
|
97
|
+
ERROS_DE_REDE = [Timeout::Error, SocketError, SystemCallError, IOError, OpenSSL::SSL::SSLError].freeze
|
|
98
|
+
|
|
99
|
+
def self.get(uri)
|
|
100
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
101
|
+
http.use_ssl = uri.scheme == "https"
|
|
102
|
+
http.open_timeout = TIMEOUT
|
|
103
|
+
http.read_timeout = TIMEOUT
|
|
104
|
+
resposta = http.get(uri.request_uri, HEADERS)
|
|
105
|
+
[resposta.code.to_i, resposta.body]
|
|
106
|
+
rescue *ERROS_DE_REDE => e
|
|
107
|
+
raise Erro, "Não deu para falar com a BrasilAPI (#{e.class})."
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
@@ -6,3 +6,10 @@ en:
|
|
|
6
6
|
cns_invalid: "is not a valid CNS"
|
|
7
7
|
pix_invalid: "is not a valid Pix key"
|
|
8
8
|
boleto_invalid: "is not a valid boleto line"
|
|
9
|
+
cep_invalid: "is not a valid CEP"
|
|
10
|
+
telefone_invalid: "is not a valid phone number"
|
|
11
|
+
placa_invalid: "is not a valid license plate"
|
|
12
|
+
renavam_invalid: "is not a valid RENAVAM"
|
|
13
|
+
titulo_eleitor_invalid: "is not a valid voter ID"
|
|
14
|
+
processo_cnj_invalid: "is not a valid case number"
|
|
15
|
+
chave_acesso_invalid: "is not a valid access key"
|
|
@@ -6,3 +6,10 @@ pt-BR:
|
|
|
6
6
|
cns_invalid: "não é um CNS válido"
|
|
7
7
|
pix_invalid: "não é uma chave Pix válida"
|
|
8
8
|
boleto_invalid: "não é uma linha digitável de boleto válida"
|
|
9
|
+
cep_invalid: "não é um CEP válido"
|
|
10
|
+
telefone_invalid: "não é um telefone válido"
|
|
11
|
+
placa_invalid: "não é uma placa válida"
|
|
12
|
+
renavam_invalid: "não é um RENAVAM válido"
|
|
13
|
+
titulo_eleitor_invalid: "não é um título de eleitor válido"
|
|
14
|
+
processo_cnj_invalid: "não é um número de processo válido"
|
|
15
|
+
chave_acesso_invalid: "não é uma chave de acesso válida"
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BrValidators
|
|
4
|
+
# Placa de veículo, nos dois modelos que circulam:
|
|
5
|
+
#
|
|
6
|
+
# - Antiga (cinza): 3 letras e 4 números, escrita "ABC-1234".
|
|
7
|
+
# - Mercosul: LLLNLNN (letra, letra, letra, número, letra, número, número),
|
|
8
|
+
# escrita "ABC1D23".
|
|
9
|
+
#
|
|
10
|
+
# Para converter a placa antiga, troca-se só o quinto caractere (o segundo
|
|
11
|
+
# número) por uma letra: 0 vira A, 1 vira B... 9 vira J. Ex.: ABC-1234 → ABC1C34.
|
|
12
|
+
# As letras de A a J nessa posição existem justamente para essa conversão, e os
|
|
13
|
+
# dois formatos valem para buscar o mesmo veículo.
|
|
14
|
+
#
|
|
15
|
+
# Fonte: Resolução CONTRAN nº 780/2019, Anexo I (item 1.2) e Anexo II.
|
|
16
|
+
module Placa
|
|
17
|
+
ANTIGA = /\A[A-Z]{3}\d{4}\z/
|
|
18
|
+
MERCOSUL = /\A[A-Z]{3}\d[A-Z]\d{2}\z/
|
|
19
|
+
|
|
20
|
+
# Pontos, hífens e espaços.
|
|
21
|
+
MASK = /[.\-[:space:]]/
|
|
22
|
+
|
|
23
|
+
# Tira a máscara e passa as letras para maiúsculas: "abc-1234" vira "ABC1234".
|
|
24
|
+
# upcase(:ascii) e scrub estão explicados no CNPJ e no CPF.
|
|
25
|
+
def self.strip(value)
|
|
26
|
+
value.to_s.scrub.gsub(MASK, "").upcase(:ascii)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# :antiga, :mercosul ou nil se a placa for inválida.
|
|
30
|
+
def self.type(value)
|
|
31
|
+
placa = strip(value)
|
|
32
|
+
return :antiga if placa.match?(ANTIGA)
|
|
33
|
+
return :mercosul if placa.match?(MERCOSUL)
|
|
34
|
+
|
|
35
|
+
nil
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.valid?(value)
|
|
39
|
+
!type(value).nil?
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Devolve "ABC-1234" (antiga) ou "ABC1D23" (Mercosul), ou nil se for inválida.
|
|
43
|
+
def self.format(value)
|
|
44
|
+
placa = strip(value)
|
|
45
|
+
case type(placa)
|
|
46
|
+
when :antiga then "#{placa[0, 3]}-#{placa[3, 4]}"
|
|
47
|
+
when :mercosul then placa
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# A placa no modelo Mercosul: converte a antiga e devolve a Mercosul como está.
|
|
52
|
+
# mercosul("ABC-1234") # => "ABC1C34"
|
|
53
|
+
def self.mercosul(value)
|
|
54
|
+
placa = strip(value)
|
|
55
|
+
case type(placa)
|
|
56
|
+
when :antiga then placa[0, 4] + ("A".ord + placa[4].to_i).chr + placa[5, 2]
|
|
57
|
+
when :mercosul then placa
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# A placa no modelo antigo: desfaz a conversão (só para letras de A a J na
|
|
62
|
+
# quinta posição) e devolve a antiga como está. Placa Mercosul emitida já no
|
|
63
|
+
# modelo novo, com outra letra, não tem placa antiga: devolve nil.
|
|
64
|
+
# antiga("ABC1C34") # => "ABC1234"
|
|
65
|
+
def self.antiga(value)
|
|
66
|
+
placa = strip(value)
|
|
67
|
+
return placa if type(placa) == :antiga
|
|
68
|
+
return nil unless type(placa) == :mercosul && placa[4].between?("A", "J")
|
|
69
|
+
|
|
70
|
+
placa[0, 4] + (placa[4].ord - "A".ord).to_s + placa[5, 2]
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BrValidators
|
|
4
|
+
# Número de processo judicial na numeração única do CNJ:
|
|
5
|
+
#
|
|
6
|
+
# NNNNNNN-DD.AAAA.J.TR.OOOO (20 dígitos)
|
|
7
|
+
# NNNNNNN número sequencial do processo no ano
|
|
8
|
+
# DD dígito verificador
|
|
9
|
+
# AAAA ano em que o processo começou
|
|
10
|
+
# J segmento do Judiciário (1 a 9, ver SEGMENTOS)
|
|
11
|
+
# TR tribunal dentro do segmento
|
|
12
|
+
# OOOO unidade de origem
|
|
13
|
+
#
|
|
14
|
+
# O dígito verificador usa módulo 97 (norma ISO 7064). Para conferir, junta-se
|
|
15
|
+
# NNNNNNN AAAA J TR OOOO e coloca-se o DD no fim; o número de 20 dígitos que sai
|
|
16
|
+
# dividido por 97 precisa deixar resto 1.
|
|
17
|
+
#
|
|
18
|
+
# Fonte: Resolução CNJ nº 65/2008, art. 1º e Anexo VIII.
|
|
19
|
+
# https://atos.cnj.jus.br/files/resolucao_comp_65_16122008_26032019140041.pdf
|
|
20
|
+
module ProcessoCNJ
|
|
21
|
+
SEGMENTOS = {
|
|
22
|
+
"1" => "Supremo Tribunal Federal",
|
|
23
|
+
"2" => "Conselho Nacional de Justiça",
|
|
24
|
+
"3" => "Superior Tribunal de Justiça",
|
|
25
|
+
"4" => "Justiça Federal",
|
|
26
|
+
"5" => "Justiça do Trabalho",
|
|
27
|
+
"6" => "Justiça Eleitoral",
|
|
28
|
+
"7" => "Justiça Militar da União",
|
|
29
|
+
"8" => "Justiça dos Estados e do Distrito Federal",
|
|
30
|
+
"9" => "Justiça Militar Estadual"
|
|
31
|
+
}.freeze
|
|
32
|
+
|
|
33
|
+
# Pontos, hífens e espaços.
|
|
34
|
+
MASK = /[.\-[:space:]]/
|
|
35
|
+
|
|
36
|
+
# O scrub está explicado no CPF: texto corrompido vira inválido, sem erro.
|
|
37
|
+
def self.strip(value)
|
|
38
|
+
value.to_s.scrub.gsub(MASK, "")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.valid?(value)
|
|
42
|
+
numero = strip(value)
|
|
43
|
+
return false unless numero.match?(/\A\d{20}\z/)
|
|
44
|
+
return false unless SEGMENTOS.key?(numero[13])
|
|
45
|
+
|
|
46
|
+
# NNNNNNN + AAAA J TR OOOO + DD
|
|
47
|
+
(numero[0, 7] + numero[9, 11] + numero[7, 2]).to_i % 97 == 1
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Devolve "0000123-45.2024.8.26.0100", ou nil se o número for inválido.
|
|
51
|
+
def self.format(value)
|
|
52
|
+
return nil unless valid?(value)
|
|
53
|
+
|
|
54
|
+
numero = strip(value)
|
|
55
|
+
"#{numero[0, 7]}-#{numero[7, 2]}.#{numero[9, 4]}.#{numero[13]}.#{numero[14, 2]}.#{numero[16, 4]}"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# O nome do segmento do Judiciário ("Justiça do Trabalho"), ou nil se o número
|
|
59
|
+
# for inválido.
|
|
60
|
+
def self.segmento(value)
|
|
61
|
+
valid?(value) ? SEGMENTOS[strip(value)[13]] : nil
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BrValidators
|
|
4
|
+
# RENAVAM (Registro Nacional de Veículos Automotores): 11 dígitos, sendo 10 de
|
|
5
|
+
# número e 1 dígito verificador.
|
|
6
|
+
#
|
|
7
|
+
# O dígito verificador usa módulo 11 com pesos de 2 a 9 ("peso 9"), da direita
|
|
8
|
+
# para a esquerda, voltando ao 2 depois do 9. Se o resto da divisão por 11 for
|
|
9
|
+
# 0 ou 1, o dígito é 0; senão, é 11 menos o resto.
|
|
10
|
+
#
|
|
11
|
+
# Os códigos antigos tinham 9 dígitos. Para validar um deles, complete com
|
|
12
|
+
# zeros à esquerda até ficar com 11 ("123456789" vira "00123456789"): os zeros
|
|
13
|
+
# não mudam a conta.
|
|
14
|
+
#
|
|
15
|
+
# Fonte: Portaria DENATRAN nº 27/2013 (11 dígitos, "dígito verificador,
|
|
16
|
+
# calculado através do módulo 11, peso 9"), válida desde abril de 2013.
|
|
17
|
+
module RENAVAM
|
|
18
|
+
# O RENAVAM não tem máscara oficial; aceitamos pontos, hífens e espaços.
|
|
19
|
+
MASK = /[.\-[:space:]]/
|
|
20
|
+
|
|
21
|
+
# O scrub está explicado no CPF: texto corrompido vira inválido, sem erro.
|
|
22
|
+
def self.strip(value)
|
|
23
|
+
value.to_s.scrub.gsub(MASK, "")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.valid?(value)
|
|
27
|
+
renavam = strip(value)
|
|
28
|
+
return false unless renavam.match?(/\A\d{11}\z/)
|
|
29
|
+
|
|
30
|
+
# 00000000000, 11111111111... não são códigos reais.
|
|
31
|
+
return false if renavam.squeeze.size == 1
|
|
32
|
+
|
|
33
|
+
renavam[10] == check_digit(renavam[0, 10])
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# O RENAVAM não tem máscara: devolve os 11 dígitos, ou nil se for inválido.
|
|
37
|
+
def self.format(value)
|
|
38
|
+
valid?(value) ? strip(value) : nil
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.check_digit(digits)
|
|
42
|
+
sum = digits.reverse.chars.each_with_index.sum { |digit, index| digit.to_i * ((index % 8) + 2) }
|
|
43
|
+
rest = sum % 11
|
|
44
|
+
(rest < 2 ? 0 : 11 - rest).to_s
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private_class_method :check_digit
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BrValidators
|
|
4
|
+
# Telefone brasileiro, fixo ou celular, com DDD.
|
|
5
|
+
#
|
|
6
|
+
# - DDD: um dos 67 códigos nacionais da Anatel.
|
|
7
|
+
# - Celular: 9 dígitos começando com 9 (o "nono dígito", Resolução Anatel
|
|
8
|
+
# 553/2010).
|
|
9
|
+
# - Fixo: 8 dígitos começando com 2, 3, 4 ou 5 (faixa da telefonia fixa no
|
|
10
|
+
# Regulamento de Numeração, Resolução Anatel 86/1998, art. 19, com a redação da
|
|
11
|
+
# Resolução 553/2010; a 86/1998 foi revogada pela 749/2022 e os números em uso
|
|
12
|
+
# continuam nesse formato).
|
|
13
|
+
#
|
|
14
|
+
# Aceita o código do país (+55) e o zero antes do DDD ("011..."). Não aceita o
|
|
15
|
+
# código da operadora (0 XX DDD ...) nem números especiais, como 0800 e 190.
|
|
16
|
+
#
|
|
17
|
+
# Fontes:
|
|
18
|
+
# - Lista dos 67 códigos nacionais (DDD): perguntas frequentes da Anatel sobre
|
|
19
|
+
# áreas locais, 2026.
|
|
20
|
+
# https://sistemas.anatel.gov.br/anexar-api/publico/anexos/download/a8c6650c72539ff2334c327ab5c169e4
|
|
21
|
+
# - Resolução 86/1998 (Regulamento de Numeração do STFC) e Resolução 553/2010.
|
|
22
|
+
module Telefone
|
|
23
|
+
# Os 67 DDDs em uso. Os que faltam na sequência (20, 23, 25, 26, 29, 30,
|
|
24
|
+
# 36, 39, 40, 50, 52, 56 a 60, 70, 72, 76, 78, 80 e 90) não existem.
|
|
25
|
+
DDDS = %w[
|
|
26
|
+
11 12 13 14 15 16 17 18 19
|
|
27
|
+
21 22 24 27 28
|
|
28
|
+
31 32 33 34 35 37 38
|
|
29
|
+
41 42 43 44 45 46 47 48 49
|
|
30
|
+
51 53 54 55
|
|
31
|
+
61 62 63 64 65 66 67 68 69
|
|
32
|
+
71 73 74 75 77 79
|
|
33
|
+
81 82 83 84 85 86 87 88 89
|
|
34
|
+
91 92 93 94 95 96 97 98 99
|
|
35
|
+
].freeze
|
|
36
|
+
|
|
37
|
+
CELULAR = /\A9\d{8}\z/
|
|
38
|
+
FIXO = /\A[2-5]\d{7}\z/
|
|
39
|
+
|
|
40
|
+
# Parênteses, "+", pontos, hífens e espaços.
|
|
41
|
+
MASK = /[()+.\-[:space:]]/
|
|
42
|
+
|
|
43
|
+
# Tira a máscara: "+55 (11) 98765-4321" vira "5511987654321".
|
|
44
|
+
# O scrub está explicado no CPF: texto corrompido vira inválido, sem erro.
|
|
45
|
+
def self.strip(value)
|
|
46
|
+
value.to_s.scrub.gsub(MASK, "")
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# :celular, :fixo ou nil se o número for inválido.
|
|
50
|
+
def self.type(value)
|
|
51
|
+
numero = nacional(value)
|
|
52
|
+
return nil unless numero && DDDS.include?(numero[0, 2])
|
|
53
|
+
|
|
54
|
+
assinante = numero[2..]
|
|
55
|
+
return :celular if assinante.match?(CELULAR)
|
|
56
|
+
return :fixo if assinante.match?(FIXO)
|
|
57
|
+
|
|
58
|
+
nil
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def self.valid?(value)
|
|
62
|
+
!type(value).nil?
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Devolve "(11) 98765-4321" ou "(11) 3456-7890", ou nil se for inválido.
|
|
66
|
+
def self.format(value)
|
|
67
|
+
return nil unless valid?(value)
|
|
68
|
+
|
|
69
|
+
numero = nacional(value)
|
|
70
|
+
"(#{numero[0, 2]}) #{numero[2...-4]}-#{numero[-4..]}"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# O DDD ("11"), ou nil se o número for inválido.
|
|
74
|
+
def self.ddd(value)
|
|
75
|
+
valid?(value) ? nacional(value)[0, 2] : nil
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# O número só com DDD e assinante (10 ou 11 dígitos), sem o código do país
|
|
79
|
+
# (55) nem o zero antes do DDD. Devolve nil se não sobrar um número desse tamanho.
|
|
80
|
+
def self.nacional(value)
|
|
81
|
+
digits = strip(value)
|
|
82
|
+
return nil unless digits.match?(/\A\d+\z/)
|
|
83
|
+
|
|
84
|
+
# Com o código do país: 55 + 10 ou 11 dígitos.
|
|
85
|
+
digits = digits.delete_prefix("55") if digits.start_with?("55") && digits.size.between?(12, 13)
|
|
86
|
+
# O DDD nunca começa com 0; um zero na frente é o prefixo de chamada nacional.
|
|
87
|
+
digits = digits.delete_prefix("0") if digits.start_with?("0")
|
|
88
|
+
|
|
89
|
+
digits.size.between?(10, 11) ? digits : nil
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
private_class_method :nacional
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BrValidators
|
|
4
|
+
# Título de eleitor: 12 dígitos.
|
|
5
|
+
# - 8 primeiros: número sequencial;
|
|
6
|
+
# - 9º e 10º: código da UF onde foi emitido (01 a 28; 28 é o exterior, "ZZ");
|
|
7
|
+
# - 11º e 12º: dígitos verificadores.
|
|
8
|
+
#
|
|
9
|
+
# 1º dígito: soma dos 8 primeiros com pesos de 2 a 9, da esquerda para a
|
|
10
|
+
# direita; o dígito é o resto da divisão por 11.
|
|
11
|
+
# 2º dígito: soma dos 2 dígitos da UF e do 1º dígito, com pesos 7, 8 e 9;
|
|
12
|
+
# também é o resto da divisão por 11.
|
|
13
|
+
# Nos dois casos, resto 10 vira 0. Em títulos de São Paulo (01) e Minas Gerais
|
|
14
|
+
# (02), resto 0 vira 1.
|
|
15
|
+
#
|
|
16
|
+
# Alguns títulos antigos de SP e MG têm 13 dígitos (9 no número sequencial).
|
|
17
|
+
# Eles não são aceitos aqui: a regra pública só cobre os de 12.
|
|
18
|
+
#
|
|
19
|
+
# Fonte: o TSE não publica o cálculo. A regra é a usada pelos sistemas e
|
|
20
|
+
# explicada pela OBMEP em "A Matemática nos Documentos: Título de Eleitor".
|
|
21
|
+
# https://clubes.obmep.org.br/blog/a-matematica-nos-documentos-titulo-de-eleitor/
|
|
22
|
+
module TituloEleitor
|
|
23
|
+
UFS = {
|
|
24
|
+
"01" => "SP", "02" => "MG", "03" => "RJ", "04" => "RS", "05" => "BA", "06" => "PR",
|
|
25
|
+
"07" => "CE", "08" => "PE", "09" => "SC", "10" => "GO", "11" => "MA", "12" => "PB",
|
|
26
|
+
"13" => "PA", "14" => "ES", "15" => "PI", "16" => "RN", "17" => "AL", "18" => "MT",
|
|
27
|
+
"19" => "MS", "20" => "DF", "21" => "SE", "22" => "AM", "23" => "RO", "24" => "AC",
|
|
28
|
+
"25" => "AP", "26" => "RR", "27" => "TO", "28" => "ZZ"
|
|
29
|
+
}.freeze
|
|
30
|
+
|
|
31
|
+
# O título impresso mostra o número em grupos separados por espaço.
|
|
32
|
+
MASK = %r{[./\-[:space:]]}
|
|
33
|
+
|
|
34
|
+
# O scrub está explicado no CPF: texto corrompido vira inválido, sem erro.
|
|
35
|
+
def self.strip(value)
|
|
36
|
+
value.to_s.scrub.gsub(MASK, "")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def self.valid?(value)
|
|
40
|
+
titulo = strip(value)
|
|
41
|
+
return false unless titulo.match?(/\A\d{12}\z/)
|
|
42
|
+
|
|
43
|
+
uf = titulo[8, 2]
|
|
44
|
+
return false unless UFS.key?(uf)
|
|
45
|
+
|
|
46
|
+
first = check_digit(weighted_sum(titulo[0, 8], 2..9), uf)
|
|
47
|
+
second = check_digit(weighted_sum("#{uf}#{first}", 7..9), uf)
|
|
48
|
+
titulo[10, 2] == "#{first}#{second}"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Devolve "1023 8501 0671", ou nil se o título for inválido.
|
|
52
|
+
def self.format(value)
|
|
53
|
+
return nil unless valid?(value)
|
|
54
|
+
|
|
55
|
+
titulo = strip(value)
|
|
56
|
+
"#{titulo[0, 4]} #{titulo[4, 4]} #{titulo[8, 4]}"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# A sigla da UF onde o título foi emitido ("SP", "ZZ" para o exterior), ou nil.
|
|
60
|
+
def self.uf(value)
|
|
61
|
+
valid?(value) ? UFS[strip(value)[8, 2]] : nil
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def self.weighted_sum(digits, weights)
|
|
65
|
+
digits.chars.zip(weights.to_a).sum { |digit, weight| digit.to_i * weight }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# codigo_uf: "01" (SP) e "02" (MG) têm a regra especial do resto 0.
|
|
69
|
+
def self.check_digit(sum, codigo_uf)
|
|
70
|
+
rest = sum % 11
|
|
71
|
+
return 1 if rest.zero? && %w[01 02].include?(codigo_uf)
|
|
72
|
+
|
|
73
|
+
rest == 10 ? 0 : rest
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private_class_method :weighted_sum, :check_digit
|
|
77
|
+
end
|
|
78
|
+
end
|
data/lib/br_validators.rb
CHANGED
|
@@ -6,6 +6,13 @@ require_relative "br_validators/cnpj"
|
|
|
6
6
|
require_relative "br_validators/cns"
|
|
7
7
|
require_relative "br_validators/pix"
|
|
8
8
|
require_relative "br_validators/boleto"
|
|
9
|
+
require_relative "br_validators/cep"
|
|
10
|
+
require_relative "br_validators/telefone"
|
|
11
|
+
require_relative "br_validators/placa"
|
|
12
|
+
require_relative "br_validators/renavam"
|
|
13
|
+
require_relative "br_validators/titulo_eleitor"
|
|
14
|
+
require_relative "br_validators/processo_cnj"
|
|
15
|
+
require_relative "br_validators/chave_acesso"
|
|
9
16
|
|
|
10
17
|
# Validação e formatação de documentos brasileiros.
|
|
11
18
|
#
|
|
@@ -15,7 +22,9 @@ require_relative "br_validators/boleto"
|
|
|
15
22
|
# format(valor) -> o documento com a máscara, ou nil se for inválido
|
|
16
23
|
# strip(valor) -> o documento sem a máscara
|
|
17
24
|
# A chave Pix é a exceção: tem valid? e type, mas não format nem strip, porque
|
|
18
|
-
# precisa estar exatamente como o Banco Central guarda.
|
|
25
|
+
# precisa estar exatamente como o Banco Central guarda.
|
|
26
|
+
# Alguns têm métodos a mais: type (Pix, Boleto, Telefone, Placa), uf (TituloEleitor),
|
|
27
|
+
# segmento (ProcessoCNJ), modelo (ChaveAcesso), ddd (Telefone), mercosul e antiga (Placa).
|
|
19
28
|
module BrValidators
|
|
20
29
|
end
|
|
21
30
|
|
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.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Pedro Pedrassolli
|
|
@@ -21,17 +21,32 @@ files:
|
|
|
21
21
|
- lib/br_validators.rb
|
|
22
22
|
- lib/br_validators/active_model.rb
|
|
23
23
|
- lib/br_validators/boleto.rb
|
|
24
|
+
- lib/br_validators/cep.rb
|
|
25
|
+
- lib/br_validators/chave_acesso.rb
|
|
24
26
|
- lib/br_validators/cnpj.rb
|
|
25
27
|
- lib/br_validators/cns.rb
|
|
28
|
+
- lib/br_validators/consulta.rb
|
|
26
29
|
- lib/br_validators/cpf.rb
|
|
27
30
|
- lib/br_validators/locale/en.yml
|
|
28
31
|
- lib/br_validators/locale/pt-BR.yml
|
|
29
32
|
- lib/br_validators/pix.rb
|
|
33
|
+
- lib/br_validators/placa.rb
|
|
34
|
+
- lib/br_validators/processo_cnj.rb
|
|
35
|
+
- lib/br_validators/renavam.rb
|
|
36
|
+
- lib/br_validators/telefone.rb
|
|
37
|
+
- lib/br_validators/titulo_eleitor.rb
|
|
30
38
|
- lib/br_validators/validators/boleto_validator.rb
|
|
39
|
+
- lib/br_validators/validators/cep_validator.rb
|
|
40
|
+
- lib/br_validators/validators/chave_acesso_validator.rb
|
|
31
41
|
- lib/br_validators/validators/cnpj_validator.rb
|
|
32
42
|
- lib/br_validators/validators/cns_validator.rb
|
|
33
43
|
- lib/br_validators/validators/cpf_validator.rb
|
|
34
44
|
- lib/br_validators/validators/pix_validator.rb
|
|
45
|
+
- lib/br_validators/validators/placa_validator.rb
|
|
46
|
+
- lib/br_validators/validators/processo_cnj_validator.rb
|
|
47
|
+
- lib/br_validators/validators/renavam_validator.rb
|
|
48
|
+
- lib/br_validators/validators/telefone_validator.rb
|
|
49
|
+
- lib/br_validators/validators/titulo_eleitor_validator.rb
|
|
35
50
|
- lib/br_validators/version.rb
|
|
36
51
|
homepage: https://github.com/PedroPedrassolli/br_validators
|
|
37
52
|
licenses:
|
|
@@ -57,5 +72,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
57
72
|
requirements: []
|
|
58
73
|
rubygems_version: 3.6.9
|
|
59
74
|
specification_version: 4
|
|
60
|
-
summary: Valida e formata CPF, CNPJ
|
|
75
|
+
summary: 'Valida e formata documentos brasileiros: CPF, CNPJ alfanumérico, CNS, Pix,
|
|
76
|
+
boleto, CEP, telefone, placa, RENAVAM, título de eleitor, chave de NF-e e processo
|
|
77
|
+
do CNJ.'
|
|
61
78
|
test_files: []
|