
π Full support for the new alphanumeric CNPJ format.
A Ruby utility to format CNPJ (Brazilian Business Tax ID).
Ruby Support
| Passing β | Passing β | Passing β |
Features
- β
Alphanumeric CNPJ: Supports 14-character alphanumeric CNPJ (digits and letters, e.g.
RK0CMT3W000100) - β
Flexible input: Accepts
StringorArrayof strings; array elements are concatenated in order - β Format agnostic: Strips non-alphanumeric characters and uppercases letters before formatting
- β
Custom delimiters:
dot_key,slash_key, anddash_keymay be empty, single-, or multi-character strings - β
Masking: Optional hiding of a character range with a configurable replacement string (
hidden,hidden_key,hidden_start,hidden_end) - β
HTML & URL output: Optional
escape(HTML entities) andencode(URI component encoding, similar to JavaScriptencodeURIComponent) - β
Length errors without throwing: Invalid length after sanitization is handled via
on_fail(default returns an empty string) - β
Minimal dependencies: Only
lacus-utils - β
Error handling: API misuse vs domain errors with a
CnpjFmt::Errormarker for library-wide rescue
Installation
Install the gem directly:
gem install cnpj-fmt
Or add it to your Gemfile and run bundle install:
gem 'cnpj-fmt'
Require
require 'cnpj-fmt'
Quick Start
require 'cnpj-fmt'
formatter = CnpjFmt::CnpjFormatter.new
formatter.format('03603568000195') # => "03.603.568/0001-95"
formatter.format('12ABC34500DE99') # => "12.ABC.345/00DE-99"
formatter.format('RK0CMT3W000100') # => "RK.0CM.T3W/0001-00"
Basic helper usage:
require 'cnpj-fmt'
cnpj = '03603568000195'
CnpjFmt.cnpj_fmt(cnpj) # => "03.603.568/0001-95"
CnpjFmt.cnpj_fmt(cnpj, hidden: true) # => "03.603.***/****-**"
CnpjFmt.cnpj_fmt( # => "03603568|0001_95"
cnpj,
dot_key: '',
slash_key: '|',
dash_key: '_'
)
Usage
The main entry points are the class CnpjFmt::CnpjFormatter, the options class CnpjFmt::CnpjFormatterOptions, and the helper CnpjFmt.cnpj_fmt.
CnpjFmt::CnpjFormatter
-
initialize(options = nil, **keywords): Optional default formatting options. Whenoptionsis given (aCnpjFmt::CnpjFormatterOptionsinstance or aHash) alone, it determines the default options; aCnpjFmt::CnpjFormatterOptionsinstance is stored by reference (mutating it later affects futureformatcalls that do not pass per-call options), while aHashbuilds a new instance. Whenoptionsis omitted (nil), the default options are built exclusively from the keyword arguments (hidden:,hidden_key:,dot_key:, β¦). Passingoptionstogether with any non-nilkeyword raisesInvalidArgumentCombinationErrorinstead of silently ignoring the keywords. Example:CnpjFmt::CnpjFormatter.new(hidden: true, slash_key: '|'). -
options: Returns the instanceβsCnpjFmt::CnpjFormatterOptions(same object used internally). -
format(cnpj_input, options = nil, **keywords): Formats a CNPJ value.Input is normalized by removing non-alphanumeric characters and uppercasing. If the sanitized length is not exactly 14, the
on_failcallback is invoked with the original input and aCnpjFmt::DomainError(InvalidLengthError); its return value is the result (nothing is thrown for length).If the input is not a
Stringor anArrayof strings,CnpjFmt::TypeMismatchErroris raised.Per-call
optionsand keyword arguments are never merged: a givenoptionsargument 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. Passingoptionstogether with any non-nilkeyword raisesInvalidArgumentCombinationError.
CnpjFmt::CnpjFormatterOptions
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.
initialize(options = nil, *extra_overrides, **keywords): Optional default options (plainHash,CnpjFmt::CnpjFormatterOptionsinstance, or keyword arguments), plus extra override objects merged in order (later overrides win).all: Returns a shallowHashcopy of all current options.copy: Returns a shallow copy of this options instance.set(options): Updates multiple fields at once; returnsself. Accepts aHashor anotherCnpjFmt::CnpjFormatterOptionsinstance. Explicitnilvalues in the update keep the current value.set_hidden_range(hidden_start, hidden_end): Validates indices in[0, 13](inclusive); ifhidden_start > hidden_end, values are swapped.nilarguments fall back to defaults (DEFAULT_HIDDEN_START/DEFAULT_HIDDEN_END).
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).
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).
Functional helper
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):
require 'cnpj-fmt'
cnpj = '03603568000195'
CnpjFmt.cnpj_fmt(cnpj) # => "03.603.568/0001-95"
CnpjFmt.cnpj_fmt(cnpj, hidden: true) # masked with defaults
CnpjFmt.cnpj_fmt( # => "03603568|0001_95"
cnpj,
dot_key: '',
slash_key: '|',
dash_key: '_'
)
CnpjFmt.cnpj_fmt(cnpj, { # Hash form
hidden: true,
hidden_key: '#'
})
Object-oriented examples
require 'cnpj-fmt'
formatter = CnpjFmt::CnpjFormatter.new
cnpj = '03603568000195'
formatter.format(cnpj) # => "03.603.568/0001-95"
formatter.format( # => "03.603.###/####-##"
cnpj,
hidden: true,
hidden_key: '#',
hidden_start: 5,
hidden_end: 13
)
Default options on the instance; per-call overrides:
require 'cnpj-fmt'
formatter = CnpjFmt::CnpjFormatter.new(hidden: true)
cnpj = '03603568000195'
formatter.format(cnpj) # uses instance masking
formatter.format(cnpj, hidden: false) # this call only: unmasked
formatter.format(cnpj) # back to instance defaults
Alphanumeric input and array input:
require 'cnpj-fmt'
formatter = CnpjFmt::CnpjFormatter.new
formatter.format('RK0CMT3W000100') # => "RK.0CM.T3W/0001-00"
formatter.format([ # => "RK.0CM.T3W/0001-00"
'RK',
'0CM',
'T3W',
'0001',
'00'
])
Input formats
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.
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.
Formatting options
| Parameter | Type | Default | Description |
|---|---|---|---|
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 |
hidden_key |
String, nil |
'*' |
Replacement for each hidden position (may be multi-character or empty); must not use disallowed key characters |
hidden_start |
Integer, nil |
5 |
Start index 0β13 (inclusive) |
hidden_end |
Integer, nil |
13 |
End index 0β13 (inclusive); if hidden_start > hidden_end, they are swapped |
dot_key |
String, nil |
'.' |
Separator between groups XX / XXX / XXX |
slash_key |
String, nil |
'/' |
Separator before the branch block |
dash_key |
String, nil |
'-' |
Separator before the last two characters |
escape |
Boolean, nil |
false |
When truthy, HTML-escapes the final string |
encode |
Boolean, nil |
false |
When truthy, URL-encodes the final string (similar to encodeURIComponent) |
on_fail |
Proc, nil |
see below | (value, error) -> String β used when sanitized length β 14 |
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.
Example with all options:
require 'cnpj-fmt'
cnpj = '03603568000195'
CnpjFmt.cnpj_fmt(
cnpj,
hidden: true,
hidden_key: '#',
hidden_start: 5,
hidden_end: 11,
dot_key: ' ',
slash_key: '|',
dash_key: '_-_',
escape: true,
encode: true,
on_fail: ->(value, _error) { value.to_s }
)
Error handling
Errors fall into two categories:
| Category | Meaning |
|---|---|
| API misuse | The caller invoked the library incorrectly (wrong type for input or options, or an invalid argument combination). |
| Domain error | The call was structurally correct, but a value violates a business rule (length, range, forbidden characters). |
Every custom error includes the CnpjFmt::Error marker module. Domain failures (InvalidLengthError, OutOfRangeError, ValidationError) inherit from CnpjFmt::DomainError (RangeError).
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.
Summary
| Class | Inherits from | Category | Trigger condition |
|---|---|---|---|
CnpjFmt::InvalidArgumentCombinationError |
ArgumentError (+ include Error) |
API misuse | Both an options instance/Hash and any non-nil keyword argument are passed at once |
CnpjFmt::TypeMismatchError |
TypeError (+ include Error) |
API misuse | CNPJ input or option has the wrong data type |
CnpjFmt::InvalidLengthError |
CnpjFmt::DomainError |
Domain error | Sanitized length is not exactly 14 (passed to on_fail as DomainError) |
CnpjFmt::OutOfRangeError |
CnpjFmt::DomainError |
Domain error | hidden_start / hidden_end outside 0β13 |
CnpjFmt::ValidationError |
CnpjFmt::DomainError |
Domain error | Key option contains a disallowed character |
CnpjFmt::Error (marker module)
- Inheritance: module marker mixed into every library error via
include(not a class). - Category: N/A (rescue target only) β not a failure mode by itself.
- When it is raised: Never raised directly; included by every custom error the library raises or constructs for
on_fail. - Example: N/A
- How to rescue it:
rescue CnpjFmt::Error
# everything this library raises
CnpjFmt::DomainError
- Inheritance:
CnpjFmt::DomainError < RangeError(includesCnpjFmt::Error) - Category: Domain error β ancestor for numeric/length domain failures.
- When it is raised: Not raised directly; prefer raising a leaf subclass.
- Example: Prefer
raise CnpjFmt::OutOfRangeError/ constructInvalidLengthErrorover raisingDomainErrordirectly. - How to rescue it:
rescue CnpjFmt::DomainError
# OutOfRangeError, InvalidLengthError, ValidationError, and other DomainError subclasses
CnpjFmt::TypeMismatchError
- Inheritance:
CnpjFmt::TypeMismatchError < TypeError(includesCnpjFmt::Error) - Category: API misuse β the caller passed a value of the wrong type.
- When it is raised: Raised when the CNPJ input is not a
Stringor anArrayof strings, when an option has the wrong type, or whenon_faildoes not return aString. - Example:
CnpjFmt::CnpjFormatter.new.format(12_345) # raises CnpjFmt::TypeMismatchError
- How to rescue it:
rescue CnpjFmt::TypeMismatchError
# this library's type-contract violation
rescue TypeError
# native type errors, including this library's TypeMismatchError
CnpjFmt::InvalidLengthError
- Inheritance:
CnpjFmt::InvalidLengthError < CnpjFmt::DomainError < RangeError(includesCnpjFmt::Error) - Category: Domain error β a collection or string length violates a business rule.
- When it is raised: Not raised from
format; constructed and passed as theDomainErrorsecond argument toon_failwhen the sanitized CNPJ does not contain exactly 14 alphanumeric characters. - Example:
CnpjFmt::CnpjFormatter.new.format(
'short',
on_fail: ->(_value, error) {
error # => #<CnpjFmt::InvalidLengthError ...> (a DomainError)
'invalid'
}
) # => "invalid"
- How to rescue it: Handle inside
on_fail(typical), or rescue if you re-raise:
rescue CnpjFmt::InvalidLengthError
# this exact length violation
rescue CnpjFmt::DomainError
# RangeError-rooted domain failures from this library
CnpjFmt::InvalidArgumentCombinationError
- Inheritance:
CnpjFmt::InvalidArgumentCombinationError < ArgumentError(includesCnpjFmt::Error) - Category: API misuse β the caller mixed mutually exclusive argument patterns.
- When it is raised: Raised when
CnpjFormatter.new,#format, orcnpj_fmtreceives both anoptionsargument (instance orHash) and any non-nilkeyword argument at the same time. - Example:
begin
CnpjFmt::CnpjFormatter.new({ slash_key: '|' }, hidden: true)
rescue CnpjFmt::InvalidArgumentCombinationError => e
puts e.
# Pass either an options instance/Hash to `options`, or keyword arguments (hidden:, ...), not both.
end
- How to rescue it:
rescue CnpjFmt::InvalidArgumentCombinationError
# this library's invalid argument combination
rescue ArgumentError
# native argument errors, including this library's InvalidArgumentCombinationError
CnpjFmt::OutOfRangeError
- Inheritance:
CnpjFmt::OutOfRangeError < CnpjFmt::DomainError < RangeError(includesCnpjFmt::Error) - Category: Domain error β a numeric value violates a business range rule.
- When it is raised: Raised when
hidden_startorhidden_endis outside the inclusive range0β13. - Example:
CnpjFmt::CnpjFormatterOptions.new(hidden_start: 14) # raises CnpjFmt::OutOfRangeError
- How to rescue it:
rescue CnpjFmt::OutOfRangeError
# this exact range violation
rescue CnpjFmt::DomainError
# RangeError-rooted domain failures from this library
CnpjFmt::ValidationError
- Inheritance:
CnpjFmt::ValidationError < CnpjFmt::DomainError < RangeError(includesCnpjFmt::Error) - Category: Domain error β a value fails a non-numeric, non-length domain rule.
- When it is raised: Raised when a key option (
hidden_key,dot_key,slash_key,dash_key) contains a disallowed character. - Example:
CnpjFmt::CnpjFormatterOptions.new(dot_key: 'Γ₯') # raises CnpjFmt::ValidationError
- How to rescue it:
rescue CnpjFmt::ValidationError
# this exact domain validation failure
rescue CnpjFmt::DomainError
# RangeError-rooted domain failures from this library
Rescue granularity
# 1) Single native class β catches type misuse from this library (and other TypeErrors).
rescue TypeError
# CnpjFmt::TypeMismatchError and any other TypeError (library or not)
# 2) CnpjFmt::DomainError β catches business-rule violations under DomainError.
rescue CnpjFmt::DomainError
# CnpjFmt::OutOfRangeError, CnpjFmt::InvalidLengthError, CnpjFmt::ValidationError,
# and other DomainError subclasses
# 3) CnpjFmt::Error β catches everything the library raises.
rescue CnpjFmt::Error
# every custom error that includes CnpjFmt::Error
# 4) Specific leaf class β catches only that exact failure mode.
rescue CnpjFmt::OutOfRangeError
# only CnpjFmt::OutOfRangeError
Notable attributes:
TypeMismatchError:actual_input,actual_type,expected_type,option_name(nil for CNPJ input)InvalidLengthError:actual_input,evaluated_input,expected_lengthOutOfRangeError:option_name,actual_input,min_expected_value,max_expected_valueValidationError:option_name,actual_input,forbidden_characters
API
Exports
After require 'cnpj-fmt':
CnpjFmt.cnpj_fmt:(cnpj_input, options = nil, **keywords) -> Stringβ convenience helper.CnpjFmt::CnpjFormatter: Class to format CNPJ with optional default options; acceptsStringorArray<String>informat.CnpjFmt::CnpjFormatterOptions: Class holding options; supports merge via constructor,set, and keyword arguments.CnpjFmt::CNPJ_LENGTH:14(constant).CnpjFmt::VERSION: gem version string.- Type predicate:
CnpjFmt::CnpjInputβCnpjFmt::CnpjInput.accept?(value)/CnpjFmt::CnpjInput === valueis true only forStringorArray<String>. - Errors:
CnpjFmt::Error,CnpjFmt::DomainError,CnpjFmt::InvalidArgumentCombinationError,CnpjFmt::TypeMismatchError,CnpjFmt::InvalidLengthError,CnpjFmt::OutOfRangeError,CnpjFmt::ValidationError.
Other available resources
CnpjFmt::CnpjFormatterOptions::CNPJ_LENGTH:14.CnpjFmt::CnpjFormatterOptions::DISALLOWED_KEY_CHARACTERS: Characters forbidden inhidden_key,dot_key,slash_key,dash_key.CnpjFmt::CnpjFormatterOptions::DEFAULT_*: Default values for each option.CnpjFmt::CnpjFormatterOptions.default_on_fail: Shared default failure callback.
Contribution & Support
We welcome contributions! Please see our Contributing Guidelines for details. If you find this project helpful, please consider:
- β Starring the repository
- π€ Contributing to the codebase
- π‘ Suggesting new features
- π Reporting bugs
License
This project is licensed under the MIT License β see the LICENSE file for details.
Changelog
See CHANGELOG for a list of changes and version history.
Made with β€οΈ by Lacus Solutions