pubid 2.0.0.pre.alpha.8 → 2.0.0.pre.alpha.9

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 (484) hide show
  1. checksums.yaml +4 -4
  2. data/data/bipm/update_codes.yaml +9 -0
  3. data/data/iec/update_codes.yaml +3 -0
  4. data/data/ieee/update_codes.yaml +107 -1
  5. data/data/nist/series.yaml +155 -0
  6. data/data/nist/update_codes.yaml +8 -0
  7. data/lib/pubid/adobe/builder.rb +52 -0
  8. data/lib/pubid/adobe/identifier.rb +49 -0
  9. data/lib/pubid/adobe/identifiers/publication.rb +31 -0
  10. data/lib/pubid/adobe/identifiers/tech_note.rb +32 -0
  11. data/lib/pubid/adobe/identifiers.rb +10 -0
  12. data/lib/pubid/adobe/parser.rb +128 -0
  13. data/lib/pubid/adobe/renderer.rb +39 -0
  14. data/lib/pubid/adobe/urn_generator.rb +42 -0
  15. data/lib/pubid/adobe/urn_parser.rb +69 -0
  16. data/lib/pubid/adobe.rb +60 -0
  17. data/lib/pubid/amca/builder.rb +2 -2
  18. data/lib/pubid/amca/identifier.rb +1 -3
  19. data/lib/pubid/amca/identifiers/base.rb +1 -6
  20. data/lib/pubid/amca/identifiers/interpretation.rb +1 -1
  21. data/lib/pubid/amca/identifiers/publication.rb +1 -1
  22. data/lib/pubid/amca/identifiers/standard.rb +1 -1
  23. data/lib/pubid/amca/identifiers.rb +0 -1
  24. data/lib/pubid/amca/single_identifier.rb +1 -1
  25. data/lib/pubid/amca.rb +3 -3
  26. data/lib/pubid/ashrae/builder.rb +22 -22
  27. data/lib/pubid/ashrae/identifier.rb +3 -4
  28. data/lib/pubid/ashrae/identifiers/addenda_package.rb +1 -1
  29. data/lib/pubid/ashrae/identifiers/addendum.rb +1 -1
  30. data/lib/pubid/ashrae/identifiers/base.rb +14 -8
  31. data/lib/pubid/ashrae/identifiers/combined_addenda.rb +1 -1
  32. data/lib/pubid/ashrae/identifiers.rb +0 -1
  33. data/lib/pubid/ashrae/parser.rb +14 -9
  34. data/lib/pubid/ashrae/renderer.rb +19 -19
  35. data/lib/pubid/ashrae/single_identifier.rb +1 -1
  36. data/lib/pubid/ashrae/supplement_identifier.rb +6 -6
  37. data/lib/pubid/ashrae.rb +3 -3
  38. data/lib/pubid/bipm/builder.rb +107 -0
  39. data/lib/pubid/bipm/identifier.rb +160 -0
  40. data/lib/pubid/bipm/identifiers/committee_document.rb +24 -0
  41. data/lib/pubid/bipm/identifiers/guide.rb +24 -0
  42. data/lib/pubid/bipm/identifiers/meeting.rb +41 -0
  43. data/lib/pubid/bipm/identifiers/mep.rb +21 -0
  44. data/lib/pubid/bipm/identifiers/metrologia_article.rb +24 -0
  45. data/lib/pubid/bipm/identifiers/si_brochure.rb +20 -0
  46. data/lib/pubid/bipm/identifiers.rb +15 -0
  47. data/lib/pubid/bipm/parser.rb +184 -0
  48. data/lib/pubid/bipm/renderer.rb +110 -0
  49. data/lib/pubid/bipm/urn_generator.rb +58 -0
  50. data/lib/pubid/bipm/urn_parser.rb +52 -0
  51. data/lib/pubid/bipm.rb +86 -0
  52. data/lib/pubid/bsi/builder.rb +14 -14
  53. data/lib/pubid/bsi/identifier.rb +0 -1
  54. data/lib/pubid/bsi/identifiers/addendum_document.rb +2 -2
  55. data/lib/pubid/bsi/identifiers/amendment.rb +4 -4
  56. data/lib/pubid/bsi/identifiers/consolidated_identifier.rb +6 -0
  57. data/lib/pubid/bsi/identifiers/corrigendum.rb +4 -4
  58. data/lib/pubid/bsi/identifiers/expert_commentary.rb +5 -5
  59. data/lib/pubid/bsi/identifiers/supplement_document.rb +2 -2
  60. data/lib/pubid/bsi/identifiers/value_added_publication.rb +6 -6
  61. data/lib/pubid/bsi/renderer.rb +8 -8
  62. data/lib/pubid/bsi/single_identifier.rb +48 -0
  63. data/lib/pubid/builder/base.rb +5 -3
  64. data/lib/pubid/bundled_identifier.rb +3 -3
  65. data/lib/pubid/calconnect/builder.rb +38 -0
  66. data/lib/pubid/calconnect/identifier.rb +117 -0
  67. data/lib/pubid/calconnect/identifiers/standard.rb +29 -0
  68. data/lib/pubid/calconnect/identifiers.rb +9 -0
  69. data/lib/pubid/calconnect/parser.rb +54 -0
  70. data/lib/pubid/calconnect/renderer.rb +36 -0
  71. data/lib/pubid/calconnect/urn_generator.rb +33 -0
  72. data/lib/pubid/calconnect/urn_parser.rb +34 -0
  73. data/lib/pubid/calconnect.rb +77 -0
  74. data/lib/pubid/ccsds/builder.rb +1 -1
  75. data/lib/pubid/ccsds/identifier.rb +1 -1
  76. data/lib/pubid/ccsds/identifiers/corrigendum.rb +2 -2
  77. data/lib/pubid/ccsds/supplement_identifier.rb +6 -6
  78. data/lib/pubid/ccsds/urn_generator.rb +3 -3
  79. data/lib/pubid/cen_cenelec/builder.rb +46 -12
  80. data/lib/pubid/cen_cenelec/identifiers/amendment.rb +2 -2
  81. data/lib/pubid/cen_cenelec/identifiers/consolidated_identifier.rb +6 -0
  82. data/lib/pubid/cen_cenelec/identifiers/corrigendum.rb +2 -2
  83. data/lib/pubid/cen_cenelec/identifiers/european_norm.rb +60 -3
  84. data/lib/pubid/cen_cenelec/identifiers/fragment.rb +2 -2
  85. data/lib/pubid/cen_cenelec/renderer.rb +5 -5
  86. data/lib/pubid/cen_cenelec/supplement_identifier.rb +5 -5
  87. data/lib/pubid/cen_cenelec/urn_generator.rb +1 -1
  88. data/lib/pubid/cen_cenelec.rb +76 -72
  89. data/lib/pubid/cie/builder.rb +139 -24
  90. data/lib/pubid/cie/identifier.rb +7 -3
  91. data/lib/pubid/cie/identifiers/bundle.rb +32 -4
  92. data/lib/pubid/cie/identifiers/code_attributes.rb +52 -0
  93. data/lib/pubid/cie/identifiers/conference.rb +9 -4
  94. data/lib/pubid/cie/identifiers/corrigendum.rb +17 -23
  95. data/lib/pubid/cie/identifiers/dual_published.rb +4 -4
  96. data/lib/pubid/cie/identifiers/identical.rb +7 -7
  97. data/lib/pubid/cie/identifiers/joint_published.rb +8 -8
  98. data/lib/pubid/cie/identifiers/proceedings.rb +39 -0
  99. data/lib/pubid/cie/identifiers/standard.rb +33 -7
  100. data/lib/pubid/cie/identifiers/supplement.rb +24 -22
  101. data/lib/pubid/cie/identifiers/tutorial_bundle.rb +6 -2
  102. data/lib/pubid/cie/identifiers.rb +2 -0
  103. data/lib/pubid/cie/parser.rb +54 -5
  104. data/lib/pubid/cie/single_identifier.rb +40 -1
  105. data/lib/pubid/cie/supplement_identifier.rb +29 -16
  106. data/lib/pubid/cie/urn_generator.rb +11 -8
  107. data/lib/pubid/cie.rb +0 -1
  108. data/lib/pubid/components/date.rb +21 -5
  109. data/lib/pubid/csa/builder.rb +1 -1
  110. data/lib/pubid/csa/composite_identifier.rb +17 -1
  111. data/lib/pubid/csa/identifier.rb +48 -3
  112. data/lib/pubid/csa/identifiers/bundled.rb +6 -0
  113. data/lib/pubid/csa/identifiers/combined.rb +6 -0
  114. data/lib/pubid/csa/identifiers/package.rb +2 -2
  115. data/lib/pubid/csa/parser.rb +6 -6
  116. data/lib/pubid/csa/wrapper_identifier.rb +17 -0
  117. data/lib/pubid/doi/builder.rb +18 -0
  118. data/lib/pubid/doi/identifier.rb +46 -0
  119. data/lib/pubid/doi/identifiers/resource.rb +30 -0
  120. data/lib/pubid/doi/identifiers.rb +9 -0
  121. data/lib/pubid/doi/parser.rb +52 -0
  122. data/lib/pubid/doi/renderer.rb +13 -0
  123. data/lib/pubid/doi.rb +55 -0
  124. data/lib/pubid/easc/builder.rb +55 -0
  125. data/lib/pubid/easc/identifier.rb +61 -0
  126. data/lib/pubid/easc/identifiers/pmg.rb +20 -0
  127. data/lib/pubid/easc/identifiers/rmg.rb +19 -0
  128. data/lib/pubid/easc/identifiers.rb +10 -0
  129. data/lib/pubid/easc/parser.rb +80 -0
  130. data/lib/pubid/easc/renderer.rb +48 -0
  131. data/lib/pubid/easc/urn_generator.rb +46 -0
  132. data/lib/pubid/easc/urn_parser.rb +57 -0
  133. data/lib/pubid/easc.rb +52 -0
  134. data/lib/pubid/ecma/builder.rb +31 -0
  135. data/lib/pubid/ecma/identifier.rb +76 -0
  136. data/lib/pubid/ecma/identifiers/memento.rb +30 -0
  137. data/lib/pubid/ecma/identifiers/standard.rb +31 -0
  138. data/lib/pubid/ecma/identifiers/technical_report.rb +31 -0
  139. data/lib/pubid/ecma/identifiers.rb +11 -0
  140. data/lib/pubid/ecma/parser.rb +41 -0
  141. data/lib/pubid/ecma/renderer.rb +46 -0
  142. data/lib/pubid/ecma/urn_generator.rb +25 -0
  143. data/lib/pubid/ecma/urn_parser.rb +35 -0
  144. data/lib/pubid/ecma.rb +67 -0
  145. data/lib/pubid/etsi/builder.rb +7 -3
  146. data/lib/pubid/etsi/identifier.rb +1 -3
  147. data/lib/pubid/etsi/identifiers/base.rb +28 -10
  148. data/lib/pubid/etsi/identifiers/etsi_standard.rb +128 -4
  149. data/lib/pubid/etsi/identifiers/supplement_identifier.rb +35 -3
  150. data/lib/pubid/etsi/identifiers.rb +0 -1
  151. data/lib/pubid/etsi/parser.rb +8 -2
  152. data/lib/pubid/etsi/renderer.rb +8 -2
  153. data/lib/pubid/etsi.rb +5 -5
  154. data/lib/pubid/export/exporter.rb +2 -1
  155. data/lib/pubid/export/flavor_exporter.rb +18 -3
  156. data/lib/pubid/gb/builder.rb +45 -0
  157. data/lib/pubid/gb/identifier.rb +64 -0
  158. data/lib/pubid/gb/identifiers/standard.rb +34 -0
  159. data/lib/pubid/gb/identifiers.rb +9 -0
  160. data/lib/pubid/gb/parser.rb +63 -0
  161. data/lib/pubid/gb/renderer.rb +35 -0
  162. data/lib/pubid/gb.rb +87 -0
  163. data/lib/pubid/gost/builder.rb +148 -0
  164. data/lib/pubid/gost/identifier.rb +49 -0
  165. data/lib/pubid/gost/identifiers/foreign_reference.rb +25 -0
  166. data/lib/pubid/gost/identifiers/harmonized.rb +39 -0
  167. data/lib/pubid/gost/identifiers/identical_adoption.rb +39 -0
  168. data/lib/pubid/gost/identifiers/interstate_standard.rb +20 -0
  169. data/lib/pubid/gost/identifiers/national_standard.rb +19 -0
  170. data/lib/pubid/gost/identifiers.rb +13 -0
  171. data/lib/pubid/gost/parser.rb +97 -0
  172. data/lib/pubid/gost/renderer.rb +48 -0
  173. data/lib/pubid/gost/urn_generator.rb +46 -0
  174. data/lib/pubid/gost/urn_parser.rb +52 -0
  175. data/lib/pubid/gost.rb +49 -0
  176. data/lib/pubid/iala/builder.rb +22 -27
  177. data/lib/pubid/iala/identifier.rb +27 -2
  178. data/lib/pubid/iala/identifiers/advice.rb +1 -1
  179. data/lib/pubid/iala/identifiers/annex.rb +10 -10
  180. data/lib/pubid/iala/identifiers/general_assembly.rb +5 -2
  181. data/lib/pubid/iala/identifiers/guideline.rb +3 -1
  182. data/lib/pubid/iala/identifiers/letter.rb +1 -1
  183. data/lib/pubid/iala/identifiers/manual.rb +3 -1
  184. data/lib/pubid/iala/identifiers/model_course.rb +3 -1
  185. data/lib/pubid/iala/identifiers/recommendation.rb +3 -1
  186. data/lib/pubid/iala/identifiers/report.rb +1 -1
  187. data/lib/pubid/iala/identifiers/resolution.rb +1 -1
  188. data/lib/pubid/iala/identifiers/standard.rb +3 -1
  189. data/lib/pubid/iala/identifiers.rb +0 -1
  190. data/lib/pubid/iala/renderer.rb +1 -1
  191. data/lib/pubid/iala/urn_generator.rb +1 -1
  192. data/lib/pubid/iala/urn_parser.rb +19 -2
  193. data/lib/pubid/iala.rb +4 -4
  194. data/lib/pubid/iana/builder.rb +20 -0
  195. data/lib/pubid/iana/identifier.rb +72 -0
  196. data/lib/pubid/iana/identifiers/registry.rb +33 -0
  197. data/lib/pubid/iana/identifiers.rb +9 -0
  198. data/lib/pubid/iana/parser.rb +38 -0
  199. data/lib/pubid/iana/renderer.rb +29 -0
  200. data/lib/pubid/iana/urn_generator.rb +15 -0
  201. data/lib/pubid/iana/urn_parser.rb +24 -0
  202. data/lib/pubid/iana.rb +69 -0
  203. data/lib/pubid/identifier.rb +210 -23
  204. data/lib/pubid/idf/builder.rb +1 -1
  205. data/lib/pubid/idf/parser.rb +2 -2
  206. data/lib/pubid/idf/renderer.rb +1 -1
  207. data/lib/pubid/idf/supplement_identifier.rb +3 -3
  208. data/lib/pubid/iec/builder.rb +35 -25
  209. data/lib/pubid/iec/identifier.rb +47 -2
  210. data/lib/pubid/iec/identifiers/base.rb +2 -2
  211. data/lib/pubid/iec/identifiers/consolidated_identifier.rb +6 -0
  212. data/lib/pubid/iec/identifiers/fragment_identifier.rb +11 -11
  213. data/lib/pubid/iec/identifiers/international_standard.rb +8 -5
  214. data/lib/pubid/iec/identifiers/sheet_identifier.rb +12 -12
  215. data/lib/pubid/iec/identifiers/technical_group.rb +30 -0
  216. data/lib/pubid/iec/identifiers/technical_report.rb +1 -1
  217. data/lib/pubid/iec/identifiers/technical_specification.rb +1 -1
  218. data/lib/pubid/iec/identifiers/vap_identifier.rb +12 -12
  219. data/lib/pubid/iec/identifiers.rb +1 -0
  220. data/lib/pubid/iec/parser.rb +51 -10
  221. data/lib/pubid/iec/renderer.rb +18 -8
  222. data/lib/pubid/iec/single_identifier.rb +1 -1
  223. data/lib/pubid/iec/supplement_identifier.rb +32 -19
  224. data/lib/pubid/iec.rb +1 -6
  225. data/lib/pubid/ieee/aiee/builder.rb +17 -3
  226. data/lib/pubid/ieee/aiee/identifier.rb +45 -61
  227. data/lib/pubid/ieee/builder.rb +341 -68
  228. data/lib/pubid/ieee/compaction.rb +121 -0
  229. data/lib/pubid/ieee/components/draft.rb +47 -7
  230. data/lib/pubid/ieee/identifier.rb +1 -3
  231. data/lib/pubid/ieee/identifiers/adopted_standard.rb +30 -5
  232. data/lib/pubid/ieee/identifiers/amendment.rb +40 -0
  233. data/lib/pubid/ieee/identifiers/base.rb +106 -13
  234. data/lib/pubid/ieee/identifiers/code_number.rb +82 -0
  235. data/lib/pubid/ieee/identifiers/conformance_identifier.rb +6 -2
  236. data/lib/pubid/ieee/identifiers/corrigendum.rb +17 -2
  237. data/lib/pubid/ieee/identifiers/csa_dual_published.rb +3 -3
  238. data/lib/pubid/ieee/identifiers/dual_identifier.rb +3 -3
  239. data/lib/pubid/ieee/identifiers/dual_published.rb +3 -3
  240. data/lib/pubid/ieee/identifiers/iec_ieee_copublished.rb +102 -5
  241. data/lib/pubid/ieee/identifiers/interpretation_identifier.rb +2 -1
  242. data/lib/pubid/ieee/identifiers/joint_development.rb +56 -11
  243. data/lib/pubid/ieee/identifiers/multi_numbered_identifier.rb +1 -1
  244. data/lib/pubid/ieee/identifiers/nesc/base.rb +90 -19
  245. data/lib/pubid/ieee/identifiers/nesc/draft.rb +20 -5
  246. data/lib/pubid/ieee/identifiers/nesc/edition.rb +32 -0
  247. data/lib/pubid/ieee/identifiers/nesc/handbook.rb +16 -5
  248. data/lib/pubid/ieee/identifiers/nesc/redline.rb +11 -2
  249. data/lib/pubid/ieee/identifiers/nesc/standard.rb +18 -3
  250. data/lib/pubid/ieee/identifiers/nesc.rb +1 -0
  251. data/lib/pubid/ieee/identifiers/parenthetical_identifier.rb +3 -3
  252. data/lib/pubid/ieee/identifiers/project_draft_identifier.rb +14 -6
  253. data/lib/pubid/ieee/identifiers/redlined_standard.rb +4 -4
  254. data/lib/pubid/ieee/identifiers/si_standard.rb +4 -1
  255. data/lib/pubid/ieee/identifiers/standard.rb +4 -1
  256. data/lib/pubid/ieee/identifiers/supplement_identifier.rb +26 -7
  257. data/lib/pubid/ieee/identifiers.rb +2 -1
  258. data/lib/pubid/ieee/ire/builder.rb +7 -6
  259. data/lib/pubid/ieee/ire/identifier.rb +48 -33
  260. data/lib/pubid/ieee/nesc/builder.rb +33 -20
  261. data/lib/pubid/ieee/nesc/parser.rb +15 -14
  262. data/lib/pubid/ieee/parser.rb +461 -43
  263. data/lib/pubid/ieee/pre_parser.rb +1 -1
  264. data/lib/pubid/ieee/renderer.rb +154 -57
  265. data/lib/pubid/ieee/typed_stages.rb +12 -1
  266. data/lib/pubid/ieee/urn_generator.rb +3 -5
  267. data/lib/pubid/ieee.rb +56 -3
  268. data/lib/pubid/ietf/builder.rb +62 -0
  269. data/lib/pubid/ietf/identifier.rb +5 -0
  270. data/lib/pubid/ietf/identifiers/base.rb +75 -0
  271. data/lib/pubid/ietf/identifiers/bcp.rb +25 -0
  272. data/lib/pubid/ietf/identifiers/fyi.rb +25 -0
  273. data/lib/pubid/ietf/identifiers/internet_draft.rb +29 -0
  274. data/lib/pubid/ietf/identifiers/rfc.rb +25 -0
  275. data/lib/pubid/ietf/identifiers/std.rb +25 -0
  276. data/lib/pubid/ietf/identifiers.rb +13 -0
  277. data/lib/pubid/ietf/parser.rb +43 -0
  278. data/lib/pubid/ietf/renderer.rb +26 -0
  279. data/lib/pubid/ietf/urn_generator.rb +23 -0
  280. data/lib/pubid/ietf/urn_parser.rb +35 -0
  281. data/lib/pubid/ietf.rb +76 -0
  282. data/lib/pubid/iho/identifier.rb +1 -3
  283. data/lib/pubid/iho/identifiers/base.rb +1 -7
  284. data/lib/pubid/iho/identifiers/bibliographic.rb +1 -1
  285. data/lib/pubid/iho/identifiers/circular_letter.rb +1 -1
  286. data/lib/pubid/iho/identifiers/miscellaneous.rb +1 -1
  287. data/lib/pubid/iho/identifiers/publication.rb +1 -1
  288. data/lib/pubid/iho/identifiers/standard.rb +1 -1
  289. data/lib/pubid/iho/identifiers.rb +0 -1
  290. data/lib/pubid/iho.rb +7 -7
  291. data/lib/pubid/isbn/builder.rb +44 -0
  292. data/lib/pubid/isbn/check_digit.rb +53 -0
  293. data/lib/pubid/isbn/identifier.rb +63 -0
  294. data/lib/pubid/isbn/identifiers/book.rb +30 -0
  295. data/lib/pubid/isbn/identifiers.rb +9 -0
  296. data/lib/pubid/isbn/parser.rb +45 -0
  297. data/lib/pubid/isbn/renderer.rb +16 -0
  298. data/lib/pubid/isbn.rb +52 -0
  299. data/lib/pubid/iso/builder.rb +9 -4
  300. data/lib/pubid/iso/combined_identifier.rb +2 -2
  301. data/lib/pubid/iso/identifier.rb +17 -1
  302. data/lib/pubid/iso/identifiers/directives_supplement.rb +8 -8
  303. data/lib/pubid/iso/normalizer.rb +2 -2
  304. data/lib/pubid/iso/parser.rb +16 -12
  305. data/lib/pubid/iso/supplement_identifier.rb +19 -6
  306. data/lib/pubid/iso/urn_generator.rb +4 -1
  307. data/lib/pubid/iso/urn_parser.rb +1 -1
  308. data/lib/pubid/itu/builder.rb +230 -29
  309. data/lib/pubid/itu/components/code.rb +48 -5
  310. data/lib/pubid/itu/components/designation.rb +35 -0
  311. data/lib/pubid/itu/components.rb +1 -0
  312. data/lib/pubid/itu/identifier.rb +1 -3
  313. data/lib/pubid/itu/identifiers/addendum.rb +15 -0
  314. data/lib/pubid/itu/identifiers/amendment.rb +6 -28
  315. data/lib/pubid/itu/identifiers/annex.rb +13 -2
  316. data/lib/pubid/itu/identifiers/annex_of_recommendation.rb +87 -0
  317. data/lib/pubid/itu/identifiers/appendix_of_recommendation.rb +92 -0
  318. data/lib/pubid/itu/identifiers/base.rb +341 -23
  319. data/lib/pubid/itu/identifiers/combined_identifier.rb +99 -23
  320. data/lib/pubid/itu/identifiers/corrigendum.rb +16 -23
  321. data/lib/pubid/itu/identifiers/errata.rb +15 -0
  322. data/lib/pubid/itu/identifiers/handbook.rb +38 -0
  323. data/lib/pubid/itu/identifiers/question.rb +63 -0
  324. data/lib/pubid/itu/identifiers/recommendation.rb +3 -2
  325. data/lib/pubid/itu/identifiers/report.rb +39 -0
  326. data/lib/pubid/itu/identifiers/special_publication.rb +3 -1
  327. data/lib/pubid/itu/identifiers/standard_serialization.rb +58 -0
  328. data/lib/pubid/itu/identifiers/supplement.rb +119 -14
  329. data/lib/pubid/itu/identifiers.rb +10 -1
  330. data/lib/pubid/itu/model.rb +1 -1
  331. data/lib/pubid/itu/parser.rb +480 -26
  332. data/lib/pubid/itu/urn_generator.rb +36 -6
  333. data/lib/pubid/itu/urn_parser.rb +8 -0
  334. data/lib/pubid/itu.rb +11 -3
  335. data/lib/pubid/jcgm/builder.rb +23 -13
  336. data/lib/pubid/jcgm/identifiers/amendment.rb +0 -2
  337. data/lib/pubid/jcgm/identifiers/corrigendum.rb +34 -0
  338. data/lib/pubid/jcgm/identifiers/gum_guide.rb +3 -3
  339. data/lib/pubid/jcgm/identifiers/meeting.rb +47 -0
  340. data/lib/pubid/jcgm/identifiers.rb +2 -0
  341. data/lib/pubid/jcgm/parser.rb +66 -6
  342. data/lib/pubid/jcgm/renderer.rb +26 -12
  343. data/lib/pubid/jcgm/single_identifier.rb +74 -5
  344. data/lib/pubid/jcgm/supplement_identifier.rb +39 -4
  345. data/lib/pubid/jcgm/urn_generator.rb +21 -13
  346. data/lib/pubid/jcgm/urn_parser.rb +15 -3
  347. data/lib/pubid/jis/identifier.rb +52 -0
  348. data/lib/pubid/nist/builder.rb +29 -9
  349. data/lib/pubid/nist/caster.rb +7 -45
  350. data/lib/pubid/nist/circular_supplement_builder.rb +7 -5
  351. data/lib/pubid/nist/components/supplement.rb +5 -2
  352. data/lib/pubid/nist/configuration.rb +7 -1
  353. data/lib/pubid/nist/identifier.rb +1 -3
  354. data/lib/pubid/nist/identifiers/base.rb +121 -37
  355. data/lib/pubid/nist/identifiers/circular.rb +1 -1
  356. data/lib/pubid/nist/identifiers/commercial_standard.rb +1 -1
  357. data/lib/pubid/nist/identifiers/commercial_standard_emergency.rb +1 -1
  358. data/lib/pubid/nist/identifiers/commercial_standards_monthly.rb +1 -1
  359. data/lib/pubid/nist/identifiers/crpl_report.rb +1 -1
  360. data/lib/pubid/nist/identifiers/dated_document.rb +1 -1
  361. data/lib/pubid/nist/identifiers/federal_information_processing_standards.rb +1 -1
  362. data/lib/pubid/nist/identifiers/grant_contractor_report.rb +1 -1
  363. data/lib/pubid/nist/identifiers/handbook.rb +1 -1
  364. data/lib/pubid/nist/identifiers/internal_report.rb +1 -1
  365. data/lib/pubid/nist/identifiers/letter_circular.rb +1 -1
  366. data/lib/pubid/nist/identifiers/miscellaneous_publication.rb +1 -1
  367. data/lib/pubid/nist/identifiers/monograph.rb +1 -1
  368. data/lib/pubid/nist/identifiers/ncstar.rb +2 -2
  369. data/lib/pubid/nist/identifiers/nsrds.rb +1 -1
  370. data/lib/pubid/nist/identifiers/owmwp.rb +1 -1
  371. data/lib/pubid/nist/identifiers/report.rb +1 -1
  372. data/lib/pubid/nist/identifiers/special_publication.rb +10 -1
  373. data/lib/pubid/nist/identifiers/technical_note.rb +1 -1
  374. data/lib/pubid/nist/identifiers.rb +0 -1
  375. data/lib/pubid/nist/parser.rb +26 -3
  376. data/lib/pubid/nist/preprocessor.rb +15 -0
  377. data/lib/pubid/nist/router.rb +2 -2
  378. data/lib/pubid/nist/series/ir.rb +0 -3
  379. data/lib/pubid/nist/supplement_identifier.rb +9 -9
  380. data/lib/pubid/nist.rb +12 -12
  381. data/lib/pubid/oasis/builder.rb +77 -0
  382. data/lib/pubid/oasis/identifier.rb +82 -0
  383. data/lib/pubid/oasis/identifiers/standard.rb +29 -0
  384. data/lib/pubid/oasis/identifiers.rb +9 -0
  385. data/lib/pubid/oasis/parser.rb +34 -0
  386. data/lib/pubid/oasis/renderer.rb +29 -0
  387. data/lib/pubid/oasis/urn_generator.rb +25 -0
  388. data/lib/pubid/oasis/urn_parser.rb +22 -0
  389. data/lib/pubid/oasis.rb +74 -0
  390. data/lib/pubid/ogc/builder.rb +33 -0
  391. data/lib/pubid/ogc/identifier.rb +77 -0
  392. data/lib/pubid/ogc/identifiers/document.rb +26 -0
  393. data/lib/pubid/ogc/identifiers.rb +10 -0
  394. data/lib/pubid/ogc/parser.rb +40 -0
  395. data/lib/pubid/ogc/renderer.rb +33 -0
  396. data/lib/pubid/ogc/urn_generator.rb +14 -0
  397. data/lib/pubid/ogc/urn_parser.rb +22 -0
  398. data/lib/pubid/ogc.rb +71 -0
  399. data/lib/pubid/oiml/builder.rb +31 -7
  400. data/lib/pubid/oiml/identifier.rb +9 -1
  401. data/lib/pubid/oiml/parser.rb +11 -12
  402. data/lib/pubid/oiml/renderer.rb +13 -13
  403. data/lib/pubid/oiml/single_identifier.rb +20 -0
  404. data/lib/pubid/oiml/supplement_identifier.rb +9 -9
  405. data/lib/pubid/oiml/urn_parser.rb +23 -6
  406. data/lib/pubid/oiml.rb +4 -0
  407. data/lib/pubid/omg/builder.rb +19 -0
  408. data/lib/pubid/omg/identifier.rb +44 -0
  409. data/lib/pubid/omg/identifiers/specification.rb +32 -0
  410. data/lib/pubid/omg/identifiers.rb +9 -0
  411. data/lib/pubid/omg/parser.rb +40 -0
  412. data/lib/pubid/omg/renderer.rb +19 -0
  413. data/lib/pubid/omg.rb +51 -0
  414. data/lib/pubid/parsers/mr_string.rb +101 -7
  415. data/lib/pubid/plateau/builder.rb +8 -4
  416. data/lib/pubid/plateau/identifier.rb +1 -3
  417. data/lib/pubid/plateau/identifiers/base.rb +1 -6
  418. data/lib/pubid/plateau/identifiers/handbook.rb +1 -1
  419. data/lib/pubid/plateau/identifiers/technical_report.rb +1 -1
  420. data/lib/pubid/plateau/identifiers.rb +0 -1
  421. data/lib/pubid/plateau/parser.rb +15 -5
  422. data/lib/pubid/plateau/renderer.rb +1 -1
  423. data/lib/pubid/plateau/supplement_identifier.rb +11 -11
  424. data/lib/pubid/plateau.rb +4 -4
  425. data/lib/pubid/renderers/human_readable.rb +3 -1
  426. data/lib/pubid/renderers/mr_string.rb +46 -4
  427. data/lib/pubid/renderers/supplement_renderer.rb +1 -1
  428. data/lib/pubid/rendering/supplement.rb +4 -4
  429. data/lib/pubid/sae/builder.rb +1 -1
  430. data/lib/pubid/sae/identifier.rb +1 -3
  431. data/lib/pubid/sae/identifiers/base.rb +10 -7
  432. data/lib/pubid/sae/identifiers.rb +0 -1
  433. data/lib/pubid/sae.rb +3 -3
  434. data/lib/pubid/tgpp/builder.rb +41 -0
  435. data/lib/pubid/tgpp/identifier.rb +91 -0
  436. data/lib/pubid/tgpp/identifiers/technical_report.rb +32 -0
  437. data/lib/pubid/tgpp/identifiers/technical_specification.rb +33 -0
  438. data/lib/pubid/tgpp/identifiers.rb +11 -0
  439. data/lib/pubid/tgpp/parser.rb +62 -0
  440. data/lib/pubid/tgpp/renderer.rb +36 -0
  441. data/lib/pubid/tgpp/urn_generator.rb +20 -0
  442. data/lib/pubid/tgpp/urn_parser.rb +28 -0
  443. data/lib/pubid/tgpp.rb +78 -0
  444. data/lib/pubid/type_resolver.rb +59 -0
  445. data/lib/pubid/un/builder.rb +42 -0
  446. data/lib/pubid/un/identifier.rb +37 -0
  447. data/lib/pubid/un/identifiers/document.rb +24 -0
  448. data/lib/pubid/un/identifiers.rb +9 -0
  449. data/lib/pubid/un/parser.rb +25 -0
  450. data/lib/pubid/un/renderer.rb +11 -0
  451. data/lib/pubid/un.rb +46 -0
  452. data/lib/pubid/version.rb +1 -1
  453. data/lib/pubid/w3c/builder.rb +55 -0
  454. data/lib/pubid/w3c/identifier.rb +101 -0
  455. data/lib/pubid/w3c/identifiers/candidate_recommendation.rb +31 -0
  456. data/lib/pubid/w3c/identifiers/candidate_recommendation_draft.rb +31 -0
  457. data/lib/pubid/w3c/identifiers/draft_note.rb +31 -0
  458. data/lib/pubid/w3c/identifiers/note.rb +30 -0
  459. data/lib/pubid/w3c/identifiers/obsolete_recommendation.rb +31 -0
  460. data/lib/pubid/w3c/identifiers/proposed_edited_recommendation.rb +31 -0
  461. data/lib/pubid/w3c/identifiers/proposed_recommendation.rb +31 -0
  462. data/lib/pubid/w3c/identifiers/recommendation.rb +31 -0
  463. data/lib/pubid/w3c/identifiers/standard.rb +12 -0
  464. data/lib/pubid/w3c/identifiers/superseded_recommendation.rb +31 -0
  465. data/lib/pubid/w3c/identifiers/working_draft.rb +30 -0
  466. data/lib/pubid/w3c/identifiers.rb +25 -0
  467. data/lib/pubid/w3c/parser.rb +43 -0
  468. data/lib/pubid/w3c/renderer.rb +35 -0
  469. data/lib/pubid/w3c/urn_generator.rb +18 -0
  470. data/lib/pubid/w3c/urn_parser.rb +36 -0
  471. data/lib/pubid/w3c.rb +67 -0
  472. data/lib/pubid/xsf/builder.rb +17 -0
  473. data/lib/pubid/xsf/identifier.rb +62 -0
  474. data/lib/pubid/xsf/identifiers/xep.rb +31 -0
  475. data/lib/pubid/xsf/identifiers.rb +9 -0
  476. data/lib/pubid/xsf/parser.rb +25 -0
  477. data/lib/pubid/xsf/renderer.rb +23 -0
  478. data/lib/pubid/xsf/urn_generator.rb +13 -0
  479. data/lib/pubid/xsf/urn_parser.rb +18 -0
  480. data/lib/pubid/xsf.rb +76 -0
  481. data/lib/pubid.rb +25 -1
  482. metadata +205 -4
  483. data/lib/pubid/cie/components/code.rb +0 -80
  484. data/lib/pubid/iala/identifiers/base.rb +0 -14
@@ -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,42 @@ 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
+ # `publisher` is *derived* from `ieee_identifier` (the serialized source
34
+ # of truth), so it must not be serialized. Emitting it breaks the
35
+ # canonical round-trip: the derived value is default-omitted on the parse
36
+ # path (publisher unset), but re-emitted after from_hash materializes the
37
+ # attribute default (the override then returns ieee_identifier.publisher,
38
+ # e.g. "AIEE", not the "IEEE" default). Drop it — ieee_identifier rebuilds
39
+ # it. (AdoptedStandard is always a top-level wrapper, never nested, so a
40
+ # to_hash-level drop is sufficient — same as JointDevelopment.)
41
+ def to_hash(*args)
42
+ hash = super
43
+ hash.delete("publisher") if hash.is_a?(::Hash)
44
+ hash
45
+ end
21
46
  end
22
47
  end
23
48
  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
@@ -16,11 +16,24 @@ module Pubid
16
16
 
17
17
  attribute :publisher, :string, default: -> { "IEEE" }
18
18
  attribute :copublisher, :string, collection: true # IEC, ISO, ANSI, etc.
19
- attribute :code, :string # Will store code as object in initialize
19
+ # NB: there is deliberately NO `attribute :code`. IEEE's document code is
20
+ # serialized as the split index columns (number/prefix/parts/separator) on
21
+ # concrete leaf types via the CodeNumber mixin; `code`/`code_obj` remain a
22
+ # runtime-only representation (built in #initialize, read by the renderer).
23
+ # This holds for every IEEE type, including the historical AIEE/IRE and
24
+ # the NESC family: all three used to descend from
25
+ # Lutaml::Model::Serializable with their own `attribute :code`, and were
26
+ # reparented onto this base (with the CodeNumber mixin on their leaves).
20
27
  attribute :year, :string
21
28
  attribute :type, :string, default: -> { "Std" } # Std, Draft Std
22
29
  attribute :draft_status, :string # Unapproved, Approved, Active Unapproved
23
30
  attribute :draft, :string # Will store draft as object
31
+ # Numbered/lettered revision id, e.g. "2" for "P802.16Rev2", "i" for the
32
+ # relaton suffix "/R-i". IEEE's native inline "Rev<n>" and relaton's
33
+ # synthetic "/R-<x>" both feed this one attribute; rendered inline as
34
+ # "Rev<id>". `::Pubid::Identifier` has no `revision` attribute, so this is
35
+ # free of the number/stage multi-flavor collision landmine.
36
+ attribute :revision, :string
24
37
  attribute :edition, :string # Edition 1.0
25
38
  attribute :month, :string
26
39
  attribute :day, :string
@@ -46,8 +59,6 @@ module Pubid
46
59
  attribute :interpretation, :boolean, default: -> {
47
60
  false
48
61
  } # /INT notation
49
- attribute :conf_number, :string # Conformance document number
50
- attribute :conf_year, :string # Conformance document year
51
62
  attribute :ashrae_number, :string # ASHRAE Guideline number
52
63
  attribute :ashrae_year, :string # ASHRAE Guideline year
53
64
  attribute :crossref, :string # IEEE cross-reference (e.g., /C62.22.1-1996)
@@ -56,7 +67,13 @@ module Pubid
56
67
  # Store actual component objects
57
68
  attr_accessor :code_obj, :draft_obj
58
69
 
59
- def initialize(**args)
70
+ # Accept either keyword args (`new(number: "802")`, the normal path) or a
71
+ # single positional attribute hash (`new({number: "802"})`). The latter is
72
+ # what the base `Pubid::Identifier#exclude`/matching machinery uses when it
73
+ # rebuilds via `self.class.new(attrs)`; without it, `exclude`/`matches?`
74
+ # raised ArgumentError for every IEEE identifier.
75
+ def initialize(args = {}, **kwargs)
76
+ args = args.merge(kwargs) unless kwargs.empty?
60
77
  super()
61
78
 
62
79
  # Handle typed_stage if provided
@@ -64,13 +81,15 @@ module Pubid
64
81
  self.typed_stage = args[:typed_stage]
65
82
  end
66
83
 
67
- # Handle code as component object
84
+ # Handle code as component object. `code` is runtime-only (not a lutaml
85
+ # attribute) — we keep the parsed Components::Code in code_obj; every
86
+ # leaf serializes the split index columns (number/prefix/parts/separator)
87
+ # via the CodeNumber mixin instead of a `code` string. The renderer reads
88
+ # code_obj (base #code returns it).
68
89
  if args[:code].is_a?(String)
69
90
  self.code_obj = Components::Code.parse(args[:code])
70
- self.code = args[:code]
71
91
  elsif args[:code]
72
92
  self.code_obj = args[:code]
73
- self.code = args[:code].to_s
74
93
  end
75
94
 
76
95
  # Handle draft as component object
@@ -127,6 +146,62 @@ module Pubid
127
146
  draft_obj.numeric_month
128
147
  end
129
148
 
149
+ # IEEE stores its publication date as separate `year`/`month`/`day`
150
+ # :string attributes, not a Components::Date, so the base #exclude's
151
+ # `:year`->`:date` remap can't reach them (it would nil an unused `date`
152
+ # component and leave the strings). After the base rebuild, nil the whole
153
+ # date cluster when `:year`/`:date` is excluded, so a date-less reference
154
+ # matches every date in the bucket (relaton partial-ref matching). NB: the
155
+ # base #exclude also previously raised for every IEEE id (positional
156
+ # `self.class.new(attrs)` vs the keyword initialize) — fixed by
157
+ # Identifier#initialize accepting a positional hash.
158
+ def exclude(*args)
159
+ result = super
160
+ if args.intersect?(%i[year date])
161
+ %i[year month day].each do |attr|
162
+ result.public_send("#{attr}=", nil) if result.respond_to?("#{attr}=")
163
+ end
164
+ end
165
+ result
166
+ end
167
+
168
+ # Compact the serialized hash (recursively, so nested Corrigendum bases
169
+ # are compacted too) after the normal serialize + canonicalize: collapse
170
+ # `typed_stage` to a scalar `stage` and strip the `/` off `draft`. See
171
+ # Compaction for why this is a hash transform, not lutaml attributes.
172
+ def to_hash(*args)
173
+ hash = super
174
+ Compaction.collapse(hash) if hash.is_a?(::Hash)
175
+ hash
176
+ end
177
+
178
+ # Inverse of the to_hash compaction: expand a scalar `stage` back into the
179
+ # `typed_stage` sub-hash (on a deep copy, recursively) before lutaml
180
+ # deserializes, so nested bases rebuild their component too. `draft` needs
181
+ # no expansion (Draft.parse accepts the slashless form).
182
+ def self.from_hash(data, options = {})
183
+ data = Compaction.expand(Compaction.deep_dup(data)) if data.is_a?(::Hash)
184
+ super
185
+ end
186
+
187
+ # Register the identifier classes the automatic `Identifiers::*` scan
188
+ # cannot see, so `Pubid::Ieee::Identifier.from_hash` can route their rows
189
+ # back. The scan only looks at classes declared *directly* under
190
+ # `Identifiers`, which misses both AIEE and IRE (they live under their own
191
+ # `Aiee`/`Ire` namespaces) and the whole NESC family (nested one level
192
+ # deeper, under `Identifiers::Nesc`).
193
+ def self.additional_identifier_classes
194
+ [
195
+ Aiee::Identifier,
196
+ Ire::Identifier,
197
+ Identifiers::Nesc::Draft,
198
+ Identifiers::Nesc::Edition,
199
+ Identifiers::Nesc::Handbook,
200
+ Identifiers::Nesc::Redline,
201
+ Identifiers::Nesc::Standard,
202
+ ]
203
+ end
204
+
130
205
  # Parse IEEE identifier string.
131
206
  #
132
207
  # PreParser owns all regex/dispatch logic; this method is a thin
@@ -245,12 +320,30 @@ module Pubid
245
320
  builder.original_input = input
246
321
  builder.build(parsed)
247
322
  end
248
- end
249
323
 
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
324
+ # IEEE stores identity in `code` (prefix/number/parts) rather than the
325
+ # generic `number`, has its own `type` string ("Std", "Draft Std"), and
326
+ # carries `year` as a bare string — none of which the generic MrString
327
+ # renderer knows about. Override the lossless MR template directly so
328
+ # every IEEE identifier round-trips (issue #142). Supplements append
329
+ # `_{type}.{number}.{year}` recursively via mr_supplement_suffix.
330
+ # Lowercased to match the all-lowercase MR convention.
331
+ def mr_publisher
332
+ publisher&.to_s&.downcase
333
+ end
334
+
335
+ def mr_type
336
+ type&.downcase
337
+ end
338
+
339
+ def mr_number_with_part
340
+ code_obj&.to_s&.downcase
341
+ end
342
+
343
+ def mr_year
344
+ year&.to_s
345
+ end
254
346
  end
347
+
255
348
  end
256
349
  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
@@ -7,8 +7,13 @@ module Pubid
7
7
  # Represents corrections to published standards
8
8
  # Example: IEEE Std 535-2013/Cor. 1-2017
9
9
  class Corrigendum < SupplementIdentifier
10
- attribute :cor_number, :string
11
- attribute :cor_year, :string
10
+ # The corrigendum's own ordinal (`number`) and `year` — uniform names,
11
+ # not `cor_number`/`cor_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.
15
+ attribute :number, :string
16
+ # `year` inherited from the base.
12
17
 
13
18
  # TYPED_STAGES for corrigendum
14
19
  # Corrigendum uses "Cor" abbreviation
@@ -19,6 +24,16 @@ module Pubid
19
24
  stage_code: "published",
20
25
  ),
21
26
  ].freeze
27
+
28
+ # MR supplement suffix: `cor.{number}.{year}` (e.g. "/cor.1.2017").
29
+ # The MrString renderer recurses into `base` and appends this so the
30
+ # full IEEE corrigendum round-trips losslessly (issue #142).
31
+ def mr_supplement_suffix
32
+ segments = ["cor"]
33
+ segments << number.to_s if number
34
+ segments << year.to_s if year
35
+ segments.join(".")
36
+ end
22
37
  end
23
38
  end
24
39
  end
@@ -6,8 +6,8 @@ module Pubid
6
6
  # CSA dual published identifier
7
7
  # Represents IEEE/CSA dual published standards
8
8
  # Example: IEEE Std 844.1-2017/CSA C22.2 No. 293.1-17
9
- class CsaDualPublished < Base
10
- attribute :ieee_identifier, Base
9
+ class CsaDualPublished < Identifier
10
+ attribute :ieee_identifier, Identifier
11
11
  # CSA identifier is stored as-is (not a Lutaml model type)
12
12
  attr_accessor :csa_identifier
13
13
 
@@ -33,7 +33,7 @@ module Pubid
33
33
  end
34
34
 
35
35
  def self.parse(string)
36
- Base.parse(string)
36
+ Identifier.parse(string)
37
37
  end
38
38
  end
39
39
  end
@@ -5,9 +5,9 @@ module Pubid
5
5
  module Identifiers
6
6
  # Dual Identifier - wraps two identifiers connected with "and"
7
7
  # Example: "ANSI C37.61-1973 and IEEE Std 321-1973"
8
- class DualIdentifier < Base
9
- attribute :first_identifier, Base, polymorphic: true
10
- attribute :second_identifier, Base, polymorphic: true
8
+ class DualIdentifier < Identifier
9
+ attribute :first_identifier, Identifier, polymorphic: true
10
+ attribute :second_identifier, Identifier, polymorphic: true
11
11
  end
12
12
  end
13
13
  end
@@ -7,12 +7,12 @@ module Pubid
7
7
  # Document jointly published by two organizations
8
8
  # Example: "ANSI C37.61-1973 and IEEE Std 321-1973"
9
9
  # This means the document was published together by both organizations
10
- class DualPublished < Base
10
+ class DualPublished < Identifier
11
11
  # First organization's identifier
12
- attribute :first_identifier, Base, polymorphic: true
12
+ attribute :first_identifier, Identifier, polymorphic: true
13
13
 
14
14
  # Second organization's identifier
15
- attribute :second_identifier, Base, polymorphic: true
15
+ attribute :second_identifier, Identifier, polymorphic: true
16
16
 
17
17
  def publisher
18
18
  # Return array of both publishers