Module: SecID::Checkable

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

Overview

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

Including classes must implement:

  • calculate_checksum method that returns the calculated checksum value

This module provides:

  • Character-to-digit mapping constants
  • Luhn algorithm variants for checksum calculation
  • valid? override that validates format and checksum
  • restore method returning full identifier string without mutation
  • restore! method to calculate and set the checksum, returning self
  • checksum attribute
  • Class-level convenience methods: restore, restore!, checksum

Examples:

Including in an identifier class

class MyIdentifier < Base
  include Checkable

  def calculate_checksum
    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 checksum 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 checksum 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

Instance Method Summary collapse

Instance Method Details

#calculate_check_digitInteger, String

Deprecated.

Use #calculate_checksum. Kept as a v7 bridge; removed in v8.

Returns:

  • (Integer, String)

Raises:



154
155
156
157
# File 'lib/sec_id/concerns/checkable.rb', line 154

def calculate_check_digit
  SecID::Deprecation.warn(old: 'calculate_check_digit', new: 'calculate_checksum')
  calculate_checksum
end

#calculate_checksumInteger, String

Subclasses must override this method to implement their checksum algorithm.

Returns:

  • (Integer, String)

    the calculated checksum

Raises:

  • (NotImplementedError)

    if subclass doesn't implement

  • (InvalidFormatError)

    if the identifier format is invalid



133
134
135
# File 'lib/sec_id/concerns/checkable.rb', line 133

def calculate_checksum
  raise NotImplementedError
end

#check_digitInteger, String

Deprecated.

Use #checksum. Kept as a v7 bridge; removed in v8.

Returns:

  • (Integer, String)


145
146
147
148
# File 'lib/sec_id/concerns/checkable.rb', line 145

def check_digit
  SecID::Deprecation.warn(old: 'check_digit', new: 'checksum')
  checksum
end

#restoreString

Returns the full identifier string with correct checksum without mutation.

Returns:

  • (String)

    the full identifier with correct checksum

Raises:



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

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

#restore!self

Calculates and sets the checksum, updating full_id.

Returns:

  • (self)

Raises:



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

def restore!
  @checksum = calculate_checksum
  @full_id = to_s
  self
end

#to_sString

Returns:

  • (String)


138
139
140
# File 'lib/sec_id/concerns/checkable.rb', line 138

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

#valid?Boolean

Validates format and checksum.

Returns:

  • (Boolean)


106
107
108
# File 'lib/sec_id/concerns/checkable.rb', line 106

def valid?
  super && checksum == calculate_checksum
end