sec_id 4.4.1 → 5.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/CHANGELOG.md +59 -3
- data/MIGRATION.md +257 -0
- data/README.md +286 -148
- data/lib/sec_id/base.rb +43 -29
- data/lib/sec_id/cei.rb +14 -2
- data/lib/sec_id/cfi.rb +41 -12
- data/lib/sec_id/cik.rb +21 -18
- data/lib/sec_id/concerns/checkable.rb +56 -14
- data/lib/sec_id/concerns/identifier_metadata.rb +56 -0
- data/lib/sec_id/concerns/normalizable.rb +60 -16
- data/lib/sec_id/concerns/validatable.rb +158 -0
- data/lib/sec_id/cusip.rb +24 -8
- data/lib/sec_id/detector.rb +156 -0
- data/lib/sec_id/errors.rb +67 -0
- data/lib/sec_id/figi.rb +38 -8
- data/lib/sec_id/fisn.rb +15 -4
- data/lib/sec_id/iban/country_rules.rb +4 -2
- data/lib/sec_id/iban.rb +59 -12
- data/lib/sec_id/isin.rb +25 -6
- data/lib/sec_id/lei.rb +22 -9
- data/lib/sec_id/occ.rb +50 -25
- data/lib/sec_id/sedol.rb +12 -7
- data/lib/sec_id/valoren.rb +29 -22
- data/lib/sec_id/version.rb +2 -2
- data/lib/sec_id/wkn.rb +10 -7
- data/lib/sec_id.rb +127 -6
- data/sec_id.gemspec +6 -3
- metadata +11 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0b0779f0d2735ded84be536bcc9690a4b106c729b03aab007a0458b6512020e8
|
|
4
|
+
data.tar.gz: 3b4d99c3534856e5e05ff68cec5355f3c7a2665e2854dad5415815e275a734b1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ec036b3fbf2c3d7a89ec823fed2dea6c8cb92be1ede8792bae623c1474454141a3dcc3da27d14c5e3734b050dd95b25672552fc9f4c6983bc68d807a7493d392
|
|
7
|
+
data.tar.gz: 397e7c17dd46c8aad067262289307317e04cdba4fac65d890aa3e3ff1a1b9ecf175ab916d9600dab7a7a5d4629b54f7750fd40f11c79ed220d3bca339a210d0c
|
data/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,62 @@ and [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/).
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
+
## [5.1.0] - 2026-02-19
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- `#==`, `#eql?`, and `#hash` methods on all identifier types — two instances of the same type with the same normalized form are equal and usable as Hash keys / in Sets
|
|
16
|
+
- `#to_h` method on all identifier types for consistent hash serialization — returns `{ type:, full_id:, normalized:, valid:, components: }` with type-specific component hashes (e.g. ISIN: `country_code`, `nsin`, `check_digit`)
|
|
17
|
+
- `#to_pretty_s` and `.to_pretty_s` display formatting methods on all identifier types, returning a human-readable string or `nil` for invalid input — with type-specific formats for IBAN (4-char groups), LEI (4-char groups), ISIN (CC + NSIN + CD), CUSIP (cusip6 + issue + CD), FIGI (prefix+G + random + CD), OCC (space-separated components), and Valoren (thousands grouping)
|
|
18
|
+
- Lookup service integration guides and runnable examples for OpenFIGI, SEC EDGAR, GLEIF, and Eurex APIs (`docs/guides/`, `examples/`)
|
|
19
|
+
- GitHub community standards files: Code of Conduct, Contributing guide, Security policy, issue templates, and PR template
|
|
20
|
+
|
|
21
|
+
## [5.0.0] - 2026-02-17
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- `Validatable`, `Normalizable`, and `IdentifierMetadata` concerns extracted from `Base`, making each responsibility independently includable
|
|
26
|
+
- `#validate` and `.validate` methods on all identifier types that eagerly trigger + cache errors and return `self`/instance
|
|
27
|
+
- `#validate!` and `.validate!` methods that raise `InvalidFormatError`, `InvalidCheckDigitError`, or `InvalidStructureError` on validation failure, returning self/instance on success
|
|
28
|
+
- Rails-like `#errors` API returning `Errors` with `details`, `messages`, `none?`, `any?`, `empty?`, `size`, `each`, and `to_a` on all identifier classes, with type-specific error detection for check digits, FIGI prefixes, CFI categories/groups, IBAN BBAN format, and OCC dates
|
|
29
|
+
- `#restore` instance method on check-digit identifiers returning the full identifier string without mutation
|
|
30
|
+
- `.restore` class method on check-digit identifiers returning the full identifier string
|
|
31
|
+
- `SecID.parse(str, types: nil)` and `SecID.parse!(str, types: nil)` methods that return a typed identifier instance for the most specific match, with optional type filtering
|
|
32
|
+
- `SecID.valid?(str, types: nil)` method for quick boolean validation against all or specific identifier types
|
|
33
|
+
- `SecID.detect(str)` method that identifies all matching identifier types for a given string, returning symbols sorted by specificity
|
|
34
|
+
- Metadata registry: `SecID.identifiers` returns all identifier classes, `SecID[:isin]` looks up by symbol key
|
|
35
|
+
- Metadata class methods on all identifiers: `short_name`, `full_name`, `id_length`, `example`, `has_check_digit?`
|
|
36
|
+
- `#normalized` and `#normalize` instance methods on all identifier types returning the canonical string form
|
|
37
|
+
- `#normalize!` instance method on all identifier types that mutates `full_id` to canonical form and returns `self`
|
|
38
|
+
- `.normalize(id)` class method on all identifier types that strips separators, upcases, validates, and returns the canonical string
|
|
39
|
+
- `SEPARATORS` constant in `Normalizable` (`/[\s-]/`) included in `Base`, with type-specific overrides for OCC and FISN (`/-/`)
|
|
40
|
+
|
|
41
|
+
### Changed
|
|
42
|
+
|
|
43
|
+
- **BREAKING:** Minimum Ruby version raised from 3.1 to 3.2 (Ruby 3.1 reached EOL on 2025-03-31)
|
|
44
|
+
- **BREAKING:** `#restore!` now returns `self` instead of a string; use `#restore` for the string return value
|
|
45
|
+
- **BREAKING:** `.restore!` now returns the restored instance instead of a string; use `.restore` for the string return value
|
|
46
|
+
- **BREAKING:** `#normalize!` on CIK, OCC, and Valoren now returns `self` instead of a string; use `#normalized` to get the canonical string
|
|
47
|
+
- **BREAKING:** Class-level `.normalize!` on CIK, OCC, and Valoren replaced by `.normalize` (non-bang) which returns the canonical string
|
|
48
|
+
- **BREAKING:** `Base#parse` always upcases input; the `upcase` keyword parameter is removed
|
|
49
|
+
- **BREAKING:** `#full_number` renamed to `#full_id` on all identifier types
|
|
50
|
+
- **BREAKING:** Ruby module renamed from `SecId` to `SecID` (e.g. `SecId::ISIN` → `SecID::ISIN`)
|
|
51
|
+
- Luhn helper methods in Checkable are now private (implementation detail)
|
|
52
|
+
|
|
53
|
+
### Removed
|
|
54
|
+
|
|
55
|
+
- Class-level `.normalize!` on CIK, OCC, and Valoren — replaced by `.normalize`
|
|
56
|
+
- `upcase` keyword parameter from `Base#parse`
|
|
57
|
+
- `#valid_format?` instance method (now private) and `.valid_format?` class method
|
|
58
|
+
- `OCC#full_symbol` method — use `#full_id` instead
|
|
59
|
+
|
|
60
|
+
### Fixed
|
|
61
|
+
|
|
62
|
+
- `to_str` now always returns the same value as `to_s` across all identifier types — previously LEI, IBAN, and Checkable identifiers could return divergent strings due to Ruby `alias` resolving to the parent class method
|
|
63
|
+
- OCC `#date` memoization for invalid dates — previously re-attempted parsing on every call instead of caching `nil`
|
|
64
|
+
- LEI `restore` and `to_s` now correctly pad single-digit check digits to 2 characters
|
|
65
|
+
- `Valoren#to_isin` no longer mutates the source instance
|
|
66
|
+
|
|
11
67
|
## [4.4.1] - 2026-02-05
|
|
12
68
|
|
|
13
69
|
### Fixed
|
|
@@ -115,7 +171,7 @@ and [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/).
|
|
|
115
171
|
|
|
116
172
|
### Added
|
|
117
173
|
|
|
118
|
-
- SEDOL numbers support: `
|
|
174
|
+
- SEDOL numbers support: `SecID::SEDOL`
|
|
119
175
|
|
|
120
176
|
### Changed
|
|
121
177
|
|
|
@@ -135,7 +191,7 @@ and [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/).
|
|
|
135
191
|
|
|
136
192
|
### Added
|
|
137
193
|
|
|
138
|
-
- CUSIP numbers support: `
|
|
194
|
+
- CUSIP numbers support: `SecID::CUSIP`
|
|
139
195
|
- CHANGELOG.md file added
|
|
140
196
|
|
|
141
197
|
### Changed
|
|
@@ -146,4 +202,4 @@ and [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/).
|
|
|
146
202
|
|
|
147
203
|
### Added
|
|
148
204
|
|
|
149
|
-
- ISIN numbers support: `
|
|
205
|
+
- ISIN numbers support: `SecID::ISIN`
|
data/MIGRATION.md
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
# Upgrading to SecID 5.0
|
|
2
|
+
|
|
3
|
+
This guide covers all breaking changes when upgrading from SecID 4.x to 5.0.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
- **Ruby 3.2+** (was 3.1+)
|
|
8
|
+
|
|
9
|
+
## Quick Reference
|
|
10
|
+
|
|
11
|
+
| What changed | Search pattern | Replacement |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| Module rename | `SecId::` | `SecID::` |
|
|
14
|
+
| Attribute rename | `.full_number` | `.full_id` |
|
|
15
|
+
| OCC method removed | `.full_symbol` | `.full_id` |
|
|
16
|
+
| ValidationResult rename | `SecID::ValidationResult` | `SecID::Errors` |
|
|
17
|
+
| `ValidationResult#valid?` | `result.valid?` | `result.none?` |
|
|
18
|
+
| `.validate` return type | `Klass.validate(id)` returns `ValidationResult` | `Klass.validate(id)` returns instance; use `.errors` for errors |
|
|
19
|
+
| `EXCEPTION_MAP` rename | `Base::EXCEPTION_MAP` | `Validatable::ERROR_MAP` |
|
|
20
|
+
| `.exception_for_error` rename | `.exception_for_error(code)` | `.error_class_for(code)` |
|
|
21
|
+
| `format_errors` rename (private) | `def format_errors` | `def detect_errors` |
|
|
22
|
+
| `validation_errors` rename (private) | `def validation_errors` | `def error_codes` |
|
|
23
|
+
| Instance `restore!` return | `= obj.restore!` | `obj.restore!` (returns `self`) or `= obj.restore` (returns string) |
|
|
24
|
+
| Class `restore!` return | `= Klass.restore!(id)` | `Klass.restore!(id)` (returns instance) or `= Klass.restore(id)` (returns string) |
|
|
25
|
+
| Instance `normalize!` return | `= obj.normalize!` | `obj.normalize!` (returns `self`) or `= obj.normalized` (returns string) |
|
|
26
|
+
| Class `normalize!` removed | `.normalize!(id)` | `.normalize(id)` |
|
|
27
|
+
| `valid_format?` removed | `.valid_format?` | `.valid?` or `.validate` |
|
|
28
|
+
| `parse` upcase param | `parse(id, upcase: false)` | `parse(id)` (always upcases) |
|
|
29
|
+
|
|
30
|
+
## Step-by-Step
|
|
31
|
+
|
|
32
|
+
### 1. Update Gemfile
|
|
33
|
+
|
|
34
|
+
```ruby
|
|
35
|
+
gem 'sec_id', '~> 5.0'
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Then run `bundle update sec_id`.
|
|
39
|
+
|
|
40
|
+
### 2. Rename module: SecId → SecID
|
|
41
|
+
|
|
42
|
+
The module was renamed to match the conventional acronym casing.
|
|
43
|
+
|
|
44
|
+
```ruby
|
|
45
|
+
# Before
|
|
46
|
+
SecId::ISIN.valid?('US5949181045')
|
|
47
|
+
|
|
48
|
+
# After
|
|
49
|
+
SecID::ISIN.valid?('US5949181045')
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Find all occurrences:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
grep -r 'SecId[^A-Z]' app/ lib/ spec/
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 3. Rename attribute: full_number → full_id
|
|
59
|
+
|
|
60
|
+
```ruby
|
|
61
|
+
# Before
|
|
62
|
+
isin.full_number # => 'US5949181045'
|
|
63
|
+
|
|
64
|
+
# After
|
|
65
|
+
isin.full_id # => 'US5949181045'
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
If you used `OCC#full_symbol`, replace it with `#full_id` as well.
|
|
69
|
+
|
|
70
|
+
Find all occurrences:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
grep -rE '\.(full_number|full_symbol)\b' app/ lib/ spec/
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 4. Update restore! usage (returns self now)
|
|
77
|
+
|
|
78
|
+
In v4, both instance and class `restore!` returned a string. In v5, they return `self`/instance for chaining. Use `restore` (without bang) to get the string.
|
|
79
|
+
|
|
80
|
+
```ruby
|
|
81
|
+
# Before (v4) — restore! returned a string
|
|
82
|
+
id_string = isin.restore!
|
|
83
|
+
|
|
84
|
+
# After (v5) — choose one:
|
|
85
|
+
id_string = isin.restore # string return (new method)
|
|
86
|
+
isin.restore! # mutates and returns self
|
|
87
|
+
|
|
88
|
+
# Class-level
|
|
89
|
+
id_string = SecID::ISIN.restore('US594918104') # returns string
|
|
90
|
+
instance = SecID::ISIN.restore!('US594918104') # returns instance
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 5. Update normalize! usage (returns self now)
|
|
94
|
+
|
|
95
|
+
Applies to CIK, OCC, and Valoren — the only types with normalization in v4.
|
|
96
|
+
|
|
97
|
+
```ruby
|
|
98
|
+
# Before (v4) — normalize! returned a string
|
|
99
|
+
normalized = cik.normalize!
|
|
100
|
+
|
|
101
|
+
# After (v5) — choose one:
|
|
102
|
+
normalized = cik.normalized # string return (new method)
|
|
103
|
+
cik.normalize! # mutates and returns self
|
|
104
|
+
|
|
105
|
+
# Class-level .normalize! removed — use .normalize
|
|
106
|
+
# Before
|
|
107
|
+
SecId::CIK.normalize!('1094517')
|
|
108
|
+
|
|
109
|
+
# After
|
|
110
|
+
SecID::CIK.normalize('1094517') # => '0001094517'
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Note: In v5, all identifier types support normalization, not just CIK/OCC/Valoren.
|
|
114
|
+
|
|
115
|
+
### 6. Remove calls to deleted methods
|
|
116
|
+
|
|
117
|
+
**`valid_format?` / `.valid_format?`** — removed from public API. Use `valid?` for boolean checks or `errors` for details:
|
|
118
|
+
|
|
119
|
+
```ruby
|
|
120
|
+
# Before
|
|
121
|
+
SecId::ISIN.valid_format?('US5949181045')
|
|
122
|
+
|
|
123
|
+
# After
|
|
124
|
+
SecID::ISIN.valid?('US5949181045')
|
|
125
|
+
# or for detailed errors:
|
|
126
|
+
SecID::ISIN.validate('US5949181045').messages
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**`OCC#full_symbol`** — use `#full_id`:
|
|
130
|
+
|
|
131
|
+
```ruby
|
|
132
|
+
# Before
|
|
133
|
+
occ.full_symbol
|
|
134
|
+
|
|
135
|
+
# After
|
|
136
|
+
occ.full_id
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### 7. Update parse calls in custom subclasses
|
|
140
|
+
|
|
141
|
+
If you subclass `SecID::Base` and call `parse` with `upcase: false`, remove that parameter. `parse` now always upcases input.
|
|
142
|
+
|
|
143
|
+
```ruby
|
|
144
|
+
# Before
|
|
145
|
+
parse(id, upcase: false)
|
|
146
|
+
|
|
147
|
+
# After
|
|
148
|
+
parse(id)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### 8. Handle now-private Luhn helpers
|
|
152
|
+
|
|
153
|
+
If you subclass `SecID::Base` with `Checkable` and call Luhn helper methods directly (`luhn_sum_double_add_double`, `luhn_sum_indexed`, `luhn_sum_standard`, etc.), these are now private. Use `calculate_check_digit` instead.
|
|
154
|
+
|
|
155
|
+
### 9. Update exception handling
|
|
156
|
+
|
|
157
|
+
The module rename affects exception class references:
|
|
158
|
+
|
|
159
|
+
```ruby
|
|
160
|
+
# Before
|
|
161
|
+
rescue SecId::InvalidFormatError
|
|
162
|
+
|
|
163
|
+
# After
|
|
164
|
+
rescue SecID::InvalidFormatError
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
v5 adds two new exception types for more granular error handling:
|
|
168
|
+
|
|
169
|
+
- `SecID::InvalidCheckDigitError` — raised when check digit doesn't match
|
|
170
|
+
- `SecID::InvalidStructureError` — raised for type-specific structural errors (FIGI prefix, CFI category/group, IBAN BBAN, OCC date)
|
|
171
|
+
|
|
172
|
+
Both inherit from `SecID::Error`, so existing `rescue SecID::Error` blocks still work. Optionally catch the specific types:
|
|
173
|
+
|
|
174
|
+
```ruby
|
|
175
|
+
begin
|
|
176
|
+
SecID::ISIN.new('US5949181040').validate!
|
|
177
|
+
rescue SecID::InvalidCheckDigitError => e
|
|
178
|
+
# handle bad check digit specifically
|
|
179
|
+
rescue SecID::InvalidFormatError => e
|
|
180
|
+
# handle format errors
|
|
181
|
+
end
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### 10. Rename ValidationResult to Errors
|
|
185
|
+
|
|
186
|
+
`ValidationResult` has been renamed to `Errors`. The `valid?` method is replaced by `none?` for clearer semantics.
|
|
187
|
+
|
|
188
|
+
```ruby
|
|
189
|
+
# Before
|
|
190
|
+
result = SecId::ISIN.new('US5949181045').errors
|
|
191
|
+
result.valid? # => true
|
|
192
|
+
|
|
193
|
+
# After
|
|
194
|
+
result = SecID::ISIN.new('US5949181045').errors
|
|
195
|
+
result.none? # => true
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Find all occurrences:
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
grep -rE 'ValidationResult|\.valid\?' app/ lib/ spec/
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### 11. Update .validate usage (returns instance now)
|
|
205
|
+
|
|
206
|
+
`.validate` now returns the identifier instance (with errors cached) instead of a `ValidationResult`/`Errors` object.
|
|
207
|
+
|
|
208
|
+
```ruby
|
|
209
|
+
# Before (v4)
|
|
210
|
+
result = SecId::ISIN.validate('US5949181045') # => #<SecId::ValidationResult>
|
|
211
|
+
result.valid?
|
|
212
|
+
|
|
213
|
+
# After (v5)
|
|
214
|
+
instance = SecID::ISIN.validate('US5949181045') # => #<SecID::ISIN>
|
|
215
|
+
instance.errors.none?
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### 12. Update subclass overrides (private API)
|
|
219
|
+
|
|
220
|
+
If you subclass `SecID::Base` and override private validation methods:
|
|
221
|
+
|
|
222
|
+
```ruby
|
|
223
|
+
# Before
|
|
224
|
+
def format_errors # renamed
|
|
225
|
+
def validation_errors # renamed
|
|
226
|
+
|
|
227
|
+
# After
|
|
228
|
+
def detect_errors
|
|
229
|
+
def error_codes
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
If you reference `EXCEPTION_MAP` or `exception_for_error`:
|
|
233
|
+
|
|
234
|
+
```ruby
|
|
235
|
+
# Before
|
|
236
|
+
Base::EXCEPTION_MAP
|
|
237
|
+
Klass.exception_for_error(:code)
|
|
238
|
+
|
|
239
|
+
# After
|
|
240
|
+
Validatable::ERROR_MAP
|
|
241
|
+
Klass.error_class_for(:code)
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### 13. Explore new features (optional)
|
|
245
|
+
|
|
246
|
+
v5 adds several features beyond the breaking changes:
|
|
247
|
+
|
|
248
|
+
- **Structured validation** — `#errors` returns an `Errors` object with `details`, `messages`, `none?`, `any?`, `empty?`, `size`, `each`
|
|
249
|
+
- **Eager validation** — `#validate` / `.validate` triggers validation and returns the instance
|
|
250
|
+
- **Fail-fast validation** — `#validate!` raises descriptive exceptions
|
|
251
|
+
- **Type detection** — `SecID.detect('US5949181045')` returns `[:isin]`
|
|
252
|
+
- **Universal parsing** — `SecID.parse('US5949181045')` returns a typed instance
|
|
253
|
+
- **Quick validation** — `SecID.valid?('US5949181045')` validates against all types
|
|
254
|
+
- **Metadata registry** — `SecID.identifiers`, `SecID[:isin]`, `SecID::ISIN.full_name`
|
|
255
|
+
- **Universal normalization** — all types now support `#normalized`, `#normalize!`, `.normalize`
|
|
256
|
+
|
|
257
|
+
See [README.md](README.md) for full usage examples.
|