Module: GovCodes::AFSC::Officer
- Defined in:
- lib/gov_codes/afsc/officer.rb
Overview
Officer AFSCs resolved against the versioned DAFOCD release index. Mirrors
Enlisted: find(code, as_of:) resolves the X-form specialty (e.g. :11BX)
or a literal bare code (e.g. :10C0) in the release in effect on as_of,
defaulting to today.
Defined Under Namespace
Class Method Summary collapse
-
.find(code, as_of: nil) ⇒ Object
Resolve an officer AFSC against the DAFOCD release in effect on
as_of(default: today). -
.find_by_acronym(acronym, as_of: nil) ⇒ Object
Resolve the officer specialty/shredout whose acronym matches
acronym(case-insensitive) in the release in effect onas_of. -
.reset_data(lookup: $LOAD_PATH) ⇒ Object
Clears the memoized lookups and resets the versioned release loader.
-
.search(prefix, as_of: nil) ⇒ Object
Walk the resolved index emitting each specialty code and its specialty+shredout combinations, returning the Codes matching
prefix.
Class Method Details
.find(code, as_of: nil) ⇒ Object
Resolve an officer AFSC against the DAFOCD release in effect on as_of
(default: today). Returns nil when the code does not parse, when neither
the X-form specialty nor a literal bare code resolves in the release, or
when as_of precedes the earliest shipped release (or no release has
taken effect yet).
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/gov_codes/afsc/officer.rb', line 84 def self.find(code, as_of: nil) code = code.to_s # Key the memo on the RESOLVED release date so equivalent as_of values # (nil, the Date, its string form, any date in the same release window) # share one slot instead of growing unbounded for time-series callers. effective_date = Releases.effective_date_for(as_of: as_of, publication: Releases::OFFICER_PUBLICATION) CODES[[code, effective_date]] ||= begin result = Parser.new(code).parse # Return nil if parsing failed or required fields are missing return nil if result.reject { |_, v| v.nil? }.empty? || result[:career_group].nil? || result[:functional_area].nil? || result[:qualification_level].nil? || result[:specific_afsc].nil? index = Releases.officer_index(as_of: as_of) # Prefer the X-form specialty key (11B3 -> :11BX). Fall back to the # literal 4-char code for bare specialties keyed that way (:10C0, :62S0, # :63G0, :63S0). By design these bare-code specialties resolve ONLY via # their literal code -- there is no X-form ladder for them, so # find("10CX")/find("62SX")/find("63GX")/find("63SX") return nil while # find("10C0")/find("62S0")/find("63G0")/find("63S0") resolve. specialty = :"#{result[:career_group]}#{result[:functional_area]}X" if !index.key?(specialty) && index.key?(result[:specific_afsc]) specialty = result[:specific_afsc] end entry = index[specialty] return nil unless entry result[:specialty] = specialty result[:specialty_name] = entry[:name] # Qualification-level title: numeric level (1-4) maps to the directory's # per-specialty title; a letter (X/Y/Z) or an absent level leaves it nil. result[:qualification_level_number] = nil result[:qualification_level_name] = nil qual = result[:qualification_level].to_s if qual.match?(/\d/) number = Integer(qual) result[:qualification_level_number] = number result[:qualification_level_name] = entry.dig(:qual_levels, number, :title) end # Shredout meaning, only when the directory documents this shredout. shredout = result[:shredout] shredout_name = shredout && entry.dig(:shredouts, shredout) result[:shredout_name] = shredout_name name = shredout_name || entry[:name] return nil if name.nil? result[:name] = name # Acronym: a documented shredout acronym wins for a shredded code; # otherwise the specialty acronym (which a consumer overlay may set). shredout_acronym = shredout && entry.dig(:shredout_acronyms, shredout) result[:acronym] = shredout_acronym || entry[:acronym] result[:effective_date] = effective_date Code.new(**result) end end |
.find_by_acronym(acronym, as_of: nil) ⇒ Object
Resolve the officer specialty/shredout whose acronym matches acronym
(case-insensitive) in the release in effect on as_of. Searches both
specialty acronyms and shredout acronyms; a shredout match returns the
concrete shredded code (so "TACPO" resolves find("19ZXB")). First match
wins: entries are scanned in index order and, within an entry, the
specialty acronym is tried before its shredout acronyms. Returns nil when
no acronym in the resolved release matches (no leakage across dates).
156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/gov_codes/afsc/officer.rb', line 156 def self.find_by_acronym(acronym, as_of: nil) acronym = acronym.to_s.upcase return nil if acronym.empty? index = Releases.officer_index(as_of: as_of) index.each do |specialty, entry| return find(specialty.to_s, as_of: as_of) if entry[:acronym].to_s.upcase == acronym (entry[:shredout_acronyms] || {}).each do |shredout, acr| return find("#{specialty}#{shredout}", as_of: as_of) if acr.to_s.upcase == acronym end end nil end |
.reset_data(lookup: $LOAD_PATH) ⇒ Object
Clears the memoized lookups and resets the versioned release loader. The
lookup keyword is accepted for interface parity with Enlisted/RI; the
versioned index is resolved from the load path at lookup time.
194 195 196 197 |
# File 'lib/gov_codes/afsc/officer.rb', line 194 def self.reset_data(lookup: $LOAD_PATH) Releases.reset! CODES.clear end |
.search(prefix, as_of: nil) ⇒ Object
Walk the resolved index emitting each specialty code and its
specialty+shredout combinations, returning the Codes matching prefix.
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/gov_codes/afsc/officer.rb', line 173 def self.search(prefix, as_of: nil) prefix = prefix.to_s.upcase index = Releases.officer_index(as_of: as_of) codes = [] index.each do |specialty, entry| specialty_code = specialty.to_s codes << specialty_code (entry[:shredouts] || {}).each_key do |shredout| codes << "#{specialty_code}#{shredout}" end end codes.select { |code| code.start_with?(prefix) } .map { |code| find(code, as_of: as_of) } .compact end |