
🚀 Full support for the new alphanumeric CNPJ format.
A Ruby toolkit to format, generate, and validate CNPJ (Brazilian Business Tax ID). It wraps cnpj-fmt, cnpj-gen, and cnpj-val in a single façade class (CnpjUtils).
Ruby Support
| Passing ✔ | Passing ✔ | Passing ✔ | Passing ✔ | Passing ✔ |
Requires Ruby ≥ 3.1 (see required_ruby_version in the gemspec).
Features
- ✅ Unified API: Class helpers
CnpjUtils.format/.generate/.is_valid - ✅ Two-tier access: Prefer
CnpjUtils::CnpjFormatter/CnpjGenerator/CnpjValidatorfor the main classes; Options, helpers, and errors live underCnpjUtils::CnpjFmt/CnpjGen/CnpjVal(root siblingsCnpjFmt/CnpjGen/CnpjValstill work) - ✅ Alphanumeric CNPJ: Format, generate, and validate 14-character numeric or alphanumeric CNPJ
- ✅ Reusable instance:
CnpjUtilsclass with optional default settings (formatter, generator, validator options or instances) - ✅ Flexible input:
#formatand#is_validaccept aStringor anArrayof strings (elements concatenated in order) - ✅ Per-call overrides: Instance defaults plus a per-call options
Hash/*Optionsinstance or keyword overrides (not both) - ✅ Error handling: Component errors propagate unchanged; this gem defines
CnpjUtils::TypeMismatchErrorandCnpjUtils::InvalidArgumentCombinationErrorfor API misuse
Installation
Install the gem directly:
gem install cnpj-utilities
Or add it to your Gemfile and run bundle install:
gem 'cnpj-utilities'
This installs cnpj-utilities together with cnpj-fmt, cnpj-gen, and cnpj-val. You do not need separate gem install / gem lines for the component packages when using cnpj-utilities.
Require
require 'cnpj-utilities'
Quick Start
Basic usage with class helpers (aliases of CnpjUtils::DEFAULT):
require 'cnpj-utilities'
cnpj = '03603568000195'
CnpjUtils.format(cnpj) # => "03.603.568/0001-95"
CnpjUtils.format(cnpj, hidden: true) # => "03.603.***/****-**"
CnpjUtils.format( # => "03603568|0001_95"
cnpj,
dot_key: '',
slash_key: '|',
dash_key: '_'
)
CnpjUtils.generate # => e.g. "AB123CDE000155" (14-char alphanumeric)
CnpjUtils.generate(format: true) # => e.g. "AB.123.CDE/0001-55"
CnpjUtils.generate(prefix: '45623767') # => e.g. "45623767000296"
CnpjUtils.generate(type: 'numeric') # => e.g. "65453043000178" (digits only)
CnpjUtils.is_valid('98765432000198') # => true
CnpjUtils.is_valid('98.765.432/0001-98') # => true
CnpjUtils.is_valid('1QB5UKALPYFP59') # => true (alphanumeric)
CnpjUtils.is_valid('98765432000199') # => false
Usage
You can work in these equivalent ways:
CnpjUtils.format/.generate/.is_valid— class helpers for quick one-off calls (forward toDEFAULT).CnpjUtils::DEFAULT— mutable shared singleton (same object the class helpers use; process-wide / not thread-isolated).CnpjUtils.new— configurable instance with shared defaults across format, generate, and validate.- Main classes under
CnpjUtils—CnpjUtils::CnpjFormatter,CnpjUtils::CnpjGenerator,CnpjUtils::CnpjValidator. - Nested package modules — Options, helpers, errors, and types via
CnpjUtils::CnpjFmt/CnpjGen/CnpjVal(e.g.CnpjUtils::CnpjFmt::CnpjFormatterOptions,CnpjUtils::CnpjFmt.cnpj_fmt). - Root sibling modules (still supported) —
CnpjFmt,CnpjGen,CnpjValunchanged.
All approaches expose the same options and behavior. For exhaustive option tables and component-specific details, see the README of each bundled package.
Formatter options
When calling #format(cnpj_input, options = nil, **keywords), all options are optional:
| Option | Type | Default | Description |
|---|---|---|---|
hidden |
Boolean |
false |
When true, mask characters in hidden_start–hidden_end with hidden_key |
hidden_key |
String |
'*' |
Character(s) used to replace masked characters |
hidden_start |
Integer |
5 |
Start index (0–13, inclusive) of the range to hide |
hidden_end |
Integer |
13 |
End index (0–13, inclusive) of the range to hide |
dot_key |
String |
'.' |
Dot delimiter (e.g. in 12.345.678) |
slash_key |
String |
'/' |
Slash delimiter (e.g. before branch …/0001-90) |
dash_key |
String |
'-' |
Dash delimiter (e.g. before check digits …-90) |
escape |
Boolean |
false |
When true, escape HTML special characters in the result |
encode |
Boolean |
false |
When true, URL-encode the result (similar to JavaScript encodeURIComponent) |
on_fail |
Proc / callable |
returns '' |
Callback when sanitized input length ≠ 14; return value is used as result |
Generator options
When calling #generate(options = nil, **keywords), all options are optional:
| Option | Type | Default | Description |
|---|---|---|---|
format |
Boolean |
false |
When true, return the generated CNPJ in standard format (00.000.000/0000-00) |
prefix |
String |
'' |
Partial start string (0–12 alphanumeric chars). Missing characters are generated and check digits computed. |
type |
String |
'alphanumeric' |
Character set for the randomly generated part: 'numeric', 'alphabetic', or 'alphanumeric'. Check digits are always numeric. |
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.
Validator options
When calling #is_valid(cnpj_input, options = nil, **keywords), all options are optional:
| Option | Type | Default | Description |
|---|---|---|---|
case_sensitive |
Boolean |
true |
When false, lowercase letters are accepted for alphanumeric CNPJ (input is uppercased before validation). |
type |
String |
'alphanumeric' |
'numeric': only digits (0–9); 'alphanumeric': digits and letters (0–9, A–Z). |
Class helpers (CnpjUtils.format / .generate / .is_valid)
These class methods are aliases of the same methods on CnpjUtils::DEFAULT. Prefer them for one-off calls:
CnpjUtils.format('03603568000195')
CnpjUtils.generate(type: 'numeric')
CnpjUtils.is_valid('98765432000198')
CnpjUtils::DEFAULT (default instance)
CnpjUtils::DEFAULT is the pre-built, mutable singleton behind the class helpers (parity with the JS default export / Python cnpj_utils). Its configuration is process-wide and shared across threads: mutating it (e.g. DEFAULT.formatter = …) affects subsequent CnpjUtils.format / .generate / .is_valid calls for every caller in the process. Prefer CnpjUtils.new or per-call options for concurrent or isolated work; custom instances stay independent of DEFAULT:
CnpjUtils::DEFAULT.formatter = { slash_key: '|' }
CnpjUtils.format('01ABC234000X56') # => "01.ABC.234|000X-56"
custom = CnpjUtils.new
custom.format('01ABC234000X56') # => "01.ABC.234/000X-56" (unaffected)
Instance methods on DEFAULT (and any CnpjUtils instance):
#format(cnpj_input, options = nil, **keywords): Formats a CNPJ string or array of strings. Delegates to the internal formatter. Input must be 14 alphanumeric characters (after sanitization); otherwiseon_failis used.#generate(options = nil, **keywords): Generates a valid CNPJ. Delegates to the internal generator.#is_valid(cnpj_input, options = nil, **keywords): Returnstrueif the CNPJ is valid. Delegates to the internal validator.
CnpjUtils (class)
For custom default formatter, generator, or validator, create your own instance:
require 'cnpj-utilities'
utils = CnpjUtils.new(
formatter: { hidden: true, hidden_key: '#' },
generator: { type: 'numeric', format: true },
validator: { type: 'numeric', case_sensitive: false }
)
utils.format('RK0CMT3W000100') # => "RK.0CM.###/####-##"
utils.generate # => e.g. "73.008.535/0005-06"
utils.is_valid('98.765.432/0001-98') # => true
# Access or replace internal instances
utils.formatter # => CnpjFmt::CnpjFormatter
utils.generator # => CnpjGen::CnpjGenerator
utils.validator # => CnpjVal::CnpjValidator
CnpjUtils.new(settings = nil, **keywords): Optional settings. Pass either a settingsHashwith:formatter,:generator, and/or:validatorkeys, or the same keys as keyword arguments — not both (passing both raisesCnpjUtils::InvalidArgumentCombinationError). Each value may be a component instance, a*Optionsinstance (stored by reference — mutating it later affects subsequent calls with no per-call override), a plain optionsHash, or omitted/nilfor defaults.#format(cnpj_input, options = nil, **keywords): Same as the default instance; per-call options override the formatter’s defaults for that call only. Pass either an optionsHash/CnpjFmt::CnpjFormatterOptionsor keyword overrides — not both.#generate(options = nil, **keywords): Same as the default instance; per-call options override the generator’s defaults. Pass either an optionsHash/CnpjGen::CnpjGeneratorOptionsor keyword overrides — not both.#is_valid(cnpj_input, options = nil, **keywords): Same as the default instance; per-call options override the validator’s defaults. Pass either an optionsHash/CnpjVal::CnpjValidatorOptionsor keyword overrides — not both.#formatter,#generator,#validator: Accessors (getters and setters) for the internal components. Setters accept the same shapes as the constructor. To change a single option without replacing the instance, mutate the component’s options (e.g.utils.formatter.options.hidden = true).
Instance defaults and per-call overrides:
require 'cnpj-utilities'
utils = CnpjUtils.new(
formatter: { hidden: true, hidden_key: '#' },
generator: { format: true },
validator: { type: 'numeric' }
)
cnpj = '03603568000195'
utils.format(cnpj) # masked (instance formatter defaults)
utils.format(cnpj, hidden: false) # this call only: unmasked
utils.generate(format: false) # this call only: compact output
utils.is_valid('1QB5UKALPYFP59') # => false (instance validator is numeric-only)
utils.is_valid( # => true for this call
'1QB5UKALPYFP59',
type: 'alphanumeric'
)
Options can also be passed as a Hash (or options instance) on each method — without keyword overrides:
utils.format(cnpj, { slash_key: '|' })
utils.generate({ prefix: '12345', type: 'numeric' })
utils.is_valid('1QB5UKALPYFP59', { case_sensitive: false })
Using component classes and nested modules
Preferred paths after require 'cnpj-utilities':
require 'cnpj-utilities'
# Main classes at the façade root
formatter = CnpjUtils::CnpjFormatter.new(hidden: true)
generator = CnpjUtils::CnpjGenerator.new(type: 'numeric')
validator = CnpjUtils::CnpjValidator.new
formatter.format('AB123XYZ000123') # => "AB.123.***/****-**"
# Options, helpers, and errors under nested package modules
= CnpjUtils::CnpjFmt::CnpjFormatterOptions.new(slash_key: '|')
CnpjUtils::CnpjFmt.cnpj_fmt('03603568000195') # => "03.603.568/0001-95"
begin
CnpjUtils::CnpjFmt.cnpj_fmt(12_345)
rescue CnpjUtils::CnpjFmt::TypeMismatchError
# wrong input type
end
Root siblings remain supported (same objects as the nests):
CnpjFmt.cnpj_fmt('01ABC234000X56', slash_key: '|') # => "01.ABC.234|000X-56"
CnpjGen.cnpj_gen(type: 'numeric') # => e.g. "65453043000178"
CnpjVal.cnpj_val('9JN7MGLJZXIO50') # => true
CnpjFmt::CnpjFormatter.new(hidden: true)
See cnpj-fmt, cnpj-gen, and cnpj-val for full option and error details.
API
Exports
After require 'cnpj-utilities':
CnpjUtils: Façade class to create a utils instance with optional default formatter, generator, and validator settings.CnpjUtils.format/.generate/.is_valid: Class helpers that forward toCnpjUtils::DEFAULT.CnpjUtils::DEFAULT: Mutable pre-builtCnpjUtilsinstance (same object the class helpers use). Process-wide / shared across threads — preferCnpjUtils.newor per-call options under concurrency.CnpjUtils::VERSION: Gem version string.- Main-class shortcuts:
CnpjUtils::CnpjFormatter,CnpjUtils::CnpjGenerator,CnpjUtils::CnpjValidator(same objects as the sibling classes). - Nested package modules:
CnpjUtils::CnpjFmt,CnpjUtils::CnpjGen,CnpjUtils::CnpjVal— full sibling surface (Options, helpers, errors, types). Options/helpers/errors are not aliased at theCnpjUtilsroot. - Root sibling modules (still supported):
CnpjFmt,CnpjGen,CnpjVal— same objects as the nests.
Errors & Exceptions
CnpjUtils defines only API-misuse errors for this gem’s argument rules. Component errors are raised by the bundled packages and propagate unchanged.
Defined by cnpj-utilities
Errors defined by this gem are API misuse only (wrong type or invalid argument combination). Every custom error includes the CnpjUtils::Error marker module. This gem defines no CnpjUtils::DomainError and no domain leaves — domain failures come only from the bundled packages and keep those packages’ namespaces (CnpjFmt::…, CnpjGen::…, CnpjVal::…).
rescue CnpjUtils::Error catches only errors this gem raises. It does not catch component errors that propagate unchanged.
Summary
| Class | Inherits from | Category | Trigger condition |
|---|---|---|---|
CnpjUtils::InvalidArgumentCombinationError |
CnpjUtils::InvalidArgumentCombinationError < ArgumentError < StandardError (+ include CnpjUtils::Error) |
API misuse | Non-nil settings/options Hash (or options instance) passed together with any non-nil keyword argument |
CnpjUtils::TypeMismatchError |
CnpjUtils::TypeMismatchError < TypeError < StandardError (+ include CnpjUtils::Error) |
API misuse | Non-nil settings argument to CnpjUtils.new is not a Hash |
CnpjUtils::Error (marker module)
- Inheritance: module marker mixed into every custom error this gem raises 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 this gem raises.
- Example: N/A
- How to rescue it:
rescue CnpjUtils::Error
# TypeMismatchError, InvalidArgumentCombinationError from this gem only
# (not CnpjFmt::*, CnpjGen::*, or CnpjVal::* errors)
CnpjUtils::TypeMismatchError
- Inheritance:
CnpjUtils::TypeMismatchError < TypeError < StandardError(includesCnpjUtils::Error) - Category: API misuse — the caller passed a value of the wrong type.
- When it is raised: Raised when
CnpjUtils.newreceives a non-nilsettingsargument that is not aHash. - Example:
CnpjUtils.new('not-a-hash') # raises CnpjUtils::TypeMismatchError
CnpjUtils.new(false) # raises CnpjUtils::TypeMismatchError (false is non-nil)
- How to rescue it:
rescue CnpjUtils::TypeMismatchError
# this gem's type-contract violation
rescue TypeError
# native type errors, including this gem's TypeMismatchError
CnpjUtils::InvalidArgumentCombinationError
- Inheritance:
CnpjUtils::InvalidArgumentCombinationError < ArgumentError < StandardError(includesCnpjUtils::Error) - Category: API misuse — the caller mixed mutually exclusive argument patterns.
- When it is raised: Raised when
CnpjUtils.new,#format,#generate,#is_valid, or the class helpers receive both a non-nilsettings/optionsHash(or options instance) and any non-nilkeyword argument at the same time. - Example:
CnpjUtils.new({ formatter: { hidden: true } }, generator: { format: true })
# raises CnpjUtils::InvalidArgumentCombinationError
CnpjUtils.format('03603568000195', { hidden: true }, slash_key: '|')
# raises CnpjUtils::InvalidArgumentCombinationError
- How to rescue it:
rescue CnpjUtils::InvalidArgumentCombinationError
# this gem's invalid signature combination
rescue ArgumentError
# native argument errors, including this gem's InvalidArgumentCombinationError
Rescue granularity
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).
require 'cnpj-utilities'
# 1) Single native class — catches misuse errors of that kind,
# including non-library ones already handled elsewhere in the consumer's code.
begin
CnpjUtils.new('not-a-hash')
rescue TypeError
# CnpjUtils::TypeMismatchError and any other TypeError (library or not)
end
begin
CnpjUtils.new({ formatter: { hidden: true } }, generator: { format: true })
rescue ArgumentError
# CnpjUtils::InvalidArgumentCombinationError and any other ArgumentError (library or not)
end
require 'cnpj-utilities'
# 2) CnpjUtils::DomainError — not applicable: this gem defines no DomainError
# (and no domain leaves). Domain failures come from bundled packages only.
# begin
# CnpjUtils.new.format(12_345)
# rescue CnpjUtils::DomainError # NameError — constant is not defined
# end
require 'cnpj-utilities'
# 3) CnpjUtils::Error — catches everything this gem raises, regardless of native ancestry.
# Does not catch CnpjFmt::*, CnpjGen::*, or CnpjVal::* errors.
begin
CnpjUtils.new('not-a-hash')
rescue CnpjUtils::Error
# every custom error that includes CnpjUtils::Error
end
require 'cnpj-utilities'
# 4) Specific leaf class — catches only that exact failure mode.
begin
CnpjUtils.new('not-a-hash')
rescue CnpjUtils::TypeMismatchError
# only CnpjUtils::TypeMismatchError
end
Propagated from bundled packages
- Formatting (
CnpjFmt):CnpjFmt::TypeMismatchError,CnpjFmt::OutOfRangeError,CnpjFmt::ValidationError,CnpjFmt::InvalidLengthError(passed toon_fail, not raised by#format), and related classes. - Generation (
CnpjGen):CnpjGen::TypeMismatchError,CnpjGen::ValidationError, and related classes. - Validation (
CnpjVal):CnpjVal::TypeMismatchError,CnpjVal::ValidationError, and related classes.
Invalid option types are typically TypeError subclasses (*::TypeMismatchError); invalid option values are domain errors under each package’s DomainError hierarchy. Validation failure returns false; formatting length failure is handled by on_fail (default returns an empty string).
require 'cnpj-utilities'
begin
CnpjUtils.new.format(12_345)
rescue CnpjFmt::TypeMismatchError => e
puts e.
end
begin
CnpjUtils.new.is_valid(12_345_678_000_198)
rescue CnpjVal::TypeMismatchError => e
puts e.
end
# Custom on_fail for invalid length
custom_fail = ->(value, _exception) { "Invalid CNPJ: #{value}" }
CnpjFmt.cnpj_fmt('123', on_fail: custom_fail) # => "Invalid CNPJ: 123"
CnpjFmt.cnpj_fmt('123') # => "" (default on_fail)
Bundled packages
| Package | Main resources | README |
|---|---|---|
cnpj-fmt |
CnpjFmt::CnpjFormatter, CnpjFmt::CnpjFormatterOptions, CnpjFmt.cnpj_fmt |
docs |
cnpj-gen |
CnpjGen::CnpjGenerator, CnpjGen::CnpjGeneratorOptions, CnpjGen.cnpj_gen |
docs |
cnpj-val |
CnpjVal::CnpjValidator, CnpjVal::CnpjValidatorOptions, CnpjVal.cnpj_val |
docs |
All of the above are pulled in as dependencies of cnpj-utilities. For exhaustive option tables, exception lists, and edge-case behavior, see each package README.
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