Class: Pubid::Itu::Builder
- Inherits:
-
Object
- Object
- Pubid::Itu::Builder
- Defined in:
- lib/pubid/itu/builder.rb
Class Method Summary collapse
Instance Method Summary collapse
- #build(data) ⇒ Object
-
#build_annex(inner_data) ⇒ Object
Build "Annex to ..." identifier.
-
#build_annex_of_recommendation(data) ⇒ Object
Build a labelled annex of a Recommendation.
-
#build_appendix_of_recommendation(data) ⇒ Object
Build a labelled appendix of a Recommendation.
-
#build_handbook(data) ⇒ Object
Build Handbook ("ITU-R 42.HDB").
-
#build_question(data) ⇒ Object
Build Question (numeric or letter-series).
-
#build_special_publication(data) ⇒ Object
Build Special Publication (OB).
- #build_supplement(data) ⇒ Object
-
#parse_common_text_twin(twin_raw) ⇒ Object
Parse the common-text twin string ("| ISO/IEC 13818-1:2022") into the appropriate flavor's identifier object.
Class Method Details
.build(parsed_data) ⇒ Object
6 7 8 |
# File 'lib/pubid/itu/builder.rb', line 6 def self.build(parsed_data) new.build(parsed_data) end |
Instance Method Details
#build(data) ⇒ Object
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 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 |
# File 'lib/pubid/itu/builder.rb', line 10 def build(data) # "Annex to ..." identifier — wraps a Special Publication base if data[:annex_to] return build_annex(data[:annex_to]) end # Common-text twin ("| ISO/IEC ..." suffix) — extract before building # the base ITU identifier, then attach to whichever class the base # resolves to. twin = parse_common_text_twin(data.delete(:common_text_twin)) # Labelled annex of a Recommendation — "ITU-T A.23 Annex A (06/2014)". # The annexed document is the :base subtree; :year/:month at this level # are the annex's own date, not the base's. if data[:annex_number] annex = build_annex_of_recommendation(data) annex.common_text_twin = twin if twin return annex end # Labelled appendix of a Recommendation — "ITU-T G.101 App. I # (05/2000)". Same shape as the annex above. if data[:appendix_number] appendix = build_appendix_of_recommendation(data) appendix.common_text_twin = twin if twin return appendix end # Operational Bulletin (Special Publication) — series == "OB" or # legacy long form ("Operational Bulletin No. ..."). # The series_dash guard keeps a series-code "ITU-T OB-1" out of this # branch, which would drop the dash and render it "ITU OB No. 1". # Belt-and-braces: `series_code_body` already refuses "OB" + dash, so # nothing reaches here with both set — the guard documents that coupling # so removing the parser's `absent?` fails loudly rather than silently # rerouting. if (data[:series].to_s == "OB" && data[:series_dash].nil?) || data[:_op_bull] sp = build_special_publication(data) sp.common_text_twin = twin if twin && sp return sp end # Check if this is a supplement identifier if data[:supplement_type] supp = build_supplement(data) supp.common_text_twin = twin if twin && supp return supp end # Handbook — "ITU-R 42.HDB" if data[:handbook_marker] return build_handbook(data) end # Question — numeric ("ITU-R 234-1/7:") or letter-series # ("ITU-R P.3/BL/7"). Both carry a :study_group marker. if data[:study_group] return build_question(data) end # Build basic recommendation or combined identifier sector = Components::Sector.new(sector: data[:sector].to_s) series = Components::Series.new(series: data[:series].to_s) if data[:series] code = build_code(data) if data[:number] date = build_date(data) if data[:year] # Combined (joint) recommendation — one or more additional # "/SERIES.CODE" designations after the primary (e.g. "G.780/Y.1351", # "G.780/Y.1351/Z.1362"). The primary stays on the base series/code. if data[:combined] return Identifiers::CombinedIdentifier.new( sector: sector, series: series, code: code, combined: build_designations(data[:combined]), date: date, version: data[:version]&.to_s, # with_series offers these slots on either side of # combined_suffixes, so a joint id can carry them too; not # forwarding them let "ITU-T H.350/X.1 attachment" compare equal to # the un-attached document. series_word: !data[:series_word].nil?, series_dash: !data[:series_dash].nil?, attachment: !data[:attachment].nil?, range_end: data[:range_end]&.to_s, language: data[:language]&.to_s, common_text_twin: twin, ) end # A Report ("Report ITU-R BT.2020-1") is shaped exactly like a # Recommendation — only the leading word, captured as :report_marker, # tells the two apart, and it has to, because the two series number # independently and the same series/number is two real documents. klass = data[:report_marker] ? Identifiers::Report : Identifiers::Recommendation klass.new( sector: sector, series: series, code: code, date: date, version: data[:version]&.to_s, series_word: !data[:series_word].nil?, series_dash: !data[:series_dash].nil?, attachment: !data[:attachment].nil?, range_end: data[:range_end]&.to_s, language: data[:language]&.to_s, common_text_twin: twin, ) end |
#build_annex(inner_data) ⇒ Object
Build "Annex to ..." identifier. The inner data is the Special Publication; the annex inherits its language.
160 161 162 163 164 165 166 |
# File 'lib/pubid/itu/builder.rb', line 160 def build_annex(inner_data) base = build_special_publication(inner_data) Identifiers::Annex.new( base: base, language: inner_data[:language]&.to_s, ) end |
#build_annex_of_recommendation(data) ⇒ Object
Build a labelled annex of a Recommendation. sector/series/code are deliberately NOT copied from the base: they are not serialized on the wrapper, so copies would make a from_hash-rebuilt annex differ from the parsed one.
172 173 174 175 176 177 178 179 |
# File 'lib/pubid/itu/builder.rb', line 172 def build_annex_of_recommendation(data) Identifiers::AnnexOfRecommendation.new( base: build(data[:base]), number: data[:annex_number].to_s, date: data[:year] ? build_date(data) : nil, language: data[:language]&.to_s, ) end |
#build_appendix_of_recommendation(data) ⇒ Object
Build a labelled appendix of a Recommendation. Like the annex above, sector/series/code are deliberately NOT copied from the base — they are not serialized on the wrapper, so copies would make a from_hash-rebuilt appendix differ from the parsed one.
185 186 187 188 189 190 191 192 193 |
# File 'lib/pubid/itu/builder.rb', line 185 def build_appendix_of_recommendation(data) Identifiers::AppendixOfRecommendation.new( base: build(data[:base]), number: data[:appendix_number].to_s, material: data[:appendix_material]&.to_s, date: data[:year] ? build_date(data) : nil, language: data[:language]&.to_s, ) end |
#build_handbook(data) ⇒ Object
Build Handbook ("ITU-R 42.HDB").
196 197 198 199 200 201 202 |
# File 'lib/pubid/itu/builder.rb', line 196 def build_handbook(data) Identifiers::Handbook.new( sector: Components::Sector.new(sector: data[:sector].to_s), code: build_code(data), date: data[:year] ? build_date(data) : nil, ) end |
#build_question(data) ⇒ Object
Build Question (numeric or letter-series).
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 |
# File 'lib/pubid/itu/builder.rb', line 205 def build_question(data) series = if data[:series] Components::Series.new(series: data[:series].to_s) end Identifiers::Question.new( sector: Components::Sector.new(sector: data[:sector].to_s), series: series, code: build_code(data), study_group: data[:study_group].to_s, has_bl: !data[:has_bl].nil?, bracketed: !data[:bracketed].nil?, has_colon: !data[:question_colon].nil?, ) end |
#build_special_publication(data) ⇒ Object
Build Special Publication (OB). Sector is silently dropped — OB is a
cross-bureau publication and Identifier rejects sector+OB
in its constructor.
149 150 151 152 153 154 155 156 |
# File 'lib/pubid/itu/builder.rb', line 149 def build_special_publication(data) Identifiers::SpecialPublication.new( series: Components::Series.new(series: "OB"), code: data[:number] ? build_code(data) : nil, date: data[:year] ? build_date(data) : nil, language: data[:language]&.to_s, ) end |
#build_supplement(data) ⇒ Object
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 |
# File 'lib/pubid/itu/builder.rb', line 221 def build_supplement(data) # Build the base identifier first base = build(data[:base]) if data[:base] # Determine supplement type supplement_type = data[:supplement_type].to_s.gsub(".", "") klass = case supplement_type when "Amd" Identifiers::Amendment when "Add" Identifiers::Addendum when "Cor" Identifiers::Corrigendum when "Err" Identifiers::Errata when "Suppl" Identifiers::Supplement else # Previously fell through to a nil class and died with # "undefined method 'new' for nil" one frame later. Any token # added to Parser#supplement_type without a class here should # say so. raise ArgumentError, "Unknown ITU supplement type: " \ "#{data[:supplement_type].inspect}" end # Build supplement date (separate from base date) supplement_date = if data[:supplement_year] Pubid::Components::Date.new( year: data[:supplement_year].to_s, month: data[:supplement_month]&.to_s, ) end # Sector/series are copied from the base when it has them; the fallback # to this level's own tokens is guarded because an annex base keeps # sector/series/code on *its* base, leaving all three nil here while the # supplement level carries no sector token of its own. # An annex base keeps sector/series/code on *its* base, so reach through # it via #root; otherwise a supplement of an annex would carry none of # them and its MR string would collapse to a bare "itu.<date>". base_identity = base&.sector ? base : base&.root sector = base_identity&.sector || (Components::Sector.new(sector: data[:sector].to_s) if data[:sector]) series = base_identity&.series || (Components::Series.new(series: data[:series].to_s) if data[:series]) attrs = { sector: sector, series: series, code: base_identity&.code, base: base, number: data[:supplement_number].to_s, # The marker is the captured space, so its ABSENCE is the flag. number_glued: data[:supplement_space].nil?, slash_joined: !data[:supplement_slash].nil?, date: supplement_date, language: data[:language]&.to_s, } # The "series" word rides on the supplement for the base-less, # series-only form; with a base it is the base's, and renders from # there. It is copied up anyway (like sector/series/code above) so the # supplement's own MR slug stays distinct — `render_supplement` and the # base-nil-guarded key_value/== only consult it when base is nil, so # the copy is render- and serialization-neutral. attrs[:series_word] = if base !!base_identity&.series_word else !data[:series_word].nil? end attrs[:technical] = true if data[:technical] && klass == Identifiers::Corrigendum klass.new(**attrs) end |
#parse_common_text_twin(twin_raw) ⇒ Object
Parse the common-text twin string ("| ISO/IEC 13818-1:2022") into the appropriate flavor's identifier object. Returns nil if the twin is empty or unparseable.
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/pubid/itu/builder.rb', line 125 def parse_common_text_twin(twin_raw) return nil if twin_raw.nil? twin_str = twin_raw.to_s.strip # Strip a leading pipe if the parser captured it twin_str = twin_str.sub(/\A\s*\|\s*/, "") return nil if twin_str.empty? case twin_str when /\AISO\/IEC\b/, /\AISO\b/ Pubid::Iso.parse(twin_str) when /\AIEC\b/ Pubid::Iec.parse(twin_str) end rescue StandardError # If the twin doesn't parse as a known flavor, leave it as nil # rather than blowing up the whole parse — the ITU half is still # usable on its own. nil end |