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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (672) hide show
  1. checksums.yaml +4 -4
  2. data/README.adoc +33 -11
  3. data/conformance/pending.yaml +4 -0
  4. data/data/bipm/update_codes.yaml +9 -0
  5. data/data/iec/update_codes.yaml +3 -0
  6. data/data/ieee/update_codes.yaml +107 -1
  7. data/{archived-gems/pubid-nist → data/nist}/series.yaml +34 -0
  8. data/data/nist/update_codes.yaml +8 -0
  9. data/lib/pubid/adobe/builder.rb +52 -0
  10. data/lib/pubid/adobe/identifier.rb +47 -0
  11. data/lib/pubid/adobe/identifiers/publication.rb +31 -0
  12. data/lib/pubid/adobe/identifiers/tech_note.rb +32 -0
  13. data/lib/pubid/adobe/identifiers.rb +10 -0
  14. data/lib/pubid/adobe/parser.rb +136 -0
  15. data/lib/pubid/adobe/renderer.rb +39 -0
  16. data/lib/pubid/adobe/urn_generator.rb +42 -0
  17. data/lib/pubid/adobe/urn_parser.rb +69 -0
  18. data/lib/pubid/adobe.rb +61 -0
  19. data/lib/pubid/amca/builder.rb +4 -4
  20. data/lib/pubid/amca/identifier.rb +1 -3
  21. data/lib/pubid/amca/identifiers/base.rb +50 -33
  22. data/lib/pubid/amca/identifiers/interpretation.rb +18 -10
  23. data/lib/pubid/amca/identifiers/publication.rb +14 -10
  24. data/lib/pubid/amca/identifiers/standard.rb +3 -1
  25. data/lib/pubid/amca/identifiers.rb +0 -1
  26. data/lib/pubid/amca/parser.rb +1 -1
  27. data/lib/pubid/amca/renderer.rb +4 -4
  28. data/lib/pubid/amca/single_identifier.rb +1 -1
  29. data/lib/pubid/amca/urn_generator.rb +2 -2
  30. data/lib/pubid/amca.rb +5 -4
  31. data/lib/pubid/ansi/builder.rb +2 -2
  32. data/lib/pubid/ansi/identifier.rb +18 -0
  33. data/lib/pubid/ansi/parser.rb +1 -1
  34. data/lib/pubid/ansi/renderer.rb +2 -2
  35. data/lib/pubid/ansi.rb +5 -5
  36. data/lib/pubid/api/builder.rb +16 -2
  37. data/lib/pubid/api/identifier.rb +25 -4
  38. data/lib/pubid/api/identifiers/mpms.rb +10 -8
  39. data/lib/pubid/api/parser.rb +1 -1
  40. data/lib/pubid/api/renderer.rb +16 -7
  41. data/lib/pubid/api/single_identifier.rb +5 -12
  42. data/lib/pubid/api.rb +1 -0
  43. data/lib/pubid/ashrae/builder.rb +53 -32
  44. data/lib/pubid/ashrae/identifier.rb +3 -4
  45. data/lib/pubid/ashrae/identifiers/addenda_package.rb +7 -1
  46. data/lib/pubid/ashrae/identifiers/addendum.rb +9 -1
  47. data/lib/pubid/ashrae/identifiers/base.rb +94 -12
  48. data/lib/pubid/ashrae/identifiers/combined_addenda.rb +8 -1
  49. data/lib/pubid/ashrae/identifiers/errata.rb +9 -0
  50. data/lib/pubid/ashrae/identifiers/guideline.rb +3 -0
  51. data/lib/pubid/ashrae/identifiers/interpretation.rb +16 -0
  52. data/lib/pubid/ashrae/identifiers/standard.rb +3 -0
  53. data/lib/pubid/ashrae/identifiers.rb +0 -1
  54. data/lib/pubid/ashrae/parser.rb +37 -17
  55. data/lib/pubid/ashrae/renderer.rb +20 -20
  56. data/lib/pubid/ashrae/single_identifier.rb +1 -1
  57. data/lib/pubid/ashrae/supplement_identifier.rb +21 -6
  58. data/lib/pubid/ashrae/urn_generator.rb +8 -2
  59. data/lib/pubid/ashrae.rb +10 -4
  60. data/lib/pubid/asme/builder.rb +11 -2
  61. data/lib/pubid/asme/identifier.rb +9 -0
  62. data/lib/pubid/asme/identifiers/standard.rb +86 -2
  63. data/lib/pubid/asme/parser.rb +1 -1
  64. data/lib/pubid/asme/renderer.rb +3 -3
  65. data/lib/pubid/asme/single_identifier.rb +8 -1
  66. data/lib/pubid/asme/urn_generator.rb +2 -2
  67. data/lib/pubid/asme.rb +1 -0
  68. data/lib/pubid/astm/builder.rb +10 -3
  69. data/lib/pubid/astm/identifier.rb +9 -0
  70. data/lib/pubid/astm/identifiers/adjunct.rb +16 -1
  71. data/lib/pubid/astm/identifiers/code_number.rb +81 -0
  72. data/lib/pubid/astm/identifiers/data_series.rb +2 -0
  73. data/lib/pubid/astm/identifiers/iso_dual_published.rb +16 -0
  74. data/lib/pubid/astm/identifiers/manual.rb +10 -0
  75. data/lib/pubid/astm/identifiers/monograph.rb +2 -0
  76. data/lib/pubid/astm/identifiers/research_report.rb +10 -0
  77. data/lib/pubid/astm/identifiers/standard.rb +10 -0
  78. data/lib/pubid/astm/identifiers/technical_report.rb +2 -0
  79. data/lib/pubid/astm/identifiers/work_in_progress.rb +2 -0
  80. data/lib/pubid/astm/identifiers.rb +1 -0
  81. data/lib/pubid/astm/parser.rb +1 -1
  82. data/lib/pubid/astm/renderer.rb +1 -1
  83. data/lib/pubid/astm/single_identifier.rb +31 -1
  84. data/lib/pubid/astm/urn_generator.rb +7 -5
  85. data/lib/pubid/astm.rb +1 -0
  86. data/lib/pubid/bipm/builder.rb +163 -0
  87. data/lib/pubid/bipm/identifier.rb +285 -0
  88. data/lib/pubid/bipm/identifiers/committee_document.rb +36 -0
  89. data/lib/pubid/bipm/identifiers/guide.rb +35 -0
  90. data/lib/pubid/bipm/identifiers/meeting.rb +53 -0
  91. data/lib/pubid/bipm/identifiers/mep.rb +33 -0
  92. data/lib/pubid/bipm/identifiers/metrologia_article.rb +48 -0
  93. data/lib/pubid/bipm/identifiers/si_brochure.rb +33 -0
  94. data/lib/pubid/bipm/identifiers.rb +15 -0
  95. data/lib/pubid/bipm/parser.rb +237 -0
  96. data/lib/pubid/bipm/renderer.rb +118 -0
  97. data/lib/pubid/bipm/urn_generator.rb +58 -0
  98. data/lib/pubid/bipm/urn_parser.rb +60 -0
  99. data/lib/pubid/bipm.rb +92 -0
  100. data/lib/pubid/bsi/builder.rb +62 -52
  101. data/lib/pubid/bsi/identifier.rb +0 -1
  102. data/lib/pubid/bsi/identifiers/addendum_document.rb +2 -2
  103. data/lib/pubid/bsi/identifiers/adopted_european_norm.rb +36 -7
  104. data/lib/pubid/bsi/identifiers/adopted_international_standard.rb +7 -6
  105. data/lib/pubid/bsi/identifiers/amendment.rb +4 -4
  106. data/lib/pubid/bsi/identifiers/british_industrial_practice.rb +0 -1
  107. data/lib/pubid/bsi/identifiers/bundled_identifier.rb +10 -0
  108. data/lib/pubid/bsi/identifiers/committee_document.rb +10 -1
  109. data/lib/pubid/bsi/identifiers/consolidated_identifier.rb +6 -0
  110. data/lib/pubid/bsi/identifiers/corrigendum.rb +4 -4
  111. data/lib/pubid/bsi/identifiers/expert_commentary.rb +5 -5
  112. data/lib/pubid/bsi/identifiers/handbook.rb +0 -2
  113. data/lib/pubid/bsi/identifiers/practice_guide.rb +0 -1
  114. data/lib/pubid/bsi/identifiers/set.rb +11 -0
  115. data/lib/pubid/bsi/identifiers/standalone_amendment.rb +10 -1
  116. data/lib/pubid/bsi/identifiers/supplement_document.rb +2 -2
  117. data/lib/pubid/bsi/identifiers/value_added_publication.rb +6 -6
  118. data/lib/pubid/bsi/parser.rb +1 -1
  119. data/lib/pubid/bsi/renderer.rb +71 -59
  120. data/lib/pubid/bsi/single_identifier.rb +64 -7
  121. data/lib/pubid/bsi/urn_generator.rb +11 -2
  122. data/lib/pubid/bsi.rb +7 -1
  123. data/lib/pubid/builder/base.rb +23 -11
  124. data/lib/pubid/bundled_identifier.rb +19 -9
  125. data/lib/pubid/calconnect/builder.rb +38 -0
  126. data/lib/pubid/calconnect/identifier.rb +121 -0
  127. data/lib/pubid/calconnect/identifiers/standard.rb +29 -0
  128. data/lib/pubid/calconnect/identifiers.rb +9 -0
  129. data/lib/pubid/calconnect/parser.rb +54 -0
  130. data/lib/pubid/calconnect/renderer.rb +36 -0
  131. data/lib/pubid/calconnect/urn_generator.rb +33 -0
  132. data/lib/pubid/calconnect/urn_parser.rb +34 -0
  133. data/lib/pubid/calconnect.rb +83 -0
  134. data/lib/pubid/ccsds/builder.rb +1 -1
  135. data/lib/pubid/ccsds/identifier.rb +10 -3
  136. data/lib/pubid/ccsds/identifiers/base.rb +4 -2
  137. data/lib/pubid/ccsds/identifiers/corrigendum.rb +4 -4
  138. data/lib/pubid/ccsds/parser.rb +1 -1
  139. data/lib/pubid/ccsds/single_identifier.rb +2 -2
  140. data/lib/pubid/ccsds/supplement_identifier.rb +6 -6
  141. data/lib/pubid/ccsds/urn_generator.rb +3 -3
  142. data/lib/pubid/ccsds.rb +1 -0
  143. data/lib/pubid/cen_cenelec/builder.rb +131 -72
  144. data/lib/pubid/cen_cenelec/identifier.rb +115 -3
  145. data/lib/pubid/cen_cenelec/identifiers/adopted_european_norm.rb +27 -16
  146. data/lib/pubid/cen_cenelec/identifiers/amendment.rb +46 -6
  147. data/lib/pubid/cen_cenelec/identifiers/base.rb +8 -9
  148. data/lib/pubid/cen_cenelec/identifiers/cen_report.rb +1 -1
  149. data/lib/pubid/cen_cenelec/identifiers/cen_workshop_agreement.rb +1 -1
  150. data/lib/pubid/cen_cenelec/identifiers/cenelec_harmonization_document.rb +1 -1
  151. data/lib/pubid/cen_cenelec/identifiers/consolidated_identifier.rb +29 -14
  152. data/lib/pubid/cen_cenelec/identifiers/corrigendum.rb +47 -8
  153. data/lib/pubid/cen_cenelec/identifiers/european_norm.rb +60 -3
  154. data/lib/pubid/cen_cenelec/identifiers/european_prestandard.rb +27 -2
  155. data/lib/pubid/cen_cenelec/identifiers/european_specification.rb +1 -1
  156. data/lib/pubid/cen_cenelec/identifiers/fragment.rb +15 -5
  157. data/lib/pubid/cen_cenelec/identifiers/guide.rb +1 -1
  158. data/lib/pubid/cen_cenelec/identifiers/harmonization_document.rb +1 -1
  159. data/lib/pubid/cen_cenelec/identifiers/technical_report.rb +1 -1
  160. data/lib/pubid/cen_cenelec/identifiers/technical_specification.rb +1 -1
  161. data/lib/pubid/cen_cenelec/parser.rb +1 -1
  162. data/lib/pubid/cen_cenelec/renderer.rb +55 -60
  163. data/lib/pubid/cen_cenelec/single_identifier.rb +20 -2
  164. data/lib/pubid/cen_cenelec/urn_generator.rb +66 -44
  165. data/lib/pubid/cen_cenelec.rb +104 -82
  166. data/lib/pubid/cie/builder.rb +149 -24
  167. data/lib/pubid/cie/identifier.rb +13 -4
  168. data/lib/pubid/cie/identifiers/bundle.rb +35 -5
  169. data/lib/pubid/cie/identifiers/code_attributes.rb +52 -0
  170. data/lib/pubid/cie/identifiers/conference.rb +11 -6
  171. data/lib/pubid/cie/identifiers/corrigendum.rb +17 -23
  172. data/lib/pubid/cie/identifiers/dual_published.rb +6 -6
  173. data/lib/pubid/cie/identifiers/identical.rb +9 -9
  174. data/lib/pubid/cie/identifiers/joint_published.rb +10 -10
  175. data/lib/pubid/cie/identifiers/proceedings.rb +41 -0
  176. data/lib/pubid/cie/identifiers/standard.rb +43 -8
  177. data/lib/pubid/cie/identifiers/supplement.rb +27 -25
  178. data/lib/pubid/cie/identifiers/tutorial_bundle.rb +7 -3
  179. data/lib/pubid/cie/identifiers.rb +2 -0
  180. data/lib/pubid/cie/parser.rb +55 -6
  181. data/lib/pubid/cie/single_identifier.rb +40 -1
  182. data/lib/pubid/cie/supplement_identifier.rb +29 -16
  183. data/lib/pubid/cie/urn_generator.rb +11 -8
  184. data/lib/pubid/cie.rb +7 -2
  185. data/lib/pubid/components/date.rb +21 -5
  186. data/lib/pubid/conformance/checks.rb +72 -0
  187. data/lib/pubid/conformance/corpus/case.rb +55 -0
  188. data/lib/pubid/conformance/corpus.rb +36 -0
  189. data/lib/pubid/conformance/generator.rb +212 -0
  190. data/lib/pubid/conformance/pending.rb +63 -0
  191. data/lib/pubid/conformance/runner.rb +153 -0
  192. data/lib/pubid/conformance.rb +55 -0
  193. data/lib/pubid/core.rb +2 -0
  194. data/lib/pubid/csa/builder.rb +48 -48
  195. data/lib/pubid/csa/composite_identifier.rb +18 -7
  196. data/lib/pubid/csa/identifier.rb +187 -80
  197. data/lib/pubid/csa/identifiers/bundled.rb +25 -9
  198. data/lib/pubid/csa/identifiers/canadian_adopted.rb +13 -13
  199. data/lib/pubid/csa/identifiers/cec.rb +44 -4
  200. data/lib/pubid/csa/identifiers/combined.rb +97 -69
  201. data/lib/pubid/csa/identifiers/csa_adopted.rb +4 -4
  202. data/lib/pubid/csa/identifiers/package.rb +4 -4
  203. data/lib/pubid/csa/parser.rb +12 -8
  204. data/lib/pubid/csa/renderer.rb +13 -5
  205. data/lib/pubid/csa/single_identifier.rb +93 -4
  206. data/lib/pubid/csa/urn_generator.rb +9 -2
  207. data/lib/pubid/csa/wrapper_identifier.rb +43 -10
  208. data/lib/pubid/csa.rb +7 -1
  209. data/lib/pubid/doi/builder.rb +18 -0
  210. data/lib/pubid/doi/identifier.rb +49 -0
  211. data/lib/pubid/doi/identifiers/resource.rb +30 -0
  212. data/lib/pubid/doi/identifiers.rb +9 -0
  213. data/lib/pubid/doi/parser.rb +52 -0
  214. data/lib/pubid/doi/renderer.rb +13 -0
  215. data/lib/pubid/doi.rb +56 -0
  216. data/lib/pubid/easc/builder.rb +55 -0
  217. data/lib/pubid/easc/identifier.rb +59 -0
  218. data/lib/pubid/easc/identifiers/pmg.rb +20 -0
  219. data/lib/pubid/easc/identifiers/rmg.rb +19 -0
  220. data/lib/pubid/easc/identifiers.rb +10 -0
  221. data/lib/pubid/easc/parser.rb +88 -0
  222. data/lib/pubid/easc/renderer.rb +48 -0
  223. data/lib/pubid/easc/urn_generator.rb +46 -0
  224. data/lib/pubid/easc/urn_parser.rb +57 -0
  225. data/lib/pubid/easc.rb +53 -0
  226. data/lib/pubid/ecma/builder.rb +45 -0
  227. data/lib/pubid/ecma/identifier.rb +147 -0
  228. data/lib/pubid/ecma/identifiers/memento.rb +30 -0
  229. data/lib/pubid/ecma/identifiers/standard.rb +31 -0
  230. data/lib/pubid/ecma/identifiers/technical_report.rb +31 -0
  231. data/lib/pubid/ecma/identifiers.rb +11 -0
  232. data/lib/pubid/ecma/parser.rb +76 -0
  233. data/lib/pubid/ecma/renderer.rb +67 -0
  234. data/lib/pubid/ecma/urn_generator.rb +46 -0
  235. data/lib/pubid/ecma/urn_parser.rb +51 -0
  236. data/lib/pubid/ecma.rb +68 -0
  237. data/lib/pubid/errors.rb +80 -0
  238. data/lib/pubid/etsi/builder.rb +13 -4
  239. data/lib/pubid/etsi/identifier.rb +1 -3
  240. data/lib/pubid/etsi/identifiers/base.rb +43 -11
  241. data/lib/pubid/etsi/identifiers/etsi_standard.rb +207 -4
  242. data/lib/pubid/etsi/identifiers/supplement_identifier.rb +45 -3
  243. data/lib/pubid/etsi/identifiers.rb +0 -1
  244. data/lib/pubid/etsi/parser.rb +9 -3
  245. data/lib/pubid/etsi/renderer.rb +8 -2
  246. data/lib/pubid/etsi.rb +6 -5
  247. data/lib/pubid/evs/builder.rb +26 -0
  248. data/lib/pubid/evs/identifier.rb +37 -0
  249. data/lib/pubid/evs/identifiers/national_adoption.rb +17 -0
  250. data/lib/pubid/evs/identifiers.rb +9 -0
  251. data/lib/pubid/evs/parser.rb +27 -0
  252. data/lib/pubid/evs/renderer.rb +30 -0
  253. data/lib/pubid/evs/urn_generator.rb +30 -0
  254. data/lib/pubid/evs/urn_parser.rb +80 -0
  255. data/lib/pubid/evs.rb +90 -0
  256. data/lib/pubid/export/exporter.rb +2 -1
  257. data/lib/pubid/export/flavor_exporter.rb +18 -3
  258. data/lib/pubid/export.rb +1 -0
  259. data/lib/pubid/format_detector.rb +1 -0
  260. data/lib/pubid/gb/builder.rb +45 -0
  261. data/lib/pubid/gb/identifier.rb +67 -0
  262. data/lib/pubid/gb/identifiers/standard.rb +34 -0
  263. data/lib/pubid/gb/identifiers.rb +9 -0
  264. data/lib/pubid/gb/parser.rb +63 -0
  265. data/lib/pubid/gb/renderer.rb +35 -0
  266. data/lib/pubid/gb.rb +88 -0
  267. data/lib/pubid/gost/builder.rb +185 -0
  268. data/lib/pubid/gost/identifier.rb +47 -0
  269. data/lib/pubid/gost/identifiers/foreign_reference.rb +25 -0
  270. data/lib/pubid/gost/identifiers/harmonized.rb +39 -0
  271. data/lib/pubid/gost/identifiers/identical_adoption.rb +39 -0
  272. data/lib/pubid/gost/identifiers/interstate_standard.rb +20 -0
  273. data/lib/pubid/gost/identifiers/national_standard.rb +19 -0
  274. data/lib/pubid/gost/identifiers.rb +13 -0
  275. data/lib/pubid/gost/parser.rb +105 -0
  276. data/lib/pubid/gost/renderer.rb +48 -0
  277. data/lib/pubid/gost/urn_generator.rb +46 -0
  278. data/lib/pubid/gost/urn_parser.rb +52 -0
  279. data/lib/pubid/gost.rb +50 -0
  280. data/lib/pubid/iala/builder.rb +22 -27
  281. data/lib/pubid/iala/identifier.rb +36 -8
  282. data/lib/pubid/iala/identifiers/advice.rb +1 -1
  283. data/lib/pubid/iala/identifiers/annex.rb +10 -10
  284. data/lib/pubid/iala/identifiers/general_assembly.rb +5 -2
  285. data/lib/pubid/iala/identifiers/guideline.rb +3 -1
  286. data/lib/pubid/iala/identifiers/letter.rb +1 -1
  287. data/lib/pubid/iala/identifiers/manual.rb +3 -1
  288. data/lib/pubid/iala/identifiers/model_course.rb +3 -1
  289. data/lib/pubid/iala/identifiers/recommendation.rb +3 -1
  290. data/lib/pubid/iala/identifiers/report.rb +1 -1
  291. data/lib/pubid/iala/identifiers/resolution.rb +1 -1
  292. data/lib/pubid/iala/identifiers/standard.rb +3 -1
  293. data/lib/pubid/iala/identifiers.rb +0 -1
  294. data/lib/pubid/iala/parser.rb +10 -2
  295. data/lib/pubid/iala/renderer.rb +1 -1
  296. data/lib/pubid/iala/urn_generator.rb +1 -1
  297. data/lib/pubid/iala/urn_parser.rb +24 -3
  298. data/lib/pubid/iala.rb +5 -4
  299. data/lib/pubid/iana/builder.rb +22 -0
  300. data/lib/pubid/iana/identifier.rb +140 -0
  301. data/lib/pubid/iana/identifiers/registry.rb +69 -0
  302. data/lib/pubid/iana/identifiers.rb +9 -0
  303. data/lib/pubid/iana/parser.rb +38 -0
  304. data/lib/pubid/iana/renderer.rb +32 -0
  305. data/lib/pubid/iana/urn_generator.rb +19 -0
  306. data/lib/pubid/iana/urn_parser.rb +24 -0
  307. data/lib/pubid/iana.rb +70 -0
  308. data/lib/pubid/identifier.rb +797 -26
  309. data/lib/pubid/identifier_metadata.rb +1 -0
  310. data/lib/pubid/idf/builder.rb +4 -4
  311. data/lib/pubid/idf/identifier.rb +18 -9
  312. data/lib/pubid/idf/parser.rb +3 -3
  313. data/lib/pubid/idf/renderer.rb +5 -5
  314. data/lib/pubid/idf/supplement_identifier.rb +3 -3
  315. data/lib/pubid/idf/urn_generator.rb +1 -1
  316. data/lib/pubid/idf.rb +7 -1
  317. data/lib/pubid/iec/builder.rb +49 -32
  318. data/lib/pubid/iec/components.rb +0 -1
  319. data/lib/pubid/iec/identifier.rb +112 -77
  320. data/lib/pubid/iec/identifiers/amendment.rb +6 -2
  321. data/lib/pubid/iec/identifiers/base.rb +2 -2
  322. data/lib/pubid/iec/identifiers/component_specification.rb +0 -10
  323. data/lib/pubid/iec/identifiers/conformity_assessment.rb +0 -10
  324. data/lib/pubid/iec/identifiers/consolidated_identifier.rb +6 -0
  325. data/lib/pubid/iec/identifiers/corrigendum.rb +2 -2
  326. data/lib/pubid/iec/identifiers/fragment_identifier.rb +13 -15
  327. data/lib/pubid/iec/identifiers/guide.rb +0 -16
  328. data/lib/pubid/iec/identifiers/international_standard.rb +22 -6
  329. data/lib/pubid/iec/identifiers/operational_document.rb +0 -10
  330. data/lib/pubid/iec/identifiers/publicly_available_specification.rb +0 -12
  331. data/lib/pubid/iec/identifiers/sheet_identifier.rb +12 -12
  332. data/lib/pubid/iec/identifiers/societal_technology_trend_report.rb +0 -10
  333. data/lib/pubid/iec/identifiers/systems_reference_document.rb +0 -10
  334. data/lib/pubid/iec/identifiers/technical_group.rb +30 -0
  335. data/lib/pubid/iec/identifiers/technical_report.rb +1 -18
  336. data/lib/pubid/iec/identifiers/technical_specification.rb +1 -18
  337. data/lib/pubid/iec/identifiers/technology_report.rb +0 -10
  338. data/lib/pubid/iec/identifiers/test_report_form.rb +0 -11
  339. data/lib/pubid/iec/identifiers/vap_identifier.rb +12 -12
  340. data/lib/pubid/iec/identifiers/white_paper.rb +0 -10
  341. data/lib/pubid/iec/identifiers/working_document.rb +24 -10
  342. data/lib/pubid/iec/identifiers.rb +1 -0
  343. data/lib/pubid/iec/parser.rb +53 -28
  344. data/lib/pubid/iec/renderer.rb +31 -18
  345. data/lib/pubid/iec/single_identifier.rb +34 -11
  346. data/lib/pubid/iec/supplement_identifier.rb +32 -19
  347. data/lib/pubid/iec/urn_generator.rb +227 -61
  348. data/lib/pubid/iec/urn_parser.rb +42 -4
  349. data/lib/pubid/iec.rb +2 -6
  350. data/lib/pubid/ieee/aiee/builder.rb +17 -3
  351. data/lib/pubid/ieee/aiee/identifier.rb +46 -62
  352. data/lib/pubid/ieee/aiee/parser.rb +1 -1
  353. data/lib/pubid/ieee/builder.rb +386 -69
  354. data/lib/pubid/ieee/compaction.rb +121 -0
  355. data/lib/pubid/ieee/components/draft.rb +47 -7
  356. data/lib/pubid/ieee/identifier.rb +1 -3
  357. data/lib/pubid/ieee/identifiers/adopted_standard.rb +62 -5
  358. data/lib/pubid/ieee/identifiers/amendment.rb +40 -0
  359. data/lib/pubid/ieee/identifiers/base.rb +148 -18
  360. data/lib/pubid/ieee/identifiers/code_number.rb +82 -0
  361. data/lib/pubid/ieee/identifiers/conformance_identifier.rb +6 -2
  362. data/lib/pubid/ieee/identifiers/corrigendum.rb +17 -2
  363. data/lib/pubid/ieee/identifiers/csa_dual_published.rb +24 -3
  364. data/lib/pubid/ieee/identifiers/dual_identifier.rb +3 -3
  365. data/lib/pubid/ieee/identifiers/dual_published.rb +58 -3
  366. data/lib/pubid/ieee/identifiers/iec_ieee_copublished.rb +102 -5
  367. data/lib/pubid/ieee/identifiers/interpretation_identifier.rb +25 -1
  368. data/lib/pubid/ieee/identifiers/joint_development.rb +64 -15
  369. data/lib/pubid/ieee/identifiers/multi_numbered_identifier.rb +58 -6
  370. data/lib/pubid/ieee/identifiers/nesc/base.rb +90 -19
  371. data/lib/pubid/ieee/identifiers/nesc/draft.rb +20 -5
  372. data/lib/pubid/ieee/identifiers/nesc/edition.rb +32 -0
  373. data/lib/pubid/ieee/identifiers/nesc/handbook.rb +16 -5
  374. data/lib/pubid/ieee/identifiers/nesc/redline.rb +11 -2
  375. data/lib/pubid/ieee/identifiers/nesc/standard.rb +18 -3
  376. data/lib/pubid/ieee/identifiers/nesc.rb +1 -0
  377. data/lib/pubid/ieee/identifiers/parenthetical_identifier.rb +3 -3
  378. data/lib/pubid/ieee/identifiers/project_draft_identifier.rb +14 -6
  379. data/lib/pubid/ieee/identifiers/redlined_standard.rb +4 -4
  380. data/lib/pubid/ieee/identifiers/si_standard.rb +4 -1
  381. data/lib/pubid/ieee/identifiers/standard.rb +4 -1
  382. data/lib/pubid/ieee/identifiers/supplement_identifier.rb +26 -7
  383. data/lib/pubid/ieee/identifiers.rb +2 -1
  384. data/lib/pubid/ieee/ire/builder.rb +7 -6
  385. data/lib/pubid/ieee/ire/identifier.rb +48 -33
  386. data/lib/pubid/ieee/ire/parser.rb +1 -1
  387. data/lib/pubid/ieee/nesc/builder.rb +33 -20
  388. data/lib/pubid/ieee/nesc/parser.rb +16 -15
  389. data/lib/pubid/ieee/parser.rb +462 -44
  390. data/lib/pubid/ieee/pre_parser.rb +1 -1
  391. data/lib/pubid/ieee/renderer.rb +158 -57
  392. data/lib/pubid/ieee/typed_stages.rb +12 -1
  393. data/lib/pubid/ieee/urn_generator.rb +35 -8
  394. data/lib/pubid/ieee.rb +63 -4
  395. data/lib/pubid/ieee_debug.rb +1 -0
  396. data/lib/pubid/ietf/builder.rb +78 -0
  397. data/lib/pubid/ietf/identifier.rb +5 -0
  398. data/lib/pubid/ietf/identifiers/base.rb +72 -0
  399. data/lib/pubid/ietf/identifiers/bcp.rb +36 -0
  400. data/lib/pubid/ietf/identifiers/fyi.rb +36 -0
  401. data/lib/pubid/ietf/identifiers/internet_draft.rb +45 -0
  402. data/lib/pubid/ietf/identifiers/rfc.rb +27 -0
  403. data/lib/pubid/ietf/identifiers/serialization.rb +51 -0
  404. data/lib/pubid/ietf/identifiers/std.rb +36 -0
  405. data/lib/pubid/ietf/identifiers.rb +14 -0
  406. data/lib/pubid/ietf/parser.rb +61 -0
  407. data/lib/pubid/ietf/renderer.rb +64 -0
  408. data/lib/pubid/ietf/urn_generator.rb +37 -0
  409. data/lib/pubid/ietf/urn_parser.rb +43 -0
  410. data/lib/pubid/ietf.rb +86 -0
  411. data/lib/pubid/iho/identifier.rb +1 -3
  412. data/lib/pubid/iho/identifiers/base.rb +11 -10
  413. data/lib/pubid/iho/identifiers/bibliographic.rb +1 -1
  414. data/lib/pubid/iho/identifiers/circular_letter.rb +1 -1
  415. data/lib/pubid/iho/identifiers/miscellaneous.rb +1 -1
  416. data/lib/pubid/iho/identifiers/publication.rb +1 -1
  417. data/lib/pubid/iho/identifiers/standard.rb +1 -1
  418. data/lib/pubid/iho/identifiers.rb +0 -1
  419. data/lib/pubid/iho/parser.rb +1 -1
  420. data/lib/pubid/iho.rb +8 -7
  421. data/lib/pubid/isbn/builder.rb +44 -0
  422. data/lib/pubid/isbn/check_digit.rb +53 -0
  423. data/lib/pubid/isbn/identifier.rb +81 -0
  424. data/lib/pubid/isbn/identifiers/book.rb +30 -0
  425. data/lib/pubid/isbn/identifiers.rb +9 -0
  426. data/lib/pubid/isbn/parser.rb +45 -0
  427. data/lib/pubid/isbn/renderer.rb +16 -0
  428. data/lib/pubid/isbn.rb +53 -0
  429. data/lib/pubid/iso/builder.rb +9 -4
  430. data/lib/pubid/iso/bundled_identifier.rb +5 -3
  431. data/lib/pubid/iso/combined_identifier.rb +6 -4
  432. data/lib/pubid/iso/identifier.rb +29 -8
  433. data/lib/pubid/iso/identifiers/directives_supplement.rb +12 -12
  434. data/lib/pubid/iso/normalizer.rb +2 -2
  435. data/lib/pubid/iso/parser.rb +17 -13
  436. data/lib/pubid/iso/supplement_identifier.rb +19 -6
  437. data/lib/pubid/iso/urn_generator.rb +4 -1
  438. data/lib/pubid/iso/urn_parser.rb +1 -1
  439. data/lib/pubid/iso.rb +7 -1
  440. data/lib/pubid/itu/builder.rb +230 -29
  441. data/lib/pubid/itu/components/code.rb +48 -5
  442. data/lib/pubid/itu/components/designation.rb +35 -0
  443. data/lib/pubid/itu/components.rb +1 -0
  444. data/lib/pubid/itu/identifier.rb +1 -3
  445. data/lib/pubid/itu/identifiers/addendum.rb +15 -0
  446. data/lib/pubid/itu/identifiers/amendment.rb +7 -29
  447. data/lib/pubid/itu/identifiers/annex.rb +13 -2
  448. data/lib/pubid/itu/identifiers/annex_of_recommendation.rb +87 -0
  449. data/lib/pubid/itu/identifiers/appendix_of_recommendation.rb +92 -0
  450. data/lib/pubid/itu/identifiers/base.rb +357 -25
  451. data/lib/pubid/itu/identifiers/combined_identifier.rb +99 -23
  452. data/lib/pubid/itu/identifiers/corrigendum.rb +16 -23
  453. data/lib/pubid/itu/identifiers/errata.rb +15 -0
  454. data/lib/pubid/itu/identifiers/handbook.rb +38 -0
  455. data/lib/pubid/itu/identifiers/question.rb +63 -0
  456. data/lib/pubid/itu/identifiers/recommendation.rb +3 -2
  457. data/lib/pubid/itu/identifiers/report.rb +39 -0
  458. data/lib/pubid/itu/identifiers/special_publication.rb +3 -1
  459. data/lib/pubid/itu/identifiers/standard_serialization.rb +58 -0
  460. data/lib/pubid/itu/identifiers/supplement.rb +119 -14
  461. data/lib/pubid/itu/identifiers.rb +10 -1
  462. data/lib/pubid/itu/model.rb +1 -1
  463. data/lib/pubid/itu/parser.rb +481 -27
  464. data/lib/pubid/itu/urn_generator.rb +36 -6
  465. data/lib/pubid/itu/urn_parser.rb +8 -0
  466. data/lib/pubid/itu.rb +12 -3
  467. data/lib/pubid/jcgm/builder.rb +24 -14
  468. data/lib/pubid/jcgm/identifier.rb +10 -0
  469. data/lib/pubid/jcgm/identifiers/amendment.rb +0 -2
  470. data/lib/pubid/jcgm/identifiers/corrigendum.rb +34 -0
  471. data/lib/pubid/jcgm/identifiers/gum_guide.rb +3 -3
  472. data/lib/pubid/jcgm/identifiers/meeting.rb +47 -0
  473. data/lib/pubid/jcgm/identifiers.rb +2 -0
  474. data/lib/pubid/jcgm/parser.rb +74 -7
  475. data/lib/pubid/jcgm/renderer.rb +33 -12
  476. data/lib/pubid/jcgm/single_identifier.rb +84 -6
  477. data/lib/pubid/jcgm/supplement_identifier.rb +39 -4
  478. data/lib/pubid/jcgm/urn_generator.rb +21 -13
  479. data/lib/pubid/jcgm/urn_parser.rb +164 -10
  480. data/lib/pubid/jcgm.rb +10 -0
  481. data/lib/pubid/jis/identifier.rb +62 -3
  482. data/lib/pubid/jis/parser.rb +1 -1
  483. data/lib/pubid/jis.rb +1 -0
  484. data/lib/pubid/nist/builder.rb +29 -9
  485. data/lib/pubid/nist/caster.rb +7 -45
  486. data/lib/pubid/nist/circular_supplement_builder.rb +7 -5
  487. data/lib/pubid/nist/components/stage.rb +2 -2
  488. data/lib/pubid/nist/components/supplement.rb +5 -2
  489. data/lib/pubid/nist/configuration.rb +6 -2
  490. data/lib/pubid/nist/identifier.rb +1 -3
  491. data/lib/pubid/nist/identifiers/base.rb +133 -39
  492. data/lib/pubid/nist/identifiers/circular.rb +10 -3
  493. data/lib/pubid/nist/identifiers/circular_supplement.rb +14 -5
  494. data/lib/pubid/nist/identifiers/commercial_standard.rb +1 -1
  495. data/lib/pubid/nist/identifiers/commercial_standard_emergency.rb +1 -1
  496. data/lib/pubid/nist/identifiers/commercial_standards_monthly.rb +8 -2
  497. data/lib/pubid/nist/identifiers/crpl_report.rb +8 -2
  498. data/lib/pubid/nist/identifiers/dated_document.rb +1 -1
  499. data/lib/pubid/nist/identifiers/federal_information_processing_standards.rb +1 -1
  500. data/lib/pubid/nist/identifiers/grant_contractor_report.rb +1 -1
  501. data/lib/pubid/nist/identifiers/handbook.rb +10 -3
  502. data/lib/pubid/nist/identifiers/internal_report.rb +13 -6
  503. data/lib/pubid/nist/identifiers/letter_circular.rb +1 -1
  504. data/lib/pubid/nist/identifiers/miscellaneous_publication.rb +15 -7
  505. data/lib/pubid/nist/identifiers/monograph.rb +15 -8
  506. data/lib/pubid/nist/identifiers/ncstar.rb +2 -2
  507. data/lib/pubid/nist/identifiers/nsrds.rb +1 -1
  508. data/lib/pubid/nist/identifiers/owmwp.rb +1 -1
  509. data/lib/pubid/nist/identifiers/report.rb +15 -7
  510. data/lib/pubid/nist/identifiers/special_publication.rb +10 -1
  511. data/lib/pubid/nist/identifiers/technical_note.rb +1 -1
  512. data/lib/pubid/nist/identifiers.rb +0 -1
  513. data/lib/pubid/nist/parser.rb +27 -4
  514. data/lib/pubid/nist/preprocessor.rb +15 -0
  515. data/lib/pubid/nist/router.rb +2 -2
  516. data/lib/pubid/nist/series/ir.rb +0 -3
  517. data/lib/pubid/nist/supplement_identifier.rb +24 -9
  518. data/lib/pubid/nist.rb +19 -13
  519. data/lib/pubid/oasis/builder.rb +79 -0
  520. data/lib/pubid/oasis/identifier.rb +160 -0
  521. data/lib/pubid/oasis/identifiers/standard.rb +29 -0
  522. data/lib/pubid/oasis/identifiers.rb +9 -0
  523. data/lib/pubid/oasis/parser.rb +34 -0
  524. data/lib/pubid/oasis/renderer.rb +29 -0
  525. data/lib/pubid/oasis/urn_generator.rb +25 -0
  526. data/lib/pubid/oasis/urn_parser.rb +22 -0
  527. data/lib/pubid/oasis.rb +80 -0
  528. data/lib/pubid/ogc/builder.rb +33 -0
  529. data/lib/pubid/ogc/identifier.rb +81 -0
  530. data/lib/pubid/ogc/identifiers/document.rb +26 -0
  531. data/lib/pubid/ogc/identifiers.rb +10 -0
  532. data/lib/pubid/ogc/parser.rb +40 -0
  533. data/lib/pubid/ogc/renderer.rb +33 -0
  534. data/lib/pubid/ogc/urn_generator.rb +14 -0
  535. data/lib/pubid/ogc/urn_parser.rb +22 -0
  536. data/lib/pubid/ogc.rb +72 -0
  537. data/lib/pubid/oiml/builder.rb +36 -8
  538. data/lib/pubid/oiml/identifier.rb +9 -1
  539. data/lib/pubid/oiml/identifiers/basic_publication.rb +2 -0
  540. data/lib/pubid/oiml/identifiers/bulletin.rb +36 -0
  541. data/lib/pubid/oiml/identifiers/code_number.rb +90 -0
  542. data/lib/pubid/oiml/identifiers/document.rb +2 -0
  543. data/lib/pubid/oiml/identifiers/expert_report.rb +2 -0
  544. data/lib/pubid/oiml/identifiers/guide.rb +2 -0
  545. data/lib/pubid/oiml/identifiers/recommendation.rb +2 -0
  546. data/lib/pubid/oiml/identifiers/seminar_report.rb +2 -0
  547. data/lib/pubid/oiml/identifiers/vocabulary.rb +2 -0
  548. data/lib/pubid/oiml/identifiers.rb +1 -0
  549. data/lib/pubid/oiml/parser.rb +12 -13
  550. data/lib/pubid/oiml/renderer.rb +13 -13
  551. data/lib/pubid/oiml/single_identifier.rb +41 -37
  552. data/lib/pubid/oiml/supplement_identifier.rb +36 -9
  553. data/lib/pubid/oiml/urn_parser.rb +23 -6
  554. data/lib/pubid/oiml.rb +10 -0
  555. data/lib/pubid/omg/builder.rb +20 -0
  556. data/lib/pubid/omg/identifier.rb +61 -0
  557. data/lib/pubid/omg/identifiers/specification.rb +32 -0
  558. data/lib/pubid/omg/identifiers.rb +9 -0
  559. data/lib/pubid/omg/parser.rb +93 -0
  560. data/lib/pubid/omg/renderer.rb +31 -0
  561. data/lib/pubid/omg.rb +52 -0
  562. data/lib/pubid/parser/grammar.rb +74 -0
  563. data/lib/pubid/parser.rb +2 -0
  564. data/lib/pubid/parsers/mr_string.rb +120 -7
  565. data/lib/pubid/plateau/builder.rb +8 -4
  566. data/lib/pubid/plateau/identifier.rb +1 -3
  567. data/lib/pubid/plateau/identifiers/base.rb +1 -6
  568. data/lib/pubid/plateau/identifiers/handbook.rb +1 -1
  569. data/lib/pubid/plateau/identifiers/technical_report.rb +1 -1
  570. data/lib/pubid/plateau/identifiers.rb +0 -1
  571. data/lib/pubid/plateau/parser.rb +16 -6
  572. data/lib/pubid/plateau/renderer.rb +1 -1
  573. data/lib/pubid/plateau/supplement_identifier.rb +11 -11
  574. data/lib/pubid/plateau.rb +14 -4
  575. data/lib/pubid/prefixes_support.rb +1 -0
  576. data/lib/pubid/renderers/annotator.rb +233 -0
  577. data/lib/pubid/renderers/base.rb +13 -0
  578. data/lib/pubid/renderers/directives_renderer.rb +3 -3
  579. data/lib/pubid/renderers/human_readable.rb +6 -4
  580. data/lib/pubid/renderers/mr_string.rb +54 -6
  581. data/lib/pubid/renderers/supplement_renderer.rb +1 -1
  582. data/lib/pubid/renderers.rb +1 -0
  583. data/lib/pubid/rendering/numbering.rb +25 -7
  584. data/lib/pubid/rendering/supplement.rb +4 -4
  585. data/lib/pubid/rendering.rb +1 -0
  586. data/lib/pubid/sae/builder.rb +1 -1
  587. data/lib/pubid/sae/identifier.rb +1 -3
  588. data/lib/pubid/sae/identifiers/base.rb +19 -9
  589. data/lib/pubid/sae/identifiers.rb +0 -1
  590. data/lib/pubid/sae/parser.rb +1 -1
  591. data/lib/pubid/sae.rb +4 -3
  592. data/lib/pubid/schema/declaration.rb +33 -0
  593. data/lib/pubid/schema/error.rb +14 -0
  594. data/lib/pubid/schema/identifier_type.rb +23 -0
  595. data/lib/pubid/schema/loader.rb +117 -0
  596. data/lib/pubid/schema/typed_stage.rb +24 -0
  597. data/lib/pubid/schema.rb +25 -0
  598. data/lib/pubid/tgpp/builder.rb +41 -0
  599. data/lib/pubid/tgpp/identifier.rb +107 -0
  600. data/lib/pubid/tgpp/identifiers/technical_report.rb +32 -0
  601. data/lib/pubid/tgpp/identifiers/technical_specification.rb +33 -0
  602. data/lib/pubid/tgpp/identifiers.rb +11 -0
  603. data/lib/pubid/tgpp/parser.rb +78 -0
  604. data/lib/pubid/tgpp/renderer.rb +52 -0
  605. data/lib/pubid/tgpp/urn_generator.rb +39 -0
  606. data/lib/pubid/tgpp/urn_parser.rb +30 -0
  607. data/lib/pubid/tgpp.rb +84 -0
  608. data/lib/pubid/type_resolver.rb +74 -0
  609. data/lib/pubid/un/builder.rb +42 -0
  610. data/lib/pubid/un/identifier.rb +40 -0
  611. data/lib/pubid/un/identifiers/document.rb +24 -0
  612. data/lib/pubid/un/identifiers.rb +9 -0
  613. data/lib/pubid/un/parser.rb +25 -0
  614. data/lib/pubid/un/renderer.rb +11 -0
  615. data/lib/pubid/un.rb +47 -0
  616. data/lib/pubid/urn_parser/errors.rb +8 -1
  617. data/lib/pubid/urn_parser.rb +1 -0
  618. data/lib/pubid/utils.rb +1 -0
  619. data/lib/pubid/version.rb +1 -1
  620. data/lib/pubid/w3c/builder.rb +55 -0
  621. data/lib/pubid/w3c/identifier.rb +130 -0
  622. data/lib/pubid/w3c/identifiers/candidate_recommendation.rb +31 -0
  623. data/lib/pubid/w3c/identifiers/candidate_recommendation_draft.rb +31 -0
  624. data/lib/pubid/w3c/identifiers/draft_note.rb +31 -0
  625. data/lib/pubid/w3c/identifiers/note.rb +30 -0
  626. data/lib/pubid/w3c/identifiers/obsolete_recommendation.rb +31 -0
  627. data/lib/pubid/w3c/identifiers/proposed_edited_recommendation.rb +31 -0
  628. data/lib/pubid/w3c/identifiers/proposed_recommendation.rb +31 -0
  629. data/lib/pubid/w3c/identifiers/recommendation.rb +31 -0
  630. data/lib/pubid/w3c/identifiers/standard.rb +12 -0
  631. data/lib/pubid/w3c/identifiers/superseded_recommendation.rb +31 -0
  632. data/lib/pubid/w3c/identifiers/working_draft.rb +30 -0
  633. data/lib/pubid/w3c/identifiers.rb +25 -0
  634. data/lib/pubid/w3c/parser.rb +43 -0
  635. data/lib/pubid/w3c/renderer.rb +35 -0
  636. data/lib/pubid/w3c/urn_generator.rb +18 -0
  637. data/lib/pubid/w3c/urn_parser.rb +36 -0
  638. data/lib/pubid/w3c.rb +68 -0
  639. data/lib/pubid/xsf/builder.rb +17 -0
  640. data/lib/pubid/xsf/identifier.rb +66 -0
  641. data/lib/pubid/xsf/identifiers/xep.rb +31 -0
  642. data/lib/pubid/xsf/identifiers.rb +9 -0
  643. data/lib/pubid/xsf/parser.rb +38 -0
  644. data/lib/pubid/xsf/renderer.rb +23 -0
  645. data/lib/pubid/xsf/urn_generator.rb +13 -0
  646. data/lib/pubid/xsf/urn_parser.rb +18 -0
  647. data/lib/pubid/xsf.rb +82 -0
  648. data/lib/pubid.rb +424 -22
  649. data/lib/tasks/conformance.rake +34 -0
  650. data/lib/tasks/docs.rake +3 -3
  651. data/lib/tasks/schema.rake +88 -0
  652. data/schema/core/joint_prefixes.yaml +24 -0
  653. data/schema/iec.yaml +1017 -0
  654. data/schema/iso.yaml +1586 -0
  655. data/schema/schema.schema.yaml +96 -0
  656. metadata +241 -19
  657. data/archived-gems/pubid-ccsds/update_codes.yaml +0 -1
  658. data/archived-gems/pubid-iec/stages.yaml +0 -129
  659. data/archived-gems/pubid-iec/update_codes.yaml +0 -67
  660. data/archived-gems/pubid-ieee/update_codes.yaml +0 -104
  661. data/archived-gems/pubid-iso/stages.yaml +0 -106
  662. data/archived-gems/pubid-iso/update_codes.yaml +0 -4
  663. data/archived-gems/pubid-itu/i18n.yaml +0 -13
  664. data/archived-gems/pubid-itu/series.yaml +0 -42
  665. data/archived-gems/pubid-nist/publishers.yaml +0 -6
  666. data/archived-gems/pubid-nist/update_codes.yaml +0 -93
  667. data/archived-gems/pubid-plateau/update_codes.yaml +0 -6
  668. data/lib/pubid/cen_cenelec/supplement_identifier.rb +0 -48
  669. data/lib/pubid/cie/components/code.rb +0 -80
  670. data/lib/pubid/iala/identifiers/base.rb +0 -14
  671. data/lib/pubid/iec/components/code.rb +0 -36
  672. /data/{archived-gems/pubid-nist → data/nist}/stages.yaml +0 -0
@@ -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,59 @@ 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
+ # The same document with a TRAILING DATE parses FLAT — no :base subtree,
294
+ # just an :interpretation marker beside the number — so the branch above
295
+ # skipped it and determine_identifier_class built a base-less
296
+ # InterpretationIdentifier that dropped the number, the part and the
297
+ # publisher: "IEEE Std 1003.2-1992/INT, Dec. 1994 Ed." rendered as
298
+ # " 1003.2-1992/INT". Mirrors build_flat_corrigendum / build_flat_amendment.
299
+ if parsed_hash[:interpretation] && !parsed_hash[:base]
300
+ return build_flat_interpretation(parsed_hash)
301
+ end
302
+
303
+ # Handle conformance supplements (check for base + conf_number)
304
+ if parsed_hash[:base] && parsed_hash[:conf_number]
180
305
  return build_conformance_supplement(parsed_hash)
181
306
  end
182
307
 
@@ -194,16 +319,32 @@ module Pubid
194
319
 
195
320
  # Route to appropriate identifier class based on content
196
321
  identifier_class = determine_identifier_class(attributes)
322
+ rename_supplement_keys(attributes)
197
323
  identifier_class.new(**attributes)
198
324
  end
199
325
 
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
326
+ # Supplement wrappers (Corrigendum/Amendment/Conformance/Interpretation)
327
+ # store their ordinal/year under the uniform `number`/`year`, but the parse
328
+ # tree and class-routing above key on the distinct
329
+ # `cor_*`/`amd_*`/`conf_*`/`int_year` names. Translate them **after**
330
+ # routing, just before construction. Guards ensure a plain standard's own
331
+ # `number`/`year` are never overwritten (it carries none of these keys).
332
+ def rename_supplement_keys(attributes)
333
+ ordinal = attributes.delete(:cor_number) ||
334
+ attributes.delete(:amd_number) || attributes.delete(:conf_number)
335
+ attributes[:number] = ordinal if ordinal
336
+ year = attributes.delete(:cor_year) || attributes.delete(:amd_year) ||
337
+ attributes.delete(:conf_year) || attributes.delete(:int_year)
338
+ attributes[:year] = year if year
339
+ end
340
+
341
+ # Reconstruct the base identifier string from a parsed `:base` subtree
342
+ # (publisher/copublishers + type + number/part/subpart/year), so it can be
343
+ # recursively re-parsed by the supplement builders (corrigendum/amendment).
344
+ # @param base_data [Hash] the `:base` subtree
345
+ # @return [String] e.g. "IEEE Std 802.1AC-2016" or "535-2013"
346
+ def reconstruct_base_string(base_data)
205
347
  base_parts = []
206
- base_data = parsed_hash[:base_identifier]
207
348
 
208
349
  # Extract publisher
209
350
  if base_data[:publishers]
@@ -256,22 +397,118 @@ module Pubid
256
397
  end
257
398
 
258
399
  base_parts << number_str
400
+ base_parts.join(" ")
401
+ end
259
402
 
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)
403
+ # Build corrigendum supplement with recursive base parsing
404
+ # @param parsed_hash [Hash] parsed data with base and supplement info
405
+ # @return [Identifiers::Corrigendum] corrigendum identifier
406
+ def build_corrigendum_supplement(parsed_hash)
407
+ # Recursively parse the base identifier reconstructed from the subtree.
408
+ base = Identifier.parse(reconstruct_base_string(parsed_hash[:base]))
265
409
 
266
- # Extract corrigendum attributes
410
+ # Extract corrigendum attributes (parse-tree keys are cor_*; the
411
+ # identifier stores them as the uniform `number`/`year`).
267
412
  cor_number = extract_value(parsed_hash[:cor_number])
268
413
  cor_year = extract_value(parsed_hash[:cor_year])
269
414
 
270
415
  # Create Corrigendum with parsed base
271
416
  Identifiers::Corrigendum.new(
272
- base_identifier: base_identifier,
273
- cor_number: cor_number,
274
- cor_year: cor_year,
417
+ base: base,
418
+ number: cor_number,
419
+ year: cor_year,
420
+ )
421
+ end
422
+
423
+ # Build a Corrigendum from the flat parse tree produced by the combined
424
+ # `…/Cor M-YYYY/D N` form, where the corrigendum lives in a top-level
425
+ # `corrigendum` hash and there is no `:base` subtree. Peel the corrigendum
426
+ # off, build the remaining attributes into the base standard (with its
427
+ # draft) via the normal single-identifier path, then wrap.
428
+ # @param parsed_hash [Hash] flat parsed data with a :corrigendum hash
429
+ # @return [Identifiers::Corrigendum]
430
+ def build_flat_corrigendum(parsed_hash)
431
+ cor_data = parsed_hash[:corrigendum]
432
+ cor_number = extract_value(cor_data[:cor_number])
433
+ cor_year = extract_value(cor_data[:cor_year]) if cor_data[:cor_year]
434
+
435
+ base_hash = parsed_hash.reject { |key, _| key == :corrigendum }
436
+ base = build_single_identifier(base_hash)
437
+
438
+ Identifiers::Corrigendum.new(
439
+ base: base,
440
+ number: cor_number,
441
+ year: cor_year,
442
+ )
443
+ end
444
+
445
+ # Build an interpretation from a FLAT parse tree — the trailing-date form,
446
+ # where the :interpretation marker sits beside the base's own keys instead
447
+ # of over a :base subtree.
448
+ #
449
+ # The trailing date is the INTERPRETATION's date (an interpretation of the
450
+ # 1992 edition of 1003.2, issued in Dec 1994), so it goes on the wrapper;
451
+ # the base keeps its own `year`. That matches the nested form, whose
452
+ # `int_year` also lands on the wrapper.
453
+ # @param parsed_hash [Hash] flat parsed data carrying :interpretation
454
+ # @return [Identifiers::InterpretationIdentifier]
455
+ def build_flat_interpretation(parsed_hash)
456
+ int_year = extract_value(parsed_hash[:trailing_year]) if parsed_hash[:trailing_year]
457
+
458
+ base_hash = parsed_hash.reject do |key, _|
459
+ %i[interpretation trailing_month trailing_year].include?(key)
460
+ end
461
+ base = build_single_identifier(base_hash)
462
+
463
+ # The trailing MONTH is deliberately dropped, matching the nested form
464
+ # (whose `int_year` carries no month either). It was not preserved
465
+ # before this change, so nothing is lost; and setting it fed the URN's
466
+ # month_day_component, which put "Dec." — a word with a dot — into a
467
+ # URN segment.
468
+ Identifiers::InterpretationIdentifier.new(
469
+ base: base,
470
+ year: int_year,
471
+ )
472
+ end
473
+
474
+ # Build amendment supplement with recursive base parsing. Mirrors
475
+ # {#build_corrigendum_supplement} — reconstructs the base identifier
476
+ # string, recursively parses it, and wraps it in an Amendment.
477
+ # @param parsed_hash [Hash] parsed data with base and amendment info
478
+ # @return [Identifiers::Amendment] amendment identifier
479
+ def build_amendment_supplement(parsed_hash)
480
+ base = Identifier.parse(reconstruct_base_string(parsed_hash[:base]))
481
+
482
+ amd_number = extract_value(parsed_hash[:amd_number])
483
+ amd_year = extract_value(parsed_hash[:amd_year])
484
+
485
+ Identifiers::Amendment.new(
486
+ base: base,
487
+ number: amd_number,
488
+ year: amd_year,
489
+ )
490
+ end
491
+
492
+ # Build an Amendment from the flat parse tree produced by the common
493
+ # `…/Amd N-YYYY` form, where the amendment lives in a top-level
494
+ # `amendment` hash and there is no `:base` subtree. Peel the amendment
495
+ # off, build the remaining attributes into the base standard via the
496
+ # normal single-identifier path, then wrap. Mirrors
497
+ # {#build_flat_corrigendum}.
498
+ # @param parsed_hash [Hash] flat parsed data with an :amendment hash
499
+ # @return [Identifiers::Amendment]
500
+ def build_flat_amendment(parsed_hash)
501
+ amd_data = parsed_hash[:amendment]
502
+ amd_number = extract_value(amd_data[:amd_number])
503
+ amd_year = extract_value(amd_data[:amd_year]) if amd_data[:amd_year]
504
+
505
+ base_hash = parsed_hash.reject { |key, _| key == :amendment }
506
+ base = build_single_identifier(base_hash)
507
+
508
+ Identifiers::Amendment.new(
509
+ base: base,
510
+ number: amd_number,
511
+ year: amd_year,
275
512
  )
276
513
  end
277
514
 
@@ -281,7 +518,7 @@ module Pubid
281
518
  def build_interpretation_supplement(parsed_hash)
282
519
  # Reconstruct base identifier string from parsed components (same logic as corrigendum)
283
520
  base_parts = []
284
- base_data = parsed_hash[:base_identifier]
521
+ base_data = parsed_hash[:base]
285
522
 
286
523
  # Extract publisher
287
524
  if base_data[:publishers]
@@ -337,15 +574,15 @@ module Pubid
337
574
  base_string = base_parts.join(" ")
338
575
 
339
576
  # Recursively parse base identifier using Base.parse
340
- base_identifier = Identifiers::Base.parse(base_string)
577
+ base = Identifier.parse(base_string)
341
578
 
342
579
  # Extract interpretation attributes
343
580
  int_year = extract_value(parsed_hash[:int_year])
344
581
 
345
- # Create InterpretationIdentifier with parsed base
582
+ # Create InterpretationIdentifier with parsed base (int_year -> year)
346
583
  Identifiers::InterpretationIdentifier.new(
347
- base_identifier: base_identifier,
348
- int_year: int_year,
584
+ base: base,
585
+ year: int_year,
349
586
  )
350
587
  end
351
588
 
@@ -355,7 +592,7 @@ module Pubid
355
592
  def build_conformance_supplement(parsed_hash)
356
593
  # Reconstruct base identifier string from parsed components (same logic as corrigendum)
357
594
  base_parts = []
358
- base_data = parsed_hash[:base_identifier]
595
+ base_data = parsed_hash[:base]
359
596
 
360
597
  # Extract publisher
361
598
  if base_data[:publishers]
@@ -411,17 +648,17 @@ module Pubid
411
648
  base_string = base_parts.join(" ")
412
649
 
413
650
  # Recursively parse base identifier using Base.parse
414
- base_identifier = Identifiers::Base.parse(base_string)
651
+ base = Identifier.parse(base_string)
415
652
 
416
653
  # Extract conformance attributes
417
654
  conf_number = extract_value(parsed_hash[:conf_number])
418
655
  conf_year = extract_value(parsed_hash[:conf_year])
419
656
 
420
- # Create ConformanceIdentifier with parsed base
657
+ # Create ConformanceIdentifier with parsed base (conf_* -> number/year)
421
658
  Identifiers::ConformanceIdentifier.new(
422
- base_identifier: base_identifier,
423
- conf_number: conf_number,
424
- conf_year: conf_year,
659
+ base: base,
660
+ number: conf_number,
661
+ year: conf_year,
425
662
  )
426
663
  end
427
664
 
@@ -445,8 +682,33 @@ module Pubid
445
682
  end
446
683
  attributes[:code] = code_str
447
684
 
448
- # Extract year
449
- attributes[:year] = extract_value(parsed[:year]) if parsed[:year]
685
+ # Extract year (and optional numeric month, e.g. the -MM of a historical
686
+ # "…-2018-05" ISO-stage date). The joint ISO/IEC rules also use the
687
+ # `edition` rule, whose year is captured as :edition_year (parser.rb);
688
+ # it is promoted to the identity only when no base :year was captured.
689
+ if parsed[:year]
690
+ attributes[:year] = extract_value(parsed[:year])
691
+ elsif parsed[:edition_year]
692
+ attributes[:year] = extract_value(parsed[:edition_year])
693
+ end
694
+ attributes[:month] = extract_value(parsed[:month]) if parsed[:month]
695
+
696
+ # Extract edition, from relaton's "/E-<n>" suffix (normalized to
697
+ # "Edition <n>.0"). nil-residue hand-off item 1.
698
+ attributes[:edition] = extract_value(parsed[:edition]) if parsed[:edition]
699
+ if parsed[:edition_month]
700
+ attributes[:edition_month] = extract_value(parsed[:edition_month])
701
+ end
702
+
703
+ # Extract draft version if present (e.g., D8 from /D8). This applies to
704
+ # both the ISO-led (bucket 5: "…FDIS P15289/D-3-2017") and IEEE-led
705
+ # forms, so it lives before the lead-party split.
706
+ if parsed[:draft_version]
707
+ draft_ver = extract_value(parsed[:draft_version])
708
+ # Remove leading 'D' if present since draft_version already has it
709
+ draft_ver = draft_ver.sub(/^D/, "") if draft_ver
710
+ attributes[:ieee_draft] = "D#{draft_ver}" if draft_ver
711
+ end
450
712
 
451
713
  # Detect lead party based on pattern
452
714
  if parsed[:iso_stage]
@@ -464,14 +726,6 @@ module Pubid
464
726
  # IEEE format - lead party is IEEE
465
727
  attributes[:lead_party] = "IEEE"
466
728
 
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
729
  # Mark as project (P prefix)
476
730
  attributes[:type] = "P"
477
731
 
@@ -555,9 +809,14 @@ module Pubid
555
809
  # Pattern: slash >> "C" >> digits >> dot >> digits >> dot >> digits >> dash >> year_digits
556
810
  # The parsed crossref is a string - extract the number part
557
811
  # Format: C62.22.1-1996 where C is part of the crossref notation
812
+ # The captured slice keeps the leading slash, which is a SEPARATOR in
813
+ # the printed reference, not part of the number. Storing it made the
814
+ # secondary's code "/C62.22.1-1996", which then failed the renderer's
815
+ # own cross-reference test (`/^C\d+\./`) and fell through to the
816
+ # " and " join. Strip it.
558
817
  secondary_id = Identifiers::Standard.new(
559
818
  publisher: "IEEE",
560
- number: extract_value(crossref),
819
+ number: extract_value(crossref).to_s.sub(%r{\A/}, ""),
561
820
  )
562
821
  elsif parsed[:secondary_joint]
563
822
  # Joint standard format: ", Std 1177-1989"
@@ -616,6 +875,11 @@ module Pubid
616
875
  return Identifiers::Corrigendum
617
876
  end
618
877
 
878
+ # Check for amendment supplements (structural: has amd_number)
879
+ if attributes[:amd_number]
880
+ return Identifiers::Amendment
881
+ end
882
+
619
883
  # Check for multi-numbered standards (structural: has crossref or joint patterns)
620
884
  # These are handled at the build() method level, not here
621
885
  # (cross-reference patterns like /C62.22.1-1996 and joint standards)
@@ -625,13 +889,16 @@ module Pubid
625
889
  return Identifiers::AdoptedStandard
626
890
  end
627
891
 
628
- # Check for redline standards (structural: has redline flag)
629
- if attributes[:redline]
630
- return Identifiers::RedlinedStandard
631
- end
892
+ # A redline is a plain Standard carrying `redline: true` (a distinct
893
+ # document from its base) — NOT the RedlinedStandard wrapper, which
894
+ # expects a nested `base` the flat build path never sets. render_base
895
+ # restores the " - Redline" suffix from the flag.
632
896
 
633
- # Default to base identifier
634
- Identifiers::Base
897
+ # Default to the concrete Standard leaf (a plain published standard).
898
+ # Routing to a leaf — rather than instantiating the shared base
899
+ # Ieee::Identifier — is what lets `number` be a reliable :string index
900
+ # key (see Identifiers::Standard).
901
+ Identifiers::Standard
635
902
  end
636
903
 
637
904
  # Detect lead party for joint development identifiers
@@ -777,6 +1044,26 @@ module Pubid
777
1044
  if parsed[:year]
778
1045
  year_str = extract_value(parsed[:year])
779
1046
  attributes[:year] = year_str
1047
+ elsif parsed[:edition_year]
1048
+ # The `edition` rule captures its own year under :edition_year (not
1049
+ # :year) so it never collides with the base -YYYY (see parser.rb).
1050
+ # With no base year the edition's year IS the document date and is
1051
+ # promoted to the identity (e.g. "IEEE Std 802.11 Edition 3.0-2009"
1052
+ # -> year 2009). When a base year exists it wins above and
1053
+ # :edition_year is dropped — the edition ordinal is still kept.
1054
+ attributes[:year] = extract_value(parsed[:edition_year])
1055
+ elsif parsed[:trailing_year]
1056
+ # No base -YYYY identity year: the trailing "Month YYYY" IS the
1057
+ # document date (e.g. "IEEE Std 519, May 1993"), so promote it.
1058
+ # When a base year exists it wins (the IEEE approval/identity year)
1059
+ # and the trailing date is intentionally dropped rather than
1060
+ # fabricating a wrong month/year pairing. The trailing captures use
1061
+ # distinct keys (:trailing_month/:trailing_year) so they never collide
1062
+ # with the base :year (Parslet no longer warns "Duplicate subtrees").
1063
+ attributes[:year] = extract_value(parsed[:trailing_year])
1064
+ if parsed[:trailing_month]
1065
+ attributes[:month] = extract_value(parsed[:trailing_month])
1066
+ end
780
1067
  end
781
1068
 
782
1069
  # Extract edition_month if parsed separately
@@ -813,6 +1100,12 @@ module Pubid
813
1100
  # Handle draft (can be complex)
814
1101
  handle_draft(parsed, attributes)
815
1102
 
1103
+ # Default draft version 1 when the type indicates a Draft but the
1104
+ # parser found no explicit version (issue #205). Real-world IEEE
1105
+ # drafts always have at least version 1; the missing suffix is a
1106
+ # source-data omission, not an undrafted document.
1107
+ apply_default_draft_version(attributes, type_value)
1108
+
816
1109
  # Handle corrigendum
817
1110
  handle_corrigendum(parsed, attributes)
818
1111
 
@@ -927,15 +1220,36 @@ module Pubid
927
1220
  attributes[key] = value if value
928
1221
  end
929
1222
 
1223
+ # Default draft version to "1" when the identifier type indicates a
1224
+ # Draft but the parser found no explicit version (issue #205).
1225
+ def apply_default_draft_version(attributes, type_value)
1226
+ return if attributes[:draft] || attributes[:draft_obj]
1227
+ return unless type_value.to_s == "Draft Std"
1228
+
1229
+ attributes[:draft] = "D1"
1230
+ # Carry the identifier's year into the draft object so the renderer
1231
+ # doesn't drop it (the renderer suppresses the `-YYYY` suffix when
1232
+ # a draft is attached, expecting the year to live inside /D...).
1233
+ attributes[:draft_obj] =
1234
+ Components::Draft.new(version: "1", year: attributes[:year])
1235
+ end
1236
+
930
1237
  # Handle draft information
931
1238
  def handle_draft(parsed, attributes)
932
1239
  draft_data = parsed[:draft] || parsed[:digit_draft]
933
1240
  return unless draft_data
934
1241
 
935
- # Draft can be an array of hash elements or a single hash
1242
+ # Draft can be an array of hash elements or a single hash. The grammar's
1243
+ # `draft_version.repeat(1,2)` splits a dotted designator like "1.2.6"
1244
+ # into ["1.2", ".6"], so DON'T let a plain merge overwrite it (that
1245
+ # dropped every part but the last — "1.2.6" -> ".6"). Collect ALL the
1246
+ # draft_version parts into an array (the version extraction below joins
1247
+ # them back into one verbatim string) and merge the remaining keys.
936
1248
  if draft_data.is_a?(Array)
937
- # Merge all elements in the array
938
- merged = draft_data.inject({}) { |result, elem| result.merge(elem) }
1249
+ hashes = draft_data.select { |e| e.is_a?(Hash) }
1250
+ versions = hashes.filter_map { |e| e[:draft_version] }
1251
+ merged = hashes.inject({}) { |result, elem| result.merge(elem) }
1252
+ merged[:draft_version] = versions unless versions.empty?
939
1253
  draft_data = merged
940
1254
  end
941
1255
 
@@ -955,6 +1269,9 @@ module Pubid
955
1269
  else
956
1270
  extract_value(dv)
957
1271
  end
1272
+ # Normalize a leading hyphen ("/D-3.0" -> "3.0"); the draft
1273
+ # designator is otherwise held verbatim so no digit is lost.
1274
+ version = version.sub(/\A-/, "") if version
958
1275
  end
959
1276
 
960
1277
  revision = extract_value(draft_data[:revision]) if draft_data[:revision]
@@ -1183,7 +1500,7 @@ module Pubid
1183
1500
 
1184
1501
  # Parse a list of identifier strings into identifier objects
1185
1502
  # @param list_data [Hash, Array, nil] the parsed identifier list data
1186
- # @return [Array<Identifiers::Base>] array of parsed identifier objects
1503
+ # @return [Array<Identifier>] array of parsed identifier objects
1187
1504
  def parse_identifier_list(list_data)
1188
1505
  return [] unless list_data
1189
1506
 
@@ -1192,12 +1509,12 @@ module Pubid
1192
1509
 
1193
1510
  # Recursively parse each identifier string
1194
1511
  id_strings.map do |id_str|
1195
- # Use Identifiers::Base.parse for recursive parsing
1196
- Identifiers::Base.parse(id_str)
1512
+ # Use Identifier.parse for recursive parsing
1513
+ Identifier.parse(id_str)
1197
1514
  rescue Parslet::ParseFailed
1198
1515
  # If parsing fails, create minimal identifier with just the string
1199
1516
  # This maintains graceful degradation
1200
- Identifiers::Base.new(parenthetical_content: id_str)
1517
+ Identifier.new(parenthetical_content: id_str)
1201
1518
  end
1202
1519
  end
1203
1520