cpf-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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -0
- data/LICENSE +9 -0
- data/README.md +657 -0
- data/README.pt.md +642 -0
- data/src/cpf-utilities/cpf_fmt.rb +10 -0
- data/src/cpf-utilities/cpf_gen.rb +10 -0
- data/src/cpf-utilities/cpf_utils.rb +395 -0
- data/src/cpf-utilities/cpf_val.rb +9 -0
- data/src/cpf-utilities/errors.rb +23 -0
- data/src/cpf-utilities/version.rb +7 -2
- data/src/cpf-utilities.rb +23 -9
- metadata +40 -11
data/README.md
ADDED
|
@@ -0,0 +1,657 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
[](https://rubygems.org/gems/cpf-utilities)
|
|
4
|
+
[](https://rubygems.org/gems/cpf-utilities)
|
|
5
|
+
[](https://www.ruby-lang.org/)
|
|
6
|
+
[](https://github.com/LacusSolutions/br-utils-ruby/actions)
|
|
7
|
+
[](https://github.com/LacusSolutions/br-utils-ruby)
|
|
8
|
+
[](https://github.com/LacusSolutions/br-utils-ruby/blob/main/LICENSE)
|
|
9
|
+
|
|
10
|
+
> π [Acessar documentaΓ§Γ£o em portuguΓͺs](./README.pt.md)
|
|
11
|
+
|
|
12
|
+
A Ruby toolkit to format, generate, and validate CPF (Brazilian Individual's Taxpayer ID). It wraps [`cpf-fmt`](https://rubygems.org/gems/cpf-fmt), [`cpf-gen`](https://rubygems.org/gems/cpf-gen), and [`cpf-val`](https://rubygems.org/gems/cpf-val) in a single faΓ§ade class (`CpfUtils`).
|
|
13
|
+
|
|
14
|
+
## Ruby Support
|
|
15
|
+
|
|
16
|
+
|  |  |  |  |  |
|
|
17
|
+
| --- | --- | --- | --- | --- |
|
|
18
|
+
| Passing β | Passing β | Passing β | Passing β | Passing β |
|
|
19
|
+
|
|
20
|
+
Requires Ruby **β₯ 3.1** (see `required_ruby_version` in the gemspec).
|
|
21
|
+
|
|
22
|
+
## Features
|
|
23
|
+
|
|
24
|
+
- β
**Unified API**: Class helpers `CpfUtils.format` / `.generate` / `.is_valid`
|
|
25
|
+
- β
**Two-tier access**: Prefer `CpfUtils::CpfFormatter` / `CpfGenerator` / `CpfValidator` for the main classes; Options, helpers, and errors live under `CpfUtils::CpfFmt` / `CpfGen` / `CpfVal` (root siblings `CpfFmt` / `CpfGen` / `CpfVal` still work)
|
|
26
|
+
- β
**Numeric CPF**: Format, generate, and validate 11-digit numeric CPF (`XXX.XXX.XXX-XX`)
|
|
27
|
+
- β
**Reusable instance**: `CpfUtils` class with optional default settings (formatter/generator options or instances; validator instance)
|
|
28
|
+
- β
**Flexible input**: `#format` and `#is_valid` accept a `String` or an `Array` of strings (elements concatenated in order)
|
|
29
|
+
- β
**Per-call overrides**: Instance defaults plus a per-call options `Hash`/`*Options` instance **or** keyword overrides on `#format` / `#generate` (not both); `#is_valid` takes input only
|
|
30
|
+
- β
**Error handling**: Component errors propagate unchanged; this gem defines `CpfUtils::TypeMismatchError` and `CpfUtils::InvalidArgumentCombinationError` for API misuse
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
Install the gem directly:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
gem install cpf-utilities
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Or add it to your `Gemfile` and run `bundle install`:
|
|
41
|
+
|
|
42
|
+
```ruby
|
|
43
|
+
gem 'cpf-utilities'
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
This installs **`cpf-utilities`** together with [`cpf-fmt`](https://rubygems.org/gems/cpf-fmt), [`cpf-gen`](https://rubygems.org/gems/cpf-gen), and [`cpf-val`](https://rubygems.org/gems/cpf-val). You do **not** need separate `gem install` / `gem` lines for the component packages when using **`cpf-utilities`**.
|
|
47
|
+
|
|
48
|
+
## Require
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
require 'cpf-utilities'
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Quick Start
|
|
55
|
+
|
|
56
|
+
Basic usage with class helpers (aliases of `CpfUtils::DEFAULT`):
|
|
57
|
+
|
|
58
|
+
```ruby
|
|
59
|
+
require 'cpf-utilities'
|
|
60
|
+
|
|
61
|
+
cpf = '12345678909'
|
|
62
|
+
|
|
63
|
+
CpfUtils.format(cpf) # => "123.456.789-09"
|
|
64
|
+
CpfUtils.format(cpf, hidden: true) # => "123.***.***-**"
|
|
65
|
+
CpfUtils.format( # => "123456789_09"
|
|
66
|
+
cpf,
|
|
67
|
+
dot_key: '',
|
|
68
|
+
dash_key: '_'
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
CpfUtils.generate # => e.g. "47844241055" (11-digit numeric)
|
|
72
|
+
CpfUtils.generate(format: true) # => e.g. "478.442.410-55"
|
|
73
|
+
CpfUtils.generate(prefix: '528250911') # => e.g. "52825091138"
|
|
74
|
+
|
|
75
|
+
CpfUtils.is_valid('12345678909') # => true
|
|
76
|
+
CpfUtils.is_valid('123.456.789-09') # => true
|
|
77
|
+
CpfUtils.is_valid('12345678900') # => false
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Usage
|
|
81
|
+
|
|
82
|
+
You can work in these equivalent ways:
|
|
83
|
+
|
|
84
|
+
1. **`CpfUtils.format` / `.generate` / `.is_valid`** β class helpers for quick one-off calls (forward to `DEFAULT`).
|
|
85
|
+
2. **`CpfUtils::DEFAULT`** β mutable shared singleton (same object the class helpers use; process-wide / not thread-isolated).
|
|
86
|
+
3. **`CpfUtils.new`** β configurable instance with shared defaults across format, generate, and validate.
|
|
87
|
+
4. **Main classes under `CpfUtils`** β `CpfUtils::CpfFormatter`, `CpfUtils::CpfGenerator`, `CpfUtils::CpfValidator`.
|
|
88
|
+
5. **Nested package modules** β Options, helpers, errors, and types via `CpfUtils::CpfFmt` / `CpfGen` / `CpfVal` (e.g. `CpfUtils::CpfFmt::CpfFormatterOptions`, `CpfUtils::CpfFmt.cpf_fmt`).
|
|
89
|
+
6. **Root sibling modules** (still supported) β `CpfFmt`, `CpfGen`, `CpfVal` unchanged.
|
|
90
|
+
|
|
91
|
+
All approaches expose the same options and behavior. For exhaustive option tables and component-specific details, see the README of each [bundled package](#bundled-packages).
|
|
92
|
+
|
|
93
|
+
### Formatter options
|
|
94
|
+
|
|
95
|
+
When calling `#format(cpf_input, options = nil, **keywords)`, all options are optional:
|
|
96
|
+
|
|
97
|
+
| Option | Type | Default | Description |
|
|
98
|
+
|--------|------|---------|-------------|
|
|
99
|
+
| `hidden` | `Boolean` | `false` | When `true`, mask digits in `hidden_start`β`hidden_end` with `hidden_key` |
|
|
100
|
+
| `hidden_key` | `String` | `'*'` | Character(s) used to replace masked digits |
|
|
101
|
+
| `hidden_start` | `Integer` | `3` | Start index (0β10, inclusive) of the range to hide |
|
|
102
|
+
| `hidden_end` | `Integer` | `10` | End index (0β10, inclusive) of the range to hide |
|
|
103
|
+
| `dot_key` | `String` | `'.'` | Dot delimiter (e.g. in `123.456.789`) |
|
|
104
|
+
| `dash_key` | `String` | `'-'` | Dash delimiter (e.g. before check digits `β¦-09`) |
|
|
105
|
+
| `escape` | `Boolean` | `false` | When `true`, escape HTML special characters in the result |
|
|
106
|
+
| `encode` | `Boolean` | `false` | When `true`, URL-encode the result (similar to JavaScript `encodeURIComponent`) |
|
|
107
|
+
| `on_fail` | `Proc` / callable | returns `''` | Callback when sanitized input length β 11; return value is used as result |
|
|
108
|
+
|
|
109
|
+
### Generator options
|
|
110
|
+
|
|
111
|
+
When calling `#generate(options = nil, **keywords)`, all options are optional:
|
|
112
|
+
|
|
113
|
+
| Option | Type | Default | Description |
|
|
114
|
+
|--------|------|---------|-------------|
|
|
115
|
+
| `format` | `Boolean` | `false` | When `true`, return the generated CPF in standard format (`000.000.000-00`) |
|
|
116
|
+
| `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. |
|
|
117
|
+
|
|
118
|
+
Prefix rules: the base (first 9 digits) cannot be all zeros; 9 repeated digits (e.g. `999999999`) are not allowed.
|
|
119
|
+
|
|
120
|
+
### Class helpers (`CpfUtils.format` / `.generate` / `.is_valid`)
|
|
121
|
+
|
|
122
|
+
These class methods are aliases of the same methods on `CpfUtils::DEFAULT`. Prefer them for one-off calls:
|
|
123
|
+
|
|
124
|
+
```ruby
|
|
125
|
+
CpfUtils.format('12345678909')
|
|
126
|
+
CpfUtils.generate(format: true)
|
|
127
|
+
CpfUtils.is_valid('12345678909')
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### `CpfUtils::DEFAULT` (default instance)
|
|
131
|
+
|
|
132
|
+
`CpfUtils::DEFAULT` is the pre-built, **mutable** singleton behind the class helpers (parity with the JS default export / Python `cpf_utils`). Its configuration is **process-wide and shared across threads**: mutating it (e.g. `DEFAULT.formatter = β¦`) affects subsequent `CpfUtils.format` / `.generate` / `.is_valid` calls for every caller in the process. Prefer `CpfUtils.new` or per-call options for concurrent or isolated work; custom instances stay independent of `DEFAULT`:
|
|
133
|
+
|
|
134
|
+
```ruby
|
|
135
|
+
CpfUtils::DEFAULT.formatter = { dash_key: '|' }
|
|
136
|
+
CpfUtils.format('12345678909') # => "123.456.789|09"
|
|
137
|
+
|
|
138
|
+
custom = CpfUtils.new
|
|
139
|
+
custom.format('12345678909') # => "123.456.789-09" (unaffected)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Instance methods on `DEFAULT` (and any `CpfUtils` instance):
|
|
143
|
+
|
|
144
|
+
- **`#format(cpf_input, options = nil, **keywords)`**: Formats a CPF string or array of strings. Delegates to the internal formatter. Input must be 11 digits (after sanitization); otherwise `on_fail` is used.
|
|
145
|
+
- **`#generate(options = nil, **keywords)`**: Generates a valid CPF. Delegates to the internal generator.
|
|
146
|
+
- **`#is_valid(cpf_input)`**: Returns `true` if the CPF is valid. Delegates to the internal validator. No per-call options β the CPF validator has none.
|
|
147
|
+
|
|
148
|
+
### `CpfUtils` (class)
|
|
149
|
+
|
|
150
|
+
For custom default formatter, generator, or validator, create your own instance:
|
|
151
|
+
|
|
152
|
+
```ruby
|
|
153
|
+
require 'cpf-utilities'
|
|
154
|
+
|
|
155
|
+
utils = CpfUtils.new(
|
|
156
|
+
formatter: { hidden: true, hidden_key: '#' },
|
|
157
|
+
generator: { format: true, prefix: '123' }
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
utils.format('47844241055') # => "478.###.###-##"
|
|
161
|
+
utils.generate # => e.g. "123.456.789-09"
|
|
162
|
+
utils.is_valid('123.456.789-09') # => true
|
|
163
|
+
|
|
164
|
+
# Access or replace internal instances
|
|
165
|
+
utils.formatter # => CpfFmt::CpfFormatter
|
|
166
|
+
utils.generator # => CpfGen::CpfGenerator
|
|
167
|
+
utils.validator # => CpfVal::CpfValidator
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
- **`CpfUtils.new(settings = nil, **keywords)`**: Optional settings. Pass either a settings `Hash` with `:formatter`, `:generator`, and/or `:validator` keys, **or** the same keys as keyword arguments β not both (passing both raises `CpfUtils::InvalidArgumentCombinationError`). For `:formatter` / `:generator`, each value may be a component instance, a `*Options` instance (stored by reference β mutating it later affects subsequent calls with no per-call override), a plain options `Hash`, or omitted/`nil` for defaults. For `:validator`, pass a `CpfVal::CpfValidator` instance, `nil`, or a duck-typed object β **not** an options `Hash` (there is no `CpfValidatorOptions`).
|
|
171
|
+
- **`#format(cpf_input, options = nil, **keywords)`**: Same as the default instance; per-call options override the formatterβs defaults for that call only. Pass either an options `Hash`/`CpfFmt::CpfFormatterOptions` **or** keyword overrides β not both.
|
|
172
|
+
- **`#generate(options = nil, **keywords)`**: Same as the default instance; per-call options override the generatorβs defaults. Pass either an options `Hash`/`CpfGen::CpfGeneratorOptions` **or** keyword overrides β not both.
|
|
173
|
+
- **`#is_valid(cpf_input)`**: Same as the default instance. No per-call options.
|
|
174
|
+
- **`#formatter`**, **`#generator`**, **`#validator`**: Accessors (getters and setters) for the internal components. Setters accept the same shapes as the constructor. To change a single formatter/generator option without replacing the instance, mutate the componentβs options (e.g. `utils.formatter.options.hidden = true`).
|
|
175
|
+
|
|
176
|
+
Instance defaults and per-call overrides:
|
|
177
|
+
|
|
178
|
+
```ruby
|
|
179
|
+
require 'cpf-utilities'
|
|
180
|
+
|
|
181
|
+
utils = CpfUtils.new(
|
|
182
|
+
formatter: { hidden: true, hidden_key: '#' },
|
|
183
|
+
generator: { format: true }
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
cpf = '12345678909'
|
|
187
|
+
|
|
188
|
+
utils.format(cpf) # masked (instance formatter defaults)
|
|
189
|
+
utils.format(cpf, hidden: false) # this call only: unmasked
|
|
190
|
+
utils.generate(format: false) # this call only: compact output
|
|
191
|
+
utils.is_valid(cpf) # => true
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Options can also be passed as a `Hash` (or options instance) on `#format` / `#generate` β without keyword overrides:
|
|
195
|
+
|
|
196
|
+
```ruby
|
|
197
|
+
utils.format(cpf, { dash_key: '|' })
|
|
198
|
+
utils.generate({ prefix: '12345', format: true })
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Using component classes and nested modules
|
|
202
|
+
|
|
203
|
+
Preferred paths after `require 'cpf-utilities'`:
|
|
204
|
+
|
|
205
|
+
```ruby
|
|
206
|
+
require 'cpf-utilities'
|
|
207
|
+
|
|
208
|
+
# Main classes at the faΓ§ade root
|
|
209
|
+
formatter = CpfUtils::CpfFormatter.new(hidden: true)
|
|
210
|
+
generator = CpfUtils::CpfGenerator.new(format: true)
|
|
211
|
+
validator = CpfUtils::CpfValidator.new
|
|
212
|
+
|
|
213
|
+
formatter.format('47844241055') # => "478.***.***-**"
|
|
214
|
+
|
|
215
|
+
# Options, helpers, and errors under nested package modules
|
|
216
|
+
options = CpfUtils::CpfFmt::CpfFormatterOptions.new(dash_key: '|')
|
|
217
|
+
CpfUtils::CpfFmt.cpf_fmt('12345678909') # => "123.456.789-09"
|
|
218
|
+
|
|
219
|
+
begin
|
|
220
|
+
CpfUtils::CpfFmt.cpf_fmt(12_345)
|
|
221
|
+
rescue CpfUtils::CpfFmt::TypeMismatchError
|
|
222
|
+
# wrong input type
|
|
223
|
+
end
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Root siblings remain supported (same objects as the nests):
|
|
227
|
+
|
|
228
|
+
```ruby
|
|
229
|
+
CpfFmt.cpf_fmt('12345678909', dash_key: '|') # => "123.456.789|09"
|
|
230
|
+
CpfGen.cpf_gen(format: true) # => e.g. "478.442.410-55"
|
|
231
|
+
CpfVal.cpf_val('12345678909') # => true
|
|
232
|
+
CpfFmt::CpfFormatter.new(hidden: true)
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
See [`cpf-fmt`](../cpf-fmt/README.md), [`cpf-gen`](../cpf-gen/README.md), and [`cpf-val`](../cpf-val/README.md) for full option and error details.
|
|
236
|
+
|
|
237
|
+
## API
|
|
238
|
+
|
|
239
|
+
### Exports
|
|
240
|
+
|
|
241
|
+
After `require 'cpf-utilities'`:
|
|
242
|
+
|
|
243
|
+
- **`CpfUtils`**: FaΓ§ade class to create a utils instance with optional default formatter, generator, and validator settings.
|
|
244
|
+
- **`CpfUtils.format` / `.generate` / `.is_valid`**: Class helpers that forward to `CpfUtils::DEFAULT`.
|
|
245
|
+
- **`CpfUtils::DEFAULT`**: Mutable pre-built `CpfUtils` instance (same object the class helpers use). Process-wide / shared across threads β prefer `CpfUtils.new` or per-call options under concurrency.
|
|
246
|
+
- **`CpfUtils::VERSION`**: Gem version string.
|
|
247
|
+
- **Main-class shortcuts**: `CpfUtils::CpfFormatter`, `CpfUtils::CpfGenerator`, `CpfUtils::CpfValidator` (same objects as the sibling classes).
|
|
248
|
+
- **Nested package modules**: `CpfUtils::CpfFmt`, `CpfUtils::CpfGen`, `CpfUtils::CpfVal` β full sibling surface (Options, helpers, errors, types). Options/helpers/errors are **not** aliased at the `CpfUtils` root.
|
|
249
|
+
- **Root sibling modules** (still supported): `CpfFmt`, `CpfGen`, `CpfVal` β same objects as the nests.
|
|
250
|
+
|
|
251
|
+
### Errors & Exceptions
|
|
252
|
+
|
|
253
|
+
`CpfUtils` defines only API-misuse errors for this gemβs argument rules. Component errors are raised by the bundled packages and propagate unchanged.
|
|
254
|
+
|
|
255
|
+
#### Defined by `cpf-utilities`
|
|
256
|
+
|
|
257
|
+
Errors defined by this gem are **API misuse** only (wrong type or invalid argument combination). Every custom error includes the `CpfUtils::Error` marker module. This gem defines **no** `CpfUtils::DomainError` and no domain leaves β domain failures come only from the [bundled packages](#propagated-from-bundled-packages) and keep those packagesβ namespaces (`CpfFmt::β¦`, `CpfGen::β¦`, `CpfVal::β¦`).
|
|
258
|
+
|
|
259
|
+
`rescue CpfUtils::Error` catches **only** errors this gem raises. It does **not** catch component errors that propagate unchanged.
|
|
260
|
+
|
|
261
|
+
##### Summary
|
|
262
|
+
|
|
263
|
+
| Class | Inherits from | Category | Trigger condition |
|
|
264
|
+
|-------|---------------|----------|-------------------|
|
|
265
|
+
| `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 |
|
|
266
|
+
| `CpfUtils::TypeMismatchError` | `CpfUtils::TypeMismatchError < TypeError < StandardError` (+ `include CpfUtils::Error`) | API misuse | Non-`nil` `settings` argument to `CpfUtils.new` is not a `Hash` |
|
|
267
|
+
|
|
268
|
+
##### `CpfUtils::Error` (marker module)
|
|
269
|
+
|
|
270
|
+
- **Inheritance:** module marker mixed into every custom error this gem raises via `include` (not a class).
|
|
271
|
+
- **Category:** N/A (rescue target only) β not a failure mode by itself.
|
|
272
|
+
- **When it is raised:** Never raised directly; included by every custom error this gem raises.
|
|
273
|
+
- **Example:** N/A
|
|
274
|
+
- **How to rescue it:**
|
|
275
|
+
|
|
276
|
+
```ruby
|
|
277
|
+
rescue CpfUtils::Error
|
|
278
|
+
# TypeMismatchError, InvalidArgumentCombinationError from this gem only
|
|
279
|
+
# (not CpfFmt::*, CpfGen::*, or CpfVal::* errors)
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
##### `CpfUtils::TypeMismatchError`
|
|
283
|
+
|
|
284
|
+
- **Inheritance:** `CpfUtils::TypeMismatchError < TypeError < StandardError` (includes `CpfUtils::Error`)
|
|
285
|
+
- **Category:** API misuse β the caller passed a value of the wrong type.
|
|
286
|
+
- **When it is raised:** Raised when `CpfUtils.new` receives a non-`nil` `settings` argument that is not a `Hash`.
|
|
287
|
+
- **Example:**
|
|
288
|
+
|
|
289
|
+
```ruby
|
|
290
|
+
CpfUtils.new('not-a-hash') # raises CpfUtils::TypeMismatchError
|
|
291
|
+
CpfUtils.new(false) # raises CpfUtils::TypeMismatchError (false is non-nil)
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
- **How to rescue it:**
|
|
295
|
+
|
|
296
|
+
```ruby
|
|
297
|
+
rescue CpfUtils::TypeMismatchError
|
|
298
|
+
# this gem's type-contract violation
|
|
299
|
+
|
|
300
|
+
rescue TypeError
|
|
301
|
+
# native type errors, including this gem's TypeMismatchError
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
##### `CpfUtils::InvalidArgumentCombinationError`
|
|
305
|
+
|
|
306
|
+
- **Inheritance:** `CpfUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (includes `CpfUtils::Error`)
|
|
307
|
+
- **Category:** API misuse β the caller mixed mutually exclusive argument patterns.
|
|
308
|
+
- **When it is raised:** Raised when `CpfUtils.new` receives both a non-`nil` settings `Hash` and any non-`nil` keyword argument (`formatter:`, `generator:`, `validator:`); or when `#format`, `#generate`, or the class helpers receive both a non-`nil` options `Hash`/`*Options` instance and any non-`nil` keyword argument at the same time. `#is_valid` has no options path and does not raise this error.
|
|
309
|
+
- **Example:**
|
|
310
|
+
|
|
311
|
+
```ruby
|
|
312
|
+
CpfUtils.new({ formatter: { hidden: true } }, generator: { format: true })
|
|
313
|
+
# raises CpfUtils::InvalidArgumentCombinationError
|
|
314
|
+
|
|
315
|
+
CpfUtils.format('12345678909', { hidden: true }, dash_key: '|')
|
|
316
|
+
# raises CpfUtils::InvalidArgumentCombinationError
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
- **How to rescue it:**
|
|
320
|
+
|
|
321
|
+
```ruby
|
|
322
|
+
rescue CpfUtils::InvalidArgumentCombinationError
|
|
323
|
+
# this gem's invalid signature combination
|
|
324
|
+
|
|
325
|
+
rescue ArgumentError
|
|
326
|
+
# native argument errors, including this gem's InvalidArgumentCombinationError
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
##### Rescue granularity
|
|
330
|
+
|
|
331
|
+
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).
|
|
332
|
+
|
|
333
|
+
```ruby
|
|
334
|
+
require 'cpf-utilities'
|
|
335
|
+
|
|
336
|
+
# 1) Single native class β catches misuse errors of that kind,
|
|
337
|
+
# including non-library ones already handled elsewhere in the consumer's code.
|
|
338
|
+
begin
|
|
339
|
+
CpfUtils.new('not-a-hash')
|
|
340
|
+
rescue TypeError
|
|
341
|
+
# CpfUtils::TypeMismatchError and any other TypeError (library or not)
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
begin
|
|
345
|
+
CpfUtils.new({ formatter: { hidden: true } }, generator: { format: true })
|
|
346
|
+
rescue ArgumentError
|
|
347
|
+
# CpfUtils::InvalidArgumentCombinationError and any other ArgumentError (library or not)
|
|
348
|
+
end
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
```ruby
|
|
352
|
+
require 'cpf-utilities'
|
|
353
|
+
|
|
354
|
+
# 2) Bundled DomainError β this gem defines no DomainError; domain failures
|
|
355
|
+
# come from component packages and keep those namespaces (e.g. CpfFmt).
|
|
356
|
+
begin
|
|
357
|
+
CpfUtils.new.format('12345678909', hidden_start: -1)
|
|
358
|
+
rescue CpfFmt::DomainError
|
|
359
|
+
# CpfFmt::OutOfRangeError, CpfFmt::ValidationError, and other DomainError subclasses
|
|
360
|
+
end
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
```ruby
|
|
364
|
+
require 'cpf-utilities'
|
|
365
|
+
|
|
366
|
+
# 3) CpfUtils::Error β catches everything this gem raises, regardless of native ancestry.
|
|
367
|
+
# Does not catch CpfFmt::*, CpfGen::*, or CpfVal::* errors.
|
|
368
|
+
begin
|
|
369
|
+
CpfUtils.new('not-a-hash')
|
|
370
|
+
rescue CpfUtils::Error
|
|
371
|
+
# every custom error that includes CpfUtils::Error
|
|
372
|
+
end
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
```ruby
|
|
376
|
+
require 'cpf-utilities'
|
|
377
|
+
|
|
378
|
+
# 4) Specific leaf class β catches only that exact failure mode.
|
|
379
|
+
begin
|
|
380
|
+
CpfUtils.new('not-a-hash')
|
|
381
|
+
rescue CpfUtils::TypeMismatchError
|
|
382
|
+
# only CpfUtils::TypeMismatchError
|
|
383
|
+
end
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
#### Propagated from bundled packages
|
|
387
|
+
|
|
388
|
+
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 **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` (default `on_fail` returns `''`).
|
|
389
|
+
|
|
390
|
+
##### Summary
|
|
391
|
+
|
|
392
|
+
| Class | Inherits from | Category | Trigger condition |
|
|
393
|
+
|-------|---------------|----------|-------------------|
|
|
394
|
+
| `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` |
|
|
395
|
+
| `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`) |
|
|
396
|
+
| `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` |
|
|
397
|
+
| `CpfGen::TypeMismatchError` | `CpfGen::TypeMismatchError < TypeError < StandardError` (+ `include CpfGen::Error`) | API misuse | Generator option (`format` / `prefix`) has the wrong type |
|
|
398
|
+
| `CpfVal::TypeMismatchError` | `CpfVal::TypeMismatchError < TypeError < StandardError` (+ `include CpfVal::Error`) | API misuse | CPF input is not a `String` or `Array` of strings |
|
|
399
|
+
| `CpfFmt::InvalidLengthError` | `CpfFmt::InvalidLengthError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Domain error | Sanitized length β 11 β **passed to `on_fail`**, not raised by `#format` |
|
|
400
|
+
| `CpfFmt::OutOfRangeError` | `CpfFmt::OutOfRangeError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Domain error | `hidden_start` / `hidden_end` outside `0`β`10` |
|
|
401
|
+
| `CpfFmt::ValidationError` | `CpfFmt::ValidationError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Domain error | `hidden_key` / `dot_key` / `dash_key` contains a disallowed character |
|
|
402
|
+
| `CpfGen::ValidationError` | `CpfGen::ValidationError < CpfGen::DomainError < RangeError < StandardError` (+ `include CpfGen::Error`) | Domain error | `prefix` is ineligible (zeroed base or 9 repeated digits) |
|
|
403
|
+
|
|
404
|
+
##### `CpfFmt::DomainError`
|
|
405
|
+
|
|
406
|
+
- **Inheritance:** `CpfFmt::DomainError < RangeError < StandardError` (includes `CpfFmt::Error`)
|
|
407
|
+
- **Category:** Domain error β ancestor for formatter domain leaves.
|
|
408
|
+
- **When it is raised:** Not raised directly; rescue target for `OutOfRangeError`, `ValidationError`, and re-raised `InvalidLengthError`.
|
|
409
|
+
- **Example:** Prefer rescuing a leaf, or `CpfFmt::DomainError` for all formatter domain failures.
|
|
410
|
+
- **How to rescue it:**
|
|
411
|
+
|
|
412
|
+
```ruby
|
|
413
|
+
rescue CpfFmt::DomainError
|
|
414
|
+
# OutOfRangeError, ValidationError, InvalidLengthError (if re-raised from on_fail)
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
##### `CpfFmt::TypeMismatchError`
|
|
418
|
+
|
|
419
|
+
- **Inheritance:** `CpfFmt::TypeMismatchError < TypeError < StandardError` (includes `CpfFmt::Error`)
|
|
420
|
+
- **Category:** API misuse β wrong type for CPF input or a formatter option.
|
|
421
|
+
- **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`.
|
|
422
|
+
- **Example:**
|
|
423
|
+
|
|
424
|
+
```ruby
|
|
425
|
+
CpfUtils.new.format(12_345) # raises CpfFmt::TypeMismatchError
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
- **How to rescue it:**
|
|
429
|
+
|
|
430
|
+
```ruby
|
|
431
|
+
rescue CpfFmt::TypeMismatchError
|
|
432
|
+
# formatter type-contract violation
|
|
433
|
+
|
|
434
|
+
rescue TypeError
|
|
435
|
+
# native type errors, including CpfFmt::TypeMismatchError
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
##### `CpfFmt::InvalidArgumentCombinationError`
|
|
439
|
+
|
|
440
|
+
- **Inheritance:** `CpfFmt::InvalidArgumentCombinationError < ArgumentError < StandardError` (includes `CpfFmt::Error`)
|
|
441
|
+
- **Category:** API misuse β mixed `options` and keywords on the formatter API.
|
|
442
|
+
- **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 faΓ§ade raises `CpfUtils::InvalidArgumentCombinationError` for the same pattern on `CpfUtils#format`.)
|
|
443
|
+
- **Example:**
|
|
444
|
+
|
|
445
|
+
```ruby
|
|
446
|
+
CpfFmt::CpfFormatter.new({ dash_key: '_' }, hidden: true)
|
|
447
|
+
# raises CpfFmt::InvalidArgumentCombinationError
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
- **How to rescue it:**
|
|
451
|
+
|
|
452
|
+
```ruby
|
|
453
|
+
rescue CpfFmt::InvalidArgumentCombinationError
|
|
454
|
+
# formatter invalid signature combination
|
|
455
|
+
|
|
456
|
+
rescue ArgumentError
|
|
457
|
+
# native argument errors, including this one
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
##### `CpfFmt::InvalidLengthError` (callback-delivered)
|
|
461
|
+
|
|
462
|
+
- **Inheritance:** `CpfFmt::InvalidLengthError < CpfFmt::DomainError < RangeError < StandardError` (includes `CpfFmt::Error`)
|
|
463
|
+
- **Category:** Domain error β sanitized CPF length is not exactly 11.
|
|
464
|
+
- **When it is raised:** **Not raised** by `#format` / `cpf_fmt`; constructed and passed as the second argument to `on_fail`.
|
|
465
|
+
- **Example:**
|
|
466
|
+
|
|
467
|
+
```ruby
|
|
468
|
+
custom_fail = ->(value, error) {
|
|
469
|
+
error # => #<CpfFmt::InvalidLengthError ...>
|
|
470
|
+
"Invalid CPF: #{value}"
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
CpfUtils.new.format('123', on_fail: custom_fail) # => "Invalid CPF: 123"
|
|
474
|
+
CpfUtils.new.format('123') # => "" (default on_fail)
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
- **How to rescue it:** Handle inside `on_fail` (typical), or rescue if you re-raise:
|
|
478
|
+
|
|
479
|
+
```ruby
|
|
480
|
+
rescue CpfFmt::InvalidLengthError
|
|
481
|
+
# this exact length violation
|
|
482
|
+
|
|
483
|
+
rescue CpfFmt::DomainError
|
|
484
|
+
# RangeError-rooted domain failures from cpf-fmt
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
##### `CpfFmt::OutOfRangeError`
|
|
488
|
+
|
|
489
|
+
- **Inheritance:** `CpfFmt::OutOfRangeError < CpfFmt::DomainError < RangeError < StandardError` (includes `CpfFmt::Error`)
|
|
490
|
+
- **Category:** Domain error β `hidden_start` / `hidden_end` outside `0`β`10`.
|
|
491
|
+
- **When it is raised:** Raised when building or applying formatter options with an out-of-range hide index.
|
|
492
|
+
- **Example:**
|
|
493
|
+
|
|
494
|
+
```ruby
|
|
495
|
+
CpfUtils.new.format('12345678909', hidden_start: -1) # raises CpfFmt::OutOfRangeError
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
- **How to rescue it:**
|
|
499
|
+
|
|
500
|
+
```ruby
|
|
501
|
+
rescue CpfFmt::OutOfRangeError
|
|
502
|
+
# this exact range violation
|
|
503
|
+
|
|
504
|
+
rescue CpfFmt::DomainError
|
|
505
|
+
# RangeError-rooted domain failures from cpf-fmt
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
##### `CpfFmt::ValidationError`
|
|
509
|
+
|
|
510
|
+
- **Inheritance:** `CpfFmt::ValidationError < CpfFmt::DomainError < RangeError < StandardError` (includes `CpfFmt::Error`)
|
|
511
|
+
- **Category:** Domain error β a key option contains a disallowed character.
|
|
512
|
+
- **When it is raised:** Raised when `hidden_key`, `dot_key`, or `dash_key` contains a forbidden character.
|
|
513
|
+
- **Example:**
|
|
514
|
+
|
|
515
|
+
```ruby
|
|
516
|
+
CpfUtils.new(formatter: { dot_key: 'Γ₯' }) # raises CpfFmt::ValidationError
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
- **How to rescue it:**
|
|
520
|
+
|
|
521
|
+
```ruby
|
|
522
|
+
rescue CpfFmt::ValidationError
|
|
523
|
+
# this exact domain validation failure
|
|
524
|
+
|
|
525
|
+
rescue CpfFmt::DomainError
|
|
526
|
+
# RangeError-rooted domain failures from cpf-fmt
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
##### `CpfGen::DomainError`
|
|
530
|
+
|
|
531
|
+
- **Inheritance:** `CpfGen::DomainError < RangeError < StandardError` (includes `CpfGen::Error`)
|
|
532
|
+
- **Category:** Domain error β ancestor for generator domain leaves.
|
|
533
|
+
- **When it is raised:** Not raised directly; rescue target for `CpfGen::ValidationError`.
|
|
534
|
+
- **Example:** Prefer `rescue CpfGen::ValidationError` or `CpfGen::DomainError`.
|
|
535
|
+
- **How to rescue it:**
|
|
536
|
+
|
|
537
|
+
```ruby
|
|
538
|
+
rescue CpfGen::DomainError
|
|
539
|
+
# ValidationError and other DomainError subclasses from cpf-gen
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
##### `CpfGen::TypeMismatchError`
|
|
543
|
+
|
|
544
|
+
- **Inheritance:** `CpfGen::TypeMismatchError < TypeError < StandardError` (includes `CpfGen::Error`)
|
|
545
|
+
- **Category:** API misuse β wrong type for a generator option.
|
|
546
|
+
- **When it is raised:** Raised when `format` or `prefix` has the wrong runtime type.
|
|
547
|
+
- **Example:**
|
|
548
|
+
|
|
549
|
+
```ruby
|
|
550
|
+
CpfUtils.new.generate(prefix: 123) # raises CpfGen::TypeMismatchError
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
- **How to rescue it:**
|
|
554
|
+
|
|
555
|
+
```ruby
|
|
556
|
+
rescue CpfGen::TypeMismatchError
|
|
557
|
+
# generator type-contract violation
|
|
558
|
+
|
|
559
|
+
rescue TypeError
|
|
560
|
+
# native type errors, including CpfGen::TypeMismatchError
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
##### `CpfGen::InvalidArgumentCombinationError`
|
|
564
|
+
|
|
565
|
+
- **Inheritance:** `CpfGen::InvalidArgumentCombinationError < ArgumentError < StandardError` (includes `CpfGen::Error`)
|
|
566
|
+
- **Category:** API misuse β mixed `options` and keywords on the generator API.
|
|
567
|
+
- **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 faΓ§ade raises `CpfUtils::InvalidArgumentCombinationError` for the same pattern on `CpfUtils#generate`.)
|
|
568
|
+
- **Example:**
|
|
569
|
+
|
|
570
|
+
```ruby
|
|
571
|
+
CpfGen::CpfGenerator.new({ format: true }, prefix: '123')
|
|
572
|
+
# raises CpfGen::InvalidArgumentCombinationError
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
- **How to rescue it:**
|
|
576
|
+
|
|
577
|
+
```ruby
|
|
578
|
+
rescue CpfGen::InvalidArgumentCombinationError
|
|
579
|
+
# generator invalid signature combination
|
|
580
|
+
|
|
581
|
+
rescue ArgumentError
|
|
582
|
+
# native argument errors, including this one
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
##### `CpfGen::ValidationError`
|
|
586
|
+
|
|
587
|
+
- **Inheritance:** `CpfGen::ValidationError < CpfGen::DomainError < RangeError < StandardError` (includes `CpfGen::Error`)
|
|
588
|
+
- **Category:** Domain error β ineligible `prefix`.
|
|
589
|
+
- **When it is raised:** Raised when `prefix` is a zeroed base (`'000000000'`) or 9 repeated digits (e.g. `'999999999'`).
|
|
590
|
+
- **Example:**
|
|
591
|
+
|
|
592
|
+
```ruby
|
|
593
|
+
CpfUtils.new.generate(prefix: '000000000') # raises CpfGen::ValidationError
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
- **How to rescue it:**
|
|
597
|
+
|
|
598
|
+
```ruby
|
|
599
|
+
rescue CpfGen::ValidationError
|
|
600
|
+
# this exact domain validation failure
|
|
601
|
+
|
|
602
|
+
rescue CpfGen::DomainError
|
|
603
|
+
# RangeError-rooted domain failures from cpf-gen
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
##### `CpfVal::TypeMismatchError`
|
|
607
|
+
|
|
608
|
+
- **Inheritance:** `CpfVal::TypeMismatchError < TypeError < StandardError` (includes `CpfVal::Error`)
|
|
609
|
+
- **Category:** API misuse β wrong type for CPF input.
|
|
610
|
+
- **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.
|
|
611
|
+
- **Example:**
|
|
612
|
+
|
|
613
|
+
```ruby
|
|
614
|
+
CpfUtils.new.is_valid(12_345_678_909) # raises CpfVal::TypeMismatchError
|
|
615
|
+
CpfUtils.new.is_valid('12345678900') # => false (invalid data, no raise)
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
- **How to rescue it:**
|
|
619
|
+
|
|
620
|
+
```ruby
|
|
621
|
+
rescue CpfVal::TypeMismatchError
|
|
622
|
+
# validator type-contract violation
|
|
623
|
+
|
|
624
|
+
rescue TypeError
|
|
625
|
+
# native type errors, including CpfVal::TypeMismatchError
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
### Bundled packages
|
|
629
|
+
|
|
630
|
+
| Package | Main resources | README |
|
|
631
|
+
|---------|----------------|--------|
|
|
632
|
+
| [`cpf-fmt`](https://rubygems.org/gems/cpf-fmt) | `CpfFmt::CpfFormatter`, `CpfFmt::CpfFormatterOptions`, `CpfFmt.cpf_fmt` | [docs](../cpf-fmt/README.md) |
|
|
633
|
+
| [`cpf-gen`](https://rubygems.org/gems/cpf-gen) | `CpfGen::CpfGenerator`, `CpfGen::CpfGeneratorOptions`, `CpfGen.cpf_gen` | [docs](../cpf-gen/README.md) |
|
|
634
|
+
| [`cpf-val`](https://rubygems.org/gems/cpf-val) | `CpfVal::CpfValidator`, `CpfVal.cpf_val` | [docs](../cpf-val/README.md) |
|
|
635
|
+
|
|
636
|
+
All of the above are pulled in as dependencies of **`cpf-utilities`**. For exhaustive option tables, exception lists, and edge-case behavior, see each package README.
|
|
637
|
+
|
|
638
|
+
## Contribution & Support
|
|
639
|
+
|
|
640
|
+
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:
|
|
641
|
+
|
|
642
|
+
- β Starring the repository
|
|
643
|
+
- π€ Contributing to the codebase
|
|
644
|
+
- π‘ [Suggesting new features](https://github.com/LacusSolutions/br-utils-ruby/issues)
|
|
645
|
+
- π [Reporting bugs](https://github.com/LacusSolutions/br-utils-ruby/issues)
|
|
646
|
+
|
|
647
|
+
## License
|
|
648
|
+
|
|
649
|
+
This project is licensed under the MIT License β see the [LICENSE](https://github.com/LacusSolutions/br-utils-ruby/blob/main/LICENSE) file for details.
|
|
650
|
+
|
|
651
|
+
## Changelog
|
|
652
|
+
|
|
653
|
+
See [CHANGELOG](./CHANGELOG.md) for a list of changes and version history.
|
|
654
|
+
|
|
655
|
+
---
|
|
656
|
+
|
|
657
|
+
Made with β€οΈ by [Lacus Solutions](https://github.com/LacusSolutions)
|