br_validators 0.2.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7297d8bd1acfbea15d3f7ef85f6bbb6cd5cc5555129c6a83bfade0e69358c14b
4
- data.tar.gz: 9eab13d77a0a931e3c7a6dc34542dc9a0a069f67f5b9c72f38b9517f457e8336
3
+ metadata.gz: 8ab3d772a8a5a9512686a875c429689adcb348af7eac07f7f795f2efa616d402
4
+ data.tar.gz: 5c222dfc1339f2c451501fd010767ce82b0111243e4768b0762a896b03b2a363
5
5
  SHA512:
6
- metadata.gz: c8de53e6d4904f91915313b8f3f68ccfbe2b4e9269cff2bc0ea2f26c5cc87d2e13b3660cbd6130e4297a8d94d249758d4f1a5e5199b188882f16307413f37e7b
7
- data.tar.gz: 6d948e9f10e7acd07d35c8e773f770931681afe19976fc447cbe29c6f37969a012629dac68ca187eb6b3f55fa8131cae69a0fd23d646eea250e2f8a719c085d7
6
+ metadata.gz: a9663daabd0c2079d63aa4990c0e5454bb47024b7e7cf1545f2db7f0a76a3c69aaf5e3141d7a808b94457a79a65eef48a0ddf5a5e378b76ec0474beb2ea87bfd
7
+ data.tar.gz: bcc9e9bce3dda58ff44c9039fb7107cdc217a0dddd997d4d935995eb524a7a0e0ad23c910203bfe540d94990a1b4cbc56b9f9c4fb84e9ea22cd0c1d7b46e6a55
data/CHANGELOG.md CHANGED
@@ -3,6 +3,22 @@
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.1] - 2026-10-26
7
+
8
+ ### Corrigido
9
+
10
+ - Traços tipográficos (meia-risca, travessão, hífen sem quebra), comuns ao copiar do Word
11
+ ou de um PDF, contam como máscara, como o hífen comum.
12
+ - Texto em outros encodings (Latin-1, Windows-1252, UTF-16) é convertido para UTF-8 antes
13
+ da validação (`BrValidators.texto`); antes, UTF-16 levantava `Encoding::CompatibilityError`.
14
+ - `ChaveAcesso` confere o código IBGE da UF e o mês: uma chave só de zeros passava.
15
+ - `Consulta`: resposta quebrada (que não é HTTP) também vira `Consulta::Erro`.
16
+
17
+ ### Outros
18
+
19
+ - A API da página de teste devolve `detalhe` (DDD, UF do título, segmento do processo e
20
+ modelo da chave de acesso).
21
+
6
22
  ## [0.2.0] - 2026-10-26
7
23
 
8
24
  ### Novo
data/README.md CHANGED
@@ -27,7 +27,7 @@ Ou direto: `gem install br_validators`. A gem está publicada no
27
27
  | Telefone | `Telefone` | fixo e celular, com os 67 DDDs da Anatel |
28
28
  | Placa | `Placa` | antiga e Mercosul, com conversão entre as duas |
29
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 |
30
+ | Chave de acesso | `ChaveAcesso` | NF-e, NFC-e, CT-e e MDF-e, inclusive com CNPJ alfanumérico; confere UF e mês |
31
31
  | Processo judicial | `ProcessoCNJ` | numeração única do CNJ |
32
32
 
33
33
  A regra de cada um está no comentário do módulo, com o link da fonte oficial (Receita
@@ -48,9 +48,10 @@ BrValidators::CNPJ.format("12abc34501de35") # => "12.ABC.345/01DE-35"
48
48
  BrValidators::CNS.format("898000000000436") # => "898 0000 0000 0436"
49
49
  ```
50
50
 
51
- Pontos, hífens e espaços são ignorados em qualquer posição, inclusive o espaço não
52
- separável que vem junto ao copiar de um PDF ou de um site. Texto corrompido (bytes que
53
- não são UTF-8 válido) só dá `false`, sem erro. Passe sempre o documento como texto: um
51
+ Pontos, traços e espaços são ignorados em qualquer posição, inclusive a meia-risca (–)
52
+ e o espaço não separável que vêm junto ao copiar do Word, de um PDF ou de um site. O texto
53
+ pode vir em qualquer encoding (UTF-8, Latin-1, UTF-16); texto corrompido só dá `false`,
54
+ sem erro. Passe sempre o documento como texto: um
54
55
  número inteiro perde os zeros à esquerda (`01234567890` vira `1234567890`).
55
56
 
56
57
  A chave Pix é um caso à parte: em vez de `format` e `strip`, ela tem `type`, que diz
@@ -159,7 +160,10 @@ Dá para testar cada validador no navegador em https://br-validators.vercel.app.
159
160
 
160
161
  ```
161
162
  GET /api/validar?documento=cnpj&valor=12.ABC.345/01DE-35
162
- {"documento":"cnpj","valido":true,"formatado":"12.ABC.345/01DE-35","tipo":null}
163
+ {"documento":"cnpj","valido":true,"formatado":"12.ABC.345/01DE-35","tipo":null,"detalhe":null}
164
+
165
+ GET /api/validar?documento=titulo_eleitor&valor=1023%208501%200671
166
+ {"documento":"titulo_eleitor","valido":true,"formatado":"1023 8501 0671","tipo":null,"detalhe":"PR"}
163
167
  ```
164
168
 
165
169
  ## Desenvolvimento
@@ -22,11 +22,10 @@ module BrValidators
22
22
  # Pontos, espaços e hífens que aparecem na linha impressa. A linha copiada do
23
23
  # PDF do boleto ou do site do banco costuma vir com espaço não separável, que
24
24
  # [:space:] também pega.
25
- MASK = /[.\-[:space:]]/
25
+ MASK = /[.\p{Pd}[:space:]]/
26
26
 
27
- # O scrub está explicado no CPF: texto corrompido vira inválido, sem erro.
28
27
  def self.strip(value)
29
- value.to_s.scrub.gsub(MASK, "")
28
+ BrValidators.texto(value).gsub(MASK, "")
30
29
  end
31
30
 
32
31
  # :cobranca, :arrecadacao ou nil se a linha for inválida.
@@ -15,11 +15,10 @@ module BrValidators
15
15
  # https://www.correios.com.br/enviar/precisa-de-ajuda/imagens/tudo-sobre-cep
16
16
  module CEP
17
17
  # Pontos, hífens e espaços (inclusive os Unicode, como o não separável).
18
- MASK = /[.\-[:space:]]/
18
+ MASK = /[.\p{Pd}[:space:]]/
19
19
 
20
- # O scrub está explicado no CPF: texto corrompido vira inválido, sem erro.
21
20
  def self.strip(value)
22
- value.to_s.scrub.gsub(MASK, "")
21
+ BrValidators.texto(value).gsub(MASK, "")
23
22
  end
24
23
 
25
24
  # 8 dígitos. "00000-000" é o CEP que sistemas usam como "não informado";
@@ -11,7 +11,9 @@ module BrValidators
11
11
  # serie (3), nNF (9), tpEmis (1), cNF (8) e o dígito verificador (1)
12
12
  #
13
13
  # Com o CNPJ alfanumérico, os 12 primeiros caracteres do CNPJ podem ter letras;
14
- # todo o resto continua só com números.
14
+ # todo o resto continua só com números. Além do dígito verificador, conferimos
15
+ # que o cUF é o código IBGE de uma UF e que o mês vai de 01 a 12 (sem isso, uma
16
+ # chave só de zeros passaria).
15
17
  #
16
18
  # O dígito verificador usa módulo 11 com pesos de 2 a 9, da direita para a
17
19
  # esquerda, sobre os 43 primeiros caracteres. Cada caractere vale o código ASCII
@@ -21,23 +23,34 @@ module BrValidators
21
23
  # Fontes: Manual de Orientação do Contribuinte da NF-e e Nota Técnica Conjunta
22
24
  # 2025.001 (CNPJ alfanumérico nos documentos fiscais eletrônicos), publicados no
23
25
  # Portal da NF-e. https://www.nfe.fazenda.gov.br/portal/
26
+ # Códigos das UFs: tabela do IBGE. https://www.ibge.gov.br/explica/codigos-dos-municipios.php
24
27
  module ChaveAcesso
25
28
  # 6 números (cUF e AAMM), 12 letras ou números (raiz e ordem do CNPJ) e 26
26
29
  # números (dígitos do CNPJ e o resto da chave).
27
30
  PATTERN = /\A\d{6}[0-9A-Z]{12}\d{26}\z/
28
31
 
32
+ # Códigos IBGE das 27 UFs (o cUF, dois primeiros dígitos da chave).
33
+ UFS_IBGE = %w[
34
+ 11 12 13 14 15 16 17
35
+ 21 22 23 24 25 26 27 28 29
36
+ 31 32 33 35
37
+ 41 42 43
38
+ 50 51 52 53
39
+ ].freeze
40
+
29
41
  # O DANFE imprime a chave em grupos de 4, separados por espaço.
30
- MASK = /[.\-[:space:]]/
42
+ MASK = /[.\p{Pd}[:space:]]/
31
43
 
32
44
  # 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.
45
+ # O upcase(:ascii) está explicado no CNPJ.
34
46
  def self.strip(value)
35
- value.to_s.scrub.gsub(MASK, "").upcase(:ascii)
47
+ BrValidators.texto(value).gsub(MASK, "").upcase(:ascii)
36
48
  end
37
49
 
38
50
  def self.valid?(value)
39
51
  chave = strip(value)
40
52
  return false unless chave.match?(PATTERN)
53
+ return false unless UFS_IBGE.include?(chave[0, 2]) && chave[4, 2].between?("01", "12")
41
54
 
42
55
  chave[43] == check_digit(chave[0, 43])
43
56
  end
@@ -16,7 +16,7 @@ module BrValidators
16
16
  module CNPJ
17
17
  # Caracteres de máscara aceitos na entrada: ponto, barra, hífen e espaços
18
18
  # (inclusive os espaços Unicode, como o não separável).
19
- MASK = %r{[./\-[:space:]]}
19
+ MASK = %r{[./\p{Pd}[:space:]]}
20
20
 
21
21
  # 12 caracteres alfanuméricos e 2 dígitos no final.
22
22
  PATTERN = /\A[0-9A-Z]{12}\d{2}\z/
@@ -27,9 +27,9 @@ module BrValidators
27
27
  #
28
28
  # upcase(:ascii) mexe só nas letras de a a z. O upcase normal segue o Unicode e
29
29
  # transforma "ı" (i sem ponto) em "I" e "ß" em "SS", o que faria um texto
30
- # errado passar como CNPJ válido. O scrub está explicado no CPF.
30
+ # errado passar como CNPJ válido.
31
31
  def self.strip(value)
32
- value.to_s.scrub.gsub(MASK, "").upcase(:ascii)
32
+ BrValidators.texto(value).gsub(MASK, "").upcase(:ascii)
33
33
  end
34
34
 
35
35
  def self.valid?(value)
@@ -21,11 +21,10 @@ module BrValidators
21
21
  # O cartão mostra o número em grupos separados por espaço; aceitamos também
22
22
  # pontos e hífens, que às vezes aparecem em sistemas antigos. Espaços Unicode
23
23
  # (como o não separável) também contam como espaço.
24
- MASK = /[.\-[:space:]]/
24
+ MASK = /[.\p{Pd}[:space:]]/
25
25
 
26
- # O scrub está explicado no CPF: texto corrompido vira inválido, sem erro.
27
26
  def self.strip(value)
28
- value.to_s.scrub.gsub(MASK, "")
27
+ BrValidators.texto(value).gsub(MASK, "")
29
28
  end
30
29
 
31
30
  def self.valid?(value)
@@ -92,9 +92,13 @@ module BrValidators
92
92
  module HTTP
93
93
  HEADERS = { "Accept" => "application/json", "User-Agent" => "br_validators/#{VERSION}" }.freeze
94
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
95
+ # Problemas de rede: não conectou, a conexão caiu, demorou demais, o
96
+ # certificado falhou ou a resposta veio quebrada (não é HTTP de verdade).
97
+ # Viram Consulta::Erro, para quem chama tratar um erro só.
98
+ ERROS_DE_REDE = [
99
+ Timeout::Error, SocketError, SystemCallError, IOError, OpenSSL::SSL::SSLError,
100
+ Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError
101
+ ].freeze
98
102
 
99
103
  def self.get(uri)
100
104
  http = Net::HTTP.new(uri.host, uri.port)
@@ -8,17 +8,19 @@ module BrValidators
8
8
  # 11 a 2 nos 10 primeiros (os 9 + o primeiro verificador). Se o resto da divisão
9
9
  # por 11 for 0 ou 1, o dígito é 0; senão, é 11 menos o resto.
10
10
  module CPF
11
- # Caracteres de máscara que aceitamos na entrada: ponto, hífen e espaços.
12
- # [:space:] pega também os espaços Unicode, como o espaço não separável que
13
- # vem junto ao copiar um número de um PDF ou de um site.
14
- MASK = /[.\-[:space:]]/
11
+ # Caracteres de máscara que aceitamos na entrada: ponto, traços e espaços.
12
+ # Ao copiar um número de um PDF, do Word ou de um site, o hífen pode virar
13
+ # meia-risca (–) ou hífen sem quebra, e o espaço pode virar o não separável:
14
+ # \p{Pd} pega qualquer traço e [:space:] qualquer espaço. Os outros módulos
15
+ # seguem a mesma ideia.
16
+ MASK = /[.\p{Pd}[:space:]]/
15
17
 
16
18
  # Tira a máscara: "123.456.789-09" vira "12345678909".
17
19
  # Não remove letras nem outros símbolos, para que valid? possa recusá-los.
18
- # O scrub troca bytes inválidos (texto corrompido) por "�", que nunca passa na
19
- # validação; sem ele, o gsub levantaria um erro em vez de devolver false.
20
+ # BrValidators.texto (texto.rb) converte para UTF-8 e troca bytes inválidos
21
+ # por "�", que nunca passa na validação.
20
22
  def self.strip(value)
21
- value.to_s.scrub.gsub(MASK, "")
23
+ BrValidators.texto(value).gsub(MASK, "")
22
24
  end
23
25
 
24
26
  def self.valid?(value)
@@ -45,9 +45,8 @@ module BrValidators
45
45
  # Devolve :cpf, :cnpj, :phone, :email ou :evp, ou nil se a chave for inválida.
46
46
  # Só os espaços nas pontas são ignorados: o resto precisa estar exatamente
47
47
  # como o DICT guarda (sem máscara, e-mail e chave aleatória em minúsculas).
48
- # O scrub está explicado no CPF: texto corrompido vira inválido, sem erro.
49
48
  def self.type(value)
50
- key = value.to_s.scrub.gsub(EDGE_SPACES, "")
49
+ key = BrValidators.texto(value).gsub(EDGE_SPACES, "")
51
50
  return nil if key.size > MAX_LENGTH
52
51
 
53
52
  found = CHECKS.find { |_type, check| check.call(key) }
@@ -18,12 +18,12 @@ module BrValidators
18
18
  MERCOSUL = /\A[A-Z]{3}\d[A-Z]\d{2}\z/
19
19
 
20
20
  # Pontos, hífens e espaços.
21
- MASK = /[.\-[:space:]]/
21
+ MASK = /[.\p{Pd}[:space:]]/
22
22
 
23
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.
24
+ # O upcase(:ascii) está explicado no CNPJ.
25
25
  def self.strip(value)
26
- value.to_s.scrub.gsub(MASK, "").upcase(:ascii)
26
+ BrValidators.texto(value).gsub(MASK, "").upcase(:ascii)
27
27
  end
28
28
 
29
29
  # :antiga, :mercosul ou nil se a placa for inválida.
@@ -31,11 +31,10 @@ module BrValidators
31
31
  }.freeze
32
32
 
33
33
  # Pontos, hífens e espaços.
34
- MASK = /[.\-[:space:]]/
34
+ MASK = /[.\p{Pd}[:space:]]/
35
35
 
36
- # O scrub está explicado no CPF: texto corrompido vira inválido, sem erro.
37
36
  def self.strip(value)
38
- value.to_s.scrub.gsub(MASK, "")
37
+ BrValidators.texto(value).gsub(MASK, "")
39
38
  end
40
39
 
41
40
  def self.valid?(value)
@@ -16,11 +16,10 @@ module BrValidators
16
16
  # calculado através do módulo 11, peso 9"), válida desde abril de 2013.
17
17
  module RENAVAM
18
18
  # O RENAVAM não tem máscara oficial; aceitamos pontos, hífens e espaços.
19
- MASK = /[.\-[:space:]]/
19
+ MASK = /[.\p{Pd}[:space:]]/
20
20
 
21
- # O scrub está explicado no CPF: texto corrompido vira inválido, sem erro.
22
21
  def self.strip(value)
23
- value.to_s.scrub.gsub(MASK, "")
22
+ BrValidators.texto(value).gsub(MASK, "")
24
23
  end
25
24
 
26
25
  def self.valid?(value)
@@ -38,12 +38,11 @@ module BrValidators
38
38
  FIXO = /\A[2-5]\d{7}\z/
39
39
 
40
40
  # Parênteses, "+", pontos, hífens e espaços.
41
- MASK = /[()+.\-[:space:]]/
41
+ MASK = /[()+.\p{Pd}[:space:]]/
42
42
 
43
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
44
  def self.strip(value)
46
- value.to_s.scrub.gsub(MASK, "")
45
+ BrValidators.texto(value).gsub(MASK, "")
47
46
  end
48
47
 
49
48
  # :celular, :fixo ou nil se o número for inválido.
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Entrada comum a todos os validadores: ver BrValidators.texto abaixo.
4
+ module BrValidators
5
+ # Converte o valor recebido em texto UTF-8 antes de tirar a máscara. Todos os
6
+ # validadores passam por aqui, para tratar a entrada do mesmo jeito:
7
+ # - nil vira "" e números viram texto;
8
+ # - texto em outro encoding (Latin-1, Windows-1252, UTF-16) é convertido;
9
+ # - bytes inválidos (texto corrompido) viram "�", que nunca passa na validação.
10
+ # Assim os validadores devolvem false em vez de levantar erro, e as máscaras
11
+ # podem usar classes Unicode, como [:space:] (espaços) e \p{Pd} (traços).
12
+ def self.texto(valor)
13
+ valor.to_s.encode(Encoding::UTF_8, invalid: :replace, undef: :replace)
14
+ end
15
+ end
@@ -29,11 +29,10 @@ module BrValidators
29
29
  }.freeze
30
30
 
31
31
  # O título impresso mostra o número em grupos separados por espaço.
32
- MASK = %r{[./\-[:space:]]}
32
+ MASK = %r{[./\p{Pd}[:space:]]}
33
33
 
34
- # O scrub está explicado no CPF: texto corrompido vira inválido, sem erro.
35
34
  def self.strip(value)
36
- value.to_s.scrub.gsub(MASK, "")
35
+ BrValidators.texto(value).gsub(MASK, "")
37
36
  end
38
37
 
39
38
  def self.valid?(value)
@@ -2,5 +2,5 @@
2
2
 
3
3
  # Versão da gem, lida também pelo gemspec.
4
4
  module BrValidators
5
- VERSION = "0.2.0"
5
+ VERSION = "0.2.1"
6
6
  end
data/lib/br_validators.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "br_validators/version"
4
+ require_relative "br_validators/texto"
4
5
  require_relative "br_validators/cpf"
5
6
  require_relative "br_validators/cnpj"
6
7
  require_relative "br_validators/cns"
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.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pedro Pedrassolli
@@ -9,6 +9,10 @@ bindir: bin
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies: []
12
+ description: 'Validação e formatação de documentos brasileiros sem dependências, com
13
+ a regra de cada um conferida na fonte oficial. Inclui validadores para ActiveModel
14
+ (validates :cpf, cpf: true) com mensagens em pt-BR e en, e consultas online opcionais
15
+ pela BrasilAPI (CEP, CNPJ, banco e DDD).'
12
16
  email:
13
17
  - pedropedrassolli@outlook.com
14
18
  executables: []
@@ -34,6 +38,7 @@ files:
34
38
  - lib/br_validators/processo_cnj.rb
35
39
  - lib/br_validators/renavam.rb
36
40
  - lib/br_validators/telefone.rb
41
+ - lib/br_validators/texto.rb
37
42
  - lib/br_validators/titulo_eleitor.rb
38
43
  - lib/br_validators/validators/boleto_validator.rb
39
44
  - lib/br_validators/validators/cep_validator.rb