Class: SecID::Base

Inherits:
Object
  • Object
show all
Includes:
Generatable, Normalizable, Validatable
Defined in:
lib/sec_id/base.rb

Overview

Base class for securities identifiers that provides a common interface for validation, normalization, and parsing.

Subclasses must implement:

  • ID_REGEX constant with named capture groups for parsing
  • initialize method that calls parse and extracts components

Subclasses with check digits should also include the Checkable concern, which provides check-digit validation, calculation, and restoration.

Examples:

Implementing a check-digit identifier

class MyIdentifier < Base
  include Checkable

  ID_REGEX = /\A(?<identifier>[A-Z]{6})(?<check_digit>\d)?\z/x

  def initialize(id)
    parts = parse(id)
    @identifier = parts[:identifier]
    @check_digit = parts[:check_digit]&.to_i
  end

  def calculate_check_digit
    validate_format_for_calculation!
    mod10(some_algorithm)
  end
end

Implementing a non-check-digit identifier

class SimpleId < Base
  ID_REGEX = /\A(?<identifier>[A-Z]{6})\z/x

  def initialize(id)
    parts = parse(id)
    @identifier = parts[:identifier]
  end
end

Direct Known Subclasses

BIC, CEI, CFI, CIK, CUSIP, DTI, FIGI, FISN, IBAN, ISIN, LEI, OCC, SEDOL, Valoren, WKN

Constant Summary

Constants included from Generatable

Generatable::ALPHA, Generatable::ALPHANUMERIC, Generatable::DIGITS

Constants included from Validatable

Validatable::ERROR_MAP

Constants included from Normalizable

Normalizable::SEPARATORS

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Validatable

#errors, #valid?, #validate, #validate!

Methods included from Normalizable

#normalize!, #normalized, #to_pretty_s, #to_s, #to_str

Constructor Details

#initialize(_sec_id_number) ⇒ Base

Subclasses must override this method.

Parameters:

  • _sec_id_number (String)

    the identifier string to parse

Raises:

  • (NotImplementedError)

    always raised in base class



111
112
113
# File 'lib/sec_id/base.rb', line 111

def initialize(_sec_id_number)
  raise NotImplementedError
end

Instance Attribute Details

#full_idString (readonly)

Returns the original input after normalization (stripped and uppercased).

Returns:

  • (String)

    the original input after normalization (stripped and uppercased)



47
48
49
# File 'lib/sec_id/base.rb', line 47

def full_id
  @full_id
end

#identifierString? (readonly)

Returns the main identifier portion (without check digit).

Returns:

  • (String, nil)

    the main identifier portion (without check digit)



50
51
52
# File 'lib/sec_id/base.rb', line 50

def identifier
  @identifier
end

Class Method Details

.detection_priorityArray

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.

Composite sort key ranking detection specificity: check-digit types first, then narrower length range, then registration order. A class the registry never saw sorts last.

Returns:

  • (Array)

    frozen [check-digit rank, length specificity, registration order]



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

def detection_priority
  @detection_priority ||=
    [has_check_digit? ? 0 : 1, length_specificity, SecID.identifiers.index(self) || Float::INFINITY].freeze
end

.exampleString

Returns a representative valid identifier string.

Returns:

  • (String)

    a representative valid identifier string



90
# File 'lib/sec_id/base.rb', line 90

def example = self::EXAMPLE

.full_nameString

Returns the full human-readable standard name.

Returns:

  • (String)

    the full human-readable standard name



73
# File 'lib/sec_id/base.rb', line 73

def full_name = self::FULL_NAME

.has_check_digit?Boolean

Returns true if this identifier type uses a check digit.

Returns:

  • (Boolean)

    true if this identifier type uses a check digit



93
94
95
96
97
# File 'lib/sec_id/base.rb', line 93

def has_check_digit?
  return @has_check_digit if defined?(@has_check_digit)

  @has_check_digit = ancestors.include?(SecID::Checkable)
end

.id_lengthInteger, ...

Returns the fixed length, valid length range, or discrete valid lengths.

Returns:

  • (Integer, Range, Array<Integer>)

    the fixed length, valid length range, or discrete valid lengths



76
# File 'lib/sec_id/base.rb', line 76

def id_length = self::ID_LENGTH

.length_specificityInteger

Specificity weight from ID_LENGTH: fewer valid lengths ranks more specific.

Returns:

  • (Integer)


87
# File 'lib/sec_id/base.rb', line 87

def length_specificity = (v = self::ID_LENGTH).is_a?(Integer) ? 1 : v.size

.length_valuesArray<Integer>, Range

Valid length values, for length-table indexing. Integer wraps to a one-element Array; Range and Array both yield their own elements.

Returns:

  • (Array<Integer>, Range)


82
# File 'lib/sec_id/base.rb', line 82

def length_values = (v = self::ID_LENGTH).is_a?(Integer) ? [v] : v

.short_nameString

Returns the unqualified class name (e.g. "ISIN", "CUSIP").

Returns:

  • (String)

    the unqualified class name (e.g. "ISIN", "CUSIP")



70
# File 'lib/sec_id/base.rb', line 70

def short_name = @short_name ||= name.split('::').last

.type_keySymbol

The type's registry symbol: SecID[SecID::ISIN.type_key] == SecID::ISIN.

Returns:

  • (Symbol)

    the registry key (e.g. :isin, :cusip)



56
# File 'lib/sec_id/base.rb', line 56

def type_key = @type_key ||= short_name.downcase.to_sym

Instance Method Details

#==(other) ⇒ Boolean Also known as: eql?

Parameters:

  • other (Object)

Returns:

  • (Boolean)


117
118
119
# File 'lib/sec_id/base.rb', line 117

def ==(other)
  other.class == self.class && comparison_id == other.comparison_id
end

#as_jsonHash

Returns a JSON-compatible hash representation.

Returns:

  • (Hash)


144
145
146
# File 'lib/sec_id/base.rb', line 144

def as_json(*)
  to_h
end

#deconstruct_keys(_keys) ⇒ Hash

Exposes the parsed components for case/in destructuring. Validity is not part of the protocol: components of unparseable input are nil, and SecID.parse returning nil is the validity guard.

Examples:

case SecID.parse('US5949181045')
in SecID::ISIN[country_code:, nsin:] then [country_code, nsin]
in nil then :invalid
end #=> ['US', '594918104']

Parameters:

  • _keys (Array<Symbol>, nil)

    the keys the pattern requests; ignored

Returns:

  • (Hash)

    the parsed components



160
# File 'lib/sec_id/base.rb', line 160

def deconstruct_keys(_keys) = components

#hashInteger

Returns:

  • (Integer)


124
125
126
# File 'lib/sec_id/base.rb', line 124

def hash
  [self.class, comparison_id].hash
end

#to_hHash

Returns a hash representation of this identifier for serialization.

Returns:

  • (Hash)

    hash with :type, :full_id, :normalized, :valid, and :components keys



131
132
133
134
135
136
137
138
139
# File 'lib/sec_id/base.rb', line 131

def to_h
  {
    type: self.class.type_key,
    full_id: full_id,
    normalized: valid? ? normalized : nil,
    valid: valid?,
    components: components
  }
end