sec_id 4.1.0 → 4.3.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/CHANGELOG.md +47 -4
- data/README.md +157 -102
- data/lib/sec_id/base.rb +118 -6
- data/lib/sec_id/cik.rb +39 -11
- data/lib/sec_id/cusip.rb +30 -8
- data/lib/sec_id/figi.rb +30 -6
- data/lib/sec_id/iban/country_rules.rb +266 -0
- data/lib/sec_id/iban.rb +158 -0
- data/lib/sec_id/isin.rb +33 -12
- data/lib/sec_id/lei.rb +67 -0
- data/lib/sec_id/normalizable.rb +35 -0
- data/lib/sec_id/occ.rb +150 -0
- data/lib/sec_id/sedol.rb +24 -6
- data/lib/sec_id/version.rb +1 -1
- data/lib/sec_id.rb +4 -0
- data/sec_id.gemspec +3 -4
- metadata +10 -17
- data/.github/dependabot.yml +0 -11
- data/.github/workflows/main.yml +0 -39
- data/.gitignore +0 -12
- data/.rspec +0 -3
- data/.rubocop.yml +0 -39
- data/Gemfile +0 -20
- data/Rakefile +0 -13
- data/bin/console +0 -15
- data/bin/setup +0 -8
data/lib/sec_id/isin.rb
CHANGED
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module SecId
|
|
4
|
-
#
|
|
4
|
+
# International Securities Identification Number (ISIN) - a 12-character alphanumeric code
|
|
5
|
+
# that uniquely identifies a security globally.
|
|
6
|
+
#
|
|
7
|
+
# Format: 2-letter country code + 9-character NSIN + 1-digit check digit
|
|
8
|
+
#
|
|
9
|
+
# @see https://en.wikipedia.org/wiki/International_Securities_Identification_Number
|
|
10
|
+
#
|
|
11
|
+
# @example Validate an ISIN
|
|
12
|
+
# SecId::ISIN.valid?('US5949181045') #=> true
|
|
13
|
+
#
|
|
14
|
+
# @example Restore check digit
|
|
15
|
+
# SecId::ISIN.restore!('US594918104') #=> 'US5949181045'
|
|
5
16
|
class ISIN < Base
|
|
17
|
+
# Regular expression for parsing ISIN components.
|
|
6
18
|
ID_REGEX = /\A
|
|
7
19
|
(?<identifier>
|
|
8
20
|
(?<country_code>[A-Z]{2})
|
|
@@ -10,6 +22,7 @@ module SecId
|
|
|
10
22
|
(?<check_digit>\d)?
|
|
11
23
|
\z/x
|
|
12
24
|
|
|
25
|
+
# Country codes that use CUSIP Global Services (CGS) for NSIN assignment.
|
|
13
26
|
CGS_COUNTRY_CODES = Set.new(
|
|
14
27
|
%w[
|
|
15
28
|
US CA AG AI AN AR AS AW BB BL BM BO BQ BR BS BZ CL CO CR CW DM DO EC FM
|
|
@@ -18,8 +31,13 @@ module SecId
|
|
|
18
31
|
]
|
|
19
32
|
).freeze
|
|
20
33
|
|
|
21
|
-
|
|
34
|
+
# @return [String, nil] the ISO 3166-1 alpha-2 country code
|
|
35
|
+
attr_reader :country_code
|
|
22
36
|
|
|
37
|
+
# @return [String, nil] the National Securities Identifying Number (9 characters)
|
|
38
|
+
attr_reader :nsin
|
|
39
|
+
|
|
40
|
+
# @param isin [String] the ISIN string to parse
|
|
23
41
|
def initialize(isin)
|
|
24
42
|
isin_parts = parse isin
|
|
25
43
|
@identifier = isin_parts[:identifier]
|
|
@@ -28,28 +46,30 @@ module SecId
|
|
|
28
46
|
@check_digit = isin_parts[:check_digit]&.to_i
|
|
29
47
|
end
|
|
30
48
|
|
|
49
|
+
# @return [Integer] the calculated check digit (0-9)
|
|
50
|
+
# @raise [InvalidFormatError] if the ISIN format is invalid
|
|
31
51
|
def calculate_check_digit
|
|
32
|
-
|
|
33
|
-
raise InvalidFormatError, "ISIN '#{full_number}' is invalid and check-digit cannot be calculated!"
|
|
34
|
-
end
|
|
35
|
-
|
|
52
|
+
validate_format_for_calculation!
|
|
36
53
|
mod10(luhn_sum)
|
|
37
54
|
end
|
|
38
55
|
|
|
39
|
-
# CUSIP
|
|
40
|
-
|
|
41
|
-
CGS_COUNTRY_CODES.include?(country_code)
|
|
42
|
-
end
|
|
43
|
-
|
|
56
|
+
# @return [CUSIP] a new CUSIP instance
|
|
57
|
+
# @raise [InvalidFormatError] if the country code is not a CGS country
|
|
44
58
|
def to_cusip
|
|
45
59
|
raise InvalidFormatError, "'#{country_code}' is not a CGS country code!" unless cgs?
|
|
46
60
|
|
|
47
61
|
CUSIP.new(nsin)
|
|
48
62
|
end
|
|
49
63
|
|
|
64
|
+
# @return [Boolean] true if the country code is a CGS country
|
|
65
|
+
def cgs?
|
|
66
|
+
CGS_COUNTRY_CODES.include?(country_code)
|
|
67
|
+
end
|
|
68
|
+
|
|
50
69
|
private
|
|
51
70
|
|
|
52
|
-
#
|
|
71
|
+
# @return [Integer] the Luhn sum
|
|
72
|
+
# @see https://en.wikipedia.org/wiki/Luhn_algorithm
|
|
53
73
|
def luhn_sum
|
|
54
74
|
reversed_id_digits.each_slice(2).reduce(0) do |sum, (even, odd)|
|
|
55
75
|
double_even = (even || 0) * 2
|
|
@@ -58,6 +78,7 @@ module SecId
|
|
|
58
78
|
end
|
|
59
79
|
end
|
|
60
80
|
|
|
81
|
+
# @return [Array<Integer>] the reversed digit array
|
|
61
82
|
def reversed_id_digits
|
|
62
83
|
identifier.each_char.flat_map(&method(:char_to_digits)).reverse!
|
|
63
84
|
end
|
data/lib/sec_id/lei.rb
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SecId
|
|
4
|
+
# Legal Entity Identifier (LEI) - a 20-character alphanumeric code that
|
|
5
|
+
# uniquely identifies legal entities participating in financial transactions.
|
|
6
|
+
#
|
|
7
|
+
# Format: 4-character LOU ID + 2-character reserved + 12-character entity ID + 2-digit check digit
|
|
8
|
+
#
|
|
9
|
+
# @see https://en.wikipedia.org/wiki/Legal_Entity_Identifier
|
|
10
|
+
# @see https://www.gleif.org/en/about-lei/iso-17442-the-lei-code-structure
|
|
11
|
+
#
|
|
12
|
+
# @example Validate a LEI
|
|
13
|
+
# SecId::LEI.valid?('529900T8BM49AURSDO55') #=> true
|
|
14
|
+
#
|
|
15
|
+
# @example Calculate check digit
|
|
16
|
+
# SecId::LEI.check_digit('529900T8BM49AURSDO') #=> 55
|
|
17
|
+
class LEI < Base
|
|
18
|
+
# Regular expression for parsing LEI components.
|
|
19
|
+
ID_REGEX = /\A
|
|
20
|
+
(?<identifier>
|
|
21
|
+
(?<lou_id>[0-9A-Z]{4})
|
|
22
|
+
(?<reserved>[0-9A-Z]{2})
|
|
23
|
+
(?<entity_id>[0-9A-Z]{12}))
|
|
24
|
+
(?<check_digit>\d{2})?
|
|
25
|
+
\z/x
|
|
26
|
+
|
|
27
|
+
# @return [String, nil] the 4-character Local Operating Unit (LOU) identifier
|
|
28
|
+
attr_reader :lou_id
|
|
29
|
+
|
|
30
|
+
# @return [String, nil] the 2-character reserved field (typically '00')
|
|
31
|
+
attr_reader :reserved
|
|
32
|
+
|
|
33
|
+
# @return [String, nil] the 12-character entity-specific identifier
|
|
34
|
+
attr_reader :entity_id
|
|
35
|
+
|
|
36
|
+
# @param lei [String] the LEI string to parse
|
|
37
|
+
def initialize(lei)
|
|
38
|
+
lei_parts = parse lei
|
|
39
|
+
@identifier = lei_parts[:identifier]
|
|
40
|
+
@lou_id = lei_parts[:lou_id]
|
|
41
|
+
@reserved = lei_parts[:reserved]
|
|
42
|
+
@entity_id = lei_parts[:entity_id]
|
|
43
|
+
@check_digit = lei_parts[:check_digit]&.to_i
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# @return [Integer] the calculated 2-digit check digit (1-98)
|
|
47
|
+
# @raise [InvalidFormatError] if the LEI format is invalid
|
|
48
|
+
def calculate_check_digit
|
|
49
|
+
validate_format_for_calculation!
|
|
50
|
+
mod97("#{numeric_identifier}00")
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# @return [String]
|
|
54
|
+
def to_s
|
|
55
|
+
return full_number unless check_digit
|
|
56
|
+
|
|
57
|
+
"#{identifier}#{check_digit.to_s.rjust(2, '0')}"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
# @return [String] the numeric string representation
|
|
63
|
+
def numeric_identifier
|
|
64
|
+
identifier.each_char.map { |char| char_to_digit(char) }.join
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SecId
|
|
4
|
+
# Provides normalize! class method delegation for identifiers that support normalization.
|
|
5
|
+
# Include this module in classes that implement an instance-level normalize! method.
|
|
6
|
+
#
|
|
7
|
+
# @example
|
|
8
|
+
# class MyIdentifier < Base
|
|
9
|
+
# include Normalizable
|
|
10
|
+
#
|
|
11
|
+
# def normalize!
|
|
12
|
+
# # implementation
|
|
13
|
+
# end
|
|
14
|
+
# end
|
|
15
|
+
#
|
|
16
|
+
# MyIdentifier.normalize!('ABC123') #=> normalized string
|
|
17
|
+
module Normalizable
|
|
18
|
+
# @api private
|
|
19
|
+
def self.included(base)
|
|
20
|
+
base.extend(ClassMethods)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Class methods added when Normalizable is included.
|
|
24
|
+
module ClassMethods
|
|
25
|
+
# Normalizes the identifier to its canonical format.
|
|
26
|
+
#
|
|
27
|
+
# @param id [String, #to_s] the identifier to normalize
|
|
28
|
+
# @return [String] the normalized identifier
|
|
29
|
+
# @raise [InvalidFormatError] if the identifier format is invalid
|
|
30
|
+
def normalize!(id)
|
|
31
|
+
new(id).normalize!
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
data/lib/sec_id/occ.rb
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'date'
|
|
4
|
+
|
|
5
|
+
module SecId
|
|
6
|
+
# OCC Option Symbol - standardized option symbol format used by Option Clearing Corporation.
|
|
7
|
+
# Format: 6-char underlying (padded) + 6-char date (YYMMDD) + type (C/P) + 8-digit strike (in mills).
|
|
8
|
+
#
|
|
9
|
+
# @note OCC identifiers have no check digit. The {#has_check_digit?} method returns false
|
|
10
|
+
# and validation includes both format and date parseability checks.
|
|
11
|
+
#
|
|
12
|
+
# @see https://en.wikipedia.org/wiki/Option_symbol#The_OCC_Option_Symbol
|
|
13
|
+
# @see https://web.archive.org/web/20120507220143/http://www.theocc.com/components/docs/initiatives/symbology/symbology_initiative_v1_8.pdf
|
|
14
|
+
#
|
|
15
|
+
# @example Validate an OCC symbol
|
|
16
|
+
# SecId::OCC.valid?('AAPL 210917C00150000') #=> true
|
|
17
|
+
#
|
|
18
|
+
# @example Build an OCC symbol from components
|
|
19
|
+
# occ = SecId::OCC.build(underlying: 'AAPL', date: '2021-09-17', type: 'C', strike: 150.0)
|
|
20
|
+
# occ.to_s #=> 'AAPL 210917C00150000'
|
|
21
|
+
class OCC < Base
|
|
22
|
+
include Normalizable
|
|
23
|
+
|
|
24
|
+
# Regular expression for parsing OCC symbol components.
|
|
25
|
+
ID_REGEX = /\A
|
|
26
|
+
(?<initial>
|
|
27
|
+
(?=.{1,6})(?<underlying>\d?[A-Z]{1,5}\d?)(?<padding>[ ]*))
|
|
28
|
+
(?<date>\d{6})
|
|
29
|
+
(?<type>[CP])
|
|
30
|
+
(?<strike_mills>\d{8})
|
|
31
|
+
\z/x
|
|
32
|
+
|
|
33
|
+
# @return [String, nil] the underlying security symbol (1-6 chars)
|
|
34
|
+
attr_reader :underlying
|
|
35
|
+
|
|
36
|
+
# @return [String, nil] the expiration date string in YYMMDD format
|
|
37
|
+
attr_reader :date_str
|
|
38
|
+
|
|
39
|
+
# @return [String, nil] the option type ('C' for call, 'P' for put)
|
|
40
|
+
attr_reader :type
|
|
41
|
+
|
|
42
|
+
# @return [String, nil] the strike price in mills (thousandths of a dollar, represented as an 8-digit string)
|
|
43
|
+
attr_reader :strike_mills
|
|
44
|
+
|
|
45
|
+
class << self
|
|
46
|
+
# Builds an OCC symbol from components.
|
|
47
|
+
#
|
|
48
|
+
# @param underlying [String] the underlying symbol (1-6 chars)
|
|
49
|
+
# @param date [String, Date] the expiration date
|
|
50
|
+
# @param type [String] 'C' for call or 'P' for put
|
|
51
|
+
# @param strike [Numeric, String] the strike price in dollars or 8-char mills string
|
|
52
|
+
# @return [OCC] a new OCC instance
|
|
53
|
+
# @raise [ArgumentError] if strike format is invalid
|
|
54
|
+
def build(underlying:, date:, type:, strike:)
|
|
55
|
+
date_obj = date.is_a?(Date) ? date : Date.parse(date)
|
|
56
|
+
strike_mills = normalize_strike_mills(strike)
|
|
57
|
+
|
|
58
|
+
new(compose_symbol(underlying, date_obj.strftime('%y%m%d'), type, strike_mills))
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Composes an OCC symbol string from its components.
|
|
62
|
+
#
|
|
63
|
+
# @param underlying [String] the underlying symbol
|
|
64
|
+
# @param date_str [String] the date in YYMMDD format
|
|
65
|
+
# @param type [String] 'C' or 'P'
|
|
66
|
+
# @param strike_mills [String, Integer] the strike in mills
|
|
67
|
+
# @return [String] the composed OCC symbol
|
|
68
|
+
def compose_symbol(underlying, date_str, type, strike_mills)
|
|
69
|
+
padded_underlying = underlying.to_s.ljust(6, "\s")
|
|
70
|
+
padded_strike = format('%08d', strike_mills.to_i)
|
|
71
|
+
|
|
72
|
+
"#{padded_underlying}#{date_str}#{type}#{padded_strike}"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
private
|
|
76
|
+
|
|
77
|
+
# @param strike [Numeric, String] strike price or 8-char mills string
|
|
78
|
+
# @return [String] 8-character strike mills string
|
|
79
|
+
# @raise [ArgumentError] if strike format is invalid
|
|
80
|
+
def normalize_strike_mills(strike)
|
|
81
|
+
case strike
|
|
82
|
+
when Numeric
|
|
83
|
+
format('%08d', (strike * 1000).to_i)
|
|
84
|
+
when String && /\A\d{8}\z/
|
|
85
|
+
strike
|
|
86
|
+
else
|
|
87
|
+
raise ArgumentError, 'Strike must be numeric or an 8-char string!'
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# @param symbol [String] the OCC symbol string to parse
|
|
93
|
+
def initialize(symbol)
|
|
94
|
+
symbol_parts = parse(symbol, upcase: false)
|
|
95
|
+
@identifier = symbol_parts[:initial]
|
|
96
|
+
@underlying = symbol_parts[:underlying]
|
|
97
|
+
@date_str = symbol_parts[:date]
|
|
98
|
+
@type = symbol_parts[:type]
|
|
99
|
+
@strike_mills = symbol_parts[:strike_mills]
|
|
100
|
+
@check_digit = nil
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# @return [Boolean] always false
|
|
104
|
+
def has_check_digit?
|
|
105
|
+
false
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Normalizes the OCC symbol to standard format with 6-char padded underlying and 8-digit strike.
|
|
109
|
+
#
|
|
110
|
+
# @return [String] the normalized OCC symbol
|
|
111
|
+
# @raise [InvalidFormatError] if the OCC symbol is invalid
|
|
112
|
+
def normalize!
|
|
113
|
+
raise InvalidFormatError, "OCC '#{full_number}' is invalid and cannot be normalized!" unless valid?
|
|
114
|
+
|
|
115
|
+
@full_number = self.class.compose_symbol(underlying, date_str, type, strike_mills)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# @return [Boolean]
|
|
119
|
+
def valid?
|
|
120
|
+
valid_format? && !date.nil? # date must be parseable
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# @return [Date, nil] the parsed date or nil if invalid
|
|
124
|
+
def date
|
|
125
|
+
return nil unless date_str
|
|
126
|
+
|
|
127
|
+
@date ||= Date.strptime(date_str, '%y%m%d')
|
|
128
|
+
rescue ArgumentError
|
|
129
|
+
nil
|
|
130
|
+
end
|
|
131
|
+
alias date_obj date
|
|
132
|
+
|
|
133
|
+
# @return [Float] strike price in dollars
|
|
134
|
+
def strike
|
|
135
|
+
@strike ||= strike_mills.to_i / 1000.0
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# @return [String]
|
|
139
|
+
def to_s
|
|
140
|
+
full_number
|
|
141
|
+
end
|
|
142
|
+
alias to_str to_s
|
|
143
|
+
|
|
144
|
+
# @deprecated Use {#full_number} instead
|
|
145
|
+
# @return [String]
|
|
146
|
+
def full_symbol
|
|
147
|
+
full_number
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
data/lib/sec_id/sedol.rb
CHANGED
|
@@ -1,32 +1,49 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module SecId
|
|
4
|
-
#
|
|
4
|
+
# Stock Exchange Daily Official List (SEDOL) - a 7-character alphanumeric code
|
|
5
|
+
# that identifies securities traded on the London Stock Exchange and other UK exchanges.
|
|
6
|
+
#
|
|
7
|
+
# Format: 6-character identifier + 1-digit check digit
|
|
8
|
+
# Note: SEDOL excludes vowels (A, E, I, O, U) to avoid forming words.
|
|
9
|
+
#
|
|
10
|
+
# @see https://en.wikipedia.org/wiki/SEDOL
|
|
11
|
+
#
|
|
12
|
+
# @example Validate a SEDOL
|
|
13
|
+
# SecId::SEDOL.valid?('B19GKT4') #=> true
|
|
14
|
+
#
|
|
15
|
+
# @example Calculate check digit
|
|
16
|
+
# SecId::SEDOL.check_digit('B19GKT') #=> 4
|
|
5
17
|
class SEDOL < Base
|
|
18
|
+
# Regular expression for parsing SEDOL components.
|
|
19
|
+
# Excludes vowels (A, E, I, O, U) from valid characters.
|
|
6
20
|
ID_REGEX = /\A
|
|
7
21
|
(?<identifier>[0-9BCDFGHJKLMNPQRSTVWXYZ]{6})
|
|
8
22
|
(?<check_digit>\d)?
|
|
9
23
|
\z/x
|
|
10
24
|
|
|
25
|
+
# Weights applied to each character position in the check digit calculation.
|
|
11
26
|
CHARACTER_WEIGHTS = [1, 3, 1, 7, 3, 9].freeze
|
|
12
27
|
|
|
28
|
+
# @param sedol [String] the SEDOL string to parse
|
|
13
29
|
def initialize(sedol)
|
|
14
30
|
sedol_parts = parse sedol
|
|
15
31
|
@identifier = sedol_parts[:identifier]
|
|
16
32
|
@check_digit = sedol_parts[:check_digit]&.to_i
|
|
17
33
|
end
|
|
18
34
|
|
|
35
|
+
# @return [Integer] the calculated check digit (0-9)
|
|
36
|
+
# @raise [InvalidFormatError] if the SEDOL format is invalid
|
|
19
37
|
def calculate_check_digit
|
|
20
|
-
|
|
21
|
-
raise InvalidFormatError, "SEDOL '#{full_number}' is invalid and check-digit cannot be calculated!"
|
|
22
|
-
end
|
|
23
|
-
|
|
38
|
+
validate_format_for_calculation!
|
|
24
39
|
mod10(weighted_sum)
|
|
25
40
|
end
|
|
26
41
|
|
|
27
42
|
private
|
|
28
43
|
|
|
29
|
-
# NOTE:
|
|
44
|
+
# NOTE: Not idiomatic Ruby, but optimized for performance.
|
|
45
|
+
#
|
|
46
|
+
# @return [Integer] the weighted sum
|
|
30
47
|
def weighted_sum
|
|
31
48
|
index = 0
|
|
32
49
|
sum = 0
|
|
@@ -39,6 +56,7 @@ module SecId
|
|
|
39
56
|
sum
|
|
40
57
|
end
|
|
41
58
|
|
|
59
|
+
# @return [Array<Integer>] array of digit values
|
|
42
60
|
def id_digits
|
|
43
61
|
@id_digits ||= identifier.each_char.map(&method(:char_to_digit))
|
|
44
62
|
end
|
data/lib/sec_id/version.rb
CHANGED
data/lib/sec_id.rb
CHANGED
|
@@ -2,12 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
require 'set'
|
|
4
4
|
require 'sec_id/version'
|
|
5
|
+
require 'sec_id/normalizable'
|
|
5
6
|
require 'sec_id/base'
|
|
6
7
|
require 'sec_id/isin'
|
|
7
8
|
require 'sec_id/cusip'
|
|
8
9
|
require 'sec_id/sedol'
|
|
9
10
|
require 'sec_id/figi'
|
|
11
|
+
require 'sec_id/lei'
|
|
12
|
+
require 'sec_id/iban'
|
|
10
13
|
require 'sec_id/cik'
|
|
14
|
+
require 'sec_id/occ'
|
|
11
15
|
|
|
12
16
|
module SecId
|
|
13
17
|
Error = Class.new(StandardError)
|
data/sec_id.gemspec
CHANGED
|
@@ -11,16 +11,15 @@ Gem::Specification.new do |spec|
|
|
|
11
11
|
spec.email = ['leonid@svyatov.ru']
|
|
12
12
|
|
|
13
13
|
spec.summary = 'Validate securities identification numbers with ease!'
|
|
14
|
-
spec.description =
|
|
14
|
+
spec.description = 'Validate, calculate check digits, and parse components of securities identifiers. ' \
|
|
15
|
+
'Supports ISIN, CUSIP, SEDOL, FIGI, LEI, IBAN, CIK, and OCC standards.'
|
|
15
16
|
spec.homepage = 'https://github.com/svyatov/sec_id'
|
|
16
17
|
spec.license = 'MIT'
|
|
17
18
|
|
|
18
19
|
spec.required_ruby_version = '>= 3.1.0'
|
|
19
20
|
|
|
20
21
|
spec.require_paths = ['lib']
|
|
21
|
-
spec.files =
|
|
22
|
-
f.match(%r{^(test|spec|features)/})
|
|
23
|
-
end
|
|
22
|
+
spec.files = Dir['lib/**/*.rb'] + %w[CHANGELOG.md LICENSE.txt README.md sec_id.gemspec]
|
|
24
23
|
|
|
25
24
|
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
26
25
|
end
|
metadata
CHANGED
|
@@ -1,41 +1,36 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sec_id
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 4.
|
|
4
|
+
version: 4.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Leonid Svyatov
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies: []
|
|
13
|
-
description:
|
|
14
|
-
|
|
12
|
+
description: Validate, calculate check digits, and parse components of securities
|
|
13
|
+
identifiers. Supports ISIN, CUSIP, SEDOL, FIGI, LEI, IBAN, CIK, and OCC standards.
|
|
15
14
|
email:
|
|
16
15
|
- leonid@svyatov.ru
|
|
17
16
|
executables: []
|
|
18
17
|
extensions: []
|
|
19
18
|
extra_rdoc_files: []
|
|
20
19
|
files:
|
|
21
|
-
- ".github/dependabot.yml"
|
|
22
|
-
- ".github/workflows/main.yml"
|
|
23
|
-
- ".gitignore"
|
|
24
|
-
- ".rspec"
|
|
25
|
-
- ".rubocop.yml"
|
|
26
20
|
- CHANGELOG.md
|
|
27
|
-
- Gemfile
|
|
28
21
|
- LICENSE.txt
|
|
29
22
|
- README.md
|
|
30
|
-
- Rakefile
|
|
31
|
-
- bin/console
|
|
32
|
-
- bin/setup
|
|
33
23
|
- lib/sec_id.rb
|
|
34
24
|
- lib/sec_id/base.rb
|
|
35
25
|
- lib/sec_id/cik.rb
|
|
36
26
|
- lib/sec_id/cusip.rb
|
|
37
27
|
- lib/sec_id/figi.rb
|
|
28
|
+
- lib/sec_id/iban.rb
|
|
29
|
+
- lib/sec_id/iban/country_rules.rb
|
|
38
30
|
- lib/sec_id/isin.rb
|
|
31
|
+
- lib/sec_id/lei.rb
|
|
32
|
+
- lib/sec_id/normalizable.rb
|
|
33
|
+
- lib/sec_id/occ.rb
|
|
39
34
|
- lib/sec_id/sedol.rb
|
|
40
35
|
- lib/sec_id/version.rb
|
|
41
36
|
- sec_id.gemspec
|
|
@@ -44,7 +39,6 @@ licenses:
|
|
|
44
39
|
- MIT
|
|
45
40
|
metadata:
|
|
46
41
|
rubygems_mfa_required: 'true'
|
|
47
|
-
post_install_message:
|
|
48
42
|
rdoc_options: []
|
|
49
43
|
require_paths:
|
|
50
44
|
- lib
|
|
@@ -59,8 +53,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
59
53
|
- !ruby/object:Gem::Version
|
|
60
54
|
version: '0'
|
|
61
55
|
requirements: []
|
|
62
|
-
rubygems_version:
|
|
63
|
-
signing_key:
|
|
56
|
+
rubygems_version: 4.0.3
|
|
64
57
|
specification_version: 4
|
|
65
58
|
summary: Validate securities identification numbers with ease!
|
|
66
59
|
test_files: []
|
data/.github/dependabot.yml
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
# To get started with Dependabot version updates, you'll need to specify which
|
|
2
|
-
# package ecosystems to update and where the package manifests are located.
|
|
3
|
-
# Please see the documentation for all configuration options:
|
|
4
|
-
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
|
5
|
-
|
|
6
|
-
version: 2
|
|
7
|
-
updates:
|
|
8
|
-
- package-ecosystem: "bundler" # See documentation for possible values
|
|
9
|
-
directory: "/" # Location of package manifests
|
|
10
|
-
schedule:
|
|
11
|
-
interval: "weekly"
|
data/.github/workflows/main.yml
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
name: CI
|
|
2
|
-
|
|
3
|
-
concurrency:
|
|
4
|
-
group: ${{ github.workflow }}-${{ github.ref }}
|
|
5
|
-
cancel-in-progress: true
|
|
6
|
-
|
|
7
|
-
on:
|
|
8
|
-
push:
|
|
9
|
-
branches: ["main"]
|
|
10
|
-
pull_request:
|
|
11
|
-
branches: ["main"]
|
|
12
|
-
|
|
13
|
-
jobs:
|
|
14
|
-
build:
|
|
15
|
-
runs-on: ubuntu-latest
|
|
16
|
-
name: Ruby ${{ matrix.ruby_version }}
|
|
17
|
-
strategy:
|
|
18
|
-
matrix:
|
|
19
|
-
ruby_version: [ruby-head, '3.3', '3.2', '3.1']
|
|
20
|
-
|
|
21
|
-
env:
|
|
22
|
-
COVERAGE: true
|
|
23
|
-
CC_TEST_REPORTER_ID: ${{ vars.CC_TEST_REPORTER_ID }}
|
|
24
|
-
|
|
25
|
-
steps:
|
|
26
|
-
- uses: actions/checkout@v4
|
|
27
|
-
|
|
28
|
-
- uses: ruby/setup-ruby@v1
|
|
29
|
-
with:
|
|
30
|
-
ruby-version: ${{ matrix.ruby_version }}
|
|
31
|
-
bundler-cache: true
|
|
32
|
-
continue-on-error: ${{ matrix.ruby_version == 'ruby-head' }}
|
|
33
|
-
|
|
34
|
-
- run: bundle exec rake
|
|
35
|
-
continue-on-error: ${{ matrix.ruby_version == 'ruby-head' }}
|
|
36
|
-
|
|
37
|
-
- uses: paambaati/codeclimate-action@v8.0.0
|
|
38
|
-
# Only upload coverage for the latest Ruby and don't run for PRs from forks
|
|
39
|
-
if: ${{ matrix.ruby_version == '3.3' && github.event.pull_request.head.repo.fork == false }}
|
data/.gitignore
DELETED
data/.rspec
DELETED
data/.rubocop.yml
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
require:
|
|
2
|
-
- rubocop-rspec
|
|
3
|
-
|
|
4
|
-
AllCops:
|
|
5
|
-
TargetRubyVersion: 3.1
|
|
6
|
-
DisplayCopNames: true
|
|
7
|
-
DisplayStyleGuide: true
|
|
8
|
-
ExtraDetails: true
|
|
9
|
-
SuggestExtensions: false
|
|
10
|
-
NewCops: enable
|
|
11
|
-
|
|
12
|
-
Layout/LineLength:
|
|
13
|
-
Max: 120
|
|
14
|
-
|
|
15
|
-
Style/Documentation:
|
|
16
|
-
Enabled: false
|
|
17
|
-
|
|
18
|
-
Style/HashEachMethods:
|
|
19
|
-
Enabled: true
|
|
20
|
-
|
|
21
|
-
Style/HashTransformKeys:
|
|
22
|
-
Enabled: true
|
|
23
|
-
|
|
24
|
-
Style/HashTransformValues:
|
|
25
|
-
Enabled: true
|
|
26
|
-
|
|
27
|
-
Metrics/BlockLength:
|
|
28
|
-
Exclude:
|
|
29
|
-
- 'spec/**/*'
|
|
30
|
-
|
|
31
|
-
Lint/MissingSuper:
|
|
32
|
-
AllowedParentClasses:
|
|
33
|
-
- Base
|
|
34
|
-
|
|
35
|
-
RSpec/MultipleExpectations:
|
|
36
|
-
Max: 5
|
|
37
|
-
|
|
38
|
-
RSpec/ExampleLength:
|
|
39
|
-
Max: 10
|
data/Gemfile
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
source 'https://rubygems.org'
|
|
4
|
-
|
|
5
|
-
git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
|
|
6
|
-
|
|
7
|
-
# Specify your gem's dependencies in sec_id.gemspec
|
|
8
|
-
gemspec
|
|
9
|
-
|
|
10
|
-
# Specify your gem's development dependencies below
|
|
11
|
-
gem 'rake', '>= 13'
|
|
12
|
-
|
|
13
|
-
gem 'rspec', '~> 3.9'
|
|
14
|
-
gem 'rspec_junit_formatter'
|
|
15
|
-
|
|
16
|
-
gem 'rubocop', '~> 1.64'
|
|
17
|
-
gem 'rubocop-rspec', '~> 3.0'
|
|
18
|
-
|
|
19
|
-
gem 'simplecov', '~> 0.22', require: false
|
|
20
|
-
gem 'simplecov_json_formatter', require: false
|
data/Rakefile
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'bundler/gem_tasks'
|
|
4
|
-
require 'rspec/core/rake_task'
|
|
5
|
-
require 'rubocop/rake_task'
|
|
6
|
-
|
|
7
|
-
RSpec::Core::RakeTask.new(:spec)
|
|
8
|
-
|
|
9
|
-
RuboCop::RakeTask.new do |task|
|
|
10
|
-
task.requires << 'rubocop-rspec'
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
task default: %i[rubocop spec]
|
data/bin/console
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
require 'bundler/setup'
|
|
5
|
-
require 'sec_id'
|
|
6
|
-
|
|
7
|
-
# You can add fixtures and/or initialization code here to make experimenting
|
|
8
|
-
# with your gem easier. You can also use a different console, if you like.
|
|
9
|
-
|
|
10
|
-
# (If you use this, don't forget to add pry to your Gemfile!)
|
|
11
|
-
# require "pry"
|
|
12
|
-
# Pry.start
|
|
13
|
-
|
|
14
|
-
require 'irb'
|
|
15
|
-
IRB.start(__FILE__)
|