cpf_cnpj 0.2.1 → 0.3.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
  SHA1:
3
- metadata.gz: de9eb28c1f452532ed8ac6d4f4de50a90dfbfcc3
4
- data.tar.gz: 245f841bb1134f3c196b9844a3aa2b2cd0dd2aff
3
+ metadata.gz: d13c9e2acfdc0d60eb9ffeb2c9008fdd6bad5d0f
4
+ data.tar.gz: bf59d4b1eb0c251086bc8d241b2811b61f7cefec
5
5
  SHA512:
6
- metadata.gz: 14b110b3d281159204b1615b7c5b382f209f34ceb2c5d5680468796d1f93424f60fd7c8f743a10a189581994dd14087f0c52b7699123d4acef65d8f062d846ff
7
- data.tar.gz: 5ba1e893019527388659e6141fef1685cc715641857192c68591af0165c891bca36915a6ac7ddb8f346a2e9f631f1bf3e0b2d7605d245aa6d75c2d0ffb14286f
6
+ metadata.gz: 778b36870ff6afc4ad0b4c5ba65aa148aef45ba8b8005c2e277808c5559d8e7b7ef4b8e06ff1036aef81b188ce765f5befea46901cbedf84e940a85d672755b2
7
+ data.tar.gz: c5a4bd47257e4afa53b44bbf514b17bb4c8b2b0726b8cb4157895f42184db1713201ace9c75550a0d566efb4bf65d9497479f1d134c678d5ffae36197177631b
data/.gitignore CHANGED
@@ -14,3 +14,4 @@ spec/reports
14
14
  test/tmp
15
15
  test/version_tmp
16
16
  tmp
17
+ Gemfile.lock
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ sudo: false
3
+ cache: bundler
4
+ rvm:
5
+ - '2.1.8'
6
+ - '2.2.3'
7
+ - '2.3.0'
data/README.md CHANGED
@@ -1,9 +1,15 @@
1
1
  # CPF/CNPJ
2
2
 
3
+ [![Build Status](https://travis-ci.org/fnando/cpf_cnpj.svg)](https://travis-ci.org/fnando/cpf_cnpj)
4
+ [![Code Climate](https://codeclimate.com/github/fnando/cpf_cnpj/badges/gpa.svg)](https://codeclimate.com/github/fnando/cpf_cnpj)
5
+ [![Gem Version](https://badge.fury.io/rb/cpf_cnpj.svg)](http://badge.fury.io/rb/cpf_cnpj)
6
+
3
7
  This gem does some [CPF](http://en.wikipedia.org/wiki/Cadastro_de_Pessoas_F%C3%ADsicas)/[CNPJ](http://en.wikipedia.org/wiki/CNPJ) magic. It allows you to create, validate and format CPF/CNPJ, even through the command-line.
4
8
 
5
9
  Just making my life easier when filling these damn numbers on internet bankings and government sites.
6
10
 
11
+ For ActiveModel/ActiveRecord validations, please check <https://github.com/fnando/validators>.
12
+
7
13
  ## Installation
8
14
 
9
15
  Add this line to your application's Gemfile:
@@ -24,16 +30,26 @@ Or install it yourself as:
24
30
 
25
31
  This library has the same API for both CNPJ/CPF, so only one of them is documented below.
26
32
 
27
- require "cpf_cnpj"
33
+ ```ruby
34
+ require "cpf_cnpj"
35
+
36
+ CPF.valid?(number) # Check if a CPF is valid
37
+ CPF.generate # Generate a random CPF number
38
+ CPF.generate(true) # Generate a formatted number
39
+
40
+ cpf = CPF.new(number)
41
+ cpf.formatted # Return formatted CPF (xxx.xxx.xxx-xx)
42
+ cpf.stripped # Return stripped CPF (xxxxxxxxxxx)
43
+ cpf.valid? # Check if CPF is valid
44
+ ```
45
+
46
+ #### Strict Validation
28
47
 
29
- CPF.valid?(number) # Check if a CPF is valid
30
- CPF.generate # Generate a random CPF number
31
- CPF.generate(true) # Generate a formatted number
48
+ By default, validations will strip any characters that aren't numbers. This means that `532#####820------857\n96` is considered a valid number. To perform a strict validation use `strict: true`.
32
49
 
33
- cpf = CPF.new(number)
34
- cpf.formatted # Return formatted CPF (xxx.xxx.xxx-xx)
35
- cpf.stripped # Return stripped CPF (xxxxxxxxxxx)
36
- cpf.valid? # Check if CPF is valid
50
+ ```ruby
51
+ CPF.valid?(number, strict: true)
52
+ ```
37
53
 
38
54
  ### Command-line
39
55
 
data/Rakefile CHANGED
@@ -1,2 +1,11 @@
1
- #!/usr/bin/env rake
2
1
  require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << 'test'
6
+ t.test_files = FileList['test/**/*_test.rb']
7
+ t.verbose = true
8
+ t.warning = false
9
+ end
10
+
11
+ task :default => :test
data/bin/cnpj CHANGED
@@ -1,3 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift "#{__dir__}/../lib"
2
3
  require "cpf_cnpj"
3
- CNPJ::CLI.new(ARGV, STDIN, STDOUT, STDERR).start
4
+ CpfCnpj::CLI.new(CNPJ, ARGV, STDIN, STDOUT, STDERR).start
data/bin/cpf CHANGED
@@ -1,3 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift "#{__dir__}/../lib"
2
3
  require "cpf_cnpj"
3
- CPF::CLI.new(ARGV, STDIN, STDOUT, STDERR).start
4
+ CpfCnpj::CLI.new(CPF, ARGV, STDIN, STDOUT, STDERR).start
@@ -14,6 +14,7 @@ Gem::Specification.new do |gem|
14
14
  gem.require_paths = ["lib"]
15
15
  gem.version = CPF::VERSION
16
16
 
17
- gem.add_development_dependency "rspec"
18
17
  gem.add_development_dependency "pry-meta"
18
+ gem.add_development_dependency "rake"
19
+ gem.add_development_dependency "minitest-utils"
19
20
  end
@@ -1,37 +1,40 @@
1
1
  class CNPJ
2
- require "cnpj/cli"
2
+ require "cpf_cnpj"
3
3
  require "cnpj/formatter"
4
- require "cnpj/generator"
5
4
  require "cnpj/verifier_digit"
6
5
 
7
6
  attr_reader :number
7
+ attr_reader :strict
8
8
 
9
9
  REGEX = /\A\d{2}\.\d{3}.\d{3}\/\d{4}-\d{2}\Z/
10
+ NUMBER_SIZE = 12
10
11
 
11
- BLACKLIST = [
12
- "00000000000000",
13
- "11111111111111",
14
- "22222222222222",
15
- "33333333333333",
16
- "44444444444444",
17
- "55555555555555",
18
- "66666666666666",
19
- "77777777777777",
20
- "88888888888888",
21
- "99999999999999"
22
- ]
12
+ BLACKLIST = %w[
13
+ 00000000000000
14
+ 11111111111111
15
+ 22222222222222
16
+ 33333333333333
17
+ 44444444444444
18
+ 55555555555555
19
+ 66666666666666
20
+ 77777777777777
21
+ 88888888888888
22
+ 9999999999999
23
+ ].freeze
23
24
 
24
- def self.valid?(number)
25
- new(number).valid?
25
+ def self.valid?(number, strict: false)
26
+ new(number, strict).valid?
26
27
  end
27
28
 
28
29
  def self.generate(formatted = false)
29
- cnpj = new(Generator.generate)
30
+ number = CpfCnpj::Generator.generate(NUMBER_SIZE, VerifierDigit)
31
+ cnpj = new(number)
30
32
  formatted ? cnpj.formatted : cnpj.stripped
31
33
  end
32
34
 
33
- def initialize(number)
35
+ def initialize(number, strict = false)
34
36
  @number = number.to_s
37
+ @strict = strict
35
38
  end
36
39
 
37
40
  def number=(number)
@@ -42,7 +45,7 @@ class CNPJ
42
45
  end
43
46
 
44
47
  def stripped
45
- @stripped ||= Formatter.strip(number)
48
+ @stripped ||= Formatter.strip(number, strict)
46
49
  end
47
50
 
48
51
  def formatted
@@ -53,14 +56,15 @@ class CNPJ
53
56
  return unless stripped.size == 14
54
57
  return if BLACKLIST.include?(stripped)
55
58
 
56
- _numbers = numbers[0...12]
57
- _numbers << VerifierDigit.generate(_numbers)
58
- _numbers << VerifierDigit.generate(_numbers)
59
+ digits = numbers[0...12]
60
+ digits << VerifierDigit.generate(digits)
61
+ digits << VerifierDigit.generate(digits)
59
62
 
60
- _numbers[-2, 2] == numbers[-2, 2]
63
+ digits[-2, 2] == numbers[-2, 2]
61
64
  end
62
65
 
63
66
  private
67
+
64
68
  def numbers
65
69
  @numbers ||= stripped.each_char.to_a.map(&:to_i)
66
70
  end
@@ -1,11 +1,14 @@
1
1
  class CNPJ
2
2
  class Formatter
3
+ STRICT_REGEX = /[\/.-]/
4
+ LOOSE_REGEX = /[^\d]/
5
+
3
6
  def self.format(number)
4
7
  strip(number).gsub(/\A(\d{2})(\d{3})(\d{3})(\d{4})(\d{2})\Z/, "\\1.\\2.\\3/\\4-\\5")
5
8
  end
6
9
 
7
- def self.strip(number)
8
- number.to_s.gsub(/[^\d]/, "")
10
+ def self.strip(number, strict = false)
11
+ number.to_s.gsub(strict ? STRICT_REGEX : LOOSE_REGEX, "")
9
12
  end
10
13
  end
11
14
  end
data/lib/cpf.rb CHANGED
@@ -1,38 +1,41 @@
1
1
  class CPF
2
- require "cpf/cli"
2
+ require "cpf_cnpj"
3
3
  require "cpf/formatter"
4
- require "cpf/generator"
5
4
  require "cpf/verifier_digit"
6
5
 
7
6
  attr_reader :number
7
+ attr_reader :strict
8
8
 
9
9
  REGEX = /\A\d{3}\.\d{3}\.\d{3}-\d{2}\Z/
10
+ NUMBER_SIZE = 9
10
11
 
11
- BLACKLIST = [
12
- "00000000000",
13
- "11111111111",
14
- "22222222222",
15
- "33333333333",
16
- "44444444444",
17
- "55555555555",
18
- "66666666666",
19
- "77777777777",
20
- "88888888888",
21
- "99999999999",
22
- "12345678909"
23
- ]
12
+ BLACKLIST = %w[
13
+ 00000000000
14
+ 11111111111
15
+ 22222222222
16
+ 33333333333
17
+ 44444444444
18
+ 55555555555
19
+ 66666666666
20
+ 77777777777
21
+ 88888888888
22
+ 99999999999
23
+ 12345678909
24
+ ].freeze
24
25
 
25
- def self.valid?(number)
26
- new(number).valid?
26
+ def self.valid?(number, strict: false)
27
+ new(number, strict).valid?
27
28
  end
28
29
 
29
30
  def self.generate(formatted = false)
30
- cpf = new(Generator.generate)
31
+ number = CpfCnpj::Generator.generate(NUMBER_SIZE, VerifierDigit)
32
+ cpf = new(number)
31
33
  formatted ? cpf.formatted : cpf.stripped
32
34
  end
33
35
 
34
- def initialize(number)
36
+ def initialize(number, strict = false)
35
37
  @number = number.to_s
38
+ @strict = strict
36
39
  end
37
40
 
38
41
  def number=(number)
@@ -43,25 +46,26 @@ class CPF
43
46
  end
44
47
 
45
48
  def stripped
46
- @stripped ||= Formatter.strip(number)
49
+ @stripped ||= Formatter.strip(number, strict)
47
50
  end
48
51
 
49
52
  def formatted
50
53
  @formatted ||= Formatter.format(number)
51
54
  end
52
55
 
53
- def valid?
56
+ def valid?(strict: false)
54
57
  return unless stripped.size == 11
55
58
  return if BLACKLIST.include?(stripped)
56
59
 
57
- _numbers = numbers[0...9]
58
- _numbers << VerifierDigit.generate(_numbers)
59
- _numbers << VerifierDigit.generate(_numbers)
60
+ digits = numbers[0...9]
61
+ digits << VerifierDigit.generate(digits)
62
+ digits << VerifierDigit.generate(digits)
60
63
 
61
- _numbers[-2, 2] == numbers[-2, 2]
64
+ digits[-2, 2] == numbers[-2, 2]
62
65
  end
63
66
 
64
67
  private
68
+
65
69
  def numbers
66
70
  @numbers ||= stripped.each_char.to_a.map(&:to_i)
67
71
  end
@@ -1,11 +1,14 @@
1
1
  class CPF
2
2
  class Formatter
3
+ STRICT_REGEX = /[.-]/
4
+ LOOSE_REGEX = /[^\d]/
5
+
3
6
  def self.format(number)
4
7
  strip(number).gsub(/\A(\d{3})(\d{3})(\d{3})(\d{2})\Z/, "\\1.\\2.\\3-\\4")
5
8
  end
6
9
 
7
- def self.strip(number)
8
- number.to_s.gsub(/[^\d]/, "")
10
+ def self.strip(number, strict = false)
11
+ number.to_s.gsub(strict ? STRICT_REGEX : LOOSE_REGEX, "")
9
12
  end
10
13
  end
11
14
  end
@@ -1,3 +1,6 @@
1
+ require "English"
1
2
  require "optparse"
3
+ require "cpf_cnpj/generator"
4
+ require "cpf_cnpj/cli"
2
5
  require "cpf"
3
6
  require "cnpj"
@@ -0,0 +1,109 @@
1
+ module CpfCnpj
2
+ class CLI
3
+ attr_reader :document_class, :arguments, :stdin, :stdout, :stderr
4
+
5
+ def initialize(document_class, arguments, stdin, stdout, stderr)
6
+ @document_class = document_class
7
+ @arguments = arguments
8
+ @stdin = stdin
9
+ @stdout = stdout
10
+ @stderr = stderr
11
+ end
12
+
13
+ def bin_name
14
+ document_name.downcase
15
+ end
16
+
17
+ def document_name
18
+ document_class.name
19
+ end
20
+
21
+ def start
22
+ options = {}
23
+
24
+ opts.banner = "Usage: #{bin_name} [options] [#{document_name} number]"
25
+ opts.separator ""
26
+ opts.separator "Specific options:"
27
+
28
+ opts.on("-c", "--check", "Check if #{document_name} is valid") do
29
+ options[:check] = true
30
+ end
31
+
32
+ opts.on("-g", "--generate", "Generate a new #{document_name}") do
33
+ options[:generate] = true
34
+ end
35
+
36
+ opts.on("-f", "--format", "Format #{document_name} with separators") do
37
+ options[:format] = true
38
+ end
39
+
40
+ opts.on("-s", "--strip", "Remove #{document_name} separators") do
41
+ options[:strip] = true
42
+ end
43
+
44
+ opts.on("-v", "--version", "Show version") do
45
+ stdout << VERSION
46
+ exit
47
+ end
48
+
49
+ opts.on_tail("-h", "--help", "Show help") do
50
+ help
51
+ exit
52
+ end
53
+
54
+ opts.parse!(arguments)
55
+ opts.permute!(arguments)
56
+
57
+ help if options.empty?
58
+ generate(options) if options[:generate]
59
+ input = stdin.tty? ? arguments.first : stdin.read
60
+ document = document_class.new(input)
61
+ validate(document)
62
+ format(document) if options[:format]
63
+ strip(document) if options[:strip]
64
+ check(document) if options[:check]
65
+ end
66
+
67
+ def validate(document)
68
+ return if document.valid?
69
+ stderr << "Error: Invalid number\n"
70
+ exit 1
71
+ end
72
+
73
+ # No-op method. CPF is always validated on CpfCnpj::CLI#start.
74
+ def check(_document)
75
+ exit
76
+ end
77
+
78
+ def generate(options)
79
+ document = document_class.new(document_class.generate)
80
+
81
+ if options[:strip]
82
+ stdout << document.stripped
83
+ else
84
+ stdout << document.formatted
85
+ end
86
+
87
+ exit
88
+ end
89
+
90
+ def format(document)
91
+ stdout << document.formatted
92
+ exit
93
+ end
94
+
95
+ def strip(document)
96
+ stdout << document.stripped
97
+ exit
98
+ end
99
+
100
+ def help
101
+ stderr << opts.to_s
102
+ exit 1
103
+ end
104
+
105
+ def opts
106
+ @opts ||= OptionParser.new
107
+ end
108
+ end
109
+ end