Class: BankTools::SE::OCR
- Inherits:
-
Object
- Object
- BankTools::SE::OCR
- Defined in:
- lib/banktools-se/ocr.rb
Defined Under Namespace
Classes: BadChecksum, BadLengthDigit, BadPadding, InvalidOCR, MustBeNumeric, OverlongOCR, TooShortOCR
Constant Summary collapse
- MIN_LENGTH =
2
- MAX_LENGTH =
25
Class Method Summary collapse
-
.find_all_in_string(string, length_digit: false, pad: "", min_length: 4, max_length: 18) ⇒ Object
max_length is 18, because the biggest allowed integer by default in a Postgres integer column (“bigint”) is 19 digits long, as is the next (disallowed) number.
- .from_number(number, length_digit: false, pad: "") ⇒ Object
- .to_number(ocr, length_digit: false, pad: "") ⇒ Object
Class Method Details
.find_all_in_string(string, length_digit: false, pad: "", min_length: 4, max_length: 18) ⇒ Object
max_length is 18, because the biggest allowed integer by default in a Postgres integer column (“bigint”) is 19 digits long, as is the next (disallowed) number. Attempting some queries with longer OCRs may cause Ruby on Rails exceptions.
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/banktools-se/ocr.rb', line 68 def self.find_all_in_string(string, length_digit: false, pad: "", min_length: 4, max_length: 18) # First, treat the input as one long string of digits. # E.g. "1234 and 5678" becomes "12345678". digit_string = string.gsub(/\D/, "") # Then find all substrings ("n-grams") of min_length, and of all other lengths, up to max_length. # So e.g. find all four-digit substrings ("1234", "2345", …), all five-digit substrings and so on. digit_string_length = digit_string.length candidates = [] 0.upto(digit_string.length - min_length) do |start_pos| min_end_pos = start_pos + min_length - 1 max_end_pos = [ start_pos + max_length, digit_string_length ].min - 1 min_end_pos.upto(max_end_pos) do |end_pos| candidates << digit_string.slice(start_pos..end_pos) end end # Get rid of any duplicates. candidates = candidates.uniq # Finally, limit these substrings to ones that are actually valid OCRs. candidates.select { |candidate| begin to_number(candidate, length_digit: length_digit, pad: pad) true rescue InvalidOCR false end } end |
.from_number(number, length_digit: false, pad: "") ⇒ Object
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
# File 'lib/banktools-se/ocr.rb', line 17 def self.from_number(number, length_digit: false, pad: "") number = number.to_s add_length_digit = length_digit pad = pad.to_s raise MustBeNumeric unless number.match(/\A\d+\z/) # Padding isn't something BGC specifies, but we needed it to support a legacy scheme. number += pad # Adding 2: 1 length digit, 1 check digit. number += ((number.length + 2) % 10).to_s if add_length_digit number_with_ocr = number + Utils.luhn_checksum(number).to_s length = number_with_ocr.length if length > MAX_LENGTH raise OverlongOCR, "OCR must be #{MIN_LENGTH} - #{MAX_LENGTH} characters (this one would be #{length} characters)" end number_with_ocr end |
.to_number(ocr, length_digit: false, pad: "") ⇒ Object
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/banktools-se/ocr.rb', line 38 def self.to_number(ocr, length_digit: false, pad: "") ocr = ocr.to_s should_have_length_digit = length_digit strip_padding = pad.to_s raise MustBeNumeric unless ocr.match(/\A\d+\z/) raise BadChecksum unless Utils.valid_luhn?(ocr) raise TooShortOCR if ocr.length < MIN_LENGTH if should_have_length_digit length_digit = ocr[-2] last_digit_of_actual_length = ocr.length.to_s[-1] raise BadLengthDigit if length_digit != last_digit_of_actual_length end digits_to_chop = 1 # Checksum. digits_to_chop += 1 if should_have_length_digit if strip_padding.length > 0 expected_padding_end = -digits_to_chop - 1 expected_padding_start = expected_padding_end - strip_padding.length + 1 raise BadPadding if ocr[expected_padding_start..expected_padding_end] != strip_padding end digits_to_chop += strip_padding.length ocr[0...-digits_to_chop] end |