cnpj-dv for Ruby
Gem Version Downloads Count Ruby Version Test Status Last Update Date Project License
π Full support for the new alphanumeric CNPJ format.
A Ruby utility to calculate check digits on CNPJ (Brazilian Business Tax ID).
Ruby Support
| Ruby 3.2 | Ruby 3.3 | Ruby 3.4 |
|---|---|---|
| Passing β | Passing β | Passing β |
Features
- β Alphanumeric CNPJ: Full support for the new alphanumeric CNPJ format (introduced in 2026)
- β
Flexible input: Accepts
StringorArrayof strings - β Format agnostic: Strips non-alphanumeric characters from string input and uppercases letters
- β Auto-expansion: Multi-character strings in arrays are joined and parsed like a single string
- β
Input validation: Rejects ineligible CNPJs (all-zero base ID
00000000, all-zero branch0000, or 12 numeric-only repeated digits) - β Lazy evaluation: Check digits are calculated only when accessed (via methods)
- β Caching: Calculated values are cached for subsequent access
- β
Minimal dependencies: Only
[lacus-utils](https://rubygems.org/gems/lacus-utils) - β
Error handling: API misuse vs domain errors with a
CnpjDV::Errormarker for library-wide rescue
Installation
Install the gem directly:
gem install cnpj-dv
Or add it to your Gemfile and run bundle install:
gem 'cnpj-dv'
Require
require 'cnpj-dv'
Quick Start
require 'cnpj-dv'
check_digits = CnpjDV::CnpjCheckDigits.new('914157320007')
check_digits.first # => '9'
check_digits.second # => '3'
check_digits.both # => '93'
check_digits.cnpj # => '91415732000793'
With alphanumeric CNPJ (new format):
require 'cnpj-dv'
check_digits = CnpjDV::CnpjCheckDigits.new('MGKGMJ9X0001')
check_digits.first # => '6'
check_digits.second # => '8'
check_digits.both # => '68'
check_digits.cnpj # => 'MGKGMJ9X000168'
Usage
The main resource of this package is the class CnpjDV::CnpjCheckDigits. Through an instance, you access CNPJ check-digit information:
initialize:CnpjDV::CnpjCheckDigits.new(cnpj_input)βcnpj_inputmust be aStringor anArrayof strings. After sanitization, the value must have 12β14 alphanumeric characters (formatting stripped from strings; letters uppercased). Only the first 12 characters are used as the base; if you pass 13 or 14 characters (e.g. a full CNPJ including prior check digits), characters 13β14 are ignored and the digits are recalculated. There are no options, keyword arguments, or configuration objects β the constructor takes only the CNPJ input.first: First check digit (13th character of the full CNPJ). Lazy, cached.second: Second check digit (14th character of the full CNPJ). Lazy, cached.both: Both check digits concatenated as a string.cnpj: The complete CNPJ as a string of 14 characters (12 base characters + 2 check digits).
Input formats
The CnpjCheckDigits class accepts multiple input formats:
String input: raw digits and/or letters, or formatted CNPJ (e.g. 91.415.732/0007-93, MG.KGM.J9X/0001-68). Non-alphanumeric characters are removed; lowercase letters are uppercased.
Array of strings: each element must be a string; values are concatenated and then parsed like a single string (e.g. ['9','1','4',β¦], ['9141','5732','0007'], ['MG','KGM','J9X','0001']). Non-string elements are not allowed.
require 'cnpj-dv'
# String β plain, formatted, or with existing check digits (only first 12 chars used)
CnpjDV::CnpjCheckDigits.new('914157320007')
CnpjDV::CnpjCheckDigits.new('91.415.732/0007')
CnpjDV::CnpjCheckDigits.new('91415732000793')
# Array of strings β single- or multi-character elements
CnpjDV::CnpjCheckDigits.new(%w[9 1 4 1 5 7 3 2 0 0 0 7])
CnpjDV::CnpjCheckDigits.new(%w[9141 5732 0007])
CnpjDV::CnpjCheckDigits.new(%w[MG KGM J9X 0001])
Error handling
Errors fall into two categories:
| Category | Meaning |
|---|---|
| API misuse | The caller invoked the library incorrectly (wrong type). Detectable from the call shape. |
| Domain error | The call was structurally correct, but a value violates a business rule (length, eligibility, format). |
Every custom error includes the CnpjDV::Error marker module. Domain failures (InvalidLengthError, ValidationError) inherit from CnpjDV::DomainError (RangeError).
Summary
| Class | Inherits from | Category | Trigger condition |
|---|---|---|---|
CnpjDV::TypeMismatchError |
TypeError (+ include Error) |
API misuse | Argument has the wrong data type |
CnpjDV::InvalidLengthError |
CnpjDV::DomainError |
Domain error | Sanitized length is not 12β14 |
CnpjDV::ValidationError |
CnpjDV::DomainError |
Domain error | Ineligible base/branch ID or repeated numeric digits |
CnpjDV::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.
- Example: N/A
- How to rescue it:
rescue CnpjDV::Error
# everything this library raises
CnpjDV::DomainError
- Inheritance:
CnpjDV::DomainError < RangeError(includesCnpjDV::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 CnpjDV::InvalidLengthErrorover raisingDomainErrordirectly. - How to rescue it:
rescue CnpjDV::DomainError
# InvalidLengthError, ValidationError, and any other DomainError subclass
CnpjDV::TypeMismatchError
- Inheritance:
CnpjDV::TypeMismatchError < TypeError(includesCnpjDV::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 (or an array contains a non-string element). - Example:
CnpjDV::CnpjCheckDigits.new(12_345_678_000_100) # raises CnpjDV::TypeMismatchError
- How to rescue it:
rescue CnpjDV::TypeMismatchError
# this library's type-contract violation
rescue TypeError
# native type errors, including this library's TypeMismatchError
CnpjDV::InvalidLengthError
- Inheritance:
CnpjDV::InvalidLengthError < CnpjDV::DomainError < RangeError(includesCnpjDV::Error) - Category: Domain error β a collection or string length violates a business rule.
- When it is raised: Raised when the sanitized CNPJ input does not contain 12 to 14 alphanumeric characters.
- Example:
CnpjDV::CnpjCheckDigits.new('12345678901') # raises CnpjDV::InvalidLengthError
- How to rescue it:
rescue CnpjDV::InvalidLengthError
# this exact length violation
rescue CnpjDV::DomainError
# RangeError-rooted domain failures from this library
CnpjDV::ValidationError
- Inheritance:
CnpjDV::ValidationError < CnpjDV::DomainError < RangeError(includesCnpjDV::Error) - Category: Domain error β a value fails a non-numeric, non-length domain rule.
- When it is raised: Raised when the base ID is
00000000, the branch ID is0000, or the first 12 characters are the same numeric digit. - Example:
CnpjDV::CnpjCheckDigits.new('000000000001') # raises CnpjDV::ValidationError
- How to rescue it:
rescue CnpjDV::ValidationError
# this exact domain validation failure
rescue CnpjDV::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
# CnpjDV::TypeMismatchError and any other TypeError (library or not)
# 2) CnpjDV::DomainError β catches business-rule violations under DomainError.
rescue CnpjDV::DomainError
# CnpjDV::InvalidLengthError, CnpjDV::ValidationError, and other DomainError subclasses
# 3) CnpjDV::Error β catches everything the library raises.
rescue CnpjDV::Error
# every custom error that includes CnpjDV::Error
# 4) Specific leaf class β catches only that exact failure mode.
rescue CnpjDV::InvalidLengthError
# only CnpjDV::InvalidLengthError
Notable attributes on raised errors:
TypeMismatchError:actual_input,actual_type,expected_typeInvalidLengthError:actual_input,evaluated_input,min_expected_length,max_expected_lengthValidationError:actual_input,reason
Other available resources
After require 'cnpj-dv':
CnpjDV::CNPJ_MIN_LENGTH:12CnpjDV::CNPJ_MAX_LENGTH:14- Errors: see above (
CnpjDV::Error,DomainError, and raised leaves)
Calculation algorithm
The package computes check digits with the official Brazilian modulo-11 rules extended to alphanumeric characters:
- Character value: each character contributes
ord(character) β 48(so0β9stay 0β9; letters use their ASCII offset from0). - Weights: from right to left, multiply by weights that cycle 2, 3, 4, 5, 6, 7, 8, 9, then repeat from 2.
- First check digit (13th position): apply steps 1β2 to the first 12 base characters; let
r = sum % 11. The digit is0ifr < 2, otherwise11 β r. - Second check digit (14th position): apply steps 1β2 to the first 12 characters plus the first check digit; same formula for
r.
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