creditcard-identifier 2.0.1 → 2.2.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.
@@ -8,7 +8,34 @@ require_relative 'creditcard_identifier/brands_detailed'
8
8
  #
9
9
  # This module provides credit card validation using bin-cc data.
10
10
  module CreditcardIdentifier
11
- VERSION = '2.0.0'
11
+ VERSION = '2.1.0'
12
+
13
+ # Luhn lookup table for doubling digits
14
+ LUHN_LOOKUP = [0, 2, 4, 6, 8, 1, 3, 5, 7, 9].freeze
15
+
16
+ ##
17
+ # Validate a credit card number using the Luhn algorithm
18
+ #
19
+ # @param number [String] Credit card number (digits only)
20
+ # @return [Boolean] true if valid according to Luhn algorithm
21
+ # @raise [TypeError] if number is not a string
22
+ def self.luhn(number)
23
+ raise TypeError, 'Expected string input' unless number.is_a?(String)
24
+ return false if number.empty?
25
+
26
+ total = 0
27
+ x2 = true
28
+
29
+ (number.length - 1).downto(0) do |i|
30
+ value = number[i].ord - 48
31
+ return false if value < 0 || value > 9
32
+
33
+ x2 = !x2
34
+ total += x2 ? LUHN_LOOKUP[value] : value
35
+ end
36
+
37
+ (total % 10).zero?
38
+ end
12
39
 
13
40
  ##
14
41
  # Credit card validator using bin-cc data.
@@ -31,11 +58,25 @@ module CreditcardIdentifier
31
58
  def find_brand(card_number, detailed: false)
32
59
  return nil if card_number.nil? || card_number.empty?
33
60
 
34
- brand = @brands.find do |b|
61
+ # Collect all matching brands
62
+ matching_brands = @brands.select do |b|
35
63
  b[:regexp_full].match?(card_number)
36
64
  end
37
65
 
38
- return nil unless brand
66
+ return nil if matching_brands.empty?
67
+
68
+ # Resolve priority: a brand with priority_over takes precedence
69
+ brand = matching_brands.first
70
+ if matching_brands.length > 1
71
+ matching_names = matching_brands.map { |b| b[:name] }
72
+ matching_brands.each do |candidate|
73
+ priority_over = candidate[:priority_over] || []
74
+ if priority_over.any? { |p| matching_names.include?(p) }
75
+ brand = candidate
76
+ break
77
+ end
78
+ end
79
+ end
39
80
 
40
81
  if detailed
41
82
  detailed_brand = @brands_detailed.find { |b| b[:scheme] == brand[:name] }
@@ -132,6 +173,15 @@ module CreditcardIdentifier
132
173
  def list_brands
133
174
  @brands.map { |b| b[:name] }
134
175
  end
176
+
177
+ ##
178
+ # Validate a credit card number using the Luhn algorithm
179
+ #
180
+ # @param number [String] Credit card number (digits only)
181
+ # @return [Boolean] true if valid according to Luhn algorithm
182
+ def luhn(number)
183
+ CreditcardIdentifier.luhn(number)
184
+ end
135
185
  end
136
186
 
137
187
  # Module-level convenience methods
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: creditcard-identifier
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Renato Viço
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-01-11 00:00:00.000000000 Z
11
+ date: 2026-01-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest