sec_id 5.2.0 → 6.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.yardopts +6 -0
- data/CHANGELOG.md +31 -0
- data/MIGRATION.md +101 -0
- data/README.md +245 -17
- data/lib/sec_id/active_model.rb +144 -0
- data/lib/sec_id/base.rb +64 -2
- data/lib/sec_id/bic/country_codes.rb +46 -0
- data/lib/sec_id/bic.rb +123 -0
- data/lib/sec_id/cei.rb +19 -7
- data/lib/sec_id/cfi/attribute_set.rb +49 -0
- data/lib/sec_id/cfi/classification.rb +107 -0
- data/lib/sec_id/cfi/field.rb +56 -0
- data/lib/sec_id/cfi/tables.rb +1033 -0
- data/lib/sec_id/cfi.rb +138 -208
- data/lib/sec_id/cik.rb +13 -0
- data/lib/sec_id/concerns/checkable.rb +2 -2
- data/lib/sec_id/concerns/generatable.rb +71 -0
- data/lib/sec_id/concerns/normalizable.rb +1 -0
- data/lib/sec_id/concerns/validatable.rb +14 -3
- data/lib/sec_id/cusip.rb +18 -7
- data/lib/sec_id/deep_freeze.rb +18 -0
- data/lib/sec_id/detector.rb +29 -31
- data/lib/sec_id/dti.rb +104 -0
- data/lib/sec_id/figi.rb +18 -0
- data/lib/sec_id/fisn.rb +31 -0
- data/lib/sec_id/iban/country_rules.rb +7 -6
- data/lib/sec_id/iban.rb +19 -1
- data/lib/sec_id/isin.rb +20 -15
- data/lib/sec_id/lei.rb +13 -0
- data/lib/sec_id/occ.rb +21 -0
- data/lib/sec_id/railtie.rb +14 -0
- data/lib/sec_id/scanner.rb +18 -27
- data/lib/sec_id/sedol.rb +16 -0
- data/lib/sec_id/valoren.rb +13 -0
- data/lib/sec_id/version.rb +2 -1
- data/lib/sec_id/wkn.rb +16 -0
- data/lib/sec_id.rb +31 -7
- data/sec_id.gemspec +11 -5
- data/sig/manifest.yaml +8 -0
- data/sig/sec_id/base.rbs +78 -0
- data/sig/sec_id/bic/country_codes.rbs +5 -0
- data/sig/sec_id/bic.rbs +30 -0
- data/sig/sec_id/cei.rbs +26 -0
- data/sig/sec_id/cfi/attribute_set.rbs +68 -0
- data/sig/sec_id/cfi/classification.rbs +23 -0
- data/sig/sec_id/cfi/field.rbs +337 -0
- data/sig/sec_id/cfi/tables.rbs +68 -0
- data/sig/sec_id/cfi.rbs +56 -0
- data/sig/sec_id/cik.rbs +19 -0
- data/sig/sec_id/concerns/checkable.rbs +41 -0
- data/sig/sec_id/concerns/generatable.rbs +26 -0
- data/sig/sec_id/concerns/normalizable.rbs +34 -0
- data/sig/sec_id/concerns/validatable.rbs +42 -0
- data/sig/sec_id/cusip.rbs +29 -0
- data/sig/sec_id/deep_freeze.rbs +6 -0
- data/sig/sec_id/detector.rbs +29 -0
- data/sig/sec_id/dti.rbs +27 -0
- data/sig/sec_id/errors.rbs +28 -0
- data/sig/sec_id/figi.rbs +31 -0
- data/sig/sec_id/fisn.rbs +24 -0
- data/sig/sec_id/iban/country_rules.rbs +10 -0
- data/sig/sec_id/iban.rbs +54 -0
- data/sig/sec_id/isin.rbs +40 -0
- data/sig/sec_id/lei.rbs +33 -0
- data/sig/sec_id/occ.rbs +45 -0
- data/sig/sec_id/scanner.rbs +44 -0
- data/sig/sec_id/sedol.rbs +34 -0
- data/sig/sec_id/valoren.rbs +29 -0
- data/sig/sec_id/version.rbs +3 -0
- data/sig/sec_id/wkn.rbs +15 -0
- data/sig/sec_id.rbs +58 -0
- metadata +55 -7
- data/lib/sec_id/concerns/identifier_metadata.rb +0 -56
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module SecID
|
|
2
|
+
# Detects which identifier types match a given string.
|
|
3
|
+
#
|
|
4
|
+
# @api private
|
|
5
|
+
class Detector
|
|
6
|
+
def initialize: (Array[singleton(Base)] identifier_list) -> void
|
|
7
|
+
def call: (SecID::input str) -> Array[Symbol]
|
|
8
|
+
def matches?: (SecID::input str) -> bool
|
|
9
|
+
def first_match: (SecID::input str) -> Base?
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def filter_candidates: (String upcased) -> Array[singleton(Base)]
|
|
14
|
+
def validate_and_sort: (String input, Array[untyped] candidates) -> Array[Symbol]
|
|
15
|
+
def precompute: () -> void
|
|
16
|
+
def build_discriminator_sets: () -> void
|
|
17
|
+
def build_length_table: () -> void
|
|
18
|
+
def stage1_special_chars: (String upcased) -> Array[singleton(Base)]?
|
|
19
|
+
def stage2_length: (Integer length) -> Array[singleton(Base)]
|
|
20
|
+
def stage3_charset: (String upcased, Array[singleton(Base)] candidates) -> Array[singleton(Base)]
|
|
21
|
+
def accepts_char?: (singleton(Base) klass, String char) -> bool
|
|
22
|
+
|
|
23
|
+
@classes: Array[singleton(Base)]
|
|
24
|
+
@slash_types: Array[singleton(Base)]
|
|
25
|
+
@space_only_types: Array[singleton(Base)]
|
|
26
|
+
@special_types: Array[singleton(Base)]
|
|
27
|
+
@candidates_by_length: Hash[Integer, Array[singleton(Base)]]
|
|
28
|
+
end
|
|
29
|
+
end
|
data/sig/sec_id/dti.rbs
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module SecID
|
|
2
|
+
# Digital Token Identifier (DTI, ISO 24165).
|
|
3
|
+
class DTI < Base
|
|
4
|
+
include Checkable
|
|
5
|
+
extend Checkable::ClassMethods
|
|
6
|
+
|
|
7
|
+
FULL_NAME: String
|
|
8
|
+
ID_LENGTH: Integer
|
|
9
|
+
EXAMPLE: String
|
|
10
|
+
VALID_CHARS_REGEX: ::Regexp
|
|
11
|
+
ID_REGEX: ::Regexp
|
|
12
|
+
ALPHABET: Array[String]
|
|
13
|
+
ALPHABET_VALUE: Hash[String, Integer]
|
|
14
|
+
GENERATE_CHARSET: Array[String]
|
|
15
|
+
GRANDFATHERED_CODES: Hash[String, String]
|
|
16
|
+
|
|
17
|
+
def initialize: (SecID::input dti) -> void
|
|
18
|
+
def calculate_check_digit: () -> String
|
|
19
|
+
private def self.generate_body: (Random random) -> String
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def components: () -> Hash[Symbol, untyped]
|
|
24
|
+
def grandfathered_check_digit: (untyped base) -> String?
|
|
25
|
+
def iso7064_mod31_30_check_char: (untyped base) -> String
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module SecID
|
|
2
|
+
# Immutable value object representing validation errors for an identifier.
|
|
3
|
+
class Errors
|
|
4
|
+
# A single error entry. `{ error:, message: }` in the source, but `Hash#freeze`
|
|
5
|
+
# in `build_error` widens the record literal, so it is modelled as a Hash.
|
|
6
|
+
type detail = Hash[Symbol, untyped]
|
|
7
|
+
|
|
8
|
+
attr_reader details: Array[detail]
|
|
9
|
+
|
|
10
|
+
def initialize: (Array[detail] errors) -> void
|
|
11
|
+
|
|
12
|
+
def messages: () -> Array[String?]
|
|
13
|
+
|
|
14
|
+
def any?: () -> bool
|
|
15
|
+
|
|
16
|
+
def empty?: () -> bool
|
|
17
|
+
|
|
18
|
+
alias none? empty?
|
|
19
|
+
|
|
20
|
+
def size: () -> Integer
|
|
21
|
+
|
|
22
|
+
def each: () { (detail) -> void } -> Array[detail]
|
|
23
|
+
|
|
24
|
+
def to_a: () -> Array[String?]
|
|
25
|
+
|
|
26
|
+
def as_json: (*untyped) -> Array[detail]
|
|
27
|
+
end
|
|
28
|
+
end
|
data/sig/sec_id/figi.rbs
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module SecID
|
|
2
|
+
# Financial Instrument Global Identifier (FIGI).
|
|
3
|
+
class FIGI < Base
|
|
4
|
+
include Checkable
|
|
5
|
+
extend Checkable::ClassMethods
|
|
6
|
+
|
|
7
|
+
FULL_NAME: String
|
|
8
|
+
ID_LENGTH: Integer
|
|
9
|
+
EXAMPLE: String
|
|
10
|
+
VALID_CHARS_REGEX: ::Regexp
|
|
11
|
+
ID_REGEX: ::Regexp
|
|
12
|
+
RESTRICTED_PREFIXES: Set[String]
|
|
13
|
+
GENERATE_CHARSET: Array[String]
|
|
14
|
+
|
|
15
|
+
attr_reader prefix: String?
|
|
16
|
+
attr_reader random_part: String?
|
|
17
|
+
|
|
18
|
+
def initialize: (SecID::input figi) -> void
|
|
19
|
+
def to_pretty_s: () -> String?
|
|
20
|
+
def calculate_check_digit: () -> Integer
|
|
21
|
+
def self.check_digit: (SecID::input id) -> Integer
|
|
22
|
+
private def self.generate_body: (Random random) -> String
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def components: () -> Hash[Symbol, untyped]
|
|
27
|
+
def valid_format?: () -> bool
|
|
28
|
+
def detect_errors: () -> Array[Symbol]
|
|
29
|
+
def validation_message: (Symbol code) -> String?
|
|
30
|
+
end
|
|
31
|
+
end
|
data/sig/sec_id/fisn.rbs
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module SecID
|
|
2
|
+
# Financial Instrument Short Name (FISN).
|
|
3
|
+
class FISN < Base
|
|
4
|
+
FULL_NAME: String
|
|
5
|
+
ID_LENGTH: ::Range[Integer]
|
|
6
|
+
EXAMPLE: String
|
|
7
|
+
VALID_CHARS_REGEX: ::Regexp
|
|
8
|
+
SEPARATORS: ::Regexp
|
|
9
|
+
ID_REGEX: ::Regexp
|
|
10
|
+
FISN_CHARSET: Array[String]
|
|
11
|
+
|
|
12
|
+
attr_reader issuer: String?
|
|
13
|
+
attr_reader description: String?
|
|
14
|
+
|
|
15
|
+
def initialize: (SecID::input fisn) -> void
|
|
16
|
+
def to_s: () -> String
|
|
17
|
+
private def self.generate_body: (Random random) -> String
|
|
18
|
+
private def self.generate_part: (Integer length, Random random) -> String
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def components: () -> Hash[Symbol, untyped]
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module SecID
|
|
2
|
+
class IBAN < Base
|
|
3
|
+
# `{ length: Integer, format: Regexp, components?: {name => [start, length]} }`, but
|
|
4
|
+
# the nested literal infers as a broad Hash, so it is modelled loosely.
|
|
5
|
+
type rule = Hash[Symbol, untyped]
|
|
6
|
+
|
|
7
|
+
COUNTRY_RULES: Hash[String, rule]
|
|
8
|
+
LENGTH_ONLY_COUNTRIES: Hash[String, Integer]
|
|
9
|
+
end
|
|
10
|
+
end
|
data/sig/sec_id/iban.rbs
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
module SecID
|
|
2
|
+
# International Bank Account Number (IBAN).
|
|
3
|
+
class IBAN < Base
|
|
4
|
+
include Checkable
|
|
5
|
+
extend Checkable::ClassMethods
|
|
6
|
+
|
|
7
|
+
FULL_NAME: String
|
|
8
|
+
ID_LENGTH: ::Range[Integer]
|
|
9
|
+
EXAMPLE: String
|
|
10
|
+
VALID_CHARS_REGEX: ::Regexp
|
|
11
|
+
ID_REGEX: ::Regexp
|
|
12
|
+
NUMERIC_COUNTRY_RULES: Array[[String, rule]]
|
|
13
|
+
|
|
14
|
+
def self.supported_countries: () -> Array[String]
|
|
15
|
+
|
|
16
|
+
# country_code/bban are used unguarded after validation; typed loosely so static
|
|
17
|
+
# Steep and runtime RBS::Test (which sees nil for invalid) agree.
|
|
18
|
+
attr_reader country_code: untyped
|
|
19
|
+
attr_reader bban: untyped
|
|
20
|
+
# Optional BBAN components; typed loosely so `#components` (which conditionally
|
|
21
|
+
# assigns them into an inferred hash) type-checks.
|
|
22
|
+
attr_reader bank_code: untyped
|
|
23
|
+
attr_reader branch_code: untyped
|
|
24
|
+
attr_reader account_number: untyped
|
|
25
|
+
attr_reader national_check: untyped
|
|
26
|
+
|
|
27
|
+
def initialize: (SecID::input iban) -> void
|
|
28
|
+
def restore: () -> String
|
|
29
|
+
def calculate_check_digit: () -> Integer
|
|
30
|
+
def self.check_digit: (SecID::input id) -> Integer
|
|
31
|
+
private def self.generate_body: (Random random) -> String
|
|
32
|
+
def valid_bban_format?: () -> bool
|
|
33
|
+
def country_rule: () -> rule?
|
|
34
|
+
def known_country?: () -> bool
|
|
35
|
+
def to_s: () -> String
|
|
36
|
+
def to_pretty_s: () -> String?
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def components: () -> Hash[Symbol, untyped]
|
|
41
|
+
def check_digit_width: () -> Integer
|
|
42
|
+
def valid_format?: () -> bool
|
|
43
|
+
def detect_errors: () -> Array[Symbol]
|
|
44
|
+
def validation_message: (Symbol code) -> String?
|
|
45
|
+
def extract_check_digit_and_bban: (String rest) -> void
|
|
46
|
+
def expected_bban_length_for_country: () -> Integer?
|
|
47
|
+
def check_digits?: (untyped rest, Integer? expected_bban_length) -> bool
|
|
48
|
+
def extract_bban_components: () -> void
|
|
49
|
+
def valid_bban_length_only?: () -> bool
|
|
50
|
+
def numeric_string_for_check: () -> String
|
|
51
|
+
|
|
52
|
+
self.@supported_countries: Array[String]
|
|
53
|
+
end
|
|
54
|
+
end
|
data/sig/sec_id/isin.rbs
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module SecID
|
|
2
|
+
# International Securities Identification Number (ISIN).
|
|
3
|
+
class ISIN < Base
|
|
4
|
+
include Checkable
|
|
5
|
+
extend Checkable::ClassMethods
|
|
6
|
+
|
|
7
|
+
FULL_NAME: String
|
|
8
|
+
ID_LENGTH: Integer
|
|
9
|
+
EXAMPLE: String
|
|
10
|
+
VALID_CHARS_REGEX: ::Regexp
|
|
11
|
+
ID_REGEX: ::Regexp
|
|
12
|
+
CGS_COUNTRY_CODES: Set[String]
|
|
13
|
+
NSIN_COUNTRY_TYPES: Hash[String, Symbol]
|
|
14
|
+
|
|
15
|
+
# Used unguarded as non-nil after validation (indexed/looked-up); typed loosely
|
|
16
|
+
# so both static Steep and runtime RBS::Test (which sees nil for invalid) agree.
|
|
17
|
+
attr_reader country_code: untyped
|
|
18
|
+
attr_reader nsin: untyped
|
|
19
|
+
|
|
20
|
+
def initialize: (SecID::input isin) -> void
|
|
21
|
+
def to_pretty_s: () -> String?
|
|
22
|
+
def calculate_check_digit: () -> Integer
|
|
23
|
+
def self.check_digit: (SecID::input id) -> Integer
|
|
24
|
+
private def self.generate_body: (Random random) -> String
|
|
25
|
+
def to_cusip: () -> CUSIP
|
|
26
|
+
def cgs?: () -> bool
|
|
27
|
+
def sedol?: () -> bool
|
|
28
|
+
def wkn?: () -> bool
|
|
29
|
+
def valoren?: () -> bool
|
|
30
|
+
def to_sedol: () -> SEDOL
|
|
31
|
+
def to_wkn: () -> WKN
|
|
32
|
+
def to_valoren: () -> Valoren
|
|
33
|
+
def nsin_type: () -> Symbol
|
|
34
|
+
def to_nsin: () -> (CUSIP | SEDOL | WKN | Valoren | String | nil)
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def components: () -> Hash[Symbol, untyped]
|
|
39
|
+
end
|
|
40
|
+
end
|
data/sig/sec_id/lei.rbs
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module SecID
|
|
2
|
+
# Legal Entity Identifier (LEI).
|
|
3
|
+
class LEI < Base
|
|
4
|
+
include Checkable
|
|
5
|
+
extend Checkable::ClassMethods
|
|
6
|
+
|
|
7
|
+
FULL_NAME: String
|
|
8
|
+
ID_LENGTH: Integer
|
|
9
|
+
EXAMPLE: String
|
|
10
|
+
VALID_CHARS_REGEX: ::Regexp
|
|
11
|
+
ID_REGEX: ::Regexp
|
|
12
|
+
|
|
13
|
+
attr_reader lou_id: String?
|
|
14
|
+
attr_reader reserved: String?
|
|
15
|
+
attr_reader entity_id: String?
|
|
16
|
+
|
|
17
|
+
# Used unguarded after validation (`#numeric_identifier`); typed loosely so static
|
|
18
|
+
# Steep and runtime RBS::Test (which sees nil for invalid) agree.
|
|
19
|
+
def identifier: () -> untyped
|
|
20
|
+
|
|
21
|
+
def initialize: (SecID::input lei) -> void
|
|
22
|
+
def to_pretty_s: () -> String?
|
|
23
|
+
def calculate_check_digit: () -> Integer
|
|
24
|
+
def self.check_digit: (SecID::input id) -> Integer
|
|
25
|
+
private def self.generate_body: (Random random) -> String
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def components: () -> Hash[Symbol, untyped]
|
|
30
|
+
def check_digit_width: () -> Integer
|
|
31
|
+
def numeric_identifier: () -> String
|
|
32
|
+
end
|
|
33
|
+
end
|
data/sig/sec_id/occ.rbs
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module SecID
|
|
2
|
+
# OCC Option Symbol.
|
|
3
|
+
class OCC < Base
|
|
4
|
+
FULL_NAME: String
|
|
5
|
+
ID_LENGTH: ::Range[Integer]
|
|
6
|
+
EXAMPLE: String
|
|
7
|
+
VALID_CHARS_REGEX: ::Regexp
|
|
8
|
+
SEPARATORS: ::Regexp
|
|
9
|
+
ID_REGEX: ::Regexp
|
|
10
|
+
|
|
11
|
+
# Used unguarded as non-nil after validation; typed loosely so static Steep and
|
|
12
|
+
# runtime RBS::Test (which sees nil for invalid) agree.
|
|
13
|
+
attr_reader underlying: untyped
|
|
14
|
+
attr_reader date_str: untyped
|
|
15
|
+
attr_reader type: untyped
|
|
16
|
+
attr_reader strike_mills: untyped
|
|
17
|
+
|
|
18
|
+
def self.generate: (?random: Random) -> OCC
|
|
19
|
+
# build/compose accept any Numeric strike (e.g. Rational) and a String or Symbol type.
|
|
20
|
+
def self.build: (underlying: untyped, date: String | Date, type: untyped, strike: Integer | Float | Rational | String) -> OCC
|
|
21
|
+
def self.compose_symbol: (untyped underlying, untyped date_str, untyped type, untyped strike_mills) -> String
|
|
22
|
+
|
|
23
|
+
def initialize: (SecID::input symbol) -> void
|
|
24
|
+
def normalized: () -> String
|
|
25
|
+
def valid?: () -> bool
|
|
26
|
+
def date: () -> Date?
|
|
27
|
+
alias date_obj date
|
|
28
|
+
def strike: () -> Float?
|
|
29
|
+
def to_s: () -> String
|
|
30
|
+
def to_pretty_s: () -> String?
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
# `Integer | Float | Rational` (not `Numeric`, which lacks `#*` in RBS) so the
|
|
35
|
+
# `when Numeric then strike * 1000` branch type-checks; String is the 8-digit form.
|
|
36
|
+
def self.normalize_strike_mills: (Integer | Float | Rational | String strike) -> String
|
|
37
|
+
|
|
38
|
+
def components: () -> Hash[Symbol, untyped]
|
|
39
|
+
def error_codes: () -> Array[Symbol]
|
|
40
|
+
def validation_message: (Symbol code) -> String?
|
|
41
|
+
|
|
42
|
+
@date: Date?
|
|
43
|
+
@strike: Float?
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
module SecID
|
|
2
|
+
# Immutable value object representing a matched identifier found in text.
|
|
3
|
+
class Match < Data
|
|
4
|
+
attr_reader type: Symbol
|
|
5
|
+
attr_reader raw: String
|
|
6
|
+
attr_reader range: Range[Integer]
|
|
7
|
+
attr_reader identifier: Base
|
|
8
|
+
|
|
9
|
+
def self.new: (type: Symbol, raw: String, range: Range[Integer], identifier: Base) -> instance
|
|
10
|
+
| (Symbol type, String raw, Range[Integer] range, Base identifier) -> instance
|
|
11
|
+
def self.[]: (type: Symbol, raw: String, range: Range[Integer], identifier: Base) -> instance
|
|
12
|
+
| (Symbol type, String raw, Range[Integer] range, Base identifier) -> instance
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Finds securities identifiers in freeform text.
|
|
16
|
+
#
|
|
17
|
+
# @api private
|
|
18
|
+
class Scanner
|
|
19
|
+
# Regex plumbing works with nilable MatchData throughout; typed `untyped` so the
|
|
20
|
+
# nil-unguarded capture access in the internal helpers stays free of noise.
|
|
21
|
+
CANDIDATE_RE: untyped
|
|
22
|
+
|
|
23
|
+
def initialize: (Array[singleton(Base)] identifier_list) -> void
|
|
24
|
+
def call: (String? text, ?classes: Array[singleton(Base)]?) { (Match) -> void } -> void
|
|
25
|
+
| (String? text, ?classes: Array[singleton(Base)]?) -> Enumerator[Match, void]
|
|
26
|
+
| (String? text, ?classes: Array[singleton(Base)]?) ?{ (Match) -> void } -> Enumerator[Match, void]?
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def precompute: () -> void
|
|
31
|
+
def build_candidate_partitions: () -> void
|
|
32
|
+
def build_length_table: () -> void
|
|
33
|
+
def scan_text: (String input, Array[singleton(Base)] target_classes) { (Match) -> void } -> void
|
|
34
|
+
def identify_candidate: (untyped match_data, Array[singleton(Base)] target_classes) -> Match?
|
|
35
|
+
def try_classes: (String raw, String cleaned, Integer start_pos, Array[singleton(Base)] classes) -> Match?
|
|
36
|
+
def best_match: (String cleaned, Array[singleton(Base)] classes) -> singleton(Base)?
|
|
37
|
+
|
|
38
|
+
@classes: Array[singleton(Base)]
|
|
39
|
+
@fisn_classes: Array[singleton(Base)]
|
|
40
|
+
@occ_classes: Array[singleton(Base)]
|
|
41
|
+
@simple_classes: Array[singleton(Base)]
|
|
42
|
+
@candidates_by_length: Hash[Integer, Array[singleton(Base)]]
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module SecID
|
|
2
|
+
# Stock Exchange Daily Official List (SEDOL).
|
|
3
|
+
class SEDOL < Base
|
|
4
|
+
include Checkable
|
|
5
|
+
extend Checkable::ClassMethods
|
|
6
|
+
|
|
7
|
+
FULL_NAME: String
|
|
8
|
+
ID_LENGTH: Integer
|
|
9
|
+
EXAMPLE: String
|
|
10
|
+
VALID_CHARS_REGEX: ::Regexp
|
|
11
|
+
ID_REGEX: ::Regexp
|
|
12
|
+
CHARACTER_WEIGHTS: Array[Integer]
|
|
13
|
+
GENERATE_CHARSET: Array[String]
|
|
14
|
+
ISIN_COUNTRY_CODES: Set[String]
|
|
15
|
+
|
|
16
|
+
# Used unguarded as non-nil after validation (`#id_digits`); typed loosely so static
|
|
17
|
+
# Steep and runtime RBS::Test (which sees nil for invalid) agree.
|
|
18
|
+
def identifier: () -> untyped
|
|
19
|
+
|
|
20
|
+
def initialize: (SecID::input sedol) -> void
|
|
21
|
+
def to_isin: (?String country_code) -> ISIN
|
|
22
|
+
def calculate_check_digit: () -> Integer
|
|
23
|
+
def self.check_digit: (SecID::input id) -> Integer
|
|
24
|
+
private def self.generate_body: (Random random) -> String
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def components: () -> Hash[Symbol, untyped]
|
|
29
|
+
def weighted_sum: () -> Integer
|
|
30
|
+
def id_digits: () -> Array[Integer]
|
|
31
|
+
|
|
32
|
+
@id_digits: Array[Integer]
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module SecID
|
|
2
|
+
class Valoren < Base
|
|
3
|
+
FULL_NAME: String
|
|
4
|
+
# Typed loosely: `#normalized` does `ID_LENGTH.max` and Range#max is nilable in RBS.
|
|
5
|
+
ID_LENGTH: untyped
|
|
6
|
+
EXAMPLE: String
|
|
7
|
+
VALID_CHARS_REGEX: ::Regexp
|
|
8
|
+
ID_REGEX: ::Regexp
|
|
9
|
+
ISIN_COUNTRY_CODES: Set[String]
|
|
10
|
+
|
|
11
|
+
attr_reader padding: String?
|
|
12
|
+
|
|
13
|
+
# Used unguarded after validation (`#normalized`/`#to_pretty_s`); typed loosely so
|
|
14
|
+
# static Steep and runtime RBS::Test (which sees nil for invalid) agree.
|
|
15
|
+
def identifier: () -> untyped
|
|
16
|
+
|
|
17
|
+
def initialize: (SecID::input valoren) -> void
|
|
18
|
+
def to_pretty_s: () -> String?
|
|
19
|
+
def to_isin: (?String country_code) -> ISIN
|
|
20
|
+
def normalized: () -> String
|
|
21
|
+
def normalize!: () -> self
|
|
22
|
+
def to_s: () -> String
|
|
23
|
+
private def self.generate_body: (Random random) -> String
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def components: () -> Hash[Symbol, untyped]
|
|
28
|
+
end
|
|
29
|
+
end
|
data/sig/sec_id/wkn.rbs
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module SecID
|
|
2
|
+
# Wertpapierkennnummer (WKN).
|
|
3
|
+
class WKN < Base
|
|
4
|
+
FULL_NAME: String
|
|
5
|
+
ID_LENGTH: Integer
|
|
6
|
+
EXAMPLE: String
|
|
7
|
+
VALID_CHARS_REGEX: ::Regexp
|
|
8
|
+
ID_REGEX: ::Regexp
|
|
9
|
+
GENERATE_CHARSET: Array[String]
|
|
10
|
+
|
|
11
|
+
def initialize: (SecID::input wkn) -> void
|
|
12
|
+
def to_isin: (?String country_code) -> ISIN
|
|
13
|
+
private def self.generate_body: (Random random) -> String
|
|
14
|
+
end
|
|
15
|
+
end
|
data/sig/sec_id.rbs
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
module SecID
|
|
2
|
+
# Base error class for all SecID errors.
|
|
3
|
+
class Error < StandardError
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
# Raised for invalid format, length, or characters.
|
|
7
|
+
class InvalidFormatError < Error
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Raised when the check digit does not match the calculated value.
|
|
11
|
+
class InvalidCheckDigitError < Error
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Raised for type-specific structural errors.
|
|
15
|
+
class InvalidStructureError < Error
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Raised when multiple identifier types match and on_ambiguous: :raise is used.
|
|
19
|
+
class AmbiguousMatchError < Error
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.[]: (Symbol key) -> singleton(Base)
|
|
23
|
+
def self.identifiers: () -> Array[singleton(Base)]
|
|
24
|
+
def self.detect: (SecID::input str) -> Array[Symbol]
|
|
25
|
+
def self.valid?: (SecID::input str, ?types: Array[Symbol]?) -> bool
|
|
26
|
+
def self.extract: (String? text, ?types: Array[Symbol]?) -> Array[Match]
|
|
27
|
+
def self.scan: (String? text, ?types: Array[Symbol]?) { (Match) -> void } -> void
|
|
28
|
+
| (String? text, ?types: Array[Symbol]?) -> Enumerator[Match, void]
|
|
29
|
+
def self.explain: (SecID::input str, ?types: Array[Symbol]?) -> Hash[Symbol, untyped]
|
|
30
|
+
def self.parse: (SecID::input str, ?types: Array[Symbol]?, on_ambiguous: :all) -> Array[Base]
|
|
31
|
+
| (SecID::input str, ?types: Array[Symbol]?, ?on_ambiguous: (:first | :raise)) -> Base?
|
|
32
|
+
| (SecID::input str, ?types: Array[Symbol]?, ?on_ambiguous: Symbol) -> untyped
|
|
33
|
+
# Single broad return, not per-`on_ambiguous` overloads: the shared `parse!` body calls
|
|
34
|
+
# `result.empty?` on the `:all` path, so precise overloads would make Steep check `.empty?`
|
|
35
|
+
# against the `:first`/`:raise` arm's `Base?` and fail. Widening here keeps `result` untyped.
|
|
36
|
+
def self.parse!: (SecID::input str, ?types: Array[Symbol]?, ?on_ambiguous: Symbol) -> (Base | Array[Base])
|
|
37
|
+
def self.generate: (Symbol key, ?random: Random) -> Base
|
|
38
|
+
|
|
39
|
+
# Singleton-method privacy must be declared inline (`private def self.…`); a bare
|
|
40
|
+
# `private` section only affects instance methods, leaving these public.
|
|
41
|
+
private def self.register_identifier: (singleton(Base) klass) -> void
|
|
42
|
+
private def self.parse_any: (SecID::input str) -> Base?
|
|
43
|
+
private def self.parse_from: (SecID::input str, Array[Symbol] types) -> Base?
|
|
44
|
+
private def self.parse_strict: (SecID::input str, Array[Symbol]? types) -> Base?
|
|
45
|
+
private def self.parse_all: (SecID::input str, Array[Symbol]? types) -> Array[Base]
|
|
46
|
+
private def self.resolve_candidates: (SecID::input str, Array[Symbol]? types) -> Array[Symbol]
|
|
47
|
+
private def self.ambiguous_message: (SecID::input str, Array[Symbol] candidates) -> String
|
|
48
|
+
private def self.parse_error_message: (SecID::input str, Array[Symbol]? types) -> String
|
|
49
|
+
private def self.detector: () -> Detector
|
|
50
|
+
private def self.scanner: () -> Scanner
|
|
51
|
+
private def self.identifier_map: () -> Hash[Symbol, singleton(Base)]
|
|
52
|
+
private def self.identifier_list: () -> Array[singleton(Base)]
|
|
53
|
+
|
|
54
|
+
self.@identifier_map: Hash[Symbol, singleton(Base)]
|
|
55
|
+
self.@identifier_list: Array[singleton(Base)]
|
|
56
|
+
self.@detector: Detector?
|
|
57
|
+
self.@scanner: Scanner?
|
|
58
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sec_id
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 6.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Leonid Svyatov
|
|
@@ -9,30 +9,43 @@ bindir: bin
|
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies: []
|
|
12
|
-
description: Validate, normalize, parse, and
|
|
13
|
-
identifier type from any string. Calculate and restore
|
|
14
|
-
|
|
12
|
+
description: Validate, normalize, parse, convert, generate, and classify securities
|
|
13
|
+
identifiers. Auto-detect identifier type from any string. Calculate and restore
|
|
14
|
+
check digits. Generate format-valid identifiers as test fixtures. Decode CFI codes
|
|
15
|
+
to full ISO 10962:2021 classifications. Includes an opt-in ActiveModel/Rails validator
|
|
16
|
+
that adds no runtime dependency. Supports ISIN, CUSIP, CEI, SEDOL, FIGI, LEI, IBAN,
|
|
17
|
+
CIK, OCC, WKN, Valoren, CFI, FISN, BIC, and DTI.
|
|
15
18
|
email:
|
|
16
19
|
- leonid@svyatov.ru
|
|
17
20
|
executables: []
|
|
18
21
|
extensions: []
|
|
19
22
|
extra_rdoc_files: []
|
|
20
23
|
files:
|
|
24
|
+
- ".yardopts"
|
|
21
25
|
- CHANGELOG.md
|
|
22
26
|
- LICENSE.txt
|
|
23
27
|
- MIGRATION.md
|
|
24
28
|
- README.md
|
|
25
29
|
- lib/sec_id.rb
|
|
30
|
+
- lib/sec_id/active_model.rb
|
|
26
31
|
- lib/sec_id/base.rb
|
|
32
|
+
- lib/sec_id/bic.rb
|
|
33
|
+
- lib/sec_id/bic/country_codes.rb
|
|
27
34
|
- lib/sec_id/cei.rb
|
|
28
35
|
- lib/sec_id/cfi.rb
|
|
36
|
+
- lib/sec_id/cfi/attribute_set.rb
|
|
37
|
+
- lib/sec_id/cfi/classification.rb
|
|
38
|
+
- lib/sec_id/cfi/field.rb
|
|
39
|
+
- lib/sec_id/cfi/tables.rb
|
|
29
40
|
- lib/sec_id/cik.rb
|
|
30
41
|
- lib/sec_id/concerns/checkable.rb
|
|
31
|
-
- lib/sec_id/concerns/
|
|
42
|
+
- lib/sec_id/concerns/generatable.rb
|
|
32
43
|
- lib/sec_id/concerns/normalizable.rb
|
|
33
44
|
- lib/sec_id/concerns/validatable.rb
|
|
34
45
|
- lib/sec_id/cusip.rb
|
|
46
|
+
- lib/sec_id/deep_freeze.rb
|
|
35
47
|
- lib/sec_id/detector.rb
|
|
48
|
+
- lib/sec_id/dti.rb
|
|
36
49
|
- lib/sec_id/errors.rb
|
|
37
50
|
- lib/sec_id/figi.rb
|
|
38
51
|
- lib/sec_id/fisn.rb
|
|
@@ -41,17 +54,52 @@ files:
|
|
|
41
54
|
- lib/sec_id/isin.rb
|
|
42
55
|
- lib/sec_id/lei.rb
|
|
43
56
|
- lib/sec_id/occ.rb
|
|
57
|
+
- lib/sec_id/railtie.rb
|
|
44
58
|
- lib/sec_id/scanner.rb
|
|
45
59
|
- lib/sec_id/sedol.rb
|
|
46
60
|
- lib/sec_id/valoren.rb
|
|
47
61
|
- lib/sec_id/version.rb
|
|
48
62
|
- lib/sec_id/wkn.rb
|
|
49
63
|
- sec_id.gemspec
|
|
64
|
+
- sig/manifest.yaml
|
|
65
|
+
- sig/sec_id.rbs
|
|
66
|
+
- sig/sec_id/base.rbs
|
|
67
|
+
- sig/sec_id/bic.rbs
|
|
68
|
+
- sig/sec_id/bic/country_codes.rbs
|
|
69
|
+
- sig/sec_id/cei.rbs
|
|
70
|
+
- sig/sec_id/cfi.rbs
|
|
71
|
+
- sig/sec_id/cfi/attribute_set.rbs
|
|
72
|
+
- sig/sec_id/cfi/classification.rbs
|
|
73
|
+
- sig/sec_id/cfi/field.rbs
|
|
74
|
+
- sig/sec_id/cfi/tables.rbs
|
|
75
|
+
- sig/sec_id/cik.rbs
|
|
76
|
+
- sig/sec_id/concerns/checkable.rbs
|
|
77
|
+
- sig/sec_id/concerns/generatable.rbs
|
|
78
|
+
- sig/sec_id/concerns/normalizable.rbs
|
|
79
|
+
- sig/sec_id/concerns/validatable.rbs
|
|
80
|
+
- sig/sec_id/cusip.rbs
|
|
81
|
+
- sig/sec_id/deep_freeze.rbs
|
|
82
|
+
- sig/sec_id/detector.rbs
|
|
83
|
+
- sig/sec_id/dti.rbs
|
|
84
|
+
- sig/sec_id/errors.rbs
|
|
85
|
+
- sig/sec_id/figi.rbs
|
|
86
|
+
- sig/sec_id/fisn.rbs
|
|
87
|
+
- sig/sec_id/iban.rbs
|
|
88
|
+
- sig/sec_id/iban/country_rules.rbs
|
|
89
|
+
- sig/sec_id/isin.rbs
|
|
90
|
+
- sig/sec_id/lei.rbs
|
|
91
|
+
- sig/sec_id/occ.rbs
|
|
92
|
+
- sig/sec_id/scanner.rbs
|
|
93
|
+
- sig/sec_id/sedol.rbs
|
|
94
|
+
- sig/sec_id/valoren.rbs
|
|
95
|
+
- sig/sec_id/version.rbs
|
|
96
|
+
- sig/sec_id/wkn.rbs
|
|
50
97
|
homepage: https://github.com/svyatov/sec_id
|
|
51
98
|
licenses:
|
|
52
99
|
- MIT
|
|
53
100
|
metadata:
|
|
54
101
|
rubygems_mfa_required: 'true'
|
|
102
|
+
documentation_uri: https://rubydoc.info/gems/sec_id
|
|
55
103
|
source_code_uri: https://github.com/svyatov/sec_id
|
|
56
104
|
changelog_uri: https://github.com/svyatov/sec_id/blob/main/CHANGELOG.md
|
|
57
105
|
bug_tracker_uri: https://github.com/svyatov/sec_id/issues
|
|
@@ -69,8 +117,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
69
117
|
- !ruby/object:Gem::Version
|
|
70
118
|
version: '0'
|
|
71
119
|
requirements: []
|
|
72
|
-
rubygems_version: 4.0.
|
|
120
|
+
rubygems_version: 4.0.12
|
|
73
121
|
specification_version: 4
|
|
74
122
|
summary: A Ruby toolkit for securities identifiers — validate, parse, normalize, detect,
|
|
75
|
-
and
|
|
123
|
+
convert, generate, and classify.
|
|
76
124
|
test_files: []
|