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

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(base) ⇒ void

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.

This method returns an undefined value.

Extends the including identifier class with the concern's class methods and checksum reader.

Parameters:

  • base (Class)

    the identifier class including this concern



64
65
66
67
# File 'lib/sec_id/concerns/checkable.rb', line 64

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

Instance Method Details

#calculate_check_digitInteger, String

Deprecated.

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

Returns:

  • (Integer, String)

Raises:



158
159
160
161
# File 'lib/sec_id/concerns/checkable.rb', line 158

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



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

def calculate_checksum
  raise NotImplementedError
end

#check_digitInteger, String

Deprecated.

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

Returns:

  • (Integer, String)


149
150
151
152
# File 'lib/sec_id/concerns/checkable.rb', line 149

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:



118
119
120
# File 'lib/sec_id/concerns/checkable.rb', line 118

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:



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

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

#to_sString

Returns:

  • (String)


142
143
144
# File 'lib/sec_id/concerns/checkable.rb', line 142

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

#valid?Boolean

Validates format and checksum.

Returns:

  • (Boolean)


110
111
112
# File 'lib/sec_id/concerns/checkable.rb', line 110

def valid?
  super && checksum == calculate_checksum
end