From 0dcb592a53545538ea39362d73525a26cfb148ba Mon Sep 17 00:00:00 2001 From: juliolmuller Date: Mon, 10 Aug 2026 17:29:19 -0300 Subject: [PATCH 01/14] chore(br-utils): set package metadata Co-authored-by: Claude Opus 5 Co-authored-by: Cursor Agent --- packages/br-utilities/Gemfile | 3 --- packages/br-utilities/br-utilities.gemspec | 9 +++++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/br-utilities/Gemfile b/packages/br-utilities/Gemfile index 2d266a0..c6122eb 100644 --- a/packages/br-utilities/Gemfile +++ b/packages/br-utilities/Gemfile @@ -4,9 +4,6 @@ source 'https://rubygems.org' gemspec -gem 'cnpj-utilities', path: '../cnpj-utilities' -gem 'cpf-utilities', path: '../cpf-utilities' - group :test do gem 'rake', '~> 13.2' gem 'rspec', '~> 3.13' diff --git a/packages/br-utilities/br-utilities.gemspec b/packages/br-utilities/br-utilities.gemspec index addc19d..60438d3 100644 --- a/packages/br-utilities/br-utilities.gemspec +++ b/packages/br-utilities/br-utilities.gemspec @@ -6,8 +6,9 @@ Gem::Specification.new do |spec| spec.name = 'br-utilities' spec.version = BrUtils::VERSION spec.authors = ['Julio L. Muller'] - spec.summary = 'Brazilian data utilities: CPF, CNPJ, and more' - spec.description = 'Unified API for CPF/CNPJ format, generate, validate (Brazilian IDs).' + spec.email = ['juliolmuller@outlook.com'] + spec.summary = 'Utilities to deal with Brazilian-related data' + spec.description = 'Utilities to deal with Brazilian-related data' spec.homepage = 'https://github.com/LacusSolutions/br-utils-ruby' spec.license = 'MIT' spec.required_ruby_version = '>= 3.1' @@ -15,6 +16,6 @@ Gem::Specification.new do |spec| spec.metadata['rubygems_mfa_required'] = 'true' spec.files = Dir['src/**/*'] + ['LICENSE', 'README.md', 'README.pt.md', 'CHANGELOG.md'] spec.require_paths = ['src'] - spec.add_dependency 'cnpj-utilities', '>= 0' - spec.add_dependency 'cpf-utilities', '>= 0' + spec.add_dependency 'cnpj-utilities', '>= 1.0.0', '< 1.1.0' + spec.add_dependency 'cpf-utilities', '>= 1.0.0', '< 1.1.0' end From f1b90d8452e85292f1daddfc2871a0e32e149419 Mon Sep 17 00:00:00 2001 From: juliolmuller Date: Mon, 10 Aug 2026 17:38:28 -0300 Subject: [PATCH 02/14] feat(br-utils): introduce unified API for BRF utilities Co-authored-by: Claude Opus 5 Co-authored-by: Cursor Agent --- packages/br-utilities/src/br-utilities.rb | 36 ++- .../br-utilities/src/br-utilities/br_utils.rb | 268 ++++++++++++++++++ .../br-utilities/src/br-utilities/cnpj_fmt.rb | 18 ++ .../br-utilities/src/br-utilities/cnpj_gen.rb | 18 ++ .../src/br-utilities/cnpj_utils.rb | 8 + .../br-utilities/src/br-utilities/cnpj_val.rb | 18 ++ .../br-utilities/src/br-utilities/cpf_fmt.rb | 18 ++ .../br-utilities/src/br-utilities/cpf_gen.rb | 18 ++ .../src/br-utilities/cpf_utils.rb | 8 + .../br-utilities/src/br-utilities/cpf_val.rb | 14 + .../br-utilities/src/br-utilities/errors.rb | 23 ++ .../br-utilities/src/br-utilities/version.rb | 6 + 12 files changed, 449 insertions(+), 4 deletions(-) create mode 100644 packages/br-utilities/src/br-utilities/br_utils.rb create mode 100644 packages/br-utilities/src/br-utilities/cnpj_fmt.rb create mode 100644 packages/br-utilities/src/br-utilities/cnpj_gen.rb create mode 100644 packages/br-utilities/src/br-utilities/cnpj_utils.rb create mode 100644 packages/br-utilities/src/br-utilities/cnpj_val.rb create mode 100644 packages/br-utilities/src/br-utilities/cpf_fmt.rb create mode 100644 packages/br-utilities/src/br-utilities/cpf_gen.rb create mode 100644 packages/br-utilities/src/br-utilities/cpf_utils.rb create mode 100644 packages/br-utilities/src/br-utilities/cpf_val.rb create mode 100644 packages/br-utilities/src/br-utilities/errors.rb diff --git a/packages/br-utilities/src/br-utilities.rb b/packages/br-utilities/src/br-utilities.rb index ac51953..ef0846e 100644 --- a/packages/br-utilities/src/br-utilities.rb +++ b/packages/br-utilities/src/br-utilities.rb @@ -4,8 +4,36 @@ require 'cnpj-utilities' require_relative 'br-utilities/version' -module BrUtils - def self.hello - 'br-utilities' - end +# Entry point for the +br-utilities+ gem. +# +# Loads sibling packages (+cpf-utilities+, +cnpj-utilities+) and defines the +# {BrUtils} façade class. +version.rb+ defines a placeholder module so the +# gemspec can read {BrUtils::VERSION}; this file promotes it to the class +# consumers instantiate. +# +# Two-tier access after +require 'br-utilities'+: +# +# - *Main shortcuts* at the façade root: {BrUtils::CpfFormatter}, +# {BrUtils::CnpjFormatter}, etc. +# - *Package nests* for the full sibling surface (Options, helpers, errors, +# types): {BrUtils::CpfFmt}, {BrUtils::CnpjUtils}, etc. (same objects as +# +::CpfFmt+, +::CnpjUtils+, …). +# - Root siblings (+CpfUtils+, +CnpjUtils+, +CpfFmt+, …) remain supported +# unchanged. +unless BrUtils.is_a?(Class) + version = BrUtils::VERSION + Object.send(:remove_const, :BrUtils) + BrUtils = Class.new + BrUtils.const_set(:VERSION, version) end + +require_relative 'br-utilities/errors' +require_relative 'br-utilities/br_utils' +require_relative 'br-utilities/cpf_fmt' +require_relative 'br-utilities/cpf_gen' +require_relative 'br-utilities/cpf_val' +require_relative 'br-utilities/cpf_utils' +require_relative 'br-utilities/cnpj_fmt' +require_relative 'br-utilities/cnpj_gen' +require_relative 'br-utilities/cnpj_val' +require_relative 'br-utilities/cnpj_utils' diff --git a/packages/br-utilities/src/br-utilities/br_utils.rb b/packages/br-utilities/src/br-utilities/br_utils.rb new file mode 100644 index 0000000..4680a7e --- /dev/null +++ b/packages/br-utilities/src/br-utilities/br_utils.rb @@ -0,0 +1,268 @@ +# frozen_string_literal: true + +require 'cpf-utilities' +require 'cnpj-utilities' + +require_relative 'errors' + +# Unified API for Brazilian-related data, like CPF (Cadastro de Pessoa Física) +# and CNPJ (Cadastro Nacional da Pessoa Jurídica). Provides a unified interface +# for formatting, generating, and validating data. Aggregates configurable +# {CpfUtils} and {CnpjUtils} instances behind a single façade. +# +# Public API: +# +# - {BrUtils.cpf}, {BrUtils.cnpj} — class helpers that alias {BrUtils::DEFAULT} +# (preferred quick path for domain accessors) +# - {BrUtils::DEFAULT} — mutable process-wide singleton (JS/Python parity; not +# thread-isolated — prefer {.new} under concurrency) +# - {BrUtils#cpf}, {BrUtils#cnpj} — instance accessors with setters +# - {BrUtils::VERSION} +# - {BrUtils::InvalidArgumentCombinationError}, {BrUtils::TypeMismatchError} +# +# Two-tier access: main-class shortcuts ({BrUtils::CpfFormatter}, etc.) and +# nested package modules ({BrUtils::CpfFmt}, {BrUtils::CpfUtils}, etc.). Root +# siblings ({CpfUtils}, {CnpjUtils}, {CpfFmt}, …) remain loadable after +# +require 'br-utilities'+. +# +# Mutating {BrUtils::DEFAULT} (e.g. via setters) affects subsequent class-helper +# calls process-wide (shared across threads). Prefer {BrUtils.new} for concurrent +# or isolated work. Custom instances are independent of +DEFAULT+. +# +# @example +# require 'br-utilities' +# +# BrUtils.cpf.format('12345678909') # => "123.456.789-09" +# BrUtils.cnpj.is_valid('91415732000793') # => true +class BrUtils + SETTINGS_KEYS = %i[cpf cnpj].freeze + FLAT_KEYS = %i[cpf_formatter cpf_generator cnpj_formatter cnpj_generator cnpj_validator].freeze + KEYWORD_KEYS = (SETTINGS_KEYS + FLAT_KEYS).freeze + + private_constant :SETTINGS_KEYS, :FLAT_KEYS, :KEYWORD_KEYS + + # Internal helpers for resolving domain utils from settings / keyword arguments. + module Helpers + module_function + + def resolve_settings(settings, keywords) + keyword_settings = compact_keywords(keywords) + raise_ambiguous_settings! if !settings.nil? && !keyword_settings.empty? + return normalize_settings(settings) unless settings.nil? + + keyword_settings + end + + def normalize_settings(settings) + raise TypeMismatchError, "BrUtils settings must be a Hash. Got #{settings.class}." unless settings.is_a?(Hash) + + SETTINGS_KEYS.each_with_object({}) do |key, resolved| + if settings.key?(key) + resolved[key] = settings[key] + elsif settings.key?(key.to_s) + resolved[key] = settings[key.to_s] + end + end + end + + def compact_keywords(keywords) + KEYWORD_KEYS.each_with_object({}) do |key, resolved| + value = keywords[key] + resolved[key] = value unless value.nil? + end + end + + def resolve_cpf_utils(resolved) + return resolve_utils(CpfUtils, resolved[:cpf]) if resolved.key?(:cpf) + return CpfUtils.new unless cpf_flat?(resolved) + + CpfUtils.new(formatter: resolved[:cpf_formatter], generator: resolved[:cpf_generator]) + end + + def cpf_flat?(resolved) + resolved.key?(:cpf_formatter) || resolved.key?(:cpf_generator) + end + + def resolve_cnpj_utils(resolved) + return resolve_utils(CnpjUtils, resolved[:cnpj]) if resolved.key?(:cnpj) + return CnpjUtils.new unless cnpj_flat?(resolved) + + CnpjUtils.new( + formatter: resolved[:cnpj_formatter], + generator: resolved[:cnpj_generator], + validator: resolved[:cnpj_validator] + ) + end + + def cnpj_flat?(resolved) + resolved.key?(:cnpj_formatter) || resolved.key?(:cnpj_generator) || resolved.key?(:cnpj_validator) + end + + def resolve_utils(utils_cls, value) + return utils_cls.new if value.nil? + return value if value.is_a?(utils_cls) + return utils_cls.new(value) if value.is_a?(Hash) + + # Duck-typed / test doubles: use the given object by reference (Python parity). + value + end + + def raise_ambiguous_settings! + option_keywords = KEYWORD_KEYS.map { |key| "#{key}:" }.join(', ') + + raise InvalidArgumentCombinationError, + 'Pass either a settings Hash to `settings`, or keyword arguments ' \ + "(#{option_keywords}), not both." + end + end + private_constant :Helpers + + # Creates a new {BrUtils} instance with customized options. All options are + # optional. If any option is omitted, it falls back to its default value. + # + # Each of +:cpf+ and +:cnpj+ accepts either a pre-built utils instance or a + # configuration {Hash} spread into the corresponding {CpfUtils} / {CnpjUtils} + # constructor. Within that Hash, each resource key (+formatter+, +generator+, + # and +validator+ for CNPJ) accepts either an options object or a mapping of + # option values. + # + # Flat +:cpf_formatter+ / +:cpf_generator+ and +:cnpj_formatter+ / + # +:cnpj_generator+ / +:cnpj_validator+ arguments are supported as a + # convenience when only individual components need customization. They are + # ignored when the corresponding +:cpf+ or +:cnpj+ argument is provided. + # + # +settings+ and the keyword arguments are never merged with each other: when + # +settings+ is given (a {Hash} with +:cpf+ and/or +:cnpj+ keys), it alone + # determines the domains; otherwise, the domains are built exclusively from the + # keyword arguments. Passing +settings+ together with any non-+nil+ keyword + # argument raises {InvalidArgumentCombinationError} instead of silently + # ignoring the keywords. + # + # @param settings [Hash, nil] settings Hash with +:cpf+ and/or +:cnpj+ keys + # (each a utils instance, settings Hash, or +nil+) + # @param keywords [Hash] +:cpf+, +:cnpj+, and/or flat component kwargs + # (mutually exclusive with +settings+) + # @raise [InvalidArgumentCombinationError] if +settings+ and a keyword argument + # are both given + # @raise [TypeMismatchError] if +settings+ is given and is not a +Hash+ + # @raise [CnpjFmt::TypeMismatchError] if CNPJ formatter options have an invalid + # type + # @raise [CnpjFmt::OutOfRangeError] if CNPJ formatter +hidden_start+ or + # +hidden_end+ are out of valid range + # @raise [CnpjFmt::ValidationError] if any CNPJ formatter key option contains a + # disallowed character + # @raise [CnpjGen::TypeMismatchError] if CNPJ generator options have an invalid + # type + # @raise [CnpjGen::ValidationError] if CNPJ generator +prefix+ is invalid or + # +type+ is not allowed + # @raise [CnpjVal::TypeMismatchError] if CNPJ validator options have an invalid + # type + # @raise [CnpjVal::ValidationError] if CNPJ validator +type+ is not allowed + # @raise [CpfFmt::TypeMismatchError] if CPF formatter options have an invalid + # type + # @raise [CpfFmt::OutOfRangeError] if CPF formatter +hidden_start+ or + # +hidden_end+ are out of valid range + # @raise [CpfFmt::ValidationError] if any CPF formatter key option contains a + # disallowed character + # @raise [CpfGen::TypeMismatchError] if CPF generator options have an invalid + # type + # @raise [CpfGen::ValidationError] if CPF generator +prefix+ is invalid + def initialize(settings = nil, **keywords) + resolved = Helpers.resolve_settings(settings, keywords) + + @cpf = Helpers.resolve_cpf_utils(resolved) + @cnpj = Helpers.resolve_cnpj_utils(resolved) + end + + # Access the CPF utilities instance. + # + # @return [CpfUtils] + attr_reader :cpf + + # Access the CNPJ utilities instance. + # + # @return [CnpjUtils] + attr_reader :cnpj + + # Sets the active CPF utilities instance. + # + # It is flexible and can handle any of these inputs: + # + # 1. A complete new instance of {CpfUtils} + # 2. A {Hash} of {CpfUtils} component settings + # 3. A partial {Hash} with options for the CPF utilities + # 4. +nil+ creates a brand new instance of {CpfUtils} with the default options + # + # Note that this resets the CPF utilities instance completely. Any previous + # options will be overridden. To alter only a single option or a few options + # of the existing instance, access it directly and use the CPF utilities' + # setters and methods (e.g. +utils.cpf.formatter.options.hidden = true+). + # + # @param value [CpfUtils, Hash, nil] + # @raise [CpfFmt::TypeMismatchError] if formatter options have an invalid type + # @raise [CpfFmt::OutOfRangeError] if formatter +hidden_start+ or +hidden_end+ + # are out of valid range + # @raise [CpfFmt::ValidationError] if any formatter key option contains a + # disallowed character + # @raise [CpfGen::TypeMismatchError] if generator options have an invalid type + # @raise [CpfGen::ValidationError] if generator +prefix+ is invalid + def cpf=(value) + @cpf = Helpers.resolve_utils(CpfUtils, value) + end + + # Sets the active CNPJ utilities instance. + # + # It is flexible and can handle any of these inputs: + # + # 1. A complete new instance of {CnpjUtils} + # 2. A {Hash} of {CnpjUtils} component settings + # 3. A partial {Hash} with options for the CNPJ utilities + # 4. +nil+ creates a brand new instance of {CnpjUtils} with the default options + # + # Note that this resets the CNPJ utilities instance completely. Any previous + # options will be overridden. To alter only a single option or a few options + # of the existing instance, access it directly and use the CNPJ utilities' + # setters and methods (e.g. +utils.cnpj.generator.options.type = 'numeric'+). + # + # @param value [CnpjUtils, Hash, nil] + # @raise [CnpjFmt::TypeMismatchError] if formatter options have an invalid type + # @raise [CnpjFmt::OutOfRangeError] if formatter +hidden_start+ or +hidden_end+ + # are out of valid range + # @raise [CnpjFmt::ValidationError] if any formatter key option contains a + # disallowed character + # @raise [CnpjGen::TypeMismatchError] if generator options have an invalid type + # @raise [CnpjGen::ValidationError] if generator +prefix+ is invalid or +type+ + # is not allowed + # @raise [CnpjVal::TypeMismatchError] if validator options have an invalid type + # @raise [CnpjVal::ValidationError] if validator +type+ is not allowed + def cnpj=(value) + @cnpj = Helpers.resolve_utils(CnpjUtils, value) + end + + # Default {BrUtils} instance with default CPF and CNPJ utilities (parity with + # the JS default export / Python +br_utils+ singleton). Configuration is + # process-wide and shared across threads: mutating this instance (e.g. via + # setters) affects subsequent {BrUtils.cpf} and {BrUtils.cnpj} calls for every + # caller in the process. Prefer {BrUtils.new} for threaded or isolated work. + DEFAULT = new + + class << self + # Access the CPF utilities from {DEFAULT} (alias of {BrUtils#cpf} on that + # instance). + # + # @return [CpfUtils] + # @see BrUtils#cpf + def cpf + DEFAULT.cpf + end + + # Access the CNPJ utilities from {DEFAULT} (alias of {BrUtils#cnpj} on that + # instance). + # + # @return [CnpjUtils] + # @see BrUtils#cnpj + def cnpj + DEFAULT.cnpj + end + end +end diff --git a/packages/br-utilities/src/br-utilities/cnpj_fmt.rb b/packages/br-utilities/src/br-utilities/cnpj_fmt.rb new file mode 100644 index 0000000..a050fec --- /dev/null +++ b/packages/br-utilities/src/br-utilities/cnpj_fmt.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class BrUtils + # CNPJ formatting utilities re-exported from +cnpj-fmt+ (via +cnpj-utilities+). + # + # Nested package module — same object as +::CnpjFmt+ (Options, helpers, errors, + # types). + CnpjFmt = ::CnpjFmt + + # Main-class shortcut for {CnpjFmt::CnpjFormatter}. + CnpjFormatter = CnpjFmt::CnpjFormatter + + # Main-class shortcut for {CnpjFmt::CnpjFormatterOptions}. + CnpjFormatterOptions = CnpjFmt::CnpjFormatterOptions + + # Main-class shortcut for {CnpjFmt::Error}. + CnpjFormatterError = CnpjFmt::Error +end diff --git a/packages/br-utilities/src/br-utilities/cnpj_gen.rb b/packages/br-utilities/src/br-utilities/cnpj_gen.rb new file mode 100644 index 0000000..94c374e --- /dev/null +++ b/packages/br-utilities/src/br-utilities/cnpj_gen.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class BrUtils + # CNPJ generation utilities re-exported from +cnpj-gen+ (via +cnpj-utilities+). + # + # Nested package module — same object as +::CnpjGen+ (Options, helpers, errors, + # types). + CnpjGen = ::CnpjGen + + # Main-class shortcut for {CnpjGen::CnpjGenerator}. + CnpjGenerator = CnpjGen::CnpjGenerator + + # Main-class shortcut for {CnpjGen::CnpjGeneratorOptions}. + CnpjGeneratorOptions = CnpjGen::CnpjGeneratorOptions + + # Main-class shortcut for {CnpjGen::Error}. + CnpjGeneratorError = CnpjGen::Error +end diff --git a/packages/br-utilities/src/br-utilities/cnpj_utils.rb b/packages/br-utilities/src/br-utilities/cnpj_utils.rb new file mode 100644 index 0000000..369f195 --- /dev/null +++ b/packages/br-utilities/src/br-utilities/cnpj_utils.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +class BrUtils + # CNPJ utilities re-exported from +cnpj-utilities+. + # + # Nested package class — same object as +::CnpjUtils+. + CnpjUtils = ::CnpjUtils +end diff --git a/packages/br-utilities/src/br-utilities/cnpj_val.rb b/packages/br-utilities/src/br-utilities/cnpj_val.rb new file mode 100644 index 0000000..68713e3 --- /dev/null +++ b/packages/br-utilities/src/br-utilities/cnpj_val.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class BrUtils + # CNPJ validation utilities re-exported from +cnpj-val+ (via +cnpj-utilities+). + # + # Nested package module — same object as +::CnpjVal+ (Options, helpers, errors, + # types). + CnpjVal = ::CnpjVal + + # Main-class shortcut for {CnpjVal::CnpjValidator}. + CnpjValidator = CnpjVal::CnpjValidator + + # Main-class shortcut for {CnpjVal::CnpjValidatorOptions}. + CnpjValidatorOptions = CnpjVal::CnpjValidatorOptions + + # Main-class shortcut for {CnpjVal::Error}. + CnpjValidatorError = CnpjVal::Error +end diff --git a/packages/br-utilities/src/br-utilities/cpf_fmt.rb b/packages/br-utilities/src/br-utilities/cpf_fmt.rb new file mode 100644 index 0000000..b1588db --- /dev/null +++ b/packages/br-utilities/src/br-utilities/cpf_fmt.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class BrUtils + # CPF formatting utilities re-exported from +cpf-fmt+ (via +cpf-utilities+). + # + # Nested package module — same object as +::CpfFmt+ (Options, helpers, errors, + # types). + CpfFmt = ::CpfFmt + + # Main-class shortcut for {CpfFmt::CpfFormatter}. + CpfFormatter = CpfFmt::CpfFormatter + + # Main-class shortcut for {CpfFmt::CpfFormatterOptions}. + CpfFormatterOptions = CpfFmt::CpfFormatterOptions + + # Main-class shortcut for {CpfFmt::Error}. + CpfFormatterError = CpfFmt::Error +end diff --git a/packages/br-utilities/src/br-utilities/cpf_gen.rb b/packages/br-utilities/src/br-utilities/cpf_gen.rb new file mode 100644 index 0000000..fbfe4b5 --- /dev/null +++ b/packages/br-utilities/src/br-utilities/cpf_gen.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class BrUtils + # CPF generation utilities re-exported from +cpf-gen+ (via +cpf-utilities+). + # + # Nested package module — same object as +::CpfGen+ (Options, helpers, errors, + # types). + CpfGen = ::CpfGen + + # Main-class shortcut for {CpfGen::CpfGenerator}. + CpfGenerator = CpfGen::CpfGenerator + + # Main-class shortcut for {CpfGen::CpfGeneratorOptions}. + CpfGeneratorOptions = CpfGen::CpfGeneratorOptions + + # Main-class shortcut for {CpfGen::Error}. + CpfGeneratorError = CpfGen::Error +end diff --git a/packages/br-utilities/src/br-utilities/cpf_utils.rb b/packages/br-utilities/src/br-utilities/cpf_utils.rb new file mode 100644 index 0000000..d86704c --- /dev/null +++ b/packages/br-utilities/src/br-utilities/cpf_utils.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +class BrUtils + # CPF utilities re-exported from +cpf-utilities+. + # + # Nested package class — same object as +::CpfUtils+. + CpfUtils = ::CpfUtils +end diff --git a/packages/br-utilities/src/br-utilities/cpf_val.rb b/packages/br-utilities/src/br-utilities/cpf_val.rb new file mode 100644 index 0000000..e27d3ed --- /dev/null +++ b/packages/br-utilities/src/br-utilities/cpf_val.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class BrUtils + # CPF validation utilities re-exported from +cpf-val+ (via +cpf-utilities+). + # + # Nested package module — same object as +::CpfVal+ (helpers, errors, types). + CpfVal = ::CpfVal + + # Main-class shortcut for {CpfVal::CpfValidator}. + CpfValidator = CpfVal::CpfValidator + + # Main-class shortcut for {CpfVal::Error}. + CpfValidatorError = CpfVal::Error +end diff --git a/packages/br-utilities/src/br-utilities/errors.rb b/packages/br-utilities/src/br-utilities/errors.rb new file mode 100644 index 0000000..6619be7 --- /dev/null +++ b/packages/br-utilities/src/br-utilities/errors.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +class BrUtils + # Marker module mixed into every custom error raised by this library. + # + # Use +rescue BrUtils::Error+ to catch every library error regardless of + # native ancestry. Domain packages raise their own error hierarchies; + # this gem only defines the misuse errors it raises itself. + module Error; end + + # API misuse error raised when an argument's runtime type does not match the + # type required by the API contract (for example, a non-Hash +settings+ value). + class TypeMismatchError < TypeError + include Error + end + + # API misuse error raised when the combination of provided arguments does not + # match any valid overload-style signature (for example, a settings Hash + # together with keyword overrides). + class InvalidArgumentCombinationError < ArgumentError + include Error + end +end diff --git a/packages/br-utilities/src/br-utilities/version.rb b/packages/br-utilities/src/br-utilities/version.rb index b8ff3f8..59b60db 100644 --- a/packages/br-utilities/src/br-utilities/version.rb +++ b/packages/br-utilities/src/br-utilities/version.rb @@ -1,5 +1,11 @@ # frozen_string_literal: true +# Placeholder module so the gemspec (and any early require of this file) can read +# {BrUtils::VERSION}. The gem entry point promotes +BrUtils+ to a class and +# reopens it for the façade implementation. module BrUtils + # Gem version string. Placeholder replaced at build/publish time. + # + # @return [String] VERSION = '0.0.0' end From fd11e9c512515f0da6859ded786f3220d9e9c7a3 Mon Sep 17 00:00:00 2001 From: juliolmuller Date: Mon, 10 Aug 2026 17:38:38 -0300 Subject: [PATCH 03/14] test(br-utils): create tests suite Co-authored-by: Claude Opus 5 Co-authored-by: Cursor Agent --- .../br-utilities/tests/br_utilities.spec.rb | 11 - packages/br-utilities/tests/br_utils.spec.rb | 1054 +++++++++++++++++ 2 files changed, 1054 insertions(+), 11 deletions(-) delete mode 100644 packages/br-utilities/tests/br_utilities.spec.rb create mode 100644 packages/br-utilities/tests/br_utils.spec.rb diff --git a/packages/br-utilities/tests/br_utilities.spec.rb b/packages/br-utilities/tests/br_utilities.spec.rb deleted file mode 100644 index c334532..0000000 --- a/packages/br-utilities/tests/br_utilities.spec.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe BrUtils do - describe '.hello' do - it 'returns br-utilities' do - expect(BrUtils.hello).to eq('br-utilities') - end - end -end diff --git a/packages/br-utilities/tests/br_utils.spec.rb b/packages/br-utilities/tests/br_utils.spec.rb new file mode 100644 index 0000000..6a02322 --- /dev/null +++ b/packages/br-utilities/tests/br_utils.spec.rb @@ -0,0 +1,1054 @@ +# frozen_string_literal: true + +require 'spec_helper' + +# Combined behavioural suite for BrUtils (JS / PHP / Python reference tests). +# +# Dropped cases (not meaningful in Ruby): +# - js/packages/br-utils/tests/output.spec.ts — UMD/CJS/ESM bundles, .d.ts wiring, +# global variable attachment, and export-string parsing (JS packaging only). +# - JavaScript `undefined` nullish values — Ruby uses `nil` only. +# - PHP getCpfUtils() / getCnpjUtils() accessor names — Ruby uses #cpf / #cnpj +# (JS/Python parity per AGENTS.md). +# - PHP read-only cpf/cnpj (no setters) — Ruby implements setters like JS/Python. +# - PHP CnpjType / CnpjValidationType enums — Ruby uses string type values. +# - PHP legacy CPF v1 InvalidArgumentException for formatter/generator options — +# Ruby mirrors JS/Python v2 structured errors (CpfFmt::*/CpfGen::*). +# - PHP phpunit/Cpf/* legacy CPF v1 component suites — Ruby aligns with +# cpf-utilities v2, not PHP CPF v1. +# - Python __slots__ / dynamic-attribute restriction — optional in Ruby; AGENTS.md +# does not require freezing or slot-like attribute locking. +# - BrUtils Options class fold/#set/nil-setter contracts — BrUtils has no Options +# class (settings are a Hash or domain utils instances; Options live on +# cpf-fmt / cnpj-fmt / … siblings). +# - BrUtils.format / .generate / .is_valid class helpers — façade owns wiring only +# (cpf/cnpj); domain operations live on CpfUtils / CnpjUtils (and their DEFAULT). + +def compact_options(**kwargs) + kwargs.compact +end + +def expect_options_containing(actual, expected) + expected.each do |key, value| + expect(actual[key]).to eq(value) + end +end + +CPF_FORMAT_FACTORIES = { + constructor_hash: lambda { |cpf, dot_key = nil, dash_key = nil| + utils = BrUtils.new(cpf: { formatter: compact_options(dot_key: dot_key, dash_key: dash_key) }) + utils.cpf.format(cpf) + }, + method_keywords: lambda { |cpf, dot_key = nil, dash_key = nil| + BrUtils.new.cpf.format(cpf, dot_key: dot_key, dash_key: dash_key) + } +}.freeze + +CPF_GENERATE_FACTORIES = { + constructor_hash: lambda { |format: nil, prefix: nil| + utils = BrUtils.new(cpf: { generator: compact_options(format: format, prefix: prefix) }) + utils.cpf.generate + }, + method_keywords: lambda { |format: nil, prefix: nil| + BrUtils.new.cpf.generate(format: format, prefix: prefix) + } +}.freeze + +CNPJ_FORMAT_FACTORIES = { + constructor_hash: lambda { |cnpj, slash_key = nil| + utils = BrUtils.new(cnpj: { formatter: compact_options(slash_key: slash_key) }) + utils.cnpj.format(cnpj) + }, + constructor_options: lambda { |cnpj, slash_key = nil| + options = CnpjFmt::CnpjFormatterOptions.new(compact_options(slash_key: slash_key)) + utils = BrUtils.new(cnpj: { formatter: options }) + utils.cnpj.format(cnpj) + }, + method_keywords: lambda { |cnpj, slash_key = nil| + BrUtils.new.cnpj.format(cnpj, slash_key: slash_key) + }, + method_options: lambda { |cnpj, slash_key = nil| + options = CnpjFmt::CnpjFormatterOptions.new(compact_options(slash_key: slash_key)) + BrUtils.new.cnpj.format(cnpj, options) + } +}.freeze + +CNPJ_GENERATE_FACTORIES = { + constructor_hash: lambda { |format: nil, prefix: nil, type: nil| + utils = BrUtils.new( + cnpj: { generator: compact_options(format: format, prefix: prefix, type: type) } + ) + utils.cnpj.generate + }, + constructor_options: lambda { |format: nil, prefix: nil, type: nil| + options = CnpjGen::CnpjGeneratorOptions.new( + compact_options(format: format, prefix: prefix, type: type) + ) + utils = BrUtils.new(cnpj: { generator: options }) + utils.cnpj.generate + }, + method_keywords: lambda { |format: nil, prefix: nil, type: nil| + BrUtils.new.cnpj.generate(format: format, prefix: prefix, type: type) + }, + method_options: lambda { |format: nil, prefix: nil, type: nil| + options = CnpjGen::CnpjGeneratorOptions.new( + compact_options(format: format, prefix: prefix, type: type) + ) + BrUtils.new.cnpj.generate(options) + } +}.freeze + +CNPJ_IS_VALID_FACTORIES = { + constructor_hash: lambda { |cnpj, type: nil, case_sensitive: nil| + utils = BrUtils.new( + cnpj: { validator: compact_options(type: type, case_sensitive: case_sensitive) } + ) + utils.cnpj.is_valid(cnpj) + }, + constructor_options: lambda { |cnpj, type: nil, case_sensitive: nil| + options = CnpjVal::CnpjValidatorOptions.new( + compact_options(type: type, case_sensitive: case_sensitive) + ) + utils = BrUtils.new(cnpj: { validator: options }) + utils.cnpj.is_valid(cnpj) + }, + method_keywords: lambda { |cnpj, type: nil, case_sensitive: nil| + BrUtils.new.cnpj.is_valid(cnpj, type: type, case_sensitive: case_sensitive) + }, + method_options: lambda { |cnpj, type: nil, case_sensitive: nil| + options = CnpjVal::CnpjValidatorOptions.new( + compact_options(type: type, case_sensitive: case_sensitive) + ) + BrUtils.new.cnpj.is_valid(cnpj, options) + } +}.freeze + +CPF_FORMAT_FACTORY_CONTEXTS = [ + ['when options are passed to the constructor as a Hash', :constructor_hash], + ['when options are passed to #format as keywords', :method_keywords] +].freeze + +CPF_GENERATE_FACTORY_CONTEXTS = [ + ['when options are passed to the constructor as a Hash', :constructor_hash], + ['when options are passed to #generate as keywords', :method_keywords] +].freeze + +CNPJ_FORMAT_FACTORY_CONTEXTS = [ + ['when options are passed to the constructor as a Hash', :constructor_hash], + ['when options are passed to the constructor as CnpjFormatterOptions', :constructor_options], + ['when options are passed to #format as keywords', :method_keywords], + ['when options are passed to #format as CnpjFormatterOptions', :method_options] +].freeze + +CNPJ_GENERATE_FACTORY_CONTEXTS = [ + ['when options are passed to the constructor as a Hash', :constructor_hash], + ['when options are passed to the constructor as CnpjGeneratorOptions', :constructor_options], + ['when options are passed to #generate as keywords', :method_keywords], + ['when options are passed to #generate as CnpjGeneratorOptions', :method_options] +].freeze + +CNPJ_IS_VALID_FACTORY_CONTEXTS = [ + ['when options are passed to the constructor as a Hash', :constructor_hash], + ['when options are passed to the constructor as CnpjValidatorOptions', :constructor_options], + ['when options are passed to #is_valid as keywords', :method_keywords], + ['when options are passed to #is_valid as CnpjValidatorOptions', :method_options] +].freeze + +RSpec.describe BrUtils do + def default_cpf_formatter_options_snapshot + CpfFmt::CpfFormatterOptions.new.all + end + + def default_cpf_generator_options_snapshot + CpfGen::CpfGeneratorOptions.new.all + end + + def default_cnpj_formatter_options_snapshot + CnpjFmt::CnpjFormatterOptions.new.all + end + + def default_cnpj_generator_options_snapshot + CnpjGen::CnpjGeneratorOptions.new.all + end + + def default_cnpj_validator_options_snapshot + CnpjVal::CnpjValidatorOptions.new.all + end + + describe 'DEFAULT' do + it 'is an instance of BrUtils' do + expect(described_class::DEFAULT).to be_a(described_class) + end + + it 'exposes cpf and cnpj domain utils' do + aggregate_failures do + expect(described_class::DEFAULT.cpf).to be_a(CpfUtils) + expect(described_class::DEFAULT.cnpj).to be_a(CnpjUtils) + end + end + end + + describe 'class helpers' do + # BrUtils façade operations are domain accessors; class helpers forward to DEFAULT. + it 'exposes cpf and cnpj' do + aggregate_failures do + expect(described_class).to respond_to(:cpf) + expect(described_class).to respond_to(:cnpj) + end + end + + context 'when calling through the class' do + it 'returns the same cpf as DEFAULT' do + expect(described_class.cpf).to equal(described_class::DEFAULT.cpf) + end + + it 'returns the same cnpj as DEFAULT' do + expect(described_class.cnpj).to equal(described_class::DEFAULT.cnpj) + end + end + + context 'when DEFAULT is mutated' do + around do |example| + original_cpf = described_class::DEFAULT.cpf + original_cnpj = described_class::DEFAULT.cnpj + example.run + described_class::DEFAULT.cpf = original_cpf + described_class::DEFAULT.cnpj = original_cnpj + end + + it 'affects subsequent class helper cpf calls' do + replacement = CpfUtils.new(formatter: { dash_key: '|' }) + described_class::DEFAULT.cpf = replacement + + aggregate_failures do + expect(described_class.cpf).to equal(replacement) + expect(described_class.cpf.format('12345678909')).to eq('123.456.789|09') + end + end + + it 'affects subsequent class helper cnpj calls' do + replacement = CnpjUtils.new(formatter: { slash_key: '|' }) + described_class::DEFAULT.cnpj = replacement + + aggregate_failures do + expect(described_class.cnpj).to equal(replacement) + expect(described_class.cnpj.format('01ABC234000X56')).to eq('01.ABC.234|000X-56') + end + end + + it 'does not affect a custom instance' do + custom = described_class.new + described_class::DEFAULT.cpf = CpfUtils.new(formatter: { dash_key: '|' }) + described_class::DEFAULT.cnpj = CnpjUtils.new(formatter: { slash_key: '|' }) + + aggregate_failures do + expect(custom.cpf.format('12345678909')).to eq('123.456.789-09') + expect(custom.cnpj.format('01ABC234000X56')).to eq('01.ABC.234/000X-56') + end + end + end + end + + describe 'loaded sibling packages' do + it 'makes cpf-utilities symbols available' do + aggregate_failures do + expect(defined?(CpfUtils)).to eq('constant') + expect(defined?(CpfFmt::CpfFormatter)).to eq('constant') + expect(defined?(CpfGen::CpfGenerator)).to eq('constant') + expect(defined?(CpfVal::CpfValidator)).to eq('constant') + expect(CpfFmt).to respond_to(:cpf_fmt) + expect(CpfGen).to respond_to(:cpf_gen) + expect(CpfVal).to respond_to(:cpf_val) + end + end + + it 'makes cnpj-utilities symbols available' do + aggregate_failures do + expect(defined?(CnpjUtils)).to eq('constant') + expect(defined?(CnpjFmt::CnpjFormatter)).to eq('constant') + expect(defined?(CnpjGen::CnpjGenerator)).to eq('constant') + expect(defined?(CnpjVal::CnpjValidator)).to eq('constant') + expect(CnpjFmt).to respond_to(:cnpj_fmt) + expect(CnpjGen).to respond_to(:cnpj_gen) + expect(CnpjVal).to respond_to(:cnpj_val) + end + end + end + + describe 'two-tier BrUtils re-exports' do + it 'nests sibling modules as the same objects' do + aggregate_failures do + expect(described_class::CpfUtils).to equal(CpfUtils) + expect(described_class::CnpjUtils).to equal(CnpjUtils) + expect(described_class::CpfFmt).to equal(CpfFmt) + expect(described_class::CpfGen).to equal(CpfGen) + expect(described_class::CpfVal).to equal(CpfVal) + expect(described_class::CnpjFmt).to equal(CnpjFmt) + expect(described_class::CnpjGen).to equal(CnpjGen) + expect(described_class::CnpjVal).to equal(CnpjVal) + end + end + + it 'aliases main cpf classes at the façade root' do + aggregate_failures do + expect(described_class::CpfFormatter).to equal(CpfFmt::CpfFormatter) + expect(described_class::CpfFormatterOptions).to equal(CpfFmt::CpfFormatterOptions) + expect(described_class::CpfGenerator).to equal(CpfGen::CpfGenerator) + expect(described_class::CpfGeneratorOptions).to equal(CpfGen::CpfGeneratorOptions) + expect(described_class::CpfValidator).to equal(CpfVal::CpfValidator) + end + end + + it 'aliases main cnpj classes at the façade root' do + aggregate_failures do + expect(described_class::CnpjFormatter).to equal(CnpjFmt::CnpjFormatter) + expect(described_class::CnpjFormatterOptions).to equal(CnpjFmt::CnpjFormatterOptions) + expect(described_class::CnpjGenerator).to equal(CnpjGen::CnpjGenerator) + expect(described_class::CnpjGeneratorOptions).to equal(CnpjGen::CnpjGeneratorOptions) + expect(described_class::CnpjValidator).to equal(CnpjVal::CnpjValidator) + expect(described_class::CnpjValidatorOptions).to equal(CnpjVal::CnpjValidatorOptions) + end + end + + context 'with nested surface smoke' do + it 'exposes Options through the nest' do + options = described_class::CpfFmt::CpfFormatterOptions.new(hidden: true) + + expect(options.hidden).to be(true) + end + + it 'exposes helpers through the nest' do + expect(described_class::CpfFmt.cpf_fmt('12345678909')).to eq('123.456.789-09') + end + + it 'exposes an error class through the nest' do + expect(described_class::CnpjFmt::OutOfRangeError).to equal(CnpjFmt::OutOfRangeError) + end + end + end + + describe '#initialize' do + context 'when called with no arguments' do + subject(:utils) { described_class.new } + + it 'creates default domain utils instances' do + aggregate_failures do + expect(utils.cpf).to be_a(CpfUtils) + expect(utils.cnpj).to be_a(CnpjUtils) + end + end + + it 'uses default domain component options' do + aggregate_failures do + expect_options_containing( + utils.cpf.formatter.options.all, + default_cpf_formatter_options_snapshot + ) + expect_options_containing( + utils.cpf.generator.options.all, + default_cpf_generator_options_snapshot + ) + expect_options_containing( + utils.cnpj.formatter.options.all, + default_cnpj_formatter_options_snapshot + ) + expect_options_containing( + utils.cnpj.generator.options.all, + default_cnpj_generator_options_snapshot + ) + expect_options_containing( + utils.cnpj.validator.options.all, + default_cnpj_validator_options_snapshot + ) + end + end + end + + context 'when called with instances of resources' do + it 'uses the passed CnpjUtils directly' do + cnpj_utils = CnpjUtils.new + utils = described_class.new(cnpj: cnpj_utils) + + aggregate_failures do + expect(utils.cnpj).to be_a(CnpjUtils) + expect(utils.cnpj).to equal(cnpj_utils) + end + end + + it 'uses the passed CpfUtils directly' do + cpf_utils = CpfUtils.new + utils = described_class.new(cpf: cpf_utils) + + aggregate_failures do + expect(utils.cpf).to be_a(CpfUtils) + expect(utils.cpf).to equal(cpf_utils) + end + end + + it 'uses the passed resources directly' do + cnpj_utils = CnpjUtils.new + cpf_utils = CpfUtils.new + utils = described_class.new(cnpj: cnpj_utils, cpf: cpf_utils) + + aggregate_failures do + expect(utils.cnpj).to equal(cnpj_utils) + expect(utils.cpf).to equal(cpf_utils) + end + end + end + + context 'when called with literal Hash parameters' do + let(:cnpj_utils_options) do + { + formatter: { + hidden: true, + hidden_key: '#', + hidden_start: 8, + hidden_end: 11, + dot_key: '_', + slash_key: '|', + dash_key: ' dv ' + }, + generator: { + format: true, + prefix: '12345678', + type: 'numeric' + }, + validator: { + type: 'numeric' + } + } + end + + let(:cpf_utils_options) do + { + formatter: { + hidden: true, + hidden_key: '#', + hidden_start: 8, + hidden_end: 10, + dot_key: '_', + dash_key: ' dv ' + }, + generator: { + format: true, + prefix: '12345678' + } + } + end + + it 'builds CnpjUtils from nested option hashes' do + utils = described_class.new(cnpj: cnpj_utils_options) + + aggregate_failures do + expect(utils.cnpj).to be_a(CnpjUtils) + expect(utils.cnpj.formatter).to be_a(CnpjFmt::CnpjFormatter) + expect_options_containing(utils.cnpj.formatter.options.all, cnpj_utils_options[:formatter]) + expect(utils.cnpj.generator).to be_a(CnpjGen::CnpjGenerator) + expect_options_containing(utils.cnpj.generator.options.all, cnpj_utils_options[:generator]) + expect(utils.cnpj.validator).to be_a(CnpjVal::CnpjValidator) + expect_options_containing(utils.cnpj.validator.options.all, cnpj_utils_options[:validator]) + end + end + + it 'builds CpfUtils from nested option hashes' do + utils = described_class.new(cpf: cpf_utils_options) + + aggregate_failures do + expect(utils.cpf).to be_a(CpfUtils) + expect(utils.cpf.formatter).to be_a(CpfFmt::CpfFormatter) + expect_options_containing(utils.cpf.formatter.options.all, cpf_utils_options[:formatter]) + expect(utils.cpf.generator).to be_a(CpfGen::CpfGenerator) + expect_options_containing(utils.cpf.generator.options.all, cpf_utils_options[:generator]) + end + end + end + + context 'when called with flat formatter and generator options' do + it 'applies cpf_formatter options' do + formatter_options = CpfFmt::CpfFormatterOptions.new(hidden: true, hidden_key: 'X') + utils = described_class.new(cpf_formatter: formatter_options) + + aggregate_failures do + expect(utils.cpf.formatter.options).to equal(formatter_options) + expect(utils.cpf.format('12345678901')).to eq('123.XXX.XXX-XX') + end + end + + it 'applies cpf_generator options' do + generator_options = CpfGen::CpfGeneratorOptions.new(format: true, prefix: '123456789') + utils = described_class.new(cpf_generator: generator_options) + + aggregate_failures do + expect(utils.cpf.generator.options).to equal(generator_options) + expect(utils.cpf.generate).to start_with('123.456.789-') + end + end + + it 'applies cnpj_formatter options' do + formatter_options = CnpjFmt::CnpjFormatterOptions.new(hidden: true, hidden_key: 'X') + utils = described_class.new(cnpj_formatter: formatter_options) + + aggregate_failures do + expect(utils.cnpj.formatter.options).to equal(formatter_options) + expect(utils.cnpj.format('11222333000181')).to eq('11.222.XXX/XXXX-XX') + end + end + + it 'applies cnpj_generator options' do + generator_options = CnpjGen::CnpjGeneratorOptions.new(format: true, prefix: '11222333') + utils = described_class.new(cnpj_generator: generator_options) + + aggregate_failures do + expect(utils.cnpj.generator.options).to equal(generator_options) + expect(utils.cnpj.generate).to start_with('11.222.333/') + end + end + + it 'applies all flat options together' do + cpf_fmt_opts = CpfFmt::CpfFormatterOptions.new(hidden: true) + cpf_gen_opts = CpfGen::CpfGeneratorOptions.new(format: true) + cnpj_fmt_opts = CnpjFmt::CnpjFormatterOptions.new(hidden: true) + cnpj_gen_opts = CnpjGen::CnpjGeneratorOptions.new(format: true) + + utils = described_class.new( + cpf_formatter: cpf_fmt_opts, + cpf_generator: cpf_gen_opts, + cnpj_formatter: cnpj_fmt_opts, + cnpj_generator: cnpj_gen_opts + ) + + aggregate_failures do + expect(utils.cpf.formatter.options).to equal(cpf_fmt_opts) + expect(utils.cpf.generator.options).to equal(cpf_gen_opts) + expect(utils.cnpj.formatter.options).to equal(cnpj_fmt_opts) + expect(utils.cnpj.generator.options).to equal(cnpj_gen_opts) + end + end + end + + context 'when called with nested settings hashes' do + it 'configures CPF formatter and generator from hashes' do + formatter_options = { + hidden: true, + hidden_key: '#', + hidden_start: 8, + hidden_end: 10, + dot_key: '_', + dash_key: ' dv ' + } + generator_options = { format: true, prefix: '12345678' } + + utils = described_class.new( + cpf: { + formatter: formatter_options, + generator: generator_options + } + ) + + aggregate_failures do + expect_options_containing(utils.cpf.formatter.options.all, formatter_options) + expect_options_containing(utils.cpf.generator.options.all, generator_options) + end + end + + it 'configures CNPJ components from hashes' do + formatter_options = { slash_key: '|' } + generator_options = { format: true, prefix: '12345' } + validator_options = { type: 'numeric', case_sensitive: false } + + utils = described_class.new( + cnpj: { + formatter: formatter_options, + generator: generator_options, + validator: validator_options + } + ) + + aggregate_failures do + expect_options_containing(utils.cnpj.formatter.options.all, formatter_options) + expect_options_containing(utils.cnpj.generator.options.all, generator_options) + expect_options_containing(utils.cnpj.validator.options.all, validator_options) + end + end + + it 'keeps provided options instances by reference' do + cnpj_formatter_options = CnpjFmt::CnpjFormatterOptions.new + cnpj_generator_options = CnpjGen::CnpjGeneratorOptions.new + cnpj_validator_options = CnpjVal::CnpjValidatorOptions.new + + utils = described_class.new( + cnpj: { + formatter: cnpj_formatter_options, + generator: cnpj_generator_options, + validator: cnpj_validator_options + } + ) + + aggregate_failures do + expect(utils.cnpj.formatter.options).to equal(cnpj_formatter_options) + expect(utils.cnpj.generator.options).to equal(cnpj_generator_options) + expect(utils.cnpj.validator.options).to equal(cnpj_validator_options) + end + end + + it 'reflects later mutation of shared options' do + cnpj_formatter_options = CnpjFmt::CnpjFormatterOptions.new + utils = described_class.new(cnpj: { formatter: cnpj_formatter_options }) + + cnpj_formatter_options.dash_key = '|' + + expect(utils.cnpj.formatter.options.all[:dash_key]).to eq('|') + end + end + + context 'when called with a settings Hash' do + it 'adopts nested domain utils from the Hash' do + cpf_utils = CpfUtils.new + cnpj_utils = CnpjUtils.new + utils = described_class.new({ cpf: cpf_utils, cnpj: cnpj_utils }) + + aggregate_failures do + expect(utils.cpf).to equal(cpf_utils) + expect(utils.cnpj).to equal(cnpj_utils) + end + end + end + + context 'when called with a non-Hash settings value' do + it 'raises TypeMismatchError for a string' do + expect { described_class.new('not-a-hash') } + .to raise_error(BrUtils::TypeMismatchError, /settings must be a Hash/) + end + + it 'raises TypeMismatchError for false' do + expect { described_class.new(false) } + .to raise_error(BrUtils::TypeMismatchError, /settings must be a Hash/) + end + + it 'raises TypeMismatchError for an array' do + expect { described_class.new([]) } + .to raise_error(BrUtils::TypeMismatchError, /settings must be a Hash/) + end + end + + context 'when called with invalid options' do + it 'raises CPF formatter OutOfRangeError' do + expect { described_class.new(cpf: { formatter: { hidden_start: -1 } }) } + .to raise_error(CpfFmt::OutOfRangeError) + end + + it 'raises CPF generator ValidationError' do + expect { described_class.new(cpf: { generator: { prefix: '000000000' } }) } + .to raise_error(CpfGen::ValidationError) + end + + it 'raises CNPJ formatter OutOfRangeError' do + expect { described_class.new(cnpj: { formatter: { hidden_start: -1 } }) } + .to raise_error(CnpjFmt::OutOfRangeError) + end + + it 'raises CNPJ formatter ValidationError' do + expect { described_class.new(cnpj: { formatter: { dash_key: "\u00e5" } }) } + .to raise_error(CnpjFmt::ValidationError) + end + + it 'raises CNPJ generator ValidationError for prefix' do + expect { described_class.new(cnpj: { generator: { prefix: '00000000' } }) } + .to raise_error(CnpjGen::ValidationError) + end + + it 'raises CNPJ generator ValidationError for type' do + expect { described_class.new(cnpj: { generator: { type: 'invalid' } }) } + .to raise_error(CnpjGen::ValidationError) + end + + it 'raises CNPJ generator TypeMismatchError' do + expect { described_class.new(cnpj: { generator: { prefix: 123 } }) } + .to raise_error(CnpjGen::TypeMismatchError) + end + + it 'raises CNPJ validator ValidationError' do + expect { described_class.new(cnpj: { validator: { type: 'invalid' } }) } + .to raise_error(CnpjVal::ValidationError) + end + end + + context 'when called with both a settings Hash and keywords' do + it 'raises InvalidArgumentCombinationError' do + expect do + described_class.new({ cpf: {} }, cnpj: CnpjUtils.new) + end.to raise_error(BrUtils::InvalidArgumentCombinationError) + end + + it 'raises for false settings with keywords' do + expect do + described_class.new(false, cpf: {}) + end.to raise_error(BrUtils::InvalidArgumentCombinationError) + end + end + end + + describe 'resource accessors' do + subject(:utils) { described_class.new } + + it 'returns the CpfUtils instance' do + expect(utils.cpf).to be_a(CpfUtils) + end + + it 'returns the CpfFormatter instance' do + expect(utils.cpf.formatter).to be_a(CpfFmt::CpfFormatter) + end + + it 'returns the CpfGenerator instance' do + expect(utils.cpf.generator).to be_a(CpfGen::CpfGenerator) + end + + it 'returns the CpfValidator instance' do + expect(utils.cpf.validator).to be_a(CpfVal::CpfValidator) + end + + it 'returns the CnpjUtils instance' do + expect(utils.cnpj).to be_a(CnpjUtils) + end + + it 'returns the CnpjFormatter instance' do + expect(utils.cnpj.formatter).to be_a(CnpjFmt::CnpjFormatter) + end + + it 'returns the CnpjGenerator instance' do + expect(utils.cnpj.generator).to be_a(CnpjGen::CnpjGenerator) + end + + it 'returns the CnpjValidator instance' do + expect(utils.cnpj.validator).to be_a(CnpjVal::CnpjValidator) + end + end + + describe '#cnpj=' do + subject(:utils) { described_class.new } + + context 'when called with a CnpjUtils instance' do + it 'sets the CnpjUtils instance' do + cnpj_utils = CnpjUtils.new + utils.cnpj = cnpj_utils + + expect(utils.cnpj).to equal(cnpj_utils) + end + end + + context 'when called with literal Hash parameters' do + let(:cnpj_utils_options) do + { + formatter: { + hidden: true, + hidden_key: '#', + hidden_start: 8, + hidden_end: 11, + dot_key: '_', + slash_key: '|', + dash_key: ' dv ' + }, + generator: { + format: true, + prefix: '12345678', + type: 'numeric' + }, + validator: { + type: 'numeric' + } + } + end + + it 'sets CnpjUtils built from options' do + utils.cnpj = cnpj_utils_options + + aggregate_failures do + expect(utils.cnpj).to be_a(CnpjUtils) + expect_options_containing(utils.cnpj.formatter.options.all, cnpj_utils_options[:formatter]) + expect_options_containing(utils.cnpj.generator.options.all, cnpj_utils_options[:generator]) + expect_options_containing(utils.cnpj.validator.options.all, cnpj_utils_options[:validator]) + end + end + end + + context 'when called with nil' do + it 'creates a new default CnpjUtils' do + original = utils.cnpj + utils.cnpj = nil + + aggregate_failures do + expect(utils.cnpj).to be_a(CnpjUtils) + expect(utils.cnpj).not_to equal(original) + end + end + end + end + + describe '#cpf=' do + subject(:utils) { described_class.new } + + context 'when called with a CpfUtils instance' do + it 'sets the CpfUtils instance' do + cpf_utils = CpfUtils.new + utils.cpf = cpf_utils + + expect(utils.cpf).to equal(cpf_utils) + end + end + + context 'when called with literal Hash parameters' do + let(:cpf_utils_options) do + { + formatter: { + hidden: true, + hidden_key: '#', + hidden_start: 8, + hidden_end: 10, + dot_key: '_', + dash_key: ' dv ' + }, + generator: { + format: true, + prefix: '12345678' + } + } + end + + it 'sets CpfUtils built from options' do + utils.cpf = cpf_utils_options + + aggregate_failures do + expect(utils.cpf).to be_a(CpfUtils) + expect_options_containing(utils.cpf.formatter.options.all, cpf_utils_options[:formatter]) + expect_options_containing(utils.cpf.generator.options.all, cpf_utils_options[:generator]) + end + end + end + + context 'when called with nil' do + it 'creates a new default CpfUtils' do + original = utils.cpf + utils.cpf = nil + + aggregate_failures do + expect(utils.cpf).to be_a(CpfUtils) + expect(utils.cpf).not_to equal(original) + end + end + end + end + + describe 'CPF utils through BrUtils' do + describe '#format' do + CPF_FORMAT_FACTORY_CONTEXTS.each do |context_description, factory_key| + context context_description do + let(:format_fn) { CPF_FORMAT_FACTORIES.fetch(factory_key) } + + it 'matches CpfFormatter#format behavior' do + input = '80976511061' + formatter = CpfFmt::CpfFormatter.new + + expect(format_fn.call(input)).to eq(formatter.format(input)) + end + + it 'forwards formatting options' do + expect(format_fn.call('80976511061', '_', ' dv ')).to eq('809_765_110 dv 61') + end + end + end + + context 'when constructor formatter defaults are set' do + it 'applies defaults when method options are omitted' do + utils = described_class.new( + cpf: { + formatter: { + hidden: true, + hidden_key: '#' + } + } + ) + + expect(utils.cpf.format('80976511061')).to include('#') + end + end + + it 'formats a basic CPF string' do + expect(described_class.new.cpf.format('12345678901')).to eq('123.456.789-01') + end + end + + describe '#generate' do + CPF_GENERATE_FACTORY_CONTEXTS.each do |context_description, factory_key| + context context_description do + let(:generate_fn) { CPF_GENERATE_FACTORIES.fetch(factory_key) } + + it 'matches CpfGenerator length behavior' do + generator = CpfGen::CpfGenerator.new + result = generate_fn.call + + aggregate_failures do + expect(result).to match(/\A\d{11}\z/) + expect(result.length).to eq(generator.generate.length) + end + end + + it 'forwards generation options' do + result = generate_fn.call(format: true, prefix: '12345') + + expect(result).to match(/\A123\.45\d\.\d{3}-\d{2}\z/) + end + + it 'returns a deterministic CPF for a full prefix' do + prefix = '123456789' + results = Array.new(20) { generate_fn.call(prefix: prefix) } + + expect(results.uniq.size).to eq(1) + end + end + end + + it 'generates an 11-digit CPF' do + expect(described_class.new.cpf.generate.length).to eq(11) + end + end + + describe '#is_valid' do + it 'returns true for a valid CPF' do + expect(described_class.new.cpf.is_valid('52998224725')).to be(true) + end + + it 'returns false for an invalid CPF' do + expect(described_class.new.cpf.is_valid('12345678901')).to be(false) + end + end + end + + describe 'CNPJ utils through BrUtils' do + describe '#format' do + CNPJ_FORMAT_FACTORY_CONTEXTS.each do |context_description, factory_key| + context context_description do + let(:format_fn) { CNPJ_FORMAT_FACTORIES.fetch(factory_key) } + + it 'matches CnpjFormatter#format behavior' do + input = '91415732000793' + formatter = CnpjFmt::CnpjFormatter.new + + expect(format_fn.call(input)).to eq(formatter.format(input)) + end + + it 'forwards formatting options' do + expect(format_fn.call('01ABC234000X56', '|')).to eq('01.ABC.234|000X-56') + end + end + end + + context 'when constructor formatter defaults are set' do + it 'applies defaults when method options are omitted' do + utils = described_class.new( + cnpj: { + formatter: { + hidden: true, + hidden_key: '#' + } + } + ) + + expect(utils.cnpj.format('12ABC34500DE99')).to include('#') + end + end + + it 'formats a basic CNPJ string' do + expect(described_class.new.cnpj.format('12345678000195')).to eq('12.345.678/0001-95') + end + end + + describe '#generate' do + CNPJ_GENERATE_FACTORY_CONTEXTS.each do |context_description, factory_key| + context context_description do + let(:generate_fn) { CNPJ_GENERATE_FACTORIES.fetch(factory_key) } + + it 'matches CnpjGenerator length behavior' do + generator = CnpjGen::CnpjGenerator.new + result = generate_fn.call + + aggregate_failures do + expect(result).to match(/\A[0-9A-Z]{14}\z/) + expect(result.length).to eq(generator.generate.length) + end + end + + it 'forwards generation options' do + result = generate_fn.call(format: true, prefix: '12345', type: 'numeric') + + expect(result).to match(%r{\A12\.345\.\d{3}/\d{4}-\d{2}\z}) + end + + it 'returns a deterministic CNPJ for a full prefix' do + prefix = '123456780009' + results = Array.new(20) { generate_fn.call(prefix: prefix) } + + expect(results.uniq.size).to eq(1) + end + end + end + + it 'generates a 14-character CNPJ' do + expect(described_class.new.cnpj.generate.length).to eq(14) + end + end + + describe '#is_valid' do + CNPJ_IS_VALID_FACTORY_CONTEXTS.each do |context_description, factory_key| + context context_description do + let(:is_valid_fn) { CNPJ_IS_VALID_FACTORIES.fetch(factory_key) } + + it 'matches CnpjValidator#is_valid behavior' do + input = '91415732000793' + validator = CnpjVal::CnpjValidator.new + + expect(is_valid_fn.call(input)).to eq(validator.is_valid(input)) + end + + it 'forwards validation options' do + input = '1QB5UKALPYFP59' + + aggregate_failures do + expect(is_valid_fn.call(input, type: 'numeric')).to be(false) + expect(is_valid_fn.call(input, type: 'alphanumeric')).to be(true) + end + end + + it 'validates formatted and unformatted strings' do + aggregate_failures do + expect(is_valid_fn.call('1QB5UKALPYFP59')).to be(true) + expect(is_valid_fn.call('1QB5.UKAL.PYF/P59')).to be(true) + expect(is_valid_fn.call('AB123CDE0001555')).to be(false) + end + end + end + end + + it 'returns true for a valid numeric CNPJ' do + expect(described_class.new.cnpj.is_valid('11222333000181')).to be(true) + end + + it 'returns false for an invalid CNPJ' do + expect(described_class.new.cnpj.is_valid('11111111111111')).to be(false) + end + end + end + + describe 'package smoke' do + it 'exposes BrUtils as a class' do + aggregate_failures do + expect(described_class).to be_a(Class) + expect(described_class.new).to be_a(described_class) + end + end + + it 'exposes a VERSION string' do + expect(described_class::VERSION).to be_a(String).and match(/\A\d+\.\d+\.\d+\z/) + end + end +end From 2ab2772ce09f46995fc2a352a84a890caef86537 Mon Sep 17 00:00:00 2001 From: juliolmuller Date: Mon, 10 Aug 2026 17:39:25 -0300 Subject: [PATCH 04/14] docs(br-utils): create CHANGELOG file Co-authored-by: Claude Opus 5 Co-authored-by: Cursor Agent --- packages/br-utilities/CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/br-utilities/CHANGELOG.md b/packages/br-utilities/CHANGELOG.md index f2eea00..87656f0 100644 --- a/packages/br-utilities/CHANGELOG.md +++ b/packages/br-utilities/CHANGELOG.md @@ -1 +1,17 @@ # br-utilities + +## 1.0.0 + +### 🚀 Stable Version Released! + +Unified toolkit to deal with Brazilian documents (CPF and CNPJ): validation, formatting, and generation of valid IDs. Main features: + +- **Unified façade**: `BrUtils` aggregates configurable `CpfUtils` and `CnpjUtils` behind `#cpf` / `#cnpj` accessors (with setters; `nil` resets to defaults). +- **Constructor flexibility**: accept a settings `Hash` or keyword args (`cpf:` / `cnpj:` nested mappings, or flat `cpf_formatter:` / `cnpj_validator:`-style kwargs; nested wins when both present). +- **Quick helpers**: `BrUtils.cpf` / `.cnpj` alias mutable `BrUtils::DEFAULT` (process-wide; prefer `BrUtils.new` under concurrency). +- **Two-tier re-exports**: main classes at the façade root (`BrUtils::CpfFormatter`, `BrUtils::CnpjValidator`, …); full sibling surface under `BrUtils::CpfFmt` / `CpfUtils` / `CnpjFmt` / …. +- **One install**: depends on `cpf-utilities` and `cnpj-utilities` so both domains ship without requiring each gem separately. +- **Alphanumeric CNPJ**: CNPJ path supports 14-character alphanumeric IDs via `cnpj-utilities` (numeric CPF via `cpf-utilities`). +- **Structured errors**: `BrUtils::TypeMismatchError` / `InvalidArgumentCombinationError` (+ `BrUtils::Error` marker) for façade misuse; domain errors propagate from nested packages. + +For detailed usage and API reference, see the [README](./README.md). From bc8c67d768cce794da2841adde426e9629ac477b Mon Sep 17 00:00:00 2001 From: juliolmuller Date: Mon, 10 Aug 2026 17:39:46 -0300 Subject: [PATCH 05/14] docs(br-utils): create README file Co-authored-by: Claude Opus 5 Co-authored-by: Cursor Agent --- packages/br-utilities/README.md | 707 ++++++++++++++++++++++++++++++++ 1 file changed, 707 insertions(+) create mode 100644 packages/br-utilities/README.md diff --git a/packages/br-utilities/README.md b/packages/br-utilities/README.md new file mode 100644 index 0000000..abc1e1d --- /dev/null +++ b/packages/br-utilities/README.md @@ -0,0 +1,707 @@ +![br-utilities for Ruby](https://br-utils.vercel.app/img/cover_br-utils.jpg) + +[![Gem Version](https://img.shields.io/gem/v/br-utilities)](https://rubygems.org/gems/br-utilities) +[![Gem Downloads](https://img.shields.io/gem/dt/br-utilities)](https://rubygems.org/gems/br-utilities) +[![Ruby Version](https://img.shields.io/gem/rv/br-utilities)](https://www.ruby-lang.org/) +[![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) +[![Last Update Date](https://img.shields.io/github/last-commit/LacusSolutions/br-utils-ruby)](https://github.com/LacusSolutions/br-utils-ruby) +[![Project License](https://img.shields.io/github/license/LacusSolutions/br-utils-ruby)](https://github.com/LacusSolutions/br-utils-ruby/blob/main/LICENSE) + +> 🚀 **Full support for the [new alphanumeric CNPJ format](https://github.com/user-attachments/files/23937961/calculodvcnpjalfanaumerico.pdf).** + +> 🌎 [Acessar documentação em português](./README.pt.md) + +A Ruby toolkit to handle the main operations with Brazilian-related data: CPF (Individual's Taxpayer ID) and CNPJ (Business Tax ID). It wraps [`cpf-utilities`](https://rubygems.org/gems/cpf-utilities) and [`cnpj-utilities`](https://rubygems.org/gems/cnpj-utilities) in a single façade class (`BrUtils`). + +## Ruby Support + +| ![Ruby 3.1](https://img.shields.io/badge/Ruby-3.1-CC342D?logo=ruby&logoColor=white) | ![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) | ![Ruby 4.0](https://img.shields.io/badge/Ruby-4.0-CC342D?logo=ruby&logoColor=white) | +| --- | --- | --- | --- | --- | +| Passing ✔ | Passing ✔ | Passing ✔ | Passing ✔ | Passing ✔ | + +Requires Ruby **≥ 3.1** (see `required_ruby_version` in the gemspec). + +## Features + +- ✅ **Unified top-level API**: Class helpers `BrUtils.cpf` / `.cnpj` alias `BrUtils::DEFAULT`; each domain offers `format`, `generate`, and `is_valid` +- ✅ **Bundled domains**: [`cpf-utilities`](https://rubygems.org/gems/cpf-utilities) and [`cnpj-utilities`](https://rubygems.org/gems/cnpj-utilities) installed together +- ✅ **Alphanumeric CNPJ**: Full support for the new alphanumeric CNPJ format (introduced in 2026) +- ✅ **Reusable instance**: `BrUtils` class with optional default CPF and CNPJ settings (nested mappings, flat component kwargs, or pre-built utils instances) +- ✅ **Two-tier access**: Prefer main-class shortcuts at the façade root (`BrUtils::CpfFormatter`, `BrUtils::CnpjValidator`, …); Options, helpers, and errors live under nested package modules (`BrUtils::CpfFmt`, `BrUtils::CnpjUtils`, …). Root siblings (`CpfUtils`, `CnpjUtils`, `CpfFmt`, …) still work +- ✅ **Per-call overrides**: Configure defaults on the façade / domain utils; override options on a single `format` / `generate` / `is_valid` call +- ✅ **Error handling**: Domain errors propagate unchanged from the bundled packages; this gem defines `BrUtils::TypeMismatchError` and `BrUtils::InvalidArgumentCombinationError` for API misuse + +## Installation + +Install the gem directly: + +```bash +gem install br-utilities +``` + +Or add it to your `Gemfile` and run `bundle install`: + +```ruby +gem 'br-utilities' +``` + +This installs **`br-utilities`** together with [`cpf-utilities`](https://rubygems.org/gems/cpf-utilities) and [`cnpj-utilities`](https://rubygems.org/gems/cnpj-utilities) (which in turn pull in the CPF and CNPJ component packages). You do **not** need separate `gem install` / `gem` lines for the domain packages when using **`br-utilities`**. + +## Require + +```ruby +require 'br-utilities' +``` + +## Quick Start + +Prefer the aggregator class helpers (`BrUtils.cpf` / `BrUtils.cnpj`) for one-off calls — they forward to `BrUtils::DEFAULT`: + +```ruby +require 'br-utilities' + +cpf = '12345678909' +cnpj = '03603568000195' + +# CPF (personal ID) +BrUtils.cpf.format(cpf) # => "123.456.789-09" +BrUtils.cpf.generate(format: true) # => e.g. "478.442.410-55" +BrUtils.cpf.is_valid('123.456.789-09') # => true + +# CNPJ (business ID) +BrUtils.cnpj.format(cnpj) # => "03.603.568/0001-95" +BrUtils.cnpj.generate(format: true) # => e.g. "AB.123.CDE/0001-55" +BrUtils.cnpj.is_valid('98765432000198') # => true +``` + +**With domain aggregators:** + +```ruby +require 'br-utilities' + +cpf = '12345678909' +cnpj = '03603568000195' + +CpfUtils.format(cpf) # => "123.456.789-09" +CnpjUtils.format(cnpj) # => "03.603.568/0001-95" +CpfUtils.is_valid(cpf) # => true +CnpjUtils.is_valid(cnpj) # => true +``` + +**With functional helpers** (root sibling modules, loaded by this gem): + +```ruby +require 'br-utilities' + +cpf = '12345678909' +cnpj = '03603568000195' + +CpfFmt.cpf_fmt(cpf) # => "123.456.789-09" +CpfVal.cpf_val(cpf) # => true +CnpjFmt.cnpj_fmt(cnpj) # => "03.603.568/0001-95" +CnpjVal.cnpj_val(cnpj) # => true +``` + +## Usage + +You can work in these equivalent ways: + +1. **`BrUtils.cpf` / `.cnpj`** — class helpers for quick one-off calls (forward to `DEFAULT`). +2. **`BrUtils::DEFAULT`** — mutable shared singleton (same object the class helpers use; process-wide / not thread-isolated). +3. **`BrUtils.new`** — configurable instance with shared defaults across both CPF and CNPJ domains. +4. **Domain aggregators** — `CpfUtils` / `CnpjUtils` (or `BrUtils::CpfUtils` / `BrUtils::CnpjUtils`) directly. +5. **Main classes under `BrUtils`** — `BrUtils::CpfFormatter`, `BrUtils::CnpjGenerator`, and related shortcuts. +6. **Nested package modules** — Options, helpers, errors, and types via `BrUtils::CpfFmt` / `CpfGen` / `CpfVal` / `CnpjFmt` / `CnpjGen` / `CnpjVal` / `CpfUtils` / `CnpjUtils`. +7. **Root sibling modules** (still supported) — `CpfFmt`, `CnpjUtils`, and the rest unchanged. + +All approaches expose the same options and behavior within each domain. For exhaustive option tables and component-specific details, see the README of each [bundled package](#bundled-packages). + +### Class helpers (`BrUtils.cpf` / `.cnpj`) + +These class methods return the same domain utils instances as `BrUtils::DEFAULT`. Prefer them for one-off calls: + +```ruby +BrUtils.cpf.format('12345678909') +BrUtils.cpf.generate(format: true) +BrUtils.cpf.is_valid('12345678909') + +BrUtils.cnpj.format('03603568000195') +BrUtils.cnpj.generate(type: 'numeric') +BrUtils.cnpj.is_valid('98765432000198') +``` + +### `BrUtils::DEFAULT` (default instance) + +`BrUtils::DEFAULT` is the pre-built, **mutable** singleton behind the class helpers (parity with the JS default export / Python `br_utils`). Its configuration is **process-wide and shared across threads**: mutating it (e.g. `DEFAULT.cpf = …`) affects subsequent `BrUtils.cpf` / `.cnpj` calls for every caller in the process. Prefer `BrUtils.new` or per-call options for concurrent or isolated work; custom instances stay independent of `DEFAULT`: + +```ruby +BrUtils::DEFAULT.cpf = CpfUtils.new(formatter: { dash_key: '|' }) +BrUtils.cpf.format('12345678909') # => "123.456.789|09" + +custom = BrUtils.new +custom.cpf.format('12345678909') # => "123.456.789-09" (unaffected) +``` + +### `BrUtils` (class) + +For custom default CPF or CNPJ utils, create your own instance: + +```ruby +require 'br-utilities' + +utils = BrUtils.new( + cpf: { + formatter: { hidden: true, hidden_key: '#' }, + generator: { format: true } + }, + cnpj: { + formatter: { hidden: true }, + generator: { type: 'numeric', format: true }, + validator: { type: 'numeric' } + } +) + +utils.cpf.format('12345678909') # => "123.###.###-##" +utils.cpf.generate # => e.g. "005.265.352-88" +utils.cnpj.format('03603568000195') # => "03.603.***/****-**" +utils.cnpj.generate # => e.g. "73.008.535/0005-06" + +# Access or replace internal domain instances +utils.cpf # => CpfUtils +utils.cnpj # => CnpjUtils +``` + +- **`BrUtils.new(settings = nil, **keywords)`**: Optional settings. Pass either a settings `Hash` with `:cpf` and/or `:cnpj` keys, **or** the same keys (plus flat component kwargs) as keyword arguments — not both (passing both raises `BrUtils::InvalidArgumentCombinationError`). + - **`:cpf` / `:cnpj`**: A pre-built `CpfUtils` / `CnpjUtils` instance **or** a configuration `Hash` spread into the corresponding utils constructor. Within that `Hash`, each resource key (`:formatter`, `:generator`, and `:validator` for CNPJ) accepts either an options object or a mapping of option values. + - **`:cpf_formatter`**, **`:cpf_generator`**, **`:cnpj_formatter`**, **`:cnpj_generator`**, **`:cnpj_validator`**: Flat convenience arguments when only individual components need customization. They are ignored when the corresponding `:cpf` or `:cnpj` argument is provided. +- **`#cpf`**, **`#cnpj`**: Accessors (getters and setters) for the domain utils instances. Setters accept a utils instance, a configuration `Hash`, or `nil` to reset to defaults (replaces the entire instance; does not merge). + +Flat constructor options (alternative to nested `:cpf` / `:cnpj` mappings): + +```ruby +require 'br-utilities' + +utils = BrUtils.new( + cpf_formatter: CpfFmt::CpfFormatterOptions.new(hidden: true, hidden_key: '#'), + cpf_generator: CpfGen::CpfGeneratorOptions.new(format: true), + cnpj_formatter: CnpjFmt::CnpjFormatterOptions.new(hidden: true, hidden_key: '#'), + cnpj_generator: CnpjGen::CnpjGeneratorOptions.new(format: true, type: 'numeric'), + cnpj_validator: CnpjVal::CnpjValidatorOptions.new(type: 'numeric') +) +``` + +Passing a settings `Hash` positional argument together with any keyword raises: + +```ruby +BrUtils.new({ cpf: {} }, cnpj: CnpjUtils.new) +# raises BrUtils::InvalidArgumentCombinationError +``` + +### Instance defaults and per-call overrides + +```ruby +require 'br-utilities' + +utils = BrUtils.new( + cpf: { + formatter: { hidden: true, hidden_key: '#' }, + generator: { format: true } + }, + cnpj: { + formatter: { hidden: true, hidden_key: '#' }, + generator: { format: true }, + validator: { type: 'numeric' } + } +) + +cpf = '12345678909' +cnpj = '03603568000195' + +utils.cpf.format(cpf) # => "123.###.###-##" +utils.cpf.format(cpf, hidden: false) # this call only: unmasked +utils.cpf.generate(format: false) # this call only: compact output + +utils.cnpj.format(cnpj) # => "03.603.###/####-##" +utils.cnpj.format(cnpj, hidden: false) # this call only: unmasked +utils.cnpj.is_valid('1QB5UKALPYFP59') # => false (instance validator is numeric-only) +utils.cnpj.is_valid( # => true for this call + '1QB5UKALPYFP59', + type: 'alphanumeric' +) +``` + +Passing a `CnpjFmt::CnpjFormatterOptions`, `CnpjGen::CnpjGeneratorOptions`, or `CnpjVal::CnpjValidatorOptions` instance into the `BrUtils` constructor stores that object by reference — mutating it later affects subsequent calls with no per-call override. + +To change a single nested option without replacing the whole domain utils, mutate via the domain accessors (e.g. `utils.cpf.formatter.options.hidden = true`). + +### CPF operations + +CPF methods are accessed via `BrUtils.cpf`, `utils.cpf`, `CpfUtils`, or the `CpfFmt` / `CpfGen` / `CpfVal` helpers. CPF uses the API from [`cpf-utilities`](../cpf-utilities/README.md). + +#### Formatting (`#format` / `CpfFmt.cpf_fmt`) + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `hidden` | `Boolean` | `false` | When `true`, mask digits in `hidden_start`–`hidden_end` with `hidden_key` | +| `hidden_key` | `String` | `'*'` | Character(s) used to replace masked digits | +| `hidden_start` | `Integer` | `3` | Start index (0–10, inclusive) of the range to hide | +| `hidden_end` | `Integer` | `10` | End index (0–10, inclusive) of the range to hide | +| `dot_key` | `String` | `'.'` | Dot delimiter (e.g. in `123.456.789`) | +| `dash_key` | `String` | `'-'` | Dash delimiter (e.g. before check digits `…-09`) | +| `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 ≠ 11; return value is used as result | + +Default **`on_fail`** returns an empty string. Invalid length does **not** raise from `#format`. + +```ruby +require 'br-utilities' + +cpf = '12345678909' + +BrUtils.cpf.format(cpf) # => "123.456.789-09" +BrUtils.cpf.format(cpf, hidden: true, hidden_key: '#') # => "123.###.###-##" +BrUtils.cpf.format(cpf, dot_key: '', dash_key: '_') # => "123456789_09" + +CpfFmt.cpf_fmt(cpf, hidden: true) # => "123.***.***-**" +``` + +#### Generation (`#generate` / `CpfGen.cpf_gen`) + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `format` | `Boolean` | `false` | When `true`, return the generated CPF in standard format (`000.000.000-00`) | +| `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. | + +Prefix rules: the base (first 9 digits) cannot be all zeros; 9 repeated digits (e.g. `999999999`) are not allowed. + +```ruby +require 'br-utilities' + +BrUtils.cpf.generate # => e.g. "11508890048" +BrUtils.cpf.generate(format: true) # => e.g. "661.134.831-00" +BrUtils.cpf.generate(prefix: '123456789') # => "12345678909" +CpfGen.cpf_gen(prefix: '123456789', format: true) # => "123.456.789-09" +``` + +#### Validation (`#is_valid` / `CpfVal.cpf_val`) + +Accepts formatted or unformatted CPF strings (or an `Array` of strings). Returns **`true`** or **`false`** without raising for invalid CPF. No validator options exist. + +```ruby +require 'br-utilities' + +BrUtils.cpf.is_valid('12345678909') # => true +BrUtils.cpf.is_valid('123.456.789-09') # => true +BrUtils.cpf.is_valid('12345678900') # => false +CpfVal.cpf_val('12345678909') # => true +``` + +### CNPJ operations + +CNPJ methods are accessed via `BrUtils.cnpj`, `utils.cnpj`, `CnpjUtils`, or the `CnpjFmt` / `CnpjGen` / `CnpjVal` helpers. CNPJ uses the API from [`cnpj-utilities`](../cnpj-utilities/README.md). + +#### Formatting (`#format` / `CnpjFmt.cnpj_fmt`) + +| 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 | + +Default **`on_fail`** returns an empty string. Wrong input types raise **`CnpjFmt::TypeMismatchError`**. + +```ruby +require 'br-utilities' + +cnpj = '03603568000195' + +BrUtils.cnpj.format(cnpj) # => "03.603.568/0001-95" +BrUtils.cnpj.format('12ABC34500DE99') # => "12.ABC.345/00DE-99" +BrUtils.cnpj.format( # => "03.603.###/####-##" + cnpj, + hidden: true, + hidden_key: '#' +) +BrUtils.cnpj.format( # => "03603568|0001_95" + cnpj, + dot_key: '', + slash_key: '|', + dash_key: '_' +) + +CnpjFmt.cnpj_fmt(cnpj) # => "03.603.568/0001-95" +``` + +#### Generation (`#generate` / `CnpjGen.cnpj_gen`) + +| 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. + +```ruby +require 'br-utilities' + +BrUtils.cnpj.generate # => e.g. "1GJTR3J3XSSA96" +BrUtils.cnpj.generate(format: true) # => e.g. "V1.J0V.8WE/DVZ7-50" +BrUtils.cnpj.generate( # => e.g. "12345678855883" + prefix: '12345678', + type: 'numeric' +) +CnpjGen.cnpj_gen(type: 'numeric') # => e.g. "65453043000178" +``` + +#### Validation (`#is_valid` / `CnpjVal.cnpj_val`) + +| 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). | + +```ruby +require 'br-utilities' + +BrUtils.cnpj.is_valid('98765432000198') # => true +BrUtils.cnpj.is_valid('98765432000199') # => false +BrUtils.cnpj.is_valid('1QB5UKALPYFP59') # => true +BrUtils.cnpj.is_valid('1QB5UKALpyfp59') # => false +BrUtils.cnpj.is_valid( # => true + '1QB5UKALpyfp59', + case_sensitive: false +) +BrUtils.cnpj.is_valid( # => false + '1QB5UKALPYFP59', + type: 'numeric' +) + +CnpjVal.cnpj_val('98765432000198') # => true +CnpjVal.cnpj_val('1QB5UKALpyfp59', case_sensitive: false) # => true +CnpjVal.cnpj_val('1QB5UKALPYFP59', type: 'numeric') # => false +``` + +Invalid CNPJ returns **`false`** without raising. Wrong input types raise **`CnpjVal::TypeMismatchError`**. + +### Domain aggregators (standalone) + +Use `CpfUtils` or `CnpjUtils` directly when you only need one domain: + +```ruby +require 'br-utilities' + +cpf_utils = CpfUtils.new( + formatter: { hidden: true }, + generator: { format: true } +) + +cnpj_utils = CnpjUtils.new( + formatter: { hidden: true }, + generator: { format: true }, + validator: { type: 'numeric' } +) + +cpf_utils.format('12345678909') # => "123.***.***-**" +cnpj_utils.format('03603568000195') # => "03.603.***/****-**" +``` + +### Accessing components + +Each domain aggregator exposes its internal formatter, generator, and validator: + +```ruby +require 'br-utilities' + +utils = BrUtils.new + +utils.cpf.formatter.format('12345678909', hidden: true) # => "123.***.***-**" +utils.cpf.generator.generate(format: true) # => e.g. "545.507.690-68" +utils.cpf.validator.is_valid('12345678909') # => true + +utils.cnpj.formatter.format('12ABC34500DE99') # => "12.ABC.345/00DE-99" +utils.cnpj.generator.generate(format: true) # => e.g. "8O.BE5.2KL/UI0Y-06" +utils.cnpj.validator.is_valid('03603568000195') # => true +``` + +### Using component classes and nested modules + +Preferred paths after `require 'br-utilities'`: + +```ruby +require 'br-utilities' + +# Main classes at the façade root +formatter = BrUtils::CpfFormatter.new(hidden: true) +generator = BrUtils::CnpjGenerator.new(type: 'numeric') +validator = BrUtils::CnpjValidator.new + +formatter.format('12345678909') # => "123.***.***-**" + +# Options, helpers, and errors under nested package modules +options = BrUtils::CpfFmt::CpfFormatterOptions.new(dash_key: '|') +BrUtils::CpfFmt.cpf_fmt('12345678909') # => "123.456.789-09" + +begin + BrUtils::CnpjFmt.cnpj_fmt(12_345) +rescue BrUtils::CnpjFmt::TypeMismatchError + # wrong input type +end +``` + +Root siblings remain supported (same objects as the nests): + +```ruby +CpfFmt.cpf_fmt('12345678909', dash_key: '|') # => "123.456.789|09" +CpfGen.cpf_gen(format: true) # => e.g. "478.442.410-55" +CpfVal.cpf_val('12345678909') # => true +CnpjFmt.cnpj_fmt('01ABC234000X56', slash_key: '|') # => "01.ABC.234|000X-56" +CnpjGen.cnpj_gen(type: 'numeric') # => e.g. "65453043000178" +CnpjVal.cnpj_val('9JN7MGLJZXIO50') # => true +``` + +See [`cpf-utilities`](../cpf-utilities/README.md) and [`cnpj-utilities`](../cnpj-utilities/README.md) for full option and error details. + +### Mixing styles + +Use `BrUtils` where a shared configuration helps, and standalone components or helpers elsewhere — they are the same underlying classes: + +```ruby +require 'br-utilities' + +utils = BrUtils.new(cnpj: { validator: { type: 'numeric' } }) + +# Via façade +utils.cpf.format('12345678909') # => "123.456.789-09" + +# Via component returned by the façade +utils.cnpj.formatter.format('12ABC34500DE99') # => "12.ABC.345/00DE-99" + +# Via a separate component instance +BrUtils::CnpjFormatter.new.format('03603568000195') # => "03.603.568/0001-95" + +# Via functional helpers +CpfFmt.cpf_fmt('12345678909') # => "123.456.789-09" +CnpjVal.cnpj_val('98.765.432/0001-98') # => true +``` + +## API + +### Exports + +After `require 'br-utilities'`: + +- **`BrUtils`**: Façade class to create an instance with optional default CPF and CNPJ utils settings. +- **`BrUtils.cpf` / `.cnpj`**: Class helpers that forward to `BrUtils::DEFAULT` domain accessors. +- **`BrUtils::DEFAULT`**: Mutable pre-built `BrUtils` instance (same object the class helpers use). Process-wide / shared across threads — prefer `BrUtils.new` or per-call options under concurrency. +- **`BrUtils::VERSION`**: Gem version string. +- **Main-class shortcuts**: `BrUtils::CpfFormatter`, `BrUtils::CpfFormatterOptions`, `BrUtils::CpfGenerator`, `BrUtils::CpfGeneratorOptions`, `BrUtils::CpfValidator`, `BrUtils::CnpjFormatter`, `BrUtils::CnpjFormatterOptions`, `BrUtils::CnpjGenerator`, `BrUtils::CnpjGeneratorOptions`, `BrUtils::CnpjValidator`, `BrUtils::CnpjValidatorOptions` (same objects as the sibling classes). Error-marker shortcuts: `BrUtils::CpfFormatterError`, `BrUtils::CpfGeneratorError`, `BrUtils::CpfValidatorError`, `BrUtils::CnpjFormatterError`, `BrUtils::CnpjGeneratorError`, `BrUtils::CnpjValidatorError`. +- **Nested package modules**: `BrUtils::CpfUtils`, `BrUtils::CnpjUtils`, `BrUtils::CpfFmt`, `BrUtils::CpfGen`, `BrUtils::CpfVal`, `BrUtils::CnpjFmt`, `BrUtils::CnpjGen`, `BrUtils::CnpjVal` — full sibling surface (Options, helpers, errors, types). +- **Root sibling modules** (still supported): `CpfUtils`, `CnpjUtils`, `CpfFmt`, `CpfGen`, `CpfVal`, `CnpjFmt`, `CnpjGen`, `CnpjVal` — same objects as the nests. + +### Errors & Exceptions + +`BrUtils` defines only API-misuse errors for this gem’s argument rules. Domain errors are raised by the bundled packages and propagate unchanged. + +#### Defined by `br-utilities` + +Errors defined by this gem are **API misuse** only (wrong type or invalid argument combination). Every custom error includes the `BrUtils::Error` marker module. This gem defines **no** `BrUtils::DomainError` and no domain leaves — domain failures come only from the [bundled packages](#propagated-from-bundled-packages) and keep those packages’ namespaces (`CpfFmt::…`, `CnpjGen::…`, …). + +`rescue BrUtils::Error` catches **only** errors this gem raises. It does **not** catch component errors that propagate unchanged. + +##### Summary + +| Class | Inherits from | Category | Trigger condition | +|-------|---------------|----------|-------------------| +| `BrUtils::InvalidArgumentCombinationError` | `BrUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include BrUtils::Error`) | API misuse | Non-`nil` settings `Hash` passed together with any non-`nil` keyword argument | +| `BrUtils::TypeMismatchError` | `BrUtils::TypeMismatchError < TypeError < StandardError` (+ `include BrUtils::Error`) | API misuse | Non-`nil` `settings` argument to `BrUtils.new` is not a `Hash` | + +##### `BrUtils::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:** + +```ruby +rescue BrUtils::Error + # TypeMismatchError, InvalidArgumentCombinationError from this gem only + # (not CpfFmt::*, CnpjGen::*, or other bundled-package errors) +``` + +##### `BrUtils::TypeMismatchError` + +- **Inheritance:** `BrUtils::TypeMismatchError < TypeError < StandardError` (includes `BrUtils::Error`) +- **Category:** API misuse — the caller passed a value of the wrong type. +- **When it is raised:** Raised when `BrUtils.new` receives a non-`nil` `settings` argument that is not a `Hash`. +- **Example:** + +```ruby +BrUtils.new('not-a-hash') # raises BrUtils::TypeMismatchError +BrUtils.new(false) # raises BrUtils::TypeMismatchError (false is non-nil) +``` + +- **How to rescue it:** + +```ruby +rescue BrUtils::TypeMismatchError + # this gem's type-contract violation + +rescue TypeError + # native type errors, including this gem's TypeMismatchError +``` + +##### `BrUtils::InvalidArgumentCombinationError` + +- **Inheritance:** `BrUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (includes `BrUtils::Error`) +- **Category:** API misuse — the caller mixed mutually exclusive argument patterns. +- **When it is raised:** Raised when `BrUtils.new` receives both a non-`nil` settings `Hash` and any non-`nil` keyword argument (`cpf:`, `cnpj:`, `cpf_formatter:`, …) at the same time. +- **Example:** + +```ruby +BrUtils.new({ cpf: { formatter: { hidden: true } } }, cnpj: { formatter: { hidden: true } }) +# raises BrUtils::InvalidArgumentCombinationError +``` + +- **How to rescue it:** + +```ruby +rescue BrUtils::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). + +```ruby +require 'br-utilities' + +# 1) Single native class — catches misuse errors of that kind, +# including non-library ones already handled elsewhere in the consumer's code. +begin + BrUtils.new('not-a-hash') +rescue TypeError + # BrUtils::TypeMismatchError and any other TypeError (library or not) +end + +begin + BrUtils.new({ cpf: {} }, cnpj: CnpjUtils.new) +rescue ArgumentError + # BrUtils::InvalidArgumentCombinationError and any other ArgumentError (library or not) +end +``` + +```ruby +require 'br-utilities' + +# 2) BrUtils::DomainError — not applicable: this gem defines no DomainError +# (and no domain leaves). Domain failures come from bundled packages only. +# begin +# BrUtils.new(cpf: { formatter: { hidden_start: -1 } }) +# rescue BrUtils::DomainError # NameError — constant is not defined +# end +``` + +```ruby +require 'br-utilities' + +# 3) BrUtils::Error — catches everything this gem raises, regardless of native ancestry. +# Does not catch CpfFmt::*, CnpjGen::*, or other bundled-package errors. +begin + BrUtils.new('not-a-hash') +rescue BrUtils::Error + # every custom error that includes BrUtils::Error +end +``` + +```ruby +require 'br-utilities' + +# 4) Specific leaf class — catches only that exact failure mode. +begin + BrUtils.new('not-a-hash') +rescue BrUtils::TypeMismatchError + # only BrUtils::TypeMismatchError +end +``` + +#### Propagated from bundled packages + +`BrUtils` does not redefine domain exception types. Construction, setters, and domain method calls raise the same errors as [`cpf-utilities`](../cpf-utilities/README.md) and [`cnpj-utilities`](../cnpj-utilities/README.md): + +- **CPF formatting**: `CpfFmt::TypeMismatchError`, `CpfFmt::OutOfRangeError`, `CpfFmt::ValidationError`, `CpfFmt::InvalidLengthError` (passed to `on_fail`, not raised by `#format`), and related classes. +- **CPF generation**: `CpfGen::TypeMismatchError`, `CpfGen::ValidationError`, and related classes. +- **CPF validation**: `CpfVal::TypeMismatchError` and related classes. +- **CNPJ formatting**: `CnpjFmt::TypeMismatchError`, `CnpjFmt::OutOfRangeError`, `CnpjFmt::ValidationError`, `CnpjFmt::InvalidLengthError` (passed to `on_fail`), and related classes. +- **CNPJ generation**: `CnpjGen::TypeMismatchError`, `CnpjGen::ValidationError`, and related classes. +- **CNPJ validation**: `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. CPF and CNPJ validation failures return `false`. Formatting length failures are handled by **`on_fail`** (default returns an empty string). + +```ruby +require 'br-utilities' + +begin + BrUtils.new.cnpj.format(12_345) +rescue CnpjFmt::TypeMismatchError => e + puts e.message +end + +begin + BrUtils.new.cnpj.is_valid(12_345_678_000_198) +rescue CnpjVal::TypeMismatchError => e + puts e.message +end + +# Custom on_fail for invalid length +custom_fail = ->(value, _exception) { "Invalid: #{value}" } + +BrUtils.cpf.format('short', on_fail: custom_fail) # => "Invalid: short" +BrUtils.cnpj.format('short', on_fail: custom_fail) # => "Invalid: short" +BrUtils.cpf.format('short') # => "" (default on_fail) +``` + +For exhaustive exception lists and edge-case behavior, see each [bundled package](#bundled-packages) README. + +### Bundled packages + +| Package | Main resources | README | +|---------|----------------|--------| +| [`cpf-utilities`](https://rubygems.org/gems/cpf-utilities) | `CpfUtils`, `CpfFormatter`, `CpfGenerator`, `CpfValidator`, `CpfFmt.cpf_fmt`, `CpfGen.cpf_gen`, `CpfVal.cpf_val` | [docs](../cpf-utilities/README.md) | +| [`cnpj-utilities`](https://rubygems.org/gems/cnpj-utilities) | `CnpjUtils`, `CnpjFormatter`, `CnpjGenerator`, `CnpjValidator`, `CnpjFmt.cnpj_fmt`, `CnpjGen.cnpj_gen`, `CnpjVal.cnpj_val` | [docs](../cnpj-utilities/README.md) | + +All of the above are pulled in as dependencies of **`br-utilities`**. Interactive demos: [CPF](https://cpf-utils.vercel.app/) and [CNPJ](https://cnpj-utils.vercel.app/). + +## Contribution & Support + +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: + +- ⭐ Starring the repository +- 🤝 Contributing to the codebase +- 💡 [Suggesting new features](https://github.com/LacusSolutions/br-utils-ruby/issues) +- 🐛 [Reporting bugs](https://github.com/LacusSolutions/br-utils-ruby/issues) + +## License + +This project is licensed under the MIT License — see the [LICENSE](https://github.com/LacusSolutions/br-utils-ruby/blob/main/LICENSE) file for details. + +## Changelog + +See [CHANGELOG](./CHANGELOG.md) for a list of changes and version history. + +--- + +Made with ❤️ by [Lacus Solutions](https://github.com/LacusSolutions) From cd6b3eb72467305762bf9e3361dc69fad2fef196 Mon Sep 17 00:00:00 2001 From: juliolmuller Date: Mon, 10 Aug 2026 17:40:10 -0300 Subject: [PATCH 06/14] docs(br-utils): create Portuguese version of README file Co-authored-by: Claude Opus 5 Co-authored-by: Cursor Agent --- packages/br-utilities/README.pt.md | 700 +++++++++++++++++++++++++++++ 1 file changed, 700 insertions(+) create mode 100644 packages/br-utilities/README.pt.md diff --git a/packages/br-utilities/README.pt.md b/packages/br-utilities/README.pt.md new file mode 100644 index 0000000..40d50ac --- /dev/null +++ b/packages/br-utilities/README.pt.md @@ -0,0 +1,700 @@ +![br-utilities para Ruby](https://br-utils.vercel.app/img/cover_br-utils.jpg) + +> 🚀 **Suporte total ao [novo formato alfanumérico de CNPJ](https://github.com/user-attachments/files/23937961/calculodvcnpjalfanaumerico.pdf).** + +> 🌎 [Access documentation in English](./README.md) + +Kit em Ruby para as principais operações com dados brasileiros: CPF (Cadastro de Pessoa Física) e CNPJ (Cadastro Nacional da Pessoa Jurídica). Envolve [`cpf-utilities`](https://rubygems.org/gems/cpf-utilities) e [`cnpj-utilities`](https://rubygems.org/gems/cnpj-utilities) em uma única classe fachada (`BrUtils`). + +## Suporte a Ruby + +| ![Ruby 3.1](https://img.shields.io/badge/Ruby-3.1-CC342D?logo=ruby&logoColor=white) | ![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) | ![Ruby 4.0](https://img.shields.io/badge/Ruby-4.0-CC342D?logo=ruby&logoColor=white) | +| --- | --- | --- | --- | --- | +| Passing ✔ | Passing ✔ | Passing ✔ | Passing ✔ | Passing ✔ | + +Requer Ruby **≥ 3.1** (veja `required_ruby_version` no gemspec). + +## Recursos + +- ✅ **API unificada de alto nível**: Helpers de classe `BrUtils.cpf` / `.cnpj` alias de `BrUtils::DEFAULT`; cada domínio oferece `format`, `generate` e `is_valid` +- ✅ **Domínios empacotados**: [`cpf-utilities`](https://rubygems.org/gems/cpf-utilities) e [`cnpj-utilities`](https://rubygems.org/gems/cnpj-utilities) instalados juntos +- ✅ **CNPJ alfanumérico**: Suporte completo ao novo formato alfanumérico de CNPJ (a partir de 2026) +- ✅ **Instância reutilizável**: Classe `BrUtils` com configurações padrão opcionais de CPF e CNPJ (mapeamentos aninhados, kwargs planos de componentes ou instâncias prontas de utils) +- ✅ **Acesso em dois níveis**: Prefira atalhos de classes principais na raiz da fachada (`BrUtils::CpfFormatter`, `BrUtils::CnpjValidator`, …); Options, helpers e erros ficam nos módulos aninhados (`BrUtils::CpfFmt`, `BrUtils::CnpjUtils`, …). Os irmãos na raiz (`CpfUtils`, `CnpjUtils`, `CpfFmt`, …) continuam funcionando +- ✅ **Sobrescritas por chamada**: Configure padrões na fachada / utils de domínio; sobrescreva opções em uma única chamada de `format` / `generate` / `is_valid` +- ✅ **Tratamento de erros**: Erros de domínio propagam inalterados dos pacotes incluídos; esta gem define `BrUtils::TypeMismatchError` e `BrUtils::InvalidArgumentCombinationError` para uso indevido da API + +## Instalação + +Instale a gem diretamente: + +```bash +gem install br-utilities +``` + +Ou adicione ao seu `Gemfile` e execute `bundle install`: + +```ruby +gem 'br-utilities' +``` + +Isso instala **`br-utilities`** junto com [`cpf-utilities`](https://rubygems.org/gems/cpf-utilities) e [`cnpj-utilities`](https://rubygems.org/gems/cnpj-utilities) (que por sua vez trazem os pacotes componentes de CPF e CNPJ). Você **não** precisa de `gem install` / linhas `gem` separados para os pacotes de domínio ao usar **`br-utilities`**. + +## Require + +```ruby +require 'br-utilities' +``` + +## Início rápido + +Prefira os helpers de classe do agregador (`BrUtils.cpf` / `BrUtils.cnpj`) para chamadas pontuais — eles encaminham para `BrUtils::DEFAULT`: + +```ruby +require 'br-utilities' + +cpf = '12345678909' +cnpj = '03603568000195' + +# CPF (pessoa física) +BrUtils.cpf.format(cpf) # => "123.456.789-09" +BrUtils.cpf.generate(format: true) # => ex.: "478.442.410-55" +BrUtils.cpf.is_valid('123.456.789-09') # => true + +# CNPJ (pessoa jurídica) +BrUtils.cnpj.format(cnpj) # => "03.603.568/0001-95" +BrUtils.cnpj.generate(format: true) # => ex.: "AB.123.CDE/0001-55" +BrUtils.cnpj.is_valid('98765432000198') # => true +``` + +**Com agregadores de domínio:** + +```ruby +require 'br-utilities' + +cpf = '12345678909' +cnpj = '03603568000195' + +CpfUtils.format(cpf) # => "123.456.789-09" +CnpjUtils.format(cnpj) # => "03.603.568/0001-95" +CpfUtils.is_valid(cpf) # => true +CnpjUtils.is_valid(cnpj) # => true +``` + +**Com helpers funcionais** (módulos irmãos na raiz, carregados por esta gem): + +```ruby +require 'br-utilities' + +cpf = '12345678909' +cnpj = '03603568000195' + +CpfFmt.cpf_fmt(cpf) # => "123.456.789-09" +CpfVal.cpf_val(cpf) # => true +CnpjFmt.cnpj_fmt(cnpj) # => "03.603.568/0001-95" +CnpjVal.cnpj_val(cnpj) # => true +``` + +## Utilização + +Você pode trabalhar destas formas equivalentes: + +1. **`BrUtils.cpf` / `.cnpj`** — helpers de classe para chamadas rápidas (encaminham para `DEFAULT`). +2. **`BrUtils::DEFAULT`** — singleton compartilhado mutável (o mesmo objeto usado pelos helpers de classe; em todo o processo / não isolado por thread). +3. **`BrUtils.new`** — instância configurável com padrões compartilhados entre os domínios CPF e CNPJ. +4. **Agregadores de domínio** — `CpfUtils` / `CnpjUtils` (ou `BrUtils::CpfUtils` / `BrUtils::CnpjUtils`) diretamente. +5. **Classes principais sob `BrUtils`** — `BrUtils::CpfFormatter`, `BrUtils::CnpjGenerator` e atalhos relacionados. +6. **Módulos aninhados do pacote** — Options, helpers, erros e tipos via `BrUtils::CpfFmt` / `CpfGen` / `CpfVal` / `CnpjFmt` / `CnpjGen` / `CnpjVal` / `CpfUtils` / `CnpjUtils`. +7. **Módulos irmãos na raiz** (ainda suportados) — `CpfFmt`, `CnpjUtils` e demais inalterados. + +Todas as abordagens expõem as mesmas opções e comportamento dentro de cada domínio. Para tabelas de opções exaustivas e detalhes específicos de cada componente, consulte o README de cada [pacote incluído](#pacotes-incluídos). + +### Helpers de classe (`BrUtils.cpf` / `.cnpj`) + +Esses métodos de classe retornam as mesmas instâncias de utils de domínio que `BrUtils::DEFAULT`. Prefira-os para chamadas pontuais: + +```ruby +BrUtils.cpf.format('12345678909') +BrUtils.cpf.generate(format: true) +BrUtils.cpf.is_valid('12345678909') + +BrUtils.cnpj.format('03603568000195') +BrUtils.cnpj.generate(type: 'numeric') +BrUtils.cnpj.is_valid('98765432000198') +``` + +### `BrUtils::DEFAULT` (instância padrão) + +`BrUtils::DEFAULT` é o singleton pré-construído e **mutável** por trás dos helpers de classe (paridade com a exportação padrão do JS / `br_utils` do Python). Sua configuração é **em todo o processo e compartilhada entre threads**: mutá-lo (ex.: `DEFAULT.cpf = …`) afeta chamadas subsequentes de `BrUtils.cpf` / `.cnpj` para todos os callers no processo. Prefira `BrUtils.new` ou opções por chamada para trabalho concorrente ou isolado; instâncias customizadas permanecem independentes de `DEFAULT`: + +```ruby +BrUtils::DEFAULT.cpf = CpfUtils.new(formatter: { dash_key: '|' }) +BrUtils.cpf.format('12345678909') # => "123.456.789|09" + +custom = BrUtils.new +custom.cpf.format('12345678909') # => "123.456.789-09" (não afetado) +``` + +### `BrUtils` (classe) + +Para utils de CPF ou CNPJ padrão customizados, crie sua própria instância: + +```ruby +require 'br-utilities' + +utils = BrUtils.new( + cpf: { + formatter: { hidden: true, hidden_key: '#' }, + generator: { format: true } + }, + cnpj: { + formatter: { hidden: true }, + generator: { type: 'numeric', format: true }, + validator: { type: 'numeric' } + } +) + +utils.cpf.format('12345678909') # => "123.###.###-##" +utils.cpf.generate # => ex.: "005.265.352-88" +utils.cnpj.format('03603568000195') # => "03.603.***/****-**" +utils.cnpj.generate # => ex.: "73.008.535/0005-06" + +# Acessar ou substituir instâncias internas de domínio +utils.cpf # => CpfUtils +utils.cnpj # => CnpjUtils +``` + +- **`BrUtils.new(settings = nil, **keywords)`**: Configurações opcionais. Passe um `Hash` de settings com chaves `:cpf` e/ou `:cnpj`, **ou** as mesmas chaves (mais kwargs planos de componentes) como argumentos nomeados — não ambos (passar ambos lança `BrUtils::InvalidArgumentCombinationError`). + - **`:cpf` / `:cnpj`**: Uma instância pronta de `CpfUtils` / `CnpjUtils` **ou** um `Hash` de configuração repassado ao construtor do utils correspondente. Dentro desse `Hash`, cada chave de recurso (`:formatter`, `:generator` e `:validator` para CNPJ) aceita um objeto de opções ou um mapeamento de valores de opção. + - **`:cpf_formatter`**, **`:cpf_generator`**, **`:cnpj_formatter`**, **`:cnpj_generator`**, **`:cnpj_validator`**: Argumentos planos de conveniência quando apenas componentes individuais precisam de customização. São ignorados quando o argumento `:cpf` ou `:cnpj` correspondente é fornecido. +- **`#cpf`**, **`#cnpj`**: Acessores (getters e setters) das instâncias de utils de domínio. Os setters aceitam uma instância de utils, um `Hash` de configuração ou `nil` para voltar aos padrões (substitui a instância inteira; não faz merge). + +Opções planas no construtor (alternativa aos mapeamentos aninhados `:cpf` / `:cnpj`): + +```ruby +require 'br-utilities' + +utils = BrUtils.new( + cpf_formatter: CpfFmt::CpfFormatterOptions.new(hidden: true, hidden_key: '#'), + cpf_generator: CpfGen::CpfGeneratorOptions.new(format: true), + cnpj_formatter: CnpjFmt::CnpjFormatterOptions.new(hidden: true, hidden_key: '#'), + cnpj_generator: CnpjGen::CnpjGeneratorOptions.new(format: true, type: 'numeric'), + cnpj_validator: CnpjVal::CnpjValidatorOptions.new(type: 'numeric') +) +``` + +Passar um `Hash` de settings posicional junto com qualquer palavra-chave lança: + +```ruby +BrUtils.new({ cpf: {} }, cnpj: CnpjUtils.new) +# lança BrUtils::InvalidArgumentCombinationError +``` + +### Padrões da instância e sobrescritas por chamada + +```ruby +require 'br-utilities' + +utils = BrUtils.new( + cpf: { + formatter: { hidden: true, hidden_key: '#' }, + generator: { format: true } + }, + cnpj: { + formatter: { hidden: true, hidden_key: '#' }, + generator: { format: true }, + validator: { type: 'numeric' } + } +) + +cpf = '12345678909' +cnpj = '03603568000195' + +utils.cpf.format(cpf) # => "123.###.###-##" +utils.cpf.format(cpf, hidden: false) # só nesta chamada: sem máscara +utils.cpf.generate(format: false) # só nesta chamada: saída compacta + +utils.cnpj.format(cnpj) # => "03.603.###/####-##" +utils.cnpj.format(cnpj, hidden: false) # só nesta chamada: sem máscara +utils.cnpj.is_valid('1QB5UKALPYFP59') # => false (validador da instância é só numérico) +utils.cnpj.is_valid( # => true nesta chamada + '1QB5UKALPYFP59', + type: 'alphanumeric' +) +``` + +Passar uma instância de `CnpjFmt::CnpjFormatterOptions`, `CnpjGen::CnpjGeneratorOptions` ou `CnpjVal::CnpjValidatorOptions` ao construtor de `BrUtils` armazena esse objeto por referência — mutá-lo depois afeta chamadas subsequentes sem sobrescrita por chamada. + +Para alterar uma única opção aninhada sem substituir o utils de domínio inteiro, mute via os acessores de domínio (ex.: `utils.cpf.formatter.options.hidden = true`). + +### Operações de CPF + +Os métodos de CPF são acessados via `BrUtils.cpf`, `utils.cpf`, `CpfUtils` ou os helpers `CpfFmt` / `CpfGen` / `CpfVal`. O CPF usa a API de [`cpf-utilities`](../cpf-utilities/README.pt.md). + +#### Formatação (`#format` / `CpfFmt.cpf_fmt`) + +| Opção | Tipo | Padrão | Descrição | +|--------|------|---------|-------------| +| `hidden` | `Boolean` | `false` | Se `true`, mascara dígitos entre `hidden_start` e `hidden_end` com `hidden_key` | +| `hidden_key` | `String` | `'*'` | Caractere(s) usados para substituir dígitos mascarados | +| `hidden_start` | `Integer` | `3` | Índice inicial (0–10, inclusivo) do intervalo a ocultar | +| `hidden_end` | `Integer` | `10` | Índice final (0–10, inclusivo) do intervalo a ocultar | +| `dot_key` | `String` | `'.'` | Delimitador de ponto (ex.: em `123.456.789`) | +| `dash_key` | `String` | `'-'` | Delimitador de hífen (ex.: antes dos dígitos verificadores `…-09`) | +| `escape` | `Boolean` | `false` | Se `true`, escapa caracteres especiais HTML no resultado | +| `encode` | `Boolean` | `false` | Se `true`, codifica o resultado para URL (similar ao `encodeURIComponent` do JavaScript) | +| `on_fail` | `Proc` / invocável | retorna `''` | Callback quando o tamanho da entrada sanitizada ≠ 11; o retorno é usado como resultado | + +O **`on_fail`** padrão retorna uma string vazia. Comprimento inválido **não** lança exceção em `#format`. + +```ruby +require 'br-utilities' + +cpf = '12345678909' + +BrUtils.cpf.format(cpf) # => "123.456.789-09" +BrUtils.cpf.format(cpf, hidden: true, hidden_key: '#') # => "123.###.###-##" +BrUtils.cpf.format(cpf, dot_key: '', dash_key: '_') # => "123456789_09" + +CpfFmt.cpf_fmt(cpf, hidden: true) # => "123.***.***-**" +``` + +#### Geração (`#generate` / `CpfGen.cpf_gen`) + +| Opção | Tipo | Padrão | Descrição | +|--------|------|---------|-------------| +| `format` | `Boolean` | `false` | Se `true`, retorna o CPF gerado no formato padrão (`000.000.000-00`) | +| `prefix` | `String` | `''` | String parcial inicial (0–9 dígitos). Não dígitos são removidos; caracteres faltantes são gerados e os dígitos verificadores calculados. Prefixos com mais de 9 dígitos são truncados silenciosamente. | + +Regras de prefixo: a base (primeiros 9 dígitos) não pode ser toda zeros; 9 dígitos repetidos (ex.: `999999999`) não são permitidos. + +```ruby +require 'br-utilities' + +BrUtils.cpf.generate # => ex.: "11508890048" +BrUtils.cpf.generate(format: true) # => ex.: "661.134.831-00" +BrUtils.cpf.generate(prefix: '123456789') # => "12345678909" +CpfGen.cpf_gen(prefix: '123456789', format: true) # => "123.456.789-09" +``` + +#### Validação (`#is_valid` / `CpfVal.cpf_val`) + +Aceita CPF formatado ou não (ou um `Array` de strings). Retorna **`true`** ou **`false`** sem lançar exceção para CPF inválido. Não há opções de validador. + +```ruby +require 'br-utilities' + +BrUtils.cpf.is_valid('12345678909') # => true +BrUtils.cpf.is_valid('123.456.789-09') # => true +BrUtils.cpf.is_valid('12345678900') # => false +CpfVal.cpf_val('12345678909') # => true +``` + +### Operações de CNPJ + +Os métodos de CNPJ são acessados via `BrUtils.cnpj`, `utils.cnpj`, `CnpjUtils` ou os helpers `CnpjFmt` / `CnpjGen` / `CnpjVal`. O CNPJ usa a API de [`cnpj-utilities`](../cnpj-utilities/README.pt.md). + +#### Formatação (`#format` / `CnpjFmt.cnpj_fmt`) + +| Opção | Tipo | Padrão | Descrição | +|--------|------|---------|-------------| +| `hidden` | `Boolean` | `false` | Se `true`, mascara caracteres entre `hidden_start` e `hidden_end` com `hidden_key` | +| `hidden_key` | `String` | `'*'` | Caractere(s) usados para substituir caracteres mascarados | +| `hidden_start` | `Integer` | `5` | Índice inicial (0–13, inclusivo) do intervalo a ocultar | +| `hidden_end` | `Integer` | `13` | Índice final (0–13, inclusivo) do intervalo a ocultar | +| `dot_key` | `String` | `'.'` | Delimitador de ponto (ex.: em `12.345.678`) | +| `slash_key` | `String` | `'/'` | Delimitador de barra (ex.: antes da filial `…/0001-90`) | +| `dash_key` | `String` | `'-'` | Delimitador de hífen (ex.: antes dos dígitos verificadores `…-90`) | +| `escape` | `Boolean` | `false` | Se `true`, escapa caracteres especiais HTML no resultado | +| `encode` | `Boolean` | `false` | Se `true`, codifica o resultado para URL (similar ao `encodeURIComponent` do JavaScript) | +| `on_fail` | `Proc` / invocável | retorna `''` | Callback quando o tamanho da entrada sanitizada ≠ 14; o retorno é usado como resultado | + +O **`on_fail`** padrão retorna uma string vazia. Tipos de entrada incorretos lançam **`CnpjFmt::TypeMismatchError`**. + +```ruby +require 'br-utilities' + +cnpj = '03603568000195' + +BrUtils.cnpj.format(cnpj) # => "03.603.568/0001-95" +BrUtils.cnpj.format('12ABC34500DE99') # => "12.ABC.345/00DE-99" +BrUtils.cnpj.format( # => "03.603.###/####-##" + cnpj, + hidden: true, + hidden_key: '#' +) +BrUtils.cnpj.format( # => "03603568|0001_95" + cnpj, + dot_key: '', + slash_key: '|', + dash_key: '_' +) + +CnpjFmt.cnpj_fmt(cnpj) # => "03.603.568/0001-95" +``` + +#### Geração (`#generate` / `CnpjGen.cnpj_gen`) + +| Opção | Tipo | Padrão | Descrição | +|--------|------|---------|-------------| +| `format` | `Boolean` | `false` | Se `true`, retorna o CNPJ gerado no formato padrão (`00.000.000/0000-00`) | +| `prefix` | `String` | `''` | String parcial inicial (0–12 caracteres alfanuméricos). Caracteres faltantes são gerados e os dígitos verificadores calculados. | +| `type` | `String` | `'alphanumeric'` | Conjunto de caracteres para a parte gerada aleatoriamente: `'numeric'`, `'alphabetic'` ou `'alphanumeric'`. **Os dígitos verificadores são sempre numéricos.** | + +Regras de prefixo: o ID base (primeiros 8 caracteres) e o ID da filial (caracteres 9–12) não podem ser todos zeros; 12 dígitos repetidos (ex.: `111111111111`) também não são permitidos. + +```ruby +require 'br-utilities' + +BrUtils.cnpj.generate # => ex.: "1GJTR3J3XSSA96" +BrUtils.cnpj.generate(format: true) # => ex.: "V1.J0V.8WE/DVZ7-50" +BrUtils.cnpj.generate( # => ex.: "12345678855883" + prefix: '12345678', + type: 'numeric' +) +CnpjGen.cnpj_gen(type: 'numeric') # => ex.: "65453043000178" +``` + +#### Validação (`#is_valid` / `CnpjVal.cnpj_val`) + +| Opção | Tipo | Padrão | Descrição | +|--------|------|---------|-------------| +| `case_sensitive` | `Boolean` | `true` | Se `false`, letras minúsculas são aceitas para CNPJ alfanumérico (a entrada é convertida para maiúsculas antes da validação). | +| `type` | `String` | `'alphanumeric'` | `'numeric'`: apenas dígitos (0–9); `'alphanumeric'`: dígitos e letras (0–9, A–Z). | + +```ruby +require 'br-utilities' + +BrUtils.cnpj.is_valid('98765432000198') # => true +BrUtils.cnpj.is_valid('98765432000199') # => false +BrUtils.cnpj.is_valid('1QB5UKALPYFP59') # => true +BrUtils.cnpj.is_valid('1QB5UKALpyfp59') # => false +BrUtils.cnpj.is_valid( # => true + '1QB5UKALpyfp59', + case_sensitive: false +) +BrUtils.cnpj.is_valid( # => false + '1QB5UKALPYFP59', + type: 'numeric' +) + +CnpjVal.cnpj_val('98765432000198') # => true +CnpjVal.cnpj_val('1QB5UKALpyfp59', case_sensitive: false) # => true +CnpjVal.cnpj_val('1QB5UKALPYFP59', type: 'numeric') # => false +``` + +CNPJ inválido retorna **`false`** sem lançar exceção. Tipos de entrada incorretos lançam **`CnpjVal::TypeMismatchError`**. + +### Agregadores de domínio (isolados) + +Use `CpfUtils` ou `CnpjUtils` diretamente quando precisar de apenas um domínio: + +```ruby +require 'br-utilities' + +cpf_utils = CpfUtils.new( + formatter: { hidden: true }, + generator: { format: true } +) + +cnpj_utils = CnpjUtils.new( + formatter: { hidden: true }, + generator: { format: true }, + validator: { type: 'numeric' } +) + +cpf_utils.format('12345678909') # => "123.***.***-**" +cnpj_utils.format('03603568000195') # => "03.603.***/****-**" +``` + +### Acessando componentes + +Cada agregador de domínio expõe seu formatador, gerador e validador internos: + +```ruby +require 'br-utilities' + +utils = BrUtils.new + +utils.cpf.formatter.format('12345678909', hidden: true) # => "123.***.***-**" +utils.cpf.generator.generate(format: true) # => ex.: "545.507.690-68" +utils.cpf.validator.is_valid('12345678909') # => true + +utils.cnpj.formatter.format('12ABC34500DE99') # => "12.ABC.345/00DE-99" +utils.cnpj.generator.generate(format: true) # => ex.: "8O.BE5.2KL/UI0Y-06" +utils.cnpj.validator.is_valid('03603568000195') # => true +``` + +### Usando classes componentes e módulos aninhados + +Caminhos preferidos após `require 'br-utilities'`: + +```ruby +require 'br-utilities' + +# Classes principais na raiz da fachada +formatter = BrUtils::CpfFormatter.new(hidden: true) +generator = BrUtils::CnpjGenerator.new(type: 'numeric') +validator = BrUtils::CnpjValidator.new + +formatter.format('12345678909') # => "123.***.***-**" + +# Options, helpers e erros nos módulos aninhados do pacote +options = BrUtils::CpfFmt::CpfFormatterOptions.new(dash_key: '|') +BrUtils::CpfFmt.cpf_fmt('12345678909') # => "123.456.789-09" + +begin + BrUtils::CnpjFmt.cnpj_fmt(12_345) +rescue BrUtils::CnpjFmt::TypeMismatchError + # tipo de entrada incorreto +end +``` + +Os irmãos na raiz continuam suportados (os mesmos objetos dos nests): + +```ruby +CpfFmt.cpf_fmt('12345678909', dash_key: '|') # => "123.456.789|09" +CpfGen.cpf_gen(format: true) # => ex.: "478.442.410-55" +CpfVal.cpf_val('12345678909') # => true +CnpjFmt.cnpj_fmt('01ABC234000X56', slash_key: '|') # => "01.ABC.234|000X-56" +CnpjGen.cnpj_gen(type: 'numeric') # => ex.: "65453043000178" +CnpjVal.cnpj_val('9JN7MGLJZXIO50') # => true +``` + +Consulte [`cpf-utilities`](../cpf-utilities/README.pt.md) e [`cnpj-utilities`](../cnpj-utilities/README.pt.md) para detalhes completos de opções e erros. + +### Misturando estilos + +Use `BrUtils` onde uma configuração compartilhada ajuda, e componentes ou helpers isolados em outros pontos — são as mesmas classes subjacentes: + +```ruby +require 'br-utilities' + +utils = BrUtils.new(cnpj: { validator: { type: 'numeric' } }) + +# Via fachada +utils.cpf.format('12345678909') # => "123.456.789-09" + +# Via componente retornado pela fachada +utils.cnpj.formatter.format('12ABC34500DE99') # => "12.ABC.345/00DE-99" + +# Via instância de componente separada +BrUtils::CnpjFormatter.new.format('03603568000195') # => "03.603.568/0001-95" + +# Via helpers funcionais +CpfFmt.cpf_fmt('12345678909') # => "123.456.789-09" +CnpjVal.cnpj_val('98.765.432/0001-98') # => true +``` + +## API + +### Exportações + +Após `require 'br-utilities'`: + +- **`BrUtils`**: Classe fachada para criar uma instância com configurações opcionais dos utils de CPF e CNPJ. +- **`BrUtils.cpf` / `.cnpj`**: Helpers de classe que encaminham para os acessores de domínio de `BrUtils::DEFAULT`. +- **`BrUtils::DEFAULT`**: Instância pré-construída e mutável de `BrUtils` (o mesmo objeto usado pelos helpers de classe). Em todo o processo / compartilhada entre threads — prefira `BrUtils.new` ou opções por chamada sob concorrência. +- **`BrUtils::VERSION`**: String da versão da gem. +- **Atalhos de classes principais**: `BrUtils::CpfFormatter`, `BrUtils::CpfFormatterOptions`, `BrUtils::CpfGenerator`, `BrUtils::CpfGeneratorOptions`, `BrUtils::CpfValidator`, `BrUtils::CnpjFormatter`, `BrUtils::CnpjFormatterOptions`, `BrUtils::CnpjGenerator`, `BrUtils::CnpjGeneratorOptions`, `BrUtils::CnpjValidator`, `BrUtils::CnpjValidatorOptions` (os mesmos objetos das classes irmãs). Atalhos de marcadores de erro: `BrUtils::CpfFormatterError`, `BrUtils::CpfGeneratorError`, `BrUtils::CpfValidatorError`, `BrUtils::CnpjFormatterError`, `BrUtils::CnpjGeneratorError`, `BrUtils::CnpjValidatorError`. +- **Módulos aninhados do pacote**: `BrUtils::CpfUtils`, `BrUtils::CnpjUtils`, `BrUtils::CpfFmt`, `BrUtils::CpfGen`, `BrUtils::CpfVal`, `BrUtils::CnpjFmt`, `BrUtils::CnpjGen`, `BrUtils::CnpjVal` — superfície completa dos irmãos (Options, helpers, erros, tipos). +- **Módulos irmãos na raiz** (ainda suportados): `CpfUtils`, `CnpjUtils`, `CpfFmt`, `CpfGen`, `CpfVal`, `CnpjFmt`, `CnpjGen`, `CnpjVal` — os mesmos objetos dos nests. + +### Erros e exceções + +`BrUtils` define apenas erros de uso indevido da API para as regras de argumentos desta gem. Erros de domínio são lançados pelos pacotes incluídos e propagam inalterados. + +#### Definidos por `br-utilities` + +Os erros definidos por esta gem são **apenas uso indevido da API** (tipo errado ou combinação inválida de argumentos). Todo erro customizado inclui o módulo marcador `BrUtils::Error`. Esta gem **não** define `BrUtils::DomainError` nem folhas de domínio — falhas de domínio vêm apenas dos [pacotes incluídos](#propagados-dos-pacotes-incluídos) e mantêm os namespaces desses pacotes (`CpfFmt::…`, `CnpjGen::…`, …). + +`rescue BrUtils::Error` captura **apenas** erros que esta gem lança. **Não** captura erros de componentes que propagam inalterados. + +##### Resumo + +| Classe | Herda de | Categoria | Condição de disparo | +|-------|---------------|----------|-------------------| +| `BrUtils::InvalidArgumentCombinationError` | `BrUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include BrUtils::Error`) | Uso indevido da API | `Hash` de settings não-`nil` passado junto com qualquer argumento nomeado não-`nil` | +| `BrUtils::TypeMismatchError` | `BrUtils::TypeMismatchError < TypeError < StandardError` (+ `include BrUtils::Error`) | Uso indevido da API | Argumento `settings` não-`nil` em `BrUtils.new` não é um `Hash` | + +##### `BrUtils::Error` (módulo marcador) + +- **Herança:** módulo marcador misturado em todo erro customizado que esta gem lança via `include` (não é uma classe). +- **Categoria:** N/A (apenas alvo de rescue) — não é um modo de falha por si só. +- **Quando é lançado:** Nunca lançado diretamente; incluído por todo erro customizado que esta gem lança. +- **Exemplo:** N/A +- **Como resgatar:** + +```ruby +rescue BrUtils::Error + # TypeMismatchError, InvalidArgumentCombinationError apenas desta gem + # (não CpfFmt::*, CnpjGen::* ou outros erros dos pacotes incluídos) +``` + +##### `BrUtils::TypeMismatchError` + +- **Herança:** `BrUtils::TypeMismatchError < TypeError < StandardError` (inclui `BrUtils::Error`) +- **Categoria:** Uso indevido da API — o caller passou um valor do tipo errado. +- **Quando é lançado:** Quando `BrUtils.new` recebe um argumento `settings` não-`nil` que não é um `Hash`. +- **Exemplo:** + +```ruby +BrUtils.new('not-a-hash') # lança BrUtils::TypeMismatchError +BrUtils.new(false) # lança BrUtils::TypeMismatchError (false é não-nil) +``` + +- **Como resgatar:** + +```ruby +rescue BrUtils::TypeMismatchError + # violação de contrato de tipo desta gem + +rescue TypeError + # erros nativos de tipo, incluindo TypeMismatchError desta gem +``` + +##### `BrUtils::InvalidArgumentCombinationError` + +- **Herança:** `BrUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (inclui `BrUtils::Error`) +- **Categoria:** Uso indevido da API — o caller misturou padrões de argumentos mutuamente exclusivos. +- **Quando é lançado:** Quando `BrUtils.new` recebe um `Hash` de settings não-`nil` e qualquer argumento nomeado não-`nil` (`cpf:`, `cnpj:`, `cpf_formatter:`, …) ao mesmo tempo. +- **Exemplo:** + +```ruby +BrUtils.new({ cpf: { formatter: { hidden: true } } }, cnpj: { formatter: { hidden: true } }) +# lança BrUtils::InvalidArgumentCombinationError +``` + +- **Como resgatar:** + +```ruby +rescue BrUtils::InvalidArgumentCombinationError + # combinação inválida de assinatura desta gem + +rescue ArgumentError + # erros nativos de argumento, incluindo InvalidArgumentCombinationError desta gem +``` + +##### Granularidade de rescue + +Cada nível é mostrado como seu próprio exemplo isolado (não os una em uma única escada de `rescue` — um handler nativo amplo tornaria cláusulas mais estreitas inalcançáveis). + +```ruby +require 'br-utilities' + +# 1) Classe nativa única — captura erros de uso indevido desse tipo, +# incluindo os não-biblioteca já tratados em outro ponto do código do consumidor. +begin + BrUtils.new('not-a-hash') +rescue TypeError + # BrUtils::TypeMismatchError e qualquer outro TypeError (biblioteca ou não) +end + +begin + BrUtils.new({ cpf: {} }, cnpj: CnpjUtils.new) +rescue ArgumentError + # BrUtils::InvalidArgumentCombinationError e qualquer outro ArgumentError (biblioteca ou não) +end +``` + +```ruby +require 'br-utilities' + +# 2) BrUtils::DomainError — não aplicável: esta gem não define DomainError +# (nem folhas de domínio). Falhas de domínio vêm apenas dos pacotes incluídos. +# begin +# BrUtils.new(cpf: { formatter: { hidden_start: -1 } }) +# rescue BrUtils::DomainError # NameError — constante não definida +# end +``` + +```ruby +require 'br-utilities' + +# 3) BrUtils::Error — captura tudo que esta gem lança, independentemente da ancestralidade nativa. +# Não captura CpfFmt::*, CnpjGen::* ou outros erros dos pacotes incluídos. +begin + BrUtils.new('not-a-hash') +rescue BrUtils::Error + # todo erro customizado que inclui BrUtils::Error +end +``` + +```ruby +require 'br-utilities' + +# 4) Classe folha específica — captura apenas aquele modo de falha exato. +begin + BrUtils.new('not-a-hash') +rescue BrUtils::TypeMismatchError + # apenas BrUtils::TypeMismatchError +end +``` + +#### Propagados dos pacotes incluídos + +`BrUtils` não redefine tipos de exceção de domínio. Construção, setters e chamadas de métodos de domínio lançam os mesmos erros de [`cpf-utilities`](../cpf-utilities/README.pt.md) e [`cnpj-utilities`](../cnpj-utilities/README.pt.md): + +- **Formatação de CPF**: `CpfFmt::TypeMismatchError`, `CpfFmt::OutOfRangeError`, `CpfFmt::ValidationError`, `CpfFmt::InvalidLengthError` (passado a `on_fail`, não lançado por `#format`) e classes relacionadas. +- **Geração de CPF**: `CpfGen::TypeMismatchError`, `CpfGen::ValidationError` e classes relacionadas. +- **Validação de CPF**: `CpfVal::TypeMismatchError` e classes relacionadas. +- **Formatação de CNPJ**: `CnpjFmt::TypeMismatchError`, `CnpjFmt::OutOfRangeError`, `CnpjFmt::ValidationError`, `CnpjFmt::InvalidLengthError` (passado a `on_fail`) e classes relacionadas. +- **Geração de CNPJ**: `CnpjGen::TypeMismatchError`, `CnpjGen::ValidationError` e classes relacionadas. +- **Validação de CNPJ**: `CnpjVal::TypeMismatchError`, `CnpjVal::ValidationError` e classes relacionadas. + +Tipos de opção inválidos são tipicamente subclasses de **`TypeError`** (`*::TypeMismatchError`); valores de opção inválidos são erros de domínio sob a hierarquia `DomainError` de cada pacote. Falhas de validação de CPF e CNPJ retornam `false`. Falhas de comprimento na formatação são tratadas por **`on_fail`** (padrão retorna string vazia). + +```ruby +require 'br-utilities' + +begin + BrUtils.new.cnpj.format(12_345) +rescue CnpjFmt::TypeMismatchError => e + puts e.message +end + +begin + BrUtils.new.cnpj.is_valid(12_345_678_000_198) +rescue CnpjVal::TypeMismatchError => e + puts e.message +end + +# on_fail customizado para comprimento inválido +custom_fail = ->(value, _exception) { "Invalid: #{value}" } + +BrUtils.cpf.format('short', on_fail: custom_fail) # => "Invalid: short" +BrUtils.cnpj.format('short', on_fail: custom_fail) # => "Invalid: short" +BrUtils.cpf.format('short') # => "" (on_fail padrão) +``` + +Para listas exaustivas de exceções e comportamento em casos extremos, consulte o README de cada [pacote incluído](#pacotes-incluídos). + +### Pacotes incluídos + +| Pacote | Principais recursos | README | +|---------|----------------|--------| +| [`cpf-utilities`](https://rubygems.org/gems/cpf-utilities) | `CpfUtils`, `CpfFormatter`, `CpfGenerator`, `CpfValidator`, `CpfFmt.cpf_fmt`, `CpfGen.cpf_gen`, `CpfVal.cpf_val` | [docs](../cpf-utilities/README.pt.md) | +| [`cnpj-utilities`](https://rubygems.org/gems/cnpj-utilities) | `CnpjUtils`, `CnpjFormatter`, `CnpjGenerator`, `CnpjValidator`, `CnpjFmt.cnpj_fmt`, `CnpjGen.cnpj_gen`, `CnpjVal.cnpj_val` | [docs](../cnpj-utilities/README.pt.md) | + +Todos os acima são puxados como dependências de **`br-utilities`**. Demos interativas: [CPF](https://cpf-utils.vercel.app/) e [CNPJ](https://cnpj-utils.vercel.app/). + +## Contribuição e suporte + +Contribuições são bem-vindas! Consulte as [Diretrizes de contribuição](https://github.com/LacusSolutions/br-utils-ruby/blob/main/CONTRIBUTING.md). Se o projeto for útil para você, considere: + +- ⭐ Dar uma estrela no repositório +- 🤝 Contribuir com código +- 💡 [Sugerir novas funcionalidades](https://github.com/LacusSolutions/br-utils-ruby/issues) +- 🐛 [Reportar bugs](https://github.com/LacusSolutions/br-utils-ruby/issues) + +## Licença + +Este projeto está sob a licença MIT — veja o arquivo [LICENSE](https://github.com/LacusSolutions/br-utils-ruby/blob/main/LICENSE). + +## Changelog + +Veja o [CHANGELOG](./CHANGELOG.md) para alterações e histórico de versões. + +--- + +Feito com ❤️ por [Lacus Solutions](https://github.com/LacusSolutions) From a199a1fe0b1cbf1c1597cc3088746e11d5a9b3af Mon Sep 17 00:00:00 2001 From: juliolmuller Date: Mon, 10 Aug 2026 17:42:11 -0300 Subject: [PATCH 07/14] docs: copy `br-utilities_ docs to project root --- README.md | 735 +++++++++++++++++++++++++++++++++++++++++++++++---- README.pt.md | 700 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1383 insertions(+), 52 deletions(-) create mode 100644 README.pt.md diff --git a/README.md b/README.md index 868d39c..abc1e1d 100644 --- a/README.md +++ b/README.md @@ -1,76 +1,707 @@ -![br-utils for Ruby](https://br-utils.vercel.app/img/cover_br-utils.jpg) +![br-utilities for Ruby](https://br-utils.vercel.app/img/cover_br-utils.jpg) -Brazilian data utilities (CPF, CNPJ, etc.) as a **multi-gem monorepo**, publishable to RubyGems with independent versioning and GitHub Actions (Trusted Publishing / OIDC). +[![Gem Version](https://img.shields.io/gem/v/br-utilities)](https://rubygems.org/gems/br-utilities) +[![Gem Downloads](https://img.shields.io/gem/dt/br-utilities)](https://rubygems.org/gems/br-utilities) +[![Ruby Version](https://img.shields.io/gem/rv/br-utilities)](https://www.ruby-lang.org/) +[![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) +[![Last Update Date](https://img.shields.io/github/last-commit/LacusSolutions/br-utils-ruby)](https://github.com/LacusSolutions/br-utils-ruby) +[![Project License](https://img.shields.io/github/license/LacusSolutions/br-utils-ruby)](https://github.com/LacusSolutions/br-utils-ruby/blob/main/LICENSE) -## Structure +> 🚀 **Full support for the [new alphanumeric CNPJ format](https://github.com/user-attachments/files/23937961/calculodvcnpjalfanaumerico.pdf).** -- **Root**: Tooling (Rake, RuboCop), shared config in `config/gems.yml`, no app code. -- **Packages**: Under `packages/` — each is a gem (e.g. `cpf-dv`, `cpf-utilities`, `br-utilities`). Internal dependencies use path in development and version constraints when published. +> 🌎 [Acessar documentação em português](./README.pt.md) -See [CONTRIBUTING.md](CONTRIBUTING.md) for folder layout, tagging, dependency resolution, and development workflow. +A Ruby toolkit to handle the main operations with Brazilian-related data: CPF (Individual's Taxpayer ID) and CNPJ (Business Tax ID). It wraps [`cpf-utilities`](https://rubygems.org/gems/cpf-utilities) and [`cnpj-utilities`](https://rubygems.org/gems/cnpj-utilities) in a single façade class (`BrUtils`). -## Local setup +## Ruby Support + +| ![Ruby 3.1](https://img.shields.io/badge/Ruby-3.1-CC342D?logo=ruby&logoColor=white) | ![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) | ![Ruby 4.0](https://img.shields.io/badge/Ruby-4.0-CC342D?logo=ruby&logoColor=white) | +| --- | --- | --- | --- | --- | +| Passing ✔ | Passing ✔ | Passing ✔ | Passing ✔ | Passing ✔ | + +Requires Ruby **≥ 3.1** (see `required_ruby_version` in the gemspec). + +## Features + +- ✅ **Unified top-level API**: Class helpers `BrUtils.cpf` / `.cnpj` alias `BrUtils::DEFAULT`; each domain offers `format`, `generate`, and `is_valid` +- ✅ **Bundled domains**: [`cpf-utilities`](https://rubygems.org/gems/cpf-utilities) and [`cnpj-utilities`](https://rubygems.org/gems/cnpj-utilities) installed together +- ✅ **Alphanumeric CNPJ**: Full support for the new alphanumeric CNPJ format (introduced in 2026) +- ✅ **Reusable instance**: `BrUtils` class with optional default CPF and CNPJ settings (nested mappings, flat component kwargs, or pre-built utils instances) +- ✅ **Two-tier access**: Prefer main-class shortcuts at the façade root (`BrUtils::CpfFormatter`, `BrUtils::CnpjValidator`, …); Options, helpers, and errors live under nested package modules (`BrUtils::CpfFmt`, `BrUtils::CnpjUtils`, …). Root siblings (`CpfUtils`, `CnpjUtils`, `CpfFmt`, …) still work +- ✅ **Per-call overrides**: Configure defaults on the façade / domain utils; override options on a single `format` / `generate` / `is_valid` call +- ✅ **Error handling**: Domain errors propagate unchanged from the bundled packages; this gem defines `BrUtils::TypeMismatchError` and `BrUtils::InvalidArgumentCombinationError` for API misuse + +## Installation + +Install the gem directly: ```bash -bundle install -rake hooks:install # enable the git hooks (pre-commit, pre-push, commit-msg) -rake monorepo:check_cycles -cd packages/cpf-dv && bundle install && rake test +gem install br-utilities +``` + +Or add it to your `Gemfile` and run `bundle install`: + +```ruby +gem 'br-utilities' +``` + +This installs **`br-utilities`** together with [`cpf-utilities`](https://rubygems.org/gems/cpf-utilities) and [`cnpj-utilities`](https://rubygems.org/gems/cnpj-utilities) (which in turn pull in the CPF and CNPJ component packages). You do **not** need separate `gem install` / `gem` lines for the domain packages when using **`br-utilities`**. + +## Require + +```ruby +require 'br-utilities' ``` -## Git hooks +## Quick Start -`rake hooks:install` points `core.hooksPath` at `.githooks/`, enabling: +Prefer the aggregator class helpers (`BrUtils.cpf` / `BrUtils.cnpj`) for one-off calls — they forward to `BrUtils::DEFAULT`: -- **pre-commit**: RuboCop auto-corrects the staged Ruby files and re-stages only the - linting changes (unstaged edits in the same files are preserved, never committed). - Aborts if offenses remain that safe auto-correction can't fix. -- **pre-push**: runs the repository specs and every package's tests in build order, - aborting the push if any test fails. -- **commit-msg**: rejects commit messages that don't follow Conventional Commits. +```ruby +require 'br-utilities' -Undo with `rake hooks:uninstall`. See [CONTRIBUTING.md](CONTRIBUTING.md#git-hooks) for details. +cpf = '12345678909' +cnpj = '03603568000195' + +# CPF (personal ID) +BrUtils.cpf.format(cpf) # => "123.456.789-09" +BrUtils.cpf.generate(format: true) # => e.g. "478.442.410-55" +BrUtils.cpf.is_valid('123.456.789-09') # => true + +# CNPJ (business ID) +BrUtils.cnpj.format(cnpj) # => "03.603.568/0001-95" +BrUtils.cnpj.generate(format: true) # => e.g. "AB.123.CDE/0001-55" +BrUtils.cnpj.is_valid('98765432000198') # => true +``` + +**With domain aggregators:** + +```ruby +require 'br-utilities' + +cpf = '12345678909' +cnpj = '03603568000195' + +CpfUtils.format(cpf) # => "123.456.789-09" +CnpjUtils.format(cnpj) # => "03.603.568/0001-95" +CpfUtils.is_valid(cpf) # => true +CnpjUtils.is_valid(cnpj) # => true +``` + +**With functional helpers** (root sibling modules, loaded by this gem): + +```ruby +require 'br-utilities' + +cpf = '12345678909' +cnpj = '03603568000195' + +CpfFmt.cpf_fmt(cpf) # => "123.456.789-09" +CpfVal.cpf_val(cpf) # => true +CnpjFmt.cnpj_fmt(cnpj) # => "03.603.568/0001-95" +CnpjVal.cnpj_val(cnpj) # => true +``` -## Commit messages +## Usage -Commits follow [Conventional Commits](https://www.conventionalcommits.org). A pure-Ruby -linter (`bin/commit-lint`) enforces this in two places: +You can work in these equivalent ways: -- **Locally**: `rake hooks:install` sets `core.hooksPath` to `.githooks`, so the - `commit-msg` hook rejects non-conforming messages. Undo with `rake hooks:uninstall`. -- **CI**: the `Commit Lint` workflow validates every commit in a push/PR. +1. **`BrUtils.cpf` / `.cnpj`** — class helpers for quick one-off calls (forward to `DEFAULT`). +2. **`BrUtils::DEFAULT`** — mutable shared singleton (same object the class helpers use; process-wide / not thread-isolated). +3. **`BrUtils.new`** — configurable instance with shared defaults across both CPF and CNPJ domains. +4. **Domain aggregators** — `CpfUtils` / `CnpjUtils` (or `BrUtils::CpfUtils` / `BrUtils::CnpjUtils`) directly. +5. **Main classes under `BrUtils`** — `BrUtils::CpfFormatter`, `BrUtils::CnpjGenerator`, and related shortcuts. +6. **Nested package modules** — Options, helpers, errors, and types via `BrUtils::CpfFmt` / `CpfGen` / `CpfVal` / `CnpjFmt` / `CnpjGen` / `CnpjVal` / `CpfUtils` / `CnpjUtils`. +7. **Root sibling modules** (still supported) — `CpfFmt`, `CnpjUtils`, and the rest unchanged. -Allowed `type`s: `build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test`. -The `(scope)` is optional; when present it must be one of the per-package scopes below -(a few differ from the gem name). Example: `feat(cpf-gen): add batch generator`. +All approaches expose the same options and behavior within each domain. For exhaustive option tables and component-specific details, see the README of each [bundled package](#bundled-packages). + +### Class helpers (`BrUtils.cpf` / `.cnpj`) + +These class methods return the same domain utils instances as `BrUtils::DEFAULT`. Prefer them for one-off calls: + +```ruby +BrUtils.cpf.format('12345678909') +BrUtils.cpf.generate(format: true) +BrUtils.cpf.is_valid('12345678909') + +BrUtils.cnpj.format('03603568000195') +BrUtils.cnpj.generate(type: 'numeric') +BrUtils.cnpj.is_valid('98765432000198') +``` + +### `BrUtils::DEFAULT` (default instance) + +`BrUtils::DEFAULT` is the pre-built, **mutable** singleton behind the class helpers (parity with the JS default export / Python `br_utils`). Its configuration is **process-wide and shared across threads**: mutating it (e.g. `DEFAULT.cpf = …`) affects subsequent `BrUtils.cpf` / `.cnpj` calls for every caller in the process. Prefer `BrUtils.new` or per-call options for concurrent or isolated work; custom instances stay independent of `DEFAULT`: + +```ruby +BrUtils::DEFAULT.cpf = CpfUtils.new(formatter: { dash_key: '|' }) +BrUtils.cpf.format('12345678909') # => "123.456.789|09" + +custom = BrUtils.new +custom.cpf.format('12345678909') # => "123.456.789-09" (unaffected) +``` + +### `BrUtils` (class) + +For custom default CPF or CNPJ utils, create your own instance: + +```ruby +require 'br-utilities' + +utils = BrUtils.new( + cpf: { + formatter: { hidden: true, hidden_key: '#' }, + generator: { format: true } + }, + cnpj: { + formatter: { hidden: true }, + generator: { type: 'numeric', format: true }, + validator: { type: 'numeric' } + } +) + +utils.cpf.format('12345678909') # => "123.###.###-##" +utils.cpf.generate # => e.g. "005.265.352-88" +utils.cnpj.format('03603568000195') # => "03.603.***/****-**" +utils.cnpj.generate # => e.g. "73.008.535/0005-06" + +# Access or replace internal domain instances +utils.cpf # => CpfUtils +utils.cnpj # => CnpjUtils +``` + +- **`BrUtils.new(settings = nil, **keywords)`**: Optional settings. Pass either a settings `Hash` with `:cpf` and/or `:cnpj` keys, **or** the same keys (plus flat component kwargs) as keyword arguments — not both (passing both raises `BrUtils::InvalidArgumentCombinationError`). + - **`:cpf` / `:cnpj`**: A pre-built `CpfUtils` / `CnpjUtils` instance **or** a configuration `Hash` spread into the corresponding utils constructor. Within that `Hash`, each resource key (`:formatter`, `:generator`, and `:validator` for CNPJ) accepts either an options object or a mapping of option values. + - **`:cpf_formatter`**, **`:cpf_generator`**, **`:cnpj_formatter`**, **`:cnpj_generator`**, **`:cnpj_validator`**: Flat convenience arguments when only individual components need customization. They are ignored when the corresponding `:cpf` or `:cnpj` argument is provided. +- **`#cpf`**, **`#cnpj`**: Accessors (getters and setters) for the domain utils instances. Setters accept a utils instance, a configuration `Hash`, or `nil` to reset to defaults (replaces the entire instance; does not merge). + +Flat constructor options (alternative to nested `:cpf` / `:cnpj` mappings): + +```ruby +require 'br-utilities' + +utils = BrUtils.new( + cpf_formatter: CpfFmt::CpfFormatterOptions.new(hidden: true, hidden_key: '#'), + cpf_generator: CpfGen::CpfGeneratorOptions.new(format: true), + cnpj_formatter: CnpjFmt::CnpjFormatterOptions.new(hidden: true, hidden_key: '#'), + cnpj_generator: CnpjGen::CnpjGeneratorOptions.new(format: true, type: 'numeric'), + cnpj_validator: CnpjVal::CnpjValidatorOptions.new(type: 'numeric') +) +``` + +Passing a settings `Hash` positional argument together with any keyword raises: + +```ruby +BrUtils.new({ cpf: {} }, cnpj: CnpjUtils.new) +# raises BrUtils::InvalidArgumentCombinationError +``` -| Scope | Package | -|-------|---------| -| `utils` | `lacus-utils` | -| `cnpj-dv` | `cnpj-dv` | -| `cnpj-fmt` | `cnpj-fmt` | -| `cnpj-gen` | `cnpj-gen` | -| `cnpj-val` | `cnpj-val` | -| `cnpj-utils` | `cnpj-utilities` | -| `cpf-dv` | `cpf-dv` | -| `cpf-fmt` | `cpf-fmt` | -| `cpf-gen` | `cpf-gen` | -| `cpf-val` | `cpf-val` | -| `cpf-utils` | `cpf-utilities` | -| `br-utils` | `br-utilities` | +### Instance defaults and per-call overrides -Lint a range manually with `rake lint:commits` (defaults to `origin/main..HEAD`, override -with `COMMIT_RANGE`). +```ruby +require 'br-utilities' + +utils = BrUtils.new( + cpf: { + formatter: { hidden: true, hidden_key: '#' }, + generator: { format: true } + }, + cnpj: { + formatter: { hidden: true, hidden_key: '#' }, + generator: { format: true }, + validator: { type: 'numeric' } + } +) + +cpf = '12345678909' +cnpj = '03603568000195' + +utils.cpf.format(cpf) # => "123.###.###-##" +utils.cpf.format(cpf, hidden: false) # this call only: unmasked +utils.cpf.generate(format: false) # this call only: compact output + +utils.cnpj.format(cnpj) # => "03.603.###/####-##" +utils.cnpj.format(cnpj, hidden: false) # this call only: unmasked +utils.cnpj.is_valid('1QB5UKALPYFP59') # => false (instance validator is numeric-only) +utils.cnpj.is_valid( # => true for this call + '1QB5UKALPYFP59', + type: 'alphanumeric' +) +``` + +Passing a `CnpjFmt::CnpjFormatterOptions`, `CnpjGen::CnpjGeneratorOptions`, or `CnpjVal::CnpjValidatorOptions` instance into the `BrUtils` constructor stores that object by reference — mutating it later affects subsequent calls with no per-call override. + +To change a single nested option without replacing the whole domain utils, mutate via the domain accessors (e.g. `utils.cpf.formatter.options.hidden = true`). + +### CPF operations + +CPF methods are accessed via `BrUtils.cpf`, `utils.cpf`, `CpfUtils`, or the `CpfFmt` / `CpfGen` / `CpfVal` helpers. CPF uses the API from [`cpf-utilities`](../cpf-utilities/README.md). + +#### Formatting (`#format` / `CpfFmt.cpf_fmt`) + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `hidden` | `Boolean` | `false` | When `true`, mask digits in `hidden_start`–`hidden_end` with `hidden_key` | +| `hidden_key` | `String` | `'*'` | Character(s) used to replace masked digits | +| `hidden_start` | `Integer` | `3` | Start index (0–10, inclusive) of the range to hide | +| `hidden_end` | `Integer` | `10` | End index (0–10, inclusive) of the range to hide | +| `dot_key` | `String` | `'.'` | Dot delimiter (e.g. in `123.456.789`) | +| `dash_key` | `String` | `'-'` | Dash delimiter (e.g. before check digits `…-09`) | +| `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 ≠ 11; return value is used as result | + +Default **`on_fail`** returns an empty string. Invalid length does **not** raise from `#format`. + +```ruby +require 'br-utilities' + +cpf = '12345678909' + +BrUtils.cpf.format(cpf) # => "123.456.789-09" +BrUtils.cpf.format(cpf, hidden: true, hidden_key: '#') # => "123.###.###-##" +BrUtils.cpf.format(cpf, dot_key: '', dash_key: '_') # => "123456789_09" + +CpfFmt.cpf_fmt(cpf, hidden: true) # => "123.***.***-**" +``` + +#### Generation (`#generate` / `CpfGen.cpf_gen`) + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `format` | `Boolean` | `false` | When `true`, return the generated CPF in standard format (`000.000.000-00`) | +| `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. | + +Prefix rules: the base (first 9 digits) cannot be all zeros; 9 repeated digits (e.g. `999999999`) are not allowed. + +```ruby +require 'br-utilities' + +BrUtils.cpf.generate # => e.g. "11508890048" +BrUtils.cpf.generate(format: true) # => e.g. "661.134.831-00" +BrUtils.cpf.generate(prefix: '123456789') # => "12345678909" +CpfGen.cpf_gen(prefix: '123456789', format: true) # => "123.456.789-09" +``` + +#### Validation (`#is_valid` / `CpfVal.cpf_val`) + +Accepts formatted or unformatted CPF strings (or an `Array` of strings). Returns **`true`** or **`false`** without raising for invalid CPF. No validator options exist. + +```ruby +require 'br-utilities' + +BrUtils.cpf.is_valid('12345678909') # => true +BrUtils.cpf.is_valid('123.456.789-09') # => true +BrUtils.cpf.is_valid('12345678900') # => false +CpfVal.cpf_val('12345678909') # => true +``` + +### CNPJ operations + +CNPJ methods are accessed via `BrUtils.cnpj`, `utils.cnpj`, `CnpjUtils`, or the `CnpjFmt` / `CnpjGen` / `CnpjVal` helpers. CNPJ uses the API from [`cnpj-utilities`](../cnpj-utilities/README.md). + +#### Formatting (`#format` / `CnpjFmt.cnpj_fmt`) + +| 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 | + +Default **`on_fail`** returns an empty string. Wrong input types raise **`CnpjFmt::TypeMismatchError`**. + +```ruby +require 'br-utilities' + +cnpj = '03603568000195' + +BrUtils.cnpj.format(cnpj) # => "03.603.568/0001-95" +BrUtils.cnpj.format('12ABC34500DE99') # => "12.ABC.345/00DE-99" +BrUtils.cnpj.format( # => "03.603.###/####-##" + cnpj, + hidden: true, + hidden_key: '#' +) +BrUtils.cnpj.format( # => "03603568|0001_95" + cnpj, + dot_key: '', + slash_key: '|', + dash_key: '_' +) + +CnpjFmt.cnpj_fmt(cnpj) # => "03.603.568/0001-95" +``` + +#### Generation (`#generate` / `CnpjGen.cnpj_gen`) + +| 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. + +```ruby +require 'br-utilities' + +BrUtils.cnpj.generate # => e.g. "1GJTR3J3XSSA96" +BrUtils.cnpj.generate(format: true) # => e.g. "V1.J0V.8WE/DVZ7-50" +BrUtils.cnpj.generate( # => e.g. "12345678855883" + prefix: '12345678', + type: 'numeric' +) +CnpjGen.cnpj_gen(type: 'numeric') # => e.g. "65453043000178" +``` + +#### Validation (`#is_valid` / `CnpjVal.cnpj_val`) + +| 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). | + +```ruby +require 'br-utilities' + +BrUtils.cnpj.is_valid('98765432000198') # => true +BrUtils.cnpj.is_valid('98765432000199') # => false +BrUtils.cnpj.is_valid('1QB5UKALPYFP59') # => true +BrUtils.cnpj.is_valid('1QB5UKALpyfp59') # => false +BrUtils.cnpj.is_valid( # => true + '1QB5UKALpyfp59', + case_sensitive: false +) +BrUtils.cnpj.is_valid( # => false + '1QB5UKALPYFP59', + type: 'numeric' +) + +CnpjVal.cnpj_val('98765432000198') # => true +CnpjVal.cnpj_val('1QB5UKALpyfp59', case_sensitive: false) # => true +CnpjVal.cnpj_val('1QB5UKALPYFP59', type: 'numeric') # => false +``` -## Releasing (one gem at a time) +Invalid CNPJ returns **`false`** without raising. Wrong input types raise **`CnpjVal::TypeMismatchError`**. -1. Bump version in `packages//src//version.rb`, commit. -2. Push tag: `git tag @` (e.g. `cpf-dv@1.0.1`), then `git push origin `. -3. Configure [RubyGems Trusted Publishing](https://guides.rubygems.org/trusted-publishing/) for this repo and the `release` environment. -4. The Release workflow runs: tests the package, builds the gem, publishes to RubyGems via OIDC. No secrets required. +### Domain aggregators (standalone) -Release leaves first (e.g. `cpf-dv`, `cpf-fmt`), then dependents (`cpf-utilities`, `cnpj-utilities`), then `br-utilities`. +Use `CpfUtils` or `CnpjUtils` directly when you only need one domain: + +```ruby +require 'br-utilities' + +cpf_utils = CpfUtils.new( + formatter: { hidden: true }, + generator: { format: true } +) + +cnpj_utils = CnpjUtils.new( + formatter: { hidden: true }, + generator: { format: true }, + validator: { type: 'numeric' } +) + +cpf_utils.format('12345678909') # => "123.***.***-**" +cnpj_utils.format('03603568000195') # => "03.603.***/****-**" +``` + +### Accessing components + +Each domain aggregator exposes its internal formatter, generator, and validator: + +```ruby +require 'br-utilities' + +utils = BrUtils.new + +utils.cpf.formatter.format('12345678909', hidden: true) # => "123.***.***-**" +utils.cpf.generator.generate(format: true) # => e.g. "545.507.690-68" +utils.cpf.validator.is_valid('12345678909') # => true + +utils.cnpj.formatter.format('12ABC34500DE99') # => "12.ABC.345/00DE-99" +utils.cnpj.generator.generate(format: true) # => e.g. "8O.BE5.2KL/UI0Y-06" +utils.cnpj.validator.is_valid('03603568000195') # => true +``` + +### Using component classes and nested modules + +Preferred paths after `require 'br-utilities'`: + +```ruby +require 'br-utilities' + +# Main classes at the façade root +formatter = BrUtils::CpfFormatter.new(hidden: true) +generator = BrUtils::CnpjGenerator.new(type: 'numeric') +validator = BrUtils::CnpjValidator.new + +formatter.format('12345678909') # => "123.***.***-**" + +# Options, helpers, and errors under nested package modules +options = BrUtils::CpfFmt::CpfFormatterOptions.new(dash_key: '|') +BrUtils::CpfFmt.cpf_fmt('12345678909') # => "123.456.789-09" + +begin + BrUtils::CnpjFmt.cnpj_fmt(12_345) +rescue BrUtils::CnpjFmt::TypeMismatchError + # wrong input type +end +``` + +Root siblings remain supported (same objects as the nests): + +```ruby +CpfFmt.cpf_fmt('12345678909', dash_key: '|') # => "123.456.789|09" +CpfGen.cpf_gen(format: true) # => e.g. "478.442.410-55" +CpfVal.cpf_val('12345678909') # => true +CnpjFmt.cnpj_fmt('01ABC234000X56', slash_key: '|') # => "01.ABC.234|000X-56" +CnpjGen.cnpj_gen(type: 'numeric') # => e.g. "65453043000178" +CnpjVal.cnpj_val('9JN7MGLJZXIO50') # => true +``` + +See [`cpf-utilities`](../cpf-utilities/README.md) and [`cnpj-utilities`](../cnpj-utilities/README.md) for full option and error details. + +### Mixing styles + +Use `BrUtils` where a shared configuration helps, and standalone components or helpers elsewhere — they are the same underlying classes: + +```ruby +require 'br-utilities' + +utils = BrUtils.new(cnpj: { validator: { type: 'numeric' } }) + +# Via façade +utils.cpf.format('12345678909') # => "123.456.789-09" + +# Via component returned by the façade +utils.cnpj.formatter.format('12ABC34500DE99') # => "12.ABC.345/00DE-99" + +# Via a separate component instance +BrUtils::CnpjFormatter.new.format('03603568000195') # => "03.603.568/0001-95" + +# Via functional helpers +CpfFmt.cpf_fmt('12345678909') # => "123.456.789-09" +CnpjVal.cnpj_val('98.765.432/0001-98') # => true +``` + +## API + +### Exports + +After `require 'br-utilities'`: + +- **`BrUtils`**: Façade class to create an instance with optional default CPF and CNPJ utils settings. +- **`BrUtils.cpf` / `.cnpj`**: Class helpers that forward to `BrUtils::DEFAULT` domain accessors. +- **`BrUtils::DEFAULT`**: Mutable pre-built `BrUtils` instance (same object the class helpers use). Process-wide / shared across threads — prefer `BrUtils.new` or per-call options under concurrency. +- **`BrUtils::VERSION`**: Gem version string. +- **Main-class shortcuts**: `BrUtils::CpfFormatter`, `BrUtils::CpfFormatterOptions`, `BrUtils::CpfGenerator`, `BrUtils::CpfGeneratorOptions`, `BrUtils::CpfValidator`, `BrUtils::CnpjFormatter`, `BrUtils::CnpjFormatterOptions`, `BrUtils::CnpjGenerator`, `BrUtils::CnpjGeneratorOptions`, `BrUtils::CnpjValidator`, `BrUtils::CnpjValidatorOptions` (same objects as the sibling classes). Error-marker shortcuts: `BrUtils::CpfFormatterError`, `BrUtils::CpfGeneratorError`, `BrUtils::CpfValidatorError`, `BrUtils::CnpjFormatterError`, `BrUtils::CnpjGeneratorError`, `BrUtils::CnpjValidatorError`. +- **Nested package modules**: `BrUtils::CpfUtils`, `BrUtils::CnpjUtils`, `BrUtils::CpfFmt`, `BrUtils::CpfGen`, `BrUtils::CpfVal`, `BrUtils::CnpjFmt`, `BrUtils::CnpjGen`, `BrUtils::CnpjVal` — full sibling surface (Options, helpers, errors, types). +- **Root sibling modules** (still supported): `CpfUtils`, `CnpjUtils`, `CpfFmt`, `CpfGen`, `CpfVal`, `CnpjFmt`, `CnpjGen`, `CnpjVal` — same objects as the nests. + +### Errors & Exceptions + +`BrUtils` defines only API-misuse errors for this gem’s argument rules. Domain errors are raised by the bundled packages and propagate unchanged. + +#### Defined by `br-utilities` + +Errors defined by this gem are **API misuse** only (wrong type or invalid argument combination). Every custom error includes the `BrUtils::Error` marker module. This gem defines **no** `BrUtils::DomainError` and no domain leaves — domain failures come only from the [bundled packages](#propagated-from-bundled-packages) and keep those packages’ namespaces (`CpfFmt::…`, `CnpjGen::…`, …). + +`rescue BrUtils::Error` catches **only** errors this gem raises. It does **not** catch component errors that propagate unchanged. + +##### Summary + +| Class | Inherits from | Category | Trigger condition | +|-------|---------------|----------|-------------------| +| `BrUtils::InvalidArgumentCombinationError` | `BrUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include BrUtils::Error`) | API misuse | Non-`nil` settings `Hash` passed together with any non-`nil` keyword argument | +| `BrUtils::TypeMismatchError` | `BrUtils::TypeMismatchError < TypeError < StandardError` (+ `include BrUtils::Error`) | API misuse | Non-`nil` `settings` argument to `BrUtils.new` is not a `Hash` | + +##### `BrUtils::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:** + +```ruby +rescue BrUtils::Error + # TypeMismatchError, InvalidArgumentCombinationError from this gem only + # (not CpfFmt::*, CnpjGen::*, or other bundled-package errors) +``` + +##### `BrUtils::TypeMismatchError` + +- **Inheritance:** `BrUtils::TypeMismatchError < TypeError < StandardError` (includes `BrUtils::Error`) +- **Category:** API misuse — the caller passed a value of the wrong type. +- **When it is raised:** Raised when `BrUtils.new` receives a non-`nil` `settings` argument that is not a `Hash`. +- **Example:** + +```ruby +BrUtils.new('not-a-hash') # raises BrUtils::TypeMismatchError +BrUtils.new(false) # raises BrUtils::TypeMismatchError (false is non-nil) +``` + +- **How to rescue it:** + +```ruby +rescue BrUtils::TypeMismatchError + # this gem's type-contract violation + +rescue TypeError + # native type errors, including this gem's TypeMismatchError +``` + +##### `BrUtils::InvalidArgumentCombinationError` + +- **Inheritance:** `BrUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (includes `BrUtils::Error`) +- **Category:** API misuse — the caller mixed mutually exclusive argument patterns. +- **When it is raised:** Raised when `BrUtils.new` receives both a non-`nil` settings `Hash` and any non-`nil` keyword argument (`cpf:`, `cnpj:`, `cpf_formatter:`, …) at the same time. +- **Example:** + +```ruby +BrUtils.new({ cpf: { formatter: { hidden: true } } }, cnpj: { formatter: { hidden: true } }) +# raises BrUtils::InvalidArgumentCombinationError +``` + +- **How to rescue it:** + +```ruby +rescue BrUtils::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). + +```ruby +require 'br-utilities' + +# 1) Single native class — catches misuse errors of that kind, +# including non-library ones already handled elsewhere in the consumer's code. +begin + BrUtils.new('not-a-hash') +rescue TypeError + # BrUtils::TypeMismatchError and any other TypeError (library or not) +end + +begin + BrUtils.new({ cpf: {} }, cnpj: CnpjUtils.new) +rescue ArgumentError + # BrUtils::InvalidArgumentCombinationError and any other ArgumentError (library or not) +end +``` + +```ruby +require 'br-utilities' + +# 2) BrUtils::DomainError — not applicable: this gem defines no DomainError +# (and no domain leaves). Domain failures come from bundled packages only. +# begin +# BrUtils.new(cpf: { formatter: { hidden_start: -1 } }) +# rescue BrUtils::DomainError # NameError — constant is not defined +# end +``` + +```ruby +require 'br-utilities' + +# 3) BrUtils::Error — catches everything this gem raises, regardless of native ancestry. +# Does not catch CpfFmt::*, CnpjGen::*, or other bundled-package errors. +begin + BrUtils.new('not-a-hash') +rescue BrUtils::Error + # every custom error that includes BrUtils::Error +end +``` + +```ruby +require 'br-utilities' + +# 4) Specific leaf class — catches only that exact failure mode. +begin + BrUtils.new('not-a-hash') +rescue BrUtils::TypeMismatchError + # only BrUtils::TypeMismatchError +end +``` + +#### Propagated from bundled packages + +`BrUtils` does not redefine domain exception types. Construction, setters, and domain method calls raise the same errors as [`cpf-utilities`](../cpf-utilities/README.md) and [`cnpj-utilities`](../cnpj-utilities/README.md): + +- **CPF formatting**: `CpfFmt::TypeMismatchError`, `CpfFmt::OutOfRangeError`, `CpfFmt::ValidationError`, `CpfFmt::InvalidLengthError` (passed to `on_fail`, not raised by `#format`), and related classes. +- **CPF generation**: `CpfGen::TypeMismatchError`, `CpfGen::ValidationError`, and related classes. +- **CPF validation**: `CpfVal::TypeMismatchError` and related classes. +- **CNPJ formatting**: `CnpjFmt::TypeMismatchError`, `CnpjFmt::OutOfRangeError`, `CnpjFmt::ValidationError`, `CnpjFmt::InvalidLengthError` (passed to `on_fail`), and related classes. +- **CNPJ generation**: `CnpjGen::TypeMismatchError`, `CnpjGen::ValidationError`, and related classes. +- **CNPJ validation**: `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. CPF and CNPJ validation failures return `false`. Formatting length failures are handled by **`on_fail`** (default returns an empty string). + +```ruby +require 'br-utilities' + +begin + BrUtils.new.cnpj.format(12_345) +rescue CnpjFmt::TypeMismatchError => e + puts e.message +end + +begin + BrUtils.new.cnpj.is_valid(12_345_678_000_198) +rescue CnpjVal::TypeMismatchError => e + puts e.message +end + +# Custom on_fail for invalid length +custom_fail = ->(value, _exception) { "Invalid: #{value}" } + +BrUtils.cpf.format('short', on_fail: custom_fail) # => "Invalid: short" +BrUtils.cnpj.format('short', on_fail: custom_fail) # => "Invalid: short" +BrUtils.cpf.format('short') # => "" (default on_fail) +``` + +For exhaustive exception lists and edge-case behavior, see each [bundled package](#bundled-packages) README. + +### Bundled packages + +| Package | Main resources | README | +|---------|----------------|--------| +| [`cpf-utilities`](https://rubygems.org/gems/cpf-utilities) | `CpfUtils`, `CpfFormatter`, `CpfGenerator`, `CpfValidator`, `CpfFmt.cpf_fmt`, `CpfGen.cpf_gen`, `CpfVal.cpf_val` | [docs](../cpf-utilities/README.md) | +| [`cnpj-utilities`](https://rubygems.org/gems/cnpj-utilities) | `CnpjUtils`, `CnpjFormatter`, `CnpjGenerator`, `CnpjValidator`, `CnpjFmt.cnpj_fmt`, `CnpjGen.cnpj_gen`, `CnpjVal.cnpj_val` | [docs](../cnpj-utilities/README.md) | + +All of the above are pulled in as dependencies of **`br-utilities`**. Interactive demos: [CPF](https://cpf-utils.vercel.app/) and [CNPJ](https://cnpj-utils.vercel.app/). + +## Contribution & Support + +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: + +- ⭐ Starring the repository +- 🤝 Contributing to the codebase +- 💡 [Suggesting new features](https://github.com/LacusSolutions/br-utils-ruby/issues) +- 🐛 [Reporting bugs](https://github.com/LacusSolutions/br-utils-ruby/issues) ## License -MIT. See [LICENSE](LICENSE). +This project is licensed under the MIT License — see the [LICENSE](https://github.com/LacusSolutions/br-utils-ruby/blob/main/LICENSE) file for details. + +## Changelog + +See [CHANGELOG](./CHANGELOG.md) for a list of changes and version history. + +--- + +Made with ❤️ by [Lacus Solutions](https://github.com/LacusSolutions) diff --git a/README.pt.md b/README.pt.md new file mode 100644 index 0000000..40d50ac --- /dev/null +++ b/README.pt.md @@ -0,0 +1,700 @@ +![br-utilities para Ruby](https://br-utils.vercel.app/img/cover_br-utils.jpg) + +> 🚀 **Suporte total ao [novo formato alfanumérico de CNPJ](https://github.com/user-attachments/files/23937961/calculodvcnpjalfanaumerico.pdf).** + +> 🌎 [Access documentation in English](./README.md) + +Kit em Ruby para as principais operações com dados brasileiros: CPF (Cadastro de Pessoa Física) e CNPJ (Cadastro Nacional da Pessoa Jurídica). Envolve [`cpf-utilities`](https://rubygems.org/gems/cpf-utilities) e [`cnpj-utilities`](https://rubygems.org/gems/cnpj-utilities) em uma única classe fachada (`BrUtils`). + +## Suporte a Ruby + +| ![Ruby 3.1](https://img.shields.io/badge/Ruby-3.1-CC342D?logo=ruby&logoColor=white) | ![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) | ![Ruby 4.0](https://img.shields.io/badge/Ruby-4.0-CC342D?logo=ruby&logoColor=white) | +| --- | --- | --- | --- | --- | +| Passing ✔ | Passing ✔ | Passing ✔ | Passing ✔ | Passing ✔ | + +Requer Ruby **≥ 3.1** (veja `required_ruby_version` no gemspec). + +## Recursos + +- ✅ **API unificada de alto nível**: Helpers de classe `BrUtils.cpf` / `.cnpj` alias de `BrUtils::DEFAULT`; cada domínio oferece `format`, `generate` e `is_valid` +- ✅ **Domínios empacotados**: [`cpf-utilities`](https://rubygems.org/gems/cpf-utilities) e [`cnpj-utilities`](https://rubygems.org/gems/cnpj-utilities) instalados juntos +- ✅ **CNPJ alfanumérico**: Suporte completo ao novo formato alfanumérico de CNPJ (a partir de 2026) +- ✅ **Instância reutilizável**: Classe `BrUtils` com configurações padrão opcionais de CPF e CNPJ (mapeamentos aninhados, kwargs planos de componentes ou instâncias prontas de utils) +- ✅ **Acesso em dois níveis**: Prefira atalhos de classes principais na raiz da fachada (`BrUtils::CpfFormatter`, `BrUtils::CnpjValidator`, …); Options, helpers e erros ficam nos módulos aninhados (`BrUtils::CpfFmt`, `BrUtils::CnpjUtils`, …). Os irmãos na raiz (`CpfUtils`, `CnpjUtils`, `CpfFmt`, …) continuam funcionando +- ✅ **Sobrescritas por chamada**: Configure padrões na fachada / utils de domínio; sobrescreva opções em uma única chamada de `format` / `generate` / `is_valid` +- ✅ **Tratamento de erros**: Erros de domínio propagam inalterados dos pacotes incluídos; esta gem define `BrUtils::TypeMismatchError` e `BrUtils::InvalidArgumentCombinationError` para uso indevido da API + +## Instalação + +Instale a gem diretamente: + +```bash +gem install br-utilities +``` + +Ou adicione ao seu `Gemfile` e execute `bundle install`: + +```ruby +gem 'br-utilities' +``` + +Isso instala **`br-utilities`** junto com [`cpf-utilities`](https://rubygems.org/gems/cpf-utilities) e [`cnpj-utilities`](https://rubygems.org/gems/cnpj-utilities) (que por sua vez trazem os pacotes componentes de CPF e CNPJ). Você **não** precisa de `gem install` / linhas `gem` separados para os pacotes de domínio ao usar **`br-utilities`**. + +## Require + +```ruby +require 'br-utilities' +``` + +## Início rápido + +Prefira os helpers de classe do agregador (`BrUtils.cpf` / `BrUtils.cnpj`) para chamadas pontuais — eles encaminham para `BrUtils::DEFAULT`: + +```ruby +require 'br-utilities' + +cpf = '12345678909' +cnpj = '03603568000195' + +# CPF (pessoa física) +BrUtils.cpf.format(cpf) # => "123.456.789-09" +BrUtils.cpf.generate(format: true) # => ex.: "478.442.410-55" +BrUtils.cpf.is_valid('123.456.789-09') # => true + +# CNPJ (pessoa jurídica) +BrUtils.cnpj.format(cnpj) # => "03.603.568/0001-95" +BrUtils.cnpj.generate(format: true) # => ex.: "AB.123.CDE/0001-55" +BrUtils.cnpj.is_valid('98765432000198') # => true +``` + +**Com agregadores de domínio:** + +```ruby +require 'br-utilities' + +cpf = '12345678909' +cnpj = '03603568000195' + +CpfUtils.format(cpf) # => "123.456.789-09" +CnpjUtils.format(cnpj) # => "03.603.568/0001-95" +CpfUtils.is_valid(cpf) # => true +CnpjUtils.is_valid(cnpj) # => true +``` + +**Com helpers funcionais** (módulos irmãos na raiz, carregados por esta gem): + +```ruby +require 'br-utilities' + +cpf = '12345678909' +cnpj = '03603568000195' + +CpfFmt.cpf_fmt(cpf) # => "123.456.789-09" +CpfVal.cpf_val(cpf) # => true +CnpjFmt.cnpj_fmt(cnpj) # => "03.603.568/0001-95" +CnpjVal.cnpj_val(cnpj) # => true +``` + +## Utilização + +Você pode trabalhar destas formas equivalentes: + +1. **`BrUtils.cpf` / `.cnpj`** — helpers de classe para chamadas rápidas (encaminham para `DEFAULT`). +2. **`BrUtils::DEFAULT`** — singleton compartilhado mutável (o mesmo objeto usado pelos helpers de classe; em todo o processo / não isolado por thread). +3. **`BrUtils.new`** — instância configurável com padrões compartilhados entre os domínios CPF e CNPJ. +4. **Agregadores de domínio** — `CpfUtils` / `CnpjUtils` (ou `BrUtils::CpfUtils` / `BrUtils::CnpjUtils`) diretamente. +5. **Classes principais sob `BrUtils`** — `BrUtils::CpfFormatter`, `BrUtils::CnpjGenerator` e atalhos relacionados. +6. **Módulos aninhados do pacote** — Options, helpers, erros e tipos via `BrUtils::CpfFmt` / `CpfGen` / `CpfVal` / `CnpjFmt` / `CnpjGen` / `CnpjVal` / `CpfUtils` / `CnpjUtils`. +7. **Módulos irmãos na raiz** (ainda suportados) — `CpfFmt`, `CnpjUtils` e demais inalterados. + +Todas as abordagens expõem as mesmas opções e comportamento dentro de cada domínio. Para tabelas de opções exaustivas e detalhes específicos de cada componente, consulte o README de cada [pacote incluído](#pacotes-incluídos). + +### Helpers de classe (`BrUtils.cpf` / `.cnpj`) + +Esses métodos de classe retornam as mesmas instâncias de utils de domínio que `BrUtils::DEFAULT`. Prefira-os para chamadas pontuais: + +```ruby +BrUtils.cpf.format('12345678909') +BrUtils.cpf.generate(format: true) +BrUtils.cpf.is_valid('12345678909') + +BrUtils.cnpj.format('03603568000195') +BrUtils.cnpj.generate(type: 'numeric') +BrUtils.cnpj.is_valid('98765432000198') +``` + +### `BrUtils::DEFAULT` (instância padrão) + +`BrUtils::DEFAULT` é o singleton pré-construído e **mutável** por trás dos helpers de classe (paridade com a exportação padrão do JS / `br_utils` do Python). Sua configuração é **em todo o processo e compartilhada entre threads**: mutá-lo (ex.: `DEFAULT.cpf = …`) afeta chamadas subsequentes de `BrUtils.cpf` / `.cnpj` para todos os callers no processo. Prefira `BrUtils.new` ou opções por chamada para trabalho concorrente ou isolado; instâncias customizadas permanecem independentes de `DEFAULT`: + +```ruby +BrUtils::DEFAULT.cpf = CpfUtils.new(formatter: { dash_key: '|' }) +BrUtils.cpf.format('12345678909') # => "123.456.789|09" + +custom = BrUtils.new +custom.cpf.format('12345678909') # => "123.456.789-09" (não afetado) +``` + +### `BrUtils` (classe) + +Para utils de CPF ou CNPJ padrão customizados, crie sua própria instância: + +```ruby +require 'br-utilities' + +utils = BrUtils.new( + cpf: { + formatter: { hidden: true, hidden_key: '#' }, + generator: { format: true } + }, + cnpj: { + formatter: { hidden: true }, + generator: { type: 'numeric', format: true }, + validator: { type: 'numeric' } + } +) + +utils.cpf.format('12345678909') # => "123.###.###-##" +utils.cpf.generate # => ex.: "005.265.352-88" +utils.cnpj.format('03603568000195') # => "03.603.***/****-**" +utils.cnpj.generate # => ex.: "73.008.535/0005-06" + +# Acessar ou substituir instâncias internas de domínio +utils.cpf # => CpfUtils +utils.cnpj # => CnpjUtils +``` + +- **`BrUtils.new(settings = nil, **keywords)`**: Configurações opcionais. Passe um `Hash` de settings com chaves `:cpf` e/ou `:cnpj`, **ou** as mesmas chaves (mais kwargs planos de componentes) como argumentos nomeados — não ambos (passar ambos lança `BrUtils::InvalidArgumentCombinationError`). + - **`:cpf` / `:cnpj`**: Uma instância pronta de `CpfUtils` / `CnpjUtils` **ou** um `Hash` de configuração repassado ao construtor do utils correspondente. Dentro desse `Hash`, cada chave de recurso (`:formatter`, `:generator` e `:validator` para CNPJ) aceita um objeto de opções ou um mapeamento de valores de opção. + - **`:cpf_formatter`**, **`:cpf_generator`**, **`:cnpj_formatter`**, **`:cnpj_generator`**, **`:cnpj_validator`**: Argumentos planos de conveniência quando apenas componentes individuais precisam de customização. São ignorados quando o argumento `:cpf` ou `:cnpj` correspondente é fornecido. +- **`#cpf`**, **`#cnpj`**: Acessores (getters e setters) das instâncias de utils de domínio. Os setters aceitam uma instância de utils, um `Hash` de configuração ou `nil` para voltar aos padrões (substitui a instância inteira; não faz merge). + +Opções planas no construtor (alternativa aos mapeamentos aninhados `:cpf` / `:cnpj`): + +```ruby +require 'br-utilities' + +utils = BrUtils.new( + cpf_formatter: CpfFmt::CpfFormatterOptions.new(hidden: true, hidden_key: '#'), + cpf_generator: CpfGen::CpfGeneratorOptions.new(format: true), + cnpj_formatter: CnpjFmt::CnpjFormatterOptions.new(hidden: true, hidden_key: '#'), + cnpj_generator: CnpjGen::CnpjGeneratorOptions.new(format: true, type: 'numeric'), + cnpj_validator: CnpjVal::CnpjValidatorOptions.new(type: 'numeric') +) +``` + +Passar um `Hash` de settings posicional junto com qualquer palavra-chave lança: + +```ruby +BrUtils.new({ cpf: {} }, cnpj: CnpjUtils.new) +# lança BrUtils::InvalidArgumentCombinationError +``` + +### Padrões da instância e sobrescritas por chamada + +```ruby +require 'br-utilities' + +utils = BrUtils.new( + cpf: { + formatter: { hidden: true, hidden_key: '#' }, + generator: { format: true } + }, + cnpj: { + formatter: { hidden: true, hidden_key: '#' }, + generator: { format: true }, + validator: { type: 'numeric' } + } +) + +cpf = '12345678909' +cnpj = '03603568000195' + +utils.cpf.format(cpf) # => "123.###.###-##" +utils.cpf.format(cpf, hidden: false) # só nesta chamada: sem máscara +utils.cpf.generate(format: false) # só nesta chamada: saída compacta + +utils.cnpj.format(cnpj) # => "03.603.###/####-##" +utils.cnpj.format(cnpj, hidden: false) # só nesta chamada: sem máscara +utils.cnpj.is_valid('1QB5UKALPYFP59') # => false (validador da instância é só numérico) +utils.cnpj.is_valid( # => true nesta chamada + '1QB5UKALPYFP59', + type: 'alphanumeric' +) +``` + +Passar uma instância de `CnpjFmt::CnpjFormatterOptions`, `CnpjGen::CnpjGeneratorOptions` ou `CnpjVal::CnpjValidatorOptions` ao construtor de `BrUtils` armazena esse objeto por referência — mutá-lo depois afeta chamadas subsequentes sem sobrescrita por chamada. + +Para alterar uma única opção aninhada sem substituir o utils de domínio inteiro, mute via os acessores de domínio (ex.: `utils.cpf.formatter.options.hidden = true`). + +### Operações de CPF + +Os métodos de CPF são acessados via `BrUtils.cpf`, `utils.cpf`, `CpfUtils` ou os helpers `CpfFmt` / `CpfGen` / `CpfVal`. O CPF usa a API de [`cpf-utilities`](../cpf-utilities/README.pt.md). + +#### Formatação (`#format` / `CpfFmt.cpf_fmt`) + +| Opção | Tipo | Padrão | Descrição | +|--------|------|---------|-------------| +| `hidden` | `Boolean` | `false` | Se `true`, mascara dígitos entre `hidden_start` e `hidden_end` com `hidden_key` | +| `hidden_key` | `String` | `'*'` | Caractere(s) usados para substituir dígitos mascarados | +| `hidden_start` | `Integer` | `3` | Índice inicial (0–10, inclusivo) do intervalo a ocultar | +| `hidden_end` | `Integer` | `10` | Índice final (0–10, inclusivo) do intervalo a ocultar | +| `dot_key` | `String` | `'.'` | Delimitador de ponto (ex.: em `123.456.789`) | +| `dash_key` | `String` | `'-'` | Delimitador de hífen (ex.: antes dos dígitos verificadores `…-09`) | +| `escape` | `Boolean` | `false` | Se `true`, escapa caracteres especiais HTML no resultado | +| `encode` | `Boolean` | `false` | Se `true`, codifica o resultado para URL (similar ao `encodeURIComponent` do JavaScript) | +| `on_fail` | `Proc` / invocável | retorna `''` | Callback quando o tamanho da entrada sanitizada ≠ 11; o retorno é usado como resultado | + +O **`on_fail`** padrão retorna uma string vazia. Comprimento inválido **não** lança exceção em `#format`. + +```ruby +require 'br-utilities' + +cpf = '12345678909' + +BrUtils.cpf.format(cpf) # => "123.456.789-09" +BrUtils.cpf.format(cpf, hidden: true, hidden_key: '#') # => "123.###.###-##" +BrUtils.cpf.format(cpf, dot_key: '', dash_key: '_') # => "123456789_09" + +CpfFmt.cpf_fmt(cpf, hidden: true) # => "123.***.***-**" +``` + +#### Geração (`#generate` / `CpfGen.cpf_gen`) + +| Opção | Tipo | Padrão | Descrição | +|--------|------|---------|-------------| +| `format` | `Boolean` | `false` | Se `true`, retorna o CPF gerado no formato padrão (`000.000.000-00`) | +| `prefix` | `String` | `''` | String parcial inicial (0–9 dígitos). Não dígitos são removidos; caracteres faltantes são gerados e os dígitos verificadores calculados. Prefixos com mais de 9 dígitos são truncados silenciosamente. | + +Regras de prefixo: a base (primeiros 9 dígitos) não pode ser toda zeros; 9 dígitos repetidos (ex.: `999999999`) não são permitidos. + +```ruby +require 'br-utilities' + +BrUtils.cpf.generate # => ex.: "11508890048" +BrUtils.cpf.generate(format: true) # => ex.: "661.134.831-00" +BrUtils.cpf.generate(prefix: '123456789') # => "12345678909" +CpfGen.cpf_gen(prefix: '123456789', format: true) # => "123.456.789-09" +``` + +#### Validação (`#is_valid` / `CpfVal.cpf_val`) + +Aceita CPF formatado ou não (ou um `Array` de strings). Retorna **`true`** ou **`false`** sem lançar exceção para CPF inválido. Não há opções de validador. + +```ruby +require 'br-utilities' + +BrUtils.cpf.is_valid('12345678909') # => true +BrUtils.cpf.is_valid('123.456.789-09') # => true +BrUtils.cpf.is_valid('12345678900') # => false +CpfVal.cpf_val('12345678909') # => true +``` + +### Operações de CNPJ + +Os métodos de CNPJ são acessados via `BrUtils.cnpj`, `utils.cnpj`, `CnpjUtils` ou os helpers `CnpjFmt` / `CnpjGen` / `CnpjVal`. O CNPJ usa a API de [`cnpj-utilities`](../cnpj-utilities/README.pt.md). + +#### Formatação (`#format` / `CnpjFmt.cnpj_fmt`) + +| Opção | Tipo | Padrão | Descrição | +|--------|------|---------|-------------| +| `hidden` | `Boolean` | `false` | Se `true`, mascara caracteres entre `hidden_start` e `hidden_end` com `hidden_key` | +| `hidden_key` | `String` | `'*'` | Caractere(s) usados para substituir caracteres mascarados | +| `hidden_start` | `Integer` | `5` | Índice inicial (0–13, inclusivo) do intervalo a ocultar | +| `hidden_end` | `Integer` | `13` | Índice final (0–13, inclusivo) do intervalo a ocultar | +| `dot_key` | `String` | `'.'` | Delimitador de ponto (ex.: em `12.345.678`) | +| `slash_key` | `String` | `'/'` | Delimitador de barra (ex.: antes da filial `…/0001-90`) | +| `dash_key` | `String` | `'-'` | Delimitador de hífen (ex.: antes dos dígitos verificadores `…-90`) | +| `escape` | `Boolean` | `false` | Se `true`, escapa caracteres especiais HTML no resultado | +| `encode` | `Boolean` | `false` | Se `true`, codifica o resultado para URL (similar ao `encodeURIComponent` do JavaScript) | +| `on_fail` | `Proc` / invocável | retorna `''` | Callback quando o tamanho da entrada sanitizada ≠ 14; o retorno é usado como resultado | + +O **`on_fail`** padrão retorna uma string vazia. Tipos de entrada incorretos lançam **`CnpjFmt::TypeMismatchError`**. + +```ruby +require 'br-utilities' + +cnpj = '03603568000195' + +BrUtils.cnpj.format(cnpj) # => "03.603.568/0001-95" +BrUtils.cnpj.format('12ABC34500DE99') # => "12.ABC.345/00DE-99" +BrUtils.cnpj.format( # => "03.603.###/####-##" + cnpj, + hidden: true, + hidden_key: '#' +) +BrUtils.cnpj.format( # => "03603568|0001_95" + cnpj, + dot_key: '', + slash_key: '|', + dash_key: '_' +) + +CnpjFmt.cnpj_fmt(cnpj) # => "03.603.568/0001-95" +``` + +#### Geração (`#generate` / `CnpjGen.cnpj_gen`) + +| Opção | Tipo | Padrão | Descrição | +|--------|------|---------|-------------| +| `format` | `Boolean` | `false` | Se `true`, retorna o CNPJ gerado no formato padrão (`00.000.000/0000-00`) | +| `prefix` | `String` | `''` | String parcial inicial (0–12 caracteres alfanuméricos). Caracteres faltantes são gerados e os dígitos verificadores calculados. | +| `type` | `String` | `'alphanumeric'` | Conjunto de caracteres para a parte gerada aleatoriamente: `'numeric'`, `'alphabetic'` ou `'alphanumeric'`. **Os dígitos verificadores são sempre numéricos.** | + +Regras de prefixo: o ID base (primeiros 8 caracteres) e o ID da filial (caracteres 9–12) não podem ser todos zeros; 12 dígitos repetidos (ex.: `111111111111`) também não são permitidos. + +```ruby +require 'br-utilities' + +BrUtils.cnpj.generate # => ex.: "1GJTR3J3XSSA96" +BrUtils.cnpj.generate(format: true) # => ex.: "V1.J0V.8WE/DVZ7-50" +BrUtils.cnpj.generate( # => ex.: "12345678855883" + prefix: '12345678', + type: 'numeric' +) +CnpjGen.cnpj_gen(type: 'numeric') # => ex.: "65453043000178" +``` + +#### Validação (`#is_valid` / `CnpjVal.cnpj_val`) + +| Opção | Tipo | Padrão | Descrição | +|--------|------|---------|-------------| +| `case_sensitive` | `Boolean` | `true` | Se `false`, letras minúsculas são aceitas para CNPJ alfanumérico (a entrada é convertida para maiúsculas antes da validação). | +| `type` | `String` | `'alphanumeric'` | `'numeric'`: apenas dígitos (0–9); `'alphanumeric'`: dígitos e letras (0–9, A–Z). | + +```ruby +require 'br-utilities' + +BrUtils.cnpj.is_valid('98765432000198') # => true +BrUtils.cnpj.is_valid('98765432000199') # => false +BrUtils.cnpj.is_valid('1QB5UKALPYFP59') # => true +BrUtils.cnpj.is_valid('1QB5UKALpyfp59') # => false +BrUtils.cnpj.is_valid( # => true + '1QB5UKALpyfp59', + case_sensitive: false +) +BrUtils.cnpj.is_valid( # => false + '1QB5UKALPYFP59', + type: 'numeric' +) + +CnpjVal.cnpj_val('98765432000198') # => true +CnpjVal.cnpj_val('1QB5UKALpyfp59', case_sensitive: false) # => true +CnpjVal.cnpj_val('1QB5UKALPYFP59', type: 'numeric') # => false +``` + +CNPJ inválido retorna **`false`** sem lançar exceção. Tipos de entrada incorretos lançam **`CnpjVal::TypeMismatchError`**. + +### Agregadores de domínio (isolados) + +Use `CpfUtils` ou `CnpjUtils` diretamente quando precisar de apenas um domínio: + +```ruby +require 'br-utilities' + +cpf_utils = CpfUtils.new( + formatter: { hidden: true }, + generator: { format: true } +) + +cnpj_utils = CnpjUtils.new( + formatter: { hidden: true }, + generator: { format: true }, + validator: { type: 'numeric' } +) + +cpf_utils.format('12345678909') # => "123.***.***-**" +cnpj_utils.format('03603568000195') # => "03.603.***/****-**" +``` + +### Acessando componentes + +Cada agregador de domínio expõe seu formatador, gerador e validador internos: + +```ruby +require 'br-utilities' + +utils = BrUtils.new + +utils.cpf.formatter.format('12345678909', hidden: true) # => "123.***.***-**" +utils.cpf.generator.generate(format: true) # => ex.: "545.507.690-68" +utils.cpf.validator.is_valid('12345678909') # => true + +utils.cnpj.formatter.format('12ABC34500DE99') # => "12.ABC.345/00DE-99" +utils.cnpj.generator.generate(format: true) # => ex.: "8O.BE5.2KL/UI0Y-06" +utils.cnpj.validator.is_valid('03603568000195') # => true +``` + +### Usando classes componentes e módulos aninhados + +Caminhos preferidos após `require 'br-utilities'`: + +```ruby +require 'br-utilities' + +# Classes principais na raiz da fachada +formatter = BrUtils::CpfFormatter.new(hidden: true) +generator = BrUtils::CnpjGenerator.new(type: 'numeric') +validator = BrUtils::CnpjValidator.new + +formatter.format('12345678909') # => "123.***.***-**" + +# Options, helpers e erros nos módulos aninhados do pacote +options = BrUtils::CpfFmt::CpfFormatterOptions.new(dash_key: '|') +BrUtils::CpfFmt.cpf_fmt('12345678909') # => "123.456.789-09" + +begin + BrUtils::CnpjFmt.cnpj_fmt(12_345) +rescue BrUtils::CnpjFmt::TypeMismatchError + # tipo de entrada incorreto +end +``` + +Os irmãos na raiz continuam suportados (os mesmos objetos dos nests): + +```ruby +CpfFmt.cpf_fmt('12345678909', dash_key: '|') # => "123.456.789|09" +CpfGen.cpf_gen(format: true) # => ex.: "478.442.410-55" +CpfVal.cpf_val('12345678909') # => true +CnpjFmt.cnpj_fmt('01ABC234000X56', slash_key: '|') # => "01.ABC.234|000X-56" +CnpjGen.cnpj_gen(type: 'numeric') # => ex.: "65453043000178" +CnpjVal.cnpj_val('9JN7MGLJZXIO50') # => true +``` + +Consulte [`cpf-utilities`](../cpf-utilities/README.pt.md) e [`cnpj-utilities`](../cnpj-utilities/README.pt.md) para detalhes completos de opções e erros. + +### Misturando estilos + +Use `BrUtils` onde uma configuração compartilhada ajuda, e componentes ou helpers isolados em outros pontos — são as mesmas classes subjacentes: + +```ruby +require 'br-utilities' + +utils = BrUtils.new(cnpj: { validator: { type: 'numeric' } }) + +# Via fachada +utils.cpf.format('12345678909') # => "123.456.789-09" + +# Via componente retornado pela fachada +utils.cnpj.formatter.format('12ABC34500DE99') # => "12.ABC.345/00DE-99" + +# Via instância de componente separada +BrUtils::CnpjFormatter.new.format('03603568000195') # => "03.603.568/0001-95" + +# Via helpers funcionais +CpfFmt.cpf_fmt('12345678909') # => "123.456.789-09" +CnpjVal.cnpj_val('98.765.432/0001-98') # => true +``` + +## API + +### Exportações + +Após `require 'br-utilities'`: + +- **`BrUtils`**: Classe fachada para criar uma instância com configurações opcionais dos utils de CPF e CNPJ. +- **`BrUtils.cpf` / `.cnpj`**: Helpers de classe que encaminham para os acessores de domínio de `BrUtils::DEFAULT`. +- **`BrUtils::DEFAULT`**: Instância pré-construída e mutável de `BrUtils` (o mesmo objeto usado pelos helpers de classe). Em todo o processo / compartilhada entre threads — prefira `BrUtils.new` ou opções por chamada sob concorrência. +- **`BrUtils::VERSION`**: String da versão da gem. +- **Atalhos de classes principais**: `BrUtils::CpfFormatter`, `BrUtils::CpfFormatterOptions`, `BrUtils::CpfGenerator`, `BrUtils::CpfGeneratorOptions`, `BrUtils::CpfValidator`, `BrUtils::CnpjFormatter`, `BrUtils::CnpjFormatterOptions`, `BrUtils::CnpjGenerator`, `BrUtils::CnpjGeneratorOptions`, `BrUtils::CnpjValidator`, `BrUtils::CnpjValidatorOptions` (os mesmos objetos das classes irmãs). Atalhos de marcadores de erro: `BrUtils::CpfFormatterError`, `BrUtils::CpfGeneratorError`, `BrUtils::CpfValidatorError`, `BrUtils::CnpjFormatterError`, `BrUtils::CnpjGeneratorError`, `BrUtils::CnpjValidatorError`. +- **Módulos aninhados do pacote**: `BrUtils::CpfUtils`, `BrUtils::CnpjUtils`, `BrUtils::CpfFmt`, `BrUtils::CpfGen`, `BrUtils::CpfVal`, `BrUtils::CnpjFmt`, `BrUtils::CnpjGen`, `BrUtils::CnpjVal` — superfície completa dos irmãos (Options, helpers, erros, tipos). +- **Módulos irmãos na raiz** (ainda suportados): `CpfUtils`, `CnpjUtils`, `CpfFmt`, `CpfGen`, `CpfVal`, `CnpjFmt`, `CnpjGen`, `CnpjVal` — os mesmos objetos dos nests. + +### Erros e exceções + +`BrUtils` define apenas erros de uso indevido da API para as regras de argumentos desta gem. Erros de domínio são lançados pelos pacotes incluídos e propagam inalterados. + +#### Definidos por `br-utilities` + +Os erros definidos por esta gem são **apenas uso indevido da API** (tipo errado ou combinação inválida de argumentos). Todo erro customizado inclui o módulo marcador `BrUtils::Error`. Esta gem **não** define `BrUtils::DomainError` nem folhas de domínio — falhas de domínio vêm apenas dos [pacotes incluídos](#propagados-dos-pacotes-incluídos) e mantêm os namespaces desses pacotes (`CpfFmt::…`, `CnpjGen::…`, …). + +`rescue BrUtils::Error` captura **apenas** erros que esta gem lança. **Não** captura erros de componentes que propagam inalterados. + +##### Resumo + +| Classe | Herda de | Categoria | Condição de disparo | +|-------|---------------|----------|-------------------| +| `BrUtils::InvalidArgumentCombinationError` | `BrUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include BrUtils::Error`) | Uso indevido da API | `Hash` de settings não-`nil` passado junto com qualquer argumento nomeado não-`nil` | +| `BrUtils::TypeMismatchError` | `BrUtils::TypeMismatchError < TypeError < StandardError` (+ `include BrUtils::Error`) | Uso indevido da API | Argumento `settings` não-`nil` em `BrUtils.new` não é um `Hash` | + +##### `BrUtils::Error` (módulo marcador) + +- **Herança:** módulo marcador misturado em todo erro customizado que esta gem lança via `include` (não é uma classe). +- **Categoria:** N/A (apenas alvo de rescue) — não é um modo de falha por si só. +- **Quando é lançado:** Nunca lançado diretamente; incluído por todo erro customizado que esta gem lança. +- **Exemplo:** N/A +- **Como resgatar:** + +```ruby +rescue BrUtils::Error + # TypeMismatchError, InvalidArgumentCombinationError apenas desta gem + # (não CpfFmt::*, CnpjGen::* ou outros erros dos pacotes incluídos) +``` + +##### `BrUtils::TypeMismatchError` + +- **Herança:** `BrUtils::TypeMismatchError < TypeError < StandardError` (inclui `BrUtils::Error`) +- **Categoria:** Uso indevido da API — o caller passou um valor do tipo errado. +- **Quando é lançado:** Quando `BrUtils.new` recebe um argumento `settings` não-`nil` que não é um `Hash`. +- **Exemplo:** + +```ruby +BrUtils.new('not-a-hash') # lança BrUtils::TypeMismatchError +BrUtils.new(false) # lança BrUtils::TypeMismatchError (false é não-nil) +``` + +- **Como resgatar:** + +```ruby +rescue BrUtils::TypeMismatchError + # violação de contrato de tipo desta gem + +rescue TypeError + # erros nativos de tipo, incluindo TypeMismatchError desta gem +``` + +##### `BrUtils::InvalidArgumentCombinationError` + +- **Herança:** `BrUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (inclui `BrUtils::Error`) +- **Categoria:** Uso indevido da API — o caller misturou padrões de argumentos mutuamente exclusivos. +- **Quando é lançado:** Quando `BrUtils.new` recebe um `Hash` de settings não-`nil` e qualquer argumento nomeado não-`nil` (`cpf:`, `cnpj:`, `cpf_formatter:`, …) ao mesmo tempo. +- **Exemplo:** + +```ruby +BrUtils.new({ cpf: { formatter: { hidden: true } } }, cnpj: { formatter: { hidden: true } }) +# lança BrUtils::InvalidArgumentCombinationError +``` + +- **Como resgatar:** + +```ruby +rescue BrUtils::InvalidArgumentCombinationError + # combinação inválida de assinatura desta gem + +rescue ArgumentError + # erros nativos de argumento, incluindo InvalidArgumentCombinationError desta gem +``` + +##### Granularidade de rescue + +Cada nível é mostrado como seu próprio exemplo isolado (não os una em uma única escada de `rescue` — um handler nativo amplo tornaria cláusulas mais estreitas inalcançáveis). + +```ruby +require 'br-utilities' + +# 1) Classe nativa única — captura erros de uso indevido desse tipo, +# incluindo os não-biblioteca já tratados em outro ponto do código do consumidor. +begin + BrUtils.new('not-a-hash') +rescue TypeError + # BrUtils::TypeMismatchError e qualquer outro TypeError (biblioteca ou não) +end + +begin + BrUtils.new({ cpf: {} }, cnpj: CnpjUtils.new) +rescue ArgumentError + # BrUtils::InvalidArgumentCombinationError e qualquer outro ArgumentError (biblioteca ou não) +end +``` + +```ruby +require 'br-utilities' + +# 2) BrUtils::DomainError — não aplicável: esta gem não define DomainError +# (nem folhas de domínio). Falhas de domínio vêm apenas dos pacotes incluídos. +# begin +# BrUtils.new(cpf: { formatter: { hidden_start: -1 } }) +# rescue BrUtils::DomainError # NameError — constante não definida +# end +``` + +```ruby +require 'br-utilities' + +# 3) BrUtils::Error — captura tudo que esta gem lança, independentemente da ancestralidade nativa. +# Não captura CpfFmt::*, CnpjGen::* ou outros erros dos pacotes incluídos. +begin + BrUtils.new('not-a-hash') +rescue BrUtils::Error + # todo erro customizado que inclui BrUtils::Error +end +``` + +```ruby +require 'br-utilities' + +# 4) Classe folha específica — captura apenas aquele modo de falha exato. +begin + BrUtils.new('not-a-hash') +rescue BrUtils::TypeMismatchError + # apenas BrUtils::TypeMismatchError +end +``` + +#### Propagados dos pacotes incluídos + +`BrUtils` não redefine tipos de exceção de domínio. Construção, setters e chamadas de métodos de domínio lançam os mesmos erros de [`cpf-utilities`](../cpf-utilities/README.pt.md) e [`cnpj-utilities`](../cnpj-utilities/README.pt.md): + +- **Formatação de CPF**: `CpfFmt::TypeMismatchError`, `CpfFmt::OutOfRangeError`, `CpfFmt::ValidationError`, `CpfFmt::InvalidLengthError` (passado a `on_fail`, não lançado por `#format`) e classes relacionadas. +- **Geração de CPF**: `CpfGen::TypeMismatchError`, `CpfGen::ValidationError` e classes relacionadas. +- **Validação de CPF**: `CpfVal::TypeMismatchError` e classes relacionadas. +- **Formatação de CNPJ**: `CnpjFmt::TypeMismatchError`, `CnpjFmt::OutOfRangeError`, `CnpjFmt::ValidationError`, `CnpjFmt::InvalidLengthError` (passado a `on_fail`) e classes relacionadas. +- **Geração de CNPJ**: `CnpjGen::TypeMismatchError`, `CnpjGen::ValidationError` e classes relacionadas. +- **Validação de CNPJ**: `CnpjVal::TypeMismatchError`, `CnpjVal::ValidationError` e classes relacionadas. + +Tipos de opção inválidos são tipicamente subclasses de **`TypeError`** (`*::TypeMismatchError`); valores de opção inválidos são erros de domínio sob a hierarquia `DomainError` de cada pacote. Falhas de validação de CPF e CNPJ retornam `false`. Falhas de comprimento na formatação são tratadas por **`on_fail`** (padrão retorna string vazia). + +```ruby +require 'br-utilities' + +begin + BrUtils.new.cnpj.format(12_345) +rescue CnpjFmt::TypeMismatchError => e + puts e.message +end + +begin + BrUtils.new.cnpj.is_valid(12_345_678_000_198) +rescue CnpjVal::TypeMismatchError => e + puts e.message +end + +# on_fail customizado para comprimento inválido +custom_fail = ->(value, _exception) { "Invalid: #{value}" } + +BrUtils.cpf.format('short', on_fail: custom_fail) # => "Invalid: short" +BrUtils.cnpj.format('short', on_fail: custom_fail) # => "Invalid: short" +BrUtils.cpf.format('short') # => "" (on_fail padrão) +``` + +Para listas exaustivas de exceções e comportamento em casos extremos, consulte o README de cada [pacote incluído](#pacotes-incluídos). + +### Pacotes incluídos + +| Pacote | Principais recursos | README | +|---------|----------------|--------| +| [`cpf-utilities`](https://rubygems.org/gems/cpf-utilities) | `CpfUtils`, `CpfFormatter`, `CpfGenerator`, `CpfValidator`, `CpfFmt.cpf_fmt`, `CpfGen.cpf_gen`, `CpfVal.cpf_val` | [docs](../cpf-utilities/README.pt.md) | +| [`cnpj-utilities`](https://rubygems.org/gems/cnpj-utilities) | `CnpjUtils`, `CnpjFormatter`, `CnpjGenerator`, `CnpjValidator`, `CnpjFmt.cnpj_fmt`, `CnpjGen.cnpj_gen`, `CnpjVal.cnpj_val` | [docs](../cnpj-utilities/README.pt.md) | + +Todos os acima são puxados como dependências de **`br-utilities`**. Demos interativas: [CPF](https://cpf-utils.vercel.app/) e [CNPJ](https://cnpj-utils.vercel.app/). + +## Contribuição e suporte + +Contribuições são bem-vindas! Consulte as [Diretrizes de contribuição](https://github.com/LacusSolutions/br-utils-ruby/blob/main/CONTRIBUTING.md). Se o projeto for útil para você, considere: + +- ⭐ Dar uma estrela no repositório +- 🤝 Contribuir com código +- 💡 [Sugerir novas funcionalidades](https://github.com/LacusSolutions/br-utils-ruby/issues) +- 🐛 [Reportar bugs](https://github.com/LacusSolutions/br-utils-ruby/issues) + +## Licença + +Este projeto está sob a licença MIT — veja o arquivo [LICENSE](https://github.com/LacusSolutions/br-utils-ruby/blob/main/LICENSE). + +## Changelog + +Veja o [CHANGELOG](./CHANGELOG.md) para alterações e histórico de versões. + +--- + +Feito com ❤️ por [Lacus Solutions](https://github.com/LacusSolutions) From 2688f4bbe56dc05e6c477371185ee4ca67b8803a Mon Sep 17 00:00:00 2001 From: juliolmuller Date: Thu, 20 Aug 2026 17:32:28 -0300 Subject: [PATCH 08/14] docs(br-utils): update README files to clarify delegation in unified API Adjustment as per @coderabbitai review comment at https://github.com/LacusSolutions/br-utils-ruby/pull/30#discussion_r3753265985. Co-authored-by: CodeRabbit AI <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Cursor Agent --- README.md | 2 +- README.pt.md | 2 +- packages/br-utilities/CHANGELOG.md | 2 +- packages/br-utilities/README.md | 2 +- packages/br-utilities/README.pt.md | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index abc1e1d..a48d42c 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Requires Ruby **≥ 3.1** (see `required_ruby_version` in the gemspec). ## Features -- ✅ **Unified top-level API**: Class helpers `BrUtils.cpf` / `.cnpj` alias `BrUtils::DEFAULT`; each domain offers `format`, `generate`, and `is_valid` +- ✅ **Unified top-level API**: Class helpers `BrUtils.cpf` / `.cnpj` delegate to `BrUtils::DEFAULT.cpf` / `.cnpj`; each domain offers `format`, `generate`, and `is_valid` - ✅ **Bundled domains**: [`cpf-utilities`](https://rubygems.org/gems/cpf-utilities) and [`cnpj-utilities`](https://rubygems.org/gems/cnpj-utilities) installed together - ✅ **Alphanumeric CNPJ**: Full support for the new alphanumeric CNPJ format (introduced in 2026) - ✅ **Reusable instance**: `BrUtils` class with optional default CPF and CNPJ settings (nested mappings, flat component kwargs, or pre-built utils instances) diff --git a/README.pt.md b/README.pt.md index 40d50ac..5e31afd 100644 --- a/README.pt.md +++ b/README.pt.md @@ -16,7 +16,7 @@ Requer Ruby **≥ 3.1** (veja `required_ruby_version` no gemspec). ## Recursos -- ✅ **API unificada de alto nível**: Helpers de classe `BrUtils.cpf` / `.cnpj` alias de `BrUtils::DEFAULT`; cada domínio oferece `format`, `generate` e `is_valid` +- ✅ **API unificada de alto nível**: Helpers de classe `BrUtils.cpf` / `.cnpj` encaminham para `BrUtils::DEFAULT.cpf` / `.cnpj`; cada domínio oferece `format`, `generate` e `is_valid` - ✅ **Domínios empacotados**: [`cpf-utilities`](https://rubygems.org/gems/cpf-utilities) e [`cnpj-utilities`](https://rubygems.org/gems/cnpj-utilities) instalados juntos - ✅ **CNPJ alfanumérico**: Suporte completo ao novo formato alfanumérico de CNPJ (a partir de 2026) - ✅ **Instância reutilizável**: Classe `BrUtils` com configurações padrão opcionais de CPF e CNPJ (mapeamentos aninhados, kwargs planos de componentes ou instâncias prontas de utils) diff --git a/packages/br-utilities/CHANGELOG.md b/packages/br-utilities/CHANGELOG.md index 87656f0..0d35ee6 100644 --- a/packages/br-utilities/CHANGELOG.md +++ b/packages/br-utilities/CHANGELOG.md @@ -8,7 +8,7 @@ Unified toolkit to deal with Brazilian documents (CPF and CNPJ): validation, for - **Unified façade**: `BrUtils` aggregates configurable `CpfUtils` and `CnpjUtils` behind `#cpf` / `#cnpj` accessors (with setters; `nil` resets to defaults). - **Constructor flexibility**: accept a settings `Hash` or keyword args (`cpf:` / `cnpj:` nested mappings, or flat `cpf_formatter:` / `cnpj_validator:`-style kwargs; nested wins when both present). -- **Quick helpers**: `BrUtils.cpf` / `.cnpj` alias mutable `BrUtils::DEFAULT` (process-wide; prefer `BrUtils.new` under concurrency). +- **Quick helpers**: `BrUtils.cpf` / `.cnpj` delegate to `BrUtils::DEFAULT.cpf` / `.cnpj` (process-wide; prefer `BrUtils.new` under concurrency). - **Two-tier re-exports**: main classes at the façade root (`BrUtils::CpfFormatter`, `BrUtils::CnpjValidator`, …); full sibling surface under `BrUtils::CpfFmt` / `CpfUtils` / `CnpjFmt` / …. - **One install**: depends on `cpf-utilities` and `cnpj-utilities` so both domains ship without requiring each gem separately. - **Alphanumeric CNPJ**: CNPJ path supports 14-character alphanumeric IDs via `cnpj-utilities` (numeric CPF via `cpf-utilities`). diff --git a/packages/br-utilities/README.md b/packages/br-utilities/README.md index abc1e1d..a48d42c 100644 --- a/packages/br-utilities/README.md +++ b/packages/br-utilities/README.md @@ -23,7 +23,7 @@ Requires Ruby **≥ 3.1** (see `required_ruby_version` in the gemspec). ## Features -- ✅ **Unified top-level API**: Class helpers `BrUtils.cpf` / `.cnpj` alias `BrUtils::DEFAULT`; each domain offers `format`, `generate`, and `is_valid` +- ✅ **Unified top-level API**: Class helpers `BrUtils.cpf` / `.cnpj` delegate to `BrUtils::DEFAULT.cpf` / `.cnpj`; each domain offers `format`, `generate`, and `is_valid` - ✅ **Bundled domains**: [`cpf-utilities`](https://rubygems.org/gems/cpf-utilities) and [`cnpj-utilities`](https://rubygems.org/gems/cnpj-utilities) installed together - ✅ **Alphanumeric CNPJ**: Full support for the new alphanumeric CNPJ format (introduced in 2026) - ✅ **Reusable instance**: `BrUtils` class with optional default CPF and CNPJ settings (nested mappings, flat component kwargs, or pre-built utils instances) diff --git a/packages/br-utilities/README.pt.md b/packages/br-utilities/README.pt.md index 40d50ac..5e31afd 100644 --- a/packages/br-utilities/README.pt.md +++ b/packages/br-utilities/README.pt.md @@ -16,7 +16,7 @@ Requer Ruby **≥ 3.1** (veja `required_ruby_version` no gemspec). ## Recursos -- ✅ **API unificada de alto nível**: Helpers de classe `BrUtils.cpf` / `.cnpj` alias de `BrUtils::DEFAULT`; cada domínio oferece `format`, `generate` e `is_valid` +- ✅ **API unificada de alto nível**: Helpers de classe `BrUtils.cpf` / `.cnpj` encaminham para `BrUtils::DEFAULT.cpf` / `.cnpj`; cada domínio oferece `format`, `generate` e `is_valid` - ✅ **Domínios empacotados**: [`cpf-utilities`](https://rubygems.org/gems/cpf-utilities) e [`cnpj-utilities`](https://rubygems.org/gems/cnpj-utilities) instalados juntos - ✅ **CNPJ alfanumérico**: Suporte completo ao novo formato alfanumérico de CNPJ (a partir de 2026) - ✅ **Instância reutilizável**: Classe `BrUtils` com configurações padrão opcionais de CPF e CNPJ (mapeamentos aninhados, kwargs planos de componentes ou instâncias prontas de utils) From f5eb7f9fce51086580bcc80e1a837d150ae6d7ad Mon Sep 17 00:00:00 2001 From: juliolmuller Date: Thu, 20 Aug 2026 17:42:46 -0300 Subject: [PATCH 09/14] docs(br-utils): update README files to clarify error propagation API Adjustment as per @coderabbitai review comment at https://github.com/LacusSolutions/br-utils-ruby/pull/30#discussion_r3753265989. Co-authored-by: CodeRabbit AI <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Cursor Agent --- README.md | 614 +++++++++++++++++++++++++++-- README.pt.md | 614 +++++++++++++++++++++++++++-- packages/br-utilities/CHANGELOG.md | 2 +- packages/br-utilities/README.md | 614 +++++++++++++++++++++++++++-- packages/br-utilities/README.pt.md | 614 +++++++++++++++++++++++++++-- 5 files changed, 2333 insertions(+), 125 deletions(-) diff --git a/README.md b/README.md index a48d42c..8b2f19c 100644 --- a/README.md +++ b/README.md @@ -607,12 +607,19 @@ end ```ruby require 'br-utilities' -# 2) BrUtils::DomainError — not applicable: this gem defines no DomainError -# (and no domain leaves). Domain failures come from bundled packages only. -# begin -# BrUtils.new(cpf: { formatter: { hidden_start: -1 } }) -# rescue BrUtils::DomainError # NameError — constant is not defined -# end +# 2) Bundled DomainError — this gem defines no DomainError; domain failures +# come from bundled packages and keep those namespaces (e.g. CpfFmt, CnpjFmt). +begin + BrUtils.new.cpf.format('12345678909', hidden_start: -1) +rescue CpfFmt::DomainError + # CpfFmt::OutOfRangeError, CpfFmt::ValidationError, and other DomainError subclasses +end + +begin + BrUtils.new.cnpj.format('91415732000793', hidden_start: -1) +rescue CnpjFmt::DomainError + # CnpjFmt::OutOfRangeError, CnpjFmt::ValidationError, and other DomainError subclasses +end ``` ```ruby @@ -640,41 +647,586 @@ end #### Propagated from bundled packages -`BrUtils` does not redefine domain exception types. Construction, setters, and domain method calls raise the same errors as [`cpf-utilities`](../cpf-utilities/README.md) and [`cnpj-utilities`](../cnpj-utilities/README.md): +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/CNPJ **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` or `CnpjFmt::InvalidLengthError` (default `on_fail` returns `''`). -- **CPF formatting**: `CpfFmt::TypeMismatchError`, `CpfFmt::OutOfRangeError`, `CpfFmt::ValidationError`, `CpfFmt::InvalidLengthError` (passed to `on_fail`, not raised by `#format`), and related classes. -- **CPF generation**: `CpfGen::TypeMismatchError`, `CpfGen::ValidationError`, and related classes. -- **CPF validation**: `CpfVal::TypeMismatchError` and related classes. -- **CNPJ formatting**: `CnpjFmt::TypeMismatchError`, `CnpjFmt::OutOfRangeError`, `CnpjFmt::ValidationError`, `CnpjFmt::InvalidLengthError` (passed to `on_fail`), and related classes. -- **CNPJ generation**: `CnpjGen::TypeMismatchError`, `CnpjGen::ValidationError`, and related classes. -- **CNPJ validation**: `CnpjVal::TypeMismatchError`, `CnpjVal::ValidationError`, and related classes. +`CpfUtils::*` / `CnpjUtils::*` misuse errors also propagate when nested aggregators are constructed or called through `BrUtils`. For exhaustive option tables and extra edge cases, see [`cpf-utilities`](../cpf-utilities/README.md) and [`cnpj-utilities`](../cnpj-utilities/README.md). -Invalid option types are typically **`TypeError`** subclasses (`*::TypeMismatchError`); invalid option values are domain errors under each package’s `DomainError` hierarchy. CPF and CNPJ validation failures return `false`. Formatting length failures are handled by **`on_fail`** (default returns an empty string). +##### Summary + +| Class | Inherits from | Category | Trigger condition | +|-------|---------------|----------|-------------------| +| `CpfUtils::InvalidArgumentCombinationError` | `CpfUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CpfUtils::Error`) | API misuse | Both an options instance/`Hash` and any non-`nil` keyword on `CpfUtils#format` / `#generate` | +| `CpfUtils::TypeMismatchError` | `CpfUtils::TypeMismatchError < TypeError < StandardError` (+ `include CpfUtils::Error`) | API misuse | Non-`nil` nested `cpf` settings `Hash` contract violation on `CpfUtils.new` | +| `CnpjUtils::InvalidArgumentCombinationError` | `CnpjUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjUtils::Error`) | API misuse | Both settings/options and any non-`nil` keyword on `CnpjUtils` construct/`#format` / `#generate` / `#is_valid` | +| `CnpjUtils::TypeMismatchError` | `CnpjUtils::TypeMismatchError < TypeError < StandardError` (+ `include CnpjUtils::Error`) | API misuse | Non-`nil` nested `cnpj` settings `Hash` contract violation on `CnpjUtils.new` | +| `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` | +| `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`) | +| `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` | +| `CpfGen::TypeMismatchError` | `CpfGen::TypeMismatchError < TypeError < StandardError` (+ `include CpfGen::Error`) | API misuse | Generator option (`format` / `prefix`) has the wrong type | +| `CpfVal::TypeMismatchError` | `CpfVal::TypeMismatchError < TypeError < StandardError` (+ `include CpfVal::Error`) | API misuse | CPF input is not a `String` or `Array` of strings | +| `CnpjFmt::InvalidArgumentCombinationError` | `CnpjFmt::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjFmt::Error`) | API misuse | Both an `options` instance/`Hash` and any non-`nil` keyword on `CnpjFormatter` / `cnpj_fmt` | +| `CnpjFmt::TypeMismatchError` | `CnpjFmt::TypeMismatchError < TypeError < StandardError` (+ `include CnpjFmt::Error`) | API misuse | CNPJ input or formatter option has the wrong type (or `on_fail` return is not a `String`) | +| `CnpjGen::InvalidArgumentCombinationError` | `CnpjGen::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjGen::Error`) | API misuse | Both an `options` instance/`Hash` and any non-`nil` keyword on `CnpjGenerator` / `cnpj_gen` | +| `CnpjGen::TypeMismatchError` | `CnpjGen::TypeMismatchError < TypeError < StandardError` (+ `include CnpjGen::Error`) | API misuse | Generator option (`format` / `prefix` / `type`) has the wrong type | +| `CnpjVal::InvalidArgumentCombinationError` | `CnpjVal::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjVal::Error`) | API misuse | Both an `options` instance/`Hash` and any non-`nil` keyword on `CnpjValidator` / `cnpj_val` | +| `CnpjVal::TypeMismatchError` | `CnpjVal::TypeMismatchError < TypeError < StandardError` (+ `include CnpjVal::Error`) | API misuse | CNPJ input or validator option has the wrong type | +| `CpfFmt::InvalidLengthError` | `CpfFmt::InvalidLengthError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Domain error | Sanitized length ≠ 11 — **passed to `on_fail`**, not raised by `#format` | +| `CpfFmt::OutOfRangeError` | `CpfFmt::OutOfRangeError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Domain error | `hidden_start` / `hidden_end` outside `0`–`10` | +| `CpfFmt::ValidationError` | `CpfFmt::ValidationError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Domain error | `hidden_key` / `dot_key` / `dash_key` contains a disallowed character | +| `CpfGen::ValidationError` | `CpfGen::ValidationError < CpfGen::DomainError < RangeError < StandardError` (+ `include CpfGen::Error`) | Domain error | `prefix` is ineligible (zeroed base or 9 repeated digits) | +| `CnpjFmt::InvalidLengthError` | `CnpjFmt::InvalidLengthError < CnpjFmt::DomainError < RangeError < StandardError` (+ `include CnpjFmt::Error`) | Domain error | Sanitized length ≠ 14 — **passed to `on_fail`**, not raised by `#format` | +| `CnpjFmt::OutOfRangeError` | `CnpjFmt::OutOfRangeError < CnpjFmt::DomainError < RangeError < StandardError` (+ `include CnpjFmt::Error`) | Domain error | `hidden_start` / `hidden_end` outside `0`–`13` | +| `CnpjFmt::ValidationError` | `CnpjFmt::ValidationError < CnpjFmt::DomainError < RangeError < StandardError` (+ `include CnpjFmt::Error`) | Domain error | `hidden_key` / `dot_key` / `slash_key` / `dash_key` contains a disallowed character | +| `CnpjGen::ValidationError` | `CnpjGen::ValidationError < CnpjGen::DomainError < RangeError < StandardError` (+ `include CnpjGen::Error`) | Domain error | Ineligible `prefix`, or `type` not in `'alphabetic'` / `'alphanumeric'` / `'numeric'` | +| `CnpjVal::ValidationError` | `CnpjVal::ValidationError < CnpjVal::DomainError < RangeError < StandardError` (+ `include CnpjVal::Error`) | Domain error | Validator `type` is not `'alphanumeric'` or `'numeric'` | + +##### `CpfFmt::DomainError` + +- **Inheritance:** `CpfFmt::DomainError < RangeError < StandardError` (includes `CpfFmt::Error`) +- **Category:** Domain error — ancestor for formatter domain leaves. +- **When it is raised:** Not raised directly; rescue target for `OutOfRangeError`, `ValidationError`, and re-raised `InvalidLengthError`. +- **Example:** Prefer rescuing a leaf, or `CpfFmt::DomainError` for all CPF formatter domain failures. +- **How to rescue it:** ```ruby -require 'br-utilities' +rescue CpfFmt::DomainError + # OutOfRangeError, ValidationError, InvalidLengthError (if re-raised from on_fail) +``` -begin - BrUtils.new.cnpj.format(12_345) -rescue CnpjFmt::TypeMismatchError => e - puts e.message -end +##### `CpfFmt::TypeMismatchError` -begin - BrUtils.new.cnpj.is_valid(12_345_678_000_198) -rescue CnpjVal::TypeMismatchError => e - puts e.message -end +- **Inheritance:** `CpfFmt::TypeMismatchError < TypeError < StandardError` (includes `CpfFmt::Error`) +- **Category:** API misuse — wrong type for CPF input or a formatter option. +- **When it is raised:** Raised when `#format` / `cpf_fmt` receives a non-`String` / non-`Array` input, an option has the wrong type, or `on_fail` does not return a `String`. +- **Example:** + +```ruby +BrUtils.new.cpf.format(12_345) # raises CpfFmt::TypeMismatchError +``` + +- **How to rescue it:** + +```ruby +rescue CpfFmt::TypeMismatchError + # formatter type-contract violation + +rescue TypeError + # native type errors, including CpfFmt::TypeMismatchError +``` + +##### `CpfFmt::InvalidArgumentCombinationError` + +- **Inheritance:** `CpfFmt::InvalidArgumentCombinationError < ArgumentError < StandardError` (includes `CpfFmt::Error`) +- **Category:** API misuse — mixed `options` and keywords on the formatter API. +- **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 CPF aggregator raises `CpfUtils::InvalidArgumentCombinationError` for the same pattern on `CpfUtils#format`.) +- **Example:** + +```ruby +CpfFmt::CpfFormatter.new({ dash_key: '_' }, hidden: true) +# raises CpfFmt::InvalidArgumentCombinationError +``` + +- **How to rescue it:** + +```ruby +rescue CpfFmt::InvalidArgumentCombinationError + # formatter invalid signature combination + +rescue ArgumentError + # native argument errors, including this one +``` + +##### `CpfFmt::InvalidLengthError` (callback-delivered) + +- **Inheritance:** `CpfFmt::InvalidLengthError < CpfFmt::DomainError < RangeError < StandardError` (includes `CpfFmt::Error`) +- **Category:** Domain error — sanitized CPF length is not exactly 11. +- **When it is raised:** **Not raised** by `#format` / `cpf_fmt`; constructed and passed as the second argument to `on_fail`. +- **Example:** + +```ruby +custom_fail = ->(value, error) { + error # => # + "Invalid CPF: #{value}" +} + +BrUtils.new.cpf.format('123', on_fail: custom_fail) # => "Invalid CPF: 123" +BrUtils.new.cpf.format('123') # => "" (default on_fail) +``` + +- **How to rescue it:** Handle inside `on_fail` (typical), or rescue if you re-raise: + +```ruby +rescue CpfFmt::InvalidLengthError + # this exact length violation + +rescue CpfFmt::DomainError + # RangeError-rooted domain failures from cpf-fmt +``` + +##### `CpfFmt::OutOfRangeError` + +- **Inheritance:** `CpfFmt::OutOfRangeError < CpfFmt::DomainError < RangeError < StandardError` (includes `CpfFmt::Error`) +- **Category:** Domain error — `hidden_start` / `hidden_end` outside `0`–`10`. +- **When it is raised:** Raised when building or applying formatter options with an out-of-range hide index. +- **Example:** -# Custom on_fail for invalid length -custom_fail = ->(value, _exception) { "Invalid: #{value}" } +```ruby +BrUtils.new.cpf.format('12345678909', hidden_start: -1) # raises CpfFmt::OutOfRangeError +``` + +- **How to rescue it:** + +```ruby +rescue CpfFmt::OutOfRangeError + # this exact range violation + +rescue CpfFmt::DomainError + # RangeError-rooted domain failures from cpf-fmt +``` + +##### `CpfFmt::ValidationError` + +- **Inheritance:** `CpfFmt::ValidationError < CpfFmt::DomainError < RangeError < StandardError` (includes `CpfFmt::Error`) +- **Category:** Domain error — a key option contains a disallowed character. +- **When it is raised:** Raised when `hidden_key`, `dot_key`, or `dash_key` contains a forbidden character. +- **Example:** + +```ruby +BrUtils.new(cpf: { formatter: { dot_key: 'å' } }) # raises CpfFmt::ValidationError +``` + +- **How to rescue it:** + +```ruby +rescue CpfFmt::ValidationError + # this exact domain validation failure + +rescue CpfFmt::DomainError + # RangeError-rooted domain failures from cpf-fmt +``` + +##### `CpfGen::DomainError` + +- **Inheritance:** `CpfGen::DomainError < RangeError < StandardError` (includes `CpfGen::Error`) +- **Category:** Domain error — ancestor for generator domain leaves. +- **When it is raised:** Not raised directly; rescue target for `CpfGen::ValidationError`. +- **Example:** Prefer `rescue CpfGen::ValidationError` or `CpfGen::DomainError`. +- **How to rescue it:** -BrUtils.cpf.format('short', on_fail: custom_fail) # => "Invalid: short" -BrUtils.cnpj.format('short', on_fail: custom_fail) # => "Invalid: short" -BrUtils.cpf.format('short') # => "" (default on_fail) +```ruby +rescue CpfGen::DomainError + # ValidationError and other DomainError subclasses from cpf-gen +``` + +##### `CpfGen::TypeMismatchError` + +- **Inheritance:** `CpfGen::TypeMismatchError < TypeError < StandardError` (includes `CpfGen::Error`) +- **Category:** API misuse — wrong type for a generator option. +- **When it is raised:** Raised when `format` or `prefix` has the wrong runtime type. +- **Example:** + +```ruby +BrUtils.new.cpf.generate(prefix: 123) # raises CpfGen::TypeMismatchError +``` + +- **How to rescue it:** + +```ruby +rescue CpfGen::TypeMismatchError + # generator type-contract violation + +rescue TypeError + # native type errors, including CpfGen::TypeMismatchError +``` + +##### `CpfGen::InvalidArgumentCombinationError` + +- **Inheritance:** `CpfGen::InvalidArgumentCombinationError < ArgumentError < StandardError` (includes `CpfGen::Error`) +- **Category:** API misuse — mixed `options` and keywords on the generator API. +- **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 CPF aggregator raises `CpfUtils::InvalidArgumentCombinationError` for the same pattern on `CpfUtils#generate`.) +- **Example:** + +```ruby +CpfGen::CpfGenerator.new({ format: true }, prefix: '123') +# raises CpfGen::InvalidArgumentCombinationError +``` + +- **How to rescue it:** + +```ruby +rescue CpfGen::InvalidArgumentCombinationError + # generator invalid signature combination + +rescue ArgumentError + # native argument errors, including this one +``` + +##### `CpfGen::ValidationError` + +- **Inheritance:** `CpfGen::ValidationError < CpfGen::DomainError < RangeError < StandardError` (includes `CpfGen::Error`) +- **Category:** Domain error — ineligible `prefix`. +- **When it is raised:** Raised when `prefix` is a zeroed base (`'000000000'`) or 9 repeated digits (e.g. `'999999999'`). +- **Example:** + +```ruby +BrUtils.new.cpf.generate(prefix: '000000000') # raises CpfGen::ValidationError +``` + +- **How to rescue it:** + +```ruby +rescue CpfGen::ValidationError + # this exact domain validation failure + +rescue CpfGen::DomainError + # RangeError-rooted domain failures from cpf-gen +``` + +##### `CpfVal::TypeMismatchError` + +- **Inheritance:** `CpfVal::TypeMismatchError < TypeError < StandardError` (includes `CpfVal::Error`) +- **Category:** API misuse — wrong type for CPF input. +- **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. +- **Example:** + +```ruby +BrUtils.new.cpf.is_valid(12_345_678_909) # raises CpfVal::TypeMismatchError +BrUtils.new.cpf.is_valid('12345678900') # => false (invalid data, no raise) +``` + +- **How to rescue it:** + +```ruby +rescue CpfVal::TypeMismatchError + # validator type-contract violation + +rescue TypeError + # native type errors, including CpfVal::TypeMismatchError +``` + +##### `CnpjFmt::DomainError` + +- **Inheritance:** `CnpjFmt::DomainError < RangeError < StandardError` (includes `CnpjFmt::Error`) +- **Category:** Domain error — ancestor for formatter domain leaves. +- **When it is raised:** Not raised directly; rescue target for `OutOfRangeError`, `ValidationError`, and re-raised `InvalidLengthError`. +- **Example:** Prefer rescuing a leaf, or `CnpjFmt::DomainError` for all CNPJ formatter domain failures. +- **How to rescue it:** + +```ruby +rescue CnpjFmt::DomainError + # OutOfRangeError, ValidationError, InvalidLengthError (if re-raised from on_fail) +``` + +##### `CnpjFmt::TypeMismatchError` + +- **Inheritance:** `CnpjFmt::TypeMismatchError < TypeError < StandardError` (includes `CnpjFmt::Error`) +- **Category:** API misuse — wrong type for CNPJ input or a formatter option. +- **When it is raised:** Raised when `#format` / `cnpj_fmt` receives a non-`String` / non-`Array` input, an option has the wrong type, or `on_fail` does not return a `String`. +- **Example:** + +```ruby +BrUtils.new.cnpj.format(12_345) # raises CnpjFmt::TypeMismatchError +``` + +- **How to rescue it:** + +```ruby +rescue CnpjFmt::TypeMismatchError + # formatter type-contract violation + +rescue TypeError + # native type errors, including CnpjFmt::TypeMismatchError +``` + +##### `CnpjFmt::InvalidArgumentCombinationError` + +- **Inheritance:** `CnpjFmt::InvalidArgumentCombinationError < ArgumentError < StandardError` (includes `CnpjFmt::Error`) +- **Category:** API misuse — mixed `options` and keywords on the formatter API. +- **When it is raised:** Raised by `CnpjFmt::CnpjFormatter` / `CnpjFmt.cnpj_fmt` when both an `options` instance/`Hash` and any non-`nil` keyword are passed. (The CNPJ aggregator raises `CnpjUtils::InvalidArgumentCombinationError` for the same pattern on `CnpjUtils#format`.) +- **Example:** + +```ruby +CnpjFmt::CnpjFormatter.new({ slash_key: '|' }, hidden: true) +# raises CnpjFmt::InvalidArgumentCombinationError ``` -For exhaustive exception lists and edge-case behavior, see each [bundled package](#bundled-packages) README. +- **How to rescue it:** + +```ruby +rescue CnpjFmt::InvalidArgumentCombinationError + # formatter invalid signature combination + +rescue ArgumentError + # native argument errors, including this one +``` + +##### `CnpjFmt::InvalidLengthError` (callback-delivered) + +- **Inheritance:** `CnpjFmt::InvalidLengthError < CnpjFmt::DomainError < RangeError < StandardError` (includes `CnpjFmt::Error`) +- **Category:** Domain error — sanitized CNPJ length is not exactly 14. +- **When it is raised:** **Not raised** by `#format` / `cnpj_fmt`; constructed and passed as the second argument to `on_fail`. +- **Example:** + +```ruby +custom_fail = ->(value, error) { + error # => # + "Invalid CNPJ: #{value}" +} + +BrUtils.new.cnpj.format('123', on_fail: custom_fail) # => "Invalid CNPJ: 123" +BrUtils.new.cnpj.format('123') # => "" (default on_fail) +``` + +- **How to rescue it:** Handle inside `on_fail` (typical), or rescue if you re-raise: + +```ruby +rescue CnpjFmt::InvalidLengthError + # this exact length violation + +rescue CnpjFmt::DomainError + # RangeError-rooted domain failures from cnpj-fmt +``` + +##### `CnpjFmt::OutOfRangeError` + +- **Inheritance:** `CnpjFmt::OutOfRangeError < CnpjFmt::DomainError < RangeError < StandardError` (includes `CnpjFmt::Error`) +- **Category:** Domain error — `hidden_start` / `hidden_end` outside `0`–`13`. +- **When it is raised:** Raised when building or applying formatter options with an out-of-range hide index. +- **Example:** + +```ruby +BrUtils.new.cnpj.format('91415732000793', hidden_start: -1) # raises CnpjFmt::OutOfRangeError +``` + +- **How to rescue it:** + +```ruby +rescue CnpjFmt::OutOfRangeError + # this exact range violation + +rescue CnpjFmt::DomainError + # RangeError-rooted domain failures from cnpj-fmt +``` + +##### `CnpjFmt::ValidationError` + +- **Inheritance:** `CnpjFmt::ValidationError < CnpjFmt::DomainError < RangeError < StandardError` (includes `CnpjFmt::Error`) +- **Category:** Domain error — a key option contains a disallowed character. +- **When it is raised:** Raised when `hidden_key`, `dot_key`, `slash_key`, or `dash_key` contains a forbidden character. +- **Example:** + +```ruby +BrUtils.new(cnpj: { formatter: { slash_key: 'å' } }) # raises CnpjFmt::ValidationError +``` + +- **How to rescue it:** + +```ruby +rescue CnpjFmt::ValidationError + # this exact domain validation failure + +rescue CnpjFmt::DomainError + # RangeError-rooted domain failures from cnpj-fmt +``` + +##### `CnpjGen::DomainError` + +- **Inheritance:** `CnpjGen::DomainError < RangeError < StandardError` (includes `CnpjGen::Error`) +- **Category:** Domain error — ancestor for generator domain leaves. +- **When it is raised:** Not raised directly; rescue target for `CnpjGen::ValidationError`. +- **Example:** Prefer `rescue CnpjGen::ValidationError` or `CnpjGen::DomainError`. +- **How to rescue it:** + +```ruby +rescue CnpjGen::DomainError + # ValidationError and other DomainError subclasses from cnpj-gen +``` + +##### `CnpjGen::TypeMismatchError` + +- **Inheritance:** `CnpjGen::TypeMismatchError < TypeError < StandardError` (includes `CnpjGen::Error`) +- **Category:** API misuse — wrong type for a generator option. +- **When it is raised:** Raised when `format`, `prefix`, or `type` has the wrong runtime type. +- **Example:** + +```ruby +BrUtils.new.cnpj.generate(prefix: 123) # raises CnpjGen::TypeMismatchError +``` + +- **How to rescue it:** + +```ruby +rescue CnpjGen::TypeMismatchError + # generator type-contract violation + +rescue TypeError + # native type errors, including CnpjGen::TypeMismatchError +``` + +##### `CnpjGen::InvalidArgumentCombinationError` + +- **Inheritance:** `CnpjGen::InvalidArgumentCombinationError < ArgumentError < StandardError` (includes `CnpjGen::Error`) +- **Category:** API misuse — mixed `options` and keywords on the generator API. +- **When it is raised:** Raised by `CnpjGen::CnpjGenerator` / `CnpjGen.cnpj_gen` when both an `options` instance/`Hash` and any non-`nil` keyword are passed. (The CNPJ aggregator raises `CnpjUtils::InvalidArgumentCombinationError` for the same pattern on `CnpjUtils#generate`.) +- **Example:** + +```ruby +CnpjGen::CnpjGenerator.new({ format: true }, prefix: '123') +# raises CnpjGen::InvalidArgumentCombinationError +``` + +- **How to rescue it:** + +```ruby +rescue CnpjGen::InvalidArgumentCombinationError + # generator invalid signature combination + +rescue ArgumentError + # native argument errors, including this one +``` + +##### `CnpjGen::ValidationError` + +- **Inheritance:** `CnpjGen::ValidationError < CnpjGen::DomainError < RangeError < StandardError` (includes `CnpjGen::Error`) +- **Category:** Domain error — ineligible `prefix` or disallowed `type`. +- **When it is raised:** Raised when `prefix` is a zeroed base/branch ID or 12 repeated digits, or when `type` is not `'alphabetic'`, `'alphanumeric'`, or `'numeric'`. +- **Example:** + +```ruby +BrUtils.new.cnpj.generate(type: 'boolean') # raises CnpjGen::ValidationError +``` + +- **How to rescue it:** + +```ruby +rescue CnpjGen::ValidationError + # this exact domain validation failure + +rescue CnpjGen::DomainError + # RangeError-rooted domain failures from cnpj-gen +``` + +##### `CnpjVal::DomainError` + +- **Inheritance:** `CnpjVal::DomainError < RangeError < StandardError` (includes `CnpjVal::Error`) +- **Category:** Domain error — ancestor for validator domain leaves. +- **When it is raised:** Not raised directly; rescue target for `CnpjVal::ValidationError`. +- **Example:** Prefer `rescue CnpjVal::ValidationError` or `CnpjVal::DomainError`. +- **How to rescue it:** + +```ruby +rescue CnpjVal::DomainError + # ValidationError and other DomainError subclasses from cnpj-val +``` + +##### `CnpjVal::TypeMismatchError` + +- **Inheritance:** `CnpjVal::TypeMismatchError < TypeError < StandardError` (includes `CnpjVal::Error`) +- **Category:** API misuse — wrong type for CNPJ input or a validator option. +- **When it is raised:** Raised when `#is_valid` / `cnpj_val` receives a value that is not a `String` or an `Array` of strings, or a validator option has the wrong type. Invalid CNPJ **data** returns `false` and does not raise. +- **Example:** + +```ruby +BrUtils.new.cnpj.is_valid(12_345_678_000_198) # raises CnpjVal::TypeMismatchError +BrUtils.new.cnpj.is_valid('00000000000000') # => false (invalid data, no raise) +``` + +- **How to rescue it:** + +```ruby +rescue CnpjVal::TypeMismatchError + # validator type-contract violation + +rescue TypeError + # native type errors, including CnpjVal::TypeMismatchError +``` + +##### `CnpjVal::InvalidArgumentCombinationError` + +- **Inheritance:** `CnpjVal::InvalidArgumentCombinationError < ArgumentError < StandardError` (includes `CnpjVal::Error`) +- **Category:** API misuse — mixed `options` and keywords on the validator API. +- **When it is raised:** Raised by `CnpjVal::CnpjValidator` / `CnpjVal.cnpj_val` when both an `options` instance/`Hash` and any non-`nil` keyword are passed. (The CNPJ aggregator raises `CnpjUtils::InvalidArgumentCombinationError` for the same pattern on `CnpjUtils#is_valid`.) +- **Example:** + +```ruby +CnpjVal.cnpj_val('98765432000198', { type: 'numeric' }, case_sensitive: false) +# raises CnpjVal::InvalidArgumentCombinationError +``` + +- **How to rescue it:** + +```ruby +rescue CnpjVal::InvalidArgumentCombinationError + # validator invalid signature combination + +rescue ArgumentError + # native argument errors, including this one +``` + +##### `CnpjVal::ValidationError` + +- **Inheritance:** `CnpjVal::ValidationError < CnpjVal::DomainError < RangeError < StandardError` (includes `CnpjVal::Error`) +- **Category:** Domain error — disallowed validator `type`. +- **When it is raised:** Raised when `type` is not `'alphanumeric'` or `'numeric'`. +- **Example:** + +```ruby +BrUtils.new.cnpj.is_valid('91415732000793', type: 'boolean') # raises CnpjVal::ValidationError +``` + +- **How to rescue it:** + +```ruby +rescue CnpjVal::ValidationError + # this exact domain validation failure + +rescue CnpjVal::DomainError + # RangeError-rooted domain failures from cnpj-val +``` + +##### `CpfUtils::TypeMismatchError` / `CpfUtils::InvalidArgumentCombinationError` + +- **Inheritance:** `CpfUtils::TypeMismatchError < TypeError` and `CpfUtils::InvalidArgumentCombinationError < ArgumentError` (both include `CpfUtils::Error`). +- **Category:** API misuse on the nested CPF aggregator. +- **When it is raised:** Raised when nested `cpf` settings to `CpfUtils.new` are a non-`Hash`, or when `CpfUtils#format` / `#generate` mix an options `Hash` with keywords. +- **Example:** + +```ruby +BrUtils.new.cpf.format({ hidden: true }, dash_key: '|') +# raises CpfUtils::InvalidArgumentCombinationError +``` + +- **How to rescue it:** + +```ruby +rescue CpfUtils::TypeMismatchError, CpfUtils::InvalidArgumentCombinationError + # CPF aggregator misuse (not BrUtils::Error) + +rescue CpfUtils::Error + # every custom error that includes CpfUtils::Error +``` + +##### `CnpjUtils::TypeMismatchError` / `CnpjUtils::InvalidArgumentCombinationError` + +- **Inheritance:** `CnpjUtils::TypeMismatchError < TypeError` and `CnpjUtils::InvalidArgumentCombinationError < ArgumentError` (both include `CnpjUtils::Error`). +- **Category:** API misuse on the nested CNPJ aggregator. +- **When it is raised:** Raised when nested `cnpj` settings to `CnpjUtils.new` are a non-`Hash`, or when `CnpjUtils#format` / `#generate` / `#is_valid` mix settings/options with keywords. +- **Example:** + +```ruby +BrUtils.new.cnpj.format({ hidden: true }, slash_key: '|') +# raises CnpjUtils::InvalidArgumentCombinationError +``` + +- **How to rescue it:** + +```ruby +rescue CnpjUtils::TypeMismatchError, CnpjUtils::InvalidArgumentCombinationError + # CNPJ aggregator misuse (not BrUtils::Error) + +rescue CnpjUtils::Error + # every custom error that includes CnpjUtils::Error +``` ### Bundled packages diff --git a/README.pt.md b/README.pt.md index 5e31afd..4a35802 100644 --- a/README.pt.md +++ b/README.pt.md @@ -600,12 +600,19 @@ end ```ruby require 'br-utilities' -# 2) BrUtils::DomainError — não aplicável: esta gem não define DomainError -# (nem folhas de domínio). Falhas de domínio vêm apenas dos pacotes incluídos. -# begin -# BrUtils.new(cpf: { formatter: { hidden_start: -1 } }) -# rescue BrUtils::DomainError # NameError — constante não definida -# end +# 2) DomainError dos pacotes — esta gem não define DomainError; falhas de domínio +# vêm dos pacotes incluídos e mantêm esses namespaces (ex.: CpfFmt, CnpjFmt). +begin + BrUtils.new.cpf.format('12345678909', hidden_start: -1) +rescue CpfFmt::DomainError + # CpfFmt::OutOfRangeError, CpfFmt::ValidationError e outras subclasses de DomainError +end + +begin + BrUtils.new.cnpj.format('91415732000793', hidden_start: -1) +rescue CnpjFmt::DomainError + # CnpjFmt::OutOfRangeError, CnpjFmt::ValidationError e outras subclasses de DomainError +end ``` ```ruby @@ -633,41 +640,586 @@ end #### Propagados dos pacotes incluídos -`BrUtils` não redefine tipos de exceção de domínio. Construção, setters e chamadas de métodos de domínio lançam os mesmos erros de [`cpf-utilities`](../cpf-utilities/README.pt.md) e [`cnpj-utilities`](../cnpj-utilities/README.pt.md): +Os erros dos componentes mantêm os namespaces dos pacotes e se propagam inalterados pela fachada (e pelas APIs aninhadas / irmãs na raiz). Cada pacote também expõe um módulo marcador `*::Error` para rescue em toda a biblioteca. **Dados** inválidos de CPF/CNPJ em `#is_valid` retornam `false` (sem raise de domínio). Falha de comprimento na formatação **não** é lançada por `#format` — é entregue a **`on_fail`** como `CpfFmt::InvalidLengthError` ou `CnpjFmt::InvalidLengthError` (`on_fail` padrão retorna `''`). -- **Formatação de CPF**: `CpfFmt::TypeMismatchError`, `CpfFmt::OutOfRangeError`, `CpfFmt::ValidationError`, `CpfFmt::InvalidLengthError` (passado a `on_fail`, não lançado por `#format`) e classes relacionadas. -- **Geração de CPF**: `CpfGen::TypeMismatchError`, `CpfGen::ValidationError` e classes relacionadas. -- **Validação de CPF**: `CpfVal::TypeMismatchError` e classes relacionadas. -- **Formatação de CNPJ**: `CnpjFmt::TypeMismatchError`, `CnpjFmt::OutOfRangeError`, `CnpjFmt::ValidationError`, `CnpjFmt::InvalidLengthError` (passado a `on_fail`) e classes relacionadas. -- **Geração de CNPJ**: `CnpjGen::TypeMismatchError`, `CnpjGen::ValidationError` e classes relacionadas. -- **Validação de CNPJ**: `CnpjVal::TypeMismatchError`, `CnpjVal::ValidationError` e classes relacionadas. +`CpfUtils::*` / `CnpjUtils::*` de uso indevido também se propagam quando agregadores aninhados são construídos ou chamados via `BrUtils`. Para tabelas de opções e casos extremos, veja [`cpf-utilities`](../cpf-utilities/README.pt.md) e [`cnpj-utilities`](../cnpj-utilities/README.pt.md). -Tipos de opção inválidos são tipicamente subclasses de **`TypeError`** (`*::TypeMismatchError`); valores de opção inválidos são erros de domínio sob a hierarquia `DomainError` de cada pacote. Falhas de validação de CPF e CNPJ retornam `false`. Falhas de comprimento na formatação são tratadas por **`on_fail`** (padrão retorna string vazia). +##### Resumo + +| Classe | Herda de | Categoria | Condição de disparo | +|-------|---------------|----------|-------------------| +| `CpfUtils::InvalidArgumentCombinationError` | `CpfUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CpfUtils::Error`) | Uso indevido da API | Instância/`Hash` de options e qualquer argumento nomeado não-`nil` em `CpfUtils#format` / `#generate` | +| `CpfUtils::TypeMismatchError` | `CpfUtils::TypeMismatchError < TypeError < StandardError` (+ `include CpfUtils::Error`) | Uso indevido da API | Violação de contrato do `Hash` de settings `cpf` aninhado em `CpfUtils.new` | +| `CnpjUtils::InvalidArgumentCombinationError` | `CnpjUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjUtils::Error`) | Uso indevido da API | Settings/options e qualquer argumento nomeado não-`nil` na construção de `CnpjUtils` / `#format` / `#generate` / `#is_valid` | +| `CnpjUtils::TypeMismatchError` | `CnpjUtils::TypeMismatchError < TypeError < StandardError` (+ `include CnpjUtils::Error`) | Uso indevido da API | Violação de contrato do `Hash` de settings `cnpj` aninhado em `CnpjUtils.new` | +| `CpfFmt::InvalidArgumentCombinationError` | `CpfFmt::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CpfFmt::Error`) | Uso indevido da API | Instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` em `CpfFormatter` / `cpf_fmt` | +| `CpfFmt::TypeMismatchError` | `CpfFmt::TypeMismatchError < TypeError < StandardError` (+ `include CpfFmt::Error`) | Uso indevido da API | Entrada de CPF ou opção do formatador tem o tipo errado (ou o retorno de `on_fail` não é `String`) | +| `CpfGen::InvalidArgumentCombinationError` | `CpfGen::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CpfGen::Error`) | Uso indevido da API | Instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` em `CpfGenerator` / `cpf_gen` | +| `CpfGen::TypeMismatchError` | `CpfGen::TypeMismatchError < TypeError < StandardError` (+ `include CpfGen::Error`) | Uso indevido da API | Opção do gerador (`format` / `prefix`) tem o tipo errado | +| `CpfVal::TypeMismatchError` | `CpfVal::TypeMismatchError < TypeError < StandardError` (+ `include CpfVal::Error`) | Uso indevido da API | Entrada de CPF não é `String` nem `Array` de strings | +| `CnpjFmt::InvalidArgumentCombinationError` | `CnpjFmt::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjFmt::Error`) | Uso indevido da API | Instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` em `CnpjFormatter` / `cnpj_fmt` | +| `CnpjFmt::TypeMismatchError` | `CnpjFmt::TypeMismatchError < TypeError < StandardError` (+ `include CnpjFmt::Error`) | Uso indevido da API | Entrada de CNPJ ou opção do formatador tem o tipo errado (ou o retorno de `on_fail` não é `String`) | +| `CnpjGen::InvalidArgumentCombinationError` | `CnpjGen::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjGen::Error`) | Uso indevido da API | Instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` em `CnpjGenerator` / `cnpj_gen` | +| `CnpjGen::TypeMismatchError` | `CnpjGen::TypeMismatchError < TypeError < StandardError` (+ `include CnpjGen::Error`) | Uso indevido da API | Opção do gerador (`format` / `prefix` / `type`) tem o tipo errado | +| `CnpjVal::InvalidArgumentCombinationError` | `CnpjVal::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjVal::Error`) | Uso indevido da API | Instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` em `CnpjValidator` / `cnpj_val` | +| `CnpjVal::TypeMismatchError` | `CnpjVal::TypeMismatchError < TypeError < StandardError` (+ `include CnpjVal::Error`) | Uso indevido da API | Entrada de CNPJ ou opção do validador tem o tipo errado | +| `CpfFmt::InvalidLengthError` | `CpfFmt::InvalidLengthError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Erro de domínio | Comprimento sanitizado ≠ 11 — **passado a `on_fail`**, não lançado por `#format` | +| `CpfFmt::OutOfRangeError` | `CpfFmt::OutOfRangeError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Erro de domínio | `hidden_start` / `hidden_end` fora de `0`–`10` | +| `CpfFmt::ValidationError` | `CpfFmt::ValidationError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Erro de domínio | `hidden_key` / `dot_key` / `dash_key` contém caractere proibido | +| `CpfGen::ValidationError` | `CpfGen::ValidationError < CpfGen::DomainError < RangeError < StandardError` (+ `include CpfGen::Error`) | Erro de domínio | `prefix` inelegível (base zerada ou 9 dígitos repetidos) | +| `CnpjFmt::InvalidLengthError` | `CnpjFmt::InvalidLengthError < CnpjFmt::DomainError < RangeError < StandardError` (+ `include CnpjFmt::Error`) | Erro de domínio | Comprimento sanitizado ≠ 14 — **passado a `on_fail`**, não lançado por `#format` | +| `CnpjFmt::OutOfRangeError` | `CnpjFmt::OutOfRangeError < CnpjFmt::DomainError < RangeError < StandardError` (+ `include CnpjFmt::Error`) | Erro de domínio | `hidden_start` / `hidden_end` fora de `0`–`13` | +| `CnpjFmt::ValidationError` | `CnpjFmt::ValidationError < CnpjFmt::DomainError < RangeError < StandardError` (+ `include CnpjFmt::Error`) | Erro de domínio | `hidden_key` / `dot_key` / `slash_key` / `dash_key` contém caractere proibido | +| `CnpjGen::ValidationError` | `CnpjGen::ValidationError < CnpjGen::DomainError < RangeError < StandardError` (+ `include CnpjGen::Error`) | Erro de domínio | `prefix` inelegível, ou `type` fora de `'alphabetic'` / `'alphanumeric'` / `'numeric'` | +| `CnpjVal::ValidationError` | `CnpjVal::ValidationError < CnpjVal::DomainError < RangeError < StandardError` (+ `include CnpjVal::Error`) | Erro de domínio | `type` do validador não é `'alphanumeric'` nem `'numeric'` | + +##### `CpfFmt::DomainError` + +- **Herança:** `CpfFmt::DomainError < RangeError < StandardError` (inclui `CpfFmt::Error`) +- **Categoria:** Erro de domínio — ancestral das folhas de domínio do formatador. +- **Quando é lançado:** Não é lançado diretamente; alvo de rescue para `OutOfRangeError`, `ValidationError` e `InvalidLengthError` relançado. +- **Exemplo:** Prefira resgatar uma folha, ou `CpfFmt::DomainError` para todas as falhas de domínio do formatador de CPF. +- **Como resgatar:** ```ruby -require 'br-utilities' +rescue CpfFmt::DomainError + # OutOfRangeError, ValidationError, InvalidLengthError (se relançado a partir de on_fail) +``` -begin - BrUtils.new.cnpj.format(12_345) -rescue CnpjFmt::TypeMismatchError => e - puts e.message -end +##### `CpfFmt::TypeMismatchError` -begin - BrUtils.new.cnpj.is_valid(12_345_678_000_198) -rescue CnpjVal::TypeMismatchError => e - puts e.message -end +- **Herança:** `CpfFmt::TypeMismatchError < TypeError < StandardError` (inclui `CpfFmt::Error`) +- **Categoria:** Uso indevido da API — tipo errado para entrada de CPF ou opção do formatador. +- **Quando é lançado:** Lançado quando `#format` / `cpf_fmt` recebe entrada que não é `String` / `Array`, uma opção tem o tipo errado, ou `on_fail` não retorna `String`. +- **Exemplo:** + +```ruby +BrUtils.new.cpf.format(12_345) # lança CpfFmt::TypeMismatchError +``` + +- **Como resgatar:** + +```ruby +rescue CpfFmt::TypeMismatchError + # violação de contrato de tipo do formatador + +rescue TypeError + # erros nativos de tipo, incluindo CpfFmt::TypeMismatchError +``` + +##### `CpfFmt::InvalidArgumentCombinationError` + +- **Herança:** `CpfFmt::InvalidArgumentCombinationError < ArgumentError < StandardError` (inclui `CpfFmt::Error`) +- **Categoria:** Uso indevido da API — `options` e argumentos nomeados misturados na API do formatador. +- **Quando é lançado:** Lançado por `CpfFmt::CpfFormatter` / `CpfFmt.cpf_fmt` quando uma instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` são passados juntos. (O agregador de CPF lança `CpfUtils::InvalidArgumentCombinationError` para o mesmo padrão em `CpfUtils#format`.) +- **Exemplo:** + +```ruby +CpfFmt::CpfFormatter.new({ dash_key: '_' }, hidden: true) +# lança CpfFmt::InvalidArgumentCombinationError +``` + +- **Como resgatar:** + +```ruby +rescue CpfFmt::InvalidArgumentCombinationError + # combinação inválida de assinatura do formatador + +rescue ArgumentError + # erros nativos de argumento, incluindo este +``` + +##### `CpfFmt::InvalidLengthError` (entregue via callback) + +- **Herança:** `CpfFmt::InvalidLengthError < CpfFmt::DomainError < RangeError < StandardError` (inclui `CpfFmt::Error`) +- **Categoria:** Erro de domínio — o comprimento sanitizado do CPF não é exatamente 11. +- **Quando é lançado:** **Não é lançado** por `#format` / `cpf_fmt`; construído e passado como segundo argumento a `on_fail`. +- **Exemplo:** + +```ruby +custom_fail = ->(value, error) { + error # => # + "Invalid CPF: #{value}" +} + +BrUtils.new.cpf.format('123', on_fail: custom_fail) # => "Invalid CPF: 123" +BrUtils.new.cpf.format('123') # => "" (on_fail padrão) +``` + +- **Como resgatar:** Trate dentro de `on_fail` (típico), ou faça rescue se relançar: + +```ruby +rescue CpfFmt::InvalidLengthError + # esta violação exata de comprimento + +rescue CpfFmt::DomainError + # falhas de domínio com raiz em RangeError de cpf-fmt +``` + +##### `CpfFmt::OutOfRangeError` + +- **Herança:** `CpfFmt::OutOfRangeError < CpfFmt::DomainError < RangeError < StandardError` (inclui `CpfFmt::Error`) +- **Categoria:** Erro de domínio — `hidden_start` / `hidden_end` fora de `0`–`10`. +- **Quando é lançado:** Lançado ao construir ou aplicar opções do formatador com índice de ocultação fora do intervalo. +- **Exemplo:** -# on_fail customizado para comprimento inválido -custom_fail = ->(value, _exception) { "Invalid: #{value}" } +```ruby +BrUtils.new.cpf.format('12345678909', hidden_start: -1) # lança CpfFmt::OutOfRangeError +``` + +- **Como resgatar:** + +```ruby +rescue CpfFmt::OutOfRangeError + # esta violação exata de intervalo + +rescue CpfFmt::DomainError + # falhas de domínio com raiz em RangeError de cpf-fmt +``` + +##### `CpfFmt::ValidationError` + +- **Herança:** `CpfFmt::ValidationError < CpfFmt::DomainError < RangeError < StandardError` (inclui `CpfFmt::Error`) +- **Categoria:** Erro de domínio — uma opção de chave contém um caractere proibido. +- **Quando é lançado:** Lançado quando `hidden_key`, `dot_key` ou `dash_key` contém um caractere proibido. +- **Exemplo:** + +```ruby +BrUtils.new(cpf: { formatter: { dot_key: 'å' } }) # lança CpfFmt::ValidationError +``` + +- **Como resgatar:** + +```ruby +rescue CpfFmt::ValidationError + # esta falha exata de validação de domínio + +rescue CpfFmt::DomainError + # falhas de domínio com raiz em RangeError de cpf-fmt +``` + +##### `CpfGen::DomainError` + +- **Herança:** `CpfGen::DomainError < RangeError < StandardError` (inclui `CpfGen::Error`) +- **Categoria:** Erro de domínio — ancestral das folhas de domínio do gerador. +- **Quando é lançado:** Não é lançado diretamente; alvo de rescue para `CpfGen::ValidationError`. +- **Exemplo:** Prefira `rescue CpfGen::ValidationError` ou `CpfGen::DomainError`. +- **Como resgatar:** -BrUtils.cpf.format('short', on_fail: custom_fail) # => "Invalid: short" -BrUtils.cnpj.format('short', on_fail: custom_fail) # => "Invalid: short" -BrUtils.cpf.format('short') # => "" (on_fail padrão) +```ruby +rescue CpfGen::DomainError + # ValidationError e outras subclasses de DomainError de cpf-gen +``` + +##### `CpfGen::TypeMismatchError` + +- **Herança:** `CpfGen::TypeMismatchError < TypeError < StandardError` (inclui `CpfGen::Error`) +- **Categoria:** Uso indevido da API — tipo errado para uma opção do gerador. +- **Quando é lançado:** Lançado quando `format` ou `prefix` tem o tipo de runtime errado. +- **Exemplo:** + +```ruby +BrUtils.new.cpf.generate(prefix: 123) # lança CpfGen::TypeMismatchError +``` + +- **Como resgatar:** + +```ruby +rescue CpfGen::TypeMismatchError + # violação de contrato de tipo do gerador + +rescue TypeError + # erros nativos de tipo, incluindo CpfGen::TypeMismatchError +``` + +##### `CpfGen::InvalidArgumentCombinationError` + +- **Herança:** `CpfGen::InvalidArgumentCombinationError < ArgumentError < StandardError` (inclui `CpfGen::Error`) +- **Categoria:** Uso indevido da API — `options` e argumentos nomeados misturados na API do gerador. +- **Quando é lançado:** Lançado por `CpfGen::CpfGenerator` / `CpfGen.cpf_gen` quando uma instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` são passados juntos. (O agregador de CPF lança `CpfUtils::InvalidArgumentCombinationError` para o mesmo padrão em `CpfUtils#generate`.) +- **Exemplo:** + +```ruby +CpfGen::CpfGenerator.new({ format: true }, prefix: '123') +# lança CpfGen::InvalidArgumentCombinationError +``` + +- **Como resgatar:** + +```ruby +rescue CpfGen::InvalidArgumentCombinationError + # combinação inválida de assinatura do gerador + +rescue ArgumentError + # erros nativos de argumento, incluindo este +``` + +##### `CpfGen::ValidationError` + +- **Herança:** `CpfGen::ValidationError < CpfGen::DomainError < RangeError < StandardError` (inclui `CpfGen::Error`) +- **Categoria:** Erro de domínio — `prefix` inelegível. +- **Quando é lançado:** Lançado quando `prefix` é uma base zerada (`'000000000'`) ou 9 dígitos repetidos (ex.: `'999999999'`). +- **Exemplo:** + +```ruby +BrUtils.new.cpf.generate(prefix: '000000000') # lança CpfGen::ValidationError +``` + +- **Como resgatar:** + +```ruby +rescue CpfGen::ValidationError + # esta falha exata de validação de domínio + +rescue CpfGen::DomainError + # falhas de domínio com raiz em RangeError de cpf-gen +``` + +##### `CpfVal::TypeMismatchError` + +- **Herança:** `CpfVal::TypeMismatchError < TypeError < StandardError` (inclui `CpfVal::Error`) +- **Categoria:** Uso indevido da API — tipo errado para entrada de CPF. +- **Quando é lançado:** Lançado quando `#is_valid` / `cpf_val` recebe um valor que não é `String` nem `Array` de strings (incluindo elemento de array que não é string). **Dados** de CPF inválidos retornam `false` e não lançam. +- **Exemplo:** + +```ruby +BrUtils.new.cpf.is_valid(12_345_678_909) # lança CpfVal::TypeMismatchError +BrUtils.new.cpf.is_valid('12345678900') # => false (dados inválidos, sem raise) +``` + +- **Como resgatar:** + +```ruby +rescue CpfVal::TypeMismatchError + # violação de contrato de tipo do validador + +rescue TypeError + # erros nativos de tipo, incluindo CpfVal::TypeMismatchError +``` + +##### `CnpjFmt::DomainError` + +- **Herança:** `CnpjFmt::DomainError < RangeError < StandardError` (inclui `CnpjFmt::Error`) +- **Categoria:** Erro de domínio — ancestral das folhas de domínio do formatador. +- **Quando é lançado:** Não é lançado diretamente; alvo de rescue para `OutOfRangeError`, `ValidationError` e `InvalidLengthError` relançado. +- **Exemplo:** Prefira resgatar uma folha, ou `CnpjFmt::DomainError` para todas as falhas de domínio do formatador de CNPJ. +- **Como resgatar:** + +```ruby +rescue CnpjFmt::DomainError + # OutOfRangeError, ValidationError, InvalidLengthError (se relançado a partir de on_fail) +``` + +##### `CnpjFmt::TypeMismatchError` + +- **Herança:** `CnpjFmt::TypeMismatchError < TypeError < StandardError` (inclui `CnpjFmt::Error`) +- **Categoria:** Uso indevido da API — tipo errado para entrada de CNPJ ou opção do formatador. +- **Quando é lançado:** Lançado quando `#format` / `cnpj_fmt` recebe entrada que não é `String` / `Array`, uma opção tem o tipo errado, ou `on_fail` não retorna `String`. +- **Exemplo:** + +```ruby +BrUtils.new.cnpj.format(12_345) # lança CnpjFmt::TypeMismatchError +``` + +- **Como resgatar:** + +```ruby +rescue CnpjFmt::TypeMismatchError + # violação de contrato de tipo do formatador + +rescue TypeError + # erros nativos de tipo, incluindo CnpjFmt::TypeMismatchError +``` + +##### `CnpjFmt::InvalidArgumentCombinationError` + +- **Herança:** `CnpjFmt::InvalidArgumentCombinationError < ArgumentError < StandardError` (inclui `CnpjFmt::Error`) +- **Categoria:** Uso indevido da API — `options` e argumentos nomeados misturados na API do formatador. +- **Quando é lançado:** Lançado por `CnpjFmt::CnpjFormatter` / `CnpjFmt.cnpj_fmt` quando uma instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` são passados juntos. (O agregador de CNPJ lança `CnpjUtils::InvalidArgumentCombinationError` para o mesmo padrão em `CnpjUtils#format`.) +- **Exemplo:** + +```ruby +CnpjFmt::CnpjFormatter.new({ slash_key: '|' }, hidden: true) +# lança CnpjFmt::InvalidArgumentCombinationError ``` -Para listas exaustivas de exceções e comportamento em casos extremos, consulte o README de cada [pacote incluído](#pacotes-incluídos). +- **Como resgatar:** + +```ruby +rescue CnpjFmt::InvalidArgumentCombinationError + # combinação inválida de assinatura do formatador + +rescue ArgumentError + # erros nativos de argumento, incluindo este +``` + +##### `CnpjFmt::InvalidLengthError` (entregue via callback) + +- **Herança:** `CnpjFmt::InvalidLengthError < CnpjFmt::DomainError < RangeError < StandardError` (inclui `CnpjFmt::Error`) +- **Categoria:** Erro de domínio — o comprimento sanitizado do CNPJ não é exatamente 14. +- **Quando é lançado:** **Não é lançado** por `#format` / `cnpj_fmt`; construído e passado como segundo argumento a `on_fail`. +- **Exemplo:** + +```ruby +custom_fail = ->(value, error) { + error # => # + "Invalid CNPJ: #{value}" +} + +BrUtils.new.cnpj.format('123', on_fail: custom_fail) # => "Invalid CNPJ: 123" +BrUtils.new.cnpj.format('123') # => "" (on_fail padrão) +``` + +- **Como resgatar:** Trate dentro de `on_fail` (típico), ou faça rescue se relançar: + +```ruby +rescue CnpjFmt::InvalidLengthError + # esta violação exata de comprimento + +rescue CnpjFmt::DomainError + # falhas de domínio com raiz em RangeError de cnpj-fmt +``` + +##### `CnpjFmt::OutOfRangeError` + +- **Herança:** `CnpjFmt::OutOfRangeError < CnpjFmt::DomainError < RangeError < StandardError` (inclui `CnpjFmt::Error`) +- **Categoria:** Erro de domínio — `hidden_start` / `hidden_end` fora de `0`–`13`. +- **Quando é lançado:** Lançado ao construir ou aplicar opções do formatador com índice de ocultação fora do intervalo. +- **Exemplo:** + +```ruby +BrUtils.new.cnpj.format('91415732000793', hidden_start: -1) # lança CnpjFmt::OutOfRangeError +``` + +- **Como resgatar:** + +```ruby +rescue CnpjFmt::OutOfRangeError + # esta violação exata de intervalo + +rescue CnpjFmt::DomainError + # falhas de domínio com raiz em RangeError de cnpj-fmt +``` + +##### `CnpjFmt::ValidationError` + +- **Herança:** `CnpjFmt::ValidationError < CnpjFmt::DomainError < RangeError < StandardError` (inclui `CnpjFmt::Error`) +- **Categoria:** Erro de domínio — uma opção de chave contém um caractere proibido. +- **Quando é lançado:** Lançado quando `hidden_key`, `dot_key`, `slash_key` ou `dash_key` contém um caractere proibido. +- **Exemplo:** + +```ruby +BrUtils.new(cnpj: { formatter: { slash_key: 'å' } }) # lança CnpjFmt::ValidationError +``` + +- **Como resgatar:** + +```ruby +rescue CnpjFmt::ValidationError + # esta falha exata de validação de domínio + +rescue CnpjFmt::DomainError + # falhas de domínio com raiz em RangeError de cnpj-fmt +``` + +##### `CnpjGen::DomainError` + +- **Herança:** `CnpjGen::DomainError < RangeError < StandardError` (inclui `CnpjGen::Error`) +- **Categoria:** Erro de domínio — ancestral das folhas de domínio do gerador. +- **Quando é lançado:** Não é lançado diretamente; alvo de rescue para `CnpjGen::ValidationError`. +- **Exemplo:** Prefira `rescue CnpjGen::ValidationError` ou `CnpjGen::DomainError`. +- **Como resgatar:** + +```ruby +rescue CnpjGen::DomainError + # ValidationError e outras subclasses de DomainError de cnpj-gen +``` + +##### `CnpjGen::TypeMismatchError` + +- **Herança:** `CnpjGen::TypeMismatchError < TypeError < StandardError` (inclui `CnpjGen::Error`) +- **Categoria:** Uso indevido da API — tipo errado para uma opção do gerador. +- **Quando é lançado:** Lançado quando `format`, `prefix` ou `type` tem o tipo de runtime errado. +- **Exemplo:** + +```ruby +BrUtils.new.cnpj.generate(prefix: 123) # lança CnpjGen::TypeMismatchError +``` + +- **Como resgatar:** + +```ruby +rescue CnpjGen::TypeMismatchError + # violação de contrato de tipo do gerador + +rescue TypeError + # erros nativos de tipo, incluindo CnpjGen::TypeMismatchError +``` + +##### `CnpjGen::InvalidArgumentCombinationError` + +- **Herança:** `CnpjGen::InvalidArgumentCombinationError < ArgumentError < StandardError` (inclui `CnpjGen::Error`) +- **Categoria:** Uso indevido da API — `options` e argumentos nomeados misturados na API do gerador. +- **Quando é lançado:** Lançado por `CnpjGen::CnpjGenerator` / `CnpjGen.cnpj_gen` quando uma instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` são passados juntos. (O agregador de CNPJ lança `CnpjUtils::InvalidArgumentCombinationError` para o mesmo padrão em `CnpjUtils#generate`.) +- **Exemplo:** + +```ruby +CnpjGen::CnpjGenerator.new({ format: true }, prefix: '123') +# lança CnpjGen::InvalidArgumentCombinationError +``` + +- **Como resgatar:** + +```ruby +rescue CnpjGen::InvalidArgumentCombinationError + # combinação inválida de assinatura do gerador + +rescue ArgumentError + # erros nativos de argumento, incluindo este +``` + +##### `CnpjGen::ValidationError` + +- **Herança:** `CnpjGen::ValidationError < CnpjGen::DomainError < RangeError < StandardError` (inclui `CnpjGen::Error`) +- **Categoria:** Erro de domínio — `prefix` inelegível ou `type` não permitido. +- **Quando é lançado:** Lançado quando `prefix` é base/filial zerada ou 12 dígitos repetidos, ou quando `type` não é `'alphabetic'`, `'alphanumeric'` ou `'numeric'`. +- **Exemplo:** + +```ruby +BrUtils.new.cnpj.generate(type: 'boolean') # lança CnpjGen::ValidationError +``` + +- **Como resgatar:** + +```ruby +rescue CnpjGen::ValidationError + # esta falha exata de validação de domínio + +rescue CnpjGen::DomainError + # falhas de domínio com raiz em RangeError de cnpj-gen +``` + +##### `CnpjVal::DomainError` + +- **Herança:** `CnpjVal::DomainError < RangeError < StandardError` (inclui `CnpjVal::Error`) +- **Categoria:** Erro de domínio — ancestral das folhas de domínio do validador. +- **Quando é lançado:** Não é lançado diretamente; alvo de rescue para `CnpjVal::ValidationError`. +- **Exemplo:** Prefira `rescue CnpjVal::ValidationError` ou `CnpjVal::DomainError`. +- **Como resgatar:** + +```ruby +rescue CnpjVal::DomainError + # ValidationError e outras subclasses de DomainError de cnpj-val +``` + +##### `CnpjVal::TypeMismatchError` + +- **Herança:** `CnpjVal::TypeMismatchError < TypeError < StandardError` (inclui `CnpjVal::Error`) +- **Categoria:** Uso indevido da API — tipo errado para entrada de CNPJ ou opção do validador. +- **Quando é lançado:** Lançado quando `#is_valid` / `cnpj_val` recebe um valor que não é `String` nem `Array` de strings, ou uma opção do validador tem o tipo errado. **Dados** de CNPJ inválidos retornam `false` e não lançam. +- **Exemplo:** + +```ruby +BrUtils.new.cnpj.is_valid(12_345_678_000_198) # lança CnpjVal::TypeMismatchError +BrUtils.new.cnpj.is_valid('00000000000000') # => false (dados inválidos, sem raise) +``` + +- **Como resgatar:** + +```ruby +rescue CnpjVal::TypeMismatchError + # violação de contrato de tipo do validador + +rescue TypeError + # erros nativos de tipo, incluindo CnpjVal::TypeMismatchError +``` + +##### `CnpjVal::InvalidArgumentCombinationError` + +- **Herança:** `CnpjVal::InvalidArgumentCombinationError < ArgumentError < StandardError` (inclui `CnpjVal::Error`) +- **Categoria:** Uso indevido da API — `options` e argumentos nomeados misturados na API do validador. +- **Quando é lançado:** Lançado por `CnpjVal::CnpjValidator` / `CnpjVal.cnpj_val` quando uma instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` são passados juntos. (O agregador de CNPJ lança `CnpjUtils::InvalidArgumentCombinationError` para o mesmo padrão em `CnpjUtils#is_valid`.) +- **Exemplo:** + +```ruby +CnpjVal.cnpj_val('98765432000198', { type: 'numeric' }, case_sensitive: false) +# lança CnpjVal::InvalidArgumentCombinationError +``` + +- **Como resgatar:** + +```ruby +rescue CnpjVal::InvalidArgumentCombinationError + # combinação inválida de assinatura do validador + +rescue ArgumentError + # erros nativos de argumento, incluindo este +``` + +##### `CnpjVal::ValidationError` + +- **Herança:** `CnpjVal::ValidationError < CnpjVal::DomainError < RangeError < StandardError` (inclui `CnpjVal::Error`) +- **Categoria:** Erro de domínio — `type` do validador não permitido. +- **Quando é lançado:** Lançado quando `type` não é `'alphanumeric'` nem `'numeric'`. +- **Exemplo:** + +```ruby +BrUtils.new.cnpj.is_valid('91415732000793', type: 'boolean') # lança CnpjVal::ValidationError +``` + +- **Como resgatar:** + +```ruby +rescue CnpjVal::ValidationError + # esta falha exata de validação de domínio + +rescue CnpjVal::DomainError + # falhas de domínio com raiz em RangeError de cnpj-val +``` + +##### `CpfUtils::TypeMismatchError` / `CpfUtils::InvalidArgumentCombinationError` + +- **Herança:** `CpfUtils::TypeMismatchError < TypeError` and `CpfUtils::InvalidArgumentCombinationError < ArgumentError` (ambos incluem `CpfUtils::Error`). +- **Categoria:** Uso indevido da API on the nested CPF aggregator. +- **Quando é lançado:** Lançado quando settings `cpf` aninhados em `CpfUtils.new` não são um `Hash`, ou quando `CpfUtils#format` / `#generate` misturam um `Hash` de options com argumentos nomeados. +- **Exemplo:** + +```ruby +BrUtils.new.cpf.format({ hidden: true }, dash_key: '|') +# lança CpfUtils::InvalidArgumentCombinationError +``` + +- **Como resgatar:** + +```ruby +rescue CpfUtils::TypeMismatchError, CpfUtils::InvalidArgumentCombinationError + # uso indevido do agregador de CPF (não BrUtils::Error) + +rescue CpfUtils::Error + # todo erro customizado que inclui CpfUtils::Error +``` + +##### `CnpjUtils::TypeMismatchError` / `CnpjUtils::InvalidArgumentCombinationError` + +- **Herança:** `CnpjUtils::TypeMismatchError < TypeError` and `CnpjUtils::InvalidArgumentCombinationError < ArgumentError` (ambos incluem `CnpjUtils::Error`). +- **Categoria:** Uso indevido da API on the nested CNPJ aggregator. +- **Quando é lançado:** Lançado quando settings `cnpj` aninhados em `CnpjUtils.new` não são um `Hash`, ou quando `CnpjUtils#format` / `#generate` / `#is_valid` misturam settings/options com argumentos nomeados. +- **Exemplo:** + +```ruby +BrUtils.new.cnpj.format({ hidden: true }, slash_key: '|') +# lança CnpjUtils::InvalidArgumentCombinationError +``` + +- **Como resgatar:** + +```ruby +rescue CnpjUtils::TypeMismatchError, CnpjUtils::InvalidArgumentCombinationError + # uso indevido do agregador de CNPJ (não BrUtils::Error) + +rescue CnpjUtils::Error + # todo erro customizado que inclui CnpjUtils::Error +``` ### Pacotes incluídos diff --git a/packages/br-utilities/CHANGELOG.md b/packages/br-utilities/CHANGELOG.md index 0d35ee6..96f520b 100644 --- a/packages/br-utilities/CHANGELOG.md +++ b/packages/br-utilities/CHANGELOG.md @@ -12,6 +12,6 @@ Unified toolkit to deal with Brazilian documents (CPF and CNPJ): validation, for - **Two-tier re-exports**: main classes at the façade root (`BrUtils::CpfFormatter`, `BrUtils::CnpjValidator`, …); full sibling surface under `BrUtils::CpfFmt` / `CpfUtils` / `CnpjFmt` / …. - **One install**: depends on `cpf-utilities` and `cnpj-utilities` so both domains ship without requiring each gem separately. - **Alphanumeric CNPJ**: CNPJ path supports 14-character alphanumeric IDs via `cnpj-utilities` (numeric CPF via `cpf-utilities`). -- **Structured errors**: `BrUtils::TypeMismatchError` / `InvalidArgumentCombinationError` (+ `BrUtils::Error` marker) for façade misuse; domain errors propagate from nested packages. +- **Structured errors**: `BrUtils::TypeMismatchError` / `InvalidArgumentCombinationError` (+ `BrUtils::Error` marker) for façade misuse; README documents every propagated `CpfFmt` / `CpfGen` / `CpfVal` / `CnpjFmt` / `CnpjGen` / `CnpjVal` leaf (including `on_fail` `InvalidLengthError`). For detailed usage and API reference, see the [README](./README.md). diff --git a/packages/br-utilities/README.md b/packages/br-utilities/README.md index a48d42c..8b2f19c 100644 --- a/packages/br-utilities/README.md +++ b/packages/br-utilities/README.md @@ -607,12 +607,19 @@ end ```ruby require 'br-utilities' -# 2) BrUtils::DomainError — not applicable: this gem defines no DomainError -# (and no domain leaves). Domain failures come from bundled packages only. -# begin -# BrUtils.new(cpf: { formatter: { hidden_start: -1 } }) -# rescue BrUtils::DomainError # NameError — constant is not defined -# end +# 2) Bundled DomainError — this gem defines no DomainError; domain failures +# come from bundled packages and keep those namespaces (e.g. CpfFmt, CnpjFmt). +begin + BrUtils.new.cpf.format('12345678909', hidden_start: -1) +rescue CpfFmt::DomainError + # CpfFmt::OutOfRangeError, CpfFmt::ValidationError, and other DomainError subclasses +end + +begin + BrUtils.new.cnpj.format('91415732000793', hidden_start: -1) +rescue CnpjFmt::DomainError + # CnpjFmt::OutOfRangeError, CnpjFmt::ValidationError, and other DomainError subclasses +end ``` ```ruby @@ -640,41 +647,586 @@ end #### Propagated from bundled packages -`BrUtils` does not redefine domain exception types. Construction, setters, and domain method calls raise the same errors as [`cpf-utilities`](../cpf-utilities/README.md) and [`cnpj-utilities`](../cnpj-utilities/README.md): +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/CNPJ **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` or `CnpjFmt::InvalidLengthError` (default `on_fail` returns `''`). -- **CPF formatting**: `CpfFmt::TypeMismatchError`, `CpfFmt::OutOfRangeError`, `CpfFmt::ValidationError`, `CpfFmt::InvalidLengthError` (passed to `on_fail`, not raised by `#format`), and related classes. -- **CPF generation**: `CpfGen::TypeMismatchError`, `CpfGen::ValidationError`, and related classes. -- **CPF validation**: `CpfVal::TypeMismatchError` and related classes. -- **CNPJ formatting**: `CnpjFmt::TypeMismatchError`, `CnpjFmt::OutOfRangeError`, `CnpjFmt::ValidationError`, `CnpjFmt::InvalidLengthError` (passed to `on_fail`), and related classes. -- **CNPJ generation**: `CnpjGen::TypeMismatchError`, `CnpjGen::ValidationError`, and related classes. -- **CNPJ validation**: `CnpjVal::TypeMismatchError`, `CnpjVal::ValidationError`, and related classes. +`CpfUtils::*` / `CnpjUtils::*` misuse errors also propagate when nested aggregators are constructed or called through `BrUtils`. For exhaustive option tables and extra edge cases, see [`cpf-utilities`](../cpf-utilities/README.md) and [`cnpj-utilities`](../cnpj-utilities/README.md). -Invalid option types are typically **`TypeError`** subclasses (`*::TypeMismatchError`); invalid option values are domain errors under each package’s `DomainError` hierarchy. CPF and CNPJ validation failures return `false`. Formatting length failures are handled by **`on_fail`** (default returns an empty string). +##### Summary + +| Class | Inherits from | Category | Trigger condition | +|-------|---------------|----------|-------------------| +| `CpfUtils::InvalidArgumentCombinationError` | `CpfUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CpfUtils::Error`) | API misuse | Both an options instance/`Hash` and any non-`nil` keyword on `CpfUtils#format` / `#generate` | +| `CpfUtils::TypeMismatchError` | `CpfUtils::TypeMismatchError < TypeError < StandardError` (+ `include CpfUtils::Error`) | API misuse | Non-`nil` nested `cpf` settings `Hash` contract violation on `CpfUtils.new` | +| `CnpjUtils::InvalidArgumentCombinationError` | `CnpjUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjUtils::Error`) | API misuse | Both settings/options and any non-`nil` keyword on `CnpjUtils` construct/`#format` / `#generate` / `#is_valid` | +| `CnpjUtils::TypeMismatchError` | `CnpjUtils::TypeMismatchError < TypeError < StandardError` (+ `include CnpjUtils::Error`) | API misuse | Non-`nil` nested `cnpj` settings `Hash` contract violation on `CnpjUtils.new` | +| `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` | +| `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`) | +| `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` | +| `CpfGen::TypeMismatchError` | `CpfGen::TypeMismatchError < TypeError < StandardError` (+ `include CpfGen::Error`) | API misuse | Generator option (`format` / `prefix`) has the wrong type | +| `CpfVal::TypeMismatchError` | `CpfVal::TypeMismatchError < TypeError < StandardError` (+ `include CpfVal::Error`) | API misuse | CPF input is not a `String` or `Array` of strings | +| `CnpjFmt::InvalidArgumentCombinationError` | `CnpjFmt::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjFmt::Error`) | API misuse | Both an `options` instance/`Hash` and any non-`nil` keyword on `CnpjFormatter` / `cnpj_fmt` | +| `CnpjFmt::TypeMismatchError` | `CnpjFmt::TypeMismatchError < TypeError < StandardError` (+ `include CnpjFmt::Error`) | API misuse | CNPJ input or formatter option has the wrong type (or `on_fail` return is not a `String`) | +| `CnpjGen::InvalidArgumentCombinationError` | `CnpjGen::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjGen::Error`) | API misuse | Both an `options` instance/`Hash` and any non-`nil` keyword on `CnpjGenerator` / `cnpj_gen` | +| `CnpjGen::TypeMismatchError` | `CnpjGen::TypeMismatchError < TypeError < StandardError` (+ `include CnpjGen::Error`) | API misuse | Generator option (`format` / `prefix` / `type`) has the wrong type | +| `CnpjVal::InvalidArgumentCombinationError` | `CnpjVal::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjVal::Error`) | API misuse | Both an `options` instance/`Hash` and any non-`nil` keyword on `CnpjValidator` / `cnpj_val` | +| `CnpjVal::TypeMismatchError` | `CnpjVal::TypeMismatchError < TypeError < StandardError` (+ `include CnpjVal::Error`) | API misuse | CNPJ input or validator option has the wrong type | +| `CpfFmt::InvalidLengthError` | `CpfFmt::InvalidLengthError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Domain error | Sanitized length ≠ 11 — **passed to `on_fail`**, not raised by `#format` | +| `CpfFmt::OutOfRangeError` | `CpfFmt::OutOfRangeError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Domain error | `hidden_start` / `hidden_end` outside `0`–`10` | +| `CpfFmt::ValidationError` | `CpfFmt::ValidationError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Domain error | `hidden_key` / `dot_key` / `dash_key` contains a disallowed character | +| `CpfGen::ValidationError` | `CpfGen::ValidationError < CpfGen::DomainError < RangeError < StandardError` (+ `include CpfGen::Error`) | Domain error | `prefix` is ineligible (zeroed base or 9 repeated digits) | +| `CnpjFmt::InvalidLengthError` | `CnpjFmt::InvalidLengthError < CnpjFmt::DomainError < RangeError < StandardError` (+ `include CnpjFmt::Error`) | Domain error | Sanitized length ≠ 14 — **passed to `on_fail`**, not raised by `#format` | +| `CnpjFmt::OutOfRangeError` | `CnpjFmt::OutOfRangeError < CnpjFmt::DomainError < RangeError < StandardError` (+ `include CnpjFmt::Error`) | Domain error | `hidden_start` / `hidden_end` outside `0`–`13` | +| `CnpjFmt::ValidationError` | `CnpjFmt::ValidationError < CnpjFmt::DomainError < RangeError < StandardError` (+ `include CnpjFmt::Error`) | Domain error | `hidden_key` / `dot_key` / `slash_key` / `dash_key` contains a disallowed character | +| `CnpjGen::ValidationError` | `CnpjGen::ValidationError < CnpjGen::DomainError < RangeError < StandardError` (+ `include CnpjGen::Error`) | Domain error | Ineligible `prefix`, or `type` not in `'alphabetic'` / `'alphanumeric'` / `'numeric'` | +| `CnpjVal::ValidationError` | `CnpjVal::ValidationError < CnpjVal::DomainError < RangeError < StandardError` (+ `include CnpjVal::Error`) | Domain error | Validator `type` is not `'alphanumeric'` or `'numeric'` | + +##### `CpfFmt::DomainError` + +- **Inheritance:** `CpfFmt::DomainError < RangeError < StandardError` (includes `CpfFmt::Error`) +- **Category:** Domain error — ancestor for formatter domain leaves. +- **When it is raised:** Not raised directly; rescue target for `OutOfRangeError`, `ValidationError`, and re-raised `InvalidLengthError`. +- **Example:** Prefer rescuing a leaf, or `CpfFmt::DomainError` for all CPF formatter domain failures. +- **How to rescue it:** ```ruby -require 'br-utilities' +rescue CpfFmt::DomainError + # OutOfRangeError, ValidationError, InvalidLengthError (if re-raised from on_fail) +``` -begin - BrUtils.new.cnpj.format(12_345) -rescue CnpjFmt::TypeMismatchError => e - puts e.message -end +##### `CpfFmt::TypeMismatchError` -begin - BrUtils.new.cnpj.is_valid(12_345_678_000_198) -rescue CnpjVal::TypeMismatchError => e - puts e.message -end +- **Inheritance:** `CpfFmt::TypeMismatchError < TypeError < StandardError` (includes `CpfFmt::Error`) +- **Category:** API misuse — wrong type for CPF input or a formatter option. +- **When it is raised:** Raised when `#format` / `cpf_fmt` receives a non-`String` / non-`Array` input, an option has the wrong type, or `on_fail` does not return a `String`. +- **Example:** + +```ruby +BrUtils.new.cpf.format(12_345) # raises CpfFmt::TypeMismatchError +``` + +- **How to rescue it:** + +```ruby +rescue CpfFmt::TypeMismatchError + # formatter type-contract violation + +rescue TypeError + # native type errors, including CpfFmt::TypeMismatchError +``` + +##### `CpfFmt::InvalidArgumentCombinationError` + +- **Inheritance:** `CpfFmt::InvalidArgumentCombinationError < ArgumentError < StandardError` (includes `CpfFmt::Error`) +- **Category:** API misuse — mixed `options` and keywords on the formatter API. +- **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 CPF aggregator raises `CpfUtils::InvalidArgumentCombinationError` for the same pattern on `CpfUtils#format`.) +- **Example:** + +```ruby +CpfFmt::CpfFormatter.new({ dash_key: '_' }, hidden: true) +# raises CpfFmt::InvalidArgumentCombinationError +``` + +- **How to rescue it:** + +```ruby +rescue CpfFmt::InvalidArgumentCombinationError + # formatter invalid signature combination + +rescue ArgumentError + # native argument errors, including this one +``` + +##### `CpfFmt::InvalidLengthError` (callback-delivered) + +- **Inheritance:** `CpfFmt::InvalidLengthError < CpfFmt::DomainError < RangeError < StandardError` (includes `CpfFmt::Error`) +- **Category:** Domain error — sanitized CPF length is not exactly 11. +- **When it is raised:** **Not raised** by `#format` / `cpf_fmt`; constructed and passed as the second argument to `on_fail`. +- **Example:** + +```ruby +custom_fail = ->(value, error) { + error # => # + "Invalid CPF: #{value}" +} + +BrUtils.new.cpf.format('123', on_fail: custom_fail) # => "Invalid CPF: 123" +BrUtils.new.cpf.format('123') # => "" (default on_fail) +``` + +- **How to rescue it:** Handle inside `on_fail` (typical), or rescue if you re-raise: + +```ruby +rescue CpfFmt::InvalidLengthError + # this exact length violation + +rescue CpfFmt::DomainError + # RangeError-rooted domain failures from cpf-fmt +``` + +##### `CpfFmt::OutOfRangeError` + +- **Inheritance:** `CpfFmt::OutOfRangeError < CpfFmt::DomainError < RangeError < StandardError` (includes `CpfFmt::Error`) +- **Category:** Domain error — `hidden_start` / `hidden_end` outside `0`–`10`. +- **When it is raised:** Raised when building or applying formatter options with an out-of-range hide index. +- **Example:** -# Custom on_fail for invalid length -custom_fail = ->(value, _exception) { "Invalid: #{value}" } +```ruby +BrUtils.new.cpf.format('12345678909', hidden_start: -1) # raises CpfFmt::OutOfRangeError +``` + +- **How to rescue it:** + +```ruby +rescue CpfFmt::OutOfRangeError + # this exact range violation + +rescue CpfFmt::DomainError + # RangeError-rooted domain failures from cpf-fmt +``` + +##### `CpfFmt::ValidationError` + +- **Inheritance:** `CpfFmt::ValidationError < CpfFmt::DomainError < RangeError < StandardError` (includes `CpfFmt::Error`) +- **Category:** Domain error — a key option contains a disallowed character. +- **When it is raised:** Raised when `hidden_key`, `dot_key`, or `dash_key` contains a forbidden character. +- **Example:** + +```ruby +BrUtils.new(cpf: { formatter: { dot_key: 'å' } }) # raises CpfFmt::ValidationError +``` + +- **How to rescue it:** + +```ruby +rescue CpfFmt::ValidationError + # this exact domain validation failure + +rescue CpfFmt::DomainError + # RangeError-rooted domain failures from cpf-fmt +``` + +##### `CpfGen::DomainError` + +- **Inheritance:** `CpfGen::DomainError < RangeError < StandardError` (includes `CpfGen::Error`) +- **Category:** Domain error — ancestor for generator domain leaves. +- **When it is raised:** Not raised directly; rescue target for `CpfGen::ValidationError`. +- **Example:** Prefer `rescue CpfGen::ValidationError` or `CpfGen::DomainError`. +- **How to rescue it:** -BrUtils.cpf.format('short', on_fail: custom_fail) # => "Invalid: short" -BrUtils.cnpj.format('short', on_fail: custom_fail) # => "Invalid: short" -BrUtils.cpf.format('short') # => "" (default on_fail) +```ruby +rescue CpfGen::DomainError + # ValidationError and other DomainError subclasses from cpf-gen +``` + +##### `CpfGen::TypeMismatchError` + +- **Inheritance:** `CpfGen::TypeMismatchError < TypeError < StandardError` (includes `CpfGen::Error`) +- **Category:** API misuse — wrong type for a generator option. +- **When it is raised:** Raised when `format` or `prefix` has the wrong runtime type. +- **Example:** + +```ruby +BrUtils.new.cpf.generate(prefix: 123) # raises CpfGen::TypeMismatchError +``` + +- **How to rescue it:** + +```ruby +rescue CpfGen::TypeMismatchError + # generator type-contract violation + +rescue TypeError + # native type errors, including CpfGen::TypeMismatchError +``` + +##### `CpfGen::InvalidArgumentCombinationError` + +- **Inheritance:** `CpfGen::InvalidArgumentCombinationError < ArgumentError < StandardError` (includes `CpfGen::Error`) +- **Category:** API misuse — mixed `options` and keywords on the generator API. +- **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 CPF aggregator raises `CpfUtils::InvalidArgumentCombinationError` for the same pattern on `CpfUtils#generate`.) +- **Example:** + +```ruby +CpfGen::CpfGenerator.new({ format: true }, prefix: '123') +# raises CpfGen::InvalidArgumentCombinationError +``` + +- **How to rescue it:** + +```ruby +rescue CpfGen::InvalidArgumentCombinationError + # generator invalid signature combination + +rescue ArgumentError + # native argument errors, including this one +``` + +##### `CpfGen::ValidationError` + +- **Inheritance:** `CpfGen::ValidationError < CpfGen::DomainError < RangeError < StandardError` (includes `CpfGen::Error`) +- **Category:** Domain error — ineligible `prefix`. +- **When it is raised:** Raised when `prefix` is a zeroed base (`'000000000'`) or 9 repeated digits (e.g. `'999999999'`). +- **Example:** + +```ruby +BrUtils.new.cpf.generate(prefix: '000000000') # raises CpfGen::ValidationError +``` + +- **How to rescue it:** + +```ruby +rescue CpfGen::ValidationError + # this exact domain validation failure + +rescue CpfGen::DomainError + # RangeError-rooted domain failures from cpf-gen +``` + +##### `CpfVal::TypeMismatchError` + +- **Inheritance:** `CpfVal::TypeMismatchError < TypeError < StandardError` (includes `CpfVal::Error`) +- **Category:** API misuse — wrong type for CPF input. +- **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. +- **Example:** + +```ruby +BrUtils.new.cpf.is_valid(12_345_678_909) # raises CpfVal::TypeMismatchError +BrUtils.new.cpf.is_valid('12345678900') # => false (invalid data, no raise) +``` + +- **How to rescue it:** + +```ruby +rescue CpfVal::TypeMismatchError + # validator type-contract violation + +rescue TypeError + # native type errors, including CpfVal::TypeMismatchError +``` + +##### `CnpjFmt::DomainError` + +- **Inheritance:** `CnpjFmt::DomainError < RangeError < StandardError` (includes `CnpjFmt::Error`) +- **Category:** Domain error — ancestor for formatter domain leaves. +- **When it is raised:** Not raised directly; rescue target for `OutOfRangeError`, `ValidationError`, and re-raised `InvalidLengthError`. +- **Example:** Prefer rescuing a leaf, or `CnpjFmt::DomainError` for all CNPJ formatter domain failures. +- **How to rescue it:** + +```ruby +rescue CnpjFmt::DomainError + # OutOfRangeError, ValidationError, InvalidLengthError (if re-raised from on_fail) +``` + +##### `CnpjFmt::TypeMismatchError` + +- **Inheritance:** `CnpjFmt::TypeMismatchError < TypeError < StandardError` (includes `CnpjFmt::Error`) +- **Category:** API misuse — wrong type for CNPJ input or a formatter option. +- **When it is raised:** Raised when `#format` / `cnpj_fmt` receives a non-`String` / non-`Array` input, an option has the wrong type, or `on_fail` does not return a `String`. +- **Example:** + +```ruby +BrUtils.new.cnpj.format(12_345) # raises CnpjFmt::TypeMismatchError +``` + +- **How to rescue it:** + +```ruby +rescue CnpjFmt::TypeMismatchError + # formatter type-contract violation + +rescue TypeError + # native type errors, including CnpjFmt::TypeMismatchError +``` + +##### `CnpjFmt::InvalidArgumentCombinationError` + +- **Inheritance:** `CnpjFmt::InvalidArgumentCombinationError < ArgumentError < StandardError` (includes `CnpjFmt::Error`) +- **Category:** API misuse — mixed `options` and keywords on the formatter API. +- **When it is raised:** Raised by `CnpjFmt::CnpjFormatter` / `CnpjFmt.cnpj_fmt` when both an `options` instance/`Hash` and any non-`nil` keyword are passed. (The CNPJ aggregator raises `CnpjUtils::InvalidArgumentCombinationError` for the same pattern on `CnpjUtils#format`.) +- **Example:** + +```ruby +CnpjFmt::CnpjFormatter.new({ slash_key: '|' }, hidden: true) +# raises CnpjFmt::InvalidArgumentCombinationError ``` -For exhaustive exception lists and edge-case behavior, see each [bundled package](#bundled-packages) README. +- **How to rescue it:** + +```ruby +rescue CnpjFmt::InvalidArgumentCombinationError + # formatter invalid signature combination + +rescue ArgumentError + # native argument errors, including this one +``` + +##### `CnpjFmt::InvalidLengthError` (callback-delivered) + +- **Inheritance:** `CnpjFmt::InvalidLengthError < CnpjFmt::DomainError < RangeError < StandardError` (includes `CnpjFmt::Error`) +- **Category:** Domain error — sanitized CNPJ length is not exactly 14. +- **When it is raised:** **Not raised** by `#format` / `cnpj_fmt`; constructed and passed as the second argument to `on_fail`. +- **Example:** + +```ruby +custom_fail = ->(value, error) { + error # => # + "Invalid CNPJ: #{value}" +} + +BrUtils.new.cnpj.format('123', on_fail: custom_fail) # => "Invalid CNPJ: 123" +BrUtils.new.cnpj.format('123') # => "" (default on_fail) +``` + +- **How to rescue it:** Handle inside `on_fail` (typical), or rescue if you re-raise: + +```ruby +rescue CnpjFmt::InvalidLengthError + # this exact length violation + +rescue CnpjFmt::DomainError + # RangeError-rooted domain failures from cnpj-fmt +``` + +##### `CnpjFmt::OutOfRangeError` + +- **Inheritance:** `CnpjFmt::OutOfRangeError < CnpjFmt::DomainError < RangeError < StandardError` (includes `CnpjFmt::Error`) +- **Category:** Domain error — `hidden_start` / `hidden_end` outside `0`–`13`. +- **When it is raised:** Raised when building or applying formatter options with an out-of-range hide index. +- **Example:** + +```ruby +BrUtils.new.cnpj.format('91415732000793', hidden_start: -1) # raises CnpjFmt::OutOfRangeError +``` + +- **How to rescue it:** + +```ruby +rescue CnpjFmt::OutOfRangeError + # this exact range violation + +rescue CnpjFmt::DomainError + # RangeError-rooted domain failures from cnpj-fmt +``` + +##### `CnpjFmt::ValidationError` + +- **Inheritance:** `CnpjFmt::ValidationError < CnpjFmt::DomainError < RangeError < StandardError` (includes `CnpjFmt::Error`) +- **Category:** Domain error — a key option contains a disallowed character. +- **When it is raised:** Raised when `hidden_key`, `dot_key`, `slash_key`, or `dash_key` contains a forbidden character. +- **Example:** + +```ruby +BrUtils.new(cnpj: { formatter: { slash_key: 'å' } }) # raises CnpjFmt::ValidationError +``` + +- **How to rescue it:** + +```ruby +rescue CnpjFmt::ValidationError + # this exact domain validation failure + +rescue CnpjFmt::DomainError + # RangeError-rooted domain failures from cnpj-fmt +``` + +##### `CnpjGen::DomainError` + +- **Inheritance:** `CnpjGen::DomainError < RangeError < StandardError` (includes `CnpjGen::Error`) +- **Category:** Domain error — ancestor for generator domain leaves. +- **When it is raised:** Not raised directly; rescue target for `CnpjGen::ValidationError`. +- **Example:** Prefer `rescue CnpjGen::ValidationError` or `CnpjGen::DomainError`. +- **How to rescue it:** + +```ruby +rescue CnpjGen::DomainError + # ValidationError and other DomainError subclasses from cnpj-gen +``` + +##### `CnpjGen::TypeMismatchError` + +- **Inheritance:** `CnpjGen::TypeMismatchError < TypeError < StandardError` (includes `CnpjGen::Error`) +- **Category:** API misuse — wrong type for a generator option. +- **When it is raised:** Raised when `format`, `prefix`, or `type` has the wrong runtime type. +- **Example:** + +```ruby +BrUtils.new.cnpj.generate(prefix: 123) # raises CnpjGen::TypeMismatchError +``` + +- **How to rescue it:** + +```ruby +rescue CnpjGen::TypeMismatchError + # generator type-contract violation + +rescue TypeError + # native type errors, including CnpjGen::TypeMismatchError +``` + +##### `CnpjGen::InvalidArgumentCombinationError` + +- **Inheritance:** `CnpjGen::InvalidArgumentCombinationError < ArgumentError < StandardError` (includes `CnpjGen::Error`) +- **Category:** API misuse — mixed `options` and keywords on the generator API. +- **When it is raised:** Raised by `CnpjGen::CnpjGenerator` / `CnpjGen.cnpj_gen` when both an `options` instance/`Hash` and any non-`nil` keyword are passed. (The CNPJ aggregator raises `CnpjUtils::InvalidArgumentCombinationError` for the same pattern on `CnpjUtils#generate`.) +- **Example:** + +```ruby +CnpjGen::CnpjGenerator.new({ format: true }, prefix: '123') +# raises CnpjGen::InvalidArgumentCombinationError +``` + +- **How to rescue it:** + +```ruby +rescue CnpjGen::InvalidArgumentCombinationError + # generator invalid signature combination + +rescue ArgumentError + # native argument errors, including this one +``` + +##### `CnpjGen::ValidationError` + +- **Inheritance:** `CnpjGen::ValidationError < CnpjGen::DomainError < RangeError < StandardError` (includes `CnpjGen::Error`) +- **Category:** Domain error — ineligible `prefix` or disallowed `type`. +- **When it is raised:** Raised when `prefix` is a zeroed base/branch ID or 12 repeated digits, or when `type` is not `'alphabetic'`, `'alphanumeric'`, or `'numeric'`. +- **Example:** + +```ruby +BrUtils.new.cnpj.generate(type: 'boolean') # raises CnpjGen::ValidationError +``` + +- **How to rescue it:** + +```ruby +rescue CnpjGen::ValidationError + # this exact domain validation failure + +rescue CnpjGen::DomainError + # RangeError-rooted domain failures from cnpj-gen +``` + +##### `CnpjVal::DomainError` + +- **Inheritance:** `CnpjVal::DomainError < RangeError < StandardError` (includes `CnpjVal::Error`) +- **Category:** Domain error — ancestor for validator domain leaves. +- **When it is raised:** Not raised directly; rescue target for `CnpjVal::ValidationError`. +- **Example:** Prefer `rescue CnpjVal::ValidationError` or `CnpjVal::DomainError`. +- **How to rescue it:** + +```ruby +rescue CnpjVal::DomainError + # ValidationError and other DomainError subclasses from cnpj-val +``` + +##### `CnpjVal::TypeMismatchError` + +- **Inheritance:** `CnpjVal::TypeMismatchError < TypeError < StandardError` (includes `CnpjVal::Error`) +- **Category:** API misuse — wrong type for CNPJ input or a validator option. +- **When it is raised:** Raised when `#is_valid` / `cnpj_val` receives a value that is not a `String` or an `Array` of strings, or a validator option has the wrong type. Invalid CNPJ **data** returns `false` and does not raise. +- **Example:** + +```ruby +BrUtils.new.cnpj.is_valid(12_345_678_000_198) # raises CnpjVal::TypeMismatchError +BrUtils.new.cnpj.is_valid('00000000000000') # => false (invalid data, no raise) +``` + +- **How to rescue it:** + +```ruby +rescue CnpjVal::TypeMismatchError + # validator type-contract violation + +rescue TypeError + # native type errors, including CnpjVal::TypeMismatchError +``` + +##### `CnpjVal::InvalidArgumentCombinationError` + +- **Inheritance:** `CnpjVal::InvalidArgumentCombinationError < ArgumentError < StandardError` (includes `CnpjVal::Error`) +- **Category:** API misuse — mixed `options` and keywords on the validator API. +- **When it is raised:** Raised by `CnpjVal::CnpjValidator` / `CnpjVal.cnpj_val` when both an `options` instance/`Hash` and any non-`nil` keyword are passed. (The CNPJ aggregator raises `CnpjUtils::InvalidArgumentCombinationError` for the same pattern on `CnpjUtils#is_valid`.) +- **Example:** + +```ruby +CnpjVal.cnpj_val('98765432000198', { type: 'numeric' }, case_sensitive: false) +# raises CnpjVal::InvalidArgumentCombinationError +``` + +- **How to rescue it:** + +```ruby +rescue CnpjVal::InvalidArgumentCombinationError + # validator invalid signature combination + +rescue ArgumentError + # native argument errors, including this one +``` + +##### `CnpjVal::ValidationError` + +- **Inheritance:** `CnpjVal::ValidationError < CnpjVal::DomainError < RangeError < StandardError` (includes `CnpjVal::Error`) +- **Category:** Domain error — disallowed validator `type`. +- **When it is raised:** Raised when `type` is not `'alphanumeric'` or `'numeric'`. +- **Example:** + +```ruby +BrUtils.new.cnpj.is_valid('91415732000793', type: 'boolean') # raises CnpjVal::ValidationError +``` + +- **How to rescue it:** + +```ruby +rescue CnpjVal::ValidationError + # this exact domain validation failure + +rescue CnpjVal::DomainError + # RangeError-rooted domain failures from cnpj-val +``` + +##### `CpfUtils::TypeMismatchError` / `CpfUtils::InvalidArgumentCombinationError` + +- **Inheritance:** `CpfUtils::TypeMismatchError < TypeError` and `CpfUtils::InvalidArgumentCombinationError < ArgumentError` (both include `CpfUtils::Error`). +- **Category:** API misuse on the nested CPF aggregator. +- **When it is raised:** Raised when nested `cpf` settings to `CpfUtils.new` are a non-`Hash`, or when `CpfUtils#format` / `#generate` mix an options `Hash` with keywords. +- **Example:** + +```ruby +BrUtils.new.cpf.format({ hidden: true }, dash_key: '|') +# raises CpfUtils::InvalidArgumentCombinationError +``` + +- **How to rescue it:** + +```ruby +rescue CpfUtils::TypeMismatchError, CpfUtils::InvalidArgumentCombinationError + # CPF aggregator misuse (not BrUtils::Error) + +rescue CpfUtils::Error + # every custom error that includes CpfUtils::Error +``` + +##### `CnpjUtils::TypeMismatchError` / `CnpjUtils::InvalidArgumentCombinationError` + +- **Inheritance:** `CnpjUtils::TypeMismatchError < TypeError` and `CnpjUtils::InvalidArgumentCombinationError < ArgumentError` (both include `CnpjUtils::Error`). +- **Category:** API misuse on the nested CNPJ aggregator. +- **When it is raised:** Raised when nested `cnpj` settings to `CnpjUtils.new` are a non-`Hash`, or when `CnpjUtils#format` / `#generate` / `#is_valid` mix settings/options with keywords. +- **Example:** + +```ruby +BrUtils.new.cnpj.format({ hidden: true }, slash_key: '|') +# raises CnpjUtils::InvalidArgumentCombinationError +``` + +- **How to rescue it:** + +```ruby +rescue CnpjUtils::TypeMismatchError, CnpjUtils::InvalidArgumentCombinationError + # CNPJ aggregator misuse (not BrUtils::Error) + +rescue CnpjUtils::Error + # every custom error that includes CnpjUtils::Error +``` ### Bundled packages diff --git a/packages/br-utilities/README.pt.md b/packages/br-utilities/README.pt.md index 5e31afd..4a35802 100644 --- a/packages/br-utilities/README.pt.md +++ b/packages/br-utilities/README.pt.md @@ -600,12 +600,19 @@ end ```ruby require 'br-utilities' -# 2) BrUtils::DomainError — não aplicável: esta gem não define DomainError -# (nem folhas de domínio). Falhas de domínio vêm apenas dos pacotes incluídos. -# begin -# BrUtils.new(cpf: { formatter: { hidden_start: -1 } }) -# rescue BrUtils::DomainError # NameError — constante não definida -# end +# 2) DomainError dos pacotes — esta gem não define DomainError; falhas de domínio +# vêm dos pacotes incluídos e mantêm esses namespaces (ex.: CpfFmt, CnpjFmt). +begin + BrUtils.new.cpf.format('12345678909', hidden_start: -1) +rescue CpfFmt::DomainError + # CpfFmt::OutOfRangeError, CpfFmt::ValidationError e outras subclasses de DomainError +end + +begin + BrUtils.new.cnpj.format('91415732000793', hidden_start: -1) +rescue CnpjFmt::DomainError + # CnpjFmt::OutOfRangeError, CnpjFmt::ValidationError e outras subclasses de DomainError +end ``` ```ruby @@ -633,41 +640,586 @@ end #### Propagados dos pacotes incluídos -`BrUtils` não redefine tipos de exceção de domínio. Construção, setters e chamadas de métodos de domínio lançam os mesmos erros de [`cpf-utilities`](../cpf-utilities/README.pt.md) e [`cnpj-utilities`](../cnpj-utilities/README.pt.md): +Os erros dos componentes mantêm os namespaces dos pacotes e se propagam inalterados pela fachada (e pelas APIs aninhadas / irmãs na raiz). Cada pacote também expõe um módulo marcador `*::Error` para rescue em toda a biblioteca. **Dados** inválidos de CPF/CNPJ em `#is_valid` retornam `false` (sem raise de domínio). Falha de comprimento na formatação **não** é lançada por `#format` — é entregue a **`on_fail`** como `CpfFmt::InvalidLengthError` ou `CnpjFmt::InvalidLengthError` (`on_fail` padrão retorna `''`). -- **Formatação de CPF**: `CpfFmt::TypeMismatchError`, `CpfFmt::OutOfRangeError`, `CpfFmt::ValidationError`, `CpfFmt::InvalidLengthError` (passado a `on_fail`, não lançado por `#format`) e classes relacionadas. -- **Geração de CPF**: `CpfGen::TypeMismatchError`, `CpfGen::ValidationError` e classes relacionadas. -- **Validação de CPF**: `CpfVal::TypeMismatchError` e classes relacionadas. -- **Formatação de CNPJ**: `CnpjFmt::TypeMismatchError`, `CnpjFmt::OutOfRangeError`, `CnpjFmt::ValidationError`, `CnpjFmt::InvalidLengthError` (passado a `on_fail`) e classes relacionadas. -- **Geração de CNPJ**: `CnpjGen::TypeMismatchError`, `CnpjGen::ValidationError` e classes relacionadas. -- **Validação de CNPJ**: `CnpjVal::TypeMismatchError`, `CnpjVal::ValidationError` e classes relacionadas. +`CpfUtils::*` / `CnpjUtils::*` de uso indevido também se propagam quando agregadores aninhados são construídos ou chamados via `BrUtils`. Para tabelas de opções e casos extremos, veja [`cpf-utilities`](../cpf-utilities/README.pt.md) e [`cnpj-utilities`](../cnpj-utilities/README.pt.md). -Tipos de opção inválidos são tipicamente subclasses de **`TypeError`** (`*::TypeMismatchError`); valores de opção inválidos são erros de domínio sob a hierarquia `DomainError` de cada pacote. Falhas de validação de CPF e CNPJ retornam `false`. Falhas de comprimento na formatação são tratadas por **`on_fail`** (padrão retorna string vazia). +##### Resumo + +| Classe | Herda de | Categoria | Condição de disparo | +|-------|---------------|----------|-------------------| +| `CpfUtils::InvalidArgumentCombinationError` | `CpfUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CpfUtils::Error`) | Uso indevido da API | Instância/`Hash` de options e qualquer argumento nomeado não-`nil` em `CpfUtils#format` / `#generate` | +| `CpfUtils::TypeMismatchError` | `CpfUtils::TypeMismatchError < TypeError < StandardError` (+ `include CpfUtils::Error`) | Uso indevido da API | Violação de contrato do `Hash` de settings `cpf` aninhado em `CpfUtils.new` | +| `CnpjUtils::InvalidArgumentCombinationError` | `CnpjUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjUtils::Error`) | Uso indevido da API | Settings/options e qualquer argumento nomeado não-`nil` na construção de `CnpjUtils` / `#format` / `#generate` / `#is_valid` | +| `CnpjUtils::TypeMismatchError` | `CnpjUtils::TypeMismatchError < TypeError < StandardError` (+ `include CnpjUtils::Error`) | Uso indevido da API | Violação de contrato do `Hash` de settings `cnpj` aninhado em `CnpjUtils.new` | +| `CpfFmt::InvalidArgumentCombinationError` | `CpfFmt::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CpfFmt::Error`) | Uso indevido da API | Instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` em `CpfFormatter` / `cpf_fmt` | +| `CpfFmt::TypeMismatchError` | `CpfFmt::TypeMismatchError < TypeError < StandardError` (+ `include CpfFmt::Error`) | Uso indevido da API | Entrada de CPF ou opção do formatador tem o tipo errado (ou o retorno de `on_fail` não é `String`) | +| `CpfGen::InvalidArgumentCombinationError` | `CpfGen::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CpfGen::Error`) | Uso indevido da API | Instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` em `CpfGenerator` / `cpf_gen` | +| `CpfGen::TypeMismatchError` | `CpfGen::TypeMismatchError < TypeError < StandardError` (+ `include CpfGen::Error`) | Uso indevido da API | Opção do gerador (`format` / `prefix`) tem o tipo errado | +| `CpfVal::TypeMismatchError` | `CpfVal::TypeMismatchError < TypeError < StandardError` (+ `include CpfVal::Error`) | Uso indevido da API | Entrada de CPF não é `String` nem `Array` de strings | +| `CnpjFmt::InvalidArgumentCombinationError` | `CnpjFmt::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjFmt::Error`) | Uso indevido da API | Instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` em `CnpjFormatter` / `cnpj_fmt` | +| `CnpjFmt::TypeMismatchError` | `CnpjFmt::TypeMismatchError < TypeError < StandardError` (+ `include CnpjFmt::Error`) | Uso indevido da API | Entrada de CNPJ ou opção do formatador tem o tipo errado (ou o retorno de `on_fail` não é `String`) | +| `CnpjGen::InvalidArgumentCombinationError` | `CnpjGen::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjGen::Error`) | Uso indevido da API | Instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` em `CnpjGenerator` / `cnpj_gen` | +| `CnpjGen::TypeMismatchError` | `CnpjGen::TypeMismatchError < TypeError < StandardError` (+ `include CnpjGen::Error`) | Uso indevido da API | Opção do gerador (`format` / `prefix` / `type`) tem o tipo errado | +| `CnpjVal::InvalidArgumentCombinationError` | `CnpjVal::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjVal::Error`) | Uso indevido da API | Instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` em `CnpjValidator` / `cnpj_val` | +| `CnpjVal::TypeMismatchError` | `CnpjVal::TypeMismatchError < TypeError < StandardError` (+ `include CnpjVal::Error`) | Uso indevido da API | Entrada de CNPJ ou opção do validador tem o tipo errado | +| `CpfFmt::InvalidLengthError` | `CpfFmt::InvalidLengthError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Erro de domínio | Comprimento sanitizado ≠ 11 — **passado a `on_fail`**, não lançado por `#format` | +| `CpfFmt::OutOfRangeError` | `CpfFmt::OutOfRangeError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Erro de domínio | `hidden_start` / `hidden_end` fora de `0`–`10` | +| `CpfFmt::ValidationError` | `CpfFmt::ValidationError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Erro de domínio | `hidden_key` / `dot_key` / `dash_key` contém caractere proibido | +| `CpfGen::ValidationError` | `CpfGen::ValidationError < CpfGen::DomainError < RangeError < StandardError` (+ `include CpfGen::Error`) | Erro de domínio | `prefix` inelegível (base zerada ou 9 dígitos repetidos) | +| `CnpjFmt::InvalidLengthError` | `CnpjFmt::InvalidLengthError < CnpjFmt::DomainError < RangeError < StandardError` (+ `include CnpjFmt::Error`) | Erro de domínio | Comprimento sanitizado ≠ 14 — **passado a `on_fail`**, não lançado por `#format` | +| `CnpjFmt::OutOfRangeError` | `CnpjFmt::OutOfRangeError < CnpjFmt::DomainError < RangeError < StandardError` (+ `include CnpjFmt::Error`) | Erro de domínio | `hidden_start` / `hidden_end` fora de `0`–`13` | +| `CnpjFmt::ValidationError` | `CnpjFmt::ValidationError < CnpjFmt::DomainError < RangeError < StandardError` (+ `include CnpjFmt::Error`) | Erro de domínio | `hidden_key` / `dot_key` / `slash_key` / `dash_key` contém caractere proibido | +| `CnpjGen::ValidationError` | `CnpjGen::ValidationError < CnpjGen::DomainError < RangeError < StandardError` (+ `include CnpjGen::Error`) | Erro de domínio | `prefix` inelegível, ou `type` fora de `'alphabetic'` / `'alphanumeric'` / `'numeric'` | +| `CnpjVal::ValidationError` | `CnpjVal::ValidationError < CnpjVal::DomainError < RangeError < StandardError` (+ `include CnpjVal::Error`) | Erro de domínio | `type` do validador não é `'alphanumeric'` nem `'numeric'` | + +##### `CpfFmt::DomainError` + +- **Herança:** `CpfFmt::DomainError < RangeError < StandardError` (inclui `CpfFmt::Error`) +- **Categoria:** Erro de domínio — ancestral das folhas de domínio do formatador. +- **Quando é lançado:** Não é lançado diretamente; alvo de rescue para `OutOfRangeError`, `ValidationError` e `InvalidLengthError` relançado. +- **Exemplo:** Prefira resgatar uma folha, ou `CpfFmt::DomainError` para todas as falhas de domínio do formatador de CPF. +- **Como resgatar:** ```ruby -require 'br-utilities' +rescue CpfFmt::DomainError + # OutOfRangeError, ValidationError, InvalidLengthError (se relançado a partir de on_fail) +``` -begin - BrUtils.new.cnpj.format(12_345) -rescue CnpjFmt::TypeMismatchError => e - puts e.message -end +##### `CpfFmt::TypeMismatchError` -begin - BrUtils.new.cnpj.is_valid(12_345_678_000_198) -rescue CnpjVal::TypeMismatchError => e - puts e.message -end +- **Herança:** `CpfFmt::TypeMismatchError < TypeError < StandardError` (inclui `CpfFmt::Error`) +- **Categoria:** Uso indevido da API — tipo errado para entrada de CPF ou opção do formatador. +- **Quando é lançado:** Lançado quando `#format` / `cpf_fmt` recebe entrada que não é `String` / `Array`, uma opção tem o tipo errado, ou `on_fail` não retorna `String`. +- **Exemplo:** + +```ruby +BrUtils.new.cpf.format(12_345) # lança CpfFmt::TypeMismatchError +``` + +- **Como resgatar:** + +```ruby +rescue CpfFmt::TypeMismatchError + # violação de contrato de tipo do formatador + +rescue TypeError + # erros nativos de tipo, incluindo CpfFmt::TypeMismatchError +``` + +##### `CpfFmt::InvalidArgumentCombinationError` + +- **Herança:** `CpfFmt::InvalidArgumentCombinationError < ArgumentError < StandardError` (inclui `CpfFmt::Error`) +- **Categoria:** Uso indevido da API — `options` e argumentos nomeados misturados na API do formatador. +- **Quando é lançado:** Lançado por `CpfFmt::CpfFormatter` / `CpfFmt.cpf_fmt` quando uma instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` são passados juntos. (O agregador de CPF lança `CpfUtils::InvalidArgumentCombinationError` para o mesmo padrão em `CpfUtils#format`.) +- **Exemplo:** + +```ruby +CpfFmt::CpfFormatter.new({ dash_key: '_' }, hidden: true) +# lança CpfFmt::InvalidArgumentCombinationError +``` + +- **Como resgatar:** + +```ruby +rescue CpfFmt::InvalidArgumentCombinationError + # combinação inválida de assinatura do formatador + +rescue ArgumentError + # erros nativos de argumento, incluindo este +``` + +##### `CpfFmt::InvalidLengthError` (entregue via callback) + +- **Herança:** `CpfFmt::InvalidLengthError < CpfFmt::DomainError < RangeError < StandardError` (inclui `CpfFmt::Error`) +- **Categoria:** Erro de domínio — o comprimento sanitizado do CPF não é exatamente 11. +- **Quando é lançado:** **Não é lançado** por `#format` / `cpf_fmt`; construído e passado como segundo argumento a `on_fail`. +- **Exemplo:** + +```ruby +custom_fail = ->(value, error) { + error # => # + "Invalid CPF: #{value}" +} + +BrUtils.new.cpf.format('123', on_fail: custom_fail) # => "Invalid CPF: 123" +BrUtils.new.cpf.format('123') # => "" (on_fail padrão) +``` + +- **Como resgatar:** Trate dentro de `on_fail` (típico), ou faça rescue se relançar: + +```ruby +rescue CpfFmt::InvalidLengthError + # esta violação exata de comprimento + +rescue CpfFmt::DomainError + # falhas de domínio com raiz em RangeError de cpf-fmt +``` + +##### `CpfFmt::OutOfRangeError` + +- **Herança:** `CpfFmt::OutOfRangeError < CpfFmt::DomainError < RangeError < StandardError` (inclui `CpfFmt::Error`) +- **Categoria:** Erro de domínio — `hidden_start` / `hidden_end` fora de `0`–`10`. +- **Quando é lançado:** Lançado ao construir ou aplicar opções do formatador com índice de ocultação fora do intervalo. +- **Exemplo:** -# on_fail customizado para comprimento inválido -custom_fail = ->(value, _exception) { "Invalid: #{value}" } +```ruby +BrUtils.new.cpf.format('12345678909', hidden_start: -1) # lança CpfFmt::OutOfRangeError +``` + +- **Como resgatar:** + +```ruby +rescue CpfFmt::OutOfRangeError + # esta violação exata de intervalo + +rescue CpfFmt::DomainError + # falhas de domínio com raiz em RangeError de cpf-fmt +``` + +##### `CpfFmt::ValidationError` + +- **Herança:** `CpfFmt::ValidationError < CpfFmt::DomainError < RangeError < StandardError` (inclui `CpfFmt::Error`) +- **Categoria:** Erro de domínio — uma opção de chave contém um caractere proibido. +- **Quando é lançado:** Lançado quando `hidden_key`, `dot_key` ou `dash_key` contém um caractere proibido. +- **Exemplo:** + +```ruby +BrUtils.new(cpf: { formatter: { dot_key: 'å' } }) # lança CpfFmt::ValidationError +``` + +- **Como resgatar:** + +```ruby +rescue CpfFmt::ValidationError + # esta falha exata de validação de domínio + +rescue CpfFmt::DomainError + # falhas de domínio com raiz em RangeError de cpf-fmt +``` + +##### `CpfGen::DomainError` + +- **Herança:** `CpfGen::DomainError < RangeError < StandardError` (inclui `CpfGen::Error`) +- **Categoria:** Erro de domínio — ancestral das folhas de domínio do gerador. +- **Quando é lançado:** Não é lançado diretamente; alvo de rescue para `CpfGen::ValidationError`. +- **Exemplo:** Prefira `rescue CpfGen::ValidationError` ou `CpfGen::DomainError`. +- **Como resgatar:** -BrUtils.cpf.format('short', on_fail: custom_fail) # => "Invalid: short" -BrUtils.cnpj.format('short', on_fail: custom_fail) # => "Invalid: short" -BrUtils.cpf.format('short') # => "" (on_fail padrão) +```ruby +rescue CpfGen::DomainError + # ValidationError e outras subclasses de DomainError de cpf-gen +``` + +##### `CpfGen::TypeMismatchError` + +- **Herança:** `CpfGen::TypeMismatchError < TypeError < StandardError` (inclui `CpfGen::Error`) +- **Categoria:** Uso indevido da API — tipo errado para uma opção do gerador. +- **Quando é lançado:** Lançado quando `format` ou `prefix` tem o tipo de runtime errado. +- **Exemplo:** + +```ruby +BrUtils.new.cpf.generate(prefix: 123) # lança CpfGen::TypeMismatchError +``` + +- **Como resgatar:** + +```ruby +rescue CpfGen::TypeMismatchError + # violação de contrato de tipo do gerador + +rescue TypeError + # erros nativos de tipo, incluindo CpfGen::TypeMismatchError +``` + +##### `CpfGen::InvalidArgumentCombinationError` + +- **Herança:** `CpfGen::InvalidArgumentCombinationError < ArgumentError < StandardError` (inclui `CpfGen::Error`) +- **Categoria:** Uso indevido da API — `options` e argumentos nomeados misturados na API do gerador. +- **Quando é lançado:** Lançado por `CpfGen::CpfGenerator` / `CpfGen.cpf_gen` quando uma instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` são passados juntos. (O agregador de CPF lança `CpfUtils::InvalidArgumentCombinationError` para o mesmo padrão em `CpfUtils#generate`.) +- **Exemplo:** + +```ruby +CpfGen::CpfGenerator.new({ format: true }, prefix: '123') +# lança CpfGen::InvalidArgumentCombinationError +``` + +- **Como resgatar:** + +```ruby +rescue CpfGen::InvalidArgumentCombinationError + # combinação inválida de assinatura do gerador + +rescue ArgumentError + # erros nativos de argumento, incluindo este +``` + +##### `CpfGen::ValidationError` + +- **Herança:** `CpfGen::ValidationError < CpfGen::DomainError < RangeError < StandardError` (inclui `CpfGen::Error`) +- **Categoria:** Erro de domínio — `prefix` inelegível. +- **Quando é lançado:** Lançado quando `prefix` é uma base zerada (`'000000000'`) ou 9 dígitos repetidos (ex.: `'999999999'`). +- **Exemplo:** + +```ruby +BrUtils.new.cpf.generate(prefix: '000000000') # lança CpfGen::ValidationError +``` + +- **Como resgatar:** + +```ruby +rescue CpfGen::ValidationError + # esta falha exata de validação de domínio + +rescue CpfGen::DomainError + # falhas de domínio com raiz em RangeError de cpf-gen +``` + +##### `CpfVal::TypeMismatchError` + +- **Herança:** `CpfVal::TypeMismatchError < TypeError < StandardError` (inclui `CpfVal::Error`) +- **Categoria:** Uso indevido da API — tipo errado para entrada de CPF. +- **Quando é lançado:** Lançado quando `#is_valid` / `cpf_val` recebe um valor que não é `String` nem `Array` de strings (incluindo elemento de array que não é string). **Dados** de CPF inválidos retornam `false` e não lançam. +- **Exemplo:** + +```ruby +BrUtils.new.cpf.is_valid(12_345_678_909) # lança CpfVal::TypeMismatchError +BrUtils.new.cpf.is_valid('12345678900') # => false (dados inválidos, sem raise) +``` + +- **Como resgatar:** + +```ruby +rescue CpfVal::TypeMismatchError + # violação de contrato de tipo do validador + +rescue TypeError + # erros nativos de tipo, incluindo CpfVal::TypeMismatchError +``` + +##### `CnpjFmt::DomainError` + +- **Herança:** `CnpjFmt::DomainError < RangeError < StandardError` (inclui `CnpjFmt::Error`) +- **Categoria:** Erro de domínio — ancestral das folhas de domínio do formatador. +- **Quando é lançado:** Não é lançado diretamente; alvo de rescue para `OutOfRangeError`, `ValidationError` e `InvalidLengthError` relançado. +- **Exemplo:** Prefira resgatar uma folha, ou `CnpjFmt::DomainError` para todas as falhas de domínio do formatador de CNPJ. +- **Como resgatar:** + +```ruby +rescue CnpjFmt::DomainError + # OutOfRangeError, ValidationError, InvalidLengthError (se relançado a partir de on_fail) +``` + +##### `CnpjFmt::TypeMismatchError` + +- **Herança:** `CnpjFmt::TypeMismatchError < TypeError < StandardError` (inclui `CnpjFmt::Error`) +- **Categoria:** Uso indevido da API — tipo errado para entrada de CNPJ ou opção do formatador. +- **Quando é lançado:** Lançado quando `#format` / `cnpj_fmt` recebe entrada que não é `String` / `Array`, uma opção tem o tipo errado, ou `on_fail` não retorna `String`. +- **Exemplo:** + +```ruby +BrUtils.new.cnpj.format(12_345) # lança CnpjFmt::TypeMismatchError +``` + +- **Como resgatar:** + +```ruby +rescue CnpjFmt::TypeMismatchError + # violação de contrato de tipo do formatador + +rescue TypeError + # erros nativos de tipo, incluindo CnpjFmt::TypeMismatchError +``` + +##### `CnpjFmt::InvalidArgumentCombinationError` + +- **Herança:** `CnpjFmt::InvalidArgumentCombinationError < ArgumentError < StandardError` (inclui `CnpjFmt::Error`) +- **Categoria:** Uso indevido da API — `options` e argumentos nomeados misturados na API do formatador. +- **Quando é lançado:** Lançado por `CnpjFmt::CnpjFormatter` / `CnpjFmt.cnpj_fmt` quando uma instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` são passados juntos. (O agregador de CNPJ lança `CnpjUtils::InvalidArgumentCombinationError` para o mesmo padrão em `CnpjUtils#format`.) +- **Exemplo:** + +```ruby +CnpjFmt::CnpjFormatter.new({ slash_key: '|' }, hidden: true) +# lança CnpjFmt::InvalidArgumentCombinationError ``` -Para listas exaustivas de exceções e comportamento em casos extremos, consulte o README de cada [pacote incluído](#pacotes-incluídos). +- **Como resgatar:** + +```ruby +rescue CnpjFmt::InvalidArgumentCombinationError + # combinação inválida de assinatura do formatador + +rescue ArgumentError + # erros nativos de argumento, incluindo este +``` + +##### `CnpjFmt::InvalidLengthError` (entregue via callback) + +- **Herança:** `CnpjFmt::InvalidLengthError < CnpjFmt::DomainError < RangeError < StandardError` (inclui `CnpjFmt::Error`) +- **Categoria:** Erro de domínio — o comprimento sanitizado do CNPJ não é exatamente 14. +- **Quando é lançado:** **Não é lançado** por `#format` / `cnpj_fmt`; construído e passado como segundo argumento a `on_fail`. +- **Exemplo:** + +```ruby +custom_fail = ->(value, error) { + error # => # + "Invalid CNPJ: #{value}" +} + +BrUtils.new.cnpj.format('123', on_fail: custom_fail) # => "Invalid CNPJ: 123" +BrUtils.new.cnpj.format('123') # => "" (on_fail padrão) +``` + +- **Como resgatar:** Trate dentro de `on_fail` (típico), ou faça rescue se relançar: + +```ruby +rescue CnpjFmt::InvalidLengthError + # esta violação exata de comprimento + +rescue CnpjFmt::DomainError + # falhas de domínio com raiz em RangeError de cnpj-fmt +``` + +##### `CnpjFmt::OutOfRangeError` + +- **Herança:** `CnpjFmt::OutOfRangeError < CnpjFmt::DomainError < RangeError < StandardError` (inclui `CnpjFmt::Error`) +- **Categoria:** Erro de domínio — `hidden_start` / `hidden_end` fora de `0`–`13`. +- **Quando é lançado:** Lançado ao construir ou aplicar opções do formatador com índice de ocultação fora do intervalo. +- **Exemplo:** + +```ruby +BrUtils.new.cnpj.format('91415732000793', hidden_start: -1) # lança CnpjFmt::OutOfRangeError +``` + +- **Como resgatar:** + +```ruby +rescue CnpjFmt::OutOfRangeError + # esta violação exata de intervalo + +rescue CnpjFmt::DomainError + # falhas de domínio com raiz em RangeError de cnpj-fmt +``` + +##### `CnpjFmt::ValidationError` + +- **Herança:** `CnpjFmt::ValidationError < CnpjFmt::DomainError < RangeError < StandardError` (inclui `CnpjFmt::Error`) +- **Categoria:** Erro de domínio — uma opção de chave contém um caractere proibido. +- **Quando é lançado:** Lançado quando `hidden_key`, `dot_key`, `slash_key` ou `dash_key` contém um caractere proibido. +- **Exemplo:** + +```ruby +BrUtils.new(cnpj: { formatter: { slash_key: 'å' } }) # lança CnpjFmt::ValidationError +``` + +- **Como resgatar:** + +```ruby +rescue CnpjFmt::ValidationError + # esta falha exata de validação de domínio + +rescue CnpjFmt::DomainError + # falhas de domínio com raiz em RangeError de cnpj-fmt +``` + +##### `CnpjGen::DomainError` + +- **Herança:** `CnpjGen::DomainError < RangeError < StandardError` (inclui `CnpjGen::Error`) +- **Categoria:** Erro de domínio — ancestral das folhas de domínio do gerador. +- **Quando é lançado:** Não é lançado diretamente; alvo de rescue para `CnpjGen::ValidationError`. +- **Exemplo:** Prefira `rescue CnpjGen::ValidationError` ou `CnpjGen::DomainError`. +- **Como resgatar:** + +```ruby +rescue CnpjGen::DomainError + # ValidationError e outras subclasses de DomainError de cnpj-gen +``` + +##### `CnpjGen::TypeMismatchError` + +- **Herança:** `CnpjGen::TypeMismatchError < TypeError < StandardError` (inclui `CnpjGen::Error`) +- **Categoria:** Uso indevido da API — tipo errado para uma opção do gerador. +- **Quando é lançado:** Lançado quando `format`, `prefix` ou `type` tem o tipo de runtime errado. +- **Exemplo:** + +```ruby +BrUtils.new.cnpj.generate(prefix: 123) # lança CnpjGen::TypeMismatchError +``` + +- **Como resgatar:** + +```ruby +rescue CnpjGen::TypeMismatchError + # violação de contrato de tipo do gerador + +rescue TypeError + # erros nativos de tipo, incluindo CnpjGen::TypeMismatchError +``` + +##### `CnpjGen::InvalidArgumentCombinationError` + +- **Herança:** `CnpjGen::InvalidArgumentCombinationError < ArgumentError < StandardError` (inclui `CnpjGen::Error`) +- **Categoria:** Uso indevido da API — `options` e argumentos nomeados misturados na API do gerador. +- **Quando é lançado:** Lançado por `CnpjGen::CnpjGenerator` / `CnpjGen.cnpj_gen` quando uma instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` são passados juntos. (O agregador de CNPJ lança `CnpjUtils::InvalidArgumentCombinationError` para o mesmo padrão em `CnpjUtils#generate`.) +- **Exemplo:** + +```ruby +CnpjGen::CnpjGenerator.new({ format: true }, prefix: '123') +# lança CnpjGen::InvalidArgumentCombinationError +``` + +- **Como resgatar:** + +```ruby +rescue CnpjGen::InvalidArgumentCombinationError + # combinação inválida de assinatura do gerador + +rescue ArgumentError + # erros nativos de argumento, incluindo este +``` + +##### `CnpjGen::ValidationError` + +- **Herança:** `CnpjGen::ValidationError < CnpjGen::DomainError < RangeError < StandardError` (inclui `CnpjGen::Error`) +- **Categoria:** Erro de domínio — `prefix` inelegível ou `type` não permitido. +- **Quando é lançado:** Lançado quando `prefix` é base/filial zerada ou 12 dígitos repetidos, ou quando `type` não é `'alphabetic'`, `'alphanumeric'` ou `'numeric'`. +- **Exemplo:** + +```ruby +BrUtils.new.cnpj.generate(type: 'boolean') # lança CnpjGen::ValidationError +``` + +- **Como resgatar:** + +```ruby +rescue CnpjGen::ValidationError + # esta falha exata de validação de domínio + +rescue CnpjGen::DomainError + # falhas de domínio com raiz em RangeError de cnpj-gen +``` + +##### `CnpjVal::DomainError` + +- **Herança:** `CnpjVal::DomainError < RangeError < StandardError` (inclui `CnpjVal::Error`) +- **Categoria:** Erro de domínio — ancestral das folhas de domínio do validador. +- **Quando é lançado:** Não é lançado diretamente; alvo de rescue para `CnpjVal::ValidationError`. +- **Exemplo:** Prefira `rescue CnpjVal::ValidationError` ou `CnpjVal::DomainError`. +- **Como resgatar:** + +```ruby +rescue CnpjVal::DomainError + # ValidationError e outras subclasses de DomainError de cnpj-val +``` + +##### `CnpjVal::TypeMismatchError` + +- **Herança:** `CnpjVal::TypeMismatchError < TypeError < StandardError` (inclui `CnpjVal::Error`) +- **Categoria:** Uso indevido da API — tipo errado para entrada de CNPJ ou opção do validador. +- **Quando é lançado:** Lançado quando `#is_valid` / `cnpj_val` recebe um valor que não é `String` nem `Array` de strings, ou uma opção do validador tem o tipo errado. **Dados** de CNPJ inválidos retornam `false` e não lançam. +- **Exemplo:** + +```ruby +BrUtils.new.cnpj.is_valid(12_345_678_000_198) # lança CnpjVal::TypeMismatchError +BrUtils.new.cnpj.is_valid('00000000000000') # => false (dados inválidos, sem raise) +``` + +- **Como resgatar:** + +```ruby +rescue CnpjVal::TypeMismatchError + # violação de contrato de tipo do validador + +rescue TypeError + # erros nativos de tipo, incluindo CnpjVal::TypeMismatchError +``` + +##### `CnpjVal::InvalidArgumentCombinationError` + +- **Herança:** `CnpjVal::InvalidArgumentCombinationError < ArgumentError < StandardError` (inclui `CnpjVal::Error`) +- **Categoria:** Uso indevido da API — `options` e argumentos nomeados misturados na API do validador. +- **Quando é lançado:** Lançado por `CnpjVal::CnpjValidator` / `CnpjVal.cnpj_val` quando uma instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` são passados juntos. (O agregador de CNPJ lança `CnpjUtils::InvalidArgumentCombinationError` para o mesmo padrão em `CnpjUtils#is_valid`.) +- **Exemplo:** + +```ruby +CnpjVal.cnpj_val('98765432000198', { type: 'numeric' }, case_sensitive: false) +# lança CnpjVal::InvalidArgumentCombinationError +``` + +- **Como resgatar:** + +```ruby +rescue CnpjVal::InvalidArgumentCombinationError + # combinação inválida de assinatura do validador + +rescue ArgumentError + # erros nativos de argumento, incluindo este +``` + +##### `CnpjVal::ValidationError` + +- **Herança:** `CnpjVal::ValidationError < CnpjVal::DomainError < RangeError < StandardError` (inclui `CnpjVal::Error`) +- **Categoria:** Erro de domínio — `type` do validador não permitido. +- **Quando é lançado:** Lançado quando `type` não é `'alphanumeric'` nem `'numeric'`. +- **Exemplo:** + +```ruby +BrUtils.new.cnpj.is_valid('91415732000793', type: 'boolean') # lança CnpjVal::ValidationError +``` + +- **Como resgatar:** + +```ruby +rescue CnpjVal::ValidationError + # esta falha exata de validação de domínio + +rescue CnpjVal::DomainError + # falhas de domínio com raiz em RangeError de cnpj-val +``` + +##### `CpfUtils::TypeMismatchError` / `CpfUtils::InvalidArgumentCombinationError` + +- **Herança:** `CpfUtils::TypeMismatchError < TypeError` and `CpfUtils::InvalidArgumentCombinationError < ArgumentError` (ambos incluem `CpfUtils::Error`). +- **Categoria:** Uso indevido da API on the nested CPF aggregator. +- **Quando é lançado:** Lançado quando settings `cpf` aninhados em `CpfUtils.new` não são um `Hash`, ou quando `CpfUtils#format` / `#generate` misturam um `Hash` de options com argumentos nomeados. +- **Exemplo:** + +```ruby +BrUtils.new.cpf.format({ hidden: true }, dash_key: '|') +# lança CpfUtils::InvalidArgumentCombinationError +``` + +- **Como resgatar:** + +```ruby +rescue CpfUtils::TypeMismatchError, CpfUtils::InvalidArgumentCombinationError + # uso indevido do agregador de CPF (não BrUtils::Error) + +rescue CpfUtils::Error + # todo erro customizado que inclui CpfUtils::Error +``` + +##### `CnpjUtils::TypeMismatchError` / `CnpjUtils::InvalidArgumentCombinationError` + +- **Herança:** `CnpjUtils::TypeMismatchError < TypeError` and `CnpjUtils::InvalidArgumentCombinationError < ArgumentError` (ambos incluem `CnpjUtils::Error`). +- **Categoria:** Uso indevido da API on the nested CNPJ aggregator. +- **Quando é lançado:** Lançado quando settings `cnpj` aninhados em `CnpjUtils.new` não são um `Hash`, ou quando `CnpjUtils#format` / `#generate` / `#is_valid` misturam settings/options com argumentos nomeados. +- **Exemplo:** + +```ruby +BrUtils.new.cnpj.format({ hidden: true }, slash_key: '|') +# lança CnpjUtils::InvalidArgumentCombinationError +``` + +- **Como resgatar:** + +```ruby +rescue CnpjUtils::TypeMismatchError, CnpjUtils::InvalidArgumentCombinationError + # uso indevido do agregador de CNPJ (não BrUtils::Error) + +rescue CnpjUtils::Error + # todo erro customizado que inclui CnpjUtils::Error +``` ### Pacotes incluídos From cbe9553406b0f013982e8ed11459954f3a619f87 Mon Sep 17 00:00:00 2001 From: juliolmuller Date: Thu, 20 Aug 2026 17:47:44 -0300 Subject: [PATCH 10/14] test(br-utils): add tests for domain utils handling and keyword precedence Adjustment as per @coderabbitai review comment at https://github.com/LacusSolutions/br-utils-ruby/pull/30#discussion_r3753266006. Co-authored-by: CodeRabbit AI <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Cursor Agent --- packages/br-utilities/tests/br_utils.spec.rb | 26 ++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/packages/br-utilities/tests/br_utils.spec.rb b/packages/br-utilities/tests/br_utils.spec.rb index 6a02322..ccc4c5f 100644 --- a/packages/br-utilities/tests/br_utils.spec.rb +++ b/packages/br-utilities/tests/br_utils.spec.rb @@ -613,6 +613,32 @@ def default_cnpj_validator_options_snapshot expect(utils.cnpj).to equal(cnpj_utils) end end + + it 'adopts domain utils from string keys' do + cpf_utils = CpfUtils.new + cnpj_utils = CnpjUtils.new + utils = described_class.new({ 'cpf' => cpf_utils, 'cnpj' => cnpj_utils }) + + aggregate_failures do + expect(utils.cpf).to equal(cpf_utils) + expect(utils.cnpj).to equal(cnpj_utils) + end + end + end + + context 'when a domain keyword and its flat keyword are both given' do + it 'ignores the flat keyword' do + cpf_utils = CpfUtils.new + utils = described_class.new( + cpf: cpf_utils, + cpf_formatter: CpfFmt::CpfFormatterOptions.new(dash_key: '|') + ) + + aggregate_failures do + expect(utils.cpf).to equal(cpf_utils) + expect(utils.cpf.formatter.options.all[:dash_key]).to eq('-') + end + end end context 'when called with a non-Hash settings value' do From d81145c4b9eadb66373cc242a1ffac09835c66a6 Mon Sep 17 00:00:00 2001 From: juliolmuller Date: Thu, 20 Aug 2026 17:53:59 -0300 Subject: [PATCH 11/14] docs: fix relative links of root-level documentation Fix as per @coderabbitai review comment at https://github.com/LacusSolutions/br-utils-ruby/pull/30#discussion_r3753266031. Co-authored-by: CodeRabbit AI <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Cursor Agent --- README.md | 14 +++++++------- README.pt.md | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 8b2f19c..984b0f1 100644 --- a/README.md +++ b/README.md @@ -236,7 +236,7 @@ To change a single nested option without replacing the whole domain utils, mutat ### CPF operations -CPF methods are accessed via `BrUtils.cpf`, `utils.cpf`, `CpfUtils`, or the `CpfFmt` / `CpfGen` / `CpfVal` helpers. CPF uses the API from [`cpf-utilities`](../cpf-utilities/README.md). +CPF methods are accessed via `BrUtils.cpf`, `utils.cpf`, `CpfUtils`, or the `CpfFmt` / `CpfGen` / `CpfVal` helpers. CPF uses the API from [`cpf-utilities`](packages/cpf-utilities/README.md). #### Formatting (`#format` / `CpfFmt.cpf_fmt`) @@ -299,7 +299,7 @@ CpfVal.cpf_val('12345678909') # => true ### CNPJ operations -CNPJ methods are accessed via `BrUtils.cnpj`, `utils.cnpj`, `CnpjUtils`, or the `CnpjFmt` / `CnpjGen` / `CnpjVal` helpers. CNPJ uses the API from [`cnpj-utilities`](../cnpj-utilities/README.md). +CNPJ methods are accessed via `BrUtils.cnpj`, `utils.cnpj`, `CnpjUtils`, or the `CnpjFmt` / `CnpjGen` / `CnpjVal` helpers. CNPJ uses the API from [`cnpj-utilities`](packages/cnpj-utilities/README.md). #### Formatting (`#format` / `CnpjFmt.cnpj_fmt`) @@ -468,7 +468,7 @@ CnpjGen.cnpj_gen(type: 'numeric') # => e.g. "65453043000178" CnpjVal.cnpj_val('9JN7MGLJZXIO50') # => true ``` -See [`cpf-utilities`](../cpf-utilities/README.md) and [`cnpj-utilities`](../cnpj-utilities/README.md) for full option and error details. +See [`cpf-utilities`](packages/cpf-utilities/README.md) and [`cnpj-utilities`](packages/cnpj-utilities/README.md) for full option and error details. ### Mixing styles @@ -649,7 +649,7 @@ end 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/CNPJ **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` or `CnpjFmt::InvalidLengthError` (default `on_fail` returns `''`). -`CpfUtils::*` / `CnpjUtils::*` misuse errors also propagate when nested aggregators are constructed or called through `BrUtils`. For exhaustive option tables and extra edge cases, see [`cpf-utilities`](../cpf-utilities/README.md) and [`cnpj-utilities`](../cnpj-utilities/README.md). +`CpfUtils::*` / `CnpjUtils::*` misuse errors also propagate when nested aggregators are constructed or called through `BrUtils`. For exhaustive option tables and extra edge cases, see [`cpf-utilities`](packages/cpf-utilities/README.md) and [`cnpj-utilities`](packages/cnpj-utilities/README.md). ##### Summary @@ -1232,8 +1232,8 @@ rescue CnpjUtils::Error | Package | Main resources | README | |---------|----------------|--------| -| [`cpf-utilities`](https://rubygems.org/gems/cpf-utilities) | `CpfUtils`, `CpfFormatter`, `CpfGenerator`, `CpfValidator`, `CpfFmt.cpf_fmt`, `CpfGen.cpf_gen`, `CpfVal.cpf_val` | [docs](../cpf-utilities/README.md) | -| [`cnpj-utilities`](https://rubygems.org/gems/cnpj-utilities) | `CnpjUtils`, `CnpjFormatter`, `CnpjGenerator`, `CnpjValidator`, `CnpjFmt.cnpj_fmt`, `CnpjGen.cnpj_gen`, `CnpjVal.cnpj_val` | [docs](../cnpj-utilities/README.md) | +| [`cpf-utilities`](https://rubygems.org/gems/cpf-utilities) | `CpfUtils`, `CpfFormatter`, `CpfGenerator`, `CpfValidator`, `CpfFmt.cpf_fmt`, `CpfGen.cpf_gen`, `CpfVal.cpf_val` | [docs](packages/cpf-utilities/README.md) | +| [`cnpj-utilities`](https://rubygems.org/gems/cnpj-utilities) | `CnpjUtils`, `CnpjFormatter`, `CnpjGenerator`, `CnpjValidator`, `CnpjFmt.cnpj_fmt`, `CnpjGen.cnpj_gen`, `CnpjVal.cnpj_val` | [docs](packages/cnpj-utilities/README.md) | All of the above are pulled in as dependencies of **`br-utilities`**. Interactive demos: [CPF](https://cpf-utils.vercel.app/) and [CNPJ](https://cnpj-utils.vercel.app/). @@ -1252,7 +1252,7 @@ This project is licensed under the MIT License — see the [LICENSE](https://git ## Changelog -See [CHANGELOG](./CHANGELOG.md) for a list of changes and version history. +See [CHANGELOG](packages/br-utilities/CHANGELOG.md) for a list of changes and version history. --- diff --git a/README.pt.md b/README.pt.md index 4a35802..842df92 100644 --- a/README.pt.md +++ b/README.pt.md @@ -229,7 +229,7 @@ Para alterar uma única opção aninhada sem substituir o utils de domínio inte ### Operações de CPF -Os métodos de CPF são acessados via `BrUtils.cpf`, `utils.cpf`, `CpfUtils` ou os helpers `CpfFmt` / `CpfGen` / `CpfVal`. O CPF usa a API de [`cpf-utilities`](../cpf-utilities/README.pt.md). +Os métodos de CPF são acessados via `BrUtils.cpf`, `utils.cpf`, `CpfUtils` ou os helpers `CpfFmt` / `CpfGen` / `CpfVal`. O CPF usa a API de [`cpf-utilities`](packages/cpf-utilities/README.pt.md). #### Formatação (`#format` / `CpfFmt.cpf_fmt`) @@ -292,7 +292,7 @@ CpfVal.cpf_val('12345678909') # => true ### Operações de CNPJ -Os métodos de CNPJ são acessados via `BrUtils.cnpj`, `utils.cnpj`, `CnpjUtils` ou os helpers `CnpjFmt` / `CnpjGen` / `CnpjVal`. O CNPJ usa a API de [`cnpj-utilities`](../cnpj-utilities/README.pt.md). +Os métodos de CNPJ são acessados via `BrUtils.cnpj`, `utils.cnpj`, `CnpjUtils` ou os helpers `CnpjFmt` / `CnpjGen` / `CnpjVal`. O CNPJ usa a API de [`cnpj-utilities`](packages/cnpj-utilities/README.pt.md). #### Formatação (`#format` / `CnpjFmt.cnpj_fmt`) @@ -461,7 +461,7 @@ CnpjGen.cnpj_gen(type: 'numeric') # => ex.: "65453043000178" CnpjVal.cnpj_val('9JN7MGLJZXIO50') # => true ``` -Consulte [`cpf-utilities`](../cpf-utilities/README.pt.md) e [`cnpj-utilities`](../cnpj-utilities/README.pt.md) para detalhes completos de opções e erros. +Consulte [`cpf-utilities`](packages/cpf-utilities/README.pt.md) e [`cnpj-utilities`](packages/cnpj-utilities/README.pt.md) para detalhes completos de opções e erros. ### Misturando estilos @@ -642,7 +642,7 @@ end Os erros dos componentes mantêm os namespaces dos pacotes e se propagam inalterados pela fachada (e pelas APIs aninhadas / irmãs na raiz). Cada pacote também expõe um módulo marcador `*::Error` para rescue em toda a biblioteca. **Dados** inválidos de CPF/CNPJ em `#is_valid` retornam `false` (sem raise de domínio). Falha de comprimento na formatação **não** é lançada por `#format` — é entregue a **`on_fail`** como `CpfFmt::InvalidLengthError` ou `CnpjFmt::InvalidLengthError` (`on_fail` padrão retorna `''`). -`CpfUtils::*` / `CnpjUtils::*` de uso indevido também se propagam quando agregadores aninhados são construídos ou chamados via `BrUtils`. Para tabelas de opções e casos extremos, veja [`cpf-utilities`](../cpf-utilities/README.pt.md) e [`cnpj-utilities`](../cnpj-utilities/README.pt.md). +`CpfUtils::*` / `CnpjUtils::*` de uso indevido também se propagam quando agregadores aninhados são construídos ou chamados via `BrUtils`. Para tabelas de opções e casos extremos, veja [`cpf-utilities`](packages/cpf-utilities/README.pt.md) e [`cnpj-utilities`](packages/cnpj-utilities/README.pt.md). ##### Resumo @@ -1225,8 +1225,8 @@ rescue CnpjUtils::Error | Pacote | Principais recursos | README | |---------|----------------|--------| -| [`cpf-utilities`](https://rubygems.org/gems/cpf-utilities) | `CpfUtils`, `CpfFormatter`, `CpfGenerator`, `CpfValidator`, `CpfFmt.cpf_fmt`, `CpfGen.cpf_gen`, `CpfVal.cpf_val` | [docs](../cpf-utilities/README.pt.md) | -| [`cnpj-utilities`](https://rubygems.org/gems/cnpj-utilities) | `CnpjUtils`, `CnpjFormatter`, `CnpjGenerator`, `CnpjValidator`, `CnpjFmt.cnpj_fmt`, `CnpjGen.cnpj_gen`, `CnpjVal.cnpj_val` | [docs](../cnpj-utilities/README.pt.md) | +| [`cpf-utilities`](https://rubygems.org/gems/cpf-utilities) | `CpfUtils`, `CpfFormatter`, `CpfGenerator`, `CpfValidator`, `CpfFmt.cpf_fmt`, `CpfGen.cpf_gen`, `CpfVal.cpf_val` | [docs](packages/cpf-utilities/README.pt.md) | +| [`cnpj-utilities`](https://rubygems.org/gems/cnpj-utilities) | `CnpjUtils`, `CnpjFormatter`, `CnpjGenerator`, `CnpjValidator`, `CnpjFmt.cnpj_fmt`, `CnpjGen.cnpj_gen`, `CnpjVal.cnpj_val` | [docs](packages/cnpj-utilities/README.pt.md) | Todos os acima são puxados como dependências de **`br-utilities`**. Demos interativas: [CPF](https://cpf-utils.vercel.app/) e [CNPJ](https://cnpj-utils.vercel.app/). @@ -1245,7 +1245,7 @@ Este projeto está sob a licença MIT — veja o arquivo [LICENSE](https://githu ## Changelog -Veja o [CHANGELOG](./CHANGELOG.md) para alterações e histórico de versões. +Veja o [CHANGELOG](packages/br-utilities/CHANGELOG.md) para alterações e histórico de versões. --- From db50bec704fa3308dcd3e6dcfe997fa3d261c129 Mon Sep 17 00:00:00 2001 From: juliolmuller Date: Fri, 21 Aug 2026 11:43:21 -0300 Subject: [PATCH 12/14] docs(br-utils): update error classes in README files for CNPJ utilities Fix as per @coderabbitai review comment at https://github.com/LacusSolutions/br-utils-ruby/pull/30#discussion_r3825305317. Co-authored-by: CodeRabbit AI <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Cursor Agent --- README.md | 24 ++++++++++++------------ README.pt.md | 26 +++++++++++++------------- packages/br-utilities/README.md | 24 ++++++++++++------------ packages/br-utilities/README.pt.md | 26 +++++++++++++------------- 4 files changed, 50 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index 984b0f1..8863577 100644 --- a/README.md +++ b/README.md @@ -655,30 +655,30 @@ Component errors keep their package namespaces and propagate unchanged through t | Class | Inherits from | Category | Trigger condition | |-------|---------------|----------|-------------------| -| `CpfUtils::InvalidArgumentCombinationError` | `CpfUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CpfUtils::Error`) | API misuse | Both an options instance/`Hash` and any non-`nil` keyword on `CpfUtils#format` / `#generate` | -| `CpfUtils::TypeMismatchError` | `CpfUtils::TypeMismatchError < TypeError < StandardError` (+ `include CpfUtils::Error`) | API misuse | Non-`nil` nested `cpf` settings `Hash` contract violation on `CpfUtils.new` | +| `CnpjFmt::InvalidArgumentCombinationError` | `CnpjFmt::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjFmt::Error`) | API misuse | Both an `options` instance/`Hash` and any non-`nil` keyword on `CnpjFormatter` / `cnpj_fmt` | +| `CnpjFmt::TypeMismatchError` | `CnpjFmt::TypeMismatchError < TypeError < StandardError` (+ `include CnpjFmt::Error`) | API misuse | CNPJ input or formatter option has the wrong type (or `on_fail` return is not a `String`) | +| `CnpjGen::InvalidArgumentCombinationError` | `CnpjGen::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjGen::Error`) | API misuse | Both an `options` instance/`Hash` and any non-`nil` keyword on `CnpjGenerator` / `cnpj_gen` | +| `CnpjGen::TypeMismatchError` | `CnpjGen::TypeMismatchError < TypeError < StandardError` (+ `include CnpjGen::Error`) | API misuse | Generator option (`format` / `prefix` / `type`) has the wrong type | | `CnpjUtils::InvalidArgumentCombinationError` | `CnpjUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjUtils::Error`) | API misuse | Both settings/options and any non-`nil` keyword on `CnpjUtils` construct/`#format` / `#generate` / `#is_valid` | | `CnpjUtils::TypeMismatchError` | `CnpjUtils::TypeMismatchError < TypeError < StandardError` (+ `include CnpjUtils::Error`) | API misuse | Non-`nil` nested `cnpj` settings `Hash` contract violation on `CnpjUtils.new` | +| `CnpjVal::InvalidArgumentCombinationError` | `CnpjVal::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjVal::Error`) | API misuse | Both an `options` instance/`Hash` and any non-`nil` keyword on `CnpjValidator` / `cnpj_val` | +| `CnpjVal::TypeMismatchError` | `CnpjVal::TypeMismatchError < TypeError < StandardError` (+ `include CnpjVal::Error`) | API misuse | CNPJ input or validator option has the wrong type | | `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` | | `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`) | | `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` | | `CpfGen::TypeMismatchError` | `CpfGen::TypeMismatchError < TypeError < StandardError` (+ `include CpfGen::Error`) | API misuse | Generator option (`format` / `prefix`) has the wrong type | +| `CpfUtils::InvalidArgumentCombinationError` | `CpfUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CpfUtils::Error`) | API misuse | Both an options instance/`Hash` and any non-`nil` keyword on `CpfUtils#format` / `#generate` | +| `CpfUtils::TypeMismatchError` | `CpfUtils::TypeMismatchError < TypeError < StandardError` (+ `include CpfUtils::Error`) | API misuse | Non-`nil` nested `cpf` settings `Hash` contract violation on `CpfUtils.new` | | `CpfVal::TypeMismatchError` | `CpfVal::TypeMismatchError < TypeError < StandardError` (+ `include CpfVal::Error`) | API misuse | CPF input is not a `String` or `Array` of strings | -| `CnpjFmt::InvalidArgumentCombinationError` | `CnpjFmt::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjFmt::Error`) | API misuse | Both an `options` instance/`Hash` and any non-`nil` keyword on `CnpjFormatter` / `cnpj_fmt` | -| `CnpjFmt::TypeMismatchError` | `CnpjFmt::TypeMismatchError < TypeError < StandardError` (+ `include CnpjFmt::Error`) | API misuse | CNPJ input or formatter option has the wrong type (or `on_fail` return is not a `String`) | -| `CnpjGen::InvalidArgumentCombinationError` | `CnpjGen::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjGen::Error`) | API misuse | Both an `options` instance/`Hash` and any non-`nil` keyword on `CnpjGenerator` / `cnpj_gen` | -| `CnpjGen::TypeMismatchError` | `CnpjGen::TypeMismatchError < TypeError < StandardError` (+ `include CnpjGen::Error`) | API misuse | Generator option (`format` / `prefix` / `type`) has the wrong type | -| `CnpjVal::InvalidArgumentCombinationError` | `CnpjVal::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjVal::Error`) | API misuse | Both an `options` instance/`Hash` and any non-`nil` keyword on `CnpjValidator` / `cnpj_val` | -| `CnpjVal::TypeMismatchError` | `CnpjVal::TypeMismatchError < TypeError < StandardError` (+ `include CnpjVal::Error`) | API misuse | CNPJ input or validator option has the wrong type | -| `CpfFmt::InvalidLengthError` | `CpfFmt::InvalidLengthError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Domain error | Sanitized length ≠ 11 — **passed to `on_fail`**, not raised by `#format` | -| `CpfFmt::OutOfRangeError` | `CpfFmt::OutOfRangeError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Domain error | `hidden_start` / `hidden_end` outside `0`–`10` | -| `CpfFmt::ValidationError` | `CpfFmt::ValidationError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Domain error | `hidden_key` / `dot_key` / `dash_key` contains a disallowed character | -| `CpfGen::ValidationError` | `CpfGen::ValidationError < CpfGen::DomainError < RangeError < StandardError` (+ `include CpfGen::Error`) | Domain error | `prefix` is ineligible (zeroed base or 9 repeated digits) | | `CnpjFmt::InvalidLengthError` | `CnpjFmt::InvalidLengthError < CnpjFmt::DomainError < RangeError < StandardError` (+ `include CnpjFmt::Error`) | Domain error | Sanitized length ≠ 14 — **passed to `on_fail`**, not raised by `#format` | | `CnpjFmt::OutOfRangeError` | `CnpjFmt::OutOfRangeError < CnpjFmt::DomainError < RangeError < StandardError` (+ `include CnpjFmt::Error`) | Domain error | `hidden_start` / `hidden_end` outside `0`–`13` | | `CnpjFmt::ValidationError` | `CnpjFmt::ValidationError < CnpjFmt::DomainError < RangeError < StandardError` (+ `include CnpjFmt::Error`) | Domain error | `hidden_key` / `dot_key` / `slash_key` / `dash_key` contains a disallowed character | | `CnpjGen::ValidationError` | `CnpjGen::ValidationError < CnpjGen::DomainError < RangeError < StandardError` (+ `include CnpjGen::Error`) | Domain error | Ineligible `prefix`, or `type` not in `'alphabetic'` / `'alphanumeric'` / `'numeric'` | | `CnpjVal::ValidationError` | `CnpjVal::ValidationError < CnpjVal::DomainError < RangeError < StandardError` (+ `include CnpjVal::Error`) | Domain error | Validator `type` is not `'alphanumeric'` or `'numeric'` | +| `CpfFmt::InvalidLengthError` | `CpfFmt::InvalidLengthError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Domain error | Sanitized length ≠ 11 — **passed to `on_fail`**, not raised by `#format` | +| `CpfFmt::OutOfRangeError` | `CpfFmt::OutOfRangeError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Domain error | `hidden_start` / `hidden_end` outside `0`–`10` | +| `CpfFmt::ValidationError` | `CpfFmt::ValidationError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Domain error | `hidden_key` / `dot_key` / `dash_key` contains a disallowed character | +| `CpfGen::ValidationError` | `CpfGen::ValidationError < CpfGen::DomainError < RangeError < StandardError` (+ `include CpfGen::Error`) | Domain error | `prefix` is ineligible (zeroed base or 9 repeated digits) | ##### `CpfFmt::DomainError` diff --git a/README.pt.md b/README.pt.md index 842df92..daf21ee 100644 --- a/README.pt.md +++ b/README.pt.md @@ -648,30 +648,30 @@ Os erros dos componentes mantêm os namespaces dos pacotes e se propagam inalter | Classe | Herda de | Categoria | Condição de disparo | |-------|---------------|----------|-------------------| -| `CpfUtils::InvalidArgumentCombinationError` | `CpfUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CpfUtils::Error`) | Uso indevido da API | Instância/`Hash` de options e qualquer argumento nomeado não-`nil` em `CpfUtils#format` / `#generate` | -| `CpfUtils::TypeMismatchError` | `CpfUtils::TypeMismatchError < TypeError < StandardError` (+ `include CpfUtils::Error`) | Uso indevido da API | Violação de contrato do `Hash` de settings `cpf` aninhado em `CpfUtils.new` | +| `CnpjFmt::InvalidArgumentCombinationError` | `CnpjFmt::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjFmt::Error`) | Uso indevido da API | Instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` em `CnpjFormatter` / `cnpj_fmt` | +| `CnpjFmt::TypeMismatchError` | `CnpjFmt::TypeMismatchError < TypeError < StandardError` (+ `include CnpjFmt::Error`) | Uso indevido da API | Entrada de CNPJ ou opção do formatador tem o tipo errado (ou o retorno de `on_fail` não é `String`) | +| `CnpjGen::InvalidArgumentCombinationError` | `CnpjGen::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjGen::Error`) | Uso indevido da API | Instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` em `CnpjGenerator` / `cnpj_gen` | +| `CnpjGen::TypeMismatchError` | `CnpjGen::TypeMismatchError < TypeError < StandardError` (+ `include CnpjGen::Error`) | Uso indevido da API | Opção do gerador (`format` / `prefix` / `type`) tem o tipo errado | | `CnpjUtils::InvalidArgumentCombinationError` | `CnpjUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjUtils::Error`) | Uso indevido da API | Settings/options e qualquer argumento nomeado não-`nil` na construção de `CnpjUtils` / `#format` / `#generate` / `#is_valid` | | `CnpjUtils::TypeMismatchError` | `CnpjUtils::TypeMismatchError < TypeError < StandardError` (+ `include CnpjUtils::Error`) | Uso indevido da API | Violação de contrato do `Hash` de settings `cnpj` aninhado em `CnpjUtils.new` | +| `CnpjVal::InvalidArgumentCombinationError` | `CnpjVal::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjVal::Error`) | Uso indevido da API | Instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` em `CnpjValidator` / `cnpj_val` | +| `CnpjVal::TypeMismatchError` | `CnpjVal::TypeMismatchError < TypeError < StandardError` (+ `include CnpjVal::Error`) | Uso indevido da API | Entrada de CNPJ ou opção do validador tem o tipo errado | | `CpfFmt::InvalidArgumentCombinationError` | `CpfFmt::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CpfFmt::Error`) | Uso indevido da API | Instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` em `CpfFormatter` / `cpf_fmt` | | `CpfFmt::TypeMismatchError` | `CpfFmt::TypeMismatchError < TypeError < StandardError` (+ `include CpfFmt::Error`) | Uso indevido da API | Entrada de CPF ou opção do formatador tem o tipo errado (ou o retorno de `on_fail` não é `String`) | | `CpfGen::InvalidArgumentCombinationError` | `CpfGen::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CpfGen::Error`) | Uso indevido da API | Instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` em `CpfGenerator` / `cpf_gen` | | `CpfGen::TypeMismatchError` | `CpfGen::TypeMismatchError < TypeError < StandardError` (+ `include CpfGen::Error`) | Uso indevido da API | Opção do gerador (`format` / `prefix`) tem o tipo errado | +| `CpfUtils::InvalidArgumentCombinationError` | `CpfUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CpfUtils::Error`) | Uso indevido da API | Instância/`Hash` de options e qualquer argumento nomeado não-`nil` em `CpfUtils#format` / `#generate` | +| `CpfUtils::TypeMismatchError` | `CpfUtils::TypeMismatchError < TypeError < StandardError` (+ `include CpfUtils::Error`) | Uso indevido da API | Violação de contrato do `Hash` de settings `cpf` aninhado em `CpfUtils.new` | | `CpfVal::TypeMismatchError` | `CpfVal::TypeMismatchError < TypeError < StandardError` (+ `include CpfVal::Error`) | Uso indevido da API | Entrada de CPF não é `String` nem `Array` de strings | -| `CnpjFmt::InvalidArgumentCombinationError` | `CnpjFmt::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjFmt::Error`) | Uso indevido da API | Instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` em `CnpjFormatter` / `cnpj_fmt` | -| `CnpjFmt::TypeMismatchError` | `CnpjFmt::TypeMismatchError < TypeError < StandardError` (+ `include CnpjFmt::Error`) | Uso indevido da API | Entrada de CNPJ ou opção do formatador tem o tipo errado (ou o retorno de `on_fail` não é `String`) | -| `CnpjGen::InvalidArgumentCombinationError` | `CnpjGen::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjGen::Error`) | Uso indevido da API | Instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` em `CnpjGenerator` / `cnpj_gen` | -| `CnpjGen::TypeMismatchError` | `CnpjGen::TypeMismatchError < TypeError < StandardError` (+ `include CnpjGen::Error`) | Uso indevido da API | Opção do gerador (`format` / `prefix` / `type`) tem o tipo errado | -| `CnpjVal::InvalidArgumentCombinationError` | `CnpjVal::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjVal::Error`) | Uso indevido da API | Instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` em `CnpjValidator` / `cnpj_val` | -| `CnpjVal::TypeMismatchError` | `CnpjVal::TypeMismatchError < TypeError < StandardError` (+ `include CnpjVal::Error`) | Uso indevido da API | Entrada de CNPJ ou opção do validador tem o tipo errado | -| `CpfFmt::InvalidLengthError` | `CpfFmt::InvalidLengthError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Erro de domínio | Comprimento sanitizado ≠ 11 — **passado a `on_fail`**, não lançado por `#format` | -| `CpfFmt::OutOfRangeError` | `CpfFmt::OutOfRangeError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Erro de domínio | `hidden_start` / `hidden_end` fora de `0`–`10` | -| `CpfFmt::ValidationError` | `CpfFmt::ValidationError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Erro de domínio | `hidden_key` / `dot_key` / `dash_key` contém caractere proibido | -| `CpfGen::ValidationError` | `CpfGen::ValidationError < CpfGen::DomainError < RangeError < StandardError` (+ `include CpfGen::Error`) | Erro de domínio | `prefix` inelegível (base zerada ou 9 dígitos repetidos) | | `CnpjFmt::InvalidLengthError` | `CnpjFmt::InvalidLengthError < CnpjFmt::DomainError < RangeError < StandardError` (+ `include CnpjFmt::Error`) | Erro de domínio | Comprimento sanitizado ≠ 14 — **passado a `on_fail`**, não lançado por `#format` | | `CnpjFmt::OutOfRangeError` | `CnpjFmt::OutOfRangeError < CnpjFmt::DomainError < RangeError < StandardError` (+ `include CnpjFmt::Error`) | Erro de domínio | `hidden_start` / `hidden_end` fora de `0`–`13` | | `CnpjFmt::ValidationError` | `CnpjFmt::ValidationError < CnpjFmt::DomainError < RangeError < StandardError` (+ `include CnpjFmt::Error`) | Erro de domínio | `hidden_key` / `dot_key` / `slash_key` / `dash_key` contém caractere proibido | | `CnpjGen::ValidationError` | `CnpjGen::ValidationError < CnpjGen::DomainError < RangeError < StandardError` (+ `include CnpjGen::Error`) | Erro de domínio | `prefix` inelegível, ou `type` fora de `'alphabetic'` / `'alphanumeric'` / `'numeric'` | -| `CnpjVal::ValidationError` | `CnpjVal::ValidationError < CnpjVal::DomainError < RangeError < StandardError` (+ `include CnpjVal::Error`) | Erro de domínio | `type` do validador não é `'alphanumeric'` nem `'numeric'` | +| `CnpjVal::ValidationError` | `CnpjVal::ValidationError < CnpjVal::DomainError < RangeError < StandardError` (+ `include CnpjVal::Error`) | Erro de domínio | `type` do validador não é `'alphanumeric'` nem `'numeric'` | +| `CpfFmt::InvalidLengthError` | `CpfFmt::InvalidLengthError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Erro de domínio | Comprimento sanitizado ≠ 11 — **passado a `on_fail`**, não lançado por `#format` | +| `CpfFmt::OutOfRangeError` | `CpfFmt::OutOfRangeError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Erro de domínio | `hidden_start` / `hidden_end` fora de `0`–`10` | +| `CpfFmt::ValidationError` | `CpfFmt::ValidationError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Erro de domínio | `hidden_key` / `dot_key` / `dash_key` contém caractere proibido | +| `CpfGen::ValidationError` | `CpfGen::ValidationError < CpfGen::DomainError < RangeError < StandardError` (+ `include CpfGen::Error`) | Erro de domínio | `prefix` inelegível (base zerada ou 9 dígitos repetidos) | ##### `CpfFmt::DomainError` diff --git a/packages/br-utilities/README.md b/packages/br-utilities/README.md index 8b2f19c..bce814e 100644 --- a/packages/br-utilities/README.md +++ b/packages/br-utilities/README.md @@ -655,30 +655,30 @@ Component errors keep their package namespaces and propagate unchanged through t | Class | Inherits from | Category | Trigger condition | |-------|---------------|----------|-------------------| -| `CpfUtils::InvalidArgumentCombinationError` | `CpfUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CpfUtils::Error`) | API misuse | Both an options instance/`Hash` and any non-`nil` keyword on `CpfUtils#format` / `#generate` | -| `CpfUtils::TypeMismatchError` | `CpfUtils::TypeMismatchError < TypeError < StandardError` (+ `include CpfUtils::Error`) | API misuse | Non-`nil` nested `cpf` settings `Hash` contract violation on `CpfUtils.new` | +| `CnpjFmt::InvalidArgumentCombinationError` | `CnpjFmt::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjFmt::Error`) | API misuse | Both an `options` instance/`Hash` and any non-`nil` keyword on `CnpjFormatter` / `cnpj_fmt` | +| `CnpjFmt::TypeMismatchError` | `CnpjFmt::TypeMismatchError < TypeError < StandardError` (+ `include CnpjFmt::Error`) | API misuse | CNPJ input or formatter option has the wrong type (or `on_fail` return is not a `String`) | +| `CnpjGen::InvalidArgumentCombinationError` | `CnpjGen::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjGen::Error`) | API misuse | Both an `options` instance/`Hash` and any non-`nil` keyword on `CnpjGenerator` / `cnpj_gen` | +| `CnpjGen::TypeMismatchError` | `CnpjGen::TypeMismatchError < TypeError < StandardError` (+ `include CnpjGen::Error`) | API misuse | Generator option (`format` / `prefix` / `type`) has the wrong type | | `CnpjUtils::InvalidArgumentCombinationError` | `CnpjUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjUtils::Error`) | API misuse | Both settings/options and any non-`nil` keyword on `CnpjUtils` construct/`#format` / `#generate` / `#is_valid` | | `CnpjUtils::TypeMismatchError` | `CnpjUtils::TypeMismatchError < TypeError < StandardError` (+ `include CnpjUtils::Error`) | API misuse | Non-`nil` nested `cnpj` settings `Hash` contract violation on `CnpjUtils.new` | +| `CnpjVal::InvalidArgumentCombinationError` | `CnpjVal::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjVal::Error`) | API misuse | Both an `options` instance/`Hash` and any non-`nil` keyword on `CnpjValidator` / `cnpj_val` | +| `CnpjVal::TypeMismatchError` | `CnpjVal::TypeMismatchError < TypeError < StandardError` (+ `include CnpjVal::Error`) | API misuse | CNPJ input or validator option has the wrong type | | `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` | | `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`) | | `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` | | `CpfGen::TypeMismatchError` | `CpfGen::TypeMismatchError < TypeError < StandardError` (+ `include CpfGen::Error`) | API misuse | Generator option (`format` / `prefix`) has the wrong type | +| `CpfUtils::InvalidArgumentCombinationError` | `CpfUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CpfUtils::Error`) | API misuse | Both an options instance/`Hash` and any non-`nil` keyword on `CpfUtils#format` / `#generate` | +| `CpfUtils::TypeMismatchError` | `CpfUtils::TypeMismatchError < TypeError < StandardError` (+ `include CpfUtils::Error`) | API misuse | Non-`nil` nested `cpf` settings `Hash` contract violation on `CpfUtils.new` | | `CpfVal::TypeMismatchError` | `CpfVal::TypeMismatchError < TypeError < StandardError` (+ `include CpfVal::Error`) | API misuse | CPF input is not a `String` or `Array` of strings | -| `CnpjFmt::InvalidArgumentCombinationError` | `CnpjFmt::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjFmt::Error`) | API misuse | Both an `options` instance/`Hash` and any non-`nil` keyword on `CnpjFormatter` / `cnpj_fmt` | -| `CnpjFmt::TypeMismatchError` | `CnpjFmt::TypeMismatchError < TypeError < StandardError` (+ `include CnpjFmt::Error`) | API misuse | CNPJ input or formatter option has the wrong type (or `on_fail` return is not a `String`) | -| `CnpjGen::InvalidArgumentCombinationError` | `CnpjGen::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjGen::Error`) | API misuse | Both an `options` instance/`Hash` and any non-`nil` keyword on `CnpjGenerator` / `cnpj_gen` | -| `CnpjGen::TypeMismatchError` | `CnpjGen::TypeMismatchError < TypeError < StandardError` (+ `include CnpjGen::Error`) | API misuse | Generator option (`format` / `prefix` / `type`) has the wrong type | -| `CnpjVal::InvalidArgumentCombinationError` | `CnpjVal::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjVal::Error`) | API misuse | Both an `options` instance/`Hash` and any non-`nil` keyword on `CnpjValidator` / `cnpj_val` | -| `CnpjVal::TypeMismatchError` | `CnpjVal::TypeMismatchError < TypeError < StandardError` (+ `include CnpjVal::Error`) | API misuse | CNPJ input or validator option has the wrong type | -| `CpfFmt::InvalidLengthError` | `CpfFmt::InvalidLengthError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Domain error | Sanitized length ≠ 11 — **passed to `on_fail`**, not raised by `#format` | -| `CpfFmt::OutOfRangeError` | `CpfFmt::OutOfRangeError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Domain error | `hidden_start` / `hidden_end` outside `0`–`10` | -| `CpfFmt::ValidationError` | `CpfFmt::ValidationError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Domain error | `hidden_key` / `dot_key` / `dash_key` contains a disallowed character | -| `CpfGen::ValidationError` | `CpfGen::ValidationError < CpfGen::DomainError < RangeError < StandardError` (+ `include CpfGen::Error`) | Domain error | `prefix` is ineligible (zeroed base or 9 repeated digits) | | `CnpjFmt::InvalidLengthError` | `CnpjFmt::InvalidLengthError < CnpjFmt::DomainError < RangeError < StandardError` (+ `include CnpjFmt::Error`) | Domain error | Sanitized length ≠ 14 — **passed to `on_fail`**, not raised by `#format` | | `CnpjFmt::OutOfRangeError` | `CnpjFmt::OutOfRangeError < CnpjFmt::DomainError < RangeError < StandardError` (+ `include CnpjFmt::Error`) | Domain error | `hidden_start` / `hidden_end` outside `0`–`13` | | `CnpjFmt::ValidationError` | `CnpjFmt::ValidationError < CnpjFmt::DomainError < RangeError < StandardError` (+ `include CnpjFmt::Error`) | Domain error | `hidden_key` / `dot_key` / `slash_key` / `dash_key` contains a disallowed character | | `CnpjGen::ValidationError` | `CnpjGen::ValidationError < CnpjGen::DomainError < RangeError < StandardError` (+ `include CnpjGen::Error`) | Domain error | Ineligible `prefix`, or `type` not in `'alphabetic'` / `'alphanumeric'` / `'numeric'` | | `CnpjVal::ValidationError` | `CnpjVal::ValidationError < CnpjVal::DomainError < RangeError < StandardError` (+ `include CnpjVal::Error`) | Domain error | Validator `type` is not `'alphanumeric'` or `'numeric'` | +| `CpfFmt::InvalidLengthError` | `CpfFmt::InvalidLengthError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Domain error | Sanitized length ≠ 11 — **passed to `on_fail`**, not raised by `#format` | +| `CpfFmt::OutOfRangeError` | `CpfFmt::OutOfRangeError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Domain error | `hidden_start` / `hidden_end` outside `0`–`10` | +| `CpfFmt::ValidationError` | `CpfFmt::ValidationError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Domain error | `hidden_key` / `dot_key` / `dash_key` contains a disallowed character | +| `CpfGen::ValidationError` | `CpfGen::ValidationError < CpfGen::DomainError < RangeError < StandardError` (+ `include CpfGen::Error`) | Domain error | `prefix` is ineligible (zeroed base or 9 repeated digits) | ##### `CpfFmt::DomainError` diff --git a/packages/br-utilities/README.pt.md b/packages/br-utilities/README.pt.md index 4a35802..b013b8a 100644 --- a/packages/br-utilities/README.pt.md +++ b/packages/br-utilities/README.pt.md @@ -648,30 +648,30 @@ Os erros dos componentes mantêm os namespaces dos pacotes e se propagam inalter | Classe | Herda de | Categoria | Condição de disparo | |-------|---------------|----------|-------------------| -| `CpfUtils::InvalidArgumentCombinationError` | `CpfUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CpfUtils::Error`) | Uso indevido da API | Instância/`Hash` de options e qualquer argumento nomeado não-`nil` em `CpfUtils#format` / `#generate` | -| `CpfUtils::TypeMismatchError` | `CpfUtils::TypeMismatchError < TypeError < StandardError` (+ `include CpfUtils::Error`) | Uso indevido da API | Violação de contrato do `Hash` de settings `cpf` aninhado em `CpfUtils.new` | +| `CnpjFmt::InvalidArgumentCombinationError` | `CnpjFmt::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjFmt::Error`) | Uso indevido da API | Instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` em `CnpjFormatter` / `cnpj_fmt` | +| `CnpjFmt::TypeMismatchError` | `CnpjFmt::TypeMismatchError < TypeError < StandardError` (+ `include CnpjFmt::Error`) | Uso indevido da API | Entrada de CNPJ ou opção do formatador tem o tipo errado (ou o retorno de `on_fail` não é `String`) | +| `CnpjGen::InvalidArgumentCombinationError` | `CnpjGen::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjGen::Error`) | Uso indevido da API | Instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` em `CnpjGenerator` / `cnpj_gen` | +| `CnpjGen::TypeMismatchError` | `CnpjGen::TypeMismatchError < TypeError < StandardError` (+ `include CnpjGen::Error`) | Uso indevido da API | Opção do gerador (`format` / `prefix` / `type`) tem o tipo errado | | `CnpjUtils::InvalidArgumentCombinationError` | `CnpjUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjUtils::Error`) | Uso indevido da API | Settings/options e qualquer argumento nomeado não-`nil` na construção de `CnpjUtils` / `#format` / `#generate` / `#is_valid` | | `CnpjUtils::TypeMismatchError` | `CnpjUtils::TypeMismatchError < TypeError < StandardError` (+ `include CnpjUtils::Error`) | Uso indevido da API | Violação de contrato do `Hash` de settings `cnpj` aninhado em `CnpjUtils.new` | +| `CnpjVal::InvalidArgumentCombinationError` | `CnpjVal::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjVal::Error`) | Uso indevido da API | Instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` em `CnpjValidator` / `cnpj_val` | +| `CnpjVal::TypeMismatchError` | `CnpjVal::TypeMismatchError < TypeError < StandardError` (+ `include CnpjVal::Error`) | Uso indevido da API | Entrada de CNPJ ou opção do validador tem o tipo errado | | `CpfFmt::InvalidArgumentCombinationError` | `CpfFmt::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CpfFmt::Error`) | Uso indevido da API | Instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` em `CpfFormatter` / `cpf_fmt` | | `CpfFmt::TypeMismatchError` | `CpfFmt::TypeMismatchError < TypeError < StandardError` (+ `include CpfFmt::Error`) | Uso indevido da API | Entrada de CPF ou opção do formatador tem o tipo errado (ou o retorno de `on_fail` não é `String`) | | `CpfGen::InvalidArgumentCombinationError` | `CpfGen::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CpfGen::Error`) | Uso indevido da API | Instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` em `CpfGenerator` / `cpf_gen` | | `CpfGen::TypeMismatchError` | `CpfGen::TypeMismatchError < TypeError < StandardError` (+ `include CpfGen::Error`) | Uso indevido da API | Opção do gerador (`format` / `prefix`) tem o tipo errado | +| `CpfUtils::InvalidArgumentCombinationError` | `CpfUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CpfUtils::Error`) | Uso indevido da API | Instância/`Hash` de options e qualquer argumento nomeado não-`nil` em `CpfUtils#format` / `#generate` | +| `CpfUtils::TypeMismatchError` | `CpfUtils::TypeMismatchError < TypeError < StandardError` (+ `include CpfUtils::Error`) | Uso indevido da API | Violação de contrato do `Hash` de settings `cpf` aninhado em `CpfUtils.new` | | `CpfVal::TypeMismatchError` | `CpfVal::TypeMismatchError < TypeError < StandardError` (+ `include CpfVal::Error`) | Uso indevido da API | Entrada de CPF não é `String` nem `Array` de strings | -| `CnpjFmt::InvalidArgumentCombinationError` | `CnpjFmt::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjFmt::Error`) | Uso indevido da API | Instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` em `CnpjFormatter` / `cnpj_fmt` | -| `CnpjFmt::TypeMismatchError` | `CnpjFmt::TypeMismatchError < TypeError < StandardError` (+ `include CnpjFmt::Error`) | Uso indevido da API | Entrada de CNPJ ou opção do formatador tem o tipo errado (ou o retorno de `on_fail` não é `String`) | -| `CnpjGen::InvalidArgumentCombinationError` | `CnpjGen::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjGen::Error`) | Uso indevido da API | Instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` em `CnpjGenerator` / `cnpj_gen` | -| `CnpjGen::TypeMismatchError` | `CnpjGen::TypeMismatchError < TypeError < StandardError` (+ `include CnpjGen::Error`) | Uso indevido da API | Opção do gerador (`format` / `prefix` / `type`) tem o tipo errado | -| `CnpjVal::InvalidArgumentCombinationError` | `CnpjVal::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjVal::Error`) | Uso indevido da API | Instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` em `CnpjValidator` / `cnpj_val` | -| `CnpjVal::TypeMismatchError` | `CnpjVal::TypeMismatchError < TypeError < StandardError` (+ `include CnpjVal::Error`) | Uso indevido da API | Entrada de CNPJ ou opção do validador tem o tipo errado | -| `CpfFmt::InvalidLengthError` | `CpfFmt::InvalidLengthError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Erro de domínio | Comprimento sanitizado ≠ 11 — **passado a `on_fail`**, não lançado por `#format` | -| `CpfFmt::OutOfRangeError` | `CpfFmt::OutOfRangeError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Erro de domínio | `hidden_start` / `hidden_end` fora de `0`–`10` | -| `CpfFmt::ValidationError` | `CpfFmt::ValidationError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Erro de domínio | `hidden_key` / `dot_key` / `dash_key` contém caractere proibido | -| `CpfGen::ValidationError` | `CpfGen::ValidationError < CpfGen::DomainError < RangeError < StandardError` (+ `include CpfGen::Error`) | Erro de domínio | `prefix` inelegível (base zerada ou 9 dígitos repetidos) | | `CnpjFmt::InvalidLengthError` | `CnpjFmt::InvalidLengthError < CnpjFmt::DomainError < RangeError < StandardError` (+ `include CnpjFmt::Error`) | Erro de domínio | Comprimento sanitizado ≠ 14 — **passado a `on_fail`**, não lançado por `#format` | | `CnpjFmt::OutOfRangeError` | `CnpjFmt::OutOfRangeError < CnpjFmt::DomainError < RangeError < StandardError` (+ `include CnpjFmt::Error`) | Erro de domínio | `hidden_start` / `hidden_end` fora de `0`–`13` | | `CnpjFmt::ValidationError` | `CnpjFmt::ValidationError < CnpjFmt::DomainError < RangeError < StandardError` (+ `include CnpjFmt::Error`) | Erro de domínio | `hidden_key` / `dot_key` / `slash_key` / `dash_key` contém caractere proibido | | `CnpjGen::ValidationError` | `CnpjGen::ValidationError < CnpjGen::DomainError < RangeError < StandardError` (+ `include CnpjGen::Error`) | Erro de domínio | `prefix` inelegível, ou `type` fora de `'alphabetic'` / `'alphanumeric'` / `'numeric'` | -| `CnpjVal::ValidationError` | `CnpjVal::ValidationError < CnpjVal::DomainError < RangeError < StandardError` (+ `include CnpjVal::Error`) | Erro de domínio | `type` do validador não é `'alphanumeric'` nem `'numeric'` | +| `CnpjVal::ValidationError` | `CnpjVal::ValidationError < CnpjVal::DomainError < RangeError < StandardError` (+ `include CnpjVal::Error`) | Erro de domínio | `type` do validador não é `'alphanumeric'` nem `'numeric'` | +| `CpfFmt::InvalidLengthError` | `CpfFmt::InvalidLengthError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Erro de domínio | Comprimento sanitizado ≠ 11 — **passado a `on_fail`**, não lançado por `#format` | +| `CpfFmt::OutOfRangeError` | `CpfFmt::OutOfRangeError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Erro de domínio | `hidden_start` / `hidden_end` fora de `0`–`10` | +| `CpfFmt::ValidationError` | `CpfFmt::ValidationError < CpfFmt::DomainError < RangeError < StandardError` (+ `include CpfFmt::Error`) | Erro de domínio | `hidden_key` / `dot_key` / `dash_key` contém caractere proibido | +| `CpfGen::ValidationError` | `CpfGen::ValidationError < CpfGen::DomainError < RangeError < StandardError` (+ `include CpfGen::Error`) | Erro de domínio | `prefix` inelegível (base zerada ou 9 dígitos repetidos) | ##### `CpfFmt::DomainError` From 23ef0b7d688c061b2fb72e37b8570a1bb285f8bc Mon Sep 17 00:00:00 2001 From: juliolmuller Date: Fri, 21 Aug 2026 11:48:25 -0300 Subject: [PATCH 13/14] docs(br-utils): enhance error class documentation in README files Fix as per @coderabbitai review comment at https://github.com/LacusSolutions/br-utils-ruby/pull/30#discussion_r3825305320. Co-authored-by: CodeRabbit AI <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Cursor Agent --- README.md | 76 +++++++++++++++++++++++------- README.pt.md | 76 +++++++++++++++++++++++------- packages/br-utilities/README.md | 76 +++++++++++++++++++++++------- packages/br-utilities/README.pt.md | 76 +++++++++++++++++++++++------- 4 files changed, 240 insertions(+), 64 deletions(-) diff --git a/README.md b/README.md index 8863577..37c5412 100644 --- a/README.md +++ b/README.md @@ -1184,11 +1184,33 @@ rescue CnpjVal::DomainError # RangeError-rooted domain failures from cnpj-val ``` -##### `CpfUtils::TypeMismatchError` / `CpfUtils::InvalidArgumentCombinationError` +##### `CpfUtils::TypeMismatchError` -- **Inheritance:** `CpfUtils::TypeMismatchError < TypeError` and `CpfUtils::InvalidArgumentCombinationError < ArgumentError` (both include `CpfUtils::Error`). -- **Category:** API misuse on the nested CPF aggregator. -- **When it is raised:** Raised when nested `cpf` settings to `CpfUtils.new` are a non-`Hash`, or when `CpfUtils#format` / `#generate` mix an options `Hash` with keywords. +- **Inheritance:** `CpfUtils::TypeMismatchError < TypeError < StandardError` (includes `CpfUtils::Error`) +- **Category:** API misuse — the caller passed a value of the wrong type. +- **When it is raised:** Raised when `CpfUtils.new` receives a non-`nil` `settings` argument that is not a `Hash`. +- **Example:** + +```ruby +CpfUtils.new('not-a-hash') # raises CpfUtils::TypeMismatchError +CpfUtils.new(false) # raises CpfUtils::TypeMismatchError (false is non-nil) +``` + +- **How to rescue it:** + +```ruby +rescue CpfUtils::TypeMismatchError + # CPF aggregator type-contract violation (not BrUtils::Error) + +rescue TypeError + # native type errors, including CpfUtils::TypeMismatchError +``` + +##### `CpfUtils::InvalidArgumentCombinationError` + +- **Inheritance:** `CpfUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (includes `CpfUtils::Error`) +- **Category:** API misuse — the caller mixed mutually exclusive argument patterns. +- **When it is raised:** Raised when `CpfUtils.new` receives both a non-`nil` settings `Hash` and any non-`nil` keyword, or when `#format` / `#generate` mix a non-`nil` options `Hash`/`*Options` with any non-`nil` keyword. `#is_valid` has no options path and does not raise this error. - **Example:** ```ruby @@ -1199,18 +1221,40 @@ BrUtils.new.cpf.format({ hidden: true }, dash_key: '|') - **How to rescue it:** ```ruby -rescue CpfUtils::TypeMismatchError, CpfUtils::InvalidArgumentCombinationError - # CPF aggregator misuse (not BrUtils::Error) +rescue CpfUtils::InvalidArgumentCombinationError + # CPF aggregator invalid signature combination (not BrUtils::Error) + +rescue ArgumentError + # native argument errors, including CpfUtils::InvalidArgumentCombinationError +``` + +##### `CnpjUtils::TypeMismatchError` + +- **Inheritance:** `CnpjUtils::TypeMismatchError < TypeError < StandardError` (includes `CnpjUtils::Error`) +- **Category:** API misuse — the caller passed a value of the wrong type. +- **When it is raised:** Raised when `CnpjUtils.new` receives a non-`nil` `settings` argument that is not a `Hash`. +- **Example:** + +```ruby +CnpjUtils.new('not-a-hash') # raises CnpjUtils::TypeMismatchError +CnpjUtils.new(false) # raises CnpjUtils::TypeMismatchError (false is non-nil) +``` + +- **How to rescue it:** + +```ruby +rescue CnpjUtils::TypeMismatchError + # CNPJ aggregator type-contract violation (not BrUtils::Error) -rescue CpfUtils::Error - # every custom error that includes CpfUtils::Error +rescue TypeError + # native type errors, including CnpjUtils::TypeMismatchError ``` -##### `CnpjUtils::TypeMismatchError` / `CnpjUtils::InvalidArgumentCombinationError` +##### `CnpjUtils::InvalidArgumentCombinationError` -- **Inheritance:** `CnpjUtils::TypeMismatchError < TypeError` and `CnpjUtils::InvalidArgumentCombinationError < ArgumentError` (both include `CnpjUtils::Error`). -- **Category:** API misuse on the nested CNPJ aggregator. -- **When it is raised:** Raised when nested `cnpj` settings to `CnpjUtils.new` are a non-`Hash`, or when `CnpjUtils#format` / `#generate` / `#is_valid` mix settings/options with keywords. +- **Inheritance:** `CnpjUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (includes `CnpjUtils::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-`nil` settings/options `Hash` (or options instance) and any non-`nil` keyword at the same time. - **Example:** ```ruby @@ -1221,11 +1265,11 @@ BrUtils.new.cnpj.format({ hidden: true }, slash_key: '|') - **How to rescue it:** ```ruby -rescue CnpjUtils::TypeMismatchError, CnpjUtils::InvalidArgumentCombinationError - # CNPJ aggregator misuse (not BrUtils::Error) +rescue CnpjUtils::InvalidArgumentCombinationError + # CNPJ aggregator invalid signature combination (not BrUtils::Error) -rescue CnpjUtils::Error - # every custom error that includes CnpjUtils::Error +rescue ArgumentError + # native argument errors, including CnpjUtils::InvalidArgumentCombinationError ``` ### Bundled packages diff --git a/README.pt.md b/README.pt.md index daf21ee..dcd0820 100644 --- a/README.pt.md +++ b/README.pt.md @@ -1177,11 +1177,33 @@ rescue CnpjVal::DomainError # falhas de domínio com raiz em RangeError de cnpj-val ``` -##### `CpfUtils::TypeMismatchError` / `CpfUtils::InvalidArgumentCombinationError` +##### `CpfUtils::TypeMismatchError` -- **Herança:** `CpfUtils::TypeMismatchError < TypeError` and `CpfUtils::InvalidArgumentCombinationError < ArgumentError` (ambos incluem `CpfUtils::Error`). -- **Categoria:** Uso indevido da API on the nested CPF aggregator. -- **Quando é lançado:** Lançado quando settings `cpf` aninhados em `CpfUtils.new` não são um `Hash`, ou quando `CpfUtils#format` / `#generate` misturam um `Hash` de options com argumentos nomeados. +- **Herança:** `CpfUtils::TypeMismatchError < TypeError < StandardError` (inclui `CpfUtils::Error`) +- **Categoria:** Uso indevido da API — o caller passou um valor do tipo errado. +- **Quando é lançado:** Quando `CpfUtils.new` recebe um argumento `settings` não-`nil` que não é um `Hash`. +- **Exemplo:** + +```ruby +CpfUtils.new('not-a-hash') # lança CpfUtils::TypeMismatchError +CpfUtils.new(false) # lança CpfUtils::TypeMismatchError (false é não-nil) +``` + +- **Como resgatar:** + +```ruby +rescue CpfUtils::TypeMismatchError + # violação de contrato de tipo do agregador de CPF (não BrUtils::Error) + +rescue TypeError + # erros nativos de tipo, incluindo CpfUtils::TypeMismatchError +``` + +##### `CpfUtils::InvalidArgumentCombinationError` + +- **Herança:** `CpfUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (inclui `CpfUtils::Error`) +- **Categoria:** Uso indevido da API — o caller misturou padrões de argumentos mutuamente exclusivos. +- **Quando é lançado:** Quando `CpfUtils.new` recebe um `Hash` de settings não-`nil` e qualquer argumento nomeado não-`nil`, ou quando `#format` / `#generate` misturam um `Hash`/`*Options` de options não-`nil` com qualquer argumento nomeado não-`nil`. `#is_valid` não tem caminho de options e não lança este erro. - **Exemplo:** ```ruby @@ -1192,18 +1214,40 @@ BrUtils.new.cpf.format({ hidden: true }, dash_key: '|') - **Como resgatar:** ```ruby -rescue CpfUtils::TypeMismatchError, CpfUtils::InvalidArgumentCombinationError - # uso indevido do agregador de CPF (não BrUtils::Error) +rescue CpfUtils::InvalidArgumentCombinationError + # combinação inválida de assinatura do agregador de CPF (não BrUtils::Error) + +rescue ArgumentError + # erros nativos de argumento, incluindo CpfUtils::InvalidArgumentCombinationError +``` + +##### `CnpjUtils::TypeMismatchError` + +- **Herança:** `CnpjUtils::TypeMismatchError < TypeError < StandardError` (inclui `CnpjUtils::Error`) +- **Categoria:** Uso indevido da API — o caller passou um valor do tipo errado. +- **Quando é lançado:** Quando `CnpjUtils.new` recebe um argumento `settings` não-`nil` que não é um `Hash`. +- **Exemplo:** + +```ruby +CnpjUtils.new('not-a-hash') # lança CnpjUtils::TypeMismatchError +CnpjUtils.new(false) # lança CnpjUtils::TypeMismatchError (false é não-nil) +``` + +- **Como resgatar:** + +```ruby +rescue CnpjUtils::TypeMismatchError + # violação de contrato de tipo do agregador de CNPJ (não BrUtils::Error) -rescue CpfUtils::Error - # todo erro customizado que inclui CpfUtils::Error +rescue TypeError + # erros nativos de tipo, incluindo CnpjUtils::TypeMismatchError ``` -##### `CnpjUtils::TypeMismatchError` / `CnpjUtils::InvalidArgumentCombinationError` +##### `CnpjUtils::InvalidArgumentCombinationError` -- **Herança:** `CnpjUtils::TypeMismatchError < TypeError` and `CnpjUtils::InvalidArgumentCombinationError < ArgumentError` (ambos incluem `CnpjUtils::Error`). -- **Categoria:** Uso indevido da API on the nested CNPJ aggregator. -- **Quando é lançado:** Lançado quando settings `cnpj` aninhados em `CnpjUtils.new` não são um `Hash`, ou quando `CnpjUtils#format` / `#generate` / `#is_valid` misturam settings/options com argumentos nomeados. +- **Herança:** `CnpjUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (inclui `CnpjUtils::Error`) +- **Categoria:** Uso indevido da API — o caller misturou padrões de argumentos mutuamente exclusivos. +- **Quando é lançado:** Quando `CnpjUtils.new`, `#format`, `#generate`, `#is_valid` ou os helpers de classe recebem um `Hash`/instância de settings/options não-`nil` e qualquer argumento nomeado não-`nil` ao mesmo tempo. - **Exemplo:** ```ruby @@ -1214,11 +1258,11 @@ BrUtils.new.cnpj.format({ hidden: true }, slash_key: '|') - **Como resgatar:** ```ruby -rescue CnpjUtils::TypeMismatchError, CnpjUtils::InvalidArgumentCombinationError - # uso indevido do agregador de CNPJ (não BrUtils::Error) +rescue CnpjUtils::InvalidArgumentCombinationError + # combinação inválida de assinatura do agregador de CNPJ (não BrUtils::Error) -rescue CnpjUtils::Error - # todo erro customizado que inclui CnpjUtils::Error +rescue ArgumentError + # erros nativos de argumento, incluindo CnpjUtils::InvalidArgumentCombinationError ``` ### Pacotes incluídos diff --git a/packages/br-utilities/README.md b/packages/br-utilities/README.md index bce814e..c3be438 100644 --- a/packages/br-utilities/README.md +++ b/packages/br-utilities/README.md @@ -1184,11 +1184,33 @@ rescue CnpjVal::DomainError # RangeError-rooted domain failures from cnpj-val ``` -##### `CpfUtils::TypeMismatchError` / `CpfUtils::InvalidArgumentCombinationError` +##### `CpfUtils::TypeMismatchError` -- **Inheritance:** `CpfUtils::TypeMismatchError < TypeError` and `CpfUtils::InvalidArgumentCombinationError < ArgumentError` (both include `CpfUtils::Error`). -- **Category:** API misuse on the nested CPF aggregator. -- **When it is raised:** Raised when nested `cpf` settings to `CpfUtils.new` are a non-`Hash`, or when `CpfUtils#format` / `#generate` mix an options `Hash` with keywords. +- **Inheritance:** `CpfUtils::TypeMismatchError < TypeError < StandardError` (includes `CpfUtils::Error`) +- **Category:** API misuse — the caller passed a value of the wrong type. +- **When it is raised:** Raised when `CpfUtils.new` receives a non-`nil` `settings` argument that is not a `Hash`. +- **Example:** + +```ruby +CpfUtils.new('not-a-hash') # raises CpfUtils::TypeMismatchError +CpfUtils.new(false) # raises CpfUtils::TypeMismatchError (false is non-nil) +``` + +- **How to rescue it:** + +```ruby +rescue CpfUtils::TypeMismatchError + # CPF aggregator type-contract violation (not BrUtils::Error) + +rescue TypeError + # native type errors, including CpfUtils::TypeMismatchError +``` + +##### `CpfUtils::InvalidArgumentCombinationError` + +- **Inheritance:** `CpfUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (includes `CpfUtils::Error`) +- **Category:** API misuse — the caller mixed mutually exclusive argument patterns. +- **When it is raised:** Raised when `CpfUtils.new` receives both a non-`nil` settings `Hash` and any non-`nil` keyword, or when `#format` / `#generate` mix a non-`nil` options `Hash`/`*Options` with any non-`nil` keyword. `#is_valid` has no options path and does not raise this error. - **Example:** ```ruby @@ -1199,18 +1221,40 @@ BrUtils.new.cpf.format({ hidden: true }, dash_key: '|') - **How to rescue it:** ```ruby -rescue CpfUtils::TypeMismatchError, CpfUtils::InvalidArgumentCombinationError - # CPF aggregator misuse (not BrUtils::Error) +rescue CpfUtils::InvalidArgumentCombinationError + # CPF aggregator invalid signature combination (not BrUtils::Error) + +rescue ArgumentError + # native argument errors, including CpfUtils::InvalidArgumentCombinationError +``` + +##### `CnpjUtils::TypeMismatchError` + +- **Inheritance:** `CnpjUtils::TypeMismatchError < TypeError < StandardError` (includes `CnpjUtils::Error`) +- **Category:** API misuse — the caller passed a value of the wrong type. +- **When it is raised:** Raised when `CnpjUtils.new` receives a non-`nil` `settings` argument that is not a `Hash`. +- **Example:** + +```ruby +CnpjUtils.new('not-a-hash') # raises CnpjUtils::TypeMismatchError +CnpjUtils.new(false) # raises CnpjUtils::TypeMismatchError (false is non-nil) +``` + +- **How to rescue it:** + +```ruby +rescue CnpjUtils::TypeMismatchError + # CNPJ aggregator type-contract violation (not BrUtils::Error) -rescue CpfUtils::Error - # every custom error that includes CpfUtils::Error +rescue TypeError + # native type errors, including CnpjUtils::TypeMismatchError ``` -##### `CnpjUtils::TypeMismatchError` / `CnpjUtils::InvalidArgumentCombinationError` +##### `CnpjUtils::InvalidArgumentCombinationError` -- **Inheritance:** `CnpjUtils::TypeMismatchError < TypeError` and `CnpjUtils::InvalidArgumentCombinationError < ArgumentError` (both include `CnpjUtils::Error`). -- **Category:** API misuse on the nested CNPJ aggregator. -- **When it is raised:** Raised when nested `cnpj` settings to `CnpjUtils.new` are a non-`Hash`, or when `CnpjUtils#format` / `#generate` / `#is_valid` mix settings/options with keywords. +- **Inheritance:** `CnpjUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (includes `CnpjUtils::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-`nil` settings/options `Hash` (or options instance) and any non-`nil` keyword at the same time. - **Example:** ```ruby @@ -1221,11 +1265,11 @@ BrUtils.new.cnpj.format({ hidden: true }, slash_key: '|') - **How to rescue it:** ```ruby -rescue CnpjUtils::TypeMismatchError, CnpjUtils::InvalidArgumentCombinationError - # CNPJ aggregator misuse (not BrUtils::Error) +rescue CnpjUtils::InvalidArgumentCombinationError + # CNPJ aggregator invalid signature combination (not BrUtils::Error) -rescue CnpjUtils::Error - # every custom error that includes CnpjUtils::Error +rescue ArgumentError + # native argument errors, including CnpjUtils::InvalidArgumentCombinationError ``` ### Bundled packages diff --git a/packages/br-utilities/README.pt.md b/packages/br-utilities/README.pt.md index b013b8a..1becc53 100644 --- a/packages/br-utilities/README.pt.md +++ b/packages/br-utilities/README.pt.md @@ -1177,11 +1177,33 @@ rescue CnpjVal::DomainError # falhas de domínio com raiz em RangeError de cnpj-val ``` -##### `CpfUtils::TypeMismatchError` / `CpfUtils::InvalidArgumentCombinationError` +##### `CpfUtils::TypeMismatchError` -- **Herança:** `CpfUtils::TypeMismatchError < TypeError` and `CpfUtils::InvalidArgumentCombinationError < ArgumentError` (ambos incluem `CpfUtils::Error`). -- **Categoria:** Uso indevido da API on the nested CPF aggregator. -- **Quando é lançado:** Lançado quando settings `cpf` aninhados em `CpfUtils.new` não são um `Hash`, ou quando `CpfUtils#format` / `#generate` misturam um `Hash` de options com argumentos nomeados. +- **Herança:** `CpfUtils::TypeMismatchError < TypeError < StandardError` (inclui `CpfUtils::Error`) +- **Categoria:** Uso indevido da API — o caller passou um valor do tipo errado. +- **Quando é lançado:** Quando `CpfUtils.new` recebe um argumento `settings` não-`nil` que não é um `Hash`. +- **Exemplo:** + +```ruby +CpfUtils.new('not-a-hash') # lança CpfUtils::TypeMismatchError +CpfUtils.new(false) # lança CpfUtils::TypeMismatchError (false é não-nil) +``` + +- **Como resgatar:** + +```ruby +rescue CpfUtils::TypeMismatchError + # violação de contrato de tipo do agregador de CPF (não BrUtils::Error) + +rescue TypeError + # erros nativos de tipo, incluindo CpfUtils::TypeMismatchError +``` + +##### `CpfUtils::InvalidArgumentCombinationError` + +- **Herança:** `CpfUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (inclui `CpfUtils::Error`) +- **Categoria:** Uso indevido da API — o caller misturou padrões de argumentos mutuamente exclusivos. +- **Quando é lançado:** Quando `CpfUtils.new` recebe um `Hash` de settings não-`nil` e qualquer argumento nomeado não-`nil`, ou quando `#format` / `#generate` misturam um `Hash`/`*Options` de options não-`nil` com qualquer argumento nomeado não-`nil`. `#is_valid` não tem caminho de options e não lança este erro. - **Exemplo:** ```ruby @@ -1192,18 +1214,40 @@ BrUtils.new.cpf.format({ hidden: true }, dash_key: '|') - **Como resgatar:** ```ruby -rescue CpfUtils::TypeMismatchError, CpfUtils::InvalidArgumentCombinationError - # uso indevido do agregador de CPF (não BrUtils::Error) +rescue CpfUtils::InvalidArgumentCombinationError + # combinação inválida de assinatura do agregador de CPF (não BrUtils::Error) + +rescue ArgumentError + # erros nativos de argumento, incluindo CpfUtils::InvalidArgumentCombinationError +``` + +##### `CnpjUtils::TypeMismatchError` + +- **Herança:** `CnpjUtils::TypeMismatchError < TypeError < StandardError` (inclui `CnpjUtils::Error`) +- **Categoria:** Uso indevido da API — o caller passou um valor do tipo errado. +- **Quando é lançado:** Quando `CnpjUtils.new` recebe um argumento `settings` não-`nil` que não é um `Hash`. +- **Exemplo:** + +```ruby +CnpjUtils.new('not-a-hash') # lança CnpjUtils::TypeMismatchError +CnpjUtils.new(false) # lança CnpjUtils::TypeMismatchError (false é não-nil) +``` + +- **Como resgatar:** + +```ruby +rescue CnpjUtils::TypeMismatchError + # violação de contrato de tipo do agregador de CNPJ (não BrUtils::Error) -rescue CpfUtils::Error - # todo erro customizado que inclui CpfUtils::Error +rescue TypeError + # erros nativos de tipo, incluindo CnpjUtils::TypeMismatchError ``` -##### `CnpjUtils::TypeMismatchError` / `CnpjUtils::InvalidArgumentCombinationError` +##### `CnpjUtils::InvalidArgumentCombinationError` -- **Herança:** `CnpjUtils::TypeMismatchError < TypeError` and `CnpjUtils::InvalidArgumentCombinationError < ArgumentError` (ambos incluem `CnpjUtils::Error`). -- **Categoria:** Uso indevido da API on the nested CNPJ aggregator. -- **Quando é lançado:** Lançado quando settings `cnpj` aninhados em `CnpjUtils.new` não são um `Hash`, ou quando `CnpjUtils#format` / `#generate` / `#is_valid` misturam settings/options com argumentos nomeados. +- **Herança:** `CnpjUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (inclui `CnpjUtils::Error`) +- **Categoria:** Uso indevido da API — o caller misturou padrões de argumentos mutuamente exclusivos. +- **Quando é lançado:** Quando `CnpjUtils.new`, `#format`, `#generate`, `#is_valid` ou os helpers de classe recebem um `Hash`/instância de settings/options não-`nil` e qualquer argumento nomeado não-`nil` ao mesmo tempo. - **Exemplo:** ```ruby @@ -1214,11 +1258,11 @@ BrUtils.new.cnpj.format({ hidden: true }, slash_key: '|') - **Como resgatar:** ```ruby -rescue CnpjUtils::TypeMismatchError, CnpjUtils::InvalidArgumentCombinationError - # uso indevido do agregador de CNPJ (não BrUtils::Error) +rescue CnpjUtils::InvalidArgumentCombinationError + # combinação inválida de assinatura do agregador de CNPJ (não BrUtils::Error) -rescue CnpjUtils::Error - # todo erro customizado que inclui CnpjUtils::Error +rescue ArgumentError + # erros nativos de argumento, incluindo CnpjUtils::InvalidArgumentCombinationError ``` ### Pacotes incluídos From d171757d8c659339a9d588eb87e63d0300fa58b0 Mon Sep 17 00:00:00 2001 From: juliolmuller Date: Fri, 21 Aug 2026 12:33:50 -0300 Subject: [PATCH 14/14] docs(br-utils): refine error class descriptions in README files Adjustment as per @coderabbitai review comment at https://github.com/LacusSolutions/br-utils-ruby/pull/30#discussion_r3831237767. Co-authored-by: CodeRabbit AI <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Cursor Agent --- README.md | 8 ++++---- README.pt.md | 8 ++++---- packages/br-utilities/README.md | 8 ++++---- packages/br-utilities/README.pt.md | 8 ++++---- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 37c5412..1b4150a 100644 --- a/README.md +++ b/README.md @@ -659,16 +659,16 @@ Component errors keep their package namespaces and propagate unchanged through t | `CnpjFmt::TypeMismatchError` | `CnpjFmt::TypeMismatchError < TypeError < StandardError` (+ `include CnpjFmt::Error`) | API misuse | CNPJ input or formatter option has the wrong type (or `on_fail` return is not a `String`) | | `CnpjGen::InvalidArgumentCombinationError` | `CnpjGen::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjGen::Error`) | API misuse | Both an `options` instance/`Hash` and any non-`nil` keyword on `CnpjGenerator` / `cnpj_gen` | | `CnpjGen::TypeMismatchError` | `CnpjGen::TypeMismatchError < TypeError < StandardError` (+ `include CnpjGen::Error`) | API misuse | Generator option (`format` / `prefix` / `type`) has the wrong type | -| `CnpjUtils::InvalidArgumentCombinationError` | `CnpjUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjUtils::Error`) | API misuse | Both settings/options and any non-`nil` keyword on `CnpjUtils` construct/`#format` / `#generate` / `#is_valid` | -| `CnpjUtils::TypeMismatchError` | `CnpjUtils::TypeMismatchError < TypeError < StandardError` (+ `include CnpjUtils::Error`) | API misuse | Non-`nil` nested `cnpj` settings `Hash` contract violation on `CnpjUtils.new` | +| `CnpjUtils::InvalidArgumentCombinationError` | `CnpjUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjUtils::Error`) | API misuse | Constructor/`#format`/`#generate`/`#is_valid`/class helpers: non-`nil` settings/options `Hash` (or options instance) with any non-`nil` keyword | +| `CnpjUtils::TypeMismatchError` | `CnpjUtils::TypeMismatchError < TypeError < StandardError` (+ `include CnpjUtils::Error`) | API misuse | Non-`nil` `settings` argument to `CnpjUtils.new` is not a `Hash` | | `CnpjVal::InvalidArgumentCombinationError` | `CnpjVal::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjVal::Error`) | API misuse | Both an `options` instance/`Hash` and any non-`nil` keyword on `CnpjValidator` / `cnpj_val` | | `CnpjVal::TypeMismatchError` | `CnpjVal::TypeMismatchError < TypeError < StandardError` (+ `include CnpjVal::Error`) | API misuse | CNPJ input or validator option has the wrong type | | `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` | | `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`) | | `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` | | `CpfGen::TypeMismatchError` | `CpfGen::TypeMismatchError < TypeError < StandardError` (+ `include CpfGen::Error`) | API misuse | Generator option (`format` / `prefix`) has the wrong type | -| `CpfUtils::InvalidArgumentCombinationError` | `CpfUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CpfUtils::Error`) | API misuse | Both an options instance/`Hash` and any non-`nil` keyword on `CpfUtils#format` / `#generate` | -| `CpfUtils::TypeMismatchError` | `CpfUtils::TypeMismatchError < TypeError < StandardError` (+ `include CpfUtils::Error`) | API misuse | Non-`nil` nested `cpf` settings `Hash` contract violation on `CpfUtils.new` | +| `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 | +| `CpfUtils::TypeMismatchError` | `CpfUtils::TypeMismatchError < TypeError < StandardError` (+ `include CpfUtils::Error`) | API misuse | Non-`nil` `settings` argument to `CpfUtils.new` is not a `Hash` | | `CpfVal::TypeMismatchError` | `CpfVal::TypeMismatchError < TypeError < StandardError` (+ `include CpfVal::Error`) | API misuse | CPF input is not a `String` or `Array` of strings | | `CnpjFmt::InvalidLengthError` | `CnpjFmt::InvalidLengthError < CnpjFmt::DomainError < RangeError < StandardError` (+ `include CnpjFmt::Error`) | Domain error | Sanitized length ≠ 14 — **passed to `on_fail`**, not raised by `#format` | | `CnpjFmt::OutOfRangeError` | `CnpjFmt::OutOfRangeError < CnpjFmt::DomainError < RangeError < StandardError` (+ `include CnpjFmt::Error`) | Domain error | `hidden_start` / `hidden_end` outside `0`–`13` | diff --git a/README.pt.md b/README.pt.md index dcd0820..ea5a152 100644 --- a/README.pt.md +++ b/README.pt.md @@ -652,16 +652,16 @@ Os erros dos componentes mantêm os namespaces dos pacotes e se propagam inalter | `CnpjFmt::TypeMismatchError` | `CnpjFmt::TypeMismatchError < TypeError < StandardError` (+ `include CnpjFmt::Error`) | Uso indevido da API | Entrada de CNPJ ou opção do formatador tem o tipo errado (ou o retorno de `on_fail` não é `String`) | | `CnpjGen::InvalidArgumentCombinationError` | `CnpjGen::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjGen::Error`) | Uso indevido da API | Instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` em `CnpjGenerator` / `cnpj_gen` | | `CnpjGen::TypeMismatchError` | `CnpjGen::TypeMismatchError < TypeError < StandardError` (+ `include CnpjGen::Error`) | Uso indevido da API | Opção do gerador (`format` / `prefix` / `type`) tem o tipo errado | -| `CnpjUtils::InvalidArgumentCombinationError` | `CnpjUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjUtils::Error`) | Uso indevido da API | Settings/options e qualquer argumento nomeado não-`nil` na construção de `CnpjUtils` / `#format` / `#generate` / `#is_valid` | -| `CnpjUtils::TypeMismatchError` | `CnpjUtils::TypeMismatchError < TypeError < StandardError` (+ `include CnpjUtils::Error`) | Uso indevido da API | Violação de contrato do `Hash` de settings `cnpj` aninhado em `CnpjUtils.new` | +| `CnpjUtils::InvalidArgumentCombinationError` | `CnpjUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjUtils::Error`) | Uso indevido da API | Construtor/`#format`/`#generate`/`#is_valid`/helpers de classe: `Hash`/instância de settings/options não-`nil` com qualquer argumento nomeado não-`nil` | +| `CnpjUtils::TypeMismatchError` | `CnpjUtils::TypeMismatchError < TypeError < StandardError` (+ `include CnpjUtils::Error`) | Uso indevido da API | Argumento `settings` não-`nil` de `CnpjUtils.new` não é um `Hash` | | `CnpjVal::InvalidArgumentCombinationError` | `CnpjVal::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjVal::Error`) | Uso indevido da API | Instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` em `CnpjValidator` / `cnpj_val` | | `CnpjVal::TypeMismatchError` | `CnpjVal::TypeMismatchError < TypeError < StandardError` (+ `include CnpjVal::Error`) | Uso indevido da API | Entrada de CNPJ ou opção do validador tem o tipo errado | | `CpfFmt::InvalidArgumentCombinationError` | `CpfFmt::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CpfFmt::Error`) | Uso indevido da API | Instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` em `CpfFormatter` / `cpf_fmt` | | `CpfFmt::TypeMismatchError` | `CpfFmt::TypeMismatchError < TypeError < StandardError` (+ `include CpfFmt::Error`) | Uso indevido da API | Entrada de CPF ou opção do formatador tem o tipo errado (ou o retorno de `on_fail` não é `String`) | | `CpfGen::InvalidArgumentCombinationError` | `CpfGen::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CpfGen::Error`) | Uso indevido da API | Instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` em `CpfGenerator` / `cpf_gen` | | `CpfGen::TypeMismatchError` | `CpfGen::TypeMismatchError < TypeError < StandardError` (+ `include CpfGen::Error`) | Uso indevido da API | Opção do gerador (`format` / `prefix`) tem o tipo errado | -| `CpfUtils::InvalidArgumentCombinationError` | `CpfUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CpfUtils::Error`) | Uso indevido da API | Instância/`Hash` de options e qualquer argumento nomeado não-`nil` em `CpfUtils#format` / `#generate` | -| `CpfUtils::TypeMismatchError` | `CpfUtils::TypeMismatchError < TypeError < StandardError` (+ `include CpfUtils::Error`) | Uso indevido da API | Violação de contrato do `Hash` de settings `cpf` aninhado em `CpfUtils.new` | +| `CpfUtils::InvalidArgumentCombinationError` | `CpfUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CpfUtils::Error`) | Uso indevido da API | Construtor: `Hash` de settings não-`nil` com qualquer argumento nomeado não-`nil`; ou `#format`/`#generate`/helpers de classe: `Hash`/instância `*Options` de options não-`nil` com qualquer argumento nomeado não-`nil` | +| `CpfUtils::TypeMismatchError` | `CpfUtils::TypeMismatchError < TypeError < StandardError` (+ `include CpfUtils::Error`) | Uso indevido da API | Argumento `settings` não-`nil` de `CpfUtils.new` não é um `Hash` | | `CpfVal::TypeMismatchError` | `CpfVal::TypeMismatchError < TypeError < StandardError` (+ `include CpfVal::Error`) | Uso indevido da API | Entrada de CPF não é `String` nem `Array` de strings | | `CnpjFmt::InvalidLengthError` | `CnpjFmt::InvalidLengthError < CnpjFmt::DomainError < RangeError < StandardError` (+ `include CnpjFmt::Error`) | Erro de domínio | Comprimento sanitizado ≠ 14 — **passado a `on_fail`**, não lançado por `#format` | | `CnpjFmt::OutOfRangeError` | `CnpjFmt::OutOfRangeError < CnpjFmt::DomainError < RangeError < StandardError` (+ `include CnpjFmt::Error`) | Erro de domínio | `hidden_start` / `hidden_end` fora de `0`–`13` | diff --git a/packages/br-utilities/README.md b/packages/br-utilities/README.md index c3be438..6df7d9f 100644 --- a/packages/br-utilities/README.md +++ b/packages/br-utilities/README.md @@ -659,16 +659,16 @@ Component errors keep their package namespaces and propagate unchanged through t | `CnpjFmt::TypeMismatchError` | `CnpjFmt::TypeMismatchError < TypeError < StandardError` (+ `include CnpjFmt::Error`) | API misuse | CNPJ input or formatter option has the wrong type (or `on_fail` return is not a `String`) | | `CnpjGen::InvalidArgumentCombinationError` | `CnpjGen::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjGen::Error`) | API misuse | Both an `options` instance/`Hash` and any non-`nil` keyword on `CnpjGenerator` / `cnpj_gen` | | `CnpjGen::TypeMismatchError` | `CnpjGen::TypeMismatchError < TypeError < StandardError` (+ `include CnpjGen::Error`) | API misuse | Generator option (`format` / `prefix` / `type`) has the wrong type | -| `CnpjUtils::InvalidArgumentCombinationError` | `CnpjUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjUtils::Error`) | API misuse | Both settings/options and any non-`nil` keyword on `CnpjUtils` construct/`#format` / `#generate` / `#is_valid` | -| `CnpjUtils::TypeMismatchError` | `CnpjUtils::TypeMismatchError < TypeError < StandardError` (+ `include CnpjUtils::Error`) | API misuse | Non-`nil` nested `cnpj` settings `Hash` contract violation on `CnpjUtils.new` | +| `CnpjUtils::InvalidArgumentCombinationError` | `CnpjUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjUtils::Error`) | API misuse | Constructor/`#format`/`#generate`/`#is_valid`/class helpers: non-`nil` settings/options `Hash` (or options instance) with any non-`nil` keyword | +| `CnpjUtils::TypeMismatchError` | `CnpjUtils::TypeMismatchError < TypeError < StandardError` (+ `include CnpjUtils::Error`) | API misuse | Non-`nil` `settings` argument to `CnpjUtils.new` is not a `Hash` | | `CnpjVal::InvalidArgumentCombinationError` | `CnpjVal::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjVal::Error`) | API misuse | Both an `options` instance/`Hash` and any non-`nil` keyword on `CnpjValidator` / `cnpj_val` | | `CnpjVal::TypeMismatchError` | `CnpjVal::TypeMismatchError < TypeError < StandardError` (+ `include CnpjVal::Error`) | API misuse | CNPJ input or validator option has the wrong type | | `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` | | `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`) | | `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` | | `CpfGen::TypeMismatchError` | `CpfGen::TypeMismatchError < TypeError < StandardError` (+ `include CpfGen::Error`) | API misuse | Generator option (`format` / `prefix`) has the wrong type | -| `CpfUtils::InvalidArgumentCombinationError` | `CpfUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CpfUtils::Error`) | API misuse | Both an options instance/`Hash` and any non-`nil` keyword on `CpfUtils#format` / `#generate` | -| `CpfUtils::TypeMismatchError` | `CpfUtils::TypeMismatchError < TypeError < StandardError` (+ `include CpfUtils::Error`) | API misuse | Non-`nil` nested `cpf` settings `Hash` contract violation on `CpfUtils.new` | +| `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 | +| `CpfUtils::TypeMismatchError` | `CpfUtils::TypeMismatchError < TypeError < StandardError` (+ `include CpfUtils::Error`) | API misuse | Non-`nil` `settings` argument to `CpfUtils.new` is not a `Hash` | | `CpfVal::TypeMismatchError` | `CpfVal::TypeMismatchError < TypeError < StandardError` (+ `include CpfVal::Error`) | API misuse | CPF input is not a `String` or `Array` of strings | | `CnpjFmt::InvalidLengthError` | `CnpjFmt::InvalidLengthError < CnpjFmt::DomainError < RangeError < StandardError` (+ `include CnpjFmt::Error`) | Domain error | Sanitized length ≠ 14 — **passed to `on_fail`**, not raised by `#format` | | `CnpjFmt::OutOfRangeError` | `CnpjFmt::OutOfRangeError < CnpjFmt::DomainError < RangeError < StandardError` (+ `include CnpjFmt::Error`) | Domain error | `hidden_start` / `hidden_end` outside `0`–`13` | diff --git a/packages/br-utilities/README.pt.md b/packages/br-utilities/README.pt.md index 1becc53..41767e5 100644 --- a/packages/br-utilities/README.pt.md +++ b/packages/br-utilities/README.pt.md @@ -652,16 +652,16 @@ Os erros dos componentes mantêm os namespaces dos pacotes e se propagam inalter | `CnpjFmt::TypeMismatchError` | `CnpjFmt::TypeMismatchError < TypeError < StandardError` (+ `include CnpjFmt::Error`) | Uso indevido da API | Entrada de CNPJ ou opção do formatador tem o tipo errado (ou o retorno de `on_fail` não é `String`) | | `CnpjGen::InvalidArgumentCombinationError` | `CnpjGen::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjGen::Error`) | Uso indevido da API | Instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` em `CnpjGenerator` / `cnpj_gen` | | `CnpjGen::TypeMismatchError` | `CnpjGen::TypeMismatchError < TypeError < StandardError` (+ `include CnpjGen::Error`) | Uso indevido da API | Opção do gerador (`format` / `prefix` / `type`) tem o tipo errado | -| `CnpjUtils::InvalidArgumentCombinationError` | `CnpjUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjUtils::Error`) | Uso indevido da API | Settings/options e qualquer argumento nomeado não-`nil` na construção de `CnpjUtils` / `#format` / `#generate` / `#is_valid` | -| `CnpjUtils::TypeMismatchError` | `CnpjUtils::TypeMismatchError < TypeError < StandardError` (+ `include CnpjUtils::Error`) | Uso indevido da API | Violação de contrato do `Hash` de settings `cnpj` aninhado em `CnpjUtils.new` | +| `CnpjUtils::InvalidArgumentCombinationError` | `CnpjUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjUtils::Error`) | Uso indevido da API | Construtor/`#format`/`#generate`/`#is_valid`/helpers de classe: `Hash`/instância de settings/options não-`nil` com qualquer argumento nomeado não-`nil` | +| `CnpjUtils::TypeMismatchError` | `CnpjUtils::TypeMismatchError < TypeError < StandardError` (+ `include CnpjUtils::Error`) | Uso indevido da API | Argumento `settings` não-`nil` de `CnpjUtils.new` não é um `Hash` | | `CnpjVal::InvalidArgumentCombinationError` | `CnpjVal::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CnpjVal::Error`) | Uso indevido da API | Instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` em `CnpjValidator` / `cnpj_val` | | `CnpjVal::TypeMismatchError` | `CnpjVal::TypeMismatchError < TypeError < StandardError` (+ `include CnpjVal::Error`) | Uso indevido da API | Entrada de CNPJ ou opção do validador tem o tipo errado | | `CpfFmt::InvalidArgumentCombinationError` | `CpfFmt::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CpfFmt::Error`) | Uso indevido da API | Instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` em `CpfFormatter` / `cpf_fmt` | | `CpfFmt::TypeMismatchError` | `CpfFmt::TypeMismatchError < TypeError < StandardError` (+ `include CpfFmt::Error`) | Uso indevido da API | Entrada de CPF ou opção do formatador tem o tipo errado (ou o retorno de `on_fail` não é `String`) | | `CpfGen::InvalidArgumentCombinationError` | `CpfGen::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CpfGen::Error`) | Uso indevido da API | Instância/`Hash` de `options` e qualquer argumento nomeado não-`nil` em `CpfGenerator` / `cpf_gen` | | `CpfGen::TypeMismatchError` | `CpfGen::TypeMismatchError < TypeError < StandardError` (+ `include CpfGen::Error`) | Uso indevido da API | Opção do gerador (`format` / `prefix`) tem o tipo errado | -| `CpfUtils::InvalidArgumentCombinationError` | `CpfUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CpfUtils::Error`) | Uso indevido da API | Instância/`Hash` de options e qualquer argumento nomeado não-`nil` em `CpfUtils#format` / `#generate` | -| `CpfUtils::TypeMismatchError` | `CpfUtils::TypeMismatchError < TypeError < StandardError` (+ `include CpfUtils::Error`) | Uso indevido da API | Violação de contrato do `Hash` de settings `cpf` aninhado em `CpfUtils.new` | +| `CpfUtils::InvalidArgumentCombinationError` | `CpfUtils::InvalidArgumentCombinationError < ArgumentError < StandardError` (+ `include CpfUtils::Error`) | Uso indevido da API | Construtor: `Hash` de settings não-`nil` com qualquer argumento nomeado não-`nil`; ou `#format`/`#generate`/helpers de classe: `Hash`/instância `*Options` de options não-`nil` com qualquer argumento nomeado não-`nil` | +| `CpfUtils::TypeMismatchError` | `CpfUtils::TypeMismatchError < TypeError < StandardError` (+ `include CpfUtils::Error`) | Uso indevido da API | Argumento `settings` não-`nil` de `CpfUtils.new` não é um `Hash` | | `CpfVal::TypeMismatchError` | `CpfVal::TypeMismatchError < TypeError < StandardError` (+ `include CpfVal::Error`) | Uso indevido da API | Entrada de CPF não é `String` nem `Array` de strings | | `CnpjFmt::InvalidLengthError` | `CnpjFmt::InvalidLengthError < CnpjFmt::DomainError < RangeError < StandardError` (+ `include CnpjFmt::Error`) | Erro de domínio | Comprimento sanitizado ≠ 14 — **passado a `on_fail`**, não lançado por `#format` | | `CnpjFmt::OutOfRangeError` | `CnpjFmt::OutOfRangeError < CnpjFmt::DomainError < RangeError < StandardError` (+ `include CnpjFmt::Error`) | Erro de domínio | `hidden_start` / `hidden_end` fora de `0`–`13` |