gov_codes 0.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 +7 -0
- data/.simplecov +15 -0
- data/.standard.yml +3 -0
- data/.tool-versions +1 -0
- data/CHANGELOG.md +12 -0
- data/README.md +89 -0
- data/Rakefile +16 -0
- data/lib/gov_codes/afsc/enlisted.rb +103 -0
- data/lib/gov_codes/afsc/enlisted.yml +725 -0
- data/lib/gov_codes/afsc/officer.rb +84 -0
- data/lib/gov_codes/afsc.rb +16 -0
- data/lib/gov_codes/data_loader.rb +75 -0
- data/lib/gov_codes/version.rb +5 -0
- data/lib/gov_codes.rb +7 -0
- data/sig/gov_codes.rbs +4 -0
- metadata +56 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 96c91603e5956e6918476045972a183754d150c8bfef8a8f7d33de5159ca4c60
|
4
|
+
data.tar.gz: 72ade76ae58f72cfcc07ff2f301bd3a7067fc5e1fb836ca91438b767ff1198ae
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cbf18e6e8e9099d19c023e12ed688374335213dc04ec9c95217a2b4f3df085fa29be735d4de828ddea254306357ff7463d4e2346e344ccde9281ac0c593ca258
|
7
|
+
data.tar.gz: 82a5a80e5d3880e65711f7ee09c0d9b3c50b866faf879360997bfec5b98830bd2fe4c49f467e3545800cd80f6e9acd6f3cc41c7735cbdf06c592ff7fdc0ee172
|
data/.simplecov
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
SimpleCov.start do
|
2
|
+
# enable_coverage :branch
|
3
|
+
|
4
|
+
# Add any files or directories you want to exclude from coverage
|
5
|
+
add_filter "/test/", "lib/gov_codes/version.rb"
|
6
|
+
|
7
|
+
# Set minimum coverage requirements
|
8
|
+
# minimum_coverage 80
|
9
|
+
|
10
|
+
# Track all files in the lib directory
|
11
|
+
track_files "lib/**/*.rb"
|
12
|
+
|
13
|
+
# Group files by module
|
14
|
+
add_group "AFSC", "lib/gov_codes/afsc"
|
15
|
+
end
|
data/.standard.yml
ADDED
data/.tool-versions
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby 3.4.3
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
6
|
+
and this project adheres to [Semantic Versioning](http://semver.org/).
|
7
|
+
|
8
|
+
## [0.1.0] - 2025-05-02
|
9
|
+
|
10
|
+
### Added
|
11
|
+
|
12
|
+
- AFSC codes
|
data/README.md
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
# Gov Codes
|
2
|
+
|
3
|
+
Understand and process information used by the US government.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'gov_codes'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
```bash
|
16
|
+
$ bundle install
|
17
|
+
```
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
```bash
|
22
|
+
$ gem install gov_codes
|
23
|
+
```
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
### Basic Usage
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
require 'gov_codes/afsc'
|
31
|
+
|
32
|
+
# Find an enlisted AFSC code
|
33
|
+
code = GovCodes::AFSC.find("1A1X2")
|
34
|
+
puts code.name # => "Aircrew operations"
|
35
|
+
puts code.career_field # => "1A"
|
36
|
+
puts code.career_field_subdivision # => "1A1"
|
37
|
+
puts code.skill_level # => "X"
|
38
|
+
puts code.specific_afsc # => "1A1X2"
|
39
|
+
puts code.shredout # => nil
|
40
|
+
|
41
|
+
# Find an officer AFSC code
|
42
|
+
code = GovCodes::AFSC.find("11M4")
|
43
|
+
puts code.name # => "Pilot"
|
44
|
+
puts code.career_group # => "11"
|
45
|
+
puts code.functional_area # => "M"
|
46
|
+
puts code.qualification_level # => "4"
|
47
|
+
puts code.shredout # => nil
|
48
|
+
```
|
49
|
+
|
50
|
+
### Extending with Custom AFSC Codes
|
51
|
+
|
52
|
+
You can extend the default AFSC codes with your own custom codes by placing a YAML file in your application's load path:
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
# In your application's lib/gov_codes/afsc/enlisted.yml
|
56
|
+
9Z:
|
57
|
+
name: Custom AFSC
|
58
|
+
subcategories:
|
59
|
+
0X1:
|
60
|
+
name: Custom Subcategory
|
61
|
+
subcategories:
|
62
|
+
A:
|
63
|
+
name: Custom Shredout
|
64
|
+
```
|
65
|
+
|
66
|
+
The gem will automatically merge your custom codes with the default codes, overriding any existing codes.
|
67
|
+
|
68
|
+
## Development
|
69
|
+
|
70
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
71
|
+
|
72
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
73
|
+
|
74
|
+
This project is managed with [Reissue](https://github.com/SOFware/reissue).
|
75
|
+
|
76
|
+
To release a new version, make your changes and be sure to update the CHANGELOG.md.
|
77
|
+
|
78
|
+
To release a new version:
|
79
|
+
|
80
|
+
1. `bundle exec rake build:checksum`
|
81
|
+
2. `bundle exec rake release`
|
82
|
+
|
83
|
+
## Contributing
|
84
|
+
|
85
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/SOFware/gov_codes.
|
86
|
+
|
87
|
+
## License
|
88
|
+
|
89
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
require "minitest/test_task"
|
5
|
+
|
6
|
+
Minitest::TestTask.create
|
7
|
+
|
8
|
+
require "standard/rake"
|
9
|
+
|
10
|
+
task default: %i[test standard]
|
11
|
+
|
12
|
+
require "reissue/gem"
|
13
|
+
|
14
|
+
Reissue::Task.create :reissue do |task|
|
15
|
+
task.version_file = "lib/gov_codes/version.rb"
|
16
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require "strscan"
|
2
|
+
require "yaml"
|
3
|
+
require_relative "../data_loader"
|
4
|
+
|
5
|
+
puts "Loading enlisted.rb"
|
6
|
+
|
7
|
+
module GovCodes
|
8
|
+
module AFSC
|
9
|
+
module Enlisted
|
10
|
+
class Parser
|
11
|
+
def initialize(code)
|
12
|
+
@code = code
|
13
|
+
end
|
14
|
+
|
15
|
+
def parse
|
16
|
+
scanner = StringScanner.new(@code.to_s)
|
17
|
+
result = {
|
18
|
+
prefix: nil,
|
19
|
+
career_group: nil,
|
20
|
+
career_field: nil,
|
21
|
+
career_field_subdivision: nil,
|
22
|
+
skill_level: nil,
|
23
|
+
specific_afsc: nil,
|
24
|
+
subcategory: nil,
|
25
|
+
shredout: nil
|
26
|
+
}
|
27
|
+
|
28
|
+
# Scan for prefix (optional)
|
29
|
+
result[:prefix] = scanner.scan(/[A-Z]/)
|
30
|
+
|
31
|
+
# Scan for career group (single digit)
|
32
|
+
career_group = scanner.scan(/\d/)
|
33
|
+
return result unless career_group
|
34
|
+
result[:career_group] = career_group.to_sym
|
35
|
+
|
36
|
+
# Scan for career field letter and combine with career group
|
37
|
+
career_field_letter = scanner.scan(/[A-Z]/)
|
38
|
+
return result unless career_field_letter
|
39
|
+
result[:career_field] = :"#{career_group}#{career_field_letter}"
|
40
|
+
|
41
|
+
# Scan for subdivision digit and combine with career field
|
42
|
+
subdivision_digit = scanner.scan(/\d/)
|
43
|
+
return result unless subdivision_digit
|
44
|
+
result[:career_field_subdivision] = :"#{result[:career_field]}#{subdivision_digit}"
|
45
|
+
|
46
|
+
# Scan for skill level
|
47
|
+
skill_level = scanner.scan(/[\dX]/)
|
48
|
+
return result unless skill_level
|
49
|
+
result[:skill_level] = skill_level.to_sym
|
50
|
+
|
51
|
+
# Scan for specific AFSC digit and combine with previous components
|
52
|
+
specific_digit = scanner.scan(/\d/)
|
53
|
+
return result unless specific_digit
|
54
|
+
result[:specific_afsc] = :"#{result[:career_field_subdivision]}#{result[:skill_level]}#{specific_digit}"
|
55
|
+
|
56
|
+
result[:subcategory] = result[:specific_afsc][2..4].to_sym
|
57
|
+
|
58
|
+
# Scan for shredout (optional)
|
59
|
+
result[:shredout] = scanner.scan(/[A-Z]/)&.to_sym
|
60
|
+
|
61
|
+
# Check if we've reached the end of the string
|
62
|
+
return result unless scanner.eos?
|
63
|
+
|
64
|
+
result
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
extend GovCodes::DataLoader
|
69
|
+
DATA = data
|
70
|
+
|
71
|
+
Code = Data.define(
|
72
|
+
:prefix,
|
73
|
+
:career_group,
|
74
|
+
:career_field,
|
75
|
+
:career_field_subdivision,
|
76
|
+
:skill_level,
|
77
|
+
:specific_afsc,
|
78
|
+
:subcategory,
|
79
|
+
:shredout,
|
80
|
+
:name
|
81
|
+
)
|
82
|
+
|
83
|
+
def self.find(code)
|
84
|
+
code = code.to_s
|
85
|
+
CODES[code] ||= begin
|
86
|
+
parser = Parser.new(code)
|
87
|
+
result = parser.parse
|
88
|
+
|
89
|
+
return nil if result.reject { |_, v| v.nil? }.empty?
|
90
|
+
|
91
|
+
# Find the name by recursively searching the codes hash
|
92
|
+
name = find_name_recursive(result)
|
93
|
+
|
94
|
+
# Add the name to the result
|
95
|
+
result[:name] = name
|
96
|
+
|
97
|
+
# Create a new Code object with the result
|
98
|
+
Code.new(**result)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,725 @@
|
|
1
|
+
1A:
|
2
|
+
name: Aircrew operations
|
3
|
+
subcategories:
|
4
|
+
1X2:
|
5
|
+
name: Mobility force aviator
|
6
|
+
subcategories:
|
7
|
+
A:
|
8
|
+
name: C-5 flight engineer
|
9
|
+
B:
|
10
|
+
name: C-5 loadmaster
|
11
|
+
C:
|
12
|
+
name: C-17 loadmaster
|
13
|
+
D:
|
14
|
+
name: C-130J loadmaster
|
15
|
+
E:
|
16
|
+
name: WC-130J loadmaster
|
17
|
+
F:
|
18
|
+
name: E-3 flight engineer
|
19
|
+
G:
|
20
|
+
name: KC-46 boom operator
|
21
|
+
H:
|
22
|
+
name: KC-135 boom operator
|
23
|
+
I:
|
24
|
+
name: KC-10 boom operator
|
25
|
+
J:
|
26
|
+
name: KC-10 flight engineer
|
27
|
+
K:
|
28
|
+
name: E-8 flight engineer
|
29
|
+
L:
|
30
|
+
name: C-130H flight engineer
|
31
|
+
M:
|
32
|
+
name: CEM mobility force aviator
|
33
|
+
N:
|
34
|
+
name: C-130H loadmaster
|
35
|
+
O:
|
36
|
+
name: EC-130H flight engineer
|
37
|
+
Z:
|
38
|
+
name: Data mask mobility force aviator
|
39
|
+
1X3:
|
40
|
+
name: Special mission aviator
|
41
|
+
subcategories:
|
42
|
+
A:
|
43
|
+
name: AC-130J gunner
|
44
|
+
B:
|
45
|
+
name: CV-22 flight engineer
|
46
|
+
C:
|
47
|
+
name: UH-1N flight engineer
|
48
|
+
D:
|
49
|
+
name: HC-130J loadmaster
|
50
|
+
E:
|
51
|
+
name: MC-130J loadmaster
|
52
|
+
F:
|
53
|
+
name: HH-60 flight engineer
|
54
|
+
G:
|
55
|
+
name: MH-139 flight engineer
|
56
|
+
H:
|
57
|
+
name: C-146 loadmaster
|
58
|
+
S:
|
59
|
+
name: CEM special mission aviator
|
60
|
+
Z:
|
61
|
+
name: SMA data masked
|
62
|
+
1X8:
|
63
|
+
name: Executive mission aviator
|
64
|
+
subcategories:
|
65
|
+
A:
|
66
|
+
name: C-32/C-40 flight attendant
|
67
|
+
B:
|
68
|
+
name: C-32/C-40 communications systems operator
|
69
|
+
C:
|
70
|
+
name: C-37 flight attendant
|
71
|
+
D:
|
72
|
+
name: C-37 flight engineer
|
73
|
+
E:
|
74
|
+
name: CEM executive mission aviator
|
75
|
+
F:
|
76
|
+
name: C-37 communications systems operator
|
77
|
+
G:
|
78
|
+
name: E-4 flight engineer
|
79
|
+
H:
|
80
|
+
name: E-4 flight attendant
|
81
|
+
I:
|
82
|
+
name: E-4 communications systems operator/technician/controller
|
83
|
+
J:
|
84
|
+
name: Presidential Airlift Group, flight engineer
|
85
|
+
K:
|
86
|
+
name: Presidential Airlift Group, flight attendant
|
87
|
+
L:
|
88
|
+
name: Presidential Airlift Group, communication system operator
|
89
|
+
Z:
|
90
|
+
name: EMA data mask
|
91
|
+
1B:
|
92
|
+
name: Cyber warfare
|
93
|
+
subcategories:
|
94
|
+
4X1:
|
95
|
+
name: Cyber warfare operations
|
96
|
+
1C:
|
97
|
+
name: Command and control systems operations
|
98
|
+
subcategories:
|
99
|
+
0X2:
|
100
|
+
name: Aviation resource management
|
101
|
+
1X1:
|
102
|
+
name: Air traffic control
|
103
|
+
3X1:
|
104
|
+
name: All-domain command and control operations (C2 OPS)
|
105
|
+
5X1:
|
106
|
+
name: Battle management ops
|
107
|
+
subcategories:
|
108
|
+
D:
|
109
|
+
name: Weapons director
|
110
|
+
6X1:
|
111
|
+
name: Space systems operations
|
112
|
+
7X1:
|
113
|
+
name: Airfield management
|
114
|
+
8X3:
|
115
|
+
name: Radar, airfield & weather systems (RAWS)
|
116
|
+
1D:
|
117
|
+
name: Cyber defense operations
|
118
|
+
subcategories:
|
119
|
+
7X1:
|
120
|
+
name: Cyber defense operations (to become IT systems technician)
|
121
|
+
subcategories:
|
122
|
+
A:
|
123
|
+
name: Network operations (to be phased out)
|
124
|
+
B:
|
125
|
+
name: Systems operations (to be phased out)
|
126
|
+
D:
|
127
|
+
name: Security operations (to be phased out)
|
128
|
+
E:
|
129
|
+
name: Client systems operations (to be phased out)
|
130
|
+
K:
|
131
|
+
name: Knowledge operations (to be phased out)
|
132
|
+
M:
|
133
|
+
name: Mission defense activities (to become cybersecurity in November 2024)
|
134
|
+
P:
|
135
|
+
name: Data operations (to become 1D7X4 data engineering)
|
136
|
+
Q:
|
137
|
+
name: Enterprise operations
|
138
|
+
R:
|
139
|
+
name: RF operations (to become 1D7X2R RF transmissions operations)
|
140
|
+
W:
|
141
|
+
name: Expeditionary communications
|
142
|
+
Z:
|
143
|
+
name: Software development operations (to be phased out)
|
144
|
+
7X2:
|
145
|
+
name: Spectrum defense operations (to become RF & electromagnetic activities)
|
146
|
+
subcategories:
|
147
|
+
F:
|
148
|
+
name: Spectrum operations (to become spectrum management)
|
149
|
+
7X3:
|
150
|
+
name: Cable and antenna defense operations (to become cable & antenna operations)
|
151
|
+
subcategories:
|
152
|
+
C:
|
153
|
+
name: Cable and antenna operations (to be phased out)
|
154
|
+
1H:
|
155
|
+
name: Aerospace physiology
|
156
|
+
subcategories:
|
157
|
+
0X1:
|
158
|
+
name: Aerospace physiology
|
159
|
+
1N:
|
160
|
+
name: Intelligence
|
161
|
+
subcategories:
|
162
|
+
0X1:
|
163
|
+
name: All Source intelligence analyst
|
164
|
+
092:
|
165
|
+
name: Intelligence superintendent
|
166
|
+
1X1:
|
167
|
+
name: Geospatial intelligence
|
168
|
+
subcategories:
|
169
|
+
A:
|
170
|
+
name: Imagery analyst
|
171
|
+
292:
|
172
|
+
name: Cryptologic intelligence superintendent
|
173
|
+
2X1:
|
174
|
+
name: Signals intelligence
|
175
|
+
subcategories:
|
176
|
+
A:
|
177
|
+
name: Electronic non-communications analyst
|
178
|
+
C:
|
179
|
+
name: Communications analyst
|
180
|
+
3X1:
|
181
|
+
name: Cryptologic language analyst
|
182
|
+
subcategories:
|
183
|
+
F:
|
184
|
+
name: Arabic
|
185
|
+
G:
|
186
|
+
name: Chinese
|
187
|
+
H:
|
188
|
+
name: Korean
|
189
|
+
I:
|
190
|
+
name: Russian
|
191
|
+
J:
|
192
|
+
name: Spanish
|
193
|
+
K:
|
194
|
+
name: Persian
|
195
|
+
M:
|
196
|
+
name: Pashto
|
197
|
+
N:
|
198
|
+
name: Urdu
|
199
|
+
Z:
|
200
|
+
name: Divested languages
|
201
|
+
4X1:
|
202
|
+
name: Cyber intelligence
|
203
|
+
subcategories:
|
204
|
+
A:
|
205
|
+
name: Analyst
|
206
|
+
4X2:
|
207
|
+
name: Cryptologic analyst & reporter
|
208
|
+
7X1:
|
209
|
+
name: Human intelligence specialist
|
210
|
+
8X1:
|
211
|
+
name: Targeting analyst
|
212
|
+
1P:
|
213
|
+
name: Aircrew flight equipment
|
214
|
+
subcategories:
|
215
|
+
0X1:
|
216
|
+
name: Aircrew flight equipment
|
217
|
+
subcategories:
|
218
|
+
A:
|
219
|
+
name: Ejection seat aircraft
|
220
|
+
B:
|
221
|
+
name: Non-ejection seat aircraft
|
222
|
+
1S:
|
223
|
+
name: Safety
|
224
|
+
subcategories:
|
225
|
+
0X1:
|
226
|
+
name: Safety
|
227
|
+
1T:
|
228
|
+
name: Special warfare enabler
|
229
|
+
subcategories:
|
230
|
+
0X1:
|
231
|
+
name: Survival, evasion, resistance, escape (SERE) specialist
|
232
|
+
1U:
|
233
|
+
name: Aircrew operations (RPA)
|
234
|
+
subcategories:
|
235
|
+
0X1:
|
236
|
+
name: Sensor operator
|
237
|
+
subcategories:
|
238
|
+
N:
|
239
|
+
name: RQ-4
|
240
|
+
Q:
|
241
|
+
name: MQ-1
|
242
|
+
R:
|
243
|
+
name: MQ-9
|
244
|
+
T:
|
245
|
+
name: MC-12
|
246
|
+
U:
|
247
|
+
name: AC-130U
|
248
|
+
V:
|
249
|
+
name: AC-130J
|
250
|
+
W:
|
251
|
+
name: AC-130W
|
252
|
+
Y:
|
253
|
+
name: CAA
|
254
|
+
1X1:
|
255
|
+
name: Remotely piloted aircraft (RPA) pilot
|
256
|
+
subcategories:
|
257
|
+
O:
|
258
|
+
name: RQ-4
|
259
|
+
R:
|
260
|
+
name: MQ-9
|
261
|
+
1W:
|
262
|
+
name: Weather
|
263
|
+
subcategories:
|
264
|
+
0X1:
|
265
|
+
name: Weather
|
266
|
+
2A:
|
267
|
+
name: Aerospace Maintenance
|
268
|
+
subcategories:
|
269
|
+
0X1:
|
270
|
+
name: Avionics Test Station and Components
|
271
|
+
3X3:
|
272
|
+
name: Tactical Aircraft Maintenance
|
273
|
+
subcategories:
|
274
|
+
A:
|
275
|
+
name: A-10/U-2
|
276
|
+
B:
|
277
|
+
name: F-15
|
278
|
+
C:
|
279
|
+
name: F-16
|
280
|
+
D:
|
281
|
+
name: F-22
|
282
|
+
E:
|
283
|
+
name: F-35
|
284
|
+
M:
|
285
|
+
name: Special Operations Forces/Personnel Recovery
|
286
|
+
3X4:
|
287
|
+
name: Fighter Aircraft Integrated Avionics
|
288
|
+
subcategories:
|
289
|
+
A:
|
290
|
+
name: A-10/F-15
|
291
|
+
B:
|
292
|
+
name: F-16
|
293
|
+
C:
|
294
|
+
name: F-22
|
295
|
+
D:
|
296
|
+
name: F-35
|
297
|
+
3X5:
|
298
|
+
name: Advanced Fighter Aircraft Integrated Avionics
|
299
|
+
3X7:
|
300
|
+
name: Tactical Aircraft Maintenance (5th Generation)
|
301
|
+
subcategories:
|
302
|
+
A:
|
303
|
+
name: F-22
|
304
|
+
B:
|
305
|
+
name: F-35
|
306
|
+
3X8:
|
307
|
+
name: Remotely Piloted Aircraft Maintenance
|
308
|
+
subcategories:
|
309
|
+
A:
|
310
|
+
name: MQ-1/MQ-9
|
311
|
+
B:
|
312
|
+
name: RQ-4
|
313
|
+
5X1:
|
314
|
+
name: Aerospace Maintenance (CEM)
|
315
|
+
5X2:
|
316
|
+
name: Helicopter/Tiltrotor Aircraft Maintenance
|
317
|
+
subcategories:
|
318
|
+
A:
|
319
|
+
name: UH-1
|
320
|
+
B:
|
321
|
+
name: HH-60
|
322
|
+
C:
|
323
|
+
name: CV-22
|
324
|
+
5X4:
|
325
|
+
name: Refuel/Bomber Aircraft Maintenance
|
326
|
+
subcategories:
|
327
|
+
A:
|
328
|
+
name: KC-46
|
329
|
+
B:
|
330
|
+
name: KC-135
|
331
|
+
C:
|
332
|
+
name: B-1
|
333
|
+
D:
|
334
|
+
name: B-2
|
335
|
+
E:
|
336
|
+
name: B-52
|
337
|
+
7X1:
|
338
|
+
name: Aircraft Metals Technology
|
339
|
+
7X2:
|
340
|
+
name: Nondestructive Inspection
|
341
|
+
7X3:
|
342
|
+
name: Aircraft Structural Maintenance
|
343
|
+
7X4:
|
344
|
+
name: Survival Equipment
|
345
|
+
8X1:
|
346
|
+
name: Mobility Air Forces Integrated Communication/Navigation/Mission Systems
|
347
|
+
8X2:
|
348
|
+
name: Mobility Air Forces Integrated Instrument and Flight Control Systems
|
349
|
+
2F:
|
350
|
+
name: Fuels
|
351
|
+
subcategories:
|
352
|
+
0X1:
|
353
|
+
name: Fuels
|
354
|
+
2G:
|
355
|
+
name: Logistics Plans
|
356
|
+
subcategories:
|
357
|
+
0X1:
|
358
|
+
name: Logistics Plans
|
359
|
+
2M:
|
360
|
+
name: Missile and Space Systems Maintenance
|
361
|
+
subcategories:
|
362
|
+
0X1:
|
363
|
+
name: Missile and Space Systems Electronic Maintenance
|
364
|
+
0X2:
|
365
|
+
name: Missile and Space Systems Maintenance
|
366
|
+
0X3:
|
367
|
+
name: Missile and Space Facilities
|
368
|
+
2P:
|
369
|
+
name: Precision Measurement Equipment Laboratory
|
370
|
+
subcategories:
|
371
|
+
0X1:
|
372
|
+
name: Precision Measurement Equipment Laboratory
|
373
|
+
2R:
|
374
|
+
name: Maintenance Management
|
375
|
+
subcategories:
|
376
|
+
0X1:
|
377
|
+
name: Maintenance Management Analysis
|
378
|
+
1X1:
|
379
|
+
name: Maintenance Management Production
|
380
|
+
2S:
|
381
|
+
name: Materiel Management
|
382
|
+
subcategories:
|
383
|
+
0X1:
|
384
|
+
name: Materiel Management
|
385
|
+
2T:
|
386
|
+
name: Transportation and Vehicle Management
|
387
|
+
subcategories:
|
388
|
+
0X1:
|
389
|
+
name: Traffic Management
|
390
|
+
1X1:
|
391
|
+
name: Vehicle Operations
|
392
|
+
2X1:
|
393
|
+
name: Vehicle Management
|
394
|
+
3X1:
|
395
|
+
name: Mission Generation Vehicular Equipment Maintenance
|
396
|
+
3X2:
|
397
|
+
name: Special Vehicle Maintenance
|
398
|
+
3X7:
|
399
|
+
name: Vehicle Management and Analysis
|
400
|
+
2W:
|
401
|
+
name: Munitions and Weapons
|
402
|
+
subcategories:
|
403
|
+
0X1:
|
404
|
+
name: Munitions Systems
|
405
|
+
1X1:
|
406
|
+
name: Aircraft Armament Systems
|
407
|
+
subcategories:
|
408
|
+
A:
|
409
|
+
name: A-10/F-15
|
410
|
+
B:
|
411
|
+
name: F-16
|
412
|
+
C:
|
413
|
+
name: F-22
|
414
|
+
D:
|
415
|
+
name: F-35
|
416
|
+
3A:
|
417
|
+
name: Personnel
|
418
|
+
subcategories:
|
419
|
+
0X1:
|
420
|
+
name: Personnel
|
421
|
+
3D:
|
422
|
+
name: Information Technology
|
423
|
+
subcategories:
|
424
|
+
0X1:
|
425
|
+
name: Knowledge Operations Management
|
426
|
+
0X2:
|
427
|
+
name: Cyber Systems Operations
|
428
|
+
0X3:
|
429
|
+
name: Cyber Surety
|
430
|
+
0X4:
|
431
|
+
name: Computer Systems Programming
|
432
|
+
1X1:
|
433
|
+
name: Client Systems
|
434
|
+
1X2:
|
435
|
+
name: Cyber Systems Operations
|
436
|
+
1X3:
|
437
|
+
name: Cyber Surety
|
438
|
+
3E:
|
439
|
+
name: Engineering
|
440
|
+
subcategories:
|
441
|
+
0X1:
|
442
|
+
name: Engineering
|
443
|
+
3F:
|
444
|
+
name: Force Support
|
445
|
+
subcategories:
|
446
|
+
0X1:
|
447
|
+
name: Services
|
448
|
+
3G:
|
449
|
+
name: Ground Transportation
|
450
|
+
subcategories:
|
451
|
+
0X1:
|
452
|
+
name: Ground Transportation
|
453
|
+
3H:
|
454
|
+
name: Historian
|
455
|
+
subcategories:
|
456
|
+
0X1:
|
457
|
+
name: Historian
|
458
|
+
3M:
|
459
|
+
name: Services
|
460
|
+
subcategories:
|
461
|
+
0X1:
|
462
|
+
name: Services
|
463
|
+
3N:
|
464
|
+
name: Public Affairs
|
465
|
+
subcategories:
|
466
|
+
0X1:
|
467
|
+
name: Public Affairs
|
468
|
+
3P:
|
469
|
+
name: Security Forces
|
470
|
+
subcategories:
|
471
|
+
0X1:
|
472
|
+
name: Security Forces
|
473
|
+
3S:
|
474
|
+
name: Support
|
475
|
+
subcategories:
|
476
|
+
0X1:
|
477
|
+
name: Support
|
478
|
+
4A:
|
479
|
+
name: Health Services Management
|
480
|
+
subcategories:
|
481
|
+
1X1:
|
482
|
+
name: Medical material
|
483
|
+
4B:
|
484
|
+
name: Biomedical Equipment
|
485
|
+
subcategories:
|
486
|
+
0X1:
|
487
|
+
name: Biomedical Equipment
|
488
|
+
4C:
|
489
|
+
name: Mental Health Service
|
490
|
+
subcategories:
|
491
|
+
0X1:
|
492
|
+
name: Mental Health Service
|
493
|
+
4D:
|
494
|
+
name: Diagnostic Imaging
|
495
|
+
subcategories:
|
496
|
+
0X1:
|
497
|
+
name: Diagnostic Imaging
|
498
|
+
4E:
|
499
|
+
name: Aerospace Physiology
|
500
|
+
subcategories:
|
501
|
+
0X1:
|
502
|
+
name: Aerospace Physiology
|
503
|
+
4H:
|
504
|
+
name: Cardiopulmonary Laboratory
|
505
|
+
subcategories:
|
506
|
+
0X1:
|
507
|
+
name: Cardiopulmonary Laboratory
|
508
|
+
4J:
|
509
|
+
name: Physical Medicine
|
510
|
+
subcategories:
|
511
|
+
0X1:
|
512
|
+
name: Physical Medicine
|
513
|
+
4M:
|
514
|
+
name: Aerospace and Operational Physiology
|
515
|
+
subcategories:
|
516
|
+
0X1:
|
517
|
+
name: Aerospace and Operational Physiology
|
518
|
+
4N:
|
519
|
+
name: Aerospace Medical Service
|
520
|
+
subcategories:
|
521
|
+
0X1:
|
522
|
+
name: Aerospace Medical Service
|
523
|
+
4P:
|
524
|
+
name: Pharmacy
|
525
|
+
subcategories:
|
526
|
+
0X1:
|
527
|
+
name: Pharmacy
|
528
|
+
4R:
|
529
|
+
name: Diagnostic Molecular
|
530
|
+
subcategories:
|
531
|
+
0X1:
|
532
|
+
name: Diagnostic Molecular
|
533
|
+
4T:
|
534
|
+
name: Laboratory
|
535
|
+
subcategories:
|
536
|
+
0X1:
|
537
|
+
name: Laboratory
|
538
|
+
0X2:
|
539
|
+
name: Histopathology
|
540
|
+
4V:
|
541
|
+
name: Ophthalmic
|
542
|
+
subcategories:
|
543
|
+
0X1:
|
544
|
+
name: Ophthalmic
|
545
|
+
subcategories:
|
546
|
+
S:
|
547
|
+
name: Ophthalmic
|
548
|
+
4Y:
|
549
|
+
name: Dental Assistant
|
550
|
+
subcategories:
|
551
|
+
0X1:
|
552
|
+
name: Dental Assistant
|
553
|
+
5J:
|
554
|
+
name: Paralegal
|
555
|
+
subcategories:
|
556
|
+
0X1:
|
557
|
+
name: Paralegal
|
558
|
+
5R:
|
559
|
+
name: Religious Affairs
|
560
|
+
subcategories:
|
561
|
+
0X1:
|
562
|
+
name: Religious Affairs
|
563
|
+
6C:
|
564
|
+
name: Contracting
|
565
|
+
subcategories:
|
566
|
+
0X1:
|
567
|
+
name: Contracting
|
568
|
+
6F:
|
569
|
+
name: Financial Management and Comptroller
|
570
|
+
subcategories:
|
571
|
+
0X1:
|
572
|
+
name: Financial Management and Comptroller
|
573
|
+
7S:
|
574
|
+
name: Special Investigations
|
575
|
+
subcategories:
|
576
|
+
0X1:
|
577
|
+
name: Special Investigations
|
578
|
+
8A:
|
579
|
+
name: Career Development
|
580
|
+
subcategories:
|
581
|
+
100:
|
582
|
+
name: Career Assistance Advisor
|
583
|
+
8B:
|
584
|
+
name: Military Training
|
585
|
+
subcategories:
|
586
|
+
000:
|
587
|
+
name: Military Training Instructor
|
588
|
+
100:
|
589
|
+
name: Military Training Leader
|
590
|
+
200:
|
591
|
+
name: Academy Military Training NCO
|
592
|
+
8C:
|
593
|
+
name: Airman & Family Readiness
|
594
|
+
subcategories:
|
595
|
+
000:
|
596
|
+
name: Airman & Family Readiness Center NCO
|
597
|
+
8D:
|
598
|
+
name: Linguist
|
599
|
+
subcategories:
|
600
|
+
100:
|
601
|
+
name: Defense Attaché
|
602
|
+
8F:
|
603
|
+
name: First Sergeant
|
604
|
+
subcategories:
|
605
|
+
000:
|
606
|
+
name: First Sergeant
|
607
|
+
8G:
|
608
|
+
name: USAF Honor Guard
|
609
|
+
subcategories:
|
610
|
+
000:
|
611
|
+
name: USAF Honor Guard
|
612
|
+
8H:
|
613
|
+
name: Nuclear Weapons
|
614
|
+
subcategories:
|
615
|
+
000:
|
616
|
+
name: Nuclear Weapons Instructor
|
617
|
+
8K:
|
618
|
+
name: Enlisted Professional Military Education
|
619
|
+
subcategories:
|
620
|
+
000:
|
621
|
+
name: Enlisted Professional Military Education Instructor
|
622
|
+
8L:
|
623
|
+
name: Recruiting
|
624
|
+
subcategories:
|
625
|
+
000:
|
626
|
+
name: Recruiting Service
|
627
|
+
100:
|
628
|
+
name: Recruiting Liaison NCO
|
629
|
+
200:
|
630
|
+
name: In-Service Recruiter
|
631
|
+
8P:
|
632
|
+
name: Security Forces
|
633
|
+
subcategories:
|
634
|
+
100:
|
635
|
+
name: Military Working Dog Handler
|
636
|
+
8R:
|
637
|
+
name: Enlisted Accessions
|
638
|
+
subcategories:
|
639
|
+
000:
|
640
|
+
name: Enlisted Accessions Recruiter
|
641
|
+
300:
|
642
|
+
name: Flight chief
|
643
|
+
subcategories:
|
644
|
+
A:
|
645
|
+
name: Flight chief
|
646
|
+
8S:
|
647
|
+
name: Missile Facility
|
648
|
+
subcategories:
|
649
|
+
000:
|
650
|
+
name: Missile Facility Manager
|
651
|
+
8T:
|
652
|
+
name: Technical Training
|
653
|
+
subcategories:
|
654
|
+
000:
|
655
|
+
name: Technical Training Instructor
|
656
|
+
9A:
|
657
|
+
name: Awaiting Retraining-Reasons Beyond Control
|
658
|
+
subcategories:
|
659
|
+
000:
|
660
|
+
name: Awaiting Retraining-Reasons Beyond Control
|
661
|
+
9C:
|
662
|
+
name: CMSgt of the Air Force
|
663
|
+
subcategories:
|
664
|
+
000:
|
665
|
+
name: CMSgt of the Air Force
|
666
|
+
9D:
|
667
|
+
name: Dormitory Manager
|
668
|
+
subcategories:
|
669
|
+
000:
|
670
|
+
name: Dormitory Manager
|
671
|
+
9E:
|
672
|
+
name: Command Chief Master Sergeant
|
673
|
+
subcategories:
|
674
|
+
000:
|
675
|
+
name: Command Chief Master Sergeant
|
676
|
+
9F:
|
677
|
+
name: First Term Airmen
|
678
|
+
subcategories:
|
679
|
+
000:
|
680
|
+
name: First Term Airmen
|
681
|
+
9G:
|
682
|
+
name: Air Force Specialty Training
|
683
|
+
subcategories:
|
684
|
+
000:
|
685
|
+
name: Air Force Specialty Training
|
686
|
+
9J:
|
687
|
+
name: Prisoner
|
688
|
+
subcategories:
|
689
|
+
000:
|
690
|
+
name: Prisoner
|
691
|
+
9L:
|
692
|
+
name: Interpreter/Translator
|
693
|
+
subcategories:
|
694
|
+
000:
|
695
|
+
name: Interpreter/Translator
|
696
|
+
9P:
|
697
|
+
name: Patient
|
698
|
+
subcategories:
|
699
|
+
000:
|
700
|
+
name: Patient
|
701
|
+
9R:
|
702
|
+
name: Civil Air Patrol-USAF Reserve Assistance
|
703
|
+
subcategories:
|
704
|
+
000:
|
705
|
+
name: Civil Air Patrol-USAF Reserve Assistance
|
706
|
+
9S:
|
707
|
+
name: Technical Applications Specialist
|
708
|
+
subcategories:
|
709
|
+
100:
|
710
|
+
name: Technical Applications Specialist
|
711
|
+
9T:
|
712
|
+
name: Basic Enlisted Airman
|
713
|
+
subcategories:
|
714
|
+
000:
|
715
|
+
name: Basic Enlisted Airman
|
716
|
+
9U:
|
717
|
+
name: Basic Enlisted Helper
|
718
|
+
subcategories:
|
719
|
+
000:
|
720
|
+
name: Basic Enlisted Helper
|
721
|
+
9W:
|
722
|
+
name: Combat Wounded Warrior
|
723
|
+
subcategories:
|
724
|
+
000:
|
725
|
+
name: Combat Wounded Warrior
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require "strscan"
|
2
|
+
require "yaml"
|
3
|
+
require_relative "../data_loader"
|
4
|
+
|
5
|
+
module GovCodes
|
6
|
+
module AFSC
|
7
|
+
module Officer
|
8
|
+
class Parser
|
9
|
+
def initialize(code)
|
10
|
+
@code = code
|
11
|
+
end
|
12
|
+
|
13
|
+
def parse
|
14
|
+
scanner = StringScanner.new(@code.to_s)
|
15
|
+
result = {
|
16
|
+
prefix: nil,
|
17
|
+
career_group: nil,
|
18
|
+
functional_area: nil,
|
19
|
+
qualification_level: nil,
|
20
|
+
shredout: nil
|
21
|
+
}
|
22
|
+
|
23
|
+
# Scan for prefix (optional)
|
24
|
+
result[:prefix] = scanner.scan(/[A-Z]/)
|
25
|
+
|
26
|
+
# Scan for career group (two digits)
|
27
|
+
career_group = scanner.scan(/\d{2}/)
|
28
|
+
return result unless career_group
|
29
|
+
result[:career_group] = career_group
|
30
|
+
|
31
|
+
# Scan for functional area (uppercase letter)
|
32
|
+
functional_area = scanner.scan(/[A-Z]/)
|
33
|
+
return result unless functional_area
|
34
|
+
result[:functional_area] = functional_area
|
35
|
+
|
36
|
+
# Scan for qualification level (digit 0-4)
|
37
|
+
qualification_level = scanner.scan(/[0-4]/)
|
38
|
+
return result unless qualification_level
|
39
|
+
result[:qualification_level] = qualification_level
|
40
|
+
|
41
|
+
# Scan for shredout (optional)
|
42
|
+
result[:shredout] = scanner.scan(/[A-Z]/)
|
43
|
+
|
44
|
+
# Check if we've reached the end of the string
|
45
|
+
return result unless scanner.eos?
|
46
|
+
|
47
|
+
result
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
extend GovCodes::DataLoader
|
52
|
+
DATA = data
|
53
|
+
|
54
|
+
Code = Data.define(
|
55
|
+
:prefix,
|
56
|
+
:career_group,
|
57
|
+
:functional_area,
|
58
|
+
:qualification_level,
|
59
|
+
:shredout,
|
60
|
+
:name
|
61
|
+
)
|
62
|
+
|
63
|
+
def self.find(code)
|
64
|
+
code = code.to_s
|
65
|
+
parser = Parser.new(code)
|
66
|
+
result = parser.parse
|
67
|
+
|
68
|
+
return nil if result.reject { |_, v| v.nil? }.empty?
|
69
|
+
|
70
|
+
# Find the name from the codes data
|
71
|
+
career_group = result[:career_group]
|
72
|
+
functional_area = result[:functional_area]
|
73
|
+
|
74
|
+
# Look up the name in the codes hash
|
75
|
+
name = find_name(career_group, functional_area)
|
76
|
+
|
77
|
+
# Add the name to the result
|
78
|
+
result[:name] = name
|
79
|
+
|
80
|
+
Code.new(**result)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require_relative "afsc/enlisted"
|
2
|
+
require_relative "afsc/officer"
|
3
|
+
|
4
|
+
module GovCodes
|
5
|
+
module AFSC
|
6
|
+
def self.find(code)
|
7
|
+
AFSC::Enlisted.find(code) ||
|
8
|
+
AFSC::Officer.find(code)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.reset_data(lookup: $LOAD_PATH)
|
12
|
+
Enlisted.reset_data(lookup:)
|
13
|
+
Officer.reset_data(lookup:)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require "yaml"
|
2
|
+
|
3
|
+
module GovCodes
|
4
|
+
# DataLoader module provides shared functionality for loading YAML data
|
5
|
+
# from multiple paths in the lookup array
|
6
|
+
module DataLoader
|
7
|
+
def self.extended(base)
|
8
|
+
base.const_set(:CODES, {})
|
9
|
+
base.private_constant(:CODES)
|
10
|
+
end
|
11
|
+
|
12
|
+
def data(lookup: $LOAD_PATH)
|
13
|
+
data = {}
|
14
|
+
|
15
|
+
namespace_parts = name.split("::")
|
16
|
+
.map { |it| it.gsub(/([A-Z])([a-z])/, '_\1\2').downcase }
|
17
|
+
.map { |it| it.sub(/^_/, "") }
|
18
|
+
|
19
|
+
# Append .yml to the last item
|
20
|
+
namespace_parts[-1] = "#{namespace_parts[-1]}.yml"
|
21
|
+
|
22
|
+
# Iterate through each path in the lookup array
|
23
|
+
files = lookup.map do |dir|
|
24
|
+
yaml_path = File.join(dir, *namespace_parts)
|
25
|
+
yaml_path if File.exist?(yaml_path)
|
26
|
+
end
|
27
|
+
.compact
|
28
|
+
.uniq
|
29
|
+
files.each do |path|
|
30
|
+
data.merge!(YAML.load_file(path, symbolize_names: true))
|
31
|
+
end
|
32
|
+
|
33
|
+
data
|
34
|
+
end
|
35
|
+
|
36
|
+
def reset_data(lookup: $LOAD_PATH)
|
37
|
+
remove_const(:DATA)
|
38
|
+
const_set(:DATA, data(lookup:).freeze)
|
39
|
+
remove_const(:CODES)
|
40
|
+
const_set(:CODES, {})
|
41
|
+
end
|
42
|
+
|
43
|
+
def find_name_recursive(result)
|
44
|
+
# Start with the career field (e.g., "1N")
|
45
|
+
base_code = result[:career_field].to_sym
|
46
|
+
|
47
|
+
loaded_data = self::DATA
|
48
|
+
# Look up in the codes hash
|
49
|
+
if loaded_data[base_code]
|
50
|
+
# If we have a subcategory, try to find a more specific name
|
51
|
+
if result[:subcategory] &&
|
52
|
+
loaded_data.dig(base_code, :subcategories) &&
|
53
|
+
loaded_data.dig(base_code, :subcategories, result[:subcategory])
|
54
|
+
|
55
|
+
subdivision = loaded_data.dig(base_code, :subcategories, result[:subcategory])
|
56
|
+
# If we have a shredout, try to find an even more specific name
|
57
|
+
if result[:shredout] &&
|
58
|
+
subdivision.dig(:subcategories) &&
|
59
|
+
subdivision.dig(:subcategories, result[:shredout])
|
60
|
+
return subdivision.dig(:subcategories, result[:shredout], :name)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Return the subdivision name if no shredout match
|
64
|
+
return subdivision.dig(:name)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Return the base name if no subdivision match
|
68
|
+
return loaded_data.dig(base_code, :name)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Return a default if no match found
|
72
|
+
"Unknown"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/lib/gov_codes.rb
ADDED
data/sig/gov_codes.rbs
ADDED
metadata
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gov_codes
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jim Gay
|
8
|
+
bindir: exe
|
9
|
+
cert_chain: []
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
|
+
dependencies: []
|
12
|
+
description: Process and understand codes used by the US government.
|
13
|
+
email:
|
14
|
+
- jim@saturnflyer.com
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- ".simplecov"
|
20
|
+
- ".standard.yml"
|
21
|
+
- ".tool-versions"
|
22
|
+
- CHANGELOG.md
|
23
|
+
- README.md
|
24
|
+
- Rakefile
|
25
|
+
- lib/gov_codes.rb
|
26
|
+
- lib/gov_codes/afsc.rb
|
27
|
+
- lib/gov_codes/afsc/enlisted.rb
|
28
|
+
- lib/gov_codes/afsc/enlisted.yml
|
29
|
+
- lib/gov_codes/afsc/officer.rb
|
30
|
+
- lib/gov_codes/data_loader.rb
|
31
|
+
- lib/gov_codes/version.rb
|
32
|
+
- sig/gov_codes.rbs
|
33
|
+
homepage: https://github.com/SOFware/gov_codes
|
34
|
+
licenses: []
|
35
|
+
metadata:
|
36
|
+
homepage_uri: https://github.com/SOFware/gov_codes
|
37
|
+
source_code_uri: https://github.com/SOFware/gov_codes
|
38
|
+
changelog_uri: https://github.com/SOFware/gov_codes/blob/main/CHANGELOG.md
|
39
|
+
rdoc_options: []
|
40
|
+
require_paths:
|
41
|
+
- lib
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 3.1.0
|
47
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
requirements: []
|
53
|
+
rubygems_version: 3.6.7
|
54
|
+
specification_version: 4
|
55
|
+
summary: Handle codes used by the US government.
|
56
|
+
test_files: []
|