br-utils 0.1.0 → 0.1.1

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: 23f897e35167135baa4eeb00f625bdd6f69656b86ce6ca62229f2dcd4a718f94
4
- data.tar.gz: 1737c2f0bc7a3799ee6a3c87d8bf065a27b992eefe4fe4907be0c0f92536bc5e
3
+ metadata.gz: 7410285ed2d94f3143069921599888b0c3c4055be41f25454e32e4e8f61a38f3
4
+ data.tar.gz: 4e6fb1f71012f46622c7a3e4dc78bf26eaa7ac864d7122f1c1ece3bc912bb94e
5
5
  SHA512:
6
- metadata.gz: d8e78d62b1b1c5173d2eed7088c55dfe3e7934d3faf0d6f527e654242228900fcebb29ac6fe84d5023a779292453d57a22f1ba954aa979c768b515ba5ac94c4e
7
- data.tar.gz: 6978c693525a310ec21fac4a00c76128fe374bc0aed83c5a897f2a62dabc57578be80dafee3be11cab72bcd106749cc966d53518caa26477568c229ebcdde994
6
+ metadata.gz: 2f3644c35d9e2332c02efefe4736ca7b0faab1c342edd5390ff706ba03b1773f7f52680e233010643de5a4077b6b55c5e246228a1485094427587a32cbc595ef
7
+ data.tar.gz: 7d6813dd96f7b6f0db6953c014ecb9609a33e95666ccee310deab668767c7fe92c677c7b6b669661a629fcd056316b670831e131864f90d942fbc178c56bc79b
@@ -0,0 +1,27 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [master]
6
+ pull_request:
7
+ branches: [master]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ ruby: ['3.1', '3.2', '3.3', '3.4']
16
+
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Set up Ruby ${{ matrix.ruby }}
21
+ uses: ruby/setup-ruby@v1
22
+ with:
23
+ ruby-version: ${{ matrix.ruby }}
24
+ bundler-cache: true
25
+
26
+ - name: Run tests
27
+ run: bundle exec rake spec
@@ -57,9 +57,12 @@ module BrazilianUtils
57
57
  :cep,
58
58
  :logradouro,
59
59
  :complemento,
60
+ :unidade,
60
61
  :bairro,
61
62
  :localidade,
62
63
  :uf,
64
+ :estado,
65
+ :regiao,
63
66
  :ibge,
64
67
  :gia,
65
68
  :ddd,
@@ -203,9 +206,12 @@ module BrazilianUtils
203
206
  cep: data['cep'],
204
207
  logradouro: data['logradouro'],
205
208
  complemento: data['complemento'],
209
+ unidade: data['unidade'],
206
210
  bairro: data['bairro'],
207
211
  localidade: data['localidade'],
208
212
  uf: data['uf'],
213
+ estado: data['estado'],
214
+ regiao: data['regiao'],
209
215
  ibge: data['ibge'],
210
216
  gia: data['gia'],
211
217
  ddd: data['ddd'],
@@ -294,9 +300,12 @@ module BrazilianUtils
294
300
  cep: address_data['cep'],
295
301
  logradouro: address_data['logradouro'],
296
302
  complemento: address_data['complemento'],
303
+ unidade: address_data['unidade'],
297
304
  bairro: address_data['bairro'],
298
305
  localidade: address_data['localidade'],
299
306
  uf: address_data['uf'],
307
+ estado: address_data['estado'],
308
+ regiao: address_data['regiao'],
300
309
  ibge: address_data['ibge'],
301
310
  gia: address_data['gia'],
302
311
  ddd: address_data['ddd'],
@@ -32,41 +32,41 @@ module BrazilianUtils
32
32
  first_verificator = digits[9]
33
33
  second_verificator = digits[10]
34
34
 
35
- # Check the 10th digit
36
- return false unless check_first_verificator(digits, first_verificator)
35
+ # Check the 10th digit; keep the raw remainder (0-10), since the
36
+ # decrement applied to the 11th digit depends on whether the *pre-cap*
37
+ # remainder overflowed (>9), not on the printed digit (always 0-9).
38
+ first_rest = calculate_first_rest(digits)
39
+ expected_first = first_rest > 9 ? 0 : first_rest
40
+ return false unless expected_first == first_verificator
37
41
 
38
42
  # Check the 11th digit
39
- check_second_verificator(digits, second_verificator, first_verificator)
43
+ check_second_verificator(digits, second_verificator, first_rest)
40
44
  end
41
45
 
42
- # Generates the first verification digit and uses it to verify the 10th digit of the CNH
46
+ # Computes the raw remainder used to derive the CNH's first verification digit.
43
47
  #
44
48
  # @param digits [Array<Integer>] Array of CNH digits
45
- # @param first_verificator [Integer] The first verification digit (10th digit)
46
- # @return [Boolean] true if the first verificator is valid
49
+ # @return [Integer] The remainder (0-10) before capping to a single printable digit
47
50
  #
48
51
  # @private
49
- def self.check_first_verificator(digits, first_verificator)
52
+ def self.calculate_first_rest(digits)
50
53
  sum = 0
51
54
  9.times do |i|
52
55
  sum += digits[i] * (9 - i)
53
56
  end
54
57
 
55
- sum = sum % 11
56
- result = sum > 9 ? 0 : sum
57
-
58
- result == first_verificator
58
+ sum % 11
59
59
  end
60
60
 
61
61
  # Generates the second verification digit and uses it to verify the 11th digit of the CNH
62
62
  #
63
63
  # @param digits [Array<Integer>] Array of CNH digits
64
64
  # @param second_verificator [Integer] The second verification digit (11th digit)
65
- # @param first_verificator [Integer] The first verification digit (10th digit)
65
+ # @param first_rest [Integer] The raw remainder (0-10) used to compute the 10th digit
66
66
  # @return [Boolean] true if the second verificator is valid
67
67
  #
68
68
  # @private
69
- def self.check_second_verificator(digits, second_verificator, first_verificator)
69
+ def self.check_second_verificator(digits, second_verificator, first_rest)
70
70
  sum = 0
71
71
  9.times do |i|
72
72
  sum += digits[i] * (i + 1)
@@ -74,7 +74,7 @@ module BrazilianUtils
74
74
 
75
75
  result = sum % 11
76
76
 
77
- if first_verificator > 9
77
+ if first_rest > 9
78
78
  result = (result - 2).negative? ? result + 9 : result - 2
79
79
  end
80
80
 
@@ -83,6 +83,6 @@ module BrazilianUtils
83
83
  result == second_verificator
84
84
  end
85
85
 
86
- private_class_method :check_first_verificator, :check_second_verificator
86
+ private_class_method :calculate_first_rest, :check_second_verificator
87
87
  end
88
88
  end
@@ -170,7 +170,7 @@ module BrazilianUtils
170
170
  (position - 8).downto(2) { |w| weights << w }
171
171
  9.downto(2) { |w| weights << w }
172
172
 
173
- val = cnpj.chars.zip(weights).sum do |digit, weight|
173
+ val = cnpj.chars.first(position - 1).zip(weights).sum do |digit, weight|
174
174
  digit.to_i * weight
175
175
  end % 11
176
176
 
@@ -161,7 +161,7 @@ module BrazilianUtils
161
161
  #
162
162
  # @private
163
163
  def self.hashdigit(cpf, position)
164
- val = cpf.chars.zip(position.downto(2)).sum do |digit, weight|
164
+ val = cpf.chars.first(position - 1).zip(position.downto(2)).sum do |digit, weight|
165
165
  digit.to_i * weight
166
166
  end % 11
167
167
 
@@ -42,7 +42,7 @@ module BrazilianUtils
42
42
  #
43
43
  # @example
44
44
  # convert_real_to_text(1523.45)
45
- # #=> "Mil, quinhentos e vinte e três reais e quarenta e cinco centavos"
45
+ # #=> "Mil quinhentos e vinte e três reais e quarenta e cinco centavos"
46
46
  #
47
47
  # convert_real_to_text(1.00)
48
48
  # #=> "Um real"
@@ -72,7 +72,10 @@ module BrazilianUtils
72
72
  if reais > 0
73
73
  reais_text = number_to_words(reais)
74
74
  currency_text = reais == 1 ? 'real' : 'reais'
75
- conector = reais_text.match?(/lhão|lhões$/) ? 'de ' : ''
75
+ # "de" only applies when the text ends in "milhão(ões)"/"bilhão(ões)"/...
76
+ # on its own (e.g. "um milhão de reais"), not when it's followed by
77
+ # more words (e.g. "um milhão e um reais", no "de").
78
+ conector = reais_text.match?(/lhão$|lhões$/) ? 'de ' : ''
76
79
  parts << "#{reais_text} #{conector}#{currency_text}"
77
80
  end
78
81
 
@@ -104,8 +107,9 @@ module BrazilianUtils
104
107
  #
105
108
  # @private
106
109
  def self.number_to_words(number)
110
+ number = number.to_i.abs
107
111
  return 'zero' if number.zero?
108
-
112
+
109
113
  # Scale names
110
114
  scales = [
111
115
  '',
@@ -115,7 +119,7 @@ module BrazilianUtils
115
119
  'trilhão',
116
120
  'quadrilhão'
117
121
  ]
118
-
122
+
119
123
  scales_plural = [
120
124
  '',
121
125
  'mil',
@@ -124,49 +128,47 @@ module BrazilianUtils
124
128
  'trilhões',
125
129
  'quadrilhões'
126
130
  ]
127
-
128
- # Break number into groups of 3 digits
131
+
132
+ # Break number into groups of 3 digits, lowest order first
133
+ # (groups[0] is units-hundreds, groups[1] is thousands, ...)
129
134
  groups = []
130
135
  temp = number
131
136
  while temp > 0
132
137
  groups << temp % 1000
133
138
  temp /= 1000
134
139
  end
135
-
136
- result = []
140
+
141
+ parts = []
137
142
  groups.each_with_index do |group, index|
138
143
  next if group.zero?
139
-
144
+
140
145
  group_text = convert_group(group)
141
146
  scale_name = group == 1 ? scales[index] : scales_plural[index]
142
-
143
- if scale_name.empty?
144
- result << group_text
145
- elsif index == 1 # "mil" doesn't need number before if it's exactly 1000
146
- if group == 1
147
- result << scale_name
148
- else
149
- result << "#{group_text} #{scale_name}"
150
- end
151
- else
152
- result << "#{group_text} #{scale_name}"
153
- end
154
- end
155
-
156
- # Join with "e" where appropriate
157
- if result.length > 1
158
- last = result.pop
159
- result_text = result.reverse.join(', ')
160
-
161
- # Check if we need "e" before the last part
162
- if number % 1000 < 100 && number % 1000 > 0
163
- "#{result_text} e #{last}"
164
- else
165
- "#{result_text}, #{last}"
166
- end
167
- else
168
- result.first || 'zero'
147
+
148
+ parts << if scale_name.empty?
149
+ group_text
150
+ elsif index == 1 && group == 1 # "mil" doesn't need "um" before it
151
+ scale_name
152
+ else
153
+ "#{group_text} #{scale_name}"
154
+ end
169
155
  end
156
+
157
+ # parts was built lowest-order group first; the spoken form reads
158
+ # highest order first (e.g. "mil duzentos e trinta e quatro", not
159
+ # "duzentos e trinta e quatro, mil").
160
+ parts.reverse!
161
+ return parts.first if parts.length == 1
162
+
163
+ # The lowest-order non-zero group (spoken last) gets an "e" in front
164
+ # when it reads as a single small/round term: below 100, or an exact
165
+ # multiple of 100 (e.g. "mil e quatrocentos", "mil e um"); otherwise
166
+ # groups are simply concatenated with a space, no comma
167
+ # (Manual de Redação da Presidência: "mil duzentos e cinquenta reais").
168
+ final_group = groups.find { |g| !g.zero? }
169
+ last = parts.pop
170
+ separator = (final_group < 100 || (final_group % 100).zero?) ? ' e ' : ' '
171
+ "#{parts.join(' ')}#{separator}#{last}"
170
172
  end
171
173
 
172
174
  # Converts a group of 3 digits (0-999) to words.
@@ -38,6 +38,13 @@ module BrazilianUtils
38
38
  [12, 25] => 'Natal'
39
39
  }.freeze
40
40
 
41
+ # Lei 14.759/2023 made November 20th (Dia Nacional de Zumbi e da
42
+ # Consciência Negra) a national holiday starting in 2024; before that it
43
+ # was only a holiday in the states/cities that already had their own law
44
+ # for it (some of which are still listed in STATE_HOLIDAYS below).
45
+ NATIONAL_CONSCIENCIA_NEGRA_MONTH_DAY = [11, 20].freeze
46
+ NATIONAL_CONSCIENCIA_NEGRA_EFFECTIVE_YEAR = 2024
47
+
41
48
  # State-specific holidays (fixed dates)
42
49
  STATE_HOLIDAYS = {
43
50
  'AC' => { [1, 23] => 'Dia do Evangélico', [6, 15] => 'Aniversário do Acre', [9, 5] => 'Dia da Amazônia', [11, 17] => 'Assinatura do Tratado de Petrópolis' },
@@ -112,6 +119,10 @@ module BrazilianUtils
112
119
  # Check national holidays
113
120
  return true if NATIONAL_HOLIDAYS.key?(month_day)
114
121
 
122
+ if month_day == NATIONAL_CONSCIENCIA_NEGRA_MONTH_DAY && date.year >= NATIONAL_CONSCIENCIA_NEGRA_EFFECTIVE_YEAR
123
+ return true
124
+ end
125
+
115
126
  # Check state holidays if UF is provided
116
127
  if uf
117
128
  state_uf = uf.to_s.upcase
@@ -138,6 +149,7 @@ module BrazilianUtils
138
149
  # convert_date_to_text("15/03/2024") #=> "Quinze de março de dois mil e vinte e quatro"
139
150
  # convert_date_to_text("invalid") #=> nil
140
151
  def self.convert_date_to_text(date)
152
+ return nil unless date.is_a?(String)
141
153
  return nil unless DATE_REGEX.match?(date)
142
154
 
143
155
  begin
@@ -1,14 +1,25 @@
1
1
  module BrazilianUtils
2
2
  module EmailUtils
3
- # Email validation pattern based on RFC 5322
4
- # This pattern validates:
5
- # - Email must not start with a dot
6
- # - Local part (before @): alphanumeric, dots, underscores, percent, plus, minus
7
- # - @ symbol required
8
- # - Domain part: alphanumeric, dots, hyphens
9
- # - Dot separator required
10
- # - TLD: at least 2 alphabetic characters
11
- EMAIL_PATTERN = /\A(?![.])[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\z/.freeze
3
+ # Email validation pattern based on RFC 5322/RFC 1035.
4
+ #
5
+ # This is stricter than brutils/python's reference regex
6
+ # (`^(?![.])[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`), which lets
7
+ # through addresses with a leading/trailing/doubled dot in the local
8
+ # part, or a domain label starting/ending with a hyphen. Each dot-joined
9
+ # segment is validated on its own:
10
+ # - Local part: one or more non-empty segments (letters, digits,
11
+ # `._%+-`) joined by single dots - no leading/trailing/consecutive dot
12
+ # - Domain labels: alphanumeric, may contain internal hyphens, but must
13
+ # not start or end with one (RFC 1035 "preferred name syntax")
14
+ # - TLD: at least 2 letters, no digits or hyphens
15
+ EMAIL_PATTERN = %r{
16
+ \A
17
+ [a-zA-Z0-9_%+-]+ (?: \. [a-zA-Z0-9_%+-]+ )* # local part
18
+ @
19
+ (?: [a-zA-Z0-9] (?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])? \. )+ # domain labels
20
+ [a-zA-Z]{2,63} # TLD
21
+ \z
22
+ }x.freeze
12
23
 
13
24
  # Checks if a string corresponds to a valid email address.
14
25
  #
@@ -39,7 +39,7 @@ module BrazilianUtils
39
39
  '2046' => 'Sociedade Anônima Aberta',
40
40
  '2054' => 'Sociedade Anônima Fechada',
41
41
  '2062' => 'Sociedade Empresária Limitada',
42
- '2070' => 'Sociedade Empresária em Nome Coletivo',
42
+ '2076' => 'Sociedade Empresária em Nome Coletivo',
43
43
  '2089' => 'Sociedade Empresária em Comandita Simples',
44
44
  '2097' => 'Sociedade Empresária em Comandita por Ações',
45
45
  '2100' => 'Sociedade Mercantil de Capital e Indústria (extinta pelo NCC/2002)',
@@ -177,7 +177,7 @@ module BrazilianUtils
177
177
  # #=> "Sociedade Empresária Limitada"
178
178
  #
179
179
  # all_codes.size
180
- # #=> 64 (total number of codes in the official table)
180
+ # #=> 60 (total number of codes in the official table)
181
181
  def self.list_all
182
182
  LEGAL_NATURE.dup
183
183
  end
@@ -177,7 +177,9 @@ module BrazilianUtils
177
177
 
178
178
  # Calculates the checksum (verification digits) for a legal process ID.
179
179
  #
180
- # The checksum is calculated as: 97 - ((basenum * 100) % 97), padded to 2 digits.
180
+ # The checksum is calculated as: 98 - ((basenum * 100) % 97), padded to 2 digits.
181
+ # This is the ISO 7064 MOD 97-10 check digit used by CNJ Resolution 65/2008,
182
+ # art. 1º, § 2º.
181
183
  #
182
184
  # @param basenum [Integer] The base number for checksum calculation
183
185
  # (without the verification digits)
@@ -186,7 +188,7 @@ module BrazilianUtils
186
188
  #
187
189
  # @private
188
190
  def self.checksum(basenum)
189
- result = 97 - ((basenum * 100) % 97)
191
+ result = 98 - ((basenum * 100) % 97)
190
192
  result.to_s.rjust(2, '0')
191
193
  end
192
194
 
@@ -36,9 +36,11 @@ module BrazilianUtils
36
36
  # convert_to_mercosul("ABC4*67")
37
37
  # #=> nil
38
38
  def self.convert_to_mercosul(license_plate)
39
- return nil unless valid_old_format?(license_plate)
39
+ return nil unless license_plate.is_a?(String)
40
40
 
41
41
  clean = remove_symbols(license_plate).upcase
42
+ return nil unless valid_old_format?(clean)
43
+
42
44
  chars = clean.chars
43
45
 
44
46
  # Convert the 5th character (index 4) - the first digit after the letters
@@ -223,6 +225,8 @@ module BrazilianUtils
223
225
  # generate('invalid')
224
226
  # #=> nil
225
227
  def self.generate(format = 'LLLNLNN')
228
+ return nil unless format.is_a?(String)
229
+
226
230
  format_upper = format.upcase
227
231
 
228
232
  return nil unless ['LLLNLNN', 'LLLNNNN'].include?(format_upper)
@@ -181,9 +181,17 @@ module BrazilianUtils
181
181
  def self.remove_international_dialing_code(phone_number)
182
182
  return '' unless phone_number.is_a?(String)
183
183
 
184
- # Check if pattern matches and length (without spaces) is > 11
185
- if INTERNATIONAL_CODE_PATTERN.match?(phone_number) && phone_number.gsub(' ', '').length > 11
186
- phone_number.sub('55', '')
184
+ # Only touch a "clean" digit string (with an optional leading '+') that
185
+ # is longer than 11 digits; anything with spaces/hyphens/etc. is left
186
+ # alone rather than partially stripped.
187
+ digits_part = phone_number.sub(/\A\+/, '')
188
+
189
+ if INTERNATIONAL_CODE_PATTERN.match?(phone_number) &&
190
+ digits_part.match?(/\A\d+\z/) && digits_part.length > 11
191
+ # Anchor to the start so only the leading "+55"/"55" is stripped
192
+ # (a plain #sub would also drop the '+' and could hit an unrelated
193
+ # "55" further into the number, e.g. an RS-state "55" DDD).
194
+ phone_number.sub(/\A\+?55/, '')
187
195
  else
188
196
  phone_number
189
197
  end
@@ -13,13 +13,11 @@ module BrazilianUtils
13
13
  'AP' => '25', 'RR' => '26', 'TO' => '27', 'ZZ' => '28'
14
14
  }.freeze
15
15
 
16
- module_function
17
-
18
16
  # Check if a Brazilian voter ID number is valid.
19
17
  #
20
18
  # @param voter_id [String] The voter ID to validate
21
19
  # @return [Boolean] true if valid, false otherwise
22
- def is_valid_voter_id(voter_id)
20
+ def self.is_valid_voter_id(voter_id)
23
21
  # Ensure voter_id is a string with only digits and valid length
24
22
  return false unless voter_id.is_a?(String)
25
23
  return false unless voter_id.match?(/^\d+$/)
@@ -44,27 +42,37 @@ module BrazilianUtils
44
42
  true
45
43
  end
46
44
 
47
- # Alias for is_valid_voter_id
48
- alias valid_voter_id? is_valid_voter_id
45
+ class << self
46
+ # Alias for is_valid_voter_id
47
+ alias valid_voter_id? is_valid_voter_id
48
+ end
49
49
 
50
50
  # Format a voter ID for display with visual spaces.
51
51
  #
52
52
  # @param voter_id [String] The voter ID to format
53
53
  # @return [String, nil] Formatted voter ID or nil if invalid
54
- def format_voter_id(voter_id)
54
+ def self.format_voter_id(voter_id)
55
55
  return nil unless is_valid_voter_id(voter_id)
56
56
 
57
- "#{voter_id[0..3]} #{voter_id[4..7]} #{voter_id[8..9]} #{voter_id[10..11]}"
57
+ # The 13-digit SP/MG variant has an extra sequential digit; grouping it
58
+ # as 4+4+2+2 (like the 12-digit case) silently drops the last digit.
59
+ if voter_id.length == 13
60
+ "#{voter_id[0..3]} #{voter_id[4..7]} #{voter_id[8]} #{voter_id[9..10]} #{voter_id[11..12]}"
61
+ else
62
+ "#{voter_id[0..3]} #{voter_id[4..7]} #{voter_id[8..9]} #{voter_id[10..11]}"
63
+ end
58
64
  end
59
65
 
60
- # Alias for format_voter_id
61
- alias format format_voter_id
66
+ class << self
67
+ # Alias for format_voter_id
68
+ alias format format_voter_id
69
+ end
62
70
 
63
71
  # Generate a random valid Brazilian voter ID.
64
72
  #
65
73
  # @param federative_union [String] The UF code (e.g., "SP", "MG", "ZZ")
66
74
  # @return [String, nil] A valid voter ID or nil if UF is invalid
67
- def generate(federative_union = 'ZZ')
75
+ def self.generate(federative_union = 'ZZ')
68
76
  federative_union = federative_union.upcase
69
77
  return nil unless UF_CODES.key?(federative_union)
70
78
 
@@ -72,7 +80,9 @@ module BrazilianUtils
72
80
  return nil unless is_federative_union_valid?(uf_number)
73
81
 
74
82
  # Generate random 8-digit sequential number
75
- sequential_number = format('%08d', rand(0..99_999_999))
83
+ # (uses rjust, not Kernel#format, since `format` is aliased to
84
+ # format_voter_id on this module's singleton class below)
85
+ sequential_number = rand(0..99_999_999).to_s.rjust(8, '0')
76
86
 
77
87
  # Calculate verification digits
78
88
  vd1 = calculate_vd1(sequential_number, uf_number)
@@ -81,11 +91,12 @@ module BrazilianUtils
81
91
  "#{sequential_number}#{uf_number}#{vd1}#{vd2}"
82
92
  end
83
93
 
84
- private
94
+ # PRIVATE METHODS
95
+ #################
85
96
 
86
97
  # Check if the length of the voter ID is valid.
87
98
  # Typically 12 digits, but can be 13 for SP and MG (edge case).
88
- def is_length_valid?(voter_id)
99
+ def self.is_length_valid?(voter_id)
89
100
  return true if voter_id.length == 12
90
101
 
91
102
  # Edge case: SP and MG can have 13 digits
@@ -94,22 +105,22 @@ module BrazilianUtils
94
105
  end
95
106
 
96
107
  # Extract the sequential number (first 8 digits).
97
- def get_sequential_number(voter_id)
108
+ def self.get_sequential_number(voter_id)
98
109
  voter_id[0..7]
99
110
  end
100
111
 
101
112
  # Extract the federative union (2 digits before last 2 digits).
102
- def get_federative_union(voter_id)
113
+ def self.get_federative_union(voter_id)
103
114
  voter_id[-4..-3]
104
115
  end
105
116
 
106
117
  # Extract the verifying digits (last 2 digits).
107
- def get_verifying_digits(voter_id)
118
+ def self.get_verifying_digits(voter_id)
108
119
  voter_id[-2..-1]
109
120
  end
110
121
 
111
122
  # Check if the federative union is valid (between '01' and '28').
112
- def is_federative_union_valid?(federative_union)
123
+ def self.is_federative_union_valid?(federative_union)
113
124
  num = federative_union.to_i
114
125
  num >= 1 && num <= 28
115
126
  end
@@ -119,7 +130,7 @@ module BrazilianUtils
119
130
  # @param sequential_number [String] First 8 digits
120
131
  # @param federative_union [String] 2-digit UF code
121
132
  # @return [Integer] The first verification digit
122
- def calculate_vd1(sequential_number, federative_union)
133
+ def self.calculate_vd1(sequential_number, federative_union)
123
134
  # Weights: 2, 3, 4, 5, 6, 7, 8, 9
124
135
  weights = (2..9).to_a
125
136
 
@@ -144,7 +155,7 @@ module BrazilianUtils
144
155
  # @param federative_union [String] 2-digit UF code
145
156
  # @param vd1 [Integer] First verification digit
146
157
  # @return [Integer] The second verification digit
147
- def calculate_vd2(federative_union, vd1)
158
+ def self.calculate_vd2(federative_union, vd1)
148
159
  # Weights: 7, 8, 9
149
160
  sum = (federative_union[0].to_i * 7) +
150
161
  (federative_union[1].to_i * 8) +
@@ -161,5 +172,9 @@ module BrazilianUtils
161
172
 
162
173
  vd2
163
174
  end
175
+
176
+ private_class_method :is_length_valid?, :get_sequential_number, :get_federative_union,
177
+ :get_verifying_digits, :is_federative_union_valid?, :calculate_vd1,
178
+ :calculate_vd2
164
179
  end
165
180
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: br-utils
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Rocha
@@ -10,6 +10,20 @@ bindir: bin
10
10
  cert_chain: []
11
11
  date: 1980-01-02 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bigdecimal
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: bundler
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -61,9 +75,9 @@ executables: []
61
75
  extensions: []
62
76
  extra_rdoc_files: []
63
77
  files:
78
+ - ".github/workflows/ci.yml"
64
79
  - ".gitignore"
65
80
  - ".rspec"
66
- - ".travis.yml"
67
81
  - Gemfile
68
82
  - LICENSE.txt
69
83
  - README.md
data/.travis.yml DELETED
@@ -1,5 +0,0 @@
1
- sudo: false
2
- language: ruby
3
- rvm:
4
- - 2.3.0
5
- before_install: gem install bundler -v 1.12.5