Module: SecID::Checkable

Included in:
CEI, CUSIP, FIGI, IBAN, ISIN, LEI, SEDOL
Defined in:
lib/sec_id/concerns/checkable.rb

Overview

Provides check-digit validation and calculation for securities identifiers. Include this module in classes that have a check digit as part of their format.

Including classes must implement:

  • calculate_check_digit method that returns the calculated check digit value

This module provides:

  • Character-to-digit mapping constants

  • Luhn algorithm variants for check-digit calculation

  • valid? override that validates format and check digit

  • restore method returning full identifier string without mutation

  • restore! method to calculate and set the check digit, returning self

  • check_digit attribute

  • Class-level convenience methods: restore, restore!, check_digit

Examples:

Including in an identifier class

class MyIdentifier < Base
  include Checkable

  def calculate_check_digit
    validate_format_for_calculation!
    mod10(luhn_sum_standard(reversed_digits_multi(identifier)))
  end
end

See Also:

Defined Under Namespace

Modules: ClassMethods

Constant Summary collapse

CHAR_TO_DIGITS =

Character-to-digit mapping for Luhn algorithm variants. Maps alphanumeric characters to digit arrays for multi-digit expansion. Used by ISIN for check-digit calculation.

{
  '0' => 0,      '1' => 1,      '2' => 2,      '3' => 3,      '4' => 4,
  '5' => 5,      '6' => 6,      '7' => 7,      '8' => 8,      '9' => 9,
  'A' => [1, 0], 'B' => [1, 1], 'C' => [1, 2], 'D' => [1, 3], 'E' => [1, 4],
  'F' => [1, 5], 'G' => [1, 6], 'H' => [1, 7], 'I' => [1, 8], 'J' => [1, 9],
  'K' => [2, 0], 'L' => [2, 1], 'M' => [2, 2], 'N' => [2, 3], 'O' => [2, 4],
  'P' => [2, 5], 'Q' => [2, 6], 'R' => [2, 7], 'S' => [2, 8], 'T' => [2, 9],
  'U' => [3, 0], 'V' => [3, 1], 'W' => [3, 2], 'X' => [3, 3], 'Y' => [3, 4], 'Z' => [3, 5],
  '*' => [3, 6], '@' => [3, 7], '#' => [3, 8]
}.freeze
CHAR_TO_DIGIT =

Character-to-digit mapping for single-digit conversion. Maps alphanumeric characters to values 0-38 (A=10, B=11, …, Z=35, *=36, @=37, #=38). Used by CUSIP, FIGI, SEDOL, LEI, and IBAN for check-digit calculations.

{
  '0' => 0,  '1' => 1,  '2' => 2,  '3' => 3,  '4' =>  4,
  '5' => 5,  '6' => 6,  '7' => 7,  '8' => 8,  '9' =>  9,
  'A' => 10, 'B' => 11, 'C' => 12, 'D' => 13, 'E' => 14,
  'F' => 15, 'G' => 16, 'H' => 17, 'I' => 18, 'J' => 19,
  'K' => 20, 'L' => 21, 'M' => 22, 'N' => 23, 'O' => 24,
  'P' => 25, 'Q' => 26, 'R' => 27, 'S' => 28, 'T' => 29,
  'U' => 30, 'V' => 31, 'W' => 32, 'X' => 33, 'Y' => 34, 'Z' => 35,
  '*' => 36, '@' => 37, '#' => 38
}.freeze

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(base) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

API:

  • private



60
61
62
63
# File 'lib/sec_id/concerns/checkable.rb', line 60

def self.included(base)
  base.attr_reader :check_digit
  base.extend(ClassMethods)
end

Instance Method Details

#calculate_check_digitInteger

Subclasses must override this method to implement their check-digit algorithm.

Returns:

  • the calculated check digit

Raises:

  • if subclass doesn’t implement

  • if the identifier format is invalid



123
124
125
# File 'lib/sec_id/concerns/checkable.rb', line 123

def calculate_check_digit
  raise NotImplementedError
end

#restoreString

Returns the full identifier string with correct check digit without mutation.

Returns:

  • the full identifier with correct check digit

Raises:

  • if the identifier format is invalid



104
105
106
# File 'lib/sec_id/concerns/checkable.rb', line 104

def restore
  "#{identifier}#{calculate_check_digit.to_s.rjust(check_digit_width, '0')}"
end

#restore!self

Calculates and sets the check digit, updating full_id.

Returns:

Raises:

  • if the identifier format is invalid



112
113
114
115
116
# File 'lib/sec_id/concerns/checkable.rb', line 112

def restore!
  @check_digit = calculate_check_digit
  @full_id = to_s
  self
end

#to_sString

Returns:



128
129
130
# File 'lib/sec_id/concerns/checkable.rb', line 128

def to_s
  "#{identifier}#{check_digit&.to_s&.rjust(check_digit_width, '0')}"
end

#valid?Boolean

Validates format and check digit.

Returns:



96
97
98
# File 'lib/sec_id/concerns/checkable.rb', line 96

def valid?
  super && check_digit == calculate_check_digit
end