br-utilities 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.
data/README.md ADDED
@@ -0,0 +1,1303 @@
1
+ ![br-utilities for Ruby](https://br-utils.vercel.app/img/cover_br-utils.jpg)
2
+
3
+ [![Gem Version](https://img.shields.io/gem/v/br-utilities)](https://rubygems.org/gems/br-utilities)
4
+ [![Gem Downloads](https://img.shields.io/gem/dt/br-utilities)](https://rubygems.org/gems/br-utilities)
5
+ [![Ruby Version](https://img.shields.io/gem/rv/br-utilities)](https://www.ruby-lang.org/)
6
+ [![Test Status](https://img.shields.io/github/actions/workflow/status/LacusSolutions/br-utils-ruby/ci.yml?label=ci/cd)](https://github.com/LacusSolutions/br-utils-ruby/actions)
7
+ [![Last Update Date](https://img.shields.io/github/last-commit/LacusSolutions/br-utils-ruby)](https://github.com/LacusSolutions/br-utils-ruby)
8
+ [![Project License](https://img.shields.io/github/license/LacusSolutions/br-utils-ruby)](https://github.com/LacusSolutions/br-utils-ruby/blob/main/LICENSE)
9
+
10
+ > 🚀 **Full support for the [new alphanumeric CNPJ format](https://github.com/user-attachments/files/23937961/calculodvcnpjalfanaumerico.pdf).**
11
+
12
+ > 🌎 [Acessar documentação em português](./README.pt.md)
13
+
14
+ A Ruby toolkit to handle the main operations with Brazilian-related data: CPF (Individual's Taxpayer ID) and CNPJ (Business Tax ID). It wraps [`cpf-utilities`](https://rubygems.org/gems/cpf-utilities) and [`cnpj-utilities`](https://rubygems.org/gems/cnpj-utilities) in a single façade class (`BrUtils`).
15
+
16
+ ## Ruby Support
17
+
18
+ | ![Ruby 3.1](https://img.shields.io/badge/Ruby-3.1-CC342D?logo=ruby&logoColor=white) | ![Ruby 3.2](https://img.shields.io/badge/Ruby-3.2-CC342D?logo=ruby&logoColor=white) | ![Ruby 3.3](https://img.shields.io/badge/Ruby-3.3-CC342D?logo=ruby&logoColor=white) | ![Ruby 3.4](https://img.shields.io/badge/Ruby-3.4-CC342D?logo=ruby&logoColor=white) | ![Ruby 4.0](https://img.shields.io/badge/Ruby-4.0-CC342D?logo=ruby&logoColor=white) |
19
+ | --- | --- | --- | --- | --- |
20
+ | Passing ✔ | Passing ✔ | Passing ✔ | Passing ✔ | Passing ✔ |
21
+
22
+ Requires Ruby **≥ 3.1** (see `required_ruby_version` in the gemspec).
23
+
24
+ ## Features
25
+
26
+ - ✅ **Unified top-level API**: Class helpers `BrUtils.cpf` / `.cnpj` delegate to `BrUtils::DEFAULT.cpf` / `.cnpj`; each domain offers `format`, `generate`, and `is_valid`
27
+ - ✅ **Bundled domains**: [`cpf-utilities`](https://rubygems.org/gems/cpf-utilities) and [`cnpj-utilities`](https://rubygems.org/gems/cnpj-utilities) installed together
28
+ - ✅ **Alphanumeric CNPJ**: Full support for the new alphanumeric CNPJ format (introduced in 2026)
29
+ - ✅ **Reusable instance**: `BrUtils` class with optional default CPF and CNPJ settings (nested mappings, flat component kwargs, or pre-built utils instances)
30
+ - ✅ **Two-tier access**: Prefer main-class shortcuts at the façade root (`BrUtils::CpfFormatter`, `BrUtils::CnpjValidator`, …); Options, helpers, and errors live under nested package modules (`BrUtils::CpfFmt`, `BrUtils::CnpjUtils`, …). Root siblings (`CpfUtils`, `CnpjUtils`, `CpfFmt`, …) still work
31
+ - ✅ **Per-call overrides**: Configure defaults on the façade / domain utils; override options on a single `format` / `generate` / `is_valid` call
32
+ - ✅ **Error handling**: Domain errors propagate unchanged from the bundled packages; this gem defines `BrUtils::TypeMismatchError` and `BrUtils::InvalidArgumentCombinationError` for API misuse
33
+
34
+ ## Installation
35
+
36
+ Install the gem directly:
37
+
38
+ ```bash
39
+ gem install br-utilities
40
+ ```
41
+
42
+ Or add it to your `Gemfile` and run `bundle install`:
43
+
44
+ ```ruby
45
+ gem 'br-utilities'
46
+ ```
47
+
48
+ This installs **`br-utilities`** together with [`cpf-utilities`](https://rubygems.org/gems/cpf-utilities) and [`cnpj-utilities`](https://rubygems.org/gems/cnpj-utilities) (which in turn pull in the CPF and CNPJ component packages). You do **not** need separate `gem install` / `gem` lines for the domain packages when using **`br-utilities`**.
49
+
50
+ ## Require
51
+
52
+ ```ruby
53
+ require 'br-utilities'
54
+ ```
55
+
56
+ ## Quick Start
57
+
58
+ Prefer the aggregator class helpers (`BrUtils.cpf` / `BrUtils.cnpj`) for one-off calls — they forward to `BrUtils::DEFAULT`:
59
+
60
+ ```ruby
61
+ require 'br-utilities'
62
+
63
+ cpf = '12345678909'
64
+ cnpj = '03603568000195'
65
+
66
+ # CPF (personal ID)
67
+ BrUtils.cpf.format(cpf) # => "123.456.789-09"
68
+ BrUtils.cpf.generate(format: true) # => e.g. "478.442.410-55"
69
+ BrUtils.cpf.is_valid('123.456.789-09') # => true
70
+
71
+ # CNPJ (business ID)
72
+ BrUtils.cnpj.format(cnpj) # => "03.603.568/0001-95"
73
+ BrUtils.cnpj.generate(format: true) # => e.g. "AB.123.CDE/0001-55"
74
+ BrUtils.cnpj.is_valid('98765432000198') # => true
75
+ ```
76
+
77
+ **With domain aggregators:**
78
+
79
+ ```ruby
80
+ require 'br-utilities'
81
+
82
+ cpf = '12345678909'
83
+ cnpj = '03603568000195'
84
+
85
+ CpfUtils.format(cpf) # => "123.456.789-09"
86
+ CnpjUtils.format(cnpj) # => "03.603.568/0001-95"
87
+ CpfUtils.is_valid(cpf) # => true
88
+ CnpjUtils.is_valid(cnpj) # => true
89
+ ```
90
+
91
+ **With functional helpers** (root sibling modules, loaded by this gem):
92
+
93
+ ```ruby
94
+ require 'br-utilities'
95
+
96
+ cpf = '12345678909'
97
+ cnpj = '03603568000195'
98
+
99
+ CpfFmt.cpf_fmt(cpf) # => "123.456.789-09"
100
+ CpfVal.cpf_val(cpf) # => true
101
+ CnpjFmt.cnpj_fmt(cnpj) # => "03.603.568/0001-95"
102
+ CnpjVal.cnpj_val(cnpj) # => true
103
+ ```
104
+
105
+ ## Usage
106
+
107
+ You can work in these equivalent ways:
108
+
109
+ 1. **`BrUtils.cpf` / `.cnpj`** — class helpers for quick one-off calls (forward to `DEFAULT`).
110
+ 2. **`BrUtils::DEFAULT`** — mutable shared singleton (same object the class helpers use; process-wide / not thread-isolated).
111
+ 3. **`BrUtils.new`** — configurable instance with shared defaults across both CPF and CNPJ domains.
112
+ 4. **Domain aggregators** — `CpfUtils` / `CnpjUtils` (or `BrUtils::CpfUtils` / `BrUtils::CnpjUtils`) directly.
113
+ 5. **Main classes under `BrUtils`** — `BrUtils::CpfFormatter`, `BrUtils::CnpjGenerator`, and related shortcuts.
114
+ 6. **Nested package modules** — Options, helpers, errors, and types via `BrUtils::CpfFmt` / `CpfGen` / `CpfVal` / `CnpjFmt` / `CnpjGen` / `CnpjVal` / `CpfUtils` / `CnpjUtils`.
115
+ 7. **Root sibling modules** (still supported) — `CpfFmt`, `CnpjUtils`, and the rest unchanged.
116
+
117
+ All approaches expose the same options and behavior within each domain. For exhaustive option tables and component-specific details, see the README of each [bundled package](#bundled-packages).
118
+
119
+ ### Class helpers (`BrUtils.cpf` / `.cnpj`)
120
+
121
+ These class methods return the same domain utils instances as `BrUtils::DEFAULT`. Prefer them for one-off calls:
122
+
123
+ ```ruby
124
+ BrUtils.cpf.format('12345678909')
125
+ BrUtils.cpf.generate(format: true)
126
+ BrUtils.cpf.is_valid('12345678909')
127
+
128
+ BrUtils.cnpj.format('03603568000195')
129
+ BrUtils.cnpj.generate(type: 'numeric')
130
+ BrUtils.cnpj.is_valid('98765432000198')
131
+ ```
132
+
133
+ ### `BrUtils::DEFAULT` (default instance)
134
+
135
+ `BrUtils::DEFAULT` is the pre-built, **mutable** singleton behind the class helpers (parity with the JS default export / Python `br_utils`). Its configuration is **process-wide and shared across threads**: mutating it (e.g. `DEFAULT.cpf = …`) affects subsequent `BrUtils.cpf` / `.cnpj` calls for every caller in the process. Prefer `BrUtils.new` or per-call options for concurrent or isolated work; custom instances stay independent of `DEFAULT`:
136
+
137
+ ```ruby
138
+ BrUtils::DEFAULT.cpf = CpfUtils.new(formatter: { dash_key: '|' })
139
+ BrUtils.cpf.format('12345678909') # => "123.456.789|09"
140
+
141
+ custom = BrUtils.new
142
+ custom.cpf.format('12345678909') # => "123.456.789-09" (unaffected)
143
+ ```
144
+
145
+ ### `BrUtils` (class)
146
+
147
+ For custom default CPF or CNPJ utils, create your own instance:
148
+
149
+ ```ruby
150
+ require 'br-utilities'
151
+
152
+ utils = BrUtils.new(
153
+ cpf: {
154
+ formatter: { hidden: true, hidden_key: '#' },
155
+ generator: { format: true }
156
+ },
157
+ cnpj: {
158
+ formatter: { hidden: true },
159
+ generator: { type: 'numeric', format: true },
160
+ validator: { type: 'numeric' }
161
+ }
162
+ )
163
+
164
+ utils.cpf.format('12345678909') # => "123.###.###-##"
165
+ utils.cpf.generate # => e.g. "005.265.352-88"
166
+ utils.cnpj.format('03603568000195') # => "03.603.***/****-**"
167
+ utils.cnpj.generate # => e.g. "73.008.535/0005-06"
168
+
169
+ # Access or replace internal domain instances
170
+ utils.cpf # => CpfUtils
171
+ utils.cnpj # => CnpjUtils
172
+ ```
173
+
174
+ - **`BrUtils.new(settings = nil, **keywords)`**: Optional settings. Pass either a settings `Hash` with `:cpf` and/or `:cnpj` keys, **or** the same keys (plus flat component kwargs) as keyword arguments — not both (passing both raises `BrUtils::InvalidArgumentCombinationError`).
175
+ - **`:cpf` / `:cnpj`**: A pre-built `CpfUtils` / `CnpjUtils` instance **or** a configuration `Hash` spread into the corresponding utils constructor. Within that `Hash`, each resource key (`:formatter`, `:generator`, and `:validator` for CNPJ) accepts either an options object or a mapping of option values.
176
+ - **`:cpf_formatter`**, **`:cpf_generator`**, **`:cnpj_formatter`**, **`:cnpj_generator`**, **`:cnpj_validator`**: Flat convenience arguments when only individual components need customization. They are ignored when the corresponding `:cpf` or `:cnpj` argument is provided.
177
+ - **`#cpf`**, **`#cnpj`**: Accessors (getters and setters) for the domain utils instances. Setters accept a utils instance, a configuration `Hash`, or `nil` to reset to defaults (replaces the entire instance; does not merge).
178
+
179
+ Flat constructor options (alternative to nested `:cpf` / `:cnpj` mappings):
180
+
181
+ ```ruby
182
+ require 'br-utilities'
183
+
184
+ utils = BrUtils.new(
185
+ cpf_formatter: CpfFmt::CpfFormatterOptions.new(hidden: true, hidden_key: '#'),
186
+ cpf_generator: CpfGen::CpfGeneratorOptions.new(format: true),
187
+ cnpj_formatter: CnpjFmt::CnpjFormatterOptions.new(hidden: true, hidden_key: '#'),
188
+ cnpj_generator: CnpjGen::CnpjGeneratorOptions.new(format: true, type: 'numeric'),
189
+ cnpj_validator: CnpjVal::CnpjValidatorOptions.new(type: 'numeric')
190
+ )
191
+ ```
192
+
193
+ Passing a settings `Hash` positional argument together with any keyword raises:
194
+
195
+ ```ruby
196
+ BrUtils.new({ cpf: {} }, cnpj: CnpjUtils.new)
197
+ # raises BrUtils::InvalidArgumentCombinationError
198
+ ```
199
+
200
+ ### Instance defaults and per-call overrides
201
+
202
+ ```ruby
203
+ require 'br-utilities'
204
+
205
+ utils = BrUtils.new(
206
+ cpf: {
207
+ formatter: { hidden: true, hidden_key: '#' },
208
+ generator: { format: true }
209
+ },
210
+ cnpj: {
211
+ formatter: { hidden: true, hidden_key: '#' },
212
+ generator: { format: true },
213
+ validator: { type: 'numeric' }
214
+ }
215
+ )
216
+
217
+ cpf = '12345678909'
218
+ cnpj = '03603568000195'
219
+
220
+ utils.cpf.format(cpf) # => "123.###.###-##"
221
+ utils.cpf.format(cpf, hidden: false) # this call only: unmasked
222
+ utils.cpf.generate(format: false) # this call only: compact output
223
+
224
+ utils.cnpj.format(cnpj) # => "03.603.###/####-##"
225
+ utils.cnpj.format(cnpj, hidden: false) # this call only: unmasked
226
+ utils.cnpj.is_valid('1QB5UKALPYFP59') # => false (instance validator is numeric-only)
227
+ utils.cnpj.is_valid( # => true for this call
228
+ '1QB5UKALPYFP59',
229
+ type: 'alphanumeric'
230
+ )
231
+ ```
232
+
233
+ Passing a `CnpjFmt::CnpjFormatterOptions`, `CnpjGen::CnpjGeneratorOptions`, or `CnpjVal::CnpjValidatorOptions` instance into the `BrUtils` constructor stores that object by reference — mutating it later affects subsequent calls with no per-call override.
234
+
235
+ To change a single nested option without replacing the whole domain utils, mutate via the domain accessors (e.g. `utils.cpf.formatter.options.hidden = true`).
236
+
237
+ ### CPF operations
238
+
239
+ CPF methods are accessed via `BrUtils.cpf`, `utils.cpf`, `CpfUtils`, or the `CpfFmt` / `CpfGen` / `CpfVal` helpers. CPF uses the API from [`cpf-utilities`](../cpf-utilities/README.md).
240
+
241
+ #### Formatting (`#format` / `CpfFmt.cpf_fmt`)
242
+
243
+ | Option | Type | Default | Description |
244
+ |--------|------|---------|-------------|
245
+ | `hidden` | `Boolean` | `false` | When `true`, mask digits in `hidden_start`–`hidden_end` with `hidden_key` |
246
+ | `hidden_key` | `String` | `'*'` | Character(s) used to replace masked digits |
247
+ | `hidden_start` | `Integer` | `3` | Start index (0–10, inclusive) of the range to hide |
248
+ | `hidden_end` | `Integer` | `10` | End index (0–10, inclusive) of the range to hide |
249
+ | `dot_key` | `String` | `'.'` | Dot delimiter (e.g. in `123.456.789`) |
250
+ | `dash_key` | `String` | `'-'` | Dash delimiter (e.g. before check digits `…-09`) |
251
+ | `escape` | `Boolean` | `false` | When `true`, escape HTML special characters in the result |
252
+ | `encode` | `Boolean` | `false` | When `true`, URL-encode the result (similar to JavaScript `encodeURIComponent`) |
253
+ | `on_fail` | `Proc` / callable | returns `''` | Callback when sanitized input length ≠ 11; return value is used as result |
254
+
255
+ Default **`on_fail`** returns an empty string. Invalid length does **not** raise from `#format`.
256
+
257
+ ```ruby
258
+ require 'br-utilities'
259
+
260
+ cpf = '12345678909'
261
+
262
+ BrUtils.cpf.format(cpf) # => "123.456.789-09"
263
+ BrUtils.cpf.format(cpf, hidden: true, hidden_key: '#') # => "123.###.###-##"
264
+ BrUtils.cpf.format(cpf, dot_key: '', dash_key: '_') # => "123456789_09"
265
+
266
+ CpfFmt.cpf_fmt(cpf, hidden: true) # => "123.***.***-**"
267
+ ```
268
+
269
+ #### Generation (`#generate` / `CpfGen.cpf_gen`)
270
+
271
+ | Option | Type | Default | Description |
272
+ |--------|------|---------|-------------|
273
+ | `format` | `Boolean` | `false` | When `true`, return the generated CPF in standard format (`000.000.000-00`) |
274
+ | `prefix` | `String` | `''` | Partial start string (0–9 digits). Non-digits are stripped; missing characters are generated and check digits computed. Prefixes longer than 9 digits are truncated silently. |
275
+
276
+ Prefix rules: the base (first 9 digits) cannot be all zeros; 9 repeated digits (e.g. `999999999`) are not allowed.
277
+
278
+ ```ruby
279
+ require 'br-utilities'
280
+
281
+ BrUtils.cpf.generate # => e.g. "11508890048"
282
+ BrUtils.cpf.generate(format: true) # => e.g. "661.134.831-00"
283
+ BrUtils.cpf.generate(prefix: '123456789') # => "12345678909"
284
+ CpfGen.cpf_gen(prefix: '123456789', format: true) # => "123.456.789-09"
285
+ ```
286
+
287
+ #### Validation (`#is_valid` / `CpfVal.cpf_val`)
288
+
289
+ Accepts formatted or unformatted CPF strings (or an `Array` of strings). Returns **`true`** or **`false`** without raising for invalid CPF. No validator options exist.
290
+
291
+ ```ruby
292
+ require 'br-utilities'
293
+
294
+ BrUtils.cpf.is_valid('12345678909') # => true
295
+ BrUtils.cpf.is_valid('123.456.789-09') # => true
296
+ BrUtils.cpf.is_valid('12345678900') # => false
297
+ CpfVal.cpf_val('12345678909') # => true
298
+ ```
299
+
300
+ ### CNPJ operations
301
+
302
+ CNPJ methods are accessed via `BrUtils.cnpj`, `utils.cnpj`, `CnpjUtils`, or the `CnpjFmt` / `CnpjGen` / `CnpjVal` helpers. CNPJ uses the API from [`cnpj-utilities`](../cnpj-utilities/README.md).
303
+
304
+ #### Formatting (`#format` / `CnpjFmt.cnpj_fmt`)
305
+
306
+ | Option | Type | Default | Description |
307
+ |--------|------|---------|-------------|
308
+ | `hidden` | `Boolean` | `false` | When `true`, mask characters in `hidden_start`–`hidden_end` with `hidden_key` |
309
+ | `hidden_key` | `String` | `'*'` | Character(s) used to replace masked characters |
310
+ | `hidden_start` | `Integer` | `5` | Start index (0–13, inclusive) of the range to hide |
311
+ | `hidden_end` | `Integer` | `13` | End index (0–13, inclusive) of the range to hide |
312
+ | `dot_key` | `String` | `'.'` | Dot delimiter (e.g. in `12.345.678`) |
313
+ | `slash_key` | `String` | `'/'` | Slash delimiter (e.g. before branch `…/0001-90`) |
314
+ | `dash_key` | `String` | `'-'` | Dash delimiter (e.g. before check digits `…-90`) |
315
+ | `escape` | `Boolean` | `false` | When `true`, escape HTML special characters in the result |
316
+ | `encode` | `Boolean` | `false` | When `true`, URL-encode the result (similar to JavaScript `encodeURIComponent`) |
317
+ | `on_fail` | `Proc` / callable | returns `''` | Callback when sanitized input length ≠ 14; return value is used as result |
318
+
319
+ Default **`on_fail`** returns an empty string. Wrong input types raise **`CnpjFmt::TypeMismatchError`**.
320
+
321
+ ```ruby
322
+ require 'br-utilities'
323
+
324
+ cnpj = '03603568000195'
325
+
326
+ BrUtils.cnpj.format(cnpj) # => "03.603.568/0001-95"
327
+ BrUtils.cnpj.format('12ABC34500DE99') # => "12.ABC.345/00DE-99"
328
+ BrUtils.cnpj.format( # => "03.603.###/####-##"
329
+ cnpj,
330
+ hidden: true,
331
+ hidden_key: '#'
332
+ )
333
+ BrUtils.cnpj.format( # => "03603568|0001_95"
334
+ cnpj,
335
+ dot_key: '',
336
+ slash_key: '|',
337
+ dash_key: '_'
338
+ )
339
+
340
+ CnpjFmt.cnpj_fmt(cnpj) # => "03.603.568/0001-95"
341
+ ```
342
+
343
+ #### Generation (`#generate` / `CnpjGen.cnpj_gen`)
344
+
345
+ | Option | Type | Default | Description |
346
+ |--------|------|---------|-------------|
347
+ | `format` | `Boolean` | `false` | When `true`, return the generated CNPJ in standard format (`00.000.000/0000-00`) |
348
+ | `prefix` | `String` | `''` | Partial start string (0–12 alphanumeric chars). Missing characters are generated and check digits computed. |
349
+ | `type` | `String` | `'alphanumeric'` | Character set for the randomly generated part: `'numeric'`, `'alphabetic'`, or `'alphanumeric'`. **Check digits are always numeric.** |
350
+
351
+ Prefix rules: base ID (first 8 chars) and branch ID (chars 9–12) cannot be all zeros; 12 repeated digits (e.g. `111111111111`) are also not allowed.
352
+
353
+ ```ruby
354
+ require 'br-utilities'
355
+
356
+ BrUtils.cnpj.generate # => e.g. "1GJTR3J3XSSA96"
357
+ BrUtils.cnpj.generate(format: true) # => e.g. "V1.J0V.8WE/DVZ7-50"
358
+ BrUtils.cnpj.generate( # => e.g. "12345678855883"
359
+ prefix: '12345678',
360
+ type: 'numeric'
361
+ )
362
+ CnpjGen.cnpj_gen(type: 'numeric') # => e.g. "65453043000178"
363
+ ```
364
+
365
+ #### Validation (`#is_valid` / `CnpjVal.cnpj_val`)
366
+
367
+ | Option | Type | Default | Description |
368
+ |--------|------|---------|-------------|
369
+ | `case_sensitive` | `Boolean` | `true` | When `false`, lowercase letters are accepted for alphanumeric CNPJ (input is uppercased before validation). |
370
+ | `type` | `String` | `'alphanumeric'` | `'numeric'`: only digits (0–9); `'alphanumeric'`: digits and letters (0–9, A–Z). |
371
+
372
+ ```ruby
373
+ require 'br-utilities'
374
+
375
+ BrUtils.cnpj.is_valid('98765432000198') # => true
376
+ BrUtils.cnpj.is_valid('98765432000199') # => false
377
+ BrUtils.cnpj.is_valid('1QB5UKALPYFP59') # => true
378
+ BrUtils.cnpj.is_valid('1QB5UKALpyfp59') # => false
379
+ BrUtils.cnpj.is_valid( # => true
380
+ '1QB5UKALpyfp59',
381
+ case_sensitive: false
382
+ )
383
+ BrUtils.cnpj.is_valid( # => false
384
+ '1QB5UKALPYFP59',
385
+ type: 'numeric'
386
+ )
387
+
388
+ CnpjVal.cnpj_val('98765432000198') # => true
389
+ CnpjVal.cnpj_val('1QB5UKALpyfp59', case_sensitive: false) # => true
390
+ CnpjVal.cnpj_val('1QB5UKALPYFP59', type: 'numeric') # => false
391
+ ```
392
+
393
+ Invalid CNPJ returns **`false`** without raising. Wrong input types raise **`CnpjVal::TypeMismatchError`**.
394
+
395
+ ### Domain aggregators (standalone)
396
+
397
+ Use `CpfUtils` or `CnpjUtils` directly when you only need one domain:
398
+
399
+ ```ruby
400
+ require 'br-utilities'
401
+
402
+ cpf_utils = CpfUtils.new(
403
+ formatter: { hidden: true },
404
+ generator: { format: true }
405
+ )
406
+
407
+ cnpj_utils = CnpjUtils.new(
408
+ formatter: { hidden: true },
409
+ generator: { format: true },
410
+ validator: { type: 'numeric' }
411
+ )
412
+
413
+ cpf_utils.format('12345678909') # => "123.***.***-**"
414
+ cnpj_utils.format('03603568000195') # => "03.603.***/****-**"
415
+ ```
416
+
417
+ ### Accessing components
418
+
419
+ Each domain aggregator exposes its internal formatter, generator, and validator:
420
+
421
+ ```ruby
422
+ require 'br-utilities'
423
+
424
+ utils = BrUtils.new
425
+
426
+ utils.cpf.formatter.format('12345678909', hidden: true) # => "123.***.***-**"
427
+ utils.cpf.generator.generate(format: true) # => e.g. "545.507.690-68"
428
+ utils.cpf.validator.is_valid('12345678909') # => true
429
+
430
+ utils.cnpj.formatter.format('12ABC34500DE99') # => "12.ABC.345/00DE-99"
431
+ utils.cnpj.generator.generate(format: true) # => e.g. "8O.BE5.2KL/UI0Y-06"
432
+ utils.cnpj.validator.is_valid('03603568000195') # => true
433
+ ```
434
+
435
+ ### Using component classes and nested modules
436
+
437
+ Preferred paths after `require 'br-utilities'`:
438
+
439
+ ```ruby
440
+ require 'br-utilities'
441
+
442
+ # Main classes at the façade root
443
+ formatter = BrUtils::CpfFormatter.new(hidden: true)
444
+ generator = BrUtils::CnpjGenerator.new(type: 'numeric')
445
+ validator = BrUtils::CnpjValidator.new
446
+
447
+ formatter.format('12345678909') # => "123.***.***-**"
448
+
449
+ # Options, helpers, and errors under nested package modules
450
+ options = BrUtils::CpfFmt::CpfFormatterOptions.new(dash_key: '|')
451
+ BrUtils::CpfFmt.cpf_fmt('12345678909') # => "123.456.789-09"
452
+
453
+ begin
454
+ BrUtils::CnpjFmt.cnpj_fmt(12_345)
455
+ rescue BrUtils::CnpjFmt::TypeMismatchError
456
+ # wrong input type
457
+ end
458
+ ```
459
+
460
+ Root siblings remain supported (same objects as the nests):
461
+
462
+ ```ruby
463
+ CpfFmt.cpf_fmt('12345678909', dash_key: '|') # => "123.456.789|09"
464
+ CpfGen.cpf_gen(format: true) # => e.g. "478.442.410-55"
465
+ CpfVal.cpf_val('12345678909') # => true
466
+ CnpjFmt.cnpj_fmt('01ABC234000X56', slash_key: '|') # => "01.ABC.234|000X-56"
467
+ CnpjGen.cnpj_gen(type: 'numeric') # => e.g. "65453043000178"
468
+ CnpjVal.cnpj_val('9JN7MGLJZXIO50') # => true
469
+ ```
470
+
471
+ See [`cpf-utilities`](../cpf-utilities/README.md) and [`cnpj-utilities`](../cnpj-utilities/README.md) for full option and error details.
472
+
473
+ ### Mixing styles
474
+
475
+ Use `BrUtils` where a shared configuration helps, and standalone components or helpers elsewhere — they are the same underlying classes:
476
+
477
+ ```ruby
478
+ require 'br-utilities'
479
+
480
+ utils = BrUtils.new(cnpj: { validator: { type: 'numeric' } })
481
+
482
+ # Via façade
483
+ utils.cpf.format('12345678909') # => "123.456.789-09"
484
+
485
+ # Via component returned by the façade
486
+ utils.cnpj.formatter.format('12ABC34500DE99') # => "12.ABC.345/00DE-99"
487
+
488
+ # Via a separate component instance
489
+ BrUtils::CnpjFormatter.new.format('03603568000195') # => "03.603.568/0001-95"
490
+
491
+ # Via functional helpers
492
+ CpfFmt.cpf_fmt('12345678909') # => "123.456.789-09"
493
+ CnpjVal.cnpj_val('98.765.432/0001-98') # => true
494
+ ```
495
+
496
+ ## API
497
+
498
+ ### Exports
499
+
500
+ After `require 'br-utilities'`:
501
+
502
+ - **`BrUtils`**: Façade class to create an instance with optional default CPF and CNPJ utils settings.
503
+ - **`BrUtils.cpf` / `.cnpj`**: Class helpers that forward to `BrUtils::DEFAULT` domain accessors.
504
+ - **`BrUtils::DEFAULT`**: Mutable pre-built `BrUtils` instance (same object the class helpers use). Process-wide / shared across threads — prefer `BrUtils.new` or per-call options under concurrency.
505
+ - **`BrUtils::VERSION`**: Gem version string.
506
+ - **Main-class shortcuts**: `BrUtils::CpfFormatter`, `BrUtils::CpfFormatterOptions`, `BrUtils::CpfGenerator`, `BrUtils::CpfGeneratorOptions`, `BrUtils::CpfValidator`, `BrUtils::CnpjFormatter`, `BrUtils::CnpjFormatterOptions`, `BrUtils::CnpjGenerator`, `BrUtils::CnpjGeneratorOptions`, `BrUtils::CnpjValidator`, `BrUtils::CnpjValidatorOptions` (same objects as the sibling classes). Error-marker shortcuts: `BrUtils::CpfFormatterError`, `BrUtils::CpfGeneratorError`, `BrUtils::CpfValidatorError`, `BrUtils::CnpjFormatterError`, `BrUtils::CnpjGeneratorError`, `BrUtils::CnpjValidatorError`.
507
+ - **Nested package modules**: `BrUtils::CpfUtils`, `BrUtils::CnpjUtils`, `BrUtils::CpfFmt`, `BrUtils::CpfGen`, `BrUtils::CpfVal`, `BrUtils::CnpjFmt`, `BrUtils::CnpjGen`, `BrUtils::CnpjVal` — full sibling surface (Options, helpers, errors, types).
508
+ - **Root sibling modules** (still supported): `CpfUtils`, `CnpjUtils`, `CpfFmt`, `CpfGen`, `CpfVal`, `CnpjFmt`, `CnpjGen`, `CnpjVal` — same objects as the nests.
509
+
510
+ ### Errors & Exceptions
511
+
512
+ `BrUtils` defines only API-misuse errors for this gem’s argument rules. Domain errors are raised by the bundled packages and propagate unchanged.
513
+
514
+ #### Defined by `br-utilities`
515
+
516
+ Errors defined by this gem are **API misuse** only (wrong type or invalid argument combination). Every custom error includes the `BrUtils::Error` marker module. This gem defines **no** `BrUtils::DomainError` and no domain leaves — domain failures come only from the [bundled packages](#propagated-from-bundled-packages) and keep those packages’ namespaces (`CpfFmt::…`, `CnpjGen::…`, …).
517
+
518
+ `rescue BrUtils::Error` catches **only** errors this gem raises. It does **not** catch component errors that propagate unchanged.
519
+
520
+ ##### Summary
521
+
522
+ | Class | Inherits from | Category | Trigger condition |
523
+ |-------|---------------|----------|-------------------|
524
+ | `BrUtils::InvalidArgumentCombinationError` | `BrUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include BrUtils::Error`) | API misuse | Non-`nil` settings `Hash` passed together with any non-`nil` keyword argument |
525
+ | `BrUtils::TypeMismatchError` | `BrUtils::TypeMismatchError < TypeError < StandardError` (+ `include BrUtils::Error`) | API misuse | Non-`nil` `settings` argument to `BrUtils.new` is not a `Hash` |
526
+
527
+ ##### `BrUtils::Error` (marker module)
528
+
529
+ - **Inheritance:** module marker mixed into every custom error this gem raises via `include` (not a class).
530
+ - **Category:** N/A (rescue target only) — not a failure mode by itself.
531
+ - **When it is raised:** Never raised directly; included by every custom error this gem raises.
532
+ - **Example:** N/A
533
+ - **How to rescue it:**
534
+
535
+ ```ruby
536
+ rescue BrUtils::Error
537
+ # TypeMismatchError, InvalidArgumentCombinationError from this gem only
538
+ # (not CpfFmt::*, CnpjGen::*, or other bundled-package errors)
539
+ ```
540
+
541
+ ##### `BrUtils::TypeMismatchError`
542
+
543
+ - **Inheritance:** `BrUtils::TypeMismatchError < TypeError < StandardError` (includes `BrUtils::Error`)
544
+ - **Category:** API misuse — the caller passed a value of the wrong type.
545
+ - **When it is raised:** Raised when `BrUtils.new` receives a non-`nil` `settings` argument that is not a `Hash`.
546
+ - **Example:**
547
+
548
+ ```ruby
549
+ BrUtils.new('not-a-hash') # raises BrUtils::TypeMismatchError
550
+ BrUtils.new(false) # raises BrUtils::TypeMismatchError (false is non-nil)
551
+ ```
552
+
553
+ - **How to rescue it:**
554
+
555
+ ```ruby
556
+ rescue BrUtils::TypeMismatchError
557
+ # this gem's type-contract violation
558
+
559
+ rescue TypeError
560
+ # native type errors, including this gem's TypeMismatchError
561
+ ```
562
+
563
+ ##### `BrUtils::InvalidArgumentCombinationError`
564
+
565
+ - **Inheritance:** `BrUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (includes `BrUtils::Error`)
566
+ - **Category:** API misuse — the caller mixed mutually exclusive argument patterns.
567
+ - **When it is raised:** Raised when `BrUtils.new` receives both a non-`nil` settings `Hash` and any non-`nil` keyword argument (`cpf:`, `cnpj:`, `cpf_formatter:`, …) at the same time.
568
+ - **Example:**
569
+
570
+ ```ruby
571
+ BrUtils.new({ cpf: { formatter: { hidden: true } } }, cnpj: { formatter: { hidden: true } })
572
+ # raises BrUtils::InvalidArgumentCombinationError
573
+ ```
574
+
575
+ - **How to rescue it:**
576
+
577
+ ```ruby
578
+ rescue BrUtils::InvalidArgumentCombinationError
579
+ # this gem's invalid signature combination
580
+
581
+ rescue ArgumentError
582
+ # native argument errors, including this gem's InvalidArgumentCombinationError
583
+ ```
584
+
585
+ ##### Rescue granularity
586
+
587
+ Each level is shown as its own standalone example (do not merge them into one `rescue` ladder — a broad native handler would make narrower clauses unreachable).
588
+
589
+ ```ruby
590
+ require 'br-utilities'
591
+
592
+ # 1) Single native class — catches misuse errors of that kind,
593
+ # including non-library ones already handled elsewhere in the consumer's code.
594
+ begin
595
+ BrUtils.new('not-a-hash')
596
+ rescue TypeError
597
+ # BrUtils::TypeMismatchError and any other TypeError (library or not)
598
+ end
599
+
600
+ begin
601
+ BrUtils.new({ cpf: {} }, cnpj: CnpjUtils.new)
602
+ rescue ArgumentError
603
+ # BrUtils::InvalidArgumentCombinationError and any other ArgumentError (library or not)
604
+ end
605
+ ```
606
+
607
+ ```ruby
608
+ require 'br-utilities'
609
+
610
+ # 2) Bundled DomainError — this gem defines no DomainError; domain failures
611
+ # come from bundled packages and keep those namespaces (e.g. CpfFmt, CnpjFmt).
612
+ begin
613
+ BrUtils.new.cpf.format('12345678909', hidden_start: -1)
614
+ rescue CpfFmt::DomainError
615
+ # CpfFmt::OutOfRangeError, CpfFmt::ValidationError, and other DomainError subclasses
616
+ end
617
+
618
+ begin
619
+ BrUtils.new.cnpj.format('91415732000793', hidden_start: -1)
620
+ rescue CnpjFmt::DomainError
621
+ # CnpjFmt::OutOfRangeError, CnpjFmt::ValidationError, and other DomainError subclasses
622
+ end
623
+ ```
624
+
625
+ ```ruby
626
+ require 'br-utilities'
627
+
628
+ # 3) BrUtils::Error — catches everything this gem raises, regardless of native ancestry.
629
+ # Does not catch CpfFmt::*, CnpjGen::*, or other bundled-package errors.
630
+ begin
631
+ BrUtils.new('not-a-hash')
632
+ rescue BrUtils::Error
633
+ # every custom error that includes BrUtils::Error
634
+ end
635
+ ```
636
+
637
+ ```ruby
638
+ require 'br-utilities'
639
+
640
+ # 4) Specific leaf class — catches only that exact failure mode.
641
+ begin
642
+ BrUtils.new('not-a-hash')
643
+ rescue BrUtils::TypeMismatchError
644
+ # only BrUtils::TypeMismatchError
645
+ end
646
+ ```
647
+
648
+ #### Propagated from bundled packages
649
+
650
+ Component errors keep their package namespaces and propagate unchanged through the façade (and via nested / root sibling APIs). Each package also exposes an `*::Error` marker module for library-wide rescue. Invalid CPF/CNPJ **data** on `#is_valid` returns `false` (no domain raise). Formatting length failure is **not** raised by `#format` — it is delivered to **`on_fail`** as `CpfFmt::InvalidLengthError` or `CnpjFmt::InvalidLengthError` (default `on_fail` returns `''`).
651
+
652
+ `CpfUtils::*` / `CnpjUtils::*` misuse errors also propagate when nested aggregators are constructed or called through `BrUtils`. For exhaustive option tables and extra edge cases, see [`cpf-utilities`](../cpf-utilities/README.md) and [`cnpj-utilities`](../cnpj-utilities/README.md).
653
+
654
+ ##### Summary
655
+
656
+ | Class | Inherits from | Category | Trigger condition |
657
+ |-------|---------------|----------|-------------------|
658
+ | `CnpjFmt::InvalidArgumentCombinationError` | `CnpjFmt::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjFmt::Error`) | API misuse | Both an `options` instance/`Hash` and any non-`nil` keyword on `CnpjFormatter` / `cnpj_fmt` |
659
+ | `CnpjFmt::TypeMismatchError` | `CnpjFmt::TypeMismatchError < TypeError < StandardError` (+ `include CnpjFmt::Error`) | API misuse | CNPJ input or formatter option has the wrong type (or `on_fail` return is not a `String`) |
660
+ | `CnpjGen::InvalidArgumentCombinationError` | `CnpjGen::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjGen::Error`) | API misuse | Both an `options` instance/`Hash` and any non-`nil` keyword on `CnpjGenerator` / `cnpj_gen` |
661
+ | `CnpjGen::TypeMismatchError` | `CnpjGen::TypeMismatchError < TypeError < StandardError` (+ `include CnpjGen::Error`) | API misuse | Generator option (`format` / `prefix` / `type`) has the wrong type |
662
+ | `CnpjUtils::InvalidArgumentCombinationError` | `CnpjUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjUtils::Error`) | API misuse | Constructor/`#format`/`#generate`/`#is_valid`/class helpers: non-`nil` settings/options `Hash` (or options instance) with any non-`nil` keyword |
663
+ | `CnpjUtils::TypeMismatchError` | `CnpjUtils::TypeMismatchError < TypeError < StandardError` (+ `include CnpjUtils::Error`) | API misuse | Non-`nil` `settings` argument to `CnpjUtils.new` is not a `Hash` |
664
+ | `CnpjVal::InvalidArgumentCombinationError` | `CnpjVal::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjVal::Error`) | API misuse | Both an `options` instance/`Hash` and any non-`nil` keyword on `CnpjValidator` / `cnpj_val` |
665
+ | `CnpjVal::TypeMismatchError` | `CnpjVal::TypeMismatchError < TypeError < StandardError` (+ `include CnpjVal::Error`) | API misuse | CNPJ input or validator option has the wrong type |
666
+ | `CpfFmt::InvalidArgumentCombinationError` | `CpfFmt::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CpfFmt::Error`) | API misuse | Both an `options` instance/`Hash` and any non-`nil` keyword on `CpfFormatter` / `cpf_fmt` |
667
+ | `CpfFmt::TypeMismatchError` | `CpfFmt::TypeMismatchError < TypeError < StandardError` (+ `include CpfFmt::Error`) | API misuse | CPF input or formatter option has the wrong type (or `on_fail` return is not a `String`) |
668
+ | `CpfGen::InvalidArgumentCombinationError` | `CpfGen::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CpfGen::Error`) | API misuse | Both an `options` instance/`Hash` and any non-`nil` keyword on `CpfGenerator` / `cpf_gen` |
669
+ | `CpfGen::TypeMismatchError` | `CpfGen::TypeMismatchError < TypeError < StandardError` (+ `include CpfGen::Error`) | API misuse | Generator option (`format` / `prefix`) has the wrong type |
670
+ | `CpfUtils::InvalidArgumentCombinationError` | `CpfUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CpfUtils::Error`) | API misuse | Constructor: non-`nil` settings `Hash` with any non-`nil` keyword; or `#format`/`#generate`/class helpers: non-`nil` options `Hash`/`*Options` with any non-`nil` keyword |
671
+ | `CpfUtils::TypeMismatchError` | `CpfUtils::TypeMismatchError < TypeError < StandardError` (+ `include CpfUtils::Error`) | API misuse | Non-`nil` `settings` argument to `CpfUtils.new` is not a `Hash` |
672
+ | `CpfVal::TypeMismatchError` | `CpfVal::TypeMismatchError < TypeError < StandardError` (+ `include CpfVal::Error`) | API misuse | CPF input is not a `String` or `Array` of strings |
673
+ | `CnpjFmt::InvalidLengthError` | `CnpjFmt::InvalidLengthError < CnpjFmt::DomainError < RangeError < StandardError` (+ `include CnpjFmt::Error`) | Domain error | Sanitized length ≠ 14 — **passed to `on_fail`**, not raised by `#format` |
674
+ | `CnpjFmt::OutOfRangeError` | `CnpjFmt::OutOfRangeError < CnpjFmt::DomainError < RangeError < StandardError` (+ `include CnpjFmt::Error`) | Domain error | `hidden_start` / `hidden_end` outside `0`–`13` |
675
+ | `CnpjFmt::ValidationError` | `CnpjFmt::ValidationError < CnpjFmt::DomainError < RangeError < StandardError` (+ `include CnpjFmt::Error`) | Domain error | `hidden_key` / `dot_key` / `slash_key` / `dash_key` contains a disallowed character |
676
+ | `CnpjGen::ValidationError` | `CnpjGen::ValidationError < CnpjGen::DomainError < RangeError < StandardError` (+ `include CnpjGen::Error`) | Domain error | Ineligible `prefix`, or `type` not in `'alphabetic'` / `'alphanumeric'` / `'numeric'` |
677
+ | `CnpjVal::ValidationError` | `CnpjVal::ValidationError < CnpjVal::DomainError < RangeError < StandardError` (+ `include CnpjVal::Error`) | Domain error | Validator `type` is not `'alphanumeric'` or `'numeric'` |
678
+ | `CpfFmt::InvalidLengthError` | `CpfFmt::InvalidLengthError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Domain error | Sanitized length ≠ 11 — **passed to `on_fail`**, not raised by `#format` |
679
+ | `CpfFmt::OutOfRangeError` | `CpfFmt::OutOfRangeError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Domain error | `hidden_start` / `hidden_end` outside `0`–`10` |
680
+ | `CpfFmt::ValidationError` | `CpfFmt::ValidationError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Domain error | `hidden_key` / `dot_key` / `dash_key` contains a disallowed character |
681
+ | `CpfGen::ValidationError` | `CpfGen::ValidationError < CpfGen::DomainError < RangeError < StandardError` (+ `include CpfGen::Error`) | Domain error | `prefix` is ineligible (zeroed base or 9 repeated digits) |
682
+
683
+ ##### `CpfFmt::DomainError`
684
+
685
+ - **Inheritance:** `CpfFmt::DomainError < RangeError < StandardError` (includes `CpfFmt::Error`)
686
+ - **Category:** Domain error — ancestor for formatter domain leaves.
687
+ - **When it is raised:** Not raised directly; rescue target for `OutOfRangeError`, `ValidationError`, and re-raised `InvalidLengthError`.
688
+ - **Example:** Prefer rescuing a leaf, or `CpfFmt::DomainError` for all CPF formatter domain failures.
689
+ - **How to rescue it:**
690
+
691
+ ```ruby
692
+ rescue CpfFmt::DomainError
693
+ # OutOfRangeError, ValidationError, InvalidLengthError (if re-raised from on_fail)
694
+ ```
695
+
696
+ ##### `CpfFmt::TypeMismatchError`
697
+
698
+ - **Inheritance:** `CpfFmt::TypeMismatchError < TypeError < StandardError` (includes `CpfFmt::Error`)
699
+ - **Category:** API misuse — wrong type for CPF input or a formatter option.
700
+ - **When it is raised:** Raised when `#format` / `cpf_fmt` receives a non-`String` / non-`Array<String>` input, an option has the wrong type, or `on_fail` does not return a `String`.
701
+ - **Example:**
702
+
703
+ ```ruby
704
+ BrUtils.new.cpf.format(12_345) # raises CpfFmt::TypeMismatchError
705
+ ```
706
+
707
+ - **How to rescue it:**
708
+
709
+ ```ruby
710
+ rescue CpfFmt::TypeMismatchError
711
+ # formatter type-contract violation
712
+
713
+ rescue TypeError
714
+ # native type errors, including CpfFmt::TypeMismatchError
715
+ ```
716
+
717
+ ##### `CpfFmt::InvalidArgumentCombinationError`
718
+
719
+ - **Inheritance:** `CpfFmt::InvalidArgumentCombinationError < ArgumentError < StandardError` (includes `CpfFmt::Error`)
720
+ - **Category:** API misuse — mixed `options` and keywords on the formatter API.
721
+ - **When it is raised:** Raised by `CpfFmt::CpfFormatter` / `CpfFmt.cpf_fmt` when both an `options` instance/`Hash` and any non-`nil` keyword are passed. (The CPF aggregator raises `CpfUtils::InvalidArgumentCombinationError` for the same pattern on `CpfUtils#format`.)
722
+ - **Example:**
723
+
724
+ ```ruby
725
+ CpfFmt::CpfFormatter.new({ dash_key: '_' }, hidden: true)
726
+ # raises CpfFmt::InvalidArgumentCombinationError
727
+ ```
728
+
729
+ - **How to rescue it:**
730
+
731
+ ```ruby
732
+ rescue CpfFmt::InvalidArgumentCombinationError
733
+ # formatter invalid signature combination
734
+
735
+ rescue ArgumentError
736
+ # native argument errors, including this one
737
+ ```
738
+
739
+ ##### `CpfFmt::InvalidLengthError` (callback-delivered)
740
+
741
+ - **Inheritance:** `CpfFmt::InvalidLengthError < CpfFmt::DomainError < RangeError < StandardError` (includes `CpfFmt::Error`)
742
+ - **Category:** Domain error — sanitized CPF length is not exactly 11.
743
+ - **When it is raised:** **Not raised** by `#format` / `cpf_fmt`; constructed and passed as the second argument to `on_fail`.
744
+ - **Example:**
745
+
746
+ ```ruby
747
+ custom_fail = ->(value, error) {
748
+ error # => #<CpfFmt::InvalidLengthError ...>
749
+ "Invalid CPF: #{value}"
750
+ }
751
+
752
+ BrUtils.new.cpf.format('123', on_fail: custom_fail) # => "Invalid CPF: 123"
753
+ BrUtils.new.cpf.format('123') # => "" (default on_fail)
754
+ ```
755
+
756
+ - **How to rescue it:** Handle inside `on_fail` (typical), or rescue if you re-raise:
757
+
758
+ ```ruby
759
+ rescue CpfFmt::InvalidLengthError
760
+ # this exact length violation
761
+
762
+ rescue CpfFmt::DomainError
763
+ # RangeError-rooted domain failures from cpf-fmt
764
+ ```
765
+
766
+ ##### `CpfFmt::OutOfRangeError`
767
+
768
+ - **Inheritance:** `CpfFmt::OutOfRangeError < CpfFmt::DomainError < RangeError < StandardError` (includes `CpfFmt::Error`)
769
+ - **Category:** Domain error — `hidden_start` / `hidden_end` outside `0`–`10`.
770
+ - **When it is raised:** Raised when building or applying formatter options with an out-of-range hide index.
771
+ - **Example:**
772
+
773
+ ```ruby
774
+ BrUtils.new.cpf.format('12345678909', hidden_start: -1) # raises CpfFmt::OutOfRangeError
775
+ ```
776
+
777
+ - **How to rescue it:**
778
+
779
+ ```ruby
780
+ rescue CpfFmt::OutOfRangeError
781
+ # this exact range violation
782
+
783
+ rescue CpfFmt::DomainError
784
+ # RangeError-rooted domain failures from cpf-fmt
785
+ ```
786
+
787
+ ##### `CpfFmt::ValidationError`
788
+
789
+ - **Inheritance:** `CpfFmt::ValidationError < CpfFmt::DomainError < RangeError < StandardError` (includes `CpfFmt::Error`)
790
+ - **Category:** Domain error — a key option contains a disallowed character.
791
+ - **When it is raised:** Raised when `hidden_key`, `dot_key`, or `dash_key` contains a forbidden character.
792
+ - **Example:**
793
+
794
+ ```ruby
795
+ BrUtils.new(cpf: { formatter: { dot_key: 'å' } }) # raises CpfFmt::ValidationError
796
+ ```
797
+
798
+ - **How to rescue it:**
799
+
800
+ ```ruby
801
+ rescue CpfFmt::ValidationError
802
+ # this exact domain validation failure
803
+
804
+ rescue CpfFmt::DomainError
805
+ # RangeError-rooted domain failures from cpf-fmt
806
+ ```
807
+
808
+ ##### `CpfGen::DomainError`
809
+
810
+ - **Inheritance:** `CpfGen::DomainError < RangeError < StandardError` (includes `CpfGen::Error`)
811
+ - **Category:** Domain error — ancestor for generator domain leaves.
812
+ - **When it is raised:** Not raised directly; rescue target for `CpfGen::ValidationError`.
813
+ - **Example:** Prefer `rescue CpfGen::ValidationError` or `CpfGen::DomainError`.
814
+ - **How to rescue it:**
815
+
816
+ ```ruby
817
+ rescue CpfGen::DomainError
818
+ # ValidationError and other DomainError subclasses from cpf-gen
819
+ ```
820
+
821
+ ##### `CpfGen::TypeMismatchError`
822
+
823
+ - **Inheritance:** `CpfGen::TypeMismatchError < TypeError < StandardError` (includes `CpfGen::Error`)
824
+ - **Category:** API misuse — wrong type for a generator option.
825
+ - **When it is raised:** Raised when `format` or `prefix` has the wrong runtime type.
826
+ - **Example:**
827
+
828
+ ```ruby
829
+ BrUtils.new.cpf.generate(prefix: 123) # raises CpfGen::TypeMismatchError
830
+ ```
831
+
832
+ - **How to rescue it:**
833
+
834
+ ```ruby
835
+ rescue CpfGen::TypeMismatchError
836
+ # generator type-contract violation
837
+
838
+ rescue TypeError
839
+ # native type errors, including CpfGen::TypeMismatchError
840
+ ```
841
+
842
+ ##### `CpfGen::InvalidArgumentCombinationError`
843
+
844
+ - **Inheritance:** `CpfGen::InvalidArgumentCombinationError < ArgumentError < StandardError` (includes `CpfGen::Error`)
845
+ - **Category:** API misuse — mixed `options` and keywords on the generator API.
846
+ - **When it is raised:** Raised by `CpfGen::CpfGenerator` / `CpfGen.cpf_gen` when both an `options` instance/`Hash` and any non-`nil` keyword are passed. (The CPF aggregator raises `CpfUtils::InvalidArgumentCombinationError` for the same pattern on `CpfUtils#generate`.)
847
+ - **Example:**
848
+
849
+ ```ruby
850
+ CpfGen::CpfGenerator.new({ format: true }, prefix: '123')
851
+ # raises CpfGen::InvalidArgumentCombinationError
852
+ ```
853
+
854
+ - **How to rescue it:**
855
+
856
+ ```ruby
857
+ rescue CpfGen::InvalidArgumentCombinationError
858
+ # generator invalid signature combination
859
+
860
+ rescue ArgumentError
861
+ # native argument errors, including this one
862
+ ```
863
+
864
+ ##### `CpfGen::ValidationError`
865
+
866
+ - **Inheritance:** `CpfGen::ValidationError < CpfGen::DomainError < RangeError < StandardError` (includes `CpfGen::Error`)
867
+ - **Category:** Domain error — ineligible `prefix`.
868
+ - **When it is raised:** Raised when `prefix` is a zeroed base (`'000000000'`) or 9 repeated digits (e.g. `'999999999'`).
869
+ - **Example:**
870
+
871
+ ```ruby
872
+ BrUtils.new.cpf.generate(prefix: '000000000') # raises CpfGen::ValidationError
873
+ ```
874
+
875
+ - **How to rescue it:**
876
+
877
+ ```ruby
878
+ rescue CpfGen::ValidationError
879
+ # this exact domain validation failure
880
+
881
+ rescue CpfGen::DomainError
882
+ # RangeError-rooted domain failures from cpf-gen
883
+ ```
884
+
885
+ ##### `CpfVal::TypeMismatchError`
886
+
887
+ - **Inheritance:** `CpfVal::TypeMismatchError < TypeError < StandardError` (includes `CpfVal::Error`)
888
+ - **Category:** API misuse — wrong type for CPF input.
889
+ - **When it is raised:** Raised when `#is_valid` / `cpf_val` receives a value that is not a `String` or an `Array` of strings (including a non-string array element). Invalid CPF **data** returns `false` and does not raise.
890
+ - **Example:**
891
+
892
+ ```ruby
893
+ BrUtils.new.cpf.is_valid(12_345_678_909) # raises CpfVal::TypeMismatchError
894
+ BrUtils.new.cpf.is_valid('12345678900') # => false (invalid data, no raise)
895
+ ```
896
+
897
+ - **How to rescue it:**
898
+
899
+ ```ruby
900
+ rescue CpfVal::TypeMismatchError
901
+ # validator type-contract violation
902
+
903
+ rescue TypeError
904
+ # native type errors, including CpfVal::TypeMismatchError
905
+ ```
906
+
907
+ ##### `CnpjFmt::DomainError`
908
+
909
+ - **Inheritance:** `CnpjFmt::DomainError < RangeError < StandardError` (includes `CnpjFmt::Error`)
910
+ - **Category:** Domain error — ancestor for formatter domain leaves.
911
+ - **When it is raised:** Not raised directly; rescue target for `OutOfRangeError`, `ValidationError`, and re-raised `InvalidLengthError`.
912
+ - **Example:** Prefer rescuing a leaf, or `CnpjFmt::DomainError` for all CNPJ formatter domain failures.
913
+ - **How to rescue it:**
914
+
915
+ ```ruby
916
+ rescue CnpjFmt::DomainError
917
+ # OutOfRangeError, ValidationError, InvalidLengthError (if re-raised from on_fail)
918
+ ```
919
+
920
+ ##### `CnpjFmt::TypeMismatchError`
921
+
922
+ - **Inheritance:** `CnpjFmt::TypeMismatchError < TypeError < StandardError` (includes `CnpjFmt::Error`)
923
+ - **Category:** API misuse — wrong type for CNPJ input or a formatter option.
924
+ - **When it is raised:** Raised when `#format` / `cnpj_fmt` receives a non-`String` / non-`Array<String>` input, an option has the wrong type, or `on_fail` does not return a `String`.
925
+ - **Example:**
926
+
927
+ ```ruby
928
+ BrUtils.new.cnpj.format(12_345) # raises CnpjFmt::TypeMismatchError
929
+ ```
930
+
931
+ - **How to rescue it:**
932
+
933
+ ```ruby
934
+ rescue CnpjFmt::TypeMismatchError
935
+ # formatter type-contract violation
936
+
937
+ rescue TypeError
938
+ # native type errors, including CnpjFmt::TypeMismatchError
939
+ ```
940
+
941
+ ##### `CnpjFmt::InvalidArgumentCombinationError`
942
+
943
+ - **Inheritance:** `CnpjFmt::InvalidArgumentCombinationError < ArgumentError < StandardError` (includes `CnpjFmt::Error`)
944
+ - **Category:** API misuse — mixed `options` and keywords on the formatter API.
945
+ - **When it is raised:** Raised by `CnpjFmt::CnpjFormatter` / `CnpjFmt.cnpj_fmt` when both an `options` instance/`Hash` and any non-`nil` keyword are passed. (The CNPJ aggregator raises `CnpjUtils::InvalidArgumentCombinationError` for the same pattern on `CnpjUtils#format`.)
946
+ - **Example:**
947
+
948
+ ```ruby
949
+ CnpjFmt::CnpjFormatter.new({ slash_key: '|' }, hidden: true)
950
+ # raises CnpjFmt::InvalidArgumentCombinationError
951
+ ```
952
+
953
+ - **How to rescue it:**
954
+
955
+ ```ruby
956
+ rescue CnpjFmt::InvalidArgumentCombinationError
957
+ # formatter invalid signature combination
958
+
959
+ rescue ArgumentError
960
+ # native argument errors, including this one
961
+ ```
962
+
963
+ ##### `CnpjFmt::InvalidLengthError` (callback-delivered)
964
+
965
+ - **Inheritance:** `CnpjFmt::InvalidLengthError < CnpjFmt::DomainError < RangeError < StandardError` (includes `CnpjFmt::Error`)
966
+ - **Category:** Domain error — sanitized CNPJ length is not exactly 14.
967
+ - **When it is raised:** **Not raised** by `#format` / `cnpj_fmt`; constructed and passed as the second argument to `on_fail`.
968
+ - **Example:**
969
+
970
+ ```ruby
971
+ custom_fail = ->(value, error) {
972
+ error # => #<CnpjFmt::InvalidLengthError ...>
973
+ "Invalid CNPJ: #{value}"
974
+ }
975
+
976
+ BrUtils.new.cnpj.format('123', on_fail: custom_fail) # => "Invalid CNPJ: 123"
977
+ BrUtils.new.cnpj.format('123') # => "" (default on_fail)
978
+ ```
979
+
980
+ - **How to rescue it:** Handle inside `on_fail` (typical), or rescue if you re-raise:
981
+
982
+ ```ruby
983
+ rescue CnpjFmt::InvalidLengthError
984
+ # this exact length violation
985
+
986
+ rescue CnpjFmt::DomainError
987
+ # RangeError-rooted domain failures from cnpj-fmt
988
+ ```
989
+
990
+ ##### `CnpjFmt::OutOfRangeError`
991
+
992
+ - **Inheritance:** `CnpjFmt::OutOfRangeError < CnpjFmt::DomainError < RangeError < StandardError` (includes `CnpjFmt::Error`)
993
+ - **Category:** Domain error — `hidden_start` / `hidden_end` outside `0`–`13`.
994
+ - **When it is raised:** Raised when building or applying formatter options with an out-of-range hide index.
995
+ - **Example:**
996
+
997
+ ```ruby
998
+ BrUtils.new.cnpj.format('91415732000793', hidden_start: -1) # raises CnpjFmt::OutOfRangeError
999
+ ```
1000
+
1001
+ - **How to rescue it:**
1002
+
1003
+ ```ruby
1004
+ rescue CnpjFmt::OutOfRangeError
1005
+ # this exact range violation
1006
+
1007
+ rescue CnpjFmt::DomainError
1008
+ # RangeError-rooted domain failures from cnpj-fmt
1009
+ ```
1010
+
1011
+ ##### `CnpjFmt::ValidationError`
1012
+
1013
+ - **Inheritance:** `CnpjFmt::ValidationError < CnpjFmt::DomainError < RangeError < StandardError` (includes `CnpjFmt::Error`)
1014
+ - **Category:** Domain error — a key option contains a disallowed character.
1015
+ - **When it is raised:** Raised when `hidden_key`, `dot_key`, `slash_key`, or `dash_key` contains a forbidden character.
1016
+ - **Example:**
1017
+
1018
+ ```ruby
1019
+ BrUtils.new(cnpj: { formatter: { slash_key: 'å' } }) # raises CnpjFmt::ValidationError
1020
+ ```
1021
+
1022
+ - **How to rescue it:**
1023
+
1024
+ ```ruby
1025
+ rescue CnpjFmt::ValidationError
1026
+ # this exact domain validation failure
1027
+
1028
+ rescue CnpjFmt::DomainError
1029
+ # RangeError-rooted domain failures from cnpj-fmt
1030
+ ```
1031
+
1032
+ ##### `CnpjGen::DomainError`
1033
+
1034
+ - **Inheritance:** `CnpjGen::DomainError < RangeError < StandardError` (includes `CnpjGen::Error`)
1035
+ - **Category:** Domain error — ancestor for generator domain leaves.
1036
+ - **When it is raised:** Not raised directly; rescue target for `CnpjGen::ValidationError`.
1037
+ - **Example:** Prefer `rescue CnpjGen::ValidationError` or `CnpjGen::DomainError`.
1038
+ - **How to rescue it:**
1039
+
1040
+ ```ruby
1041
+ rescue CnpjGen::DomainError
1042
+ # ValidationError and other DomainError subclasses from cnpj-gen
1043
+ ```
1044
+
1045
+ ##### `CnpjGen::TypeMismatchError`
1046
+
1047
+ - **Inheritance:** `CnpjGen::TypeMismatchError < TypeError < StandardError` (includes `CnpjGen::Error`)
1048
+ - **Category:** API misuse — wrong type for a generator option.
1049
+ - **When it is raised:** Raised when `format`, `prefix`, or `type` has the wrong runtime type.
1050
+ - **Example:**
1051
+
1052
+ ```ruby
1053
+ BrUtils.new.cnpj.generate(prefix: 123) # raises CnpjGen::TypeMismatchError
1054
+ ```
1055
+
1056
+ - **How to rescue it:**
1057
+
1058
+ ```ruby
1059
+ rescue CnpjGen::TypeMismatchError
1060
+ # generator type-contract violation
1061
+
1062
+ rescue TypeError
1063
+ # native type errors, including CnpjGen::TypeMismatchError
1064
+ ```
1065
+
1066
+ ##### `CnpjGen::InvalidArgumentCombinationError`
1067
+
1068
+ - **Inheritance:** `CnpjGen::InvalidArgumentCombinationError < ArgumentError < StandardError` (includes `CnpjGen::Error`)
1069
+ - **Category:** API misuse — mixed `options` and keywords on the generator API.
1070
+ - **When it is raised:** Raised by `CnpjGen::CnpjGenerator` / `CnpjGen.cnpj_gen` when both an `options` instance/`Hash` and any non-`nil` keyword are passed. (The CNPJ aggregator raises `CnpjUtils::InvalidArgumentCombinationError` for the same pattern on `CnpjUtils#generate`.)
1071
+ - **Example:**
1072
+
1073
+ ```ruby
1074
+ CnpjGen::CnpjGenerator.new({ format: true }, prefix: '123')
1075
+ # raises CnpjGen::InvalidArgumentCombinationError
1076
+ ```
1077
+
1078
+ - **How to rescue it:**
1079
+
1080
+ ```ruby
1081
+ rescue CnpjGen::InvalidArgumentCombinationError
1082
+ # generator invalid signature combination
1083
+
1084
+ rescue ArgumentError
1085
+ # native argument errors, including this one
1086
+ ```
1087
+
1088
+ ##### `CnpjGen::ValidationError`
1089
+
1090
+ - **Inheritance:** `CnpjGen::ValidationError < CnpjGen::DomainError < RangeError < StandardError` (includes `CnpjGen::Error`)
1091
+ - **Category:** Domain error — ineligible `prefix` or disallowed `type`.
1092
+ - **When it is raised:** Raised when `prefix` is a zeroed base/branch ID or 12 repeated digits, or when `type` is not `'alphabetic'`, `'alphanumeric'`, or `'numeric'`.
1093
+ - **Example:**
1094
+
1095
+ ```ruby
1096
+ BrUtils.new.cnpj.generate(type: 'boolean') # raises CnpjGen::ValidationError
1097
+ ```
1098
+
1099
+ - **How to rescue it:**
1100
+
1101
+ ```ruby
1102
+ rescue CnpjGen::ValidationError
1103
+ # this exact domain validation failure
1104
+
1105
+ rescue CnpjGen::DomainError
1106
+ # RangeError-rooted domain failures from cnpj-gen
1107
+ ```
1108
+
1109
+ ##### `CnpjVal::DomainError`
1110
+
1111
+ - **Inheritance:** `CnpjVal::DomainError < RangeError < StandardError` (includes `CnpjVal::Error`)
1112
+ - **Category:** Domain error — ancestor for validator domain leaves.
1113
+ - **When it is raised:** Not raised directly; rescue target for `CnpjVal::ValidationError`.
1114
+ - **Example:** Prefer `rescue CnpjVal::ValidationError` or `CnpjVal::DomainError`.
1115
+ - **How to rescue it:**
1116
+
1117
+ ```ruby
1118
+ rescue CnpjVal::DomainError
1119
+ # ValidationError and other DomainError subclasses from cnpj-val
1120
+ ```
1121
+
1122
+ ##### `CnpjVal::TypeMismatchError`
1123
+
1124
+ - **Inheritance:** `CnpjVal::TypeMismatchError < TypeError < StandardError` (includes `CnpjVal::Error`)
1125
+ - **Category:** API misuse — wrong type for CNPJ input or a validator option.
1126
+ - **When it is raised:** Raised when `#is_valid` / `cnpj_val` receives a value that is not a `String` or an `Array` of strings, or a validator option has the wrong type. Invalid CNPJ **data** returns `false` and does not raise.
1127
+ - **Example:**
1128
+
1129
+ ```ruby
1130
+ BrUtils.new.cnpj.is_valid(12_345_678_000_198) # raises CnpjVal::TypeMismatchError
1131
+ BrUtils.new.cnpj.is_valid('00000000000000') # => false (invalid data, no raise)
1132
+ ```
1133
+
1134
+ - **How to rescue it:**
1135
+
1136
+ ```ruby
1137
+ rescue CnpjVal::TypeMismatchError
1138
+ # validator type-contract violation
1139
+
1140
+ rescue TypeError
1141
+ # native type errors, including CnpjVal::TypeMismatchError
1142
+ ```
1143
+
1144
+ ##### `CnpjVal::InvalidArgumentCombinationError`
1145
+
1146
+ - **Inheritance:** `CnpjVal::InvalidArgumentCombinationError < ArgumentError < StandardError` (includes `CnpjVal::Error`)
1147
+ - **Category:** API misuse — mixed `options` and keywords on the validator API.
1148
+ - **When it is raised:** Raised by `CnpjVal::CnpjValidator` / `CnpjVal.cnpj_val` when both an `options` instance/`Hash` and any non-`nil` keyword are passed. (The CNPJ aggregator raises `CnpjUtils::InvalidArgumentCombinationError` for the same pattern on `CnpjUtils#is_valid`.)
1149
+ - **Example:**
1150
+
1151
+ ```ruby
1152
+ CnpjVal.cnpj_val('98765432000198', { type: 'numeric' }, case_sensitive: false)
1153
+ # raises CnpjVal::InvalidArgumentCombinationError
1154
+ ```
1155
+
1156
+ - **How to rescue it:**
1157
+
1158
+ ```ruby
1159
+ rescue CnpjVal::InvalidArgumentCombinationError
1160
+ # validator invalid signature combination
1161
+
1162
+ rescue ArgumentError
1163
+ # native argument errors, including this one
1164
+ ```
1165
+
1166
+ ##### `CnpjVal::ValidationError`
1167
+
1168
+ - **Inheritance:** `CnpjVal::ValidationError < CnpjVal::DomainError < RangeError < StandardError` (includes `CnpjVal::Error`)
1169
+ - **Category:** Domain error — disallowed validator `type`.
1170
+ - **When it is raised:** Raised when `type` is not `'alphanumeric'` or `'numeric'`.
1171
+ - **Example:**
1172
+
1173
+ ```ruby
1174
+ BrUtils.new.cnpj.is_valid('91415732000793', type: 'boolean') # raises CnpjVal::ValidationError
1175
+ ```
1176
+
1177
+ - **How to rescue it:**
1178
+
1179
+ ```ruby
1180
+ rescue CnpjVal::ValidationError
1181
+ # this exact domain validation failure
1182
+
1183
+ rescue CnpjVal::DomainError
1184
+ # RangeError-rooted domain failures from cnpj-val
1185
+ ```
1186
+
1187
+ ##### `CpfUtils::TypeMismatchError`
1188
+
1189
+ - **Inheritance:** `CpfUtils::TypeMismatchError < TypeError < StandardError` (includes `CpfUtils::Error`)
1190
+ - **Category:** API misuse — the caller passed a value of the wrong type.
1191
+ - **When it is raised:** Raised when `CpfUtils.new` receives a non-`nil` `settings` argument that is not a `Hash`.
1192
+ - **Example:**
1193
+
1194
+ ```ruby
1195
+ CpfUtils.new('not-a-hash') # raises CpfUtils::TypeMismatchError
1196
+ CpfUtils.new(false) # raises CpfUtils::TypeMismatchError (false is non-nil)
1197
+ ```
1198
+
1199
+ - **How to rescue it:**
1200
+
1201
+ ```ruby
1202
+ rescue CpfUtils::TypeMismatchError
1203
+ # CPF aggregator type-contract violation (not BrUtils::Error)
1204
+
1205
+ rescue TypeError
1206
+ # native type errors, including CpfUtils::TypeMismatchError
1207
+ ```
1208
+
1209
+ ##### `CpfUtils::InvalidArgumentCombinationError`
1210
+
1211
+ - **Inheritance:** `CpfUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (includes `CpfUtils::Error`)
1212
+ - **Category:** API misuse — the caller mixed mutually exclusive argument patterns.
1213
+ - **When it is raised:** Raised when `CpfUtils.new` receives both a non-`nil` settings `Hash` and any non-`nil` keyword, or when `#format` / `#generate` mix a non-`nil` options `Hash`/`*Options` with any non-`nil` keyword. `#is_valid` has no options path and does not raise this error.
1214
+ - **Example:**
1215
+
1216
+ ```ruby
1217
+ BrUtils.new.cpf.format({ hidden: true }, dash_key: '|')
1218
+ # raises CpfUtils::InvalidArgumentCombinationError
1219
+ ```
1220
+
1221
+ - **How to rescue it:**
1222
+
1223
+ ```ruby
1224
+ rescue CpfUtils::InvalidArgumentCombinationError
1225
+ # CPF aggregator invalid signature combination (not BrUtils::Error)
1226
+
1227
+ rescue ArgumentError
1228
+ # native argument errors, including CpfUtils::InvalidArgumentCombinationError
1229
+ ```
1230
+
1231
+ ##### `CnpjUtils::TypeMismatchError`
1232
+
1233
+ - **Inheritance:** `CnpjUtils::TypeMismatchError < TypeError < StandardError` (includes `CnpjUtils::Error`)
1234
+ - **Category:** API misuse — the caller passed a value of the wrong type.
1235
+ - **When it is raised:** Raised when `CnpjUtils.new` receives a non-`nil` `settings` argument that is not a `Hash`.
1236
+ - **Example:**
1237
+
1238
+ ```ruby
1239
+ CnpjUtils.new('not-a-hash') # raises CnpjUtils::TypeMismatchError
1240
+ CnpjUtils.new(false) # raises CnpjUtils::TypeMismatchError (false is non-nil)
1241
+ ```
1242
+
1243
+ - **How to rescue it:**
1244
+
1245
+ ```ruby
1246
+ rescue CnpjUtils::TypeMismatchError
1247
+ # CNPJ aggregator type-contract violation (not BrUtils::Error)
1248
+
1249
+ rescue TypeError
1250
+ # native type errors, including CnpjUtils::TypeMismatchError
1251
+ ```
1252
+
1253
+ ##### `CnpjUtils::InvalidArgumentCombinationError`
1254
+
1255
+ - **Inheritance:** `CnpjUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (includes `CnpjUtils::Error`)
1256
+ - **Category:** API misuse — the caller mixed mutually exclusive argument patterns.
1257
+ - **When it is raised:** Raised when `CnpjUtils.new`, `#format`, `#generate`, `#is_valid`, or the class helpers receive both a non-`nil` settings/options `Hash` (or options instance) and any non-`nil` keyword at the same time.
1258
+ - **Example:**
1259
+
1260
+ ```ruby
1261
+ BrUtils.new.cnpj.format({ hidden: true }, slash_key: '|')
1262
+ # raises CnpjUtils::InvalidArgumentCombinationError
1263
+ ```
1264
+
1265
+ - **How to rescue it:**
1266
+
1267
+ ```ruby
1268
+ rescue CnpjUtils::InvalidArgumentCombinationError
1269
+ # CNPJ aggregator invalid signature combination (not BrUtils::Error)
1270
+
1271
+ rescue ArgumentError
1272
+ # native argument errors, including CnpjUtils::InvalidArgumentCombinationError
1273
+ ```
1274
+
1275
+ ### Bundled packages
1276
+
1277
+ | Package | Main resources | README |
1278
+ |---------|----------------|--------|
1279
+ | [`cpf-utilities`](https://rubygems.org/gems/cpf-utilities) | `CpfUtils`, `CpfFormatter`, `CpfGenerator`, `CpfValidator`, `CpfFmt.cpf_fmt`, `CpfGen.cpf_gen`, `CpfVal.cpf_val` | [docs](../cpf-utilities/README.md) |
1280
+ | [`cnpj-utilities`](https://rubygems.org/gems/cnpj-utilities) | `CnpjUtils`, `CnpjFormatter`, `CnpjGenerator`, `CnpjValidator`, `CnpjFmt.cnpj_fmt`, `CnpjGen.cnpj_gen`, `CnpjVal.cnpj_val` | [docs](../cnpj-utilities/README.md) |
1281
+
1282
+ All of the above are pulled in as dependencies of **`br-utilities`**. Interactive demos: [CPF](https://cpf-utils.vercel.app/) and [CNPJ](https://cnpj-utils.vercel.app/).
1283
+
1284
+ ## Contribution & Support
1285
+
1286
+ We welcome contributions! Please see our [Contributing Guidelines](https://github.com/LacusSolutions/br-utils-ruby/blob/main/CONTRIBUTING.md) for details. If you find this project helpful, please consider:
1287
+
1288
+ - ⭐ Starring the repository
1289
+ - 🤝 Contributing to the codebase
1290
+ - 💡 [Suggesting new features](https://github.com/LacusSolutions/br-utils-ruby/issues)
1291
+ - 🐛 [Reporting bugs](https://github.com/LacusSolutions/br-utils-ruby/issues)
1292
+
1293
+ ## License
1294
+
1295
+ This project is licensed under the MIT License — see the [LICENSE](https://github.com/LacusSolutions/br-utils-ruby/blob/main/LICENSE) file for details.
1296
+
1297
+ ## Changelog
1298
+
1299
+ See [CHANGELOG](./CHANGELOG.md) for a list of changes and version history.
1300
+
1301
+ ---
1302
+
1303
+ Made with ❤️ by [Lacus Solutions](https://github.com/LacusSolutions)