cpf_cnpj_plus 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: da3b0a99c5007b263786ecff3baa97962e733b978c900b43fbe3221a6c6bd29e
4
- data.tar.gz: 41d063f24c63805ce0e16cf824537436344166c906f45fcbd04d011519517161
3
+ metadata.gz: 0be5c78bb01f594041dc6648af5e88c793a37909288d2e805ec4df415b1e80f3
4
+ data.tar.gz: c44eb35f3cd67072ba120f5c74960b295ba2abf8a902262743c2688f5ca8fad9
5
5
  SHA512:
6
- metadata.gz: 530da6d176545b6c82b6b9226e2bb43925b76742d35ab66a38826ef3eacba32d90494ebe8447fc49aa3bf332ad01e919f984491d4d3972b376f07f059f02680d
7
- data.tar.gz: 39b949f17039432825917e19c595f7b0ec90ccada9d389eef7dfa4f9bc4bac55ce0381d22288339ccacdd40e18c67e84a5ec415ebd95087929e604f4f0295e21
6
+ metadata.gz: 4437f396b413c092653cd6f1960d5686e392fdb1bc488bb9244b919443d3de9c992d4187ef88f29e88b939a8bc3a086f49d4f71ac53e95b3fa1d8711c97c8a00
7
+ data.tar.gz: 685071ce041e859639aa0cd3c6091133481c725234b419bd49f18e36761339bcd55455ce751ac01fdf12643f08e8bd7f44f9c3c3976dee0c5bb6986f298bab79
data/CHANGELOG.md CHANGED
@@ -1,5 +1,43 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.4.0] - 2026-08-01
4
+
5
+ - Adicionada API unificada que detecta CPF ou CNPJ pelo tamanho do
6
+ documento: `CpfCnpjPlus.valid?`, `CpfCnpjPlus.format` e
7
+ `CpfCnpjPlus.generate(:cpf | :cnpj, alphanumeric:)`.
8
+ - Adicionado `CpfCnpjPlus.alphanumeric_cnpj?`, que identifica se um CNPJ
9
+ válido já usa o novo padrão alfanumérico (útil para auditar bases de
10
+ dados antes da migração de julho de 2026).
11
+ - `format_cpf`/`format_cnpj` (e a nova `format`) agora aceitam a opção
12
+ `strict: true`, que retorna `nil` quando os dígitos verificadores ou a
13
+ estrutura do documento são inválidos, em vez de formatar um documento
14
+ incorreto silenciosamente.
15
+ - Adicionado validador opcional para ActiveModel/Rails
16
+ (`require "cpf_cnpj_plus/active_model_validator"`), permitindo
17
+ `validates :campo, cpf_cnpj: true` (ou `type: :cpf`/`type: :cnpj`) em
18
+ models. Não é carregado por padrão e não adiciona `activemodel` como
19
+ dependência de runtime.
20
+ - Testes adicionados para todas as features acima (100% de cobertura de
21
+ linhas).
22
+
23
+ ## [0.3.1] - 2026-08-01
24
+
25
+ - Corrigido `exe/cnpj_plus --check`, que chamava um método inexistente
26
+ (`CpfCnpjPlus.valid?`) e quebrava com `NoMethodError`; agora usa
27
+ `valid_cnpj?`.
28
+ - Corrigida a rejeição de CNPJs alfanuméricos "conhecidamente inválidos":
29
+ antes só bases numéricas com todos os dígitos iguais eram rejeitadas;
30
+ agora qualquer base de 12 caracteres repetidos é rejeitada, incluindo
31
+ letras (ex.: `"AAAAAAAAAAAA45"`).
32
+ - Removida dependência morta de `byebug` nos specs do validator, que
33
+ quebrava `bundle exec rspec` em ambiente limpo (CI).
34
+ - Corrigido o workflow de CI, que disparava em push apenas na branch
35
+ `master` e nunca era executado em `main`.
36
+ - Corrigida a documentação do README, que citava um método
37
+ `CpfCnpjPlus.generate` inexistente (o correto é `generate_cpf`/`generate_cnpj`).
38
+ - Adicionados testes para os casos acima e para os executáveis
39
+ `cnpj_plus`/`cpf_plus`.
40
+
3
41
  ## [0.1.0] - 2025-07-06
4
42
 
5
43
  - Initial release
data/README.md CHANGED
@@ -1,6 +1,10 @@
1
1
  # CPF CNPJ Plus
2
2
 
3
- **cpf_cnpj_plus** é uma biblioteca Ruby para **validar, gerar e formatar** números de CPF e CNPJ brasileiros, tanto via código Ruby quanto pela linha de comando.
3
+ [![Gem Version](https://img.shields.io/gem/v/cpf_cnpj_plus.svg)](https://rubygems.org/gems/cpf_cnpj_plus)
4
+ [![Ruby](https://github.com/DougNeo/cpf_cnpj_plus/actions/workflows/main.yml/badge.svg)](https://github.com/DougNeo/cpf_cnpj_plus/actions/workflows/main.yml)
5
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE.txt)
6
+
7
+ **cpf_cnpj_plus** é uma biblioteca Ruby para **validar, gerar e formatar** números de CPF e CNPJ brasileiros. Funciona em **Ruby puro** (sem dependências de runtime), tem um **validador opcional para Rails/ActiveModel**, e oferece **ferramentas de linha de comando**.
4
8
 
5
9
  ---
6
10
 
@@ -40,22 +44,57 @@ A gem oferece uma API unificada para CPF e CNPJ. Exemplos:
40
44
  ```ruby
41
45
  require "cpf_cnpj_plus"
42
46
 
43
- CPF
47
+ # CPF
44
48
  CpfCnpjPlus.valid_cpf?("123.456.789-09") # => true ou false
45
49
  CpfCnpjPlus.format_cpf("12345678909") # => "123.456.789-09"
46
- CpfCnpjPlus.generate # => Gera um CPF válido
50
+ CpfCnpjPlus.generate_cpf # => Gera um CPF válido
47
51
 
48
- CNPJ
52
+ # CNPJ
49
53
  CpfCnpjPlus.valid_cnpj?("12.345.678/0001-95") # => true ou false
50
54
  CpfCnpjPlus.format_cnpj("12345678000195") # => "12.345.678/0001-95"
51
- CpfCnpjPlus.generate # => Gera um CNPJ válido
55
+ CpfCnpjPlus.generate_cnpj # => Gera um CNPJ válido
56
+
57
+ # CNPJ Alfanumérico (novo formato)
58
+ CpfCnpjPlus.valid_cnpj?("12.ABC.345/01DE-35") # => true
59
+ CpfCnpjPlus.format_cnpj("12ABC34501DE35") # => "12.ABC.345/01DE-35"
60
+ CpfCnpjPlus.generate_cnpj(alphanumeric: true) # => Gera um CNPJ alfanumérico válido
61
+ CpfCnpjPlus.alphanumeric_cnpj?("12ABC34501DE35") # => true (identifica se já usa o novo padrão)
62
+
63
+ # API unificada (detecta CPF ou CNPJ pelo tamanho do documento)
64
+ CpfCnpjPlus.valid?("123.456.789-09") # => true ou false
65
+ CpfCnpjPlus.valid?("12.345.678/0001-95") # => true ou false
66
+ CpfCnpjPlus.format("12345678909") # => "123.456.789-09"
67
+ CpfCnpjPlus.format("12345678000195") # => "12.345.678/0001-95"
68
+ CpfCnpjPlus.generate(:cpf) # => Gera um CPF válido
69
+ CpfCnpjPlus.generate(:cnpj, alphanumeric: true) # => Gera um CNPJ alfanumérico válido
70
+
71
+ # Formatação estrita (retorna nil se os dígitos verificadores forem inválidos)
72
+ CpfCnpjPlus.format_cpf("12345678900", strict: true) # => nil (dígitos verificadores errados)
73
+ CpfCnpjPlus.format_cnpj("12345678000199", strict: true) # => nil (dígitos verificadores errados)
74
+ ```
75
+
76
+ ### Validação em Rails/ActiveModel
77
+
78
+ A gem também traz um validador opcional para uso com `ActiveModel::Validations`
79
+ (inclusive em models Rails). Ele **não** é carregado junto com
80
+ `require "cpf_cnpj_plus"` — é preciso requerê-lo explicitamente, e `activemodel`
81
+ precisa estar disponível na sua aplicação:
82
+
83
+ ```ruby
84
+ require "cpf_cnpj_plus/active_model_validator"
85
+
86
+ class Empresa
87
+ include ActiveModel::Model
88
+ attr_accessor :cnpj, :cpf
52
89
 
53
- CNPJ Alfanumérico (novo formato)
54
- CpfCnpjPlus.valid_cnpj?("A1234567B00195") # => true ou false
55
- CpfCnpjPlus.format_cnpj("A1234567B00195") # => "A1.234.567/B001-95"
56
- CpfCnpjPlus.generate(alphanumeric: true) # => Gera um CNPJ alfanumérico válido
90
+ validates :cnpj, cpf_cnpj: { type: :cnpj } # aceita CNPJ numérico ou alfanumérico
91
+ validates :cpf, cpf_cnpj: { type: :cpf }
92
+ # ou, para detectar o tipo automaticamente pelo tamanho:
93
+ # validates :documento, cpf_cnpj: true
94
+ end
57
95
  ```
58
96
 
97
+ Suporta as opções padrão do Rails (`allow_nil`, `allow_blank`, `message`).
59
98
 
60
99
  ### Linha de comando
61
100
 
@@ -66,21 +105,27 @@ $ cpf_plus --check 123.456.789-09
66
105
  $ cpf_plus --format 12345678909
67
106
  $ cpf_plus --generate
68
107
  $ cnpj_plus --check 12.345.678/0001-95
69
- $ cnpj_plus --check A12.345.67B/0019-5
108
+ $ cnpj_plus --check 12.ABC.345/01DE-35
70
109
  $ cnpj_plus --generate
71
110
  ```
72
111
 
73
112
  ## Compatibilidade
74
113
 
75
- - Ruby >= 3.1.0
114
+ - Ruby >= 3.1.0 (testado em CI nas versões 3.1, 3.2, 3.3, 3.4 e 4.0).
115
+ - Sem dependências de runtime — funciona em qualquer projeto Ruby puro.
116
+ - Uso com Rails/ActiveModel é opcional: só é necessário se você requerer
117
+ `cpf_cnpj_plus/active_model_validator` (veja a seção "Validação em
118
+ Rails/ActiveModel" acima).
76
119
 
77
120
  ## Contribuição
78
121
 
79
122
  1. Faça um fork do projeto
80
123
  2. Crie sua branch (`git checkout -b minha-feature`)
81
- 3. Commit suas alterações (`git commit -am 'Adiciona nova feature'`)
82
- 4. Faça push para a branch (`git push origin minha-feature`)
83
- 5. Abra um Pull Request
124
+ 3. Rode `bundle install` e depois `bundle exec rake` (specs + rubocop) para
125
+ garantir que tudo continua passando
126
+ 4. Commit suas alterações (`git commit -am 'Adiciona nova feature'`)
127
+ 5. Faça push para a branch (`git push origin minha-feature`)
128
+ 6. Abra um Pull Request
84
129
 
85
130
  ## Licença
86
131
 
data/exe/cnpj_plus CHANGED
@@ -14,7 +14,7 @@ OptionParser.new do |opts|
14
14
  end.parse!
15
15
 
16
16
  if options[:check]
17
- valid = CpfCnpjPlus.valid?(options[:check])
17
+ valid = CpfCnpjPlus.valid_cnpj?(options[:check])
18
18
  puts valid ? "✅ CNPJ válido" : "❌ CNPJ inválido"
19
19
  elsif options[:format]
20
20
  result = CpfCnpjPlus.format_cnpj(options[:format])
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_model"
4
+ require_relative "../cpf_cnpj_plus"
5
+
6
+ # Validador ActiveModel opcional. Não é carregado por padrão junto com a gem —
7
+ # quem quiser usar em um model Rails/ActiveModel deve fazer:
8
+ #
9
+ # require "cpf_cnpj_plus/active_model_validator"
10
+ #
11
+ # class Empresa
12
+ # include ActiveModel::Model
13
+ # attr_accessor :cnpj
14
+ #
15
+ # validates :cnpj, cpf_cnpj: true # detecta CPF ou CNPJ pelo tamanho
16
+ # validates :cnpj, cpf_cnpj: { type: :cnpj } # aceita só CNPJ (numérico ou alfanumérico)
17
+ # validates :cpf, cpf_cnpj: { type: :cpf } # aceita só CPF
18
+ # end
19
+ class CpfCnpjValidator < ActiveModel::EachValidator
20
+ def validate_each(record, attribute, value)
21
+ return if options[:allow_nil] && value.nil?
22
+ return if options[:allow_blank] && (value.nil? || value.to_s.strip.empty?)
23
+
24
+ record.errors.add(attribute, options.fetch(:message, :invalid)) unless valid_document?(value)
25
+ end
26
+
27
+ private
28
+
29
+ def valid_document?(value)
30
+ case options[:type]&.to_sym
31
+ when :cpf then CpfCnpjPlus.valid_cpf?(value)
32
+ when :cnpj then CpfCnpjPlus.valid_cnpj?(value)
33
+ when nil then CpfCnpjPlus.valid?(value)
34
+ else
35
+ raise ArgumentError, "type deve ser :cpf ou :cnpj (recebido: #{options[:type].inspect})"
36
+ end
37
+ end
38
+ end
@@ -6,11 +6,12 @@ module CpfCnpjPlus
6
6
  # Suporta o formato numérico tradicional e o novo formato alfanumérico,
7
7
  # aplicando a máscara padrão XX.XXX.XXX/XXXX-XX.
8
8
  class Cnpj
9
- def self.format(cnpj)
10
- cnpj = cnpj.to_s.upcase.gsub(/[^A-Z0-9]/, "")
11
- return nil unless cnpj.length == 14
9
+ def self.format(cnpj, strict: false)
10
+ cleaned = cnpj.to_s.upcase.gsub(/[^A-Z0-9]/, "")
11
+ return nil unless cleaned.length == 14
12
+ return nil if strict && !Validator::Cnpj.valid?(cleaned)
12
13
 
13
- "#{cnpj[0..1]}.#{cnpj[2..4]}.#{cnpj[5..7]}/#{cnpj[8..11]}-#{cnpj[12..13]}"
14
+ "#{cleaned[0..1]}.#{cleaned[2..4]}.#{cleaned[5..7]}/#{cleaned[8..11]}-#{cleaned[12..13]}"
14
15
  end
15
16
  end
16
17
  end
@@ -5,11 +5,12 @@ module CpfCnpjPlus
5
5
  # Responsável por formatar números de CPF para exibição.
6
6
  # Aplica a máscara padrão XXX.XXX.XXX-XX a partir de uma string numérica.
7
7
  class Cpf
8
- def self.format(cpf)
9
- cpf = cpf.to_s.gsub(/[^0-9]/, "")
10
- return nil unless cpf.length == 11
8
+ def self.format(cpf, strict: false)
9
+ cleaned = cpf.to_s.gsub(/[^0-9]/, "")
10
+ return nil unless cleaned.length == 11
11
+ return nil if strict && !Validator::Cpf.valid?(cleaned)
11
12
 
12
- "#{cpf[0..2]}.#{cpf[3..5]}.#{cpf[6..8]}-#{cpf[9..10]}"
13
+ "#{cleaned[0..2]}.#{cleaned[3..5]}.#{cleaned[6..8]}-#{cleaned[9..10]}"
13
14
  end
14
15
  end
15
16
  end
@@ -57,11 +57,14 @@ module CpfCnpjPlus
57
57
  digit
58
58
  end
59
59
 
60
+ def self.alphanumeric?(cnpj)
61
+ cnpj = normalize(cnpj)
62
+ valid?(cnpj) && cnpj[0..11].match?(/[A-Z]/)
63
+ end
64
+
60
65
  def self.cnpj_not_valid?(cnpj)
61
- cnpj = cnpj.to_s.gsub(/[^0-9]/, "")
62
- %w[00000000000000 11111111111111 22222222222222 33333333333333
63
- 44444444444444 55555555555555 66666666666666 77777777777777
64
- 88888888888888 99999999999999].include?(cnpj)
66
+ base = cnpj.to_s[0..11]
67
+ base.to_s.length == 12 && base.chars.uniq.length == 1
65
68
  end
66
69
  end
67
70
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CpfCnpjPlus
4
- VERSION = "0.3.0"
4
+ VERSION = "0.4.0"
5
5
  end
data/lib/cpf_cnpj_plus.rb CHANGED
@@ -16,8 +16,8 @@ module CpfCnpjPlus
16
16
  CpfCnpjPlus::Validator::Cpf.valid?(cpf)
17
17
  end
18
18
 
19
- def self.format_cpf(cpf)
20
- CpfCnpjPlus::Formatter::Cpf.format(cpf)
19
+ def self.format_cpf(cpf, strict: false)
20
+ CpfCnpjPlus::Formatter::Cpf.format(cpf, strict: strict)
21
21
  end
22
22
 
23
23
  def self.generate_cpf
@@ -30,11 +30,49 @@ module CpfCnpjPlus
30
30
  CpfCnpjPlus::Validator::Cnpj.valid?(cnpj)
31
31
  end
32
32
 
33
- def self.format_cnpj(cnpj)
34
- CpfCnpjPlus::Formatter::Cnpj.format(cnpj)
33
+ def self.format_cnpj(cnpj, strict: false)
34
+ CpfCnpjPlus::Formatter::Cnpj.format(cnpj, strict: strict)
35
35
  end
36
36
 
37
37
  def self.generate_cnpj(alphanumeric: false)
38
38
  CpfCnpjPlus::Generator::Cnpj.generate(alphanumeric: alphanumeric)
39
39
  end
40
+
41
+ def self.alphanumeric_cnpj?(cnpj)
42
+ CpfCnpjPlus::Validator::Cnpj.alphanumeric?(cnpj)
43
+ end
44
+
45
+ # API unificada (detecta CPF ou CNPJ pelo tamanho do documento)
46
+
47
+ def self.valid?(doc)
48
+ case document_type(doc)
49
+ when :cpf then valid_cpf?(doc)
50
+ when :cnpj then valid_cnpj?(doc)
51
+ else false
52
+ end
53
+ end
54
+
55
+ def self.format(doc, strict: false)
56
+ case document_type(doc)
57
+ when :cpf then format_cpf(doc, strict: strict)
58
+ when :cnpj then format_cnpj(doc, strict: strict)
59
+ end
60
+ end
61
+
62
+ def self.generate(type, alphanumeric: false)
63
+ case type.to_s.downcase
64
+ when "cpf" then generate_cpf
65
+ when "cnpj" then generate_cnpj(alphanumeric: alphanumeric)
66
+ else
67
+ raise ArgumentError, "type deve ser :cpf ou :cnpj (recebido: #{type.inspect})"
68
+ end
69
+ end
70
+
71
+ def self.document_type(doc)
72
+ case doc.to_s.upcase.gsub(/[^A-Z0-9]/, "").length
73
+ when 11 then :cpf
74
+ when 14 then :cnpj
75
+ end
76
+ end
77
+ private_class_method :document_type
40
78
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cpf_cnpj_plus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - DougNeo
@@ -9,6 +9,20 @@ bindir: exe
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: activemodel
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :development
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
12
26
  - !ruby/object:Gem::Dependency
13
27
  name: simplecov
14
28
  requirement: !ruby/object:Gem::Requirement
@@ -23,10 +37,9 @@ dependencies:
23
37
  - - ">="
24
38
  - !ruby/object:Gem::Version
25
39
  version: '0'
26
- description: |-
27
- Fornece métodos práticos para validar, gerar e formatar números de CPF e CNPJ, incluindo
28
- ferramentas de linha de comando para facilitar integrações em sistemas bancários,
29
- governamentais e aplicações Ruby em geral.
40
+ description: Fornece métodos práticos para validar, gerar e formatar números de CPF
41
+ e CNPJ (incluindo o novo CNPJ alfanumérico), com API para Ruby puro, validador opcional
42
+ para Rails/ActiveModel e ferramentas de linha de comando.
30
43
  email:
31
44
  - doug.neo@gmail.com
32
45
  executables:
@@ -35,7 +48,6 @@ executables:
35
48
  extensions: []
36
49
  extra_rdoc_files: []
37
50
  files:
38
- - ".byebug_history"
39
51
  - ".rspec"
40
52
  - ".rubocop.yml"
41
53
  - CHANGELOG.md
@@ -46,6 +58,7 @@ files:
46
58
  - exe/cnpj_plus
47
59
  - exe/cpf_plus
48
60
  - lib/cpf_cnpj_plus.rb
61
+ - lib/cpf_cnpj_plus/active_model_validator.rb
49
62
  - lib/cpf_cnpj_plus/formatter.rb
50
63
  - lib/cpf_cnpj_plus/formatter/cnpj.rb
51
64
  - lib/cpf_cnpj_plus/formatter/cpf.rb
@@ -62,7 +75,7 @@ licenses:
62
75
  - MIT
63
76
  metadata:
64
77
  allowed_push_host: https://rubygems.org
65
- homepage_uri: https://github.com/DougNeo/cpf_cnpj_plus
78
+ rubygems_mfa_required: 'true'
66
79
  source_code_uri: https://github.com/DougNeo/cpf_cnpj_plus
67
80
  changelog_uri: https://github.com/DougNeo/cpf_cnpj_plus/blob/main/CHANGELOG.md
68
81
  rdoc_options: []
data/.byebug_history DELETED
@@ -1,4 +0,0 @@
1
- c
2
- described_class.valid_cpf?("10321255780")
3
- described_class.valid_cpf?("103.212.557-80")
4
- described_class.valid_cpf?("10321255780")