cnpj-val 0.0.0 → 1.0.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 +18 -0
- data/LICENSE +9 -0
- data/README.md +368 -0
- data/README.pt.md +355 -0
- data/src/cnpj-val/cnpj_val.rb +37 -0
- data/src/cnpj-val/cnpj_validator.rb +171 -0
- data/src/cnpj-val/cnpj_validator_options.rb +202 -0
- data/src/cnpj-val/errors.rb +90 -0
- data/src/cnpj-val/types.rb +61 -0
- data/src/cnpj-val/version.rb +1 -1
- data/src/cnpj-val.rb +42 -4
- metadata +44 -7
data/README.pt.md
ADDED
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
> 🚀 **Suporte total ao [novo formato alfanumérico de CNPJ](https://github.com/user-attachments/files/23937961/calculodvcnpjalfanaumerico.pdf).**
|
|
4
|
+
|
|
5
|
+
> 🌎 [Access documentation in English](./README.md)
|
|
6
|
+
|
|
7
|
+
Utilitário em Ruby para validar CNPJ (Cadastro Nacional da Pessoa Jurídica).
|
|
8
|
+
|
|
9
|
+
## Recursos
|
|
10
|
+
|
|
11
|
+
- ✅ **CNPJ alfanumérico**: Valida CNPJ de 14 caracteres no formato numérico ou alfanumérico
|
|
12
|
+
- ✅ **Entrada flexível**: Aceita `String` ou `Array` de strings; elementos do array são concatenados na ordem
|
|
13
|
+
- ✅ **Agnóstico ao formato**: Remove caracteres não alfanuméricos (ou não numéricos quando `type` é `numeric`) e opcionalmente converte para maiúsculas antes de validar
|
|
14
|
+
- ✅ **Sensibilidade a maiúsculas opcional**: Com `case_sensitive` em `false`, letras minúsculas são aceitas para CNPJ alfanumérico
|
|
15
|
+
- ✅ **Sobrescrita por chamada**: Padrões da instância podem ser sobrescritos apenas naquela chamada a `is_valid`
|
|
16
|
+
- ✅ **Tratamento de erros**: Uso incorreto da API vs erros de domínio, com marcador `CnpjVal::Error` para rescue em toda a biblioteca
|
|
17
|
+
- ✅ **Dependências mínimas**: [`cnpj-dv`](https://rubygems.org/gems/cnpj-dv) para cálculo dos dígitos verificadores e [`lacus-utils`](https://rubygems.org/gems/lacus-utils) para descrição de tipos nas mensagens de erro
|
|
18
|
+
- ✅ **API dupla**: Orientada a objetos (`CnpjVal::CnpjValidator`) e funcional (`CnpjVal.cnpj_val`)
|
|
19
|
+
|
|
20
|
+
## Instalação
|
|
21
|
+
|
|
22
|
+
Instale a gem diretamente:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
gem install cnpj-val
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Ou adicione ao seu `Gemfile` e execute `bundle install`:
|
|
29
|
+
|
|
30
|
+
```ruby
|
|
31
|
+
gem 'cnpj-val'
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Require
|
|
35
|
+
|
|
36
|
+
```ruby
|
|
37
|
+
require 'cnpj-val'
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Início rápido
|
|
41
|
+
|
|
42
|
+
```ruby
|
|
43
|
+
require 'cnpj-val'
|
|
44
|
+
|
|
45
|
+
validator = CnpjVal::CnpjValidator.new
|
|
46
|
+
|
|
47
|
+
validator.is_valid('98765432000198') # => true
|
|
48
|
+
validator.is_valid('98.765.432/0001-98') # => true
|
|
49
|
+
validator.is_valid('98765432000199') # => false
|
|
50
|
+
|
|
51
|
+
validator.is_valid('1QB5UKALPYFP59') # => true (alfanumérico)
|
|
52
|
+
validator.is_valid('1QB5UKALpyfp59') # => false (padrão é case-sensitive)
|
|
53
|
+
validator.is_valid('1QB5UKALpyfp59', case_sensitive: false) # => true
|
|
54
|
+
|
|
55
|
+
validator.is_valid('96206256120884') # => true (numérico)
|
|
56
|
+
validator.is_valid('1QB5UKALPYFP59', type: 'numeric') # => false (letras removidas → comprimento ≠ 14)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Helper funcional:
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
require 'cnpj-val'
|
|
63
|
+
|
|
64
|
+
CnpjVal.cnpj_val('98765432000198') # => true
|
|
65
|
+
CnpjVal.cnpj_val('98.765.432/0001-98') # => true
|
|
66
|
+
CnpjVal.cnpj_val('98765432000199') # => false
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Utilização
|
|
70
|
+
|
|
71
|
+
Os pontos principais são a classe `CnpjVal::CnpjValidator`, a classe de opções `CnpjVal::CnpjValidatorOptions` e o helper `CnpjVal.cnpj_val`.
|
|
72
|
+
|
|
73
|
+
### `CnpjVal::CnpjValidator`
|
|
74
|
+
|
|
75
|
+
- **`initialize(options = nil, **keywords)`**: Opções padrão de validação. Quando `options` é fornecido isoladamente (instância de `CnpjVal::CnpjValidatorOptions` ou `Hash`), ele determina as opções padrão; uma instância de `CnpjVal::CnpjValidatorOptions` é armazenada por referência (mutações posteriores afetam futuras chamadas de `is_valid` que não passarem opções por chamada), enquanto um `Hash` cria uma nova instância. Quando `options` é omitido (`nil`), as opções padrão são construídas exclusivamente a partir dos argumentos nomeados (`case_sensitive:`, `type:`). Passar `options` junto com qualquer argumento nomeado não `nil` gera `InvalidArgumentCombinationError`, em vez de ignorar os argumentos nomeados silenciosamente. Exemplo: `CnpjVal::CnpjValidator.new(type: 'numeric', case_sensitive: false)`.
|
|
76
|
+
- **`options`**: Retorna o `CnpjVal::CnpjValidatorOptions` da instância (o mesmo objeto usado internamente).
|
|
77
|
+
- **`is_valid(cnpj_input, options = nil, **keywords)`**: Valida um valor CNPJ.
|
|
78
|
+
|
|
79
|
+
A entrada é normalizada para string (arrays de strings são concatenados). Quando `case_sensitive` é `false`, a string é convertida para maiúsculas antes da sanitização. Caracteres são removidos conforme `type`. Se o comprimento após sanitização não for exatamente **14**, se os dois últimos caracteres não forem dígitos ou se os dígitos verificadores não coincidirem (`CnpjDV::CnpjCheckDigits` de **`cnpj-dv`**), o método retorna `false` — nenhuma exceção é lançada por falha de validação.
|
|
80
|
+
|
|
81
|
+
Se a entrada não for `String` nem `Array` de strings, é lançada **`CnpjVal::TypeMismatchError`**.
|
|
82
|
+
|
|
83
|
+
`options` por chamada e argumentos nomeados nunca são mesclados: um `options` fornecido isoladamente sobrescreve totalmente os padrões da instância nesta chamada; caso contrário, qualquer argumento nomeado fornecido sobrescreve os padrões da instância nesta chamada. Quando nenhum dos dois é fornecido, os padrões da instância são usados como estão. Os padrões da instância nunca são alterados por uma sobrescrita pontual. Passar `options` junto com qualquer argumento nomeado não `nil` gera `InvalidArgumentCombinationError`.
|
|
84
|
+
|
|
85
|
+
```ruby
|
|
86
|
+
require 'cnpj-val'
|
|
87
|
+
|
|
88
|
+
validator = CnpjVal::CnpjValidator.new(type: 'numeric')
|
|
89
|
+
|
|
90
|
+
validator.is_valid('98.765.432/0001-98') # => true
|
|
91
|
+
validator.is_valid('1QB5UKALPYFP59') # => false (letras removidas → comprimento ≠ 14)
|
|
92
|
+
validator.is_valid('1QB5UKALpyfp59', type: 'alphanumeric', case_sensitive: false) # => true
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Padrões na instância; sobrescrita por chamada:
|
|
96
|
+
|
|
97
|
+
```ruby
|
|
98
|
+
require 'cnpj-val'
|
|
99
|
+
|
|
100
|
+
validator = CnpjVal::CnpjValidator.new(case_sensitive: false)
|
|
101
|
+
|
|
102
|
+
validator.is_valid('1qb5ukalpyfp59') # => true (padrões da instância)
|
|
103
|
+
validator.is_valid('1qb5ukalpyfp59', case_sensitive: true) # só nesta chamada: false
|
|
104
|
+
validator.is_valid('1qb5ukalpyfp59') # => true de novo
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### `CnpjVal::CnpjValidatorOptions`
|
|
108
|
+
|
|
109
|
+
Armazena configurações do validador (`case_sensitive`, `type`). Construa com um `Hash` opcional ou instância de `CnpjVal::CnpjValidatorOptions`, objetos extras de sobrescrita (mesclados em ordem) e/ou argumentos nomeados. Expõe acessores: `case_sensitive`, `type`.
|
|
110
|
+
|
|
111
|
+
- **`all`**: Retorna um snapshot superficial congelado (`Hash`) de todas as opções atuais.
|
|
112
|
+
- **`set(options)`**: Atualiza vários campos de uma vez; retorna `self`. Aceita um `Hash` ou outra instância de `CnpjVal::CnpjValidatorOptions`. Chaves omitidas mantêm o valor atual.
|
|
113
|
+
|
|
114
|
+
```ruby
|
|
115
|
+
require 'cnpj-val'
|
|
116
|
+
|
|
117
|
+
options = CnpjVal::CnpjValidatorOptions.new(case_sensitive: false, type: 'numeric')
|
|
118
|
+
options.case_sensitive # => false
|
|
119
|
+
options.type # => "numeric"
|
|
120
|
+
options.set({ type: 'alphanumeric' }) # mescla e retorna self
|
|
121
|
+
options.all # => snapshot congelado das opções atuais
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Helper funcional
|
|
125
|
+
|
|
126
|
+
`CnpjVal.cnpj_val` instancia um novo `CnpjVal::CnpjValidator` com os mesmos parâmetros do construtor e chama `is_valid(cnpj_input)` uma vez. Passe argumentos nomeados **ou** um `Hash`/instância de `CnpjVal::CnpjValidatorOptions` para as opções — não ambos (passar `options` com qualquer argumento nomeado não-`nil` lança `InvalidArgumentCombinationError`):
|
|
127
|
+
|
|
128
|
+
```ruby
|
|
129
|
+
require 'cnpj-val'
|
|
130
|
+
|
|
131
|
+
CnpjVal.cnpj_val('98765432000198') # => true
|
|
132
|
+
CnpjVal.cnpj_val('1QB5UKALpyfp59', case_sensitive: false) # => true
|
|
133
|
+
CnpjVal.cnpj_val('1QB5UKALPYFP59', type: 'numeric') # => false
|
|
134
|
+
CnpjVal.cnpj_val('1QB5UKALpyfp59', { # forma com Hash
|
|
135
|
+
type: 'alphanumeric',
|
|
136
|
+
case_sensitive: false,
|
|
137
|
+
}) # => true
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Formatos de entrada
|
|
141
|
+
|
|
142
|
+
**String:** Dígitos e/ou letras brutos, ou CNPJ já formatado (ex.: `98.765.432/0001-98`, `1Q.B5U.KAL/PYFP-59`). Caracteres são removidos conforme `type`; quando `case_sensitive` é `false`, letras são convertidas para maiúsculas antes da validação alfanumérica.
|
|
143
|
+
|
|
144
|
+
**Array de strings:** Cada elemento deve ser `String`; os valores são concatenados (ex.: por dígito, segmentos agrupados ou misturados com pontuação). Elementos que não sejam string lançam **`CnpjVal::TypeMismatchError`**.
|
|
145
|
+
|
|
146
|
+
```ruby
|
|
147
|
+
require 'cnpj-val'
|
|
148
|
+
|
|
149
|
+
CnpjVal.cnpj_val(['1', 'Q', 'B', '5', 'U', 'K', 'A', 'L', 'P', 'Y', 'F', 'P', '5', '9']) # => true
|
|
150
|
+
CnpjVal.cnpj_val(['1Q.B5U', 'KAL', 'PYFP-59']) # => true
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Opções de validação
|
|
154
|
+
|
|
155
|
+
| Parâmetro | Tipo | Padrão | Descrição |
|
|
156
|
+
|-----------|------|---------|-------------|
|
|
157
|
+
| `type` | `'alphanumeric'` \| `'numeric'` \| `nil` | `'alphanumeric'` | Conjunto de caracteres após sanitização: alfanumérico (`0`–`9`, `A`–`Z`, `a`–`z`) ou apenas numérico (`0`–`9`) |
|
|
158
|
+
| `case_sensitive` | `Boolean`, `nil` | `true` | Se `false`, letras minúsculas são convertidas para maiúsculas antes da validação alfanumérica |
|
|
159
|
+
|
|
160
|
+
CNPJ inválido (comprimento errado após sanitização, dígitos verificadores inválidos, base/filial inelegíveis `00000000` / `0000`, dígitos repetidos, caracteres não numéricos nos verificadores) retorna **`false`** — nenhuma exceção é lançada por falha de validação.
|
|
161
|
+
|
|
162
|
+
Exemplo com todas as opções:
|
|
163
|
+
|
|
164
|
+
```ruby
|
|
165
|
+
require 'cnpj-val'
|
|
166
|
+
|
|
167
|
+
CnpjVal.cnpj_val(
|
|
168
|
+
'1QB5UKALpyfp59',
|
|
169
|
+
type: 'alphanumeric',
|
|
170
|
+
case_sensitive: false,
|
|
171
|
+
)
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Tratamento de erros
|
|
175
|
+
|
|
176
|
+
Os erros se dividem em duas categorias:
|
|
177
|
+
|
|
178
|
+
| Categoria | Significado |
|
|
179
|
+
|---|---|
|
|
180
|
+
| **Uso incorreto da API** | O chamador usou a biblioteca de forma incorreta (tipo errado para a entrada CNPJ ou para uma opção, ou combinação inválida de argumentos). |
|
|
181
|
+
| **Erro de domínio** | A forma da chamada era válida, mas um valor viola uma regra de negócio (`type` inválido). |
|
|
182
|
+
|
|
183
|
+
Todo erro customizado inclui o módulo marcador `CnpjVal::Error`. Falhas de domínio (`ValidationError`) herdam de `CnpjVal::DomainError` (`RangeError`). Dados de CNPJ inválidos retornam `false` (não levantam erro).
|
|
184
|
+
|
|
185
|
+
**Importante:** passar ao mesmo tempo uma instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` levanta `InvalidArgumentCombinationError`.
|
|
186
|
+
|
|
187
|
+
#### Resumo
|
|
188
|
+
|
|
189
|
+
| Classe | Herda de | Categoria | Condição de disparo |
|
|
190
|
+
|---|---|---|---|
|
|
191
|
+
| `CnpjVal::InvalidArgumentCombinationError` | `ArgumentError` (+ `include Error`) | Uso incorreto da API | Instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` passados ao mesmo tempo |
|
|
192
|
+
| `CnpjVal::TypeMismatchError` | `TypeError` (+ `include Error`) | Uso incorreto da API | Entrada CNPJ ou opção com tipo de dado incorreto |
|
|
193
|
+
| `CnpjVal::ValidationError` | `CnpjVal::DomainError` | Erro de domínio | `type` fora dos valores permitidos |
|
|
194
|
+
|
|
195
|
+
#### `CnpjVal::Error` (módulo marcador)
|
|
196
|
+
|
|
197
|
+
- **Herança:** módulo marcador misturado em todo erro da biblioteca via `include` (não é uma classe).
|
|
198
|
+
- **Categoria:** N/A (apenas alvo de rescue) — não é um modo de falha por si só.
|
|
199
|
+
- **Quando é levantado:** Nunca levantado diretamente; incluído por todo erro customizado que a biblioteca levanta.
|
|
200
|
+
- **Exemplo:** N/A
|
|
201
|
+
- **Como resgatá-lo:**
|
|
202
|
+
|
|
203
|
+
```ruby
|
|
204
|
+
rescue CnpjVal::Error
|
|
205
|
+
# tudo o que esta biblioteca levanta
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
#### `CnpjVal::DomainError`
|
|
209
|
+
|
|
210
|
+
- **Herança:** `CnpjVal::DomainError < RangeError` (inclui `CnpjVal::Error`)
|
|
211
|
+
- **Categoria:** Erro de domínio — ancestral de todas as falhas de domínio.
|
|
212
|
+
- **Quando é levantado:** Não levantado diretamente; prefira levantar uma subclasse folha.
|
|
213
|
+
- **Exemplo:** Prefira `raise CnpjVal::ValidationError` a levantar `DomainError` diretamente.
|
|
214
|
+
- **Como resgatá-lo:**
|
|
215
|
+
|
|
216
|
+
```ruby
|
|
217
|
+
rescue CnpjVal::DomainError
|
|
218
|
+
# ValidationError e outras subclasses de DomainError
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
#### `CnpjVal::TypeMismatchError`
|
|
222
|
+
|
|
223
|
+
- **Herança:** `CnpjVal::TypeMismatchError < TypeError` (inclui `CnpjVal::Error`)
|
|
224
|
+
- **Categoria:** Uso incorreto da API — o chamador passou um valor do tipo errado.
|
|
225
|
+
- **Quando é levantado:** Levantado quando a entrada CNPJ não é `String` nem `Array` de strings, ou quando uma opção do validador (`type`) tem o tipo de runtime incorreto.
|
|
226
|
+
- **Exemplo:**
|
|
227
|
+
|
|
228
|
+
```ruby
|
|
229
|
+
CnpjVal.cnpj_val(12_345_678_000_198) # levanta CnpjVal::TypeMismatchError
|
|
230
|
+
CnpjVal.cnpj_val('98765432000198', type: 123) # levanta CnpjVal::TypeMismatchError
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
- **Como resgatá-lo:**
|
|
234
|
+
|
|
235
|
+
```ruby
|
|
236
|
+
rescue CnpjVal::TypeMismatchError
|
|
237
|
+
# violação de contrato de tipo desta biblioteca
|
|
238
|
+
|
|
239
|
+
rescue TypeError
|
|
240
|
+
# erros nativos de tipo, incluindo TypeMismatchError desta biblioteca
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
#### `CnpjVal::InvalidArgumentCombinationError`
|
|
244
|
+
|
|
245
|
+
- **Herança:** `CnpjVal::InvalidArgumentCombinationError < ArgumentError` (inclui `CnpjVal::Error`)
|
|
246
|
+
- **Categoria:** Uso incorreto da API — os argumentos fornecidos não formam uma assinatura válida da API.
|
|
247
|
+
- **Quando é levantado:** Levantado quando `CnpjValidator.new`, `#is_valid` ou `cnpj_val` recebe ao mesmo tempo um argumento `options` (instância ou `Hash`) e qualquer argumento nomeado não `nil`.
|
|
248
|
+
- **Exemplo:**
|
|
249
|
+
|
|
250
|
+
```ruby
|
|
251
|
+
begin
|
|
252
|
+
CnpjVal.cnpj_val('98765432000198', { type: 'numeric' }, case_sensitive: false)
|
|
253
|
+
rescue CnpjVal::InvalidArgumentCombinationError => e
|
|
254
|
+
puts e.message
|
|
255
|
+
# Pass either an options instance/Hash to `options`, or keyword arguments (case_sensitive:, type:), not both.
|
|
256
|
+
end
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
- **Como resgatá-lo:**
|
|
260
|
+
|
|
261
|
+
```ruby
|
|
262
|
+
rescue CnpjVal::InvalidArgumentCombinationError
|
|
263
|
+
# combinação inválida de assinatura desta biblioteca
|
|
264
|
+
|
|
265
|
+
rescue ArgumentError
|
|
266
|
+
# erros nativos de argumento, incluindo InvalidArgumentCombinationError desta biblioteca
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
#### `CnpjVal::ValidationError`
|
|
270
|
+
|
|
271
|
+
- **Herança:** `CnpjVal::ValidationError < CnpjVal::DomainError < RangeError` (inclui `CnpjVal::Error`)
|
|
272
|
+
- **Categoria:** Erro de domínio — um valor falha uma regra de domínio que não é numérica nem de comprimento.
|
|
273
|
+
- **Quando é levantado:** Levantado quando `type` não é `'alphanumeric'` nem `'numeric'`.
|
|
274
|
+
- **Exemplo:**
|
|
275
|
+
|
|
276
|
+
```ruby
|
|
277
|
+
CnpjVal.cnpj_val('98765432000198', type: 'invalid') # levanta CnpjVal::ValidationError
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
- **Como resgatá-lo:**
|
|
281
|
+
|
|
282
|
+
```ruby
|
|
283
|
+
rescue CnpjVal::ValidationError
|
|
284
|
+
# esta falha exata de validação de domínio
|
|
285
|
+
|
|
286
|
+
rescue CnpjVal::DomainError
|
|
287
|
+
# ValidationError e outras subclasses de DomainError
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
#### Granularidade de rescue
|
|
291
|
+
|
|
292
|
+
```ruby
|
|
293
|
+
# 1) Classe nativa única — captura erros de uso incorreto desse tipo.
|
|
294
|
+
rescue TypeError
|
|
295
|
+
# CnpjVal::TypeMismatchError e qualquer outro TypeError (da biblioteca ou não)
|
|
296
|
+
|
|
297
|
+
# 2) CnpjVal::DomainError — captura todas as violações de regra de negócio sob DomainError.
|
|
298
|
+
rescue CnpjVal::DomainError
|
|
299
|
+
# CnpjVal::ValidationError e outras subclasses de DomainError
|
|
300
|
+
|
|
301
|
+
# 3) CnpjVal::Error — captura tudo o que a biblioteca levanta.
|
|
302
|
+
rescue CnpjVal::Error
|
|
303
|
+
# todo erro customizado que inclui CnpjVal::Error
|
|
304
|
+
|
|
305
|
+
# 4) Classe folha específica — captura apenas aquele modo de falha.
|
|
306
|
+
rescue CnpjVal::ValidationError
|
|
307
|
+
# apenas CnpjVal::ValidationError
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
Atributos notáveis nos erros levantados:
|
|
311
|
+
|
|
312
|
+
- `TypeMismatchError`: `option_name` (nil para entrada CNPJ), `actual_input`, `actual_type`, `expected_type`
|
|
313
|
+
- `ValidationError`: `option_name`, `actual_input`, `expected_values`
|
|
314
|
+
|
|
315
|
+
## API
|
|
316
|
+
|
|
317
|
+
### Exportações
|
|
318
|
+
|
|
319
|
+
Após `require 'cnpj-val'`:
|
|
320
|
+
|
|
321
|
+
- **`CnpjVal.cnpj_val`**: `(cnpj_input, options = nil, **keywords) -> Boolean` — helper de conveniência.
|
|
322
|
+
- **`CnpjVal::CnpjValidator`**: Classe para validar CNPJ com opções padrão opcionais; aceita `String` ou `Array<String>` em `is_valid`.
|
|
323
|
+
- **`CnpjVal::CnpjValidatorOptions`**: Classe que armazena opções; suporta mesclagem via construtor, `set` e argumentos nomeados.
|
|
324
|
+
- **`CnpjVal::CNPJ_LENGTH`**: `14` (constante).
|
|
325
|
+
- **`CnpjVal::VERSION`**: string de versão da gem.
|
|
326
|
+
- **Predicado de tipo**: `CnpjVal::CnpjInput` — `CnpjVal::CnpjInput.accept?(value)` / `CnpjVal::CnpjInput === value` é verdadeiro apenas para `String` ou `Array<String>`.
|
|
327
|
+
- **Marcadores de tipo**: `CnpjVal::CnpjType`, `CnpjVal::CnpjValidatorOptionsInput`.
|
|
328
|
+
- **Erros**: `CnpjVal::Error`, `CnpjVal::DomainError`, `CnpjVal::InvalidArgumentCombinationError`, `CnpjVal::TypeMismatchError`, `CnpjVal::ValidationError`.
|
|
329
|
+
|
|
330
|
+
### Outros recursos disponíveis
|
|
331
|
+
|
|
332
|
+
- **`CnpjVal::CnpjValidatorOptions::CNPJ_LENGTH`**: `14`.
|
|
333
|
+
- **`CnpjVal::CnpjValidatorOptions::DEFAULT_CASE_SENSITIVE`**: `true`.
|
|
334
|
+
- **`CnpjVal::CnpjValidatorOptions::DEFAULT_TYPE`**: `'alphanumeric'`.
|
|
335
|
+
|
|
336
|
+
## Contribuição e suporte
|
|
337
|
+
|
|
338
|
+
Contribuições são bem-vindas! Consulte as [Diretrizes de contribuição](https://github.com/LacusSolutions/br-utils-ruby/blob/main/CONTRIBUTING.md). Se este projeto for útil para você, considere:
|
|
339
|
+
|
|
340
|
+
- ⭐ Dar uma estrela ao repositório
|
|
341
|
+
- 🤝 Contribuir com o código
|
|
342
|
+
- 💡 [Sugerir novos recursos](https://github.com/LacusSolutions/br-utils-ruby/issues)
|
|
343
|
+
- 🐛 [Reportar bugs](https://github.com/LacusSolutions/br-utils-ruby/issues)
|
|
344
|
+
|
|
345
|
+
## Licença
|
|
346
|
+
|
|
347
|
+
Este projeto está licenciado sob a MIT License — consulte o arquivo [LICENSE](https://github.com/LacusSolutions/br-utils-ruby/blob/main/LICENSE).
|
|
348
|
+
|
|
349
|
+
## Changelog
|
|
350
|
+
|
|
351
|
+
Consulte o [CHANGELOG](./CHANGELOG.md) para histórico de versões e alterações.
|
|
352
|
+
|
|
353
|
+
---
|
|
354
|
+
|
|
355
|
+
Feito com ❤️ por [Lacus Solutions](https://github.com/LacusSolutions)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'cnpj_validator'
|
|
4
|
+
|
|
5
|
+
module CnpjVal
|
|
6
|
+
# Helper function to simplify the usage of the {CnpjValidator} class.
|
|
7
|
+
#
|
|
8
|
+
# If no options are provided, it validates a CNPJ string or array of strings
|
|
9
|
+
# using default settings. If options are provided, they control case
|
|
10
|
+
# sensitivity and the type of characters to be validated. Invalid CNPJ data
|
|
11
|
+
# returns +false+; only API misuse raises documented errors.
|
|
12
|
+
#
|
|
13
|
+
# Pass either keyword arguments **or** a {Hash}/{CnpjValidatorOptions} instance
|
|
14
|
+
# for options — not both.
|
|
15
|
+
#
|
|
16
|
+
# @param cnpj_input [String, Array<String>] CNPJ value as a string or array of
|
|
17
|
+
# strings
|
|
18
|
+
# @param options [CnpjValidatorOptions, Hash, nil] default validator options
|
|
19
|
+
# @param keywords [Hash] option keyword overrides (mutually exclusive with
|
|
20
|
+
# +options+; see {CnpjValidatorOptions::OPTION_KEYS})
|
|
21
|
+
# @return [Boolean] +true+ when valid, +false+ otherwise
|
|
22
|
+
# @raise [InvalidArgumentCombinationError] if +options+ and a keyword argument
|
|
23
|
+
# are both given
|
|
24
|
+
# @raise [TypeMismatchError] if the input is not a +String+ or +Array<String>+,
|
|
25
|
+
# or if any option has an invalid type
|
|
26
|
+
# @raise [ValidationError] if +type+ is not one of the allowed values
|
|
27
|
+
# @see CnpjValidator#is_valid for detailed option descriptions
|
|
28
|
+
# @see CnpjValidator
|
|
29
|
+
#
|
|
30
|
+
# @example
|
|
31
|
+
# CnpjVal.cnpj_val('91415732000793') # => true
|
|
32
|
+
# CnpjVal.cnpj_val('9JN7MGLJZXIO50') # => true
|
|
33
|
+
# CnpjVal.cnpj_val('9JN7MGLJZXIO51') # => false
|
|
34
|
+
def self.cnpj_val(cnpj_input, options = nil, **keywords)
|
|
35
|
+
CnpjValidator.new(options, **keywords).is_valid(cnpj_input)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'cnpj-dv'
|
|
4
|
+
|
|
5
|
+
require_relative 'cnpj_validator_options'
|
|
6
|
+
require_relative 'errors'
|
|
7
|
+
require_relative 'types'
|
|
8
|
+
|
|
9
|
+
module CnpjVal
|
|
10
|
+
# Validator for CNPJ (Cadastro Nacional da Pessoa Jurídica) identifiers.
|
|
11
|
+
#
|
|
12
|
+
# Validates CNPJ strings according to the Brazilian CNPJ validation algorithm.
|
|
13
|
+
# Invalid CNPJ data returns +false+; only API misuse raises documented errors.
|
|
14
|
+
class CnpjValidator
|
|
15
|
+
NUMERIC_PATTERN = /\D/
|
|
16
|
+
ALPHANUMERIC_PATTERN = /[^0-9A-Za-z]/
|
|
17
|
+
|
|
18
|
+
private_constant :NUMERIC_PATTERN, :ALPHANUMERIC_PATTERN
|
|
19
|
+
|
|
20
|
+
# Returns the default options used by this validator when per-call options
|
|
21
|
+
# are not provided.
|
|
22
|
+
#
|
|
23
|
+
# Note that the returned object is the same instance used internally;
|
|
24
|
+
# mutating it (e.g. via setters on {CnpjValidatorOptions}) affects future
|
|
25
|
+
# {#is_valid} calls that do not pass +options+.
|
|
26
|
+
#
|
|
27
|
+
# @return [CnpjValidatorOptions] the instance default options
|
|
28
|
+
attr_reader :options
|
|
29
|
+
|
|
30
|
+
# Creates a new validator with optional default options.
|
|
31
|
+
#
|
|
32
|
+
# Default options apply to every call to {#is_valid} unless overridden by the
|
|
33
|
+
# per-call +options+ argument or keyword overrides. Options control case
|
|
34
|
+
# sensitivity and whether the CNPJ input is alphanumeric or numeric.
|
|
35
|
+
#
|
|
36
|
+
# +options+ and the keyword arguments are never merged with each other: when
|
|
37
|
+
# +options+ is given alone, it determines the default options; a
|
|
38
|
+
# {CnpjValidatorOptions} instance is stored by reference, while a {Hash}
|
|
39
|
+
# builds a new instance. When +options+ is omitted (+nil+), the default
|
|
40
|
+
# options are built exclusively from the keyword arguments, with
|
|
41
|
+
# {CnpjValidatorOptions} filling in its own defaults for every keyword left
|
|
42
|
+
# as +nil+. Passing +options+ together with any non-+nil+ keyword argument
|
|
43
|
+
# raises {InvalidArgumentCombinationError} instead of silently ignoring the
|
|
44
|
+
# keywords.
|
|
45
|
+
#
|
|
46
|
+
# @param options [CnpjValidatorOptions, Hash, nil] default validator options
|
|
47
|
+
# @param keywords [Hash] option keyword overrides (mutually exclusive with
|
|
48
|
+
# +options+; see {CnpjValidatorOptions::OPTION_KEYS})
|
|
49
|
+
# @raise [InvalidArgumentCombinationError] if +options+ and a keyword argument
|
|
50
|
+
# are both given
|
|
51
|
+
# @raise [TypeMismatchError] if any option has an invalid type
|
|
52
|
+
# @raise [ValidationError] if +type+ is not one of the allowed values
|
|
53
|
+
def initialize(options = nil, **keywords)
|
|
54
|
+
@options = resolve_default_options(options, keywords)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Validates a CNPJ input.
|
|
58
|
+
#
|
|
59
|
+
# +options+ and the keyword arguments are never merged with each other: when
|
|
60
|
+
# +options+ is given alone, it fully overrides the instance defaults for this
|
|
61
|
+
# call; otherwise, any given non-+nil+ keyword argument overrides the
|
|
62
|
+
# instance default options for this call. When neither +options+ nor any
|
|
63
|
+
# keyword argument is given, the instance defaults are used as-is. The
|
|
64
|
+
# instance defaults are never mutated by a per-call override. Passing
|
|
65
|
+
# +options+ together with any non-+nil+ keyword argument raises
|
|
66
|
+
# {InvalidArgumentCombinationError} instead of silently ignoring the keywords.
|
|
67
|
+
#
|
|
68
|
+
# @param cnpj_input [String, Array<String>] CNPJ value as a string or array of
|
|
69
|
+
# strings
|
|
70
|
+
# @param options [CnpjValidatorOptions, Hash, nil] per-call option overrides
|
|
71
|
+
# @param keywords [Hash] per-call option keyword overrides (mutually exclusive
|
|
72
|
+
# with +options+; see {CnpjValidatorOptions::OPTION_KEYS})
|
|
73
|
+
# @return [Boolean] +true+ when valid, +false+ otherwise
|
|
74
|
+
# @raise [InvalidArgumentCombinationError] if +options+ and a keyword argument
|
|
75
|
+
# are both given
|
|
76
|
+
# @raise [TypeMismatchError] if the input is not a +String+ or +Array<String>+,
|
|
77
|
+
# or if any option has an invalid type
|
|
78
|
+
# @raise [ValidationError] if +type+ is not one of the allowed values
|
|
79
|
+
#
|
|
80
|
+
# @example
|
|
81
|
+
# CnpjValidator.new(type: 'numeric').is_valid('12651319934215') # => true
|
|
82
|
+
# rubocop:disable Naming/PredicatePrefix -- public API matches JS/Python `is_valid`
|
|
83
|
+
def is_valid(cnpj_input, options = nil, **keywords)
|
|
84
|
+
actual_options = resolve_call_options(options, keywords)
|
|
85
|
+
sanitized_cnpj = sanitize_cnpj_input(cnpj_input, actual_options)
|
|
86
|
+
|
|
87
|
+
return false unless valid_sanitized_length?(sanitized_cnpj)
|
|
88
|
+
return false unless numeric_check_digits?(sanitized_cnpj)
|
|
89
|
+
|
|
90
|
+
validate_with_check_digits(sanitized_cnpj)
|
|
91
|
+
end
|
|
92
|
+
# rubocop:enable Naming/PredicatePrefix
|
|
93
|
+
|
|
94
|
+
private
|
|
95
|
+
|
|
96
|
+
def resolve_default_options(options, keywords)
|
|
97
|
+
keyword_overrides = compact_keyword_overrides(keywords)
|
|
98
|
+
raise_ambiguous_options! if options && !keyword_overrides.empty?
|
|
99
|
+
return options if options.is_a?(CnpjValidatorOptions)
|
|
100
|
+
return CnpjValidatorOptions.new(options) if options
|
|
101
|
+
|
|
102
|
+
CnpjValidatorOptions.new(**keywords)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def resolve_call_options(options, keywords)
|
|
106
|
+
keyword_overrides = compact_keyword_overrides(keywords)
|
|
107
|
+
raise_ambiguous_options! if options && !keyword_overrides.empty?
|
|
108
|
+
return @options.copy.set(options) if options
|
|
109
|
+
return @options if keyword_overrides.empty?
|
|
110
|
+
|
|
111
|
+
@options.copy.set(keyword_overrides)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def compact_keyword_overrides(keywords)
|
|
115
|
+
CnpjValidatorOptions::OPTION_KEYS.each_with_object({}) do |key, overrides|
|
|
116
|
+
value = keywords[key]
|
|
117
|
+
overrides[key] = value unless value.nil?
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def raise_ambiguous_options!
|
|
122
|
+
option_keywords = CnpjValidatorOptions::OPTION_KEYS.map { |key| "#{key}:" }.join(', ')
|
|
123
|
+
|
|
124
|
+
raise InvalidArgumentCombinationError,
|
|
125
|
+
"Pass either an options instance/Hash to `options`, or keyword arguments (#{option_keywords}), " \
|
|
126
|
+
'not both.'
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def sanitize_cnpj_input(cnpj_input, actual_options)
|
|
130
|
+
actual_input = to_string_input(cnpj_input)
|
|
131
|
+
working_input = actual_options.case_sensitive ? actual_input : actual_input.upcase
|
|
132
|
+
|
|
133
|
+
sanitize(working_input, actual_options.type)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def to_string_input(cnpj_input)
|
|
137
|
+
raise TypeMismatchError.new(cnpj_input, 'string or string[]') unless CnpjInput.accept?(cnpj_input)
|
|
138
|
+
|
|
139
|
+
return cnpj_input if cnpj_input.is_a?(String)
|
|
140
|
+
|
|
141
|
+
cnpj_input.join
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def sanitize(value, cnpj_type)
|
|
145
|
+
if cnpj_type == 'numeric'
|
|
146
|
+
value.gsub(NUMERIC_PATTERN, '')
|
|
147
|
+
else
|
|
148
|
+
value.gsub(ALPHANUMERIC_PATTERN, '')
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def numeric_check_digits?(sanitized_cnpj)
|
|
153
|
+
twelfth = sanitized_cnpj[12]
|
|
154
|
+
thirteenth = sanitized_cnpj[13]
|
|
155
|
+
|
|
156
|
+
twelfth.between?('0', '9') && thirteenth.between?('0', '9')
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def valid_sanitized_length?(sanitized_cnpj)
|
|
160
|
+
sanitized_cnpj.length == CnpjValidatorOptions::CNPJ_LENGTH
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def validate_with_check_digits(sanitized_cnpj)
|
|
164
|
+
cnpj_check_digits = CnpjDV::CnpjCheckDigits.new(sanitized_cnpj)
|
|
165
|
+
|
|
166
|
+
sanitized_cnpj == cnpj_check_digits.cnpj
|
|
167
|
+
rescue CnpjDV::Error
|
|
168
|
+
false
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|