cnpj-fmt 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 03e33e5073a45ab75edda8bca71c8db1155e07e1c584b10a752f3a64c3e70620
4
- data.tar.gz: 60c9f8e4b87bb82b430a06e88b8f8b703e9e9abe706202f18037d1c9f70c4c33
3
+ metadata.gz: de41b993d08e5fd1f5ae78715c3e53d258fbfa9317128b956bff8a0f85df229e
4
+ data.tar.gz: 0f1f079bc24c168dd6034725e1f182ce42bc147ec1631fee1aced8a2c9064558
5
5
  SHA512:
6
- metadata.gz: 8a59629f593629f82c49474a81980924b612f539e35701ccfc1dc3cfb58a4be42c8673d30b692fded3d90a2bc5e8b2f1f2528a130f4fa543ee6594ea5d13f891
7
- data.tar.gz: c0a7fd31115be7f8f3eecf3638e356ec3ccf0398e9e79babf5c5aadef3903f246deb2e4b03e7178d5a2083e7bd08c896503f755733bbcad87d2e6b2e4c241421
6
+ metadata.gz: 63db23a5ecaf644557b6d35b32070e011a4e62654d3ec758563e7722d6e081dd07d01a215a6c4c52ca282cac23e2cb615a67282735d703fc8d4a0dfa84e957a9
7
+ data.tar.gz: 738675dbee04c5f80969f4ec77f095070163b41e0ae2f75855cc24e18246618bc260fe1a9620f6a2b2edaa35e5dbb20274de740d594585baa50f983a6e13a028
data/CHANGELOG.md ADDED
@@ -0,0 +1,18 @@
1
+ # cnpj-fmt
2
+
3
+ ## 1.0.0
4
+
5
+ ### πŸš€ Stable Version Released!
6
+
7
+ Utility module to format CNPJ (Brazilian legal entity ID) strings. Main features:
8
+
9
+ - **Multiple interfaces**: supports `CnpjFmt.cnpj_fmt` and `CnpjFmt::CnpjFormatter` with shared `CnpjFmt::CnpjFormatterOptions` defaults; an `options` argument (instance or `Hash`) is never merged with keyword overrides β€” passing both at once raises `InvalidArgumentCombinationError`.
10
+ - **Alphanumeric CNPJ**: formats 14-character alphanumeric input (digits and `A–Z`); strips punctuation and uppercases letters before delimiter insertion.
11
+ - **Flexible input**: accepts a `String` or `Array<String>` and concatenates sequence items before sanitization.
12
+ - **Customizable output**: configurable `dot_key`, `slash_key`, and `dash_key` delimiters for flexible `XX.XXX.XXX/XXXX-XX` layouts.
13
+ - **Privacy masking**: `hidden`, `hidden_key`, `hidden_start`, and `hidden_end` mask sensitive character ranges in the formatted string.
14
+ - **Post-format transforms**: optional `escape` (HTML) and `encode` (URL) applied after successful formatting.
15
+ - **Structured errors**: `CnpjFmt::Error` marker with misuse leaves (`TypeMismatchError`, `InvalidArgumentCombinationError`), domain leaves (`InvalidLengthError`, `OutOfRangeError`, `ValidationError` under `DomainError`); `on_fail` receives `(original_input, DomainError)` (length failures pass `InvalidLengthError`).
16
+ - **Strict options merging**: `CnpjFormatterOptions.new`/`#set` fold positional `Hash`/instance layers left to right, then apply keyword overrides with the highest precedence, filling any still-unresolved option with its `DEFAULT_*` value; property setters never accept `nil` directly β€” pass the matching `DEFAULT_*` constant (or use `#set_hidden_range`) to reset explicitly.
17
+
18
+ For detailed usage and API reference, see the [README](./README.md).
data/LICENSE ADDED
@@ -0,0 +1,9 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Julio L. Muller
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,463 @@
1
+ ![cnpj-fmt for Ruby](https://br-utils.vercel.app/img/cover_cnpj-fmt.jpg)
2
+
3
+ [![Gem Version](https://img.shields.io/gem/v/cnpj-fmt)](https://rubygems.org/gems/cnpj-fmt)
4
+ [![Gem Downloads](https://img.shields.io/gem/dt/cnpj-fmt)](https://rubygems.org/gems/cnpj-fmt)
5
+ [![Ruby Version](https://img.shields.io/gem/rv/cnpj-fmt)](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 utility to format CNPJ (Brazilian Business Tax ID).
15
+
16
+ ## Ruby Support
17
+
18
+ | ![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) |
19
+ | --- | --- | --- |
20
+ | Passing βœ” | Passing βœ” | Passing βœ” |
21
+
22
+ ## Features
23
+
24
+ - βœ… **Alphanumeric CNPJ**: Supports 14-character alphanumeric CNPJ (digits and letters, e.g. `RK0CMT3W000100`)
25
+ - βœ… **Flexible input**: Accepts `String` or `Array` of strings; array elements are concatenated in order
26
+ - βœ… **Format agnostic**: Strips non-alphanumeric characters and uppercases letters before formatting
27
+ - βœ… **Custom delimiters**: `dot_key`, `slash_key`, and `dash_key` may be empty, single-, or multi-character strings
28
+ - βœ… **Masking**: Optional hiding of a character range with a configurable replacement string (`hidden`, `hidden_key`, `hidden_start`, `hidden_end`)
29
+ - βœ… **HTML & URL output**: Optional `escape` (HTML entities) and `encode` (URI component encoding, similar to JavaScript `encodeURIComponent`)
30
+ - βœ… **Length errors without throwing**: Invalid length after sanitization is handled via `on_fail` (default returns an empty string)
31
+ - βœ… **Minimal dependencies**: Only [`lacus-utils`](https://rubygems.org/gems/lacus-utils)
32
+ - βœ… **Error handling**: API misuse vs domain errors with a `CnpjFmt::Error` marker for library-wide rescue
33
+
34
+ ## Installation
35
+
36
+ Install the gem directly:
37
+
38
+ ```bash
39
+ gem install cnpj-fmt
40
+ ```
41
+
42
+ Or add it to your `Gemfile` and run `bundle install`:
43
+
44
+ ```ruby
45
+ gem 'cnpj-fmt'
46
+ ```
47
+
48
+ ## Require
49
+
50
+ ```ruby
51
+ require 'cnpj-fmt'
52
+ ```
53
+
54
+ ## Quick Start
55
+
56
+ ```ruby
57
+ require 'cnpj-fmt'
58
+
59
+ formatter = CnpjFmt::CnpjFormatter.new
60
+
61
+ formatter.format('03603568000195') # => "03.603.568/0001-95"
62
+ formatter.format('12ABC34500DE99') # => "12.ABC.345/00DE-99"
63
+ formatter.format('RK0CMT3W000100') # => "RK.0CM.T3W/0001-00"
64
+ ```
65
+
66
+ Basic helper usage:
67
+
68
+ ```ruby
69
+ require 'cnpj-fmt'
70
+
71
+ cnpj = '03603568000195'
72
+
73
+ CnpjFmt.cnpj_fmt(cnpj) # => "03.603.568/0001-95"
74
+ CnpjFmt.cnpj_fmt(cnpj, hidden: true) # => "03.603.***/****-**"
75
+ CnpjFmt.cnpj_fmt( # => "03603568|0001_95"
76
+ cnpj,
77
+ dot_key: '',
78
+ slash_key: '|',
79
+ dash_key: '_'
80
+ )
81
+ ```
82
+
83
+ ## Usage
84
+
85
+ The main entry points are the class `CnpjFmt::CnpjFormatter`, the options class `CnpjFmt::CnpjFormatterOptions`, and the helper `CnpjFmt.cnpj_fmt`.
86
+
87
+ ### `CnpjFmt::CnpjFormatter`
88
+
89
+ - **`initialize(options = nil, **keywords)`**: Optional default formatting options. When `options` is given (a `CnpjFmt::CnpjFormatterOptions` instance or a `Hash`) alone, it determines the default options; a `CnpjFmt::CnpjFormatterOptions` instance is stored by reference (mutating it later affects future `format` calls that do not pass per-call options), while a `Hash` builds a new instance. When `options` is omitted (`nil`), the default options are built exclusively from the keyword arguments (`hidden:`, `hidden_key:`, `dot_key:`, …). Passing `options` together with any non-`nil` keyword raises `InvalidArgumentCombinationError` instead of silently ignoring the keywords. Example: `CnpjFmt::CnpjFormatter.new(hidden: true, slash_key: '|')`.
90
+ - **`options`**: Returns the instance’s `CnpjFmt::CnpjFormatterOptions` (same object used internally).
91
+ - **`format(cnpj_input, options = nil, **keywords)`**: Formats a CNPJ value.
92
+
93
+ Input is normalized by removing non-alphanumeric characters and uppercasing. If the sanitized length is not exactly **14**, the **`on_fail`** callback is invoked with the original input and a `CnpjFmt::DomainError` (`InvalidLengthError`); its return value is the result (nothing is thrown for length).
94
+
95
+ If the input is not a `String` or an `Array` of strings, **`CnpjFmt::TypeMismatchError`** is raised.
96
+
97
+ Per-call `options` and keyword arguments are never merged: a given `options` argument alone fully overrides the instance defaults for this call; otherwise, any given keyword overrides the instance defaults for this call. When neither is given, the instance defaults are used as-is. The instance defaults are never mutated by a per-call override. Passing `options` together with any non-`nil` keyword raises `InvalidArgumentCombinationError`.
98
+
99
+ ### `CnpjFmt::CnpjFormatterOptions`
100
+
101
+ Holds all formatter settings, with validation and merge support. Exposes properties: `hidden`, `hidden_key`, `hidden_start`, `hidden_end`, `dot_key`, `slash_key`, `dash_key`, `escape`, `encode`, `on_fail`.
102
+
103
+ - **`initialize(options = nil, *extra_overrides, **keywords)`**: Optional default options (plain `Hash`, `CnpjFmt::CnpjFormatterOptions` instance, or keyword arguments), plus extra override objects merged in order (later overrides win).
104
+ - **`all`**: Returns a shallow `Hash` copy of all current options.
105
+ - **`copy`**: Returns a shallow copy of this options instance.
106
+ - **`set(options)`**: Updates multiple fields at once; returns `self`. Accepts a `Hash` or another `CnpjFmt::CnpjFormatterOptions` instance. Explicit `nil` values in the update keep the current value.
107
+ - **`set_hidden_range(hidden_start, hidden_end)`**: Validates indices in **`[0, 13]`** (inclusive); if `hidden_start > hidden_end`, values are swapped. `nil` arguments fall back to defaults (`DEFAULT_HIDDEN_START` / `DEFAULT_HIDDEN_END`).
108
+
109
+ **`hidden_start` / `hidden_end`**: Indices refer to the **14-character normalized CNPJ string** (before inserting punctuation). The inclusive range is replaced internally by placeholders, then `hidden_key` is substituted (supports multi-character keys and empty string).
110
+
111
+ **Key options** (`hidden_key`, `dot_key`, `slash_key`, `dash_key`): Must be strings and must not contain any character in `CnpjFmt::CnpjFormatterOptions::DISALLOWED_KEY_CHARACTERS` (reserved for internal formatting).
112
+
113
+ ### Functional helper
114
+
115
+ `CnpjFmt.cnpj_fmt` builds a new `CnpjFmt::CnpjFormatter` from the same constructor parameters and calls `format(cnpj_input)` once. Pass either keyword arguments **or** a `Hash`/`CnpjFmt::CnpjFormatterOptions` instance for options β€” not both (passing `options` with any non-`nil` keyword raises `InvalidArgumentCombinationError`):
116
+
117
+ ```ruby
118
+ require 'cnpj-fmt'
119
+
120
+ cnpj = '03603568000195'
121
+
122
+ CnpjFmt.cnpj_fmt(cnpj) # => "03.603.568/0001-95"
123
+ CnpjFmt.cnpj_fmt(cnpj, hidden: true) # masked with defaults
124
+ CnpjFmt.cnpj_fmt( # => "03603568|0001_95"
125
+ cnpj,
126
+ dot_key: '',
127
+ slash_key: '|',
128
+ dash_key: '_'
129
+ )
130
+ CnpjFmt.cnpj_fmt(cnpj, { # Hash form
131
+ hidden: true,
132
+ hidden_key: '#'
133
+ })
134
+ ```
135
+
136
+ ### Object-oriented examples
137
+
138
+ ```ruby
139
+ require 'cnpj-fmt'
140
+
141
+ formatter = CnpjFmt::CnpjFormatter.new
142
+ cnpj = '03603568000195'
143
+
144
+ formatter.format(cnpj) # => "03.603.568/0001-95"
145
+ formatter.format( # => "03.603.###/####-##"
146
+ cnpj,
147
+ hidden: true,
148
+ hidden_key: '#',
149
+ hidden_start: 5,
150
+ hidden_end: 13
151
+ )
152
+ ```
153
+
154
+ Default options on the instance; per-call overrides:
155
+
156
+ ```ruby
157
+ require 'cnpj-fmt'
158
+
159
+ formatter = CnpjFmt::CnpjFormatter.new(hidden: true)
160
+ cnpj = '03603568000195'
161
+
162
+ formatter.format(cnpj) # uses instance masking
163
+ formatter.format(cnpj, hidden: false) # this call only: unmasked
164
+ formatter.format(cnpj) # back to instance defaults
165
+ ```
166
+
167
+ Alphanumeric input and array input:
168
+
169
+ ```ruby
170
+ require 'cnpj-fmt'
171
+
172
+ formatter = CnpjFmt::CnpjFormatter.new
173
+
174
+ formatter.format('RK0CMT3W000100') # => "RK.0CM.T3W/0001-00"
175
+ formatter.format([ # => "RK.0CM.T3W/0001-00"
176
+ 'RK',
177
+ '0CM',
178
+ 'T3W',
179
+ '0001',
180
+ '00'
181
+ ])
182
+ ```
183
+
184
+ ### Input formats
185
+
186
+ **String:** Raw digits and/or letters, or already formatted CNPJ (e.g. `12.345.678/0009-10`, `12.ABC.345/00DE-99`). Non-alphanumeric characters are removed; lowercase letters are uppercased.
187
+
188
+ **Array of strings:** Each element must be a `String`; values are concatenated (e.g. per digit, grouped segments, or mixed with punctuation β€” all are stripped during normalization). Non-string elements are not allowed.
189
+
190
+ ### Formatting options
191
+
192
+ | Parameter | Type | Default | Description |
193
+ |-----------|------|---------|-------------|
194
+ | `hidden` | `Boolean`, `nil` | `false` | When truthy, replaces the inclusive index range `[hidden_start, hidden_end]` on the normalized 14-character string before punctuation is applied |
195
+ | `hidden_key` | `String`, `nil` | `'*'` | Replacement for each hidden position (may be multi-character or empty); must not use disallowed key characters |
196
+ | `hidden_start` | `Integer`, `nil` | `5` | Start index `0`–`13` (inclusive) |
197
+ | `hidden_end` | `Integer`, `nil` | `13` | End index `0`–`13` (inclusive); if `hidden_start > hidden_end`, they are swapped |
198
+ | `dot_key` | `String`, `nil` | `'.'` | Separator between groups `XX` / `XXX` / `XXX` |
199
+ | `slash_key` | `String`, `nil` | `'/'` | Separator before the branch block |
200
+ | `dash_key` | `String`, `nil` | `'-'` | Separator before the last two characters |
201
+ | `escape` | `Boolean`, `nil` | `false` | When truthy, HTML-escapes the final string |
202
+ | `encode` | `Boolean`, `nil` | `false` | When truthy, URL-encodes the final string (similar to `encodeURIComponent`) |
203
+ | `on_fail` | `Proc`, `nil` | see below | `(value, error) -> String` β€” used when sanitized length β‰  14 |
204
+
205
+ Default **`on_fail`** returns an empty string. Signature: `(original_input, error) -> String`, where `error` is a **`CnpjFmt::DomainError`** (currently an `InvalidLengthError` with `actual_input`, `evaluated_input`, `expected_length`). The callback return value must be a `String`; otherwise **`CnpjFmt::TypeMismatchError`** is raised.
206
+
207
+ Example with all options:
208
+
209
+ ```ruby
210
+ require 'cnpj-fmt'
211
+
212
+ cnpj = '03603568000195'
213
+
214
+ CnpjFmt.cnpj_fmt(
215
+ cnpj,
216
+ hidden: true,
217
+ hidden_key: '#',
218
+ hidden_start: 5,
219
+ hidden_end: 11,
220
+ dot_key: ' ',
221
+ slash_key: '|',
222
+ dash_key: '_-_',
223
+ escape: true,
224
+ encode: true,
225
+ on_fail: ->(value, _error) { value.to_s }
226
+ )
227
+ ```
228
+
229
+ ### Error handling
230
+
231
+ Errors fall into two categories:
232
+
233
+ | Category | Meaning |
234
+ |---|---|
235
+ | **API misuse** | The caller invoked the library incorrectly (wrong type for input or options, or an invalid argument combination). |
236
+ | **Domain error** | The call was structurally correct, but a value violates a business rule (length, range, forbidden characters). |
237
+
238
+ Every custom error includes the `CnpjFmt::Error` marker module. Domain failures (`InvalidLengthError`, `OutOfRangeError`, `ValidationError`) inherit from `CnpjFmt::DomainError` (`RangeError`).
239
+
240
+ **Important:** length failures are **constructed as `InvalidLengthError` and passed to `on_fail` as a `DomainError`**, not raised from `format` / `cnpj_fmt`. Passing both an `options` instance/`Hash` and any non-`nil` keyword argument raises `InvalidArgumentCombinationError`.
241
+
242
+ #### Summary
243
+
244
+ | Class | Inherits from | Category | Trigger condition |
245
+ |---|---|---|---|
246
+ | `CnpjFmt::InvalidArgumentCombinationError` | `ArgumentError` (+ `include Error`) | API misuse | Both an `options` instance/`Hash` and any non-`nil` keyword argument are passed at once |
247
+ | `CnpjFmt::TypeMismatchError` | `TypeError` (+ `include Error`) | API misuse | CNPJ input or option has the wrong data type |
248
+ | `CnpjFmt::InvalidLengthError` | `CnpjFmt::DomainError` | Domain error | Sanitized length is not exactly 14 (passed to `on_fail` as `DomainError`) |
249
+ | `CnpjFmt::OutOfRangeError` | `CnpjFmt::DomainError` | Domain error | `hidden_start` / `hidden_end` outside `0`–`13` |
250
+ | `CnpjFmt::ValidationError` | `CnpjFmt::DomainError` | Domain error | Key option contains a disallowed character |
251
+
252
+ #### `CnpjFmt::Error` (marker module)
253
+
254
+ - **Inheritance:** module marker mixed into every library error via `include` (not a class).
255
+ - **Category:** N/A (rescue target only) β€” not a failure mode by itself.
256
+ - **When it is raised:** Never raised directly; included by every custom error the library raises or constructs for `on_fail`.
257
+ - **Example:** N/A
258
+ - **How to rescue it:**
259
+
260
+ ```ruby
261
+ rescue CnpjFmt::Error
262
+ # everything this library raises
263
+ ```
264
+
265
+ #### `CnpjFmt::DomainError`
266
+
267
+ - **Inheritance:** `CnpjFmt::DomainError < RangeError` (includes `CnpjFmt::Error`)
268
+ - **Category:** Domain error β€” ancestor for numeric/length domain failures.
269
+ - **When it is raised:** Not raised directly; prefer raising a leaf subclass.
270
+ - **Example:** Prefer `raise CnpjFmt::OutOfRangeError` / construct `InvalidLengthError` over raising `DomainError` directly.
271
+ - **How to rescue it:**
272
+
273
+ ```ruby
274
+ rescue CnpjFmt::DomainError
275
+ # OutOfRangeError, InvalidLengthError, ValidationError, and other DomainError subclasses
276
+ ```
277
+
278
+ #### `CnpjFmt::TypeMismatchError`
279
+
280
+ - **Inheritance:** `CnpjFmt::TypeMismatchError < TypeError` (includes `CnpjFmt::Error`)
281
+ - **Category:** API misuse β€” the caller passed a value of the wrong type.
282
+ - **When it is raised:** Raised when the CNPJ input is not a `String` or an `Array` of strings, when an option has the wrong type, or when `on_fail` does not return a `String`.
283
+ - **Example:**
284
+
285
+ ```ruby
286
+ CnpjFmt::CnpjFormatter.new.format(12_345) # raises CnpjFmt::TypeMismatchError
287
+ ```
288
+
289
+ - **How to rescue it:**
290
+
291
+ ```ruby
292
+ rescue CnpjFmt::TypeMismatchError
293
+ # this library's type-contract violation
294
+
295
+ rescue TypeError
296
+ # native type errors, including this library's TypeMismatchError
297
+ ```
298
+
299
+ #### `CnpjFmt::InvalidLengthError`
300
+
301
+ - **Inheritance:** `CnpjFmt::InvalidLengthError < CnpjFmt::DomainError < RangeError` (includes `CnpjFmt::Error`)
302
+ - **Category:** Domain error β€” a collection or string length violates a business rule.
303
+ - **When it is raised:** Not raised from `format`; constructed and passed as the `DomainError` second argument to `on_fail` when the sanitized CNPJ does not contain exactly 14 alphanumeric characters.
304
+ - **Example:**
305
+
306
+ ```ruby
307
+ CnpjFmt::CnpjFormatter.new.format(
308
+ 'short',
309
+ on_fail: ->(_value, error) {
310
+ error # => #<CnpjFmt::InvalidLengthError ...> (a DomainError)
311
+ 'invalid'
312
+ }
313
+ ) # => "invalid"
314
+
315
+ ```
316
+
317
+ - **How to rescue it:** Handle inside `on_fail` (typical), or rescue if you re-raise:
318
+
319
+ ```ruby
320
+ rescue CnpjFmt::InvalidLengthError
321
+ # this exact length violation
322
+
323
+ rescue CnpjFmt::DomainError
324
+ # RangeError-rooted domain failures from this library
325
+ ```
326
+
327
+ #### `CnpjFmt::InvalidArgumentCombinationError`
328
+
329
+ - **Inheritance:** `CnpjFmt::InvalidArgumentCombinationError < ArgumentError` (includes `CnpjFmt::Error`)
330
+ - **Category:** API misuse β€” the caller mixed mutually exclusive argument patterns.
331
+ - **When it is raised:** Raised when `CnpjFormatter.new`, `#format`, or `cnpj_fmt` receives both an `options` argument (instance or `Hash`) and any non-`nil` keyword argument at the same time.
332
+ - **Example:**
333
+
334
+ ```ruby
335
+ begin
336
+ CnpjFmt::CnpjFormatter.new({ slash_key: '|' }, hidden: true)
337
+ rescue CnpjFmt::InvalidArgumentCombinationError => e
338
+ puts e.message
339
+ # Pass either an options instance/Hash to `options`, or keyword arguments (hidden:, ...), not both.
340
+ end
341
+ ```
342
+
343
+ - **How to rescue it:**
344
+
345
+ ```ruby
346
+ rescue CnpjFmt::InvalidArgumentCombinationError
347
+ # this library's invalid argument combination
348
+
349
+ rescue ArgumentError
350
+ # native argument errors, including this library's InvalidArgumentCombinationError
351
+ ```
352
+
353
+ #### `CnpjFmt::OutOfRangeError`
354
+
355
+ - **Inheritance:** `CnpjFmt::OutOfRangeError < CnpjFmt::DomainError < RangeError` (includes `CnpjFmt::Error`)
356
+ - **Category:** Domain error β€” a numeric value violates a business range rule.
357
+ - **When it is raised:** Raised when `hidden_start` or `hidden_end` is outside the inclusive range `0`–`13`.
358
+ - **Example:**
359
+
360
+ ```ruby
361
+ CnpjFmt::CnpjFormatterOptions.new(hidden_start: 14) # raises CnpjFmt::OutOfRangeError
362
+ ```
363
+
364
+ - **How to rescue it:**
365
+
366
+ ```ruby
367
+ rescue CnpjFmt::OutOfRangeError
368
+ # this exact range violation
369
+
370
+ rescue CnpjFmt::DomainError
371
+ # RangeError-rooted domain failures from this library
372
+ ```
373
+
374
+ #### `CnpjFmt::ValidationError`
375
+
376
+ - **Inheritance:** `CnpjFmt::ValidationError < CnpjFmt::DomainError < RangeError` (includes `CnpjFmt::Error`)
377
+ - **Category:** Domain error β€” a value fails a non-numeric, non-length domain rule.
378
+ - **When it is raised:** Raised when a key option (`hidden_key`, `dot_key`, `slash_key`, `dash_key`) contains a disallowed character.
379
+ - **Example:**
380
+
381
+ ```ruby
382
+ CnpjFmt::CnpjFormatterOptions.new(dot_key: 'Γ₯') # raises CnpjFmt::ValidationError
383
+ ```
384
+
385
+ - **How to rescue it:**
386
+
387
+ ```ruby
388
+ rescue CnpjFmt::ValidationError
389
+ # this exact domain validation failure
390
+
391
+ rescue CnpjFmt::DomainError
392
+ # RangeError-rooted domain failures from this library
393
+ ```
394
+
395
+ #### Rescue granularity
396
+
397
+ ```ruby
398
+ # 1) Single native class β€” catches type misuse from this library (and other TypeErrors).
399
+ rescue TypeError
400
+ # CnpjFmt::TypeMismatchError and any other TypeError (library or not)
401
+
402
+ # 2) CnpjFmt::DomainError β€” catches business-rule violations under DomainError.
403
+ rescue CnpjFmt::DomainError
404
+ # CnpjFmt::OutOfRangeError, CnpjFmt::InvalidLengthError, CnpjFmt::ValidationError,
405
+ # and other DomainError subclasses
406
+
407
+ # 3) CnpjFmt::Error β€” catches everything the library raises.
408
+ rescue CnpjFmt::Error
409
+ # every custom error that includes CnpjFmt::Error
410
+
411
+ # 4) Specific leaf class β€” catches only that exact failure mode.
412
+ rescue CnpjFmt::OutOfRangeError
413
+ # only CnpjFmt::OutOfRangeError
414
+ ```
415
+
416
+ Notable attributes:
417
+
418
+ - `TypeMismatchError`: `actual_input`, `actual_type`, `expected_type`, `option_name` (nil for CNPJ input)
419
+ - `InvalidLengthError`: `actual_input`, `evaluated_input`, `expected_length`
420
+ - `OutOfRangeError`: `option_name`, `actual_input`, `min_expected_value`, `max_expected_value`
421
+ - `ValidationError`: `option_name`, `actual_input`, `forbidden_characters`
422
+
423
+ ## API
424
+
425
+ ### Exports
426
+
427
+ After `require 'cnpj-fmt'`:
428
+
429
+ - **`CnpjFmt.cnpj_fmt`**: `(cnpj_input, options = nil, **keywords) -> String` β€” convenience helper.
430
+ - **`CnpjFmt::CnpjFormatter`**: Class to format CNPJ with optional default options; accepts `String` or `Array<String>` in `format`.
431
+ - **`CnpjFmt::CnpjFormatterOptions`**: Class holding options; supports merge via constructor, `set`, and keyword arguments.
432
+ - **`CnpjFmt::CNPJ_LENGTH`**: `14` (constant).
433
+ - **`CnpjFmt::VERSION`**: gem version string.
434
+ - **Type predicate**: `CnpjFmt::CnpjInput` β€” `CnpjFmt::CnpjInput.accept?(value)` / `CnpjFmt::CnpjInput === value` is true only for `String` or `Array<String>`.
435
+ - **Errors**: `CnpjFmt::Error`, `CnpjFmt::DomainError`, `CnpjFmt::InvalidArgumentCombinationError`, `CnpjFmt::TypeMismatchError`, `CnpjFmt::InvalidLengthError`, `CnpjFmt::OutOfRangeError`, `CnpjFmt::ValidationError`.
436
+
437
+ ### Other available resources
438
+
439
+ - **`CnpjFmt::CnpjFormatterOptions::CNPJ_LENGTH`**: `14`.
440
+ - **`CnpjFmt::CnpjFormatterOptions::DISALLOWED_KEY_CHARACTERS`**: Characters forbidden in `hidden_key`, `dot_key`, `slash_key`, `dash_key`.
441
+ - **`CnpjFmt::CnpjFormatterOptions::DEFAULT_*`**: Default values for each option.
442
+ - **`CnpjFmt::CnpjFormatterOptions.default_on_fail`**: Shared default failure callback.
443
+
444
+ ## Contribution & Support
445
+
446
+ 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:
447
+
448
+ - ⭐ Starring the repository
449
+ - 🀝 Contributing to the codebase
450
+ - πŸ’‘ [Suggesting new features](https://github.com/LacusSolutions/br-utils-ruby/issues)
451
+ - πŸ› [Reporting bugs](https://github.com/LacusSolutions/br-utils-ruby/issues)
452
+
453
+ ## License
454
+
455
+ This project is licensed under the MIT License β€” see the [LICENSE](https://github.com/LacusSolutions/br-utils-ruby/blob/main/LICENSE) file for details.
456
+
457
+ ## Changelog
458
+
459
+ See [CHANGELOG](./CHANGELOG.md) for a list of changes and version history.
460
+
461
+ ---
462
+
463
+ Made with ❀️ by [Lacus Solutions](https://github.com/LacusSolutions)