pubid 2.0.0.pre.alpha.7 → 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 (485) hide show
  1. checksums.yaml +4 -4
  2. data/README.adoc +9 -50
  3. data/data/bipm/update_codes.yaml +9 -0
  4. data/data/iec/update_codes.yaml +3 -0
  5. data/data/ieee/update_codes.yaml +107 -1
  6. data/data/nist/series.yaml +155 -0
  7. data/data/nist/update_codes.yaml +8 -0
  8. data/lib/pubid/adobe/builder.rb +52 -0
  9. data/lib/pubid/adobe/identifier.rb +49 -0
  10. data/lib/pubid/adobe/identifiers/publication.rb +31 -0
  11. data/lib/pubid/adobe/identifiers/tech_note.rb +32 -0
  12. data/lib/pubid/adobe/identifiers.rb +10 -0
  13. data/lib/pubid/adobe/parser.rb +128 -0
  14. data/lib/pubid/adobe/renderer.rb +39 -0
  15. data/lib/pubid/adobe/urn_generator.rb +42 -0
  16. data/lib/pubid/adobe/urn_parser.rb +69 -0
  17. data/lib/pubid/adobe.rb +60 -0
  18. data/lib/pubid/amca/builder.rb +2 -2
  19. data/lib/pubid/amca/identifier.rb +1 -3
  20. data/lib/pubid/amca/identifiers/base.rb +1 -6
  21. data/lib/pubid/amca/identifiers/interpretation.rb +1 -1
  22. data/lib/pubid/amca/identifiers/publication.rb +1 -1
  23. data/lib/pubid/amca/identifiers/standard.rb +1 -1
  24. data/lib/pubid/amca/identifiers.rb +0 -1
  25. data/lib/pubid/amca/single_identifier.rb +1 -1
  26. data/lib/pubid/amca.rb +3 -3
  27. data/lib/pubid/ashrae/builder.rb +22 -22
  28. data/lib/pubid/ashrae/identifier.rb +3 -4
  29. data/lib/pubid/ashrae/identifiers/addenda_package.rb +1 -1
  30. data/lib/pubid/ashrae/identifiers/addendum.rb +1 -1
  31. data/lib/pubid/ashrae/identifiers/base.rb +14 -8
  32. data/lib/pubid/ashrae/identifiers/combined_addenda.rb +1 -1
  33. data/lib/pubid/ashrae/identifiers.rb +0 -1
  34. data/lib/pubid/ashrae/parser.rb +14 -9
  35. data/lib/pubid/ashrae/renderer.rb +19 -19
  36. data/lib/pubid/ashrae/single_identifier.rb +1 -1
  37. data/lib/pubid/ashrae/supplement_identifier.rb +6 -6
  38. data/lib/pubid/ashrae.rb +3 -3
  39. data/lib/pubid/bipm/builder.rb +107 -0
  40. data/lib/pubid/bipm/identifier.rb +160 -0
  41. data/lib/pubid/bipm/identifiers/committee_document.rb +24 -0
  42. data/lib/pubid/bipm/identifiers/guide.rb +24 -0
  43. data/lib/pubid/bipm/identifiers/meeting.rb +41 -0
  44. data/lib/pubid/bipm/identifiers/mep.rb +21 -0
  45. data/lib/pubid/bipm/identifiers/metrologia_article.rb +24 -0
  46. data/lib/pubid/bipm/identifiers/si_brochure.rb +20 -0
  47. data/lib/pubid/bipm/identifiers.rb +15 -0
  48. data/lib/pubid/bipm/parser.rb +184 -0
  49. data/lib/pubid/bipm/renderer.rb +110 -0
  50. data/lib/pubid/bipm/urn_generator.rb +58 -0
  51. data/lib/pubid/bipm/urn_parser.rb +52 -0
  52. data/lib/pubid/bipm.rb +86 -0
  53. data/lib/pubid/bsi/builder.rb +14 -14
  54. data/lib/pubid/bsi/identifier.rb +0 -1
  55. data/lib/pubid/bsi/identifiers/addendum_document.rb +2 -2
  56. data/lib/pubid/bsi/identifiers/amendment.rb +4 -4
  57. data/lib/pubid/bsi/identifiers/consolidated_identifier.rb +6 -0
  58. data/lib/pubid/bsi/identifiers/corrigendum.rb +4 -4
  59. data/lib/pubid/bsi/identifiers/expert_commentary.rb +5 -5
  60. data/lib/pubid/bsi/identifiers/supplement_document.rb +2 -2
  61. data/lib/pubid/bsi/identifiers/value_added_publication.rb +6 -6
  62. data/lib/pubid/bsi/renderer.rb +8 -8
  63. data/lib/pubid/bsi/single_identifier.rb +48 -0
  64. data/lib/pubid/builder/base.rb +5 -3
  65. data/lib/pubid/bundled_identifier.rb +3 -3
  66. data/lib/pubid/calconnect/builder.rb +38 -0
  67. data/lib/pubid/calconnect/identifier.rb +117 -0
  68. data/lib/pubid/calconnect/identifiers/standard.rb +29 -0
  69. data/lib/pubid/calconnect/identifiers.rb +9 -0
  70. data/lib/pubid/calconnect/parser.rb +54 -0
  71. data/lib/pubid/calconnect/renderer.rb +36 -0
  72. data/lib/pubid/calconnect/urn_generator.rb +33 -0
  73. data/lib/pubid/calconnect/urn_parser.rb +34 -0
  74. data/lib/pubid/calconnect.rb +77 -0
  75. data/lib/pubid/ccsds/builder.rb +1 -1
  76. data/lib/pubid/ccsds/identifier.rb +1 -1
  77. data/lib/pubid/ccsds/identifiers/corrigendum.rb +2 -2
  78. data/lib/pubid/ccsds/supplement_identifier.rb +6 -6
  79. data/lib/pubid/ccsds/urn_generator.rb +3 -3
  80. data/lib/pubid/cen_cenelec/builder.rb +46 -12
  81. data/lib/pubid/cen_cenelec/identifiers/amendment.rb +2 -2
  82. data/lib/pubid/cen_cenelec/identifiers/consolidated_identifier.rb +6 -0
  83. data/lib/pubid/cen_cenelec/identifiers/corrigendum.rb +2 -2
  84. data/lib/pubid/cen_cenelec/identifiers/european_norm.rb +60 -3
  85. data/lib/pubid/cen_cenelec/identifiers/fragment.rb +2 -2
  86. data/lib/pubid/cen_cenelec/renderer.rb +5 -5
  87. data/lib/pubid/cen_cenelec/supplement_identifier.rb +5 -5
  88. data/lib/pubid/cen_cenelec/urn_generator.rb +1 -1
  89. data/lib/pubid/cen_cenelec.rb +76 -72
  90. data/lib/pubid/cie/builder.rb +139 -24
  91. data/lib/pubid/cie/identifier.rb +7 -3
  92. data/lib/pubid/cie/identifiers/bundle.rb +32 -4
  93. data/lib/pubid/cie/identifiers/code_attributes.rb +52 -0
  94. data/lib/pubid/cie/identifiers/conference.rb +9 -4
  95. data/lib/pubid/cie/identifiers/corrigendum.rb +17 -23
  96. data/lib/pubid/cie/identifiers/dual_published.rb +4 -4
  97. data/lib/pubid/cie/identifiers/identical.rb +7 -7
  98. data/lib/pubid/cie/identifiers/joint_published.rb +8 -8
  99. data/lib/pubid/cie/identifiers/proceedings.rb +39 -0
  100. data/lib/pubid/cie/identifiers/standard.rb +33 -7
  101. data/lib/pubid/cie/identifiers/supplement.rb +24 -22
  102. data/lib/pubid/cie/identifiers/tutorial_bundle.rb +6 -2
  103. data/lib/pubid/cie/identifiers.rb +2 -0
  104. data/lib/pubid/cie/parser.rb +54 -5
  105. data/lib/pubid/cie/single_identifier.rb +40 -1
  106. data/lib/pubid/cie/supplement_identifier.rb +29 -16
  107. data/lib/pubid/cie/urn_generator.rb +11 -8
  108. data/lib/pubid/cie.rb +0 -1
  109. data/lib/pubid/components/date.rb +21 -5
  110. data/lib/pubid/csa/builder.rb +1 -1
  111. data/lib/pubid/csa/composite_identifier.rb +17 -1
  112. data/lib/pubid/csa/identifier.rb +48 -3
  113. data/lib/pubid/csa/identifiers/bundled.rb +6 -0
  114. data/lib/pubid/csa/identifiers/combined.rb +6 -0
  115. data/lib/pubid/csa/identifiers/package.rb +2 -2
  116. data/lib/pubid/csa/parser.rb +6 -6
  117. data/lib/pubid/csa/wrapper_identifier.rb +17 -0
  118. data/lib/pubid/doi/builder.rb +18 -0
  119. data/lib/pubid/doi/identifier.rb +46 -0
  120. data/lib/pubid/doi/identifiers/resource.rb +30 -0
  121. data/lib/pubid/doi/identifiers.rb +9 -0
  122. data/lib/pubid/doi/parser.rb +52 -0
  123. data/lib/pubid/doi/renderer.rb +13 -0
  124. data/lib/pubid/doi.rb +55 -0
  125. data/lib/pubid/easc/builder.rb +55 -0
  126. data/lib/pubid/easc/identifier.rb +61 -0
  127. data/lib/pubid/easc/identifiers/pmg.rb +20 -0
  128. data/lib/pubid/easc/identifiers/rmg.rb +19 -0
  129. data/lib/pubid/easc/identifiers.rb +10 -0
  130. data/lib/pubid/easc/parser.rb +80 -0
  131. data/lib/pubid/easc/renderer.rb +48 -0
  132. data/lib/pubid/easc/urn_generator.rb +46 -0
  133. data/lib/pubid/easc/urn_parser.rb +57 -0
  134. data/lib/pubid/easc.rb +52 -0
  135. data/lib/pubid/ecma/builder.rb +31 -0
  136. data/lib/pubid/ecma/identifier.rb +76 -0
  137. data/lib/pubid/ecma/identifiers/memento.rb +30 -0
  138. data/lib/pubid/ecma/identifiers/standard.rb +31 -0
  139. data/lib/pubid/ecma/identifiers/technical_report.rb +31 -0
  140. data/lib/pubid/ecma/identifiers.rb +11 -0
  141. data/lib/pubid/ecma/parser.rb +41 -0
  142. data/lib/pubid/ecma/renderer.rb +46 -0
  143. data/lib/pubid/ecma/urn_generator.rb +25 -0
  144. data/lib/pubid/ecma/urn_parser.rb +35 -0
  145. data/lib/pubid/ecma.rb +67 -0
  146. data/lib/pubid/etsi/builder.rb +7 -3
  147. data/lib/pubid/etsi/identifier.rb +1 -3
  148. data/lib/pubid/etsi/identifiers/base.rb +28 -10
  149. data/lib/pubid/etsi/identifiers/etsi_standard.rb +128 -4
  150. data/lib/pubid/etsi/identifiers/supplement_identifier.rb +35 -3
  151. data/lib/pubid/etsi/identifiers.rb +0 -1
  152. data/lib/pubid/etsi/parser.rb +8 -2
  153. data/lib/pubid/etsi/renderer.rb +8 -2
  154. data/lib/pubid/etsi.rb +5 -5
  155. data/lib/pubid/export/exporter.rb +2 -1
  156. data/lib/pubid/export/flavor_exporter.rb +18 -3
  157. data/lib/pubid/gb/builder.rb +45 -0
  158. data/lib/pubid/gb/identifier.rb +64 -0
  159. data/lib/pubid/gb/identifiers/standard.rb +34 -0
  160. data/lib/pubid/gb/identifiers.rb +9 -0
  161. data/lib/pubid/gb/parser.rb +63 -0
  162. data/lib/pubid/gb/renderer.rb +35 -0
  163. data/lib/pubid/gb.rb +87 -0
  164. data/lib/pubid/gost/builder.rb +148 -0
  165. data/lib/pubid/gost/identifier.rb +49 -0
  166. data/lib/pubid/gost/identifiers/foreign_reference.rb +25 -0
  167. data/lib/pubid/gost/identifiers/harmonized.rb +39 -0
  168. data/lib/pubid/gost/identifiers/identical_adoption.rb +39 -0
  169. data/lib/pubid/gost/identifiers/interstate_standard.rb +20 -0
  170. data/lib/pubid/gost/identifiers/national_standard.rb +19 -0
  171. data/lib/pubid/gost/identifiers.rb +13 -0
  172. data/lib/pubid/gost/parser.rb +97 -0
  173. data/lib/pubid/gost/renderer.rb +48 -0
  174. data/lib/pubid/gost/urn_generator.rb +46 -0
  175. data/lib/pubid/gost/urn_parser.rb +52 -0
  176. data/lib/pubid/gost.rb +49 -0
  177. data/lib/pubid/iala/builder.rb +22 -27
  178. data/lib/pubid/iala/identifier.rb +27 -2
  179. data/lib/pubid/iala/identifiers/advice.rb +1 -1
  180. data/lib/pubid/iala/identifiers/annex.rb +14 -12
  181. data/lib/pubid/iala/identifiers/general_assembly.rb +5 -2
  182. data/lib/pubid/iala/identifiers/guideline.rb +3 -1
  183. data/lib/pubid/iala/identifiers/letter.rb +1 -1
  184. data/lib/pubid/iala/identifiers/manual.rb +3 -1
  185. data/lib/pubid/iala/identifiers/model_course.rb +3 -1
  186. data/lib/pubid/iala/identifiers/recommendation.rb +3 -1
  187. data/lib/pubid/iala/identifiers/report.rb +1 -1
  188. data/lib/pubid/iala/identifiers/resolution.rb +1 -1
  189. data/lib/pubid/iala/identifiers/standard.rb +3 -1
  190. data/lib/pubid/iala/identifiers.rb +0 -1
  191. data/lib/pubid/iala/renderer.rb +1 -1
  192. data/lib/pubid/iala/urn_generator.rb +1 -1
  193. data/lib/pubid/iala/urn_parser.rb +19 -2
  194. data/lib/pubid/iala.rb +4 -4
  195. data/lib/pubid/iana/builder.rb +20 -0
  196. data/lib/pubid/iana/identifier.rb +72 -0
  197. data/lib/pubid/iana/identifiers/registry.rb +33 -0
  198. data/lib/pubid/iana/identifiers.rb +9 -0
  199. data/lib/pubid/iana/parser.rb +38 -0
  200. data/lib/pubid/iana/renderer.rb +29 -0
  201. data/lib/pubid/iana/urn_generator.rb +15 -0
  202. data/lib/pubid/iana/urn_parser.rb +24 -0
  203. data/lib/pubid/iana.rb +69 -0
  204. data/lib/pubid/identifier.rb +210 -23
  205. data/lib/pubid/idf/builder.rb +1 -1
  206. data/lib/pubid/idf/parser.rb +2 -2
  207. data/lib/pubid/idf/renderer.rb +1 -1
  208. data/lib/pubid/idf/supplement_identifier.rb +3 -3
  209. data/lib/pubid/iec/builder.rb +35 -25
  210. data/lib/pubid/iec/identifier.rb +47 -2
  211. data/lib/pubid/iec/identifiers/base.rb +2 -2
  212. data/lib/pubid/iec/identifiers/consolidated_identifier.rb +6 -0
  213. data/lib/pubid/iec/identifiers/fragment_identifier.rb +11 -11
  214. data/lib/pubid/iec/identifiers/international_standard.rb +8 -5
  215. data/lib/pubid/iec/identifiers/sheet_identifier.rb +12 -12
  216. data/lib/pubid/iec/identifiers/technical_group.rb +30 -0
  217. data/lib/pubid/iec/identifiers/technical_report.rb +1 -1
  218. data/lib/pubid/iec/identifiers/technical_specification.rb +1 -1
  219. data/lib/pubid/iec/identifiers/vap_identifier.rb +12 -12
  220. data/lib/pubid/iec/identifiers.rb +1 -0
  221. data/lib/pubid/iec/parser.rb +51 -10
  222. data/lib/pubid/iec/renderer.rb +18 -8
  223. data/lib/pubid/iec/single_identifier.rb +1 -1
  224. data/lib/pubid/iec/supplement_identifier.rb +32 -19
  225. data/lib/pubid/iec.rb +1 -6
  226. data/lib/pubid/ieee/aiee/builder.rb +17 -3
  227. data/lib/pubid/ieee/aiee/identifier.rb +45 -61
  228. data/lib/pubid/ieee/builder.rb +341 -68
  229. data/lib/pubid/ieee/compaction.rb +121 -0
  230. data/lib/pubid/ieee/components/draft.rb +47 -7
  231. data/lib/pubid/ieee/identifier.rb +1 -3
  232. data/lib/pubid/ieee/identifiers/adopted_standard.rb +30 -5
  233. data/lib/pubid/ieee/identifiers/amendment.rb +40 -0
  234. data/lib/pubid/ieee/identifiers/base.rb +106 -13
  235. data/lib/pubid/ieee/identifiers/code_number.rb +82 -0
  236. data/lib/pubid/ieee/identifiers/conformance_identifier.rb +6 -2
  237. data/lib/pubid/ieee/identifiers/corrigendum.rb +17 -2
  238. data/lib/pubid/ieee/identifiers/csa_dual_published.rb +3 -3
  239. data/lib/pubid/ieee/identifiers/dual_identifier.rb +3 -3
  240. data/lib/pubid/ieee/identifiers/dual_published.rb +3 -3
  241. data/lib/pubid/ieee/identifiers/iec_ieee_copublished.rb +102 -5
  242. data/lib/pubid/ieee/identifiers/interpretation_identifier.rb +2 -1
  243. data/lib/pubid/ieee/identifiers/joint_development.rb +56 -11
  244. data/lib/pubid/ieee/identifiers/multi_numbered_identifier.rb +1 -1
  245. data/lib/pubid/ieee/identifiers/nesc/base.rb +90 -19
  246. data/lib/pubid/ieee/identifiers/nesc/draft.rb +20 -5
  247. data/lib/pubid/ieee/identifiers/nesc/edition.rb +32 -0
  248. data/lib/pubid/ieee/identifiers/nesc/handbook.rb +16 -5
  249. data/lib/pubid/ieee/identifiers/nesc/redline.rb +11 -2
  250. data/lib/pubid/ieee/identifiers/nesc/standard.rb +18 -3
  251. data/lib/pubid/ieee/identifiers/nesc.rb +1 -0
  252. data/lib/pubid/ieee/identifiers/parenthetical_identifier.rb +3 -3
  253. data/lib/pubid/ieee/identifiers/project_draft_identifier.rb +14 -6
  254. data/lib/pubid/ieee/identifiers/redlined_standard.rb +4 -4
  255. data/lib/pubid/ieee/identifiers/si_standard.rb +4 -1
  256. data/lib/pubid/ieee/identifiers/standard.rb +4 -1
  257. data/lib/pubid/ieee/identifiers/supplement_identifier.rb +26 -7
  258. data/lib/pubid/ieee/identifiers.rb +2 -1
  259. data/lib/pubid/ieee/ire/builder.rb +7 -6
  260. data/lib/pubid/ieee/ire/identifier.rb +48 -33
  261. data/lib/pubid/ieee/nesc/builder.rb +33 -20
  262. data/lib/pubid/ieee/nesc/parser.rb +15 -14
  263. data/lib/pubid/ieee/parser.rb +461 -43
  264. data/lib/pubid/ieee/pre_parser.rb +1 -1
  265. data/lib/pubid/ieee/renderer.rb +154 -57
  266. data/lib/pubid/ieee/typed_stages.rb +12 -1
  267. data/lib/pubid/ieee/urn_generator.rb +3 -5
  268. data/lib/pubid/ieee.rb +56 -3
  269. data/lib/pubid/ietf/builder.rb +62 -0
  270. data/lib/pubid/ietf/identifier.rb +5 -0
  271. data/lib/pubid/ietf/identifiers/base.rb +75 -0
  272. data/lib/pubid/ietf/identifiers/bcp.rb +25 -0
  273. data/lib/pubid/ietf/identifiers/fyi.rb +25 -0
  274. data/lib/pubid/ietf/identifiers/internet_draft.rb +29 -0
  275. data/lib/pubid/ietf/identifiers/rfc.rb +25 -0
  276. data/lib/pubid/ietf/identifiers/std.rb +25 -0
  277. data/lib/pubid/ietf/identifiers.rb +13 -0
  278. data/lib/pubid/ietf/parser.rb +43 -0
  279. data/lib/pubid/ietf/renderer.rb +26 -0
  280. data/lib/pubid/ietf/urn_generator.rb +23 -0
  281. data/lib/pubid/ietf/urn_parser.rb +35 -0
  282. data/lib/pubid/ietf.rb +76 -0
  283. data/lib/pubid/iho/identifier.rb +1 -3
  284. data/lib/pubid/iho/identifiers/base.rb +1 -7
  285. data/lib/pubid/iho/identifiers/bibliographic.rb +1 -1
  286. data/lib/pubid/iho/identifiers/circular_letter.rb +1 -1
  287. data/lib/pubid/iho/identifiers/miscellaneous.rb +1 -1
  288. data/lib/pubid/iho/identifiers/publication.rb +1 -1
  289. data/lib/pubid/iho/identifiers/standard.rb +1 -1
  290. data/lib/pubid/iho/identifiers.rb +0 -1
  291. data/lib/pubid/iho.rb +7 -7
  292. data/lib/pubid/isbn/builder.rb +44 -0
  293. data/lib/pubid/isbn/check_digit.rb +53 -0
  294. data/lib/pubid/isbn/identifier.rb +63 -0
  295. data/lib/pubid/isbn/identifiers/book.rb +30 -0
  296. data/lib/pubid/isbn/identifiers.rb +9 -0
  297. data/lib/pubid/isbn/parser.rb +45 -0
  298. data/lib/pubid/isbn/renderer.rb +16 -0
  299. data/lib/pubid/isbn.rb +52 -0
  300. data/lib/pubid/iso/builder.rb +9 -4
  301. data/lib/pubid/iso/combined_identifier.rb +2 -2
  302. data/lib/pubid/iso/identifier.rb +17 -1
  303. data/lib/pubid/iso/identifiers/directives_supplement.rb +8 -8
  304. data/lib/pubid/iso/normalizer.rb +2 -2
  305. data/lib/pubid/iso/parser.rb +16 -12
  306. data/lib/pubid/iso/supplement_identifier.rb +19 -6
  307. data/lib/pubid/iso/urn_generator.rb +4 -1
  308. data/lib/pubid/iso/urn_parser.rb +1 -1
  309. data/lib/pubid/itu/builder.rb +230 -29
  310. data/lib/pubid/itu/components/code.rb +48 -5
  311. data/lib/pubid/itu/components/designation.rb +35 -0
  312. data/lib/pubid/itu/components.rb +1 -0
  313. data/lib/pubid/itu/identifier.rb +1 -3
  314. data/lib/pubid/itu/identifiers/addendum.rb +15 -0
  315. data/lib/pubid/itu/identifiers/amendment.rb +6 -28
  316. data/lib/pubid/itu/identifiers/annex.rb +13 -2
  317. data/lib/pubid/itu/identifiers/annex_of_recommendation.rb +87 -0
  318. data/lib/pubid/itu/identifiers/appendix_of_recommendation.rb +92 -0
  319. data/lib/pubid/itu/identifiers/base.rb +341 -23
  320. data/lib/pubid/itu/identifiers/combined_identifier.rb +99 -23
  321. data/lib/pubid/itu/identifiers/corrigendum.rb +16 -23
  322. data/lib/pubid/itu/identifiers/errata.rb +15 -0
  323. data/lib/pubid/itu/identifiers/handbook.rb +38 -0
  324. data/lib/pubid/itu/identifiers/question.rb +63 -0
  325. data/lib/pubid/itu/identifiers/recommendation.rb +3 -2
  326. data/lib/pubid/itu/identifiers/report.rb +39 -0
  327. data/lib/pubid/itu/identifiers/special_publication.rb +3 -1
  328. data/lib/pubid/itu/identifiers/standard_serialization.rb +58 -0
  329. data/lib/pubid/itu/identifiers/supplement.rb +119 -14
  330. data/lib/pubid/itu/identifiers.rb +10 -1
  331. data/lib/pubid/itu/model.rb +1 -1
  332. data/lib/pubid/itu/parser.rb +480 -26
  333. data/lib/pubid/itu/urn_generator.rb +36 -6
  334. data/lib/pubid/itu/urn_parser.rb +8 -0
  335. data/lib/pubid/itu.rb +11 -3
  336. data/lib/pubid/jcgm/builder.rb +23 -13
  337. data/lib/pubid/jcgm/identifiers/amendment.rb +0 -2
  338. data/lib/pubid/jcgm/identifiers/corrigendum.rb +34 -0
  339. data/lib/pubid/jcgm/identifiers/gum_guide.rb +3 -3
  340. data/lib/pubid/jcgm/identifiers/meeting.rb +47 -0
  341. data/lib/pubid/jcgm/identifiers.rb +2 -0
  342. data/lib/pubid/jcgm/parser.rb +66 -6
  343. data/lib/pubid/jcgm/renderer.rb +26 -12
  344. data/lib/pubid/jcgm/single_identifier.rb +74 -5
  345. data/lib/pubid/jcgm/supplement_identifier.rb +39 -4
  346. data/lib/pubid/jcgm/urn_generator.rb +21 -13
  347. data/lib/pubid/jcgm/urn_parser.rb +15 -3
  348. data/lib/pubid/jis/identifier.rb +52 -0
  349. data/lib/pubid/nist/builder.rb +29 -9
  350. data/lib/pubid/nist/caster.rb +7 -45
  351. data/lib/pubid/nist/circular_supplement_builder.rb +7 -5
  352. data/lib/pubid/nist/components/supplement.rb +5 -2
  353. data/lib/pubid/nist/configuration.rb +7 -1
  354. data/lib/pubid/nist/identifier.rb +1 -3
  355. data/lib/pubid/nist/identifiers/base.rb +121 -37
  356. data/lib/pubid/nist/identifiers/circular.rb +1 -1
  357. data/lib/pubid/nist/identifiers/commercial_standard.rb +1 -1
  358. data/lib/pubid/nist/identifiers/commercial_standard_emergency.rb +1 -1
  359. data/lib/pubid/nist/identifiers/commercial_standards_monthly.rb +1 -1
  360. data/lib/pubid/nist/identifiers/crpl_report.rb +1 -1
  361. data/lib/pubid/nist/identifiers/dated_document.rb +1 -1
  362. data/lib/pubid/nist/identifiers/federal_information_processing_standards.rb +1 -1
  363. data/lib/pubid/nist/identifiers/grant_contractor_report.rb +1 -1
  364. data/lib/pubid/nist/identifiers/handbook.rb +1 -1
  365. data/lib/pubid/nist/identifiers/internal_report.rb +1 -1
  366. data/lib/pubid/nist/identifiers/letter_circular.rb +1 -1
  367. data/lib/pubid/nist/identifiers/miscellaneous_publication.rb +1 -1
  368. data/lib/pubid/nist/identifiers/monograph.rb +1 -1
  369. data/lib/pubid/nist/identifiers/ncstar.rb +2 -2
  370. data/lib/pubid/nist/identifiers/nsrds.rb +1 -1
  371. data/lib/pubid/nist/identifiers/owmwp.rb +1 -1
  372. data/lib/pubid/nist/identifiers/report.rb +1 -1
  373. data/lib/pubid/nist/identifiers/special_publication.rb +10 -1
  374. data/lib/pubid/nist/identifiers/technical_note.rb +1 -1
  375. data/lib/pubid/nist/identifiers.rb +0 -1
  376. data/lib/pubid/nist/parser.rb +29 -4
  377. data/lib/pubid/nist/preprocessor.rb +15 -0
  378. data/lib/pubid/nist/router.rb +3 -2
  379. data/lib/pubid/nist/series/ir.rb +0 -3
  380. data/lib/pubid/nist/supplement_identifier.rb +9 -9
  381. data/lib/pubid/nist.rb +13 -12
  382. data/lib/pubid/oasis/builder.rb +77 -0
  383. data/lib/pubid/oasis/identifier.rb +82 -0
  384. data/lib/pubid/oasis/identifiers/standard.rb +29 -0
  385. data/lib/pubid/oasis/identifiers.rb +9 -0
  386. data/lib/pubid/oasis/parser.rb +34 -0
  387. data/lib/pubid/oasis/renderer.rb +29 -0
  388. data/lib/pubid/oasis/urn_generator.rb +25 -0
  389. data/lib/pubid/oasis/urn_parser.rb +22 -0
  390. data/lib/pubid/oasis.rb +74 -0
  391. data/lib/pubid/ogc/builder.rb +33 -0
  392. data/lib/pubid/ogc/identifier.rb +77 -0
  393. data/lib/pubid/ogc/identifiers/document.rb +26 -0
  394. data/lib/pubid/ogc/identifiers.rb +10 -0
  395. data/lib/pubid/ogc/parser.rb +40 -0
  396. data/lib/pubid/ogc/renderer.rb +33 -0
  397. data/lib/pubid/ogc/urn_generator.rb +14 -0
  398. data/lib/pubid/ogc/urn_parser.rb +22 -0
  399. data/lib/pubid/ogc.rb +71 -0
  400. data/lib/pubid/oiml/builder.rb +31 -7
  401. data/lib/pubid/oiml/identifier.rb +9 -1
  402. data/lib/pubid/oiml/parser.rb +11 -12
  403. data/lib/pubid/oiml/renderer.rb +13 -13
  404. data/lib/pubid/oiml/single_identifier.rb +20 -0
  405. data/lib/pubid/oiml/supplement_identifier.rb +9 -9
  406. data/lib/pubid/oiml/urn_parser.rb +23 -6
  407. data/lib/pubid/oiml.rb +4 -0
  408. data/lib/pubid/omg/builder.rb +19 -0
  409. data/lib/pubid/omg/identifier.rb +44 -0
  410. data/lib/pubid/omg/identifiers/specification.rb +32 -0
  411. data/lib/pubid/omg/identifiers.rb +9 -0
  412. data/lib/pubid/omg/parser.rb +40 -0
  413. data/lib/pubid/omg/renderer.rb +19 -0
  414. data/lib/pubid/omg.rb +51 -0
  415. data/lib/pubid/parsers/mr_string.rb +101 -7
  416. data/lib/pubid/plateau/builder.rb +8 -4
  417. data/lib/pubid/plateau/identifier.rb +1 -3
  418. data/lib/pubid/plateau/identifiers/base.rb +1 -6
  419. data/lib/pubid/plateau/identifiers/handbook.rb +1 -1
  420. data/lib/pubid/plateau/identifiers/technical_report.rb +1 -1
  421. data/lib/pubid/plateau/identifiers.rb +0 -1
  422. data/lib/pubid/plateau/parser.rb +15 -5
  423. data/lib/pubid/plateau/renderer.rb +1 -1
  424. data/lib/pubid/plateau/supplement_identifier.rb +11 -11
  425. data/lib/pubid/plateau.rb +4 -4
  426. data/lib/pubid/renderers/human_readable.rb +3 -1
  427. data/lib/pubid/renderers/mr_string.rb +46 -4
  428. data/lib/pubid/renderers/supplement_renderer.rb +1 -1
  429. data/lib/pubid/rendering/supplement.rb +4 -4
  430. data/lib/pubid/sae/builder.rb +1 -1
  431. data/lib/pubid/sae/identifier.rb +1 -3
  432. data/lib/pubid/sae/identifiers/base.rb +10 -7
  433. data/lib/pubid/sae/identifiers.rb +0 -1
  434. data/lib/pubid/sae.rb +3 -3
  435. data/lib/pubid/tgpp/builder.rb +41 -0
  436. data/lib/pubid/tgpp/identifier.rb +91 -0
  437. data/lib/pubid/tgpp/identifiers/technical_report.rb +32 -0
  438. data/lib/pubid/tgpp/identifiers/technical_specification.rb +33 -0
  439. data/lib/pubid/tgpp/identifiers.rb +11 -0
  440. data/lib/pubid/tgpp/parser.rb +62 -0
  441. data/lib/pubid/tgpp/renderer.rb +36 -0
  442. data/lib/pubid/tgpp/urn_generator.rb +20 -0
  443. data/lib/pubid/tgpp/urn_parser.rb +28 -0
  444. data/lib/pubid/tgpp.rb +78 -0
  445. data/lib/pubid/type_resolver.rb +59 -0
  446. data/lib/pubid/un/builder.rb +42 -0
  447. data/lib/pubid/un/identifier.rb +37 -0
  448. data/lib/pubid/un/identifiers/document.rb +24 -0
  449. data/lib/pubid/un/identifiers.rb +9 -0
  450. data/lib/pubid/un/parser.rb +25 -0
  451. data/lib/pubid/un/renderer.rb +11 -0
  452. data/lib/pubid/un.rb +46 -0
  453. data/lib/pubid/version.rb +1 -1
  454. data/lib/pubid/w3c/builder.rb +55 -0
  455. data/lib/pubid/w3c/identifier.rb +101 -0
  456. data/lib/pubid/w3c/identifiers/candidate_recommendation.rb +31 -0
  457. data/lib/pubid/w3c/identifiers/candidate_recommendation_draft.rb +31 -0
  458. data/lib/pubid/w3c/identifiers/draft_note.rb +31 -0
  459. data/lib/pubid/w3c/identifiers/note.rb +30 -0
  460. data/lib/pubid/w3c/identifiers/obsolete_recommendation.rb +31 -0
  461. data/lib/pubid/w3c/identifiers/proposed_edited_recommendation.rb +31 -0
  462. data/lib/pubid/w3c/identifiers/proposed_recommendation.rb +31 -0
  463. data/lib/pubid/w3c/identifiers/recommendation.rb +31 -0
  464. data/lib/pubid/w3c/identifiers/standard.rb +12 -0
  465. data/lib/pubid/w3c/identifiers/superseded_recommendation.rb +31 -0
  466. data/lib/pubid/w3c/identifiers/working_draft.rb +30 -0
  467. data/lib/pubid/w3c/identifiers.rb +25 -0
  468. data/lib/pubid/w3c/parser.rb +43 -0
  469. data/lib/pubid/w3c/renderer.rb +35 -0
  470. data/lib/pubid/w3c/urn_generator.rb +18 -0
  471. data/lib/pubid/w3c/urn_parser.rb +36 -0
  472. data/lib/pubid/w3c.rb +67 -0
  473. data/lib/pubid/xsf/builder.rb +17 -0
  474. data/lib/pubid/xsf/identifier.rb +62 -0
  475. data/lib/pubid/xsf/identifiers/xep.rb +31 -0
  476. data/lib/pubid/xsf/identifiers.rb +9 -0
  477. data/lib/pubid/xsf/parser.rb +25 -0
  478. data/lib/pubid/xsf/renderer.rb +23 -0
  479. data/lib/pubid/xsf/urn_generator.rb +13 -0
  480. data/lib/pubid/xsf/urn_parser.rb +18 -0
  481. data/lib/pubid/xsf.rb +76 -0
  482. data/lib/pubid.rb +25 -1
  483. metadata +205 -4
  484. data/lib/pubid/cie/components/code.rb +0 -80
  485. data/lib/pubid/iala/identifiers/base.rb +0 -14
@@ -8,7 +8,7 @@ module Pubid
8
8
  attr_accessor :original_input
9
9
  attr_reader :identifier_class
10
10
 
11
- def initialize(identifier_class = Identifiers::Base)
11
+ def initialize(identifier_class = Identifier)
12
12
  @identifier_class = identifier_class
13
13
  end
14
14
 
@@ -16,46 +16,58 @@ module Pubid
16
16
  # @param parsed [Hash, Array] the parsed identifier data
17
17
  # @return [identifier] the constructed identifier object
18
18
  def build(parsed)
19
+ # Parslet returns a top-level *array* of hashes when a repeated capture
20
+ # (e.g. the two subparts in "7.4-3-2") stops the sibling captures from
21
+ # hash-merging. The dispatch checks below need hash access, so read them
22
+ # off a merged view (`d`); build_single_identifier re-handles the array
23
+ # itself (preserving every subpart), so it still gets the original.
24
+ d = parsed.is_a?(Array) ? merge_parsed_array(parsed) : parsed
25
+
19
26
  # Handle CSA dual published patterns
20
- if parsed[:ieee_portion] && parsed[:csa_portion]
27
+ if d[:ieee_portion] && d[:csa_portion]
21
28
  return build_csa_dual_published(parsed)
22
29
  end
23
30
 
31
+ # Handle the historical IEEE/IPCEA cable designation (S-135)
32
+ if d[:s_number]
33
+ return build_s_designation(parsed)
34
+ end
35
+
24
36
  # Handle combined AIEE identifiers (from "Nos X and Y" preprocessing)
25
- if parsed[:first_aiee] && parsed[:second_aiee]
37
+ if d[:first_aiee] && d[:second_aiee]
26
38
  return build_combined_aiee(parsed)
27
39
  end
28
40
 
29
41
  # Handle dual published patterns
30
- if parsed[:first] && parsed[:second]
42
+ if d[:first] && d[:second]
31
43
  return build_dual_published(parsed)
32
44
  end
33
45
 
34
46
  # Handle IEC/IEEE copublished patterns
35
- if parsed[:content]
47
+ if d[:content]
36
48
  return build_iec_ieee_copublished(parsed)
37
49
  end
38
50
 
39
51
  # Handle NESC identifiers (National Electrical Safety Code)
40
- if parsed[:nesc]
52
+ if d[:nesc]
41
53
  nesc_builder = Nesc::Builder.new
42
- return nesc_builder.build(parsed[:nesc])
54
+ return nesc_builder.build(d[:nesc])
43
55
  end
44
56
 
45
57
  # Handle AIEE identifiers (American Institute of Electrical Engineers)
46
- if parsed[:aiee]
58
+ if d[:aiee]
47
59
  aiee_builder = Aiee::Builder.new
48
- return aiee_builder.build(parsed[:aiee])
60
+ return aiee_builder.build(d[:aiee])
49
61
  end
50
62
 
51
63
  # Handle IRE identifiers (Institute of Radio Engineers)
52
- if parsed[:ire]
64
+ if d[:ire]
53
65
  ire_builder = Ire::Builder.new
54
- return ire_builder.build(parsed[:ire])
66
+ return ire_builder.build(d[:ire])
55
67
  end
56
68
 
57
69
  # Handle IEEE/ASTM SI/PSI identifiers (Système International)
58
- if parsed[:si_type]
70
+ if d[:si_type]
59
71
  return build_si_psi_identifier(parsed)
60
72
  end
61
73
 
@@ -112,13 +124,59 @@ module Pubid
112
124
  end
113
125
 
114
126
  Identifiers::IecIeeeCopublished.new(
115
- copublished_number: copublished_number,
116
127
  draft_info: draft_info,
117
128
  iec_year: iec_year,
118
129
  date_info: date_info,
130
+ **copublished_structured(copublished_number),
119
131
  )
120
132
  end
121
133
 
134
+ # Decompose an IEC/IEEE `copublished_number` into the split index columns
135
+ # that reconstruct it losslessly (IecIeeeCopublished#copublished_number is
136
+ # the inverse). First peel a trailing *publication year* (19xx/20xx with
137
+ # its `-`/`:` separator) into `year`/`year_sep` — a bare 4-digit strip
138
+ # would wrongly eat a part like "60076.57-1202" (1202 is a part, not a
139
+ # year). Then tokenise the remainder on `.`/`-`, keeping each part's
140
+ # leading separator (a single code mixes them). So `number` narrows to the
141
+ # bucket, `parts` matches within it, and `separators` renders it back.
142
+ # `year_sep` is omitted when the default `-`. Returns empty for no number.
143
+ def copublished_structured(copublished_number)
144
+ return {} if copublished_number.nil? || copublished_number.empty?
145
+
146
+ remainder, year, year_sep = peel_copublished_year(copublished_number)
147
+ tokens = remainder.scan(/\A[^.\-]+|[.\-][^.\-]+/)
148
+ { number: tokens.shift }
149
+ .merge(copublished_parts(tokens))
150
+ .merge(copublished_year(year, year_sep))
151
+ end
152
+
153
+ # The `parts`/`separators` columns from the tokens after the number.
154
+ def copublished_parts(tokens)
155
+ return {} if tokens.empty?
156
+
157
+ { parts: tokens.map { |t| t[1..] }, separators: tokens.map { |t| t[0] } }
158
+ end
159
+
160
+ # The `year`/`year_sep` columns; `year_sep` is omitted when the default `-`.
161
+ def copublished_year(year, year_sep)
162
+ return {} unless year
163
+
164
+ year_sep == "-" ? { year: year } : { year: year, year_sep: year_sep }
165
+ end
166
+
167
+ # Peel a trailing publication year (19xx/20xx with its `-`/`:` separator)
168
+ # off a copublished number, returning [remainder, year, year_sep].
169
+ def peel_copublished_year(copublished_number)
170
+ return [copublished_number, nil, nil] unless
171
+ copublished_number =~ /([-:])((?:19|20)\d\d)\z/
172
+
173
+ year_sep = Regexp.last_match(1)
174
+ year = Regexp.last_match(2)
175
+ trimmed = copublished_number[0...(copublished_number.length -
176
+ year_sep.length - year.length)]
177
+ [trimmed, year, year_sep]
178
+ end
179
+
122
180
  # Build dual published identifier
123
181
  def build_dual_published(parsed)
124
182
  first_id = build_single_identifier(parsed[:first])
@@ -153,6 +211,33 @@ module Pubid
153
211
  )
154
212
  end
155
213
 
214
+ # Build the historical IEEE/IPCEA cable designation (e.g. S-135).
215
+ #
216
+ # The "S-135" designation is modelled as a plain Standard whose code holds
217
+ # the whole "S-135" string as `number` (prefix nil). It is passed as the
218
+ # `number:` split column directly — NOT as `code:` — so it bypasses
219
+ # Components::Code.parse, which would peel "S" as a prefix and leave an
220
+ # empty number (breaking the relaton index key and the round-trip). This
221
+ # keeps `root.number` == "S-135" and renders the dash back verbatim.
222
+ #
223
+ # The "/IPCEA …" slash co-designation is stored verbatim in `crossref`
224
+ # (rendered as-is); the "(IPCEA …)" parenthetical variant flows through
225
+ # the normal `parenthetical_content` path.
226
+ def build_s_designation(parsed)
227
+ attributes = {
228
+ number: extract_value(parsed[:s_number]),
229
+ publisher: extract_value(parsed[:publisher]) || "IEEE",
230
+ typed_stage: Pubid::Ieee.locate_stage("Std"),
231
+ }
232
+
233
+ if parsed[:ipcea_copub]
234
+ attributes[:crossref] = extract_value(parsed[:ipcea_copub])
235
+ end
236
+ handle_parameters(parsed, attributes)
237
+
238
+ Identifiers::Standard.new(**attributes)
239
+ end
240
+
156
241
  # Build single identifier
157
242
  def build_single_identifier(parsed, building_secondary: false)
158
243
  # Parslet can return array of hashes - merge them
@@ -164,19 +249,49 @@ module Pubid
164
249
  return build_multi_numbered_identifier(parsed_hash)
165
250
  end
166
251
 
167
- # Handle corrigendum supplements (check for base_identifier + cor_number)
252
+ # Handle corrigendum supplements (check for base + cor_number)
168
253
  # This enables recursive base identifier parsing like ISO/IEC Amendment
169
- if parsed_hash[:base_identifier] && parsed_hash[:cor_number]
254
+ if parsed_hash[:base] && parsed_hash[:cor_number]
170
255
  return build_corrigendum_supplement(parsed_hash)
171
256
  end
172
257
 
173
- # Handle interpretation supplements (check for base_identifier + int_year)
174
- if parsed_hash[:base_identifier] && (parsed_hash[:int_year] || parsed_hash[:interpretation])
258
+ # Handle the flat draft + corrigendum form (relaton's `…/D-N/CorM-YYYY`,
259
+ # normalized to `…/Cor M-YYYY/D N`): the parser emits a top-level
260
+ # `corrigendum` hash alongside `draft`/number with NO `:base` subtree, so
261
+ # the supplement path above is skipped. Rebuild a real base standard
262
+ # (carrying the draft) and wrap it, so `to_s` renders correctly from a
263
+ # fresh parse and after a from_hash round-trip. The `!parsed_hash[:base]`
264
+ # guard keeps this disjoint from the nested-base branch above; a
265
+ # `:base` + nested-`corrigendum`-hash shape is not produced by the
266
+ # grammar, so it would fall through to `extract_attributes` (as before).
267
+ if parsed_hash[:corrigendum].is_a?(Hash) && !parsed_hash[:base]
268
+ return build_flat_corrigendum(parsed_hash)
269
+ end
270
+
271
+ # Handle amendment supplements — mirrors the corrigendum path above.
272
+ # Every amendment the grammar produces is FLAT (a top-level `amendment`
273
+ # hash, no `:base`): the `amendment` rule only ever appears inline via
274
+ # `amendment.maybe`, so `build_flat_amendment` carries all real traffic
275
+ # (`IEEE Std …/Amd N-YYYY` and `ISO/IEC/IEEE 8802.x/AmdN-YYYY`). The
276
+ # recursive-base branch below is kept for structural parity with
277
+ # corrigendum (which has a dedicated `corrigendum_identifier` rule
278
+ # emitting a `:base` + top-level `:amd_number`-style shape); no amendment
279
+ # grammar rule emits it today, so it would only fire if one were added.
280
+ if parsed_hash[:base] && parsed_hash[:amd_number]
281
+ return build_amendment_supplement(parsed_hash)
282
+ end
283
+
284
+ if parsed_hash[:amendment].is_a?(Hash) && !parsed_hash[:base]
285
+ return build_flat_amendment(parsed_hash)
286
+ end
287
+
288
+ # Handle interpretation supplements (check for base + int_year)
289
+ if parsed_hash[:base] && (parsed_hash[:int_year] || parsed_hash[:interpretation])
175
290
  return build_interpretation_supplement(parsed_hash)
176
291
  end
177
292
 
178
- # Handle conformance supplements (check for base_identifier + conf_number)
179
- if parsed_hash[:base_identifier] && parsed_hash[:conf_number]
293
+ # Handle conformance supplements (check for base + conf_number)
294
+ if parsed_hash[:base] && parsed_hash[:conf_number]
180
295
  return build_conformance_supplement(parsed_hash)
181
296
  end
182
297
 
@@ -194,16 +309,32 @@ module Pubid
194
309
 
195
310
  # Route to appropriate identifier class based on content
196
311
  identifier_class = determine_identifier_class(attributes)
312
+ rename_supplement_keys(attributes)
197
313
  identifier_class.new(**attributes)
198
314
  end
199
315
 
200
- # Build corrigendum supplement with recursive base parsing
201
- # @param parsed_hash [Hash] parsed data with base and supplement info
202
- # @return [Identifiers::Corrigendum] corrigendum identifier
203
- def build_corrigendum_supplement(parsed_hash)
204
- # Reconstruct base identifier string from parsed components
316
+ # Supplement wrappers (Corrigendum/Amendment/Conformance/Interpretation)
317
+ # store their ordinal/year under the uniform `number`/`year`, but the parse
318
+ # tree and class-routing above key on the distinct
319
+ # `cor_*`/`amd_*`/`conf_*`/`int_year` names. Translate them **after**
320
+ # routing, just before construction. Guards ensure a plain standard's own
321
+ # `number`/`year` are never overwritten (it carries none of these keys).
322
+ def rename_supplement_keys(attributes)
323
+ ordinal = attributes.delete(:cor_number) ||
324
+ attributes.delete(:amd_number) || attributes.delete(:conf_number)
325
+ attributes[:number] = ordinal if ordinal
326
+ year = attributes.delete(:cor_year) || attributes.delete(:amd_year) ||
327
+ attributes.delete(:conf_year) || attributes.delete(:int_year)
328
+ attributes[:year] = year if year
329
+ end
330
+
331
+ # Reconstruct the base identifier string from a parsed `:base` subtree
332
+ # (publisher/copublishers + type + number/part/subpart/year), so it can be
333
+ # recursively re-parsed by the supplement builders (corrigendum/amendment).
334
+ # @param base_data [Hash] the `:base` subtree
335
+ # @return [String] e.g. "IEEE Std 802.1AC-2016" or "535-2013"
336
+ def reconstruct_base_string(base_data)
205
337
  base_parts = []
206
- base_data = parsed_hash[:base_identifier]
207
338
 
208
339
  # Extract publisher
209
340
  if base_data[:publishers]
@@ -256,22 +387,89 @@ module Pubid
256
387
  end
257
388
 
258
389
  base_parts << number_str
390
+ base_parts.join(" ")
391
+ end
259
392
 
260
- # Build base string and recursively parse it
261
- base_string = base_parts.join(" ")
262
-
263
- # Recursively parse base identifier using Base.parse
264
- base_identifier = Identifiers::Base.parse(base_string)
393
+ # Build corrigendum supplement with recursive base parsing
394
+ # @param parsed_hash [Hash] parsed data with base and supplement info
395
+ # @return [Identifiers::Corrigendum] corrigendum identifier
396
+ def build_corrigendum_supplement(parsed_hash)
397
+ # Recursively parse the base identifier reconstructed from the subtree.
398
+ base = Identifier.parse(reconstruct_base_string(parsed_hash[:base]))
265
399
 
266
- # Extract corrigendum attributes
400
+ # Extract corrigendum attributes (parse-tree keys are cor_*; the
401
+ # identifier stores them as the uniform `number`/`year`).
267
402
  cor_number = extract_value(parsed_hash[:cor_number])
268
403
  cor_year = extract_value(parsed_hash[:cor_year])
269
404
 
270
405
  # Create Corrigendum with parsed base
271
406
  Identifiers::Corrigendum.new(
272
- base_identifier: base_identifier,
273
- cor_number: cor_number,
274
- cor_year: cor_year,
407
+ base: base,
408
+ number: cor_number,
409
+ year: cor_year,
410
+ )
411
+ end
412
+
413
+ # Build a Corrigendum from the flat parse tree produced by the combined
414
+ # `…/Cor M-YYYY/D N` form, where the corrigendum lives in a top-level
415
+ # `corrigendum` hash and there is no `:base` subtree. Peel the corrigendum
416
+ # off, build the remaining attributes into the base standard (with its
417
+ # draft) via the normal single-identifier path, then wrap.
418
+ # @param parsed_hash [Hash] flat parsed data with a :corrigendum hash
419
+ # @return [Identifiers::Corrigendum]
420
+ def build_flat_corrigendum(parsed_hash)
421
+ cor_data = parsed_hash[:corrigendum]
422
+ cor_number = extract_value(cor_data[:cor_number])
423
+ cor_year = extract_value(cor_data[:cor_year]) if cor_data[:cor_year]
424
+
425
+ base_hash = parsed_hash.reject { |key, _| key == :corrigendum }
426
+ base = build_single_identifier(base_hash)
427
+
428
+ Identifiers::Corrigendum.new(
429
+ base: base,
430
+ number: cor_number,
431
+ year: cor_year,
432
+ )
433
+ end
434
+
435
+ # Build amendment supplement with recursive base parsing. Mirrors
436
+ # {#build_corrigendum_supplement} — reconstructs the base identifier
437
+ # string, recursively parses it, and wraps it in an Amendment.
438
+ # @param parsed_hash [Hash] parsed data with base and amendment info
439
+ # @return [Identifiers::Amendment] amendment identifier
440
+ def build_amendment_supplement(parsed_hash)
441
+ base = Identifier.parse(reconstruct_base_string(parsed_hash[:base]))
442
+
443
+ amd_number = extract_value(parsed_hash[:amd_number])
444
+ amd_year = extract_value(parsed_hash[:amd_year])
445
+
446
+ Identifiers::Amendment.new(
447
+ base: base,
448
+ number: amd_number,
449
+ year: amd_year,
450
+ )
451
+ end
452
+
453
+ # Build an Amendment from the flat parse tree produced by the common
454
+ # `…/Amd N-YYYY` form, where the amendment lives in a top-level
455
+ # `amendment` hash and there is no `:base` subtree. Peel the amendment
456
+ # off, build the remaining attributes into the base standard via the
457
+ # normal single-identifier path, then wrap. Mirrors
458
+ # {#build_flat_corrigendum}.
459
+ # @param parsed_hash [Hash] flat parsed data with an :amendment hash
460
+ # @return [Identifiers::Amendment]
461
+ def build_flat_amendment(parsed_hash)
462
+ amd_data = parsed_hash[:amendment]
463
+ amd_number = extract_value(amd_data[:amd_number])
464
+ amd_year = extract_value(amd_data[:amd_year]) if amd_data[:amd_year]
465
+
466
+ base_hash = parsed_hash.reject { |key, _| key == :amendment }
467
+ base = build_single_identifier(base_hash)
468
+
469
+ Identifiers::Amendment.new(
470
+ base: base,
471
+ number: amd_number,
472
+ year: amd_year,
275
473
  )
276
474
  end
277
475
 
@@ -281,7 +479,7 @@ module Pubid
281
479
  def build_interpretation_supplement(parsed_hash)
282
480
  # Reconstruct base identifier string from parsed components (same logic as corrigendum)
283
481
  base_parts = []
284
- base_data = parsed_hash[:base_identifier]
482
+ base_data = parsed_hash[:base]
285
483
 
286
484
  # Extract publisher
287
485
  if base_data[:publishers]
@@ -337,15 +535,15 @@ module Pubid
337
535
  base_string = base_parts.join(" ")
338
536
 
339
537
  # Recursively parse base identifier using Base.parse
340
- base_identifier = Identifiers::Base.parse(base_string)
538
+ base = Identifier.parse(base_string)
341
539
 
342
540
  # Extract interpretation attributes
343
541
  int_year = extract_value(parsed_hash[:int_year])
344
542
 
345
- # Create InterpretationIdentifier with parsed base
543
+ # Create InterpretationIdentifier with parsed base (int_year -> year)
346
544
  Identifiers::InterpretationIdentifier.new(
347
- base_identifier: base_identifier,
348
- int_year: int_year,
545
+ base: base,
546
+ year: int_year,
349
547
  )
350
548
  end
351
549
 
@@ -355,7 +553,7 @@ module Pubid
355
553
  def build_conformance_supplement(parsed_hash)
356
554
  # Reconstruct base identifier string from parsed components (same logic as corrigendum)
357
555
  base_parts = []
358
- base_data = parsed_hash[:base_identifier]
556
+ base_data = parsed_hash[:base]
359
557
 
360
558
  # Extract publisher
361
559
  if base_data[:publishers]
@@ -411,17 +609,17 @@ module Pubid
411
609
  base_string = base_parts.join(" ")
412
610
 
413
611
  # Recursively parse base identifier using Base.parse
414
- base_identifier = Identifiers::Base.parse(base_string)
612
+ base = Identifier.parse(base_string)
415
613
 
416
614
  # Extract conformance attributes
417
615
  conf_number = extract_value(parsed_hash[:conf_number])
418
616
  conf_year = extract_value(parsed_hash[:conf_year])
419
617
 
420
- # Create ConformanceIdentifier with parsed base
618
+ # Create ConformanceIdentifier with parsed base (conf_* -> number/year)
421
619
  Identifiers::ConformanceIdentifier.new(
422
- base_identifier: base_identifier,
423
- conf_number: conf_number,
424
- conf_year: conf_year,
620
+ base: base,
621
+ number: conf_number,
622
+ year: conf_year,
425
623
  )
426
624
  end
427
625
 
@@ -445,8 +643,33 @@ module Pubid
445
643
  end
446
644
  attributes[:code] = code_str
447
645
 
448
- # Extract year
449
- attributes[:year] = extract_value(parsed[:year]) if parsed[:year]
646
+ # Extract year (and optional numeric month, e.g. the -MM of a historical
647
+ # "…-2018-05" ISO-stage date). The joint ISO/IEC rules also use the
648
+ # `edition` rule, whose year is captured as :edition_year (parser.rb);
649
+ # it is promoted to the identity only when no base :year was captured.
650
+ if parsed[:year]
651
+ attributes[:year] = extract_value(parsed[:year])
652
+ elsif parsed[:edition_year]
653
+ attributes[:year] = extract_value(parsed[:edition_year])
654
+ end
655
+ attributes[:month] = extract_value(parsed[:month]) if parsed[:month]
656
+
657
+ # Extract edition, from relaton's "/E-<n>" suffix (normalized to
658
+ # "Edition <n>.0"). nil-residue hand-off item 1.
659
+ attributes[:edition] = extract_value(parsed[:edition]) if parsed[:edition]
660
+ if parsed[:edition_month]
661
+ attributes[:edition_month] = extract_value(parsed[:edition_month])
662
+ end
663
+
664
+ # Extract draft version if present (e.g., D8 from /D8). This applies to
665
+ # both the ISO-led (bucket 5: "…FDIS P15289/D-3-2017") and IEEE-led
666
+ # forms, so it lives before the lead-party split.
667
+ if parsed[:draft_version]
668
+ draft_ver = extract_value(parsed[:draft_version])
669
+ # Remove leading 'D' if present since draft_version already has it
670
+ draft_ver = draft_ver.sub(/^D/, "") if draft_ver
671
+ attributes[:ieee_draft] = "D#{draft_ver}" if draft_ver
672
+ end
450
673
 
451
674
  # Detect lead party based on pattern
452
675
  if parsed[:iso_stage]
@@ -464,14 +687,6 @@ module Pubid
464
687
  # IEEE format - lead party is IEEE
465
688
  attributes[:lead_party] = "IEEE"
466
689
 
467
- # Extract draft version if present (e.g., D8 from /D8)
468
- if parsed[:draft_version]
469
- draft_ver = extract_value(parsed[:draft_version])
470
- # Remove leading 'D' if present since draft_version already has it
471
- draft_ver = draft_ver.sub(/^D/, "") if draft_ver
472
- attributes[:ieee_draft] = "D#{draft_ver}" if draft_ver
473
- end
474
-
475
690
  # Mark as project (P prefix)
476
691
  attributes[:type] = "P"
477
692
 
@@ -616,6 +831,11 @@ module Pubid
616
831
  return Identifiers::Corrigendum
617
832
  end
618
833
 
834
+ # Check for amendment supplements (structural: has amd_number)
835
+ if attributes[:amd_number]
836
+ return Identifiers::Amendment
837
+ end
838
+
619
839
  # Check for multi-numbered standards (structural: has crossref or joint patterns)
620
840
  # These are handled at the build() method level, not here
621
841
  # (cross-reference patterns like /C62.22.1-1996 and joint standards)
@@ -625,13 +845,16 @@ module Pubid
625
845
  return Identifiers::AdoptedStandard
626
846
  end
627
847
 
628
- # Check for redline standards (structural: has redline flag)
629
- if attributes[:redline]
630
- return Identifiers::RedlinedStandard
631
- end
848
+ # A redline is a plain Standard carrying `redline: true` (a distinct
849
+ # document from its base) — NOT the RedlinedStandard wrapper, which
850
+ # expects a nested `base` the flat build path never sets. render_base
851
+ # restores the " - Redline" suffix from the flag.
632
852
 
633
- # Default to base identifier
634
- Identifiers::Base
853
+ # Default to the concrete Standard leaf (a plain published standard).
854
+ # Routing to a leaf — rather than instantiating the shared base
855
+ # Ieee::Identifier — is what lets `number` be a reliable :string index
856
+ # key (see Identifiers::Standard).
857
+ Identifiers::Standard
635
858
  end
636
859
 
637
860
  # Detect lead party for joint development identifiers
@@ -777,6 +1000,26 @@ module Pubid
777
1000
  if parsed[:year]
778
1001
  year_str = extract_value(parsed[:year])
779
1002
  attributes[:year] = year_str
1003
+ elsif parsed[:edition_year]
1004
+ # The `edition` rule captures its own year under :edition_year (not
1005
+ # :year) so it never collides with the base -YYYY (see parser.rb).
1006
+ # With no base year the edition's year IS the document date and is
1007
+ # promoted to the identity (e.g. "IEEE Std 802.11 Edition 3.0-2009"
1008
+ # -> year 2009). When a base year exists it wins above and
1009
+ # :edition_year is dropped — the edition ordinal is still kept.
1010
+ attributes[:year] = extract_value(parsed[:edition_year])
1011
+ elsif parsed[:trailing_year]
1012
+ # No base -YYYY identity year: the trailing "Month YYYY" IS the
1013
+ # document date (e.g. "IEEE Std 519, May 1993"), so promote it.
1014
+ # When a base year exists it wins (the IEEE approval/identity year)
1015
+ # and the trailing date is intentionally dropped rather than
1016
+ # fabricating a wrong month/year pairing. The trailing captures use
1017
+ # distinct keys (:trailing_month/:trailing_year) so they never collide
1018
+ # with the base :year (Parslet no longer warns "Duplicate subtrees").
1019
+ attributes[:year] = extract_value(parsed[:trailing_year])
1020
+ if parsed[:trailing_month]
1021
+ attributes[:month] = extract_value(parsed[:trailing_month])
1022
+ end
780
1023
  end
781
1024
 
782
1025
  # Extract edition_month if parsed separately
@@ -813,6 +1056,12 @@ module Pubid
813
1056
  # Handle draft (can be complex)
814
1057
  handle_draft(parsed, attributes)
815
1058
 
1059
+ # Default draft version 1 when the type indicates a Draft but the
1060
+ # parser found no explicit version (issue #205). Real-world IEEE
1061
+ # drafts always have at least version 1; the missing suffix is a
1062
+ # source-data omission, not an undrafted document.
1063
+ apply_default_draft_version(attributes, type_value)
1064
+
816
1065
  # Handle corrigendum
817
1066
  handle_corrigendum(parsed, attributes)
818
1067
 
@@ -927,15 +1176,36 @@ module Pubid
927
1176
  attributes[key] = value if value
928
1177
  end
929
1178
 
1179
+ # Default draft version to "1" when the identifier type indicates a
1180
+ # Draft but the parser found no explicit version (issue #205).
1181
+ def apply_default_draft_version(attributes, type_value)
1182
+ return if attributes[:draft] || attributes[:draft_obj]
1183
+ return unless type_value.to_s == "Draft Std"
1184
+
1185
+ attributes[:draft] = "D1"
1186
+ # Carry the identifier's year into the draft object so the renderer
1187
+ # doesn't drop it (the renderer suppresses the `-YYYY` suffix when
1188
+ # a draft is attached, expecting the year to live inside /D...).
1189
+ attributes[:draft_obj] =
1190
+ Components::Draft.new(version: "1", year: attributes[:year])
1191
+ end
1192
+
930
1193
  # Handle draft information
931
1194
  def handle_draft(parsed, attributes)
932
1195
  draft_data = parsed[:draft] || parsed[:digit_draft]
933
1196
  return unless draft_data
934
1197
 
935
- # Draft can be an array of hash elements or a single hash
1198
+ # Draft can be an array of hash elements or a single hash. The grammar's
1199
+ # `draft_version.repeat(1,2)` splits a dotted designator like "1.2.6"
1200
+ # into ["1.2", ".6"], so DON'T let a plain merge overwrite it (that
1201
+ # dropped every part but the last — "1.2.6" -> ".6"). Collect ALL the
1202
+ # draft_version parts into an array (the version extraction below joins
1203
+ # them back into one verbatim string) and merge the remaining keys.
936
1204
  if draft_data.is_a?(Array)
937
- # Merge all elements in the array
938
- merged = draft_data.inject({}) { |result, elem| result.merge(elem) }
1205
+ hashes = draft_data.select { |e| e.is_a?(Hash) }
1206
+ versions = hashes.filter_map { |e| e[:draft_version] }
1207
+ merged = hashes.inject({}) { |result, elem| result.merge(elem) }
1208
+ merged[:draft_version] = versions unless versions.empty?
939
1209
  draft_data = merged
940
1210
  end
941
1211
 
@@ -955,6 +1225,9 @@ module Pubid
955
1225
  else
956
1226
  extract_value(dv)
957
1227
  end
1228
+ # Normalize a leading hyphen ("/D-3.0" -> "3.0"); the draft
1229
+ # designator is otherwise held verbatim so no digit is lost.
1230
+ version = version.sub(/\A-/, "") if version
958
1231
  end
959
1232
 
960
1233
  revision = extract_value(draft_data[:revision]) if draft_data[:revision]
@@ -1183,7 +1456,7 @@ module Pubid
1183
1456
 
1184
1457
  # Parse a list of identifier strings into identifier objects
1185
1458
  # @param list_data [Hash, Array, nil] the parsed identifier list data
1186
- # @return [Array<Identifiers::Base>] array of parsed identifier objects
1459
+ # @return [Array<Identifier>] array of parsed identifier objects
1187
1460
  def parse_identifier_list(list_data)
1188
1461
  return [] unless list_data
1189
1462
 
@@ -1192,12 +1465,12 @@ module Pubid
1192
1465
 
1193
1466
  # Recursively parse each identifier string
1194
1467
  id_strings.map do |id_str|
1195
- # Use Identifiers::Base.parse for recursive parsing
1196
- Identifiers::Base.parse(id_str)
1468
+ # Use Identifier.parse for recursive parsing
1469
+ Identifier.parse(id_str)
1197
1470
  rescue Parslet::ParseFailed
1198
1471
  # If parsing fails, create minimal identifier with just the string
1199
1472
  # This maintains graceful degradation
1200
- Identifiers::Base.new(parenthetical_content: id_str)
1473
+ Identifier.new(parenthetical_content: id_str)
1201
1474
  end
1202
1475
  end
1203
1476