pubid 2.0.0.pre.alpha.8 → 2.0.0.pre.alpha.11

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.
Files changed (672) hide show
  1. checksums.yaml +4 -4
  2. data/README.adoc +33 -11
  3. data/conformance/pending.yaml +4 -0
  4. data/data/bipm/update_codes.yaml +9 -0
  5. data/data/iec/update_codes.yaml +3 -0
  6. data/data/ieee/update_codes.yaml +107 -1
  7. data/{archived-gems/pubid-nist → data/nist}/series.yaml +34 -0
  8. data/data/nist/update_codes.yaml +8 -0
  9. data/lib/pubid/adobe/builder.rb +52 -0
  10. data/lib/pubid/adobe/identifier.rb +47 -0
  11. data/lib/pubid/adobe/identifiers/publication.rb +31 -0
  12. data/lib/pubid/adobe/identifiers/tech_note.rb +32 -0
  13. data/lib/pubid/adobe/identifiers.rb +10 -0
  14. data/lib/pubid/adobe/parser.rb +136 -0
  15. data/lib/pubid/adobe/renderer.rb +39 -0
  16. data/lib/pubid/adobe/urn_generator.rb +42 -0
  17. data/lib/pubid/adobe/urn_parser.rb +69 -0
  18. data/lib/pubid/adobe.rb +61 -0
  19. data/lib/pubid/amca/builder.rb +4 -4
  20. data/lib/pubid/amca/identifier.rb +1 -3
  21. data/lib/pubid/amca/identifiers/base.rb +50 -33
  22. data/lib/pubid/amca/identifiers/interpretation.rb +18 -10
  23. data/lib/pubid/amca/identifiers/publication.rb +14 -10
  24. data/lib/pubid/amca/identifiers/standard.rb +3 -1
  25. data/lib/pubid/amca/identifiers.rb +0 -1
  26. data/lib/pubid/amca/parser.rb +1 -1
  27. data/lib/pubid/amca/renderer.rb +4 -4
  28. data/lib/pubid/amca/single_identifier.rb +1 -1
  29. data/lib/pubid/amca/urn_generator.rb +2 -2
  30. data/lib/pubid/amca.rb +5 -4
  31. data/lib/pubid/ansi/builder.rb +2 -2
  32. data/lib/pubid/ansi/identifier.rb +18 -0
  33. data/lib/pubid/ansi/parser.rb +1 -1
  34. data/lib/pubid/ansi/renderer.rb +2 -2
  35. data/lib/pubid/ansi.rb +5 -5
  36. data/lib/pubid/api/builder.rb +16 -2
  37. data/lib/pubid/api/identifier.rb +25 -4
  38. data/lib/pubid/api/identifiers/mpms.rb +10 -8
  39. data/lib/pubid/api/parser.rb +1 -1
  40. data/lib/pubid/api/renderer.rb +16 -7
  41. data/lib/pubid/api/single_identifier.rb +5 -12
  42. data/lib/pubid/api.rb +1 -0
  43. data/lib/pubid/ashrae/builder.rb +53 -32
  44. data/lib/pubid/ashrae/identifier.rb +3 -4
  45. data/lib/pubid/ashrae/identifiers/addenda_package.rb +7 -1
  46. data/lib/pubid/ashrae/identifiers/addendum.rb +9 -1
  47. data/lib/pubid/ashrae/identifiers/base.rb +94 -12
  48. data/lib/pubid/ashrae/identifiers/combined_addenda.rb +8 -1
  49. data/lib/pubid/ashrae/identifiers/errata.rb +9 -0
  50. data/lib/pubid/ashrae/identifiers/guideline.rb +3 -0
  51. data/lib/pubid/ashrae/identifiers/interpretation.rb +16 -0
  52. data/lib/pubid/ashrae/identifiers/standard.rb +3 -0
  53. data/lib/pubid/ashrae/identifiers.rb +0 -1
  54. data/lib/pubid/ashrae/parser.rb +37 -17
  55. data/lib/pubid/ashrae/renderer.rb +20 -20
  56. data/lib/pubid/ashrae/single_identifier.rb +1 -1
  57. data/lib/pubid/ashrae/supplement_identifier.rb +21 -6
  58. data/lib/pubid/ashrae/urn_generator.rb +8 -2
  59. data/lib/pubid/ashrae.rb +10 -4
  60. data/lib/pubid/asme/builder.rb +11 -2
  61. data/lib/pubid/asme/identifier.rb +9 -0
  62. data/lib/pubid/asme/identifiers/standard.rb +86 -2
  63. data/lib/pubid/asme/parser.rb +1 -1
  64. data/lib/pubid/asme/renderer.rb +3 -3
  65. data/lib/pubid/asme/single_identifier.rb +8 -1
  66. data/lib/pubid/asme/urn_generator.rb +2 -2
  67. data/lib/pubid/asme.rb +1 -0
  68. data/lib/pubid/astm/builder.rb +10 -3
  69. data/lib/pubid/astm/identifier.rb +9 -0
  70. data/lib/pubid/astm/identifiers/adjunct.rb +16 -1
  71. data/lib/pubid/astm/identifiers/code_number.rb +81 -0
  72. data/lib/pubid/astm/identifiers/data_series.rb +2 -0
  73. data/lib/pubid/astm/identifiers/iso_dual_published.rb +16 -0
  74. data/lib/pubid/astm/identifiers/manual.rb +10 -0
  75. data/lib/pubid/astm/identifiers/monograph.rb +2 -0
  76. data/lib/pubid/astm/identifiers/research_report.rb +10 -0
  77. data/lib/pubid/astm/identifiers/standard.rb +10 -0
  78. data/lib/pubid/astm/identifiers/technical_report.rb +2 -0
  79. data/lib/pubid/astm/identifiers/work_in_progress.rb +2 -0
  80. data/lib/pubid/astm/identifiers.rb +1 -0
  81. data/lib/pubid/astm/parser.rb +1 -1
  82. data/lib/pubid/astm/renderer.rb +1 -1
  83. data/lib/pubid/astm/single_identifier.rb +31 -1
  84. data/lib/pubid/astm/urn_generator.rb +7 -5
  85. data/lib/pubid/astm.rb +1 -0
  86. data/lib/pubid/bipm/builder.rb +163 -0
  87. data/lib/pubid/bipm/identifier.rb +285 -0
  88. data/lib/pubid/bipm/identifiers/committee_document.rb +36 -0
  89. data/lib/pubid/bipm/identifiers/guide.rb +35 -0
  90. data/lib/pubid/bipm/identifiers/meeting.rb +53 -0
  91. data/lib/pubid/bipm/identifiers/mep.rb +33 -0
  92. data/lib/pubid/bipm/identifiers/metrologia_article.rb +48 -0
  93. data/lib/pubid/bipm/identifiers/si_brochure.rb +33 -0
  94. data/lib/pubid/bipm/identifiers.rb +15 -0
  95. data/lib/pubid/bipm/parser.rb +237 -0
  96. data/lib/pubid/bipm/renderer.rb +118 -0
  97. data/lib/pubid/bipm/urn_generator.rb +58 -0
  98. data/lib/pubid/bipm/urn_parser.rb +60 -0
  99. data/lib/pubid/bipm.rb +92 -0
  100. data/lib/pubid/bsi/builder.rb +62 -52
  101. data/lib/pubid/bsi/identifier.rb +0 -1
  102. data/lib/pubid/bsi/identifiers/addendum_document.rb +2 -2
  103. data/lib/pubid/bsi/identifiers/adopted_european_norm.rb +36 -7
  104. data/lib/pubid/bsi/identifiers/adopted_international_standard.rb +7 -6
  105. data/lib/pubid/bsi/identifiers/amendment.rb +4 -4
  106. data/lib/pubid/bsi/identifiers/british_industrial_practice.rb +0 -1
  107. data/lib/pubid/bsi/identifiers/bundled_identifier.rb +10 -0
  108. data/lib/pubid/bsi/identifiers/committee_document.rb +10 -1
  109. data/lib/pubid/bsi/identifiers/consolidated_identifier.rb +6 -0
  110. data/lib/pubid/bsi/identifiers/corrigendum.rb +4 -4
  111. data/lib/pubid/bsi/identifiers/expert_commentary.rb +5 -5
  112. data/lib/pubid/bsi/identifiers/handbook.rb +0 -2
  113. data/lib/pubid/bsi/identifiers/practice_guide.rb +0 -1
  114. data/lib/pubid/bsi/identifiers/set.rb +11 -0
  115. data/lib/pubid/bsi/identifiers/standalone_amendment.rb +10 -1
  116. data/lib/pubid/bsi/identifiers/supplement_document.rb +2 -2
  117. data/lib/pubid/bsi/identifiers/value_added_publication.rb +6 -6
  118. data/lib/pubid/bsi/parser.rb +1 -1
  119. data/lib/pubid/bsi/renderer.rb +71 -59
  120. data/lib/pubid/bsi/single_identifier.rb +64 -7
  121. data/lib/pubid/bsi/urn_generator.rb +11 -2
  122. data/lib/pubid/bsi.rb +7 -1
  123. data/lib/pubid/builder/base.rb +23 -11
  124. data/lib/pubid/bundled_identifier.rb +19 -9
  125. data/lib/pubid/calconnect/builder.rb +38 -0
  126. data/lib/pubid/calconnect/identifier.rb +121 -0
  127. data/lib/pubid/calconnect/identifiers/standard.rb +29 -0
  128. data/lib/pubid/calconnect/identifiers.rb +9 -0
  129. data/lib/pubid/calconnect/parser.rb +54 -0
  130. data/lib/pubid/calconnect/renderer.rb +36 -0
  131. data/lib/pubid/calconnect/urn_generator.rb +33 -0
  132. data/lib/pubid/calconnect/urn_parser.rb +34 -0
  133. data/lib/pubid/calconnect.rb +83 -0
  134. data/lib/pubid/ccsds/builder.rb +1 -1
  135. data/lib/pubid/ccsds/identifier.rb +10 -3
  136. data/lib/pubid/ccsds/identifiers/base.rb +4 -2
  137. data/lib/pubid/ccsds/identifiers/corrigendum.rb +4 -4
  138. data/lib/pubid/ccsds/parser.rb +1 -1
  139. data/lib/pubid/ccsds/single_identifier.rb +2 -2
  140. data/lib/pubid/ccsds/supplement_identifier.rb +6 -6
  141. data/lib/pubid/ccsds/urn_generator.rb +3 -3
  142. data/lib/pubid/ccsds.rb +1 -0
  143. data/lib/pubid/cen_cenelec/builder.rb +131 -72
  144. data/lib/pubid/cen_cenelec/identifier.rb +115 -3
  145. data/lib/pubid/cen_cenelec/identifiers/adopted_european_norm.rb +27 -16
  146. data/lib/pubid/cen_cenelec/identifiers/amendment.rb +46 -6
  147. data/lib/pubid/cen_cenelec/identifiers/base.rb +8 -9
  148. data/lib/pubid/cen_cenelec/identifiers/cen_report.rb +1 -1
  149. data/lib/pubid/cen_cenelec/identifiers/cen_workshop_agreement.rb +1 -1
  150. data/lib/pubid/cen_cenelec/identifiers/cenelec_harmonization_document.rb +1 -1
  151. data/lib/pubid/cen_cenelec/identifiers/consolidated_identifier.rb +29 -14
  152. data/lib/pubid/cen_cenelec/identifiers/corrigendum.rb +47 -8
  153. data/lib/pubid/cen_cenelec/identifiers/european_norm.rb +60 -3
  154. data/lib/pubid/cen_cenelec/identifiers/european_prestandard.rb +27 -2
  155. data/lib/pubid/cen_cenelec/identifiers/european_specification.rb +1 -1
  156. data/lib/pubid/cen_cenelec/identifiers/fragment.rb +15 -5
  157. data/lib/pubid/cen_cenelec/identifiers/guide.rb +1 -1
  158. data/lib/pubid/cen_cenelec/identifiers/harmonization_document.rb +1 -1
  159. data/lib/pubid/cen_cenelec/identifiers/technical_report.rb +1 -1
  160. data/lib/pubid/cen_cenelec/identifiers/technical_specification.rb +1 -1
  161. data/lib/pubid/cen_cenelec/parser.rb +1 -1
  162. data/lib/pubid/cen_cenelec/renderer.rb +55 -60
  163. data/lib/pubid/cen_cenelec/single_identifier.rb +20 -2
  164. data/lib/pubid/cen_cenelec/urn_generator.rb +66 -44
  165. data/lib/pubid/cen_cenelec.rb +104 -82
  166. data/lib/pubid/cie/builder.rb +149 -24
  167. data/lib/pubid/cie/identifier.rb +13 -4
  168. data/lib/pubid/cie/identifiers/bundle.rb +35 -5
  169. data/lib/pubid/cie/identifiers/code_attributes.rb +52 -0
  170. data/lib/pubid/cie/identifiers/conference.rb +11 -6
  171. data/lib/pubid/cie/identifiers/corrigendum.rb +17 -23
  172. data/lib/pubid/cie/identifiers/dual_published.rb +6 -6
  173. data/lib/pubid/cie/identifiers/identical.rb +9 -9
  174. data/lib/pubid/cie/identifiers/joint_published.rb +10 -10
  175. data/lib/pubid/cie/identifiers/proceedings.rb +41 -0
  176. data/lib/pubid/cie/identifiers/standard.rb +43 -8
  177. data/lib/pubid/cie/identifiers/supplement.rb +27 -25
  178. data/lib/pubid/cie/identifiers/tutorial_bundle.rb +7 -3
  179. data/lib/pubid/cie/identifiers.rb +2 -0
  180. data/lib/pubid/cie/parser.rb +55 -6
  181. data/lib/pubid/cie/single_identifier.rb +40 -1
  182. data/lib/pubid/cie/supplement_identifier.rb +29 -16
  183. data/lib/pubid/cie/urn_generator.rb +11 -8
  184. data/lib/pubid/cie.rb +7 -2
  185. data/lib/pubid/components/date.rb +21 -5
  186. data/lib/pubid/conformance/checks.rb +72 -0
  187. data/lib/pubid/conformance/corpus/case.rb +55 -0
  188. data/lib/pubid/conformance/corpus.rb +36 -0
  189. data/lib/pubid/conformance/generator.rb +212 -0
  190. data/lib/pubid/conformance/pending.rb +63 -0
  191. data/lib/pubid/conformance/runner.rb +153 -0
  192. data/lib/pubid/conformance.rb +55 -0
  193. data/lib/pubid/core.rb +2 -0
  194. data/lib/pubid/csa/builder.rb +48 -48
  195. data/lib/pubid/csa/composite_identifier.rb +18 -7
  196. data/lib/pubid/csa/identifier.rb +187 -80
  197. data/lib/pubid/csa/identifiers/bundled.rb +25 -9
  198. data/lib/pubid/csa/identifiers/canadian_adopted.rb +13 -13
  199. data/lib/pubid/csa/identifiers/cec.rb +44 -4
  200. data/lib/pubid/csa/identifiers/combined.rb +97 -69
  201. data/lib/pubid/csa/identifiers/csa_adopted.rb +4 -4
  202. data/lib/pubid/csa/identifiers/package.rb +4 -4
  203. data/lib/pubid/csa/parser.rb +12 -8
  204. data/lib/pubid/csa/renderer.rb +13 -5
  205. data/lib/pubid/csa/single_identifier.rb +93 -4
  206. data/lib/pubid/csa/urn_generator.rb +9 -2
  207. data/lib/pubid/csa/wrapper_identifier.rb +43 -10
  208. data/lib/pubid/csa.rb +7 -1
  209. data/lib/pubid/doi/builder.rb +18 -0
  210. data/lib/pubid/doi/identifier.rb +49 -0
  211. data/lib/pubid/doi/identifiers/resource.rb +30 -0
  212. data/lib/pubid/doi/identifiers.rb +9 -0
  213. data/lib/pubid/doi/parser.rb +52 -0
  214. data/lib/pubid/doi/renderer.rb +13 -0
  215. data/lib/pubid/doi.rb +56 -0
  216. data/lib/pubid/easc/builder.rb +55 -0
  217. data/lib/pubid/easc/identifier.rb +59 -0
  218. data/lib/pubid/easc/identifiers/pmg.rb +20 -0
  219. data/lib/pubid/easc/identifiers/rmg.rb +19 -0
  220. data/lib/pubid/easc/identifiers.rb +10 -0
  221. data/lib/pubid/easc/parser.rb +88 -0
  222. data/lib/pubid/easc/renderer.rb +48 -0
  223. data/lib/pubid/easc/urn_generator.rb +46 -0
  224. data/lib/pubid/easc/urn_parser.rb +57 -0
  225. data/lib/pubid/easc.rb +53 -0
  226. data/lib/pubid/ecma/builder.rb +45 -0
  227. data/lib/pubid/ecma/identifier.rb +147 -0
  228. data/lib/pubid/ecma/identifiers/memento.rb +30 -0
  229. data/lib/pubid/ecma/identifiers/standard.rb +31 -0
  230. data/lib/pubid/ecma/identifiers/technical_report.rb +31 -0
  231. data/lib/pubid/ecma/identifiers.rb +11 -0
  232. data/lib/pubid/ecma/parser.rb +76 -0
  233. data/lib/pubid/ecma/renderer.rb +67 -0
  234. data/lib/pubid/ecma/urn_generator.rb +46 -0
  235. data/lib/pubid/ecma/urn_parser.rb +51 -0
  236. data/lib/pubid/ecma.rb +68 -0
  237. data/lib/pubid/errors.rb +80 -0
  238. data/lib/pubid/etsi/builder.rb +13 -4
  239. data/lib/pubid/etsi/identifier.rb +1 -3
  240. data/lib/pubid/etsi/identifiers/base.rb +43 -11
  241. data/lib/pubid/etsi/identifiers/etsi_standard.rb +207 -4
  242. data/lib/pubid/etsi/identifiers/supplement_identifier.rb +45 -3
  243. data/lib/pubid/etsi/identifiers.rb +0 -1
  244. data/lib/pubid/etsi/parser.rb +9 -3
  245. data/lib/pubid/etsi/renderer.rb +8 -2
  246. data/lib/pubid/etsi.rb +6 -5
  247. data/lib/pubid/evs/builder.rb +26 -0
  248. data/lib/pubid/evs/identifier.rb +37 -0
  249. data/lib/pubid/evs/identifiers/national_adoption.rb +17 -0
  250. data/lib/pubid/evs/identifiers.rb +9 -0
  251. data/lib/pubid/evs/parser.rb +27 -0
  252. data/lib/pubid/evs/renderer.rb +30 -0
  253. data/lib/pubid/evs/urn_generator.rb +30 -0
  254. data/lib/pubid/evs/urn_parser.rb +80 -0
  255. data/lib/pubid/evs.rb +90 -0
  256. data/lib/pubid/export/exporter.rb +2 -1
  257. data/lib/pubid/export/flavor_exporter.rb +18 -3
  258. data/lib/pubid/export.rb +1 -0
  259. data/lib/pubid/format_detector.rb +1 -0
  260. data/lib/pubid/gb/builder.rb +45 -0
  261. data/lib/pubid/gb/identifier.rb +67 -0
  262. data/lib/pubid/gb/identifiers/standard.rb +34 -0
  263. data/lib/pubid/gb/identifiers.rb +9 -0
  264. data/lib/pubid/gb/parser.rb +63 -0
  265. data/lib/pubid/gb/renderer.rb +35 -0
  266. data/lib/pubid/gb.rb +88 -0
  267. data/lib/pubid/gost/builder.rb +185 -0
  268. data/lib/pubid/gost/identifier.rb +47 -0
  269. data/lib/pubid/gost/identifiers/foreign_reference.rb +25 -0
  270. data/lib/pubid/gost/identifiers/harmonized.rb +39 -0
  271. data/lib/pubid/gost/identifiers/identical_adoption.rb +39 -0
  272. data/lib/pubid/gost/identifiers/interstate_standard.rb +20 -0
  273. data/lib/pubid/gost/identifiers/national_standard.rb +19 -0
  274. data/lib/pubid/gost/identifiers.rb +13 -0
  275. data/lib/pubid/gost/parser.rb +105 -0
  276. data/lib/pubid/gost/renderer.rb +48 -0
  277. data/lib/pubid/gost/urn_generator.rb +46 -0
  278. data/lib/pubid/gost/urn_parser.rb +52 -0
  279. data/lib/pubid/gost.rb +50 -0
  280. data/lib/pubid/iala/builder.rb +22 -27
  281. data/lib/pubid/iala/identifier.rb +36 -8
  282. data/lib/pubid/iala/identifiers/advice.rb +1 -1
  283. data/lib/pubid/iala/identifiers/annex.rb +10 -10
  284. data/lib/pubid/iala/identifiers/general_assembly.rb +5 -2
  285. data/lib/pubid/iala/identifiers/guideline.rb +3 -1
  286. data/lib/pubid/iala/identifiers/letter.rb +1 -1
  287. data/lib/pubid/iala/identifiers/manual.rb +3 -1
  288. data/lib/pubid/iala/identifiers/model_course.rb +3 -1
  289. data/lib/pubid/iala/identifiers/recommendation.rb +3 -1
  290. data/lib/pubid/iala/identifiers/report.rb +1 -1
  291. data/lib/pubid/iala/identifiers/resolution.rb +1 -1
  292. data/lib/pubid/iala/identifiers/standard.rb +3 -1
  293. data/lib/pubid/iala/identifiers.rb +0 -1
  294. data/lib/pubid/iala/parser.rb +10 -2
  295. data/lib/pubid/iala/renderer.rb +1 -1
  296. data/lib/pubid/iala/urn_generator.rb +1 -1
  297. data/lib/pubid/iala/urn_parser.rb +24 -3
  298. data/lib/pubid/iala.rb +5 -4
  299. data/lib/pubid/iana/builder.rb +22 -0
  300. data/lib/pubid/iana/identifier.rb +140 -0
  301. data/lib/pubid/iana/identifiers/registry.rb +69 -0
  302. data/lib/pubid/iana/identifiers.rb +9 -0
  303. data/lib/pubid/iana/parser.rb +38 -0
  304. data/lib/pubid/iana/renderer.rb +32 -0
  305. data/lib/pubid/iana/urn_generator.rb +19 -0
  306. data/lib/pubid/iana/urn_parser.rb +24 -0
  307. data/lib/pubid/iana.rb +70 -0
  308. data/lib/pubid/identifier.rb +797 -26
  309. data/lib/pubid/identifier_metadata.rb +1 -0
  310. data/lib/pubid/idf/builder.rb +4 -4
  311. data/lib/pubid/idf/identifier.rb +18 -9
  312. data/lib/pubid/idf/parser.rb +3 -3
  313. data/lib/pubid/idf/renderer.rb +5 -5
  314. data/lib/pubid/idf/supplement_identifier.rb +3 -3
  315. data/lib/pubid/idf/urn_generator.rb +1 -1
  316. data/lib/pubid/idf.rb +7 -1
  317. data/lib/pubid/iec/builder.rb +49 -32
  318. data/lib/pubid/iec/components.rb +0 -1
  319. data/lib/pubid/iec/identifier.rb +112 -77
  320. data/lib/pubid/iec/identifiers/amendment.rb +6 -2
  321. data/lib/pubid/iec/identifiers/base.rb +2 -2
  322. data/lib/pubid/iec/identifiers/component_specification.rb +0 -10
  323. data/lib/pubid/iec/identifiers/conformity_assessment.rb +0 -10
  324. data/lib/pubid/iec/identifiers/consolidated_identifier.rb +6 -0
  325. data/lib/pubid/iec/identifiers/corrigendum.rb +2 -2
  326. data/lib/pubid/iec/identifiers/fragment_identifier.rb +13 -15
  327. data/lib/pubid/iec/identifiers/guide.rb +0 -16
  328. data/lib/pubid/iec/identifiers/international_standard.rb +22 -6
  329. data/lib/pubid/iec/identifiers/operational_document.rb +0 -10
  330. data/lib/pubid/iec/identifiers/publicly_available_specification.rb +0 -12
  331. data/lib/pubid/iec/identifiers/sheet_identifier.rb +12 -12
  332. data/lib/pubid/iec/identifiers/societal_technology_trend_report.rb +0 -10
  333. data/lib/pubid/iec/identifiers/systems_reference_document.rb +0 -10
  334. data/lib/pubid/iec/identifiers/technical_group.rb +30 -0
  335. data/lib/pubid/iec/identifiers/technical_report.rb +1 -18
  336. data/lib/pubid/iec/identifiers/technical_specification.rb +1 -18
  337. data/lib/pubid/iec/identifiers/technology_report.rb +0 -10
  338. data/lib/pubid/iec/identifiers/test_report_form.rb +0 -11
  339. data/lib/pubid/iec/identifiers/vap_identifier.rb +12 -12
  340. data/lib/pubid/iec/identifiers/white_paper.rb +0 -10
  341. data/lib/pubid/iec/identifiers/working_document.rb +24 -10
  342. data/lib/pubid/iec/identifiers.rb +1 -0
  343. data/lib/pubid/iec/parser.rb +53 -28
  344. data/lib/pubid/iec/renderer.rb +31 -18
  345. data/lib/pubid/iec/single_identifier.rb +34 -11
  346. data/lib/pubid/iec/supplement_identifier.rb +32 -19
  347. data/lib/pubid/iec/urn_generator.rb +227 -61
  348. data/lib/pubid/iec/urn_parser.rb +42 -4
  349. data/lib/pubid/iec.rb +2 -6
  350. data/lib/pubid/ieee/aiee/builder.rb +17 -3
  351. data/lib/pubid/ieee/aiee/identifier.rb +46 -62
  352. data/lib/pubid/ieee/aiee/parser.rb +1 -1
  353. data/lib/pubid/ieee/builder.rb +386 -69
  354. data/lib/pubid/ieee/compaction.rb +121 -0
  355. data/lib/pubid/ieee/components/draft.rb +47 -7
  356. data/lib/pubid/ieee/identifier.rb +1 -3
  357. data/lib/pubid/ieee/identifiers/adopted_standard.rb +62 -5
  358. data/lib/pubid/ieee/identifiers/amendment.rb +40 -0
  359. data/lib/pubid/ieee/identifiers/base.rb +148 -18
  360. data/lib/pubid/ieee/identifiers/code_number.rb +82 -0
  361. data/lib/pubid/ieee/identifiers/conformance_identifier.rb +6 -2
  362. data/lib/pubid/ieee/identifiers/corrigendum.rb +17 -2
  363. data/lib/pubid/ieee/identifiers/csa_dual_published.rb +24 -3
  364. data/lib/pubid/ieee/identifiers/dual_identifier.rb +3 -3
  365. data/lib/pubid/ieee/identifiers/dual_published.rb +58 -3
  366. data/lib/pubid/ieee/identifiers/iec_ieee_copublished.rb +102 -5
  367. data/lib/pubid/ieee/identifiers/interpretation_identifier.rb +25 -1
  368. data/lib/pubid/ieee/identifiers/joint_development.rb +64 -15
  369. data/lib/pubid/ieee/identifiers/multi_numbered_identifier.rb +58 -6
  370. data/lib/pubid/ieee/identifiers/nesc/base.rb +90 -19
  371. data/lib/pubid/ieee/identifiers/nesc/draft.rb +20 -5
  372. data/lib/pubid/ieee/identifiers/nesc/edition.rb +32 -0
  373. data/lib/pubid/ieee/identifiers/nesc/handbook.rb +16 -5
  374. data/lib/pubid/ieee/identifiers/nesc/redline.rb +11 -2
  375. data/lib/pubid/ieee/identifiers/nesc/standard.rb +18 -3
  376. data/lib/pubid/ieee/identifiers/nesc.rb +1 -0
  377. data/lib/pubid/ieee/identifiers/parenthetical_identifier.rb +3 -3
  378. data/lib/pubid/ieee/identifiers/project_draft_identifier.rb +14 -6
  379. data/lib/pubid/ieee/identifiers/redlined_standard.rb +4 -4
  380. data/lib/pubid/ieee/identifiers/si_standard.rb +4 -1
  381. data/lib/pubid/ieee/identifiers/standard.rb +4 -1
  382. data/lib/pubid/ieee/identifiers/supplement_identifier.rb +26 -7
  383. data/lib/pubid/ieee/identifiers.rb +2 -1
  384. data/lib/pubid/ieee/ire/builder.rb +7 -6
  385. data/lib/pubid/ieee/ire/identifier.rb +48 -33
  386. data/lib/pubid/ieee/ire/parser.rb +1 -1
  387. data/lib/pubid/ieee/nesc/builder.rb +33 -20
  388. data/lib/pubid/ieee/nesc/parser.rb +16 -15
  389. data/lib/pubid/ieee/parser.rb +462 -44
  390. data/lib/pubid/ieee/pre_parser.rb +1 -1
  391. data/lib/pubid/ieee/renderer.rb +158 -57
  392. data/lib/pubid/ieee/typed_stages.rb +12 -1
  393. data/lib/pubid/ieee/urn_generator.rb +35 -8
  394. data/lib/pubid/ieee.rb +63 -4
  395. data/lib/pubid/ieee_debug.rb +1 -0
  396. data/lib/pubid/ietf/builder.rb +78 -0
  397. data/lib/pubid/ietf/identifier.rb +5 -0
  398. data/lib/pubid/ietf/identifiers/base.rb +72 -0
  399. data/lib/pubid/ietf/identifiers/bcp.rb +36 -0
  400. data/lib/pubid/ietf/identifiers/fyi.rb +36 -0
  401. data/lib/pubid/ietf/identifiers/internet_draft.rb +45 -0
  402. data/lib/pubid/ietf/identifiers/rfc.rb +27 -0
  403. data/lib/pubid/ietf/identifiers/serialization.rb +51 -0
  404. data/lib/pubid/ietf/identifiers/std.rb +36 -0
  405. data/lib/pubid/ietf/identifiers.rb +14 -0
  406. data/lib/pubid/ietf/parser.rb +61 -0
  407. data/lib/pubid/ietf/renderer.rb +64 -0
  408. data/lib/pubid/ietf/urn_generator.rb +37 -0
  409. data/lib/pubid/ietf/urn_parser.rb +43 -0
  410. data/lib/pubid/ietf.rb +86 -0
  411. data/lib/pubid/iho/identifier.rb +1 -3
  412. data/lib/pubid/iho/identifiers/base.rb +11 -10
  413. data/lib/pubid/iho/identifiers/bibliographic.rb +1 -1
  414. data/lib/pubid/iho/identifiers/circular_letter.rb +1 -1
  415. data/lib/pubid/iho/identifiers/miscellaneous.rb +1 -1
  416. data/lib/pubid/iho/identifiers/publication.rb +1 -1
  417. data/lib/pubid/iho/identifiers/standard.rb +1 -1
  418. data/lib/pubid/iho/identifiers.rb +0 -1
  419. data/lib/pubid/iho/parser.rb +1 -1
  420. data/lib/pubid/iho.rb +8 -7
  421. data/lib/pubid/isbn/builder.rb +44 -0
  422. data/lib/pubid/isbn/check_digit.rb +53 -0
  423. data/lib/pubid/isbn/identifier.rb +81 -0
  424. data/lib/pubid/isbn/identifiers/book.rb +30 -0
  425. data/lib/pubid/isbn/identifiers.rb +9 -0
  426. data/lib/pubid/isbn/parser.rb +45 -0
  427. data/lib/pubid/isbn/renderer.rb +16 -0
  428. data/lib/pubid/isbn.rb +53 -0
  429. data/lib/pubid/iso/builder.rb +9 -4
  430. data/lib/pubid/iso/bundled_identifier.rb +5 -3
  431. data/lib/pubid/iso/combined_identifier.rb +6 -4
  432. data/lib/pubid/iso/identifier.rb +29 -8
  433. data/lib/pubid/iso/identifiers/directives_supplement.rb +12 -12
  434. data/lib/pubid/iso/normalizer.rb +2 -2
  435. data/lib/pubid/iso/parser.rb +17 -13
  436. data/lib/pubid/iso/supplement_identifier.rb +19 -6
  437. data/lib/pubid/iso/urn_generator.rb +4 -1
  438. data/lib/pubid/iso/urn_parser.rb +1 -1
  439. data/lib/pubid/iso.rb +7 -1
  440. data/lib/pubid/itu/builder.rb +230 -29
  441. data/lib/pubid/itu/components/code.rb +48 -5
  442. data/lib/pubid/itu/components/designation.rb +35 -0
  443. data/lib/pubid/itu/components.rb +1 -0
  444. data/lib/pubid/itu/identifier.rb +1 -3
  445. data/lib/pubid/itu/identifiers/addendum.rb +15 -0
  446. data/lib/pubid/itu/identifiers/amendment.rb +7 -29
  447. data/lib/pubid/itu/identifiers/annex.rb +13 -2
  448. data/lib/pubid/itu/identifiers/annex_of_recommendation.rb +87 -0
  449. data/lib/pubid/itu/identifiers/appendix_of_recommendation.rb +92 -0
  450. data/lib/pubid/itu/identifiers/base.rb +357 -25
  451. data/lib/pubid/itu/identifiers/combined_identifier.rb +99 -23
  452. data/lib/pubid/itu/identifiers/corrigendum.rb +16 -23
  453. data/lib/pubid/itu/identifiers/errata.rb +15 -0
  454. data/lib/pubid/itu/identifiers/handbook.rb +38 -0
  455. data/lib/pubid/itu/identifiers/question.rb +63 -0
  456. data/lib/pubid/itu/identifiers/recommendation.rb +3 -2
  457. data/lib/pubid/itu/identifiers/report.rb +39 -0
  458. data/lib/pubid/itu/identifiers/special_publication.rb +3 -1
  459. data/lib/pubid/itu/identifiers/standard_serialization.rb +58 -0
  460. data/lib/pubid/itu/identifiers/supplement.rb +119 -14
  461. data/lib/pubid/itu/identifiers.rb +10 -1
  462. data/lib/pubid/itu/model.rb +1 -1
  463. data/lib/pubid/itu/parser.rb +481 -27
  464. data/lib/pubid/itu/urn_generator.rb +36 -6
  465. data/lib/pubid/itu/urn_parser.rb +8 -0
  466. data/lib/pubid/itu.rb +12 -3
  467. data/lib/pubid/jcgm/builder.rb +24 -14
  468. data/lib/pubid/jcgm/identifier.rb +10 -0
  469. data/lib/pubid/jcgm/identifiers/amendment.rb +0 -2
  470. data/lib/pubid/jcgm/identifiers/corrigendum.rb +34 -0
  471. data/lib/pubid/jcgm/identifiers/gum_guide.rb +3 -3
  472. data/lib/pubid/jcgm/identifiers/meeting.rb +47 -0
  473. data/lib/pubid/jcgm/identifiers.rb +2 -0
  474. data/lib/pubid/jcgm/parser.rb +74 -7
  475. data/lib/pubid/jcgm/renderer.rb +33 -12
  476. data/lib/pubid/jcgm/single_identifier.rb +84 -6
  477. data/lib/pubid/jcgm/supplement_identifier.rb +39 -4
  478. data/lib/pubid/jcgm/urn_generator.rb +21 -13
  479. data/lib/pubid/jcgm/urn_parser.rb +164 -10
  480. data/lib/pubid/jcgm.rb +10 -0
  481. data/lib/pubid/jis/identifier.rb +62 -3
  482. data/lib/pubid/jis/parser.rb +1 -1
  483. data/lib/pubid/jis.rb +1 -0
  484. data/lib/pubid/nist/builder.rb +29 -9
  485. data/lib/pubid/nist/caster.rb +7 -45
  486. data/lib/pubid/nist/circular_supplement_builder.rb +7 -5
  487. data/lib/pubid/nist/components/stage.rb +2 -2
  488. data/lib/pubid/nist/components/supplement.rb +5 -2
  489. data/lib/pubid/nist/configuration.rb +6 -2
  490. data/lib/pubid/nist/identifier.rb +1 -3
  491. data/lib/pubid/nist/identifiers/base.rb +133 -39
  492. data/lib/pubid/nist/identifiers/circular.rb +10 -3
  493. data/lib/pubid/nist/identifiers/circular_supplement.rb +14 -5
  494. data/lib/pubid/nist/identifiers/commercial_standard.rb +1 -1
  495. data/lib/pubid/nist/identifiers/commercial_standard_emergency.rb +1 -1
  496. data/lib/pubid/nist/identifiers/commercial_standards_monthly.rb +8 -2
  497. data/lib/pubid/nist/identifiers/crpl_report.rb +8 -2
  498. data/lib/pubid/nist/identifiers/dated_document.rb +1 -1
  499. data/lib/pubid/nist/identifiers/federal_information_processing_standards.rb +1 -1
  500. data/lib/pubid/nist/identifiers/grant_contractor_report.rb +1 -1
  501. data/lib/pubid/nist/identifiers/handbook.rb +10 -3
  502. data/lib/pubid/nist/identifiers/internal_report.rb +13 -6
  503. data/lib/pubid/nist/identifiers/letter_circular.rb +1 -1
  504. data/lib/pubid/nist/identifiers/miscellaneous_publication.rb +15 -7
  505. data/lib/pubid/nist/identifiers/monograph.rb +15 -8
  506. data/lib/pubid/nist/identifiers/ncstar.rb +2 -2
  507. data/lib/pubid/nist/identifiers/nsrds.rb +1 -1
  508. data/lib/pubid/nist/identifiers/owmwp.rb +1 -1
  509. data/lib/pubid/nist/identifiers/report.rb +15 -7
  510. data/lib/pubid/nist/identifiers/special_publication.rb +10 -1
  511. data/lib/pubid/nist/identifiers/technical_note.rb +1 -1
  512. data/lib/pubid/nist/identifiers.rb +0 -1
  513. data/lib/pubid/nist/parser.rb +27 -4
  514. data/lib/pubid/nist/preprocessor.rb +15 -0
  515. data/lib/pubid/nist/router.rb +2 -2
  516. data/lib/pubid/nist/series/ir.rb +0 -3
  517. data/lib/pubid/nist/supplement_identifier.rb +24 -9
  518. data/lib/pubid/nist.rb +19 -13
  519. data/lib/pubid/oasis/builder.rb +79 -0
  520. data/lib/pubid/oasis/identifier.rb +160 -0
  521. data/lib/pubid/oasis/identifiers/standard.rb +29 -0
  522. data/lib/pubid/oasis/identifiers.rb +9 -0
  523. data/lib/pubid/oasis/parser.rb +34 -0
  524. data/lib/pubid/oasis/renderer.rb +29 -0
  525. data/lib/pubid/oasis/urn_generator.rb +25 -0
  526. data/lib/pubid/oasis/urn_parser.rb +22 -0
  527. data/lib/pubid/oasis.rb +80 -0
  528. data/lib/pubid/ogc/builder.rb +33 -0
  529. data/lib/pubid/ogc/identifier.rb +81 -0
  530. data/lib/pubid/ogc/identifiers/document.rb +26 -0
  531. data/lib/pubid/ogc/identifiers.rb +10 -0
  532. data/lib/pubid/ogc/parser.rb +40 -0
  533. data/lib/pubid/ogc/renderer.rb +33 -0
  534. data/lib/pubid/ogc/urn_generator.rb +14 -0
  535. data/lib/pubid/ogc/urn_parser.rb +22 -0
  536. data/lib/pubid/ogc.rb +72 -0
  537. data/lib/pubid/oiml/builder.rb +36 -8
  538. data/lib/pubid/oiml/identifier.rb +9 -1
  539. data/lib/pubid/oiml/identifiers/basic_publication.rb +2 -0
  540. data/lib/pubid/oiml/identifiers/bulletin.rb +36 -0
  541. data/lib/pubid/oiml/identifiers/code_number.rb +90 -0
  542. data/lib/pubid/oiml/identifiers/document.rb +2 -0
  543. data/lib/pubid/oiml/identifiers/expert_report.rb +2 -0
  544. data/lib/pubid/oiml/identifiers/guide.rb +2 -0
  545. data/lib/pubid/oiml/identifiers/recommendation.rb +2 -0
  546. data/lib/pubid/oiml/identifiers/seminar_report.rb +2 -0
  547. data/lib/pubid/oiml/identifiers/vocabulary.rb +2 -0
  548. data/lib/pubid/oiml/identifiers.rb +1 -0
  549. data/lib/pubid/oiml/parser.rb +12 -13
  550. data/lib/pubid/oiml/renderer.rb +13 -13
  551. data/lib/pubid/oiml/single_identifier.rb +41 -37
  552. data/lib/pubid/oiml/supplement_identifier.rb +36 -9
  553. data/lib/pubid/oiml/urn_parser.rb +23 -6
  554. data/lib/pubid/oiml.rb +10 -0
  555. data/lib/pubid/omg/builder.rb +20 -0
  556. data/lib/pubid/omg/identifier.rb +61 -0
  557. data/lib/pubid/omg/identifiers/specification.rb +32 -0
  558. data/lib/pubid/omg/identifiers.rb +9 -0
  559. data/lib/pubid/omg/parser.rb +93 -0
  560. data/lib/pubid/omg/renderer.rb +31 -0
  561. data/lib/pubid/omg.rb +52 -0
  562. data/lib/pubid/parser/grammar.rb +74 -0
  563. data/lib/pubid/parser.rb +2 -0
  564. data/lib/pubid/parsers/mr_string.rb +120 -7
  565. data/lib/pubid/plateau/builder.rb +8 -4
  566. data/lib/pubid/plateau/identifier.rb +1 -3
  567. data/lib/pubid/plateau/identifiers/base.rb +1 -6
  568. data/lib/pubid/plateau/identifiers/handbook.rb +1 -1
  569. data/lib/pubid/plateau/identifiers/technical_report.rb +1 -1
  570. data/lib/pubid/plateau/identifiers.rb +0 -1
  571. data/lib/pubid/plateau/parser.rb +16 -6
  572. data/lib/pubid/plateau/renderer.rb +1 -1
  573. data/lib/pubid/plateau/supplement_identifier.rb +11 -11
  574. data/lib/pubid/plateau.rb +14 -4
  575. data/lib/pubid/prefixes_support.rb +1 -0
  576. data/lib/pubid/renderers/annotator.rb +233 -0
  577. data/lib/pubid/renderers/base.rb +13 -0
  578. data/lib/pubid/renderers/directives_renderer.rb +3 -3
  579. data/lib/pubid/renderers/human_readable.rb +6 -4
  580. data/lib/pubid/renderers/mr_string.rb +54 -6
  581. data/lib/pubid/renderers/supplement_renderer.rb +1 -1
  582. data/lib/pubid/renderers.rb +1 -0
  583. data/lib/pubid/rendering/numbering.rb +25 -7
  584. data/lib/pubid/rendering/supplement.rb +4 -4
  585. data/lib/pubid/rendering.rb +1 -0
  586. data/lib/pubid/sae/builder.rb +1 -1
  587. data/lib/pubid/sae/identifier.rb +1 -3
  588. data/lib/pubid/sae/identifiers/base.rb +19 -9
  589. data/lib/pubid/sae/identifiers.rb +0 -1
  590. data/lib/pubid/sae/parser.rb +1 -1
  591. data/lib/pubid/sae.rb +4 -3
  592. data/lib/pubid/schema/declaration.rb +33 -0
  593. data/lib/pubid/schema/error.rb +14 -0
  594. data/lib/pubid/schema/identifier_type.rb +23 -0
  595. data/lib/pubid/schema/loader.rb +117 -0
  596. data/lib/pubid/schema/typed_stage.rb +24 -0
  597. data/lib/pubid/schema.rb +25 -0
  598. data/lib/pubid/tgpp/builder.rb +41 -0
  599. data/lib/pubid/tgpp/identifier.rb +107 -0
  600. data/lib/pubid/tgpp/identifiers/technical_report.rb +32 -0
  601. data/lib/pubid/tgpp/identifiers/technical_specification.rb +33 -0
  602. data/lib/pubid/tgpp/identifiers.rb +11 -0
  603. data/lib/pubid/tgpp/parser.rb +78 -0
  604. data/lib/pubid/tgpp/renderer.rb +52 -0
  605. data/lib/pubid/tgpp/urn_generator.rb +39 -0
  606. data/lib/pubid/tgpp/urn_parser.rb +30 -0
  607. data/lib/pubid/tgpp.rb +84 -0
  608. data/lib/pubid/type_resolver.rb +74 -0
  609. data/lib/pubid/un/builder.rb +42 -0
  610. data/lib/pubid/un/identifier.rb +40 -0
  611. data/lib/pubid/un/identifiers/document.rb +24 -0
  612. data/lib/pubid/un/identifiers.rb +9 -0
  613. data/lib/pubid/un/parser.rb +25 -0
  614. data/lib/pubid/un/renderer.rb +11 -0
  615. data/lib/pubid/un.rb +47 -0
  616. data/lib/pubid/urn_parser/errors.rb +8 -1
  617. data/lib/pubid/urn_parser.rb +1 -0
  618. data/lib/pubid/utils.rb +1 -0
  619. data/lib/pubid/version.rb +1 -1
  620. data/lib/pubid/w3c/builder.rb +55 -0
  621. data/lib/pubid/w3c/identifier.rb +130 -0
  622. data/lib/pubid/w3c/identifiers/candidate_recommendation.rb +31 -0
  623. data/lib/pubid/w3c/identifiers/candidate_recommendation_draft.rb +31 -0
  624. data/lib/pubid/w3c/identifiers/draft_note.rb +31 -0
  625. data/lib/pubid/w3c/identifiers/note.rb +30 -0
  626. data/lib/pubid/w3c/identifiers/obsolete_recommendation.rb +31 -0
  627. data/lib/pubid/w3c/identifiers/proposed_edited_recommendation.rb +31 -0
  628. data/lib/pubid/w3c/identifiers/proposed_recommendation.rb +31 -0
  629. data/lib/pubid/w3c/identifiers/recommendation.rb +31 -0
  630. data/lib/pubid/w3c/identifiers/standard.rb +12 -0
  631. data/lib/pubid/w3c/identifiers/superseded_recommendation.rb +31 -0
  632. data/lib/pubid/w3c/identifiers/working_draft.rb +30 -0
  633. data/lib/pubid/w3c/identifiers.rb +25 -0
  634. data/lib/pubid/w3c/parser.rb +43 -0
  635. data/lib/pubid/w3c/renderer.rb +35 -0
  636. data/lib/pubid/w3c/urn_generator.rb +18 -0
  637. data/lib/pubid/w3c/urn_parser.rb +36 -0
  638. data/lib/pubid/w3c.rb +68 -0
  639. data/lib/pubid/xsf/builder.rb +17 -0
  640. data/lib/pubid/xsf/identifier.rb +66 -0
  641. data/lib/pubid/xsf/identifiers/xep.rb +31 -0
  642. data/lib/pubid/xsf/identifiers.rb +9 -0
  643. data/lib/pubid/xsf/parser.rb +38 -0
  644. data/lib/pubid/xsf/renderer.rb +23 -0
  645. data/lib/pubid/xsf/urn_generator.rb +13 -0
  646. data/lib/pubid/xsf/urn_parser.rb +18 -0
  647. data/lib/pubid/xsf.rb +82 -0
  648. data/lib/pubid.rb +424 -22
  649. data/lib/tasks/conformance.rake +34 -0
  650. data/lib/tasks/docs.rake +3 -3
  651. data/lib/tasks/schema.rake +88 -0
  652. data/schema/core/joint_prefixes.yaml +24 -0
  653. data/schema/iec.yaml +1017 -0
  654. data/schema/iso.yaml +1586 -0
  655. data/schema/schema.schema.yaml +96 -0
  656. metadata +241 -19
  657. data/archived-gems/pubid-ccsds/update_codes.yaml +0 -1
  658. data/archived-gems/pubid-iec/stages.yaml +0 -129
  659. data/archived-gems/pubid-iec/update_codes.yaml +0 -67
  660. data/archived-gems/pubid-ieee/update_codes.yaml +0 -104
  661. data/archived-gems/pubid-iso/stages.yaml +0 -106
  662. data/archived-gems/pubid-iso/update_codes.yaml +0 -4
  663. data/archived-gems/pubid-itu/i18n.yaml +0 -13
  664. data/archived-gems/pubid-itu/series.yaml +0 -42
  665. data/archived-gems/pubid-nist/publishers.yaml +0 -6
  666. data/archived-gems/pubid-nist/update_codes.yaml +0 -93
  667. data/archived-gems/pubid-plateau/update_codes.yaml +0 -6
  668. data/lib/pubid/cen_cenelec/supplement_identifier.rb +0 -48
  669. data/lib/pubid/cie/components/code.rb +0 -80
  670. data/lib/pubid/iala/identifiers/base.rb +0 -14
  671. data/lib/pubid/iec/components/code.rb +0 -36
  672. /data/{archived-gems/pubid-nist → data/nist}/stages.yaml +0 -0
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pubid
4
+ module Ieee
5
+ # Serialization compaction for IEEE hashes — a recursive transform hooked
6
+ # into Ieee::Identifier#to_hash (collapse) and .from_hash (expand). Two
7
+ # compactions, both provably lossless and nested-safe (they descend into a
8
+ # Corrigendum `base` too):
9
+ #
10
+ # 1. **typed_stage -> `stage` scalar.** The multi-field `typed_stage` object
11
+ # becomes a single `stage` key holding its canonical abbreviation,
12
+ # rebuilt from the TYPED_STAGES registry. The abbreviation keys the
13
+ # registry uniquely, and the collapse fires only when the registry's
14
+ # reconstruction is byte-identical to the serialized sub-hash — an
15
+ # unrecognised / non-matching stage is left as the full object (fails
16
+ # closed).
17
+ #
18
+ # 2. **`draft` slash strip.** `Components::Draft#to_s` renders `"/D3"`; the
19
+ # leading `/` is boilerplate, so the serialized form is `"D3"`. No expand
20
+ # step is needed — `Draft.parse` already accepts the slashless form and
21
+ # reproduces `"/D3"` (verified across the whole corpus), so from_hash and
22
+ # rendering are unchanged.
23
+ #
24
+ # A recursive hash transform (rather than lutaml attributes) is deliberate:
25
+ # `stage` collides with the inherited `Components::Stage` attribute name (a
26
+ # redefinition would reintroduce the multi-flavor nondeterminism we hit with
27
+ # `number`), and only a whole-hash walk reaches a nested Corrigendum `base`,
28
+ # which a per-object `to_hash` override cannot (the nested-serialization
29
+ # wall). The runtime `typed_stage`/`draft_obj` objects are untouched.
30
+ module Compaction
31
+ module_function
32
+
33
+ # abbr => canonical serialized typed_stage sub-hash, mirroring what the
34
+ # parent serializer + canonicalizer emit (nils dropped, and a default
35
+ # `project_status: false` dropped). Memoized.
36
+ def sub_hash_for(abbr)
37
+ (@sub_hash_for ||= build_sub_hash_map)[abbr]
38
+ end
39
+
40
+ def build_sub_hash_map
41
+ TypedStages::TYPED_STAGES.to_h { |ts| [ts.abbr.first, entry_sub_hash(ts)] }
42
+ end
43
+
44
+ # NB: this must list every Components::TypedStage attribute. If a field is
45
+ # added there but not here, the collapse guard fails closed — affected
46
+ # stages stay full objects (still lossless) but stop compacting; extend
47
+ # this list to keep them compact.
48
+ def entry_sub_hash(typed_stage)
49
+ {
50
+ "abbr" => typed_stage.abbr,
51
+ "stage_code" => typed_stage.stage_code,
52
+ "type_code" => typed_stage.type_code,
53
+ "iso_stage_equivalent" => typed_stage.iso_stage_equivalent,
54
+ "ieee_draft_equivalent" => typed_stage.ieee_draft_equivalent,
55
+ "approval_status" => typed_stage.approval_status,
56
+ "project_status" => typed_stage.project_status || nil,
57
+ }.compact
58
+ end
59
+
60
+ # Recursively compact every hash in the tree: collapse a reproducible
61
+ # `typed_stage` to a `stage` scalar and strip the leading `/` off `draft`.
62
+ def collapse(node)
63
+ collapse_hash(node) if node.is_a?(::Hash)
64
+ node.each { |v| collapse(v) } if node.is_a?(::Array)
65
+ node
66
+ end
67
+
68
+ def collapse_hash(node)
69
+ collapse_stage(node)
70
+ draft = node["draft"]
71
+ node["draft"] = draft.delete_prefix("/") if draft.is_a?(::String)
72
+ node.each_value { |v| collapse(v) }
73
+ end
74
+
75
+ # Collapse only when the registry rebuilds the exact same sub-hash, so no
76
+ # information is ever lost.
77
+ def collapse_stage(node)
78
+ ts = node["typed_stage"]
79
+ abbr = ts["abbr"]&.first if ts.is_a?(::Hash)
80
+ return unless abbr && sub_hash_for(abbr) == ts
81
+
82
+ node.delete("typed_stage")
83
+ node["stage"] = abbr
84
+ end
85
+
86
+ # Inverse of {collapse} for `stage`: replace a recognised `stage` scalar
87
+ # with its registry typed_stage sub-hash so lutaml rebuilds the component
88
+ # (nested bases included). `draft` needs no expansion — Draft.parse
89
+ # accepts the slashless form. An unrecognised value is left alone.
90
+ def expand(node)
91
+ expand_hash(node) if node.is_a?(::Hash)
92
+ node.each { |v| expand(v) } if node.is_a?(::Array)
93
+ node
94
+ end
95
+
96
+ def expand_hash(node)
97
+ key = stage_key(node)
98
+ sub = sub_hash_for(node[key].to_s) if key
99
+ if sub
100
+ node.delete(key)
101
+ node["typed_stage"] = deep_dup(sub)
102
+ end
103
+ node.each_value { |v| expand(v) }
104
+ end
105
+
106
+ def stage_key(node)
107
+ return :stage if node.key?(:stage)
108
+
109
+ "stage" if node.key?("stage")
110
+ end
111
+
112
+ def deep_dup(node)
113
+ case node
114
+ when ::Hash then node.transform_values { |v| deep_dup(v) }
115
+ when ::Array then node.map { |v| deep_dup(v) }
116
+ else node
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -42,19 +42,59 @@ day: nil)
42
42
  self.day = day
43
43
  end
44
44
 
45
- # Parse a draft string like "D5", "D3.4", or "5" into a Draft object.
45
+ # Month names as an alternation, longest-first so "September" wins over
46
+ # "Sep"; each may carry a trailing period ("Sept."). Used to split a
47
+ # rendered draft's trailing date back out.
48
+ MONTH_ALTERNATION =
49
+ MONTH_NAMES.keys.sort_by { |m| -m.length }
50
+ .map { |m| "#{Regexp.escape(m)}\\.?" }.join("|").freeze
51
+ private_constant :MONTH_ALTERNATION
52
+
53
+ DATE_SUFFIX =
54
+ /(?:(, | )(#{MONTH_ALTERNATION})(?: (\d{1,2}))?(?:, | )| )((?:19|20)\d{2}[a-z]{0,2})\z/
55
+ private_constant :DATE_SUFFIX
56
+
57
+ # Parse a rendered draft such as "/D3", "/D3.4", "/D7 Jul 2019", or a
58
+ # bare "D5"/"5" back into a Draft. This is the exact inverse of #to_s:
59
+ # `Draft.parse(d.to_s).to_s == d.to_s` must hold, because the identifier
60
+ # stores the draft as its rendered string and rebuilds the object with
61
+ # this method after `from_hash` (a mismatch breaks `to_hash` round-trip).
46
62
  # @param value [String, Draft] the value to coerce
47
63
  # @return [Draft]
48
64
  def self.parse(value)
49
65
  return value if value.is_a?(Draft)
50
66
 
51
- str = value.to_s
52
- if str =~ /^D(\d+)(?:\.(\d+))?/
53
- new(version: $1, revision: $2)
54
- else
55
- new(version: str)
56
- end
67
+ body = strip_prefix(value.to_s)
68
+ version, month, day, year, comma = split_date(body)
69
+ draft = new(version: version, month: month, day: day, year: year)
70
+ draft.comma_before_month = comma
71
+ draft
72
+ end
73
+
74
+ # Remove the leading "/D" (or a bare leading "D"/"/") that #to_s emits,
75
+ # leaving the version and any trailing date.
76
+ def self.strip_prefix(str)
77
+ return str[2..] if str.start_with?("/D")
78
+ return str[1..] if str.start_with?("/")
79
+ return str[1..] if str.start_with?("D") && str.length > 1
80
+
81
+ str
82
+ end
83
+ private_class_method :strip_prefix
84
+
85
+ # Split a trailing rendered date off the version, so the version stays
86
+ # clean (the SI/PSI renderer reads `draft_obj.version` directly). Returns
87
+ # [version, month, day, year, comma_before_month].
88
+ def self.split_date(body)
89
+ m = body.match(DATE_SUFFIX)
90
+ return [body, nil, nil, nil, false] unless m
91
+
92
+ version = body[0...m.begin(0)]
93
+ # The leading group is nil for the month-less " YEAR" form.
94
+ comma = m[1] == ", "
95
+ [version, m[2], m[3], m[4], comma]
57
96
  end
97
+ private_class_method :split_date
58
98
 
59
99
  # Convert month name to numeric value
60
100
  def convert_month(month_name)
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Pubid::Ieee::Identifier is the IEEE base class (a real Pubid::Identifier
4
- # subclass); IEEE builds its identifiers as instances of it directly. Its body,
5
- # `.parse`, and the Identifiers::Base back-compat alias live in
4
+ # subclass); IEEE builds its identifiers as instances of it directly. Its body and `.parse` live in
6
5
  # identifiers/base.rb. This file just ensures it is loaded.
7
- require_relative "identifiers/base"
@@ -7,17 +7,74 @@ module Pubid
7
7
  # IEEE's adoption of another organization's standard
8
8
  # Example: "IEEE Standard No 18-1968 (ANSI C55.1-1968)"
9
9
  # This means IEEE adopted ANSI's standard C55.1-1968 as their Std No 18-1968
10
- class AdoptedStandard < Base
11
- # IEEE's identifier for this adopted standard
12
- attribute :ieee_identifier, Base, polymorphic: true
10
+ class AdoptedStandard < Identifier
11
+ # IEEE's identifier for this adopted standard.
12
+ #
13
+ # Typed as the cross-flavor base `::Pubid::Identifier` (not the
14
+ # IEEE-only `Identifier`) so serialization never rejects a cross-flavor
15
+ # value. lutaml enforces the declared attribute type; `polymorphic:
16
+ # true` permits only *subclasses* of it, and a `Pubid::Ansi::…` /
17
+ # `Pubid::Iec::…` object is not a subclass of `Pubid::Ieee::Identifier`.
18
+ # The cross-flavor child carries its own `_type`, so `from_hash` routes
19
+ # it back via `TypeResolver`.
20
+ attribute :ieee_identifier, ::Pubid::Identifier, polymorphic: true
13
21
 
14
- # Original identifiers from ANSI/ISO/IEC/etc (array for multi-part adoptions)
15
- attribute :adopted_identifiers, Base, polymorphic: true,
22
+ # Original identifiers from ANSI/ASME/ISO/IEC/etc (array for multi-part
23
+ # adoptions). Cross-flavor by design: an `IEEE Std <n> (ANSI <m>)`
24
+ # adoption stores a `Pubid::Ansi::…` object here. See `ieee_identifier`
25
+ # above for why this must be `::Pubid::Identifier`, not IEEE-only.
26
+ attribute :adopted_identifiers, ::Pubid::Identifier, polymorphic: true,
16
27
  collection: true
17
28
 
18
29
  def publisher
19
30
  ieee_identifier&.publisher || "IEEE"
20
31
  end
32
+
33
+ # An adoption names one document by two designations and carries no
34
+ # number of its own, so `root.number` — the key relaton-index sorts and
35
+ # bsearches on — was "". Walk to the IEEE designation, which is the one
36
+ # IEEE files the document under, and which is also the source of truth
37
+ # for `publisher` above.
38
+ def root
39
+ ieee_identifier ? ieee_identifier.root : self
40
+ end
41
+
42
+ # The document number must reach the URN and the MR slug too, not only
43
+ # `#root` — the project rule that an identity-bearing marker reaches
44
+ # every identity surface. `UrnGenerator#code_component` and the base
45
+ # `mr_number_with_part` both read `code_obj`, which a wrapper does not
46
+ # have, so all 66 adoptions collapsed onto 5 URNs and 5 slugs — and
47
+ # `to_slug` is an output FILENAME, so that is 66 documents overwriting
48
+ # each other. Delegate to the IEEE designation, as `#root` does.
49
+ #
50
+ # `code_obj` is a plain attr_accessor on the base, not a lutaml
51
+ # attribute, so overriding it as a method is safe here.
52
+ def code_obj
53
+ ieee_identifier&.code_obj
54
+ end
55
+
56
+ # Likewise the year: without it "AIEE No 15-1944" and "AIEE No 15-1959"
57
+ # — two different documents — produced the same slug "aiee.std.15".
58
+ # This is the `mr_year` HOOK, not a `#year` method: `year` is a lutaml
59
+ # attribute on the base, and a method of that name would shadow the
60
+ # generated accessor (the CLAUDE.md landmine).
61
+ def mr_year
62
+ (year || ieee_identifier&.year)&.to_s
63
+ end
64
+
65
+ # `publisher` is *derived* from `ieee_identifier` (the serialized source
66
+ # of truth), so it must not be serialized. Emitting it breaks the
67
+ # canonical round-trip: the derived value is default-omitted on the parse
68
+ # path (publisher unset), but re-emitted after from_hash materializes the
69
+ # attribute default (the override then returns ieee_identifier.publisher,
70
+ # e.g. "AIEE", not the "IEEE" default). Drop it — ieee_identifier rebuilds
71
+ # it. (AdoptedStandard is always a top-level wrapper, never nested, so a
72
+ # to_hash-level drop is sufficient — same as JointDevelopment.)
73
+ def to_hash(*args)
74
+ hash = super
75
+ hash.delete("publisher") if hash.is_a?(::Hash)
76
+ hash
77
+ end
21
78
  end
22
79
  end
23
80
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pubid
4
+ module Ieee
5
+ module Identifiers
6
+ # Amendment identifier for IEEE standards
7
+ # Represents additions/changes to a published standard
8
+ # Example: IEEE Std 802.3-2018/Amd 4-2020
9
+ class Amendment < SupplementIdentifier
10
+ # The amendment's own ordinal (`number`) and `year` — uniform names,
11
+ # not `amd_number`/`amd_year`. `number` is a plain string redefining the
12
+ # inherited Components::Code (leaf-safe, like the standards); `year` is
13
+ # the inherited base :string. `root.number` still returns the *base*
14
+ # document number, so the index key is unaffected. Mirrors Corrigendum.
15
+ attribute :number, :string
16
+ # `year` inherited from the base.
17
+
18
+ # TYPED_STAGES for amendment
19
+ # Amendment uses "Amd" abbreviation
20
+ TYPED_STAGES = [
21
+ Components::TypedStage.new(
22
+ abbr: ["Amd"],
23
+ type_code: "amendment",
24
+ stage_code: "published",
25
+ ),
26
+ ].freeze
27
+
28
+ # MR supplement suffix: `amd.{number}.{year}` (e.g. "/amd.4.2020").
29
+ # The MrString renderer recurses into `base` and appends this so the
30
+ # full IEEE amendment round-trips losslessly (mirrors Corrigendum).
31
+ def mr_supplement_suffix
32
+ segments = ["amd"]
33
+ segments << number.to_s if number
34
+ segments << year.to_s if year
35
+ segments.join(".")
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -7,7 +7,7 @@ module Pubid
7
7
  end
8
8
 
9
9
  # Base class for all IEEE identifiers. Canonical name
10
- # Pubid::Ieee::Identifier (Identifiers::Base is a back-compat alias). IEEE
10
+ # Pubid::Ieee::Identifier. IEEE
11
11
  # builds its identifiers as instances of this class directly.
12
12
  class Identifier < ::Pubid::Identifier
13
13
  # Generate URN for this identifier
@@ -15,12 +15,27 @@ module Pubid
15
15
  # @return [String] URN representation
16
16
 
17
17
  attribute :publisher, :string, default: -> { "IEEE" }
18
- attribute :copublisher, :string, collection: true # IEC, ISO, ANSI, etc.
19
- attribute :code, :string # Will store code as object in initialize
18
+ # IEC, ISO, ANSI, etc. `initialize_empty` makes from_hash give `[]`, the
19
+ # value a parsed identifier holds, so the two paths stay `==`.
20
+ attribute :copublisher, :string, collection: true, initialize_empty: true
21
+ # NB: there is deliberately NO `attribute :code`. IEEE's document code is
22
+ # serialized as the split index columns (number/prefix/parts/separator) on
23
+ # concrete leaf types via the CodeNumber mixin; `code`/`code_obj` remain a
24
+ # runtime-only representation (built in #initialize, read by the renderer).
25
+ # This holds for every IEEE type, including the historical AIEE/IRE and
26
+ # the NESC family: all three used to descend from
27
+ # Lutaml::Model::Serializable with their own `attribute :code`, and were
28
+ # reparented onto this base (with the CodeNumber mixin on their leaves).
20
29
  attribute :year, :string
21
30
  attribute :type, :string, default: -> { "Std" } # Std, Draft Std
22
31
  attribute :draft_status, :string # Unapproved, Approved, Active Unapproved
23
32
  attribute :draft, :string # Will store draft as object
33
+ # Numbered/lettered revision id, e.g. "2" for "P802.16Rev2", "i" for the
34
+ # relaton suffix "/R-i". IEEE's native inline "Rev<n>" and relaton's
35
+ # synthetic "/R-<x>" both feed this one attribute; rendered inline as
36
+ # "Rev<id>". `::Pubid::Identifier` has no `revision` attribute, so this is
37
+ # free of the number/stage multi-flavor collision landmine.
38
+ attribute :revision, :string
24
39
  attribute :edition, :string # Edition 1.0
25
40
  attribute :month, :string
26
41
  attribute :day, :string
@@ -46,8 +61,6 @@ module Pubid
46
61
  attribute :interpretation, :boolean, default: -> {
47
62
  false
48
63
  } # /INT notation
49
- attribute :conf_number, :string # Conformance document number
50
- attribute :conf_year, :string # Conformance document year
51
64
  attribute :ashrae_number, :string # ASHRAE Guideline number
52
65
  attribute :ashrae_year, :string # ASHRAE Guideline year
53
66
  attribute :crossref, :string # IEEE cross-reference (e.g., /C62.22.1-1996)
@@ -56,7 +69,24 @@ module Pubid
56
69
  # Store actual component objects
57
70
  attr_accessor :code_obj, :draft_obj
58
71
 
59
- def initialize(**args)
72
+ # Accept either keyword args (`new(number: "802")`, the normal path) or a
73
+ # single positional attribute hash (`new({number: "802"})`). The latter is
74
+ # what the base `Pubid::Identifier#exclude`/matching machinery uses when it
75
+ # rebuilds via `self.class.new(attrs)`; without it, `exclude`/`matches?`
76
+ # raised ArgumentError for every IEEE identifier.
77
+ # Constructor parameters that are not lutaml attributes: `code` is
78
+ # runtime-only (kept as `code_obj`), and the draft forms are turned into
79
+ # component objects below.
80
+ def self.extra_init_keys
81
+ %i[code draft draft_obj]
82
+ end
83
+
84
+ def initialize(args = {}, **kwargs)
85
+ args = args.merge(kwargs) unless kwargs.empty?
86
+ # This override calls `super()` with no attributes and assigns through
87
+ # setters instead, so it bypasses the base constructor's coercion and
88
+ # unknown-key check. Apply them here to keep the cross-flavor contract.
89
+ args = self.class.normalize_init_attributes(args)
60
90
  super()
61
91
 
62
92
  # Handle typed_stage if provided
@@ -64,13 +94,15 @@ module Pubid
64
94
  self.typed_stage = args[:typed_stage]
65
95
  end
66
96
 
67
- # Handle code as component object
97
+ # Handle code as component object. `code` is runtime-only (not a lutaml
98
+ # attribute) — we keep the parsed Components::Code in code_obj; every
99
+ # leaf serializes the split index columns (number/prefix/parts/separator)
100
+ # via the CodeNumber mixin instead of a `code` string. The renderer reads
101
+ # code_obj (base #code returns it).
68
102
  if args[:code].is_a?(String)
69
103
  self.code_obj = Components::Code.parse(args[:code])
70
- self.code = args[:code]
71
104
  elsif args[:code]
72
105
  self.code_obj = args[:code]
73
- self.code = args[:code].to_s
74
106
  end
75
107
 
76
108
  # Handle draft as component object
@@ -85,13 +117,26 @@ module Pubid
85
117
  self.draft = args[:draft].to_s
86
118
  end
87
119
 
88
- # Set other attributes
120
+ # Set other attributes.
121
+ #
122
+ # A key the class accepts but does not declare as a lutaml attribute
123
+ # (see `extra_init_keys`) is assigned through its plain accessor.
124
+ # Without that, `CsaDualPublished#csa_identifier` — an `attr_accessor`
125
+ # by design, "stored as-is (not a Lutaml model type)" — was never
126
+ # assigned at all, and `renderer.rb` read nil from it:
127
+ # `IEEE Std 844.1-2017/CSA C22.2 No. 293.1-17` rendered back as
128
+ # `IEEE Std 844.1-2017/CSA`, losing the CSA designation entirely.
89
129
  attrs = self.class.attributes
130
+ extra = self.class.extra_init_keys
90
131
  args.each do |key, value|
91
132
  next if %i[code draft draft_obj typed_stage].include?(key)
92
133
 
93
134
  setter = :"#{key}="
94
- public_send(setter, value) if attrs.key?(key)
135
+ if attrs.key?(key)
136
+ public_send(setter, value)
137
+ elsif extra.include?(key) && respond_to?(setter)
138
+ public_send(setter, value)
139
+ end
95
140
  end
96
141
  end
97
142
 
@@ -127,14 +172,75 @@ module Pubid
127
172
  draft_obj.numeric_month
128
173
  end
129
174
 
175
+ # IEEE stores its publication date as separate `year`/`month`/`day`
176
+ # :string attributes, not a Components::Date, so the base #exclude's
177
+ # `:year`->`:date` remap can't reach them (it would nil an unused `date`
178
+ # component and leave the strings). After the base rebuild, nil the whole
179
+ # date cluster when `:year`/`:date` is excluded, so a date-less reference
180
+ # matches every date in the bucket (relaton partial-ref matching). NB: the
181
+ # base #exclude also previously raised for every IEEE id (positional
182
+ # `self.class.new(attrs)` vs the keyword initialize) — fixed by
183
+ # Identifier#initialize accepting a positional hash.
184
+ def exclude(*args)
185
+ result = super
186
+ if args.intersect?(%i[year date])
187
+ %i[year month day].each do |attr|
188
+ result.public_send("#{attr}=", nil) if result.respond_to?("#{attr}=")
189
+ end
190
+ end
191
+ result
192
+ end
193
+
194
+ # Compact the serialized hash (recursively, so nested Corrigendum bases
195
+ # are compacted too) after the normal serialize + canonicalize: collapse
196
+ # `typed_stage` to a scalar `stage` and strip the `/` off `draft`. See
197
+ # Compaction for why this is a hash transform, not lutaml attributes.
198
+ def to_hash(*args)
199
+ hash = super
200
+ Compaction.collapse(hash) if hash.is_a?(::Hash)
201
+ hash
202
+ end
203
+
204
+ # Inverse of the to_hash compaction: expand a scalar `stage` back into the
205
+ # `typed_stage` sub-hash (on a deep copy, recursively) before lutaml
206
+ # deserializes, so nested bases rebuild their component too. `draft` needs
207
+ # no expansion (Draft.parse accepts the slashless form).
208
+ def self.from_hash(data, options = {})
209
+ data = Compaction.expand(Compaction.deep_dup(data)) if data.is_a?(::Hash)
210
+ super
211
+ end
212
+
213
+ # Register the identifier classes the automatic `Identifiers::*` scan
214
+ # cannot see, so `Pubid::Ieee::Identifier.from_hash` can route their rows
215
+ # back. The scan only looks at classes declared *directly* under
216
+ # `Identifiers`, which misses both AIEE and IRE (they live under their own
217
+ # `Aiee`/`Ire` namespaces) and the whole NESC family (nested one level
218
+ # deeper, under `Identifiers::Nesc`).
219
+ def self.additional_identifier_classes
220
+ [
221
+ Aiee::Identifier,
222
+ Ire::Identifier,
223
+ Identifiers::Nesc::Draft,
224
+ Identifiers::Nesc::Edition,
225
+ Identifiers::Nesc::Handbook,
226
+ Identifiers::Nesc::Redline,
227
+ Identifiers::Nesc::Standard,
228
+ ]
229
+ end
230
+
130
231
  # Parse IEEE identifier string.
131
232
  #
132
233
  # PreParser owns all regex/dispatch logic; this method is a thin
133
234
  # orchestrator that consumes a PreParser::Result and routes to the
134
235
  # correct builder.
135
236
  def self.parse(input)
237
+ unless input.is_a?(String)
238
+ raise Pubid::Errors::InvalidInputError,
239
+ Pubid::INPUT_NOT_A_STRING_MESSAGE
240
+ end
241
+
136
242
  if input.length > Pubid::MAX_INPUT_LENGTH
137
- raise ArgumentError, Pubid::INPUT_TOO_LONG_MESSAGE
243
+ raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
138
244
  end
139
245
 
140
246
  result = PreParser.preprocess(input)
@@ -200,9 +306,15 @@ module Pubid
200
306
  def self.build_aiee_asa_adoption(parts)
201
307
  aiee_id = parse_single(parts[0])
202
308
  asa_id = parse_single(parts[1])
309
+ # `adopted_identifierS` — AdoptedStandard declares a collection. The
310
+ # singular spelling was not an attribute, so lutaml dropped it without
311
+ # a word and the ASA half of these 13 identifiers was never stored:
312
+ # `AIEE No 18-1934 (ASA C55 1934)` rendered back as `AIEE No 18-1934`.
313
+ # The unknown-key contract turned that silent loss into a raise, which
314
+ # is how it was found.
203
315
  Identifiers::AdoptedStandard.new(
204
316
  ieee_identifier: aiee_id,
205
- adopted_identifier: asa_id,
317
+ adopted_identifiers: [asa_id],
206
318
  )
207
319
  end
208
320
  private_class_method :build_aiee_asa_adoption
@@ -245,12 +357,30 @@ module Pubid
245
357
  builder.original_input = input
246
358
  builder.build(parsed)
247
359
  end
248
- end
249
360
 
250
- module Identifiers
251
- # Backward-compatible alias: IEEE's base class used to be
252
- # Pubid::Ieee::Identifiers::Base. It is now Pubid::Ieee::Identifier.
253
- Base = Pubid::Ieee::Identifier
361
+ # IEEE stores identity in `code` (prefix/number/parts) rather than the
362
+ # generic `number`, has its own `type` string ("Std", "Draft Std"), and
363
+ # carries `year` as a bare string — none of which the generic MrString
364
+ # renderer knows about. Override the lossless MR template directly so
365
+ # every IEEE identifier round-trips (issue #142). Supplements append
366
+ # `_{type}.{number}.{year}` recursively via mr_supplement_suffix.
367
+ # Lowercased to match the all-lowercase MR convention.
368
+ def mr_publisher
369
+ publisher&.to_s&.downcase
370
+ end
371
+
372
+ def mr_type
373
+ type&.downcase
374
+ end
375
+
376
+ def mr_number_with_part
377
+ code_obj&.to_s&.downcase
378
+ end
379
+
380
+ def mr_year
381
+ year&.to_s
382
+ end
254
383
  end
384
+
255
385
  end
256
386
  end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pubid
4
+ module Ieee
5
+ module Identifiers
6
+ # Mixin for concrete IEEE leaf identifiers that are their own `#root` and
7
+ # carry a document code. It splits the code into separate, index-friendly
8
+ # scalar attributes and, crucially, **replaces** the serialized `code`
9
+ # string with them:
10
+ #
11
+ # number :string bare-digit narrowing key ("802", "37")
12
+ # prefix :string letter series designator ("C"), if any
13
+ # parts :string collection dotted/dashed parts (["16", "1"])
14
+ # separator :string "." or "-", to render the parts back
15
+ #
16
+ # relaton's index-v2 keys its binary search on `id.root.number.to_s` and
17
+ # narrows a part-less reference (`IEEE 802`) to the whole bucket of parts
18
+ # (`802.11`, `802.16`, `802.16.1`), then matches `parts` — so `number`
19
+ # (part-less) and `parts` MUST be separate columns. The full `code` string
20
+ # is not serialized at all; `code_obj` (the runtime Components::Code the
21
+ # renderer/urn_generator read) is rebuilt losslessly from the four fields.
22
+ #
23
+ # Why a leaf mixin and not the base: IEEE keeps identity in `code`, so the
24
+ # base class's inherited generic `number` (a `Components::Code`) is nil.
25
+ # Redefining `attribute :number, :string` on the shared,
26
+ # **directly-instantiated** `Ieee::Identifier` base resolves against the
27
+ # parent's `Components::Code` **nondeterministically under multi-flavor
28
+ # load** (it passes IEEE-only but flips once every flavor is required,
29
+ # e.g. the full suite). Applying the split only on leaf classes that are
30
+ # never a superclass avoids that merge ambiguity — the same pattern
31
+ # CIE/IHO/IETF use. Never define a `#number` *method*: it collides with
32
+ # lutaml's generated accessor and corrupts attribute resolution.
33
+ module CodeNumber
34
+ def self.included(base)
35
+ base.attribute :number, :string
36
+ base.attribute :prefix, :string
37
+ base.attribute :parts, :string, collection: true, default: -> { [] }
38
+ base.attribute :separator, :string
39
+ end
40
+
41
+ # Accept keyword args or a single positional hash (the latter is what the
42
+ # base #exclude/matching rebuild passes) — see Identifier#initialize.
43
+ def initialize(args = {}, **kwargs)
44
+ super
45
+ sync_split_from_code_obj
46
+ end
47
+
48
+ # On the parse path `code_obj` is already built (base #initialize parses
49
+ # it from the `code:` arg); hoist its fields onto the split attributes
50
+ # so they serialize.
51
+ def sync_split_from_code_obj
52
+ co = code_obj
53
+ return unless co
54
+
55
+ self.number = co.number
56
+ self.prefix = co.prefix
57
+ self.parts = co.parts
58
+ self.separator = co.original_separator
59
+ end
60
+
61
+ # After from_hash there is no `code` string and no cached object, so
62
+ # rebuild code_obj from the split fields. On the parse path base
63
+ # #initialize already set @code_obj, so the `||=` returns it unchanged.
64
+ def code_obj
65
+ @code_obj ||= rebuild_code_obj_from_split
66
+ end
67
+
68
+ def rebuild_code_obj_from_split
69
+ return nil if number.nil? || number.to_s.empty?
70
+
71
+ nonempty_parts = parts.nil? || parts.empty? ? nil : parts
72
+ Components::Code.new(
73
+ prefix: prefix,
74
+ number: number,
75
+ parts: nonempty_parts,
76
+ original_separator: separator,
77
+ )
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -7,8 +7,12 @@ module Pubid
7
7
  # Represents conformance test documents
8
8
  # Example: IEEE Std 802.16/Conformance01-2003
9
9
  class ConformanceIdentifier < SupplementIdentifier
10
- attribute :conf_number, :string
11
- attribute :conf_year, :string
10
+ # Uniform `number` (the conformance ordinal) + inherited `year`, not
11
+ # `conf_number`/`conf_year`. `number` is a plain string redefining the
12
+ # inherited Components::Code (leaf-safe). `root.number` still returns the
13
+ # base document number.
14
+ attribute :number, :string
15
+ # `year` inherited from the base.
12
16
 
13
17
  # TYPED_STAGES for conformance
14
18
  # Conformance uses "Conformance" abbreviation