pubid 2.0.0.pre.alpha.2 → 2.0.0.pre.alpha.4

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 (473) hide show
  1. checksums.yaml +4 -4
  2. data/README.adoc +5 -1
  3. data/data/nist/update_codes.yaml +25 -0
  4. data/lib/pubid/amca/builder.rb +2 -2
  5. data/lib/pubid/amca/identifier.rb +4 -55
  6. data/lib/pubid/amca/identifiers/base.rb +58 -47
  7. data/lib/pubid/amca/identifiers/interpretation.rb +0 -21
  8. data/lib/pubid/amca/identifiers/publication.rb +0 -17
  9. data/lib/pubid/amca/identifiers/standard.rb +0 -4
  10. data/lib/pubid/amca/renderer.rb +82 -0
  11. data/lib/pubid/amca/single_identifier.rb +0 -23
  12. data/lib/pubid/amca/urn_generator.rb +2 -1
  13. data/lib/pubid/amca/urn_parser.rb +28 -0
  14. data/lib/pubid/amca.rb +42 -1
  15. data/lib/pubid/ansi/builder.rb +5 -3
  16. data/lib/pubid/ansi/identifier.rb +1 -43
  17. data/lib/pubid/ansi/identifiers/american_national_standard.rb +2 -1
  18. data/lib/pubid/ansi/identifiers/standard.rb +2 -3
  19. data/lib/pubid/ansi/renderer.rb +53 -0
  20. data/lib/pubid/ansi/single_identifier.rb +2 -31
  21. data/lib/pubid/ansi/urn_generator.rb +3 -38
  22. data/lib/pubid/ansi/urn_parser.rb +23 -0
  23. data/lib/pubid/ansi.rb +38 -3
  24. data/lib/pubid/api/builder.rb +33 -74
  25. data/lib/pubid/api/identifier.rb +5 -52
  26. data/lib/pubid/api/identifiers/base.rb +2 -14
  27. data/lib/pubid/api/identifiers/bulletin.rb +0 -2
  28. data/lib/pubid/api/identifiers/continuous_operations_standard.rb +0 -2
  29. data/lib/pubid/api/identifiers/mpms.rb +0 -20
  30. data/lib/pubid/api/identifiers/publication.rb +0 -2
  31. data/lib/pubid/api/identifiers/recommended_practice.rb +0 -2
  32. data/lib/pubid/api/identifiers/specification.rb +0 -2
  33. data/lib/pubid/api/identifiers/standard.rb +0 -2
  34. data/lib/pubid/api/identifiers/technical_report.rb +0 -2
  35. data/lib/pubid/api/identifiers/typeless_standard.rb +0 -16
  36. data/lib/pubid/api/identifiers.rb +18 -0
  37. data/lib/pubid/api/renderer.rb +89 -0
  38. data/lib/pubid/api/single_identifier.rb +5 -20
  39. data/lib/pubid/api/urn_generator.rb +0 -18
  40. data/lib/pubid/api/urn_parser.rb +35 -0
  41. data/lib/pubid/api.rb +51 -5
  42. data/lib/pubid/ashrae/builder.rb +3 -3
  43. data/lib/pubid/ashrae/identifier.rb +7 -55
  44. data/lib/pubid/ashrae/identifiers/addenda_package.rb +0 -10
  45. data/lib/pubid/ashrae/identifiers/addendum.rb +0 -19
  46. data/lib/pubid/ashrae/identifiers/base.rb +29 -15
  47. data/lib/pubid/ashrae/identifiers/combined_addenda.rb +0 -15
  48. data/lib/pubid/ashrae/identifiers/errata.rb +0 -10
  49. data/lib/pubid/ashrae/identifiers/interpretation.rb +0 -10
  50. data/lib/pubid/ashrae/parser.rb +4 -0
  51. data/lib/pubid/ashrae/renderer.rb +117 -0
  52. data/lib/pubid/ashrae/single_identifier.rb +0 -13
  53. data/lib/pubid/ashrae/urn_generator.rb +0 -8
  54. data/lib/pubid/ashrae/urn_parser.rb +27 -0
  55. data/lib/pubid/ashrae.rb +46 -1
  56. data/lib/pubid/asme/components/code.rb +10 -2
  57. data/lib/pubid/asme/identifier.rb +0 -46
  58. data/lib/pubid/asme/identifiers/base.rb +0 -60
  59. data/lib/pubid/asme/renderer.rb +66 -0
  60. data/lib/pubid/asme/single_identifier.rb +3 -1
  61. data/lib/pubid/asme/urn_parser.rb +31 -0
  62. data/lib/pubid/asme.rb +42 -1
  63. data/lib/pubid/astm/components/code.rb +9 -0
  64. data/lib/pubid/{jis → astm}/components.rb +1 -1
  65. data/lib/pubid/astm/identifier.rb +0 -77
  66. data/lib/pubid/astm/identifiers/adjunct.rb +0 -8
  67. data/lib/pubid/astm/identifiers/data_series.rb +0 -14
  68. data/lib/pubid/astm/identifiers/iso_dual_published.rb +9 -34
  69. data/lib/pubid/astm/identifiers/manual.rb +0 -27
  70. data/lib/pubid/astm/identifiers/monograph.rb +0 -14
  71. data/lib/pubid/astm/identifiers/research_report.rb +0 -7
  72. data/lib/pubid/astm/identifiers/standard.rb +0 -39
  73. data/lib/pubid/astm/identifiers/technical_report.rb +0 -13
  74. data/lib/pubid/astm/identifiers/work_in_progress.rb +0 -11
  75. data/lib/pubid/astm/identifiers.rb +18 -0
  76. data/lib/pubid/astm/renderer.rb +172 -0
  77. data/lib/pubid/astm/single_identifier.rb +3 -11
  78. data/lib/pubid/astm/urn_parser.rb +30 -0
  79. data/lib/pubid/astm.rb +39 -27
  80. data/lib/pubid/bsi/builder.rb +21 -12
  81. data/lib/pubid/bsi/identifier.rb +5 -85
  82. data/lib/pubid/bsi/identifiers/addendum_document.rb +2 -36
  83. data/lib/pubid/bsi/identifiers/adopted_european_norm.rb +8 -48
  84. data/lib/pubid/bsi/identifiers/adopted_international_standard.rb +8 -39
  85. data/lib/pubid/bsi/identifiers/aerospace_standard.rb +2 -55
  86. data/lib/pubid/bsi/identifiers/amendment.rb +2 -22
  87. data/lib/pubid/bsi/identifiers/british_industrial_practice.rb +1 -6
  88. data/lib/pubid/bsi/identifiers/british_standard.rb +2 -1
  89. data/lib/pubid/bsi/identifiers/bundled_identifier.rb +3 -87
  90. data/lib/pubid/bsi/identifiers/committee_document.rb +0 -16
  91. data/lib/pubid/bsi/identifiers/consolidated_identifier.rb +3 -87
  92. data/lib/pubid/bsi/identifiers/corrigendum.rb +2 -10
  93. data/lib/pubid/bsi/identifiers/detailed_specification.rb +0 -36
  94. data/lib/pubid/bsi/identifiers/disc.rb +0 -29
  95. data/lib/pubid/bsi/identifiers/draft_document.rb +2 -46
  96. data/lib/pubid/bsi/identifiers/electronic_book.rb +2 -38
  97. data/lib/pubid/bsi/identifiers/expert_commentary.rb +2 -18
  98. data/lib/pubid/bsi/identifiers/explanatory_supplement.rb +0 -47
  99. data/lib/pubid/bsi/identifiers/flex.rb +0 -35
  100. data/lib/pubid/bsi/identifiers/handbook.rb +1 -15
  101. data/lib/pubid/bsi/identifiers/index.rb +0 -32
  102. data/lib/pubid/bsi/identifiers/method.rb +0 -41
  103. data/lib/pubid/bsi/identifiers/national_annex.rb +4 -29
  104. data/lib/pubid/bsi/identifiers/practice_guide.rb +1 -6
  105. data/lib/pubid/bsi/identifiers/publicly_available_specification.rb +2 -54
  106. data/lib/pubid/bsi/identifiers/published_document.rb +2 -54
  107. data/lib/pubid/bsi/identifiers/section.rb +0 -30
  108. data/lib/pubid/bsi/identifiers/set.rb +3 -20
  109. data/lib/pubid/bsi/identifiers/standalone_amendment.rb +0 -9
  110. data/lib/pubid/bsi/identifiers/supplement_document.rb +2 -24
  111. data/lib/pubid/bsi/identifiers/supplementary_index.rb +0 -46
  112. data/lib/pubid/bsi/identifiers/technical_specification.rb +2 -47
  113. data/lib/pubid/bsi/identifiers/test_method.rb +0 -32
  114. data/lib/pubid/bsi/identifiers/value_added_publication.rb +2 -17
  115. data/lib/pubid/bsi/identifiers.rb +0 -1
  116. data/lib/pubid/bsi/renderer.rb +1050 -0
  117. data/lib/pubid/bsi/single_identifier.rb +33 -78
  118. data/lib/pubid/bsi/urn_generator.rb +2 -3
  119. data/lib/pubid/bsi/urn_parser.rb +52 -0
  120. data/lib/pubid/bsi.rb +232 -2
  121. data/lib/pubid/builder/base.rb +57 -10
  122. data/lib/pubid/bundled_identifier.rb +0 -1
  123. data/lib/pubid/ccsds/builder.rb +4 -3
  124. data/lib/pubid/ccsds/identifier.rb +53 -68
  125. data/lib/pubid/ccsds/identifiers/base.rb +11 -61
  126. data/lib/pubid/ccsds/identifiers/corrigendum.rb +7 -6
  127. data/lib/pubid/ccsds/parser.rb +4 -2
  128. data/lib/pubid/ccsds/supplement_identifier.rb +15 -11
  129. data/lib/pubid/ccsds/urn_generator.rb +3 -3
  130. data/lib/pubid/ccsds/urn_parser.rb +20 -0
  131. data/lib/pubid/ccsds.rb +39 -1
  132. data/lib/pubid/cen_cenelec/builder.rb +12 -14
  133. data/lib/pubid/cen_cenelec/identifier.rb +11 -40
  134. data/lib/pubid/cen_cenelec/identifiers/adopted_european_norm.rb +9 -4
  135. data/lib/pubid/cen_cenelec/identifiers/amendment.rb +0 -10
  136. data/lib/pubid/cen_cenelec/identifiers/base.rb +2 -45
  137. data/lib/pubid/cen_cenelec/identifiers/cen_report.rb +2 -1
  138. data/lib/pubid/cen_cenelec/identifiers/cen_workshop_agreement.rb +2 -1
  139. data/lib/pubid/cen_cenelec/identifiers/consolidated_identifier.rb +0 -27
  140. data/lib/pubid/cen_cenelec/identifiers/corrigendum.rb +0 -15
  141. data/lib/pubid/cen_cenelec/identifiers/european_norm.rb +2 -1
  142. data/lib/pubid/cen_cenelec/identifiers/european_prestandard.rb +2 -8
  143. data/lib/pubid/cen_cenelec/identifiers/european_specification.rb +2 -1
  144. data/lib/pubid/cen_cenelec/identifiers/fragment.rb +0 -4
  145. data/lib/pubid/cen_cenelec/identifiers/harmonization_document.rb +2 -1
  146. data/lib/pubid/cen_cenelec/identifiers/technical_report.rb +2 -1
  147. data/lib/pubid/cen_cenelec/identifiers/technical_specification.rb +2 -1
  148. data/lib/pubid/cen_cenelec/renderer.rb +261 -0
  149. data/lib/pubid/cen_cenelec/single_identifier.rb +9 -90
  150. data/lib/pubid/cen_cenelec/urn_generator.rb +6 -6
  151. data/lib/pubid/cen_cenelec/urn_parser.rb +28 -0
  152. data/lib/pubid/cen_cenelec.rb +172 -1
  153. data/lib/pubid/cie/components/code.rb +8 -0
  154. data/lib/pubid/cie/identifier.rb +10 -57
  155. data/lib/pubid/cie/single_identifier.rb +4 -4
  156. data/lib/pubid/cie/urn_parser.rb +28 -0
  157. data/lib/pubid/cie.rb +47 -1
  158. data/lib/pubid/components/adoption.rb +104 -0
  159. data/lib/pubid/components/code.rb +22 -8
  160. data/lib/pubid/components/date.rb +23 -16
  161. data/lib/pubid/components/edition.rb +9 -6
  162. data/lib/pubid/components/iteration.rb +32 -0
  163. data/lib/pubid/components/language.rb +6 -4
  164. data/lib/pubid/components/locality.rb +10 -1
  165. data/lib/pubid/components/publisher.rb +9 -6
  166. data/lib/pubid/components/relationship.rb +151 -0
  167. data/lib/pubid/components/stage.rb +5 -14
  168. data/lib/pubid/components/supplement.rb +184 -0
  169. data/lib/pubid/components/type.rb +5 -15
  170. data/lib/pubid/components/typed_stage.rb +10 -11
  171. data/lib/pubid/components.rb +4 -1
  172. data/lib/pubid/core/update_codes.rb +28 -7
  173. data/lib/pubid/csa/identifier.rb +9 -59
  174. data/lib/pubid/csa/identifiers/base.rb +0 -123
  175. data/lib/pubid/csa/identifiers/cec.rb +0 -103
  176. data/lib/pubid/csa/identifiers/series.rb +0 -104
  177. data/lib/pubid/csa/renderer.rb +292 -0
  178. data/lib/pubid/csa/single_identifier.rb +1 -1
  179. data/lib/pubid/csa/urn_generator.rb +1 -1
  180. data/lib/pubid/csa/urn_parser.rb +33 -0
  181. data/lib/pubid/csa.rb +46 -1
  182. data/lib/pubid/etsi/components/code.rb +10 -9
  183. data/lib/pubid/etsi/components/version.rb +0 -5
  184. data/lib/pubid/etsi/identifier.rb +4 -55
  185. data/lib/pubid/etsi/identifiers/base.rb +31 -27
  186. data/lib/pubid/etsi/identifiers/supplement_identifier.rb +1 -11
  187. data/lib/pubid/etsi/renderer.rb +42 -0
  188. data/lib/pubid/etsi/urn_parser.rb +34 -0
  189. data/lib/pubid/etsi.rb +44 -1
  190. data/lib/pubid/export/exporter.rb +4 -46
  191. data/lib/pubid/export/flavor_exporter.rb +111 -278
  192. data/lib/pubid/export.rb +0 -6
  193. data/lib/pubid/identifier.rb +92 -18
  194. data/lib/pubid/identifier_metadata.rb +1 -1
  195. data/lib/pubid/idf/builder.rb +3 -3
  196. data/lib/pubid/idf/identifier.rb +7 -66
  197. data/lib/pubid/idf/identifiers/amendment.rb +2 -1
  198. data/lib/pubid/idf/identifiers/corrigendum.rb +2 -1
  199. data/lib/pubid/idf/identifiers/international_standard.rb +2 -1
  200. data/lib/pubid/idf/identifiers/reviewed_method.rb +2 -1
  201. data/lib/pubid/idf/parser.rb +3 -2
  202. data/lib/pubid/idf/renderer.rb +84 -0
  203. data/lib/pubid/idf/supplement_identifier.rb +2 -10
  204. data/lib/pubid/idf/urn_generator.rb +4 -39
  205. data/lib/pubid/idf/urn_parser.rb +25 -0
  206. data/lib/pubid/idf.rb +55 -1
  207. data/lib/pubid/iec/builder.rb +46 -64
  208. data/lib/pubid/iec/components/code.rb +8 -32
  209. data/lib/pubid/iec/components/publisher.rb +0 -1
  210. data/lib/pubid/iec/components.rb +14 -0
  211. data/lib/pubid/iec/identifier.rb +251 -213
  212. data/lib/pubid/iec/identifiers/amendment.rb +2 -3
  213. data/lib/pubid/iec/identifiers/base.rb +5 -33
  214. data/lib/pubid/iec/identifiers/component_specification.rb +3 -3
  215. data/lib/pubid/iec/identifiers/conformity_assessment.rb +1 -2
  216. data/lib/pubid/iec/identifiers/consolidated_identifier.rb +23 -26
  217. data/lib/pubid/iec/identifiers/corrigendum.rb +2 -3
  218. data/lib/pubid/iec/identifiers/fragment_identifier.rb +35 -24
  219. data/lib/pubid/iec/identifiers/guide.rb +0 -2
  220. data/lib/pubid/iec/identifiers/international_standard.rb +2 -3
  221. data/lib/pubid/iec/identifiers/interpretation_sheet.rb +2 -3
  222. data/lib/pubid/iec/identifiers/operational_document.rb +3 -3
  223. data/lib/pubid/iec/identifiers/publicly_available_specification.rb +2 -3
  224. data/lib/pubid/iec/identifiers/sheet_identifier.rb +17 -11
  225. data/lib/pubid/iec/identifiers/societal_technology_trend_report.rb +3 -3
  226. data/lib/pubid/iec/identifiers/systems_reference_document.rb +2 -3
  227. data/lib/pubid/iec/identifiers/technical_report.rb +2 -3
  228. data/lib/pubid/iec/identifiers/technical_specification.rb +2 -3
  229. data/lib/pubid/iec/identifiers/technology_report.rb +1 -2
  230. data/lib/pubid/iec/identifiers/test_report_form.rb +3 -35
  231. data/lib/pubid/iec/identifiers/vap_identifier.rb +24 -21
  232. data/lib/pubid/iec/identifiers/white_paper.rb +3 -3
  233. data/lib/pubid/iec/identifiers/working_document.rb +2 -50
  234. data/lib/pubid/iec/identifiers.rb +30 -0
  235. data/lib/pubid/iec/parser.rb +13 -12
  236. data/lib/pubid/iec/renderer.rb +254 -0
  237. data/lib/pubid/iec/single_identifier.rb +6 -12
  238. data/lib/pubid/iec/supplement_identifier.rb +58 -54
  239. data/lib/pubid/iec/urn_generator.rb +3 -3
  240. data/lib/pubid/iec/urn_parser.rb +3 -3
  241. data/lib/pubid/iec.rb +43 -67
  242. data/lib/pubid/ieee/builder.rb +12 -12
  243. data/lib/pubid/ieee/components/code.rb +8 -0
  244. data/lib/pubid/ieee/components/draft.rb +14 -0
  245. data/lib/pubid/ieee/components/relationship.rb +5 -149
  246. data/lib/pubid/ieee/identifier.rb +5 -52
  247. data/lib/pubid/ieee/identifiers/adopted_standard.rb +0 -9
  248. data/lib/pubid/ieee/identifiers/base.rb +221 -556
  249. data/lib/pubid/ieee/identifiers/conformance_identifier.rb +0 -10
  250. data/lib/pubid/ieee/identifiers/corrigendum.rb +0 -12
  251. data/lib/pubid/ieee/identifiers/csa_dual_published.rb +0 -10
  252. data/lib/pubid/ieee/identifiers/dual_identifier.rb +0 -4
  253. data/lib/pubid/ieee/identifiers/dual_published.rb +0 -4
  254. data/lib/pubid/ieee/identifiers/iec_ieee_copublished.rb +0 -9
  255. data/lib/pubid/ieee/identifiers/interpretation_identifier.rb +0 -10
  256. data/lib/pubid/ieee/identifiers/joint_development.rb +2 -0
  257. data/lib/pubid/ieee/identifiers/multi_numbered_identifier.rb +0 -18
  258. data/lib/pubid/ieee/identifiers/parenthetical_identifier.rb +0 -6
  259. data/lib/pubid/ieee/identifiers/project_draft_identifier.rb +15 -0
  260. data/lib/pubid/ieee/identifiers/redlined_standard.rb +0 -7
  261. data/lib/pubid/ieee/identifiers/si_standard.rb +0 -38
  262. data/lib/pubid/ieee/identifiers/standard.rb +1 -1
  263. data/lib/pubid/ieee/pre_parser.rb +301 -0
  264. data/lib/pubid/ieee/renderer.rb +307 -0
  265. data/lib/pubid/ieee/urn_parser.rb +34 -0
  266. data/lib/pubid/ieee.rb +66 -1
  267. data/lib/pubid/ieee_debug.rb +0 -1
  268. data/lib/pubid/iho/builder.rb +2 -2
  269. data/lib/pubid/iho/identifier.rb +7 -59
  270. data/lib/pubid/iho/identifiers/base.rb +64 -31
  271. data/lib/pubid/iho/parser.rb +3 -3
  272. data/lib/pubid/iho/renderer.rb +30 -0
  273. data/lib/pubid/iho/urn_generator.rb +2 -2
  274. data/lib/pubid/iho/urn_parser.rb +58 -0
  275. data/lib/pubid/iho.rb +52 -1
  276. data/lib/pubid/iso/builder.rb +55 -53
  277. data/lib/pubid/iso/bundled_identifier.rb +51 -0
  278. data/lib/pubid/iso/components/code.rb +7 -19
  279. data/lib/pubid/iso/components/publisher.rb +10 -8
  280. data/lib/pubid/iso/components.rb +2 -4
  281. data/lib/pubid/iso/identifier.rb +219 -252
  282. data/lib/pubid/iso/identifiers/addendum.rb +9 -6
  283. data/lib/pubid/iso/identifiers/amendment.rb +8 -4
  284. data/lib/pubid/iso/identifiers/corrigendum.rb +4 -4
  285. data/lib/pubid/iso/identifiers/data.rb +0 -1
  286. data/lib/pubid/iso/identifiers/directives.rb +8 -2
  287. data/lib/pubid/iso/identifiers/directives_supplement.rb +43 -14
  288. data/lib/pubid/iso/identifiers/extract.rb +2 -2
  289. data/lib/pubid/iso/identifiers/guide.rb +0 -1
  290. data/lib/pubid/iso/identifiers/international_standard.rb +4 -4
  291. data/lib/pubid/iso/identifiers/international_standardized_profile.rb +4 -4
  292. data/lib/pubid/iso/identifiers/international_workshop_agreement.rb +10 -4
  293. data/lib/pubid/iso/identifiers/pas.rb +2 -2
  294. data/lib/pubid/iso/identifiers/recommendation.rb +2 -2
  295. data/lib/pubid/iso/identifiers/supplement.rb +11 -3
  296. data/lib/pubid/iso/identifiers/tc_document.rb +44 -15
  297. data/lib/pubid/iso/identifiers/technical_report.rb +4 -4
  298. data/lib/pubid/iso/identifiers/technical_specification.rb +2 -2
  299. data/lib/pubid/iso/identifiers/technology_trends_assessments.rb +2 -2
  300. data/lib/pubid/iso/identifiers.rb +0 -1
  301. data/lib/pubid/iso/normalizer.rb +89 -0
  302. data/lib/pubid/iso/parser.rb +22 -4
  303. data/lib/pubid/iso/single_identifier.rb +0 -4
  304. data/lib/pubid/iso/supplement_identifier.rb +15 -2
  305. data/lib/pubid/iso/urn_generator.rb +66 -182
  306. data/lib/pubid/iso/urn_parser.rb +12 -7
  307. data/lib/pubid/iso.rb +177 -2
  308. data/lib/pubid/itu/builder.rb +0 -12
  309. data/lib/pubid/itu/components/code.rb +9 -7
  310. data/lib/pubid/itu/components/sector.rb +15 -4
  311. data/lib/pubid/itu/components/series.rb +0 -4
  312. data/lib/pubid/itu/components.rb +11 -0
  313. data/lib/pubid/itu/identifier.rb +4 -116
  314. data/lib/pubid/itu/identifiers/amendment.rb +0 -2
  315. data/lib/pubid/itu/identifiers/annex.rb +0 -2
  316. data/lib/pubid/itu/identifiers/base.rb +124 -128
  317. data/lib/pubid/itu/identifiers/combined_identifier.rb +0 -2
  318. data/lib/pubid/itu/identifiers/corrigendum.rb +0 -2
  319. data/lib/pubid/itu/identifiers/recommendation.rb +0 -2
  320. data/lib/pubid/itu/identifiers/special_publication.rb +0 -2
  321. data/lib/pubid/itu/identifiers/supplement.rb +0 -2
  322. data/lib/pubid/itu/urn_parser.rb +23 -0
  323. data/lib/pubid/itu.rb +44 -1
  324. data/lib/pubid/jcgm/builder.rb +16 -8
  325. data/lib/pubid/jcgm/identifier.rb +0 -43
  326. data/lib/pubid/jcgm/identifiers/amendment.rb +0 -9
  327. data/lib/pubid/jcgm/identifiers/gum_guide.rb +0 -12
  328. data/lib/pubid/jcgm/renderer.rb +68 -0
  329. data/lib/pubid/jcgm/single_identifier.rb +0 -8
  330. data/lib/pubid/jcgm/urn_generator.rb +4 -6
  331. data/lib/pubid/jcgm/urn_parser.rb +23 -0
  332. data/lib/pubid/jcgm.rb +43 -2
  333. data/lib/pubid/jis/builder.rb +44 -52
  334. data/lib/pubid/jis/identifier.rb +120 -46
  335. data/lib/pubid/jis/identifiers/amendment.rb +1 -1
  336. data/lib/pubid/jis/identifiers/corrigendum.rb +16 -0
  337. data/lib/pubid/jis/identifiers/standard.rb +2 -1
  338. data/lib/pubid/jis/identifiers/technical_report.rb +2 -1
  339. data/lib/pubid/jis/identifiers/technical_specification.rb +2 -1
  340. data/lib/pubid/jis/identifiers.rb +1 -1
  341. data/lib/pubid/jis/parser.rb +31 -5
  342. data/lib/pubid/jis/renderer.rb +69 -0
  343. data/lib/pubid/jis/single_identifier.rb +6 -12
  344. data/lib/pubid/jis/supplement_identifier.rb +17 -14
  345. data/lib/pubid/jis/urn_parser.rb +23 -0
  346. data/lib/pubid/jis.rb +42 -2
  347. data/lib/pubid/nist/builder.rb +63 -1871
  348. data/lib/pubid/nist/caster.rb +1272 -0
  349. data/lib/pubid/nist/circular_supplement_builder.rb +291 -0
  350. data/lib/pubid/nist/components/code.rb +9 -20
  351. data/lib/pubid/nist/components/supplement.rb +2 -2
  352. data/lib/pubid/nist/components.rb +0 -1
  353. data/lib/pubid/nist/identifier.rb +8 -60
  354. data/lib/pubid/nist/identifiers/base.rb +564 -492
  355. data/lib/pubid/nist/identifiers/circular.rb +7 -2
  356. data/lib/pubid/nist/identifiers/circular_supplement.rb +2 -1
  357. data/lib/pubid/nist/identifiers/commercial_standard.rb +2 -1
  358. data/lib/pubid/nist/identifiers/commercial_standard_emergency.rb +6 -4
  359. data/lib/pubid/nist/identifiers/commercial_standards_monthly.rb +10 -3
  360. data/lib/pubid/nist/identifiers/crpl_report.rb +8 -8
  361. data/lib/pubid/nist/identifiers/dated_document.rb +49 -0
  362. data/lib/pubid/nist/identifiers/federal_information_processing_standards.rb +15 -24
  363. data/lib/pubid/nist/identifiers/grant_contractor_report.rb +2 -1
  364. data/lib/pubid/nist/identifiers/handbook.rb +2 -1
  365. data/lib/pubid/nist/identifiers/internal_report.rb +2 -1
  366. data/lib/pubid/nist/identifiers/letter_circular.rb +2 -1
  367. data/lib/pubid/nist/identifiers/miscellaneous_publication.rb +5 -4
  368. data/lib/pubid/nist/identifiers/monograph.rb +7 -3
  369. data/lib/pubid/nist/identifiers/report.rb +4 -2
  370. data/lib/pubid/nist/identifiers/special_publication.rb +2 -1
  371. data/lib/pubid/nist/identifiers/technical_note.rb +3 -2
  372. data/lib/pubid/nist/identifiers.rb +1 -0
  373. data/lib/pubid/nist/parser.rb +62 -452
  374. data/lib/pubid/nist/parser_output_normalizer.rb +233 -0
  375. data/lib/pubid/nist/preprocessor.rb +428 -0
  376. data/lib/pubid/nist/renderer.rb +43 -0
  377. data/lib/pubid/nist/router.rb +148 -0
  378. data/lib/pubid/nist/series/base.rb +58 -0
  379. data/lib/pubid/nist/series/crpl.rb +13 -0
  380. data/lib/pubid/nist/series/fips.rb +14 -0
  381. data/lib/pubid/nist/series/ir.rb +60 -0
  382. data/lib/pubid/nist/series/letter_preserving.rb +15 -0
  383. data/lib/pubid/nist/series/mono.rb +19 -0
  384. data/lib/pubid/nist/series/ncstar.rb +20 -0
  385. data/lib/pubid/nist/series.rb +49 -0
  386. data/lib/pubid/nist/supplement_identifier.rb +3 -1
  387. data/lib/pubid/nist/urn_parser.rb +67 -0
  388. data/lib/pubid/nist.rb +86 -4
  389. data/lib/pubid/oiml/builder.rb +21 -3
  390. data/lib/pubid/oiml/components/code.rb +17 -0
  391. data/lib/pubid/oiml/identifier.rb +40 -46
  392. data/lib/pubid/oiml/identifiers/annex.rb +13 -46
  393. data/lib/pubid/oiml/identifiers/errata.rb +15 -0
  394. data/lib/pubid/oiml/identifiers.rb +1 -1
  395. data/lib/pubid/oiml/parser.rb +59 -12
  396. data/lib/pubid/oiml/renderer.rb +171 -0
  397. data/lib/pubid/oiml/single_identifier.rb +61 -42
  398. data/lib/pubid/oiml/supplement_identifier.rb +31 -17
  399. data/lib/pubid/oiml/urn_generator.rb +0 -8
  400. data/lib/pubid/oiml/urn_parser.rb +22 -0
  401. data/lib/pubid/oiml.rb +42 -1
  402. data/lib/pubid/plateau/identifier.rb +4 -55
  403. data/lib/pubid/plateau/identifiers/base.rb +45 -36
  404. data/lib/pubid/plateau/identifiers/handbook.rb +0 -6
  405. data/lib/pubid/plateau/identifiers/technical_report.rb +0 -4
  406. data/lib/pubid/plateau/renderer.rb +51 -0
  407. data/lib/pubid/plateau/supplement_identifier.rb +0 -4
  408. data/lib/pubid/plateau/urn_parser.rb +43 -0
  409. data/lib/pubid/plateau.rb +43 -1
  410. data/lib/pubid/renderers/directives_renderer.rb +22 -8
  411. data/lib/pubid/renderers/guide_renderer.rb +4 -2
  412. data/lib/pubid/renderers/human_readable.rb +18 -7
  413. data/lib/pubid/rendering/context.rb +28 -19
  414. data/lib/pubid/rendering.rb +0 -3
  415. data/lib/pubid/sae/components/date.rb +8 -0
  416. data/lib/pubid/sae/components/type.rb +5 -1
  417. data/lib/pubid/sae/identifier.rb +4 -35
  418. data/lib/pubid/sae/identifiers/base.rb +26 -31
  419. data/lib/pubid/sae/renderer.rb +36 -0
  420. data/lib/pubid/sae/urn_generator.rb +2 -10
  421. data/lib/pubid/sae/urn_parser.rb +36 -0
  422. data/lib/pubid/sae.rb +42 -1
  423. data/lib/pubid/urn_generator/base.rb +12 -12
  424. data/lib/pubid/urn_parser/base.rb +81 -0
  425. data/lib/pubid/urn_parser/errors.rb +9 -0
  426. data/lib/pubid/urn_parser.rb +14 -0
  427. data/lib/pubid/version.rb +1 -1
  428. data/lib/pubid.rb +45 -7
  429. data/lib/tasks/export.rake +6 -5
  430. data/lib/tasks/website-data.json +1940 -1882
  431. metadata +75 -45
  432. data/lib/pubid/amca/scheme.rb +0 -16
  433. data/lib/pubid/ansi/scheme.rb +0 -15
  434. data/lib/pubid/api/scheme.rb +0 -66
  435. data/lib/pubid/ashrae/scheme.rb +0 -53
  436. data/lib/pubid/asme/scheme.rb +0 -37
  437. data/lib/pubid/astm/scheme.rb +0 -55
  438. data/lib/pubid/bsi/identifiers/base.rb +0 -11
  439. data/lib/pubid/bsi/scheme.rb +0 -243
  440. data/lib/pubid/ccsds/scheme.rb +0 -57
  441. data/lib/pubid/cen_cenelec/scheme.rb +0 -164
  442. data/lib/pubid/cie/scheme.rb +0 -64
  443. data/lib/pubid/components/factory.rb +0 -50
  444. data/lib/pubid/csa/scheme.rb +0 -44
  445. data/lib/pubid/etsi/scheme.rb +0 -42
  446. data/lib/pubid/export/data_class_exporter.rb +0 -59
  447. data/lib/pubid/export/ieee_exporter.rb +0 -78
  448. data/lib/pubid/export/itu_exporter.rb +0 -66
  449. data/lib/pubid/export/nist_exporter.rb +0 -64
  450. data/lib/pubid/export/registry_exporter.rb +0 -90
  451. data/lib/pubid/export/scheme_exporter.rb +0 -70
  452. data/lib/pubid/identifier_registry.rb +0 -198
  453. data/lib/pubid/idf/scheme.rb +0 -61
  454. data/lib/pubid/iec/scheme.rb +0 -71
  455. data/lib/pubid/ieee/scheme.rb +0 -90
  456. data/lib/pubid/iho/scheme.rb +0 -29
  457. data/lib/pubid/iso/identifiers/base.rb +0 -115
  458. data/lib/pubid/iso/scheme.rb +0 -193
  459. data/lib/pubid/itu/scheme.rb +0 -174
  460. data/lib/pubid/jcgm/scheme.rb +0 -60
  461. data/lib/pubid/jis/components/code.rb +0 -59
  462. data/lib/pubid/jis/identifiers/base.rb +0 -72
  463. data/lib/pubid/jis/scheme.rb +0 -49
  464. data/lib/pubid/nist/components/publisher.rb +0 -24
  465. data/lib/pubid/nist/scheme.rb +0 -199
  466. data/lib/pubid/oiml/identifiers/base.rb +0 -36
  467. data/lib/pubid/oiml/scheme.rb +0 -46
  468. data/lib/pubid/plateau/scheme.rb +0 -45
  469. data/lib/pubid/rendering/base.rb +0 -73
  470. data/lib/pubid/rendering/common.rb +0 -211
  471. data/lib/pubid/rendering/format.rb +0 -25
  472. data/lib/pubid/sae/scheme.rb +0 -47
  473. data/lib/pubid/scheme.rb +0 -219
@@ -6,586 +6,251 @@ module Pubid
6
6
  # Forward declare component classes
7
7
  end
8
8
 
9
- module Identifiers
10
- # Base class for all IEEE identifiers
11
- class Base < Pubid::Identifier
12
- # Generate URN for this identifier
13
- #
14
- # @return [String] URN representation
15
-
16
- attribute :publisher, :string, default: -> { "IEEE" }
17
- attribute :copublisher, :string, collection: true # IEC, ISO, ANSI, etc.
18
- attribute :code, :string # Will store code as object in initialize
19
- attribute :year, :string
20
- attribute :type, :string, default: -> { "Std" } # Std, Draft Std
21
- attribute :draft_status, :string # Unapproved, Approved, Active Unapproved
22
- attribute :draft, :string # Will store draft as object
23
- attribute :edition, :string # Edition 1.0
24
- attribute :month, :string
25
- attribute :day, :string
26
- attribute :redline, :boolean, default: -> { false }
27
- attribute :amendments, Base, collection: true # Amendment identifiers
28
- attribute :corrigenda, Base, collection: true # Corrigendum identifiers
29
- attribute :revision_of, Base # Revision relationships
30
- attribute :incorporates, Base, collection: true # Incorporated documents
31
- attribute :supersedes, Base, collection: true # Superseded documents
32
- attribute :supplement_to, Base # For supplements
33
- attribute :iso_identifier, :string # For IEC/IEEE formats
34
- attribute :parenthetical_content, :string # Raw parenthetical content
35
- attribute :note, :string # Parenthetical notes
36
- attribute :adoption, :string # Adoption notes
37
- attribute :amendment_to, :string # Amendment to relationships
38
- attribute :edition_month, :string # Month part from Edition YYYY-MM
39
- attribute :space_before_draft, :boolean, default: -> {
40
- false
41
- } # Track space before /D
42
- attribute :typed_stage, Components::TypedStage # TYPED_STAGE integration
43
- attribute :relationships, Components::Relationship, collection: true # Relationship metadata
44
- attribute :nickname, :string # Book nickname (e.g., "[The Orange Book]")
45
- attribute :interpretation, :boolean, default: -> {
46
- false
47
- } # /INT notation
48
- attribute :conf_number, :string # Conformance document number
49
- attribute :conf_year, :string # Conformance document year
50
- attribute :ashrae_number, :string # ASHRAE Guideline number
51
- attribute :ashrae_year, :string # ASHRAE Guideline year
52
- attribute :crossref, :string # IEEE cross-reference (e.g., /C62.22.1-1996)
53
- attribute :reaffirmed, :string # Reaffirmed year (e.g., "2010" for (R2010))
54
-
55
- # Store actual component objects
56
- attr_accessor :code_obj, :draft_obj
57
-
58
- def initialize(**args)
59
- super()
60
-
61
- # Handle typed_stage if provided
62
- if args[:typed_stage]
63
- self.typed_stage = args[:typed_stage]
64
- end
65
-
66
- # Handle code as component object
67
- if args[:code].is_a?(String)
68
- self.code_obj = Components::Code.parse(args[:code])
69
- self.code = args[:code]
70
- elsif args[:code]
71
- self.code_obj = args[:code]
72
- self.code = args[:code].to_s
73
- end
74
-
75
- # Handle draft as component object
76
- if args[:draft_obj]
77
- self.draft_obj = args[:draft_obj]
78
- self.draft = args[:draft_obj].to_s
79
- elsif args[:draft]
80
- # If draft is passed as string, try to create Draft object
81
- if args[:draft].is_a?(String)
82
- # Try to parse the string to extract version/revision
83
- if args[:draft] =~ /^D(\d+)(?:\.(\d+))?/
84
- version = $1
85
- revision = $2
86
- self.draft_obj = Components::Draft.new(version: version,
87
- revision: revision)
88
- else
89
- # Simple case - treat as version
90
- self.draft_obj = Components::Draft.new(version: args[:draft])
91
- end
92
- self.draft = draft_obj.to_s
93
- else
94
- self.draft_obj = args[:draft]
95
- self.draft = args[:draft].to_s
96
- end
97
- end
98
-
99
- # Set other attributes
100
- attrs = self.class.attributes
101
- args.each do |key, value|
102
- next if %i[code draft draft_obj typed_stage].include?(key)
103
-
104
- setter = :"#{key}="
105
- public_send(setter, value) if attrs.key?(key)
106
- end
9
+ # Base class for all IEEE identifiers. Canonical name
10
+ # Pubid::Ieee::Identifier (Identifiers::Base is a back-compat alias). IEEE
11
+ # builds its identifiers as instances of this class directly.
12
+ class Identifier < ::Pubid::Identifier
13
+ # Generate URN for this identifier
14
+ #
15
+ # @return [String] URN representation
16
+
17
+ attribute :publisher, :string, default: -> { "IEEE" }
18
+ attribute :copublisher, :string, collection: true # IEC, ISO, ANSI, etc.
19
+ attribute :code, :string # Will store code as object in initialize
20
+ attribute :year, :string
21
+ attribute :type, :string, default: -> { "Std" } # Std, Draft Std
22
+ attribute :draft_status, :string # Unapproved, Approved, Active Unapproved
23
+ attribute :draft, :string # Will store draft as object
24
+ attribute :edition, :string # Edition 1.0
25
+ attribute :month, :string
26
+ attribute :day, :string
27
+ attribute :redline, :boolean, default: -> { false }
28
+ attribute :amendments, Identifier, collection: true # Amendment identifiers
29
+ attribute :corrigenda, Identifier, collection: true # Corrigendum identifiers
30
+ attribute :revision_of, Identifier # Revision relationships
31
+ attribute :incorporates, Identifier, collection: true # Incorporated documents
32
+ attribute :supersedes, Identifier, collection: true # Superseded documents
33
+ attribute :supplement_to, Identifier # For supplements
34
+ attribute :iso_identifier, :string # For IEC/IEEE formats
35
+ attribute :parenthetical_content, :string # Raw parenthetical content
36
+ attribute :note, :string # Parenthetical notes
37
+ attribute :adoption, :string # Adoption notes
38
+ attribute :amendment_to, :string # Amendment to relationships
39
+ attribute :edition_month, :string # Month part from Edition YYYY-MM
40
+ attribute :space_before_draft, :boolean, default: -> {
41
+ false
42
+ } # Track space before /D
43
+ attribute :typed_stage, Components::TypedStage # TYPED_STAGE integration
44
+ attribute :relationships, Components::Relationship, collection: true # Relationship metadata
45
+ attribute :nickname, :string # Book nickname (e.g., "[The Orange Book]")
46
+ attribute :interpretation, :boolean, default: -> {
47
+ false
48
+ } # /INT notation
49
+ attribute :conf_number, :string # Conformance document number
50
+ attribute :conf_year, :string # Conformance document year
51
+ attribute :ashrae_number, :string # ASHRAE Guideline number
52
+ attribute :ashrae_year, :string # ASHRAE Guideline year
53
+ attribute :crossref, :string # IEEE cross-reference (e.g., /C62.22.1-1996)
54
+ attribute :reaffirmed, :string # Reaffirmed year (e.g., "2010" for (R2010))
55
+
56
+ # Store actual component objects
57
+ attr_accessor :code_obj, :draft_obj
58
+
59
+ def initialize(**args)
60
+ super()
61
+
62
+ # Handle typed_stage if provided
63
+ if args[:typed_stage]
64
+ self.typed_stage = args[:typed_stage]
107
65
  end
108
66
 
109
- # Override accessors to return component objects
110
- def code
111
- code_obj
67
+ # Handle code as component object
68
+ if args[:code].is_a?(String)
69
+ self.code_obj = Components::Code.parse(args[:code])
70
+ self.code = args[:code]
71
+ elsif args[:code]
72
+ self.code_obj = args[:code]
73
+ self.code = args[:code].to_s
112
74
  end
113
75
 
114
- def draft
115
- draft_obj
76
+ # Handle draft as component object
77
+ if args[:draft_obj]
78
+ self.draft_obj = args[:draft_obj]
79
+ self.draft = args[:draft_obj].to_s
80
+ elsif args[:draft].is_a?(String)
81
+ self.draft_obj = Components::Draft.parse(args[:draft])
82
+ self.draft = draft_obj.to_s
83
+ elsif args[:draft]
84
+ self.draft_obj = args[:draft]
85
+ self.draft = args[:draft].to_s
116
86
  end
117
87
 
118
- # Expose numeric month from draft if available
119
- def draft_month
120
- return nil unless draft_obj.is_a?(Components::Draft)
88
+ # Set other attributes
89
+ attrs = self.class.attributes
90
+ args.each do |key, value|
91
+ next if %i[code draft draft_obj typed_stage].include?(key)
121
92
 
122
- draft_obj.numeric_month
93
+ setter = :"#{key}="
94
+ public_send(setter, value) if attrs.key?(key)
123
95
  end
96
+ end
124
97
 
125
- # Parse IEEE identifier string
126
- def self.parse(input)
127
- # Preprocessing: Convert comma-separated dual standards to "and" format
128
- # This must happen BEFORE the "and" check below
129
- # Pattern: "IEEE Std 960-1989, Std 1177-1989" -> "IEEE Std 960-1989 and IEEE Std 1177-1989"
130
- input = input.gsub(/(\d{4}),\s+Std\s/, '\1 and IEEE Std ')
131
-
132
- # Check for AIEE identifiers (but NOT those with parentheses which have relationships/adoptions)
133
- # AIEE has its own parser/builder for simple identifiers
134
- # AIEE inputs with parentheses are handled by IEEE parser for relationship/adoption parsing
135
- if input.start_with?("AIEE ") && !input.include?("(")
136
- return Aiee::Identifier.parse(input)
137
- end
138
-
139
- # Check for IEC/IEEE copublished patterns first (before other checks)
140
- if input.start_with?("IEC/IEEE ")
141
- return parse_single(input)
142
- end
143
-
144
- # Check for semicolon-separated dual identifiers (IEC-first patterns)
145
- # Pattern: "IEC 61523-3 First edition 2004-09; IEEE 1497"
146
- if input.include?("; ")
147
- parts = input.split("; ")
148
- if parts.length == 2
149
- # Try to parse both parts
150
- begin
151
- first = parse_single(parts[0].strip)
152
- second = parse_single(parts[1].strip)
153
-
154
- return Identifiers::DualPublished.new(
155
- first_identifier: first,
156
- second_identifier: second,
157
- )
158
- rescue Parslet::ParseFailed
159
- # If parsing fails, continue with normal flow
160
- end
161
- end
162
- end
163
-
164
- # PREPROCESS: Handle (R####) (Revision of...) pattern
165
- # Convert to single parenthetical: (Reaffirmed ####, Revision of...)
166
- # This allows parser to capture both in one parenthetical
167
- if input =~ /\(R(\d{4})\)\s*\(Revision of ([^)]+)\)/
168
- year = $1
169
- $2
170
- # Replace the two parentheticals with reaffirmed info extracted
171
- # Parse normally but capture year first
172
- input_modified = input.sub(
173
- /\(R(\d{4})\)\s*\(Revision of ([^)]+)\)/, "(Revision of \\2)"
174
- )
175
-
176
- # Parse the modified input
177
- result = parse_single(input_modified)
178
- # Add reaffirmed attribute
179
- if result.class.attributes.key?(:reaffirmed)
180
- result.reaffirmed = year
181
- end
182
- return result
183
- end
184
-
185
- # PREPROCESS: Handle (Reaffirmed ####) (Revision of...) pattern (full word format)
186
- # Pattern: "ANSI/IEEE Std 101-1987 (Reaffirmed 2010) (Revision of IEEE Std 101-1972)"
187
- if input =~ /\(Reaffirmed\s+(\d{4})\)\s*\(Revision of ([^)]+)\)/
188
- year = $1
189
- $2
190
- # Replace the two parentheticals
191
- input_modified = input.sub(
192
- /\(Reaffirmed\s+(\d{4})\)\s*\(Revision of ([^)]+)\)/, "(Revision of \\2)"
193
- )
194
-
195
- # Parse the modified input
196
- result = parse_single(input_modified)
197
- # Add reaffirmed attribute
198
- if result.class.attributes.key?(:reaffirmed)
199
- result.reaffirmed = year
200
- end
201
- return result
202
- end
203
-
204
- # NEW Session 174: Check for IRE dual published pattern
205
- # Pattern after preprocessing: "IEEE Std 218-1956 (R1980) (56 IRE 28.S2)"
206
- # First parenthetical: (Rxxx) reaffirmed
207
- # Second parenthetical: IRE identifier
208
- if /\(R\d{4}\)\s*\((\d+\s+IRE[^)]+)\)/.match?(input)
209
- main_part = input.split(" (R").first.strip # Get "IEEE Std 218-1956"
210
- reaffirmed_year = input.match(/\(R(\d{4})\)/)[1]
211
- ire_part = input.match(/\((\d+\s+IRE[^)]+)\)/)[1]
212
-
213
- begin
214
- # Parse main identifier
215
- ieee_id = parse_single(main_part)
216
- # Add reaffirmed year
217
- ieee_id.reaffirmed = reaffirmed_year if ieee_id.class.attributes.key?(:reaffirmed)
218
-
219
- # Parse IRE identifier
220
- ire_id = parse_single(ire_part)
221
-
222
- return Identifiers::DualPublished.new(
223
- first_identifier: ieee_id,
224
- second_identifier: ire_id,
225
- )
226
- rescue Parslet::ParseFailed
227
- # If parsing fails, fall through to regular processing
228
- end
229
- end
230
-
231
- # Check for space-separated dual identifiers (e.g., "IEC 62014-5 IEEE Std 1734-2011")
232
- # This must be checked before " and " pattern
233
- # Look for pattern where a second publisher appears after the first complete identifier
234
- # Publishers: IEEE, AIEE, ANSI, ASA, IEC, ISO, ASTM, NACE, NSF, ASHRAE, NCTA, AESC
235
- publishers = %w[IEEE AIEE ANSI ASA IEC ISO ASTM NACE NSF ASHRAE NCTA
236
- AESC]
237
-
238
- # Find all positions where publishers appear (but NOT inside parentheses)
239
- # Publishers inside parentheses are part of relationship clauses, not dual published patterns
240
- publisher_positions = []
241
- publishers.each do |pub|
242
- # Look for publisher at word boundaries (preceded by space or start of string)
243
- regex = /(?:^|\s)(#{Regexp.escape(pub)})(?:\s|\/)/
244
- input.scan(regex) do
245
- match_pos = Regexp.last_match.begin(1)
246
-
247
- # Check if this publisher is inside parentheses (relationship clause)
248
- # Count parens before this position
249
- before_match = input[0...match_pos]
250
- paren_count = before_match.count("(") - before_match.count(")")
251
-
252
- # Skip publishers inside parentheses - they're part of relationship clauses
253
- next if paren_count.positive?
254
-
255
- publisher_positions << { pos: match_pos, publisher: pub }
256
- end
257
- end
258
-
259
- # If we have 2 or more publishers at distinct positions (not co-publishers with /)
260
- if publisher_positions.length >= 2
261
- # Sort by position
262
- publisher_positions.sort_by! { |p| p[:pos] }
263
-
264
- # Check if they're not part of a co-published pattern (Publisher1/Publisher2)
265
- # by ensuring there's no slash between them
266
- first_pub = publisher_positions[0]
267
- second_pub = publisher_positions[1]
268
-
269
- # Get the substring between the two publishers
270
- between = input[first_pub[:pos]..(second_pub[:pos] - 1)]
271
-
272
- # If there's no slash and no " and ", this might be space-separated dual
273
- if !between.include?("/") && !between.include?(" and ")
274
- # Try to split at the second publisher position
275
- # Back up to find the space before the second publisher
276
- split_pos = second_pub[:pos]
277
- while split_pos.positive? && input[split_pos - 1] == " "
278
- split_pos -= 1
279
- end
280
-
281
- first_part = input[0...split_pos].strip
282
- second_part = input[split_pos..].strip
283
-
284
- # Try to parse both parts
285
- begin
286
- first = parse_single(first_part)
287
- second = parse_single(second_part)
288
-
289
- # Only treat as dual if both parse successfully
290
- return Identifiers::DualPublished.new(
291
- first_identifier: first,
292
- second_identifier: second,
293
- )
294
- rescue Parslet::ParseFailed
295
- # If parsing fails, continue with normal flow
296
- end
297
- end
298
- end
299
-
300
- # Check for dual published patterns with " and "
301
- if input.include?(" and ")
302
- # DON'T split if " and " is inside parentheses (likely a relationship clause)
303
- # Check if parentheses are balanced and " and " is inside them
304
- paren_count = 0
305
- and_outside_parens = false
306
- and_position = nil
307
-
308
- input.each_char.with_index do |char, i|
309
- paren_count += 1 if char == "("
310
- paren_count -= 1 if char == ")"
311
-
312
- # Check if " and " starts at this position and we're outside parens
313
- if paren_count.zero? && input[i..(i + 4)] == " and "
314
- and_outside_parens = true
315
- and_position = i
316
- break
317
- end
318
- end
319
-
320
- # Only split if " and " is outside parentheses
321
- if and_outside_parens && and_position
322
- # Split at the found position only (not at all " and " occurrences)
323
- first_part = input[0...and_position].strip
324
- second_part = input[(and_position + 5)..].strip
325
-
326
- # Parse each part separately
327
- first = parse_single(first_part)
328
- second = parse_single(second_part)
329
-
330
- return Identifiers::DualPublished.new(
331
- first_identifier: first,
332
- second_identifier: second,
333
- )
334
- end
335
- end
336
-
337
- # NEW Session 171: Check for dual published patterns with " & " (ampersand)
338
- if input.include?(" & ")
339
- # DON'T split if " & " is inside parentheses
340
- paren_count = 0
341
- ampersand_outside_parens = false
342
-
343
- input.each_char.with_index do |char, i|
344
- paren_count += 1 if char == "("
345
- paren_count -= 1 if char == ")"
346
-
347
- # Check if " & " starts at this position and we're outside parens
348
- if paren_count.zero? && input[i..(i + 2)] == " & "
349
- ampersand_outside_parens = true
350
- break
351
- end
352
- end
353
-
354
- # Only split if " & " is outside parentheses
355
- if ampersand_outside_parens
356
- parts = input.split(" & ")
357
- if parts.length == 2
358
- # Parse each part separately
359
- first = parse_single(parts[0].strip)
360
- second = parse_single(parts[1].strip)
361
-
362
- return Identifiers::DualPublished.new(
363
- first_identifier: first,
364
- second_identifier: second,
365
- )
366
- end
367
- end
368
- end
369
-
370
- # Special case: AIEE identifiers with ASA parenthetical references
371
- # Pattern: "AIEE No 18-1934 (ASA C55 1934)"
372
- if input.match?(/^AIEE\s+/) && input.include?("(") && input.include?("ASA")
373
- main_part = input.split("(").first.strip
374
- adoption_match = input.match(/\((ASA[^)]+)\)/)
375
-
376
- if adoption_match
377
- adoption_part = adoption_match.captures.first
378
- # Parse the main AIEE identifier
379
- begin
380
- aiee_id = parse_single(main_part)
381
- # Parse the ASA identifier
382
- asa_id = parse_single(adoption_part)
98
+ # Override accessors to return component objects.
99
+ def code
100
+ code_obj
101
+ end
383
102
 
384
- return Identifiers::AdoptedStandard.new(
385
- ieee_identifier: aiee_id,
386
- adopted_identifier: asa_id,
387
- )
388
- rescue Parslet::ParseFailed
389
- # If parsing fails, fall through to regular processing
390
- end
391
- end
392
- end
103
+ def draft
104
+ draft_obj
105
+ end
393
106
 
394
- # Check for adopted standards (parenthetical adoptions)
395
- # Only consider it an adoption if the parenthetical content looks like an identifier
396
- if input.include?("(") && input.include?(")") && !input.start_with?("IEC/IEEE ")
397
- # Extract the part before parentheses and the adoption part
398
- main_part = input.split("(").first.strip
399
- adoption_match = input.match(/\(([^)]+)\)/)
400
- adoption_part = adoption_match&.captures&.first
107
+ # Lazily rebuild the parsed component objects from the underlying string
108
+ # attributes. After from_hash, lutaml restores the :code/:draft strings
109
+ # (@code/@draft) but not these objects; the renderer reads code_obj/
110
+ # draft_obj directly, so rebuild on demand to render a deserialized
111
+ # identifier identically to the parsed one. On the parse path code_obj/
112
+ # draft_obj are already set, so the `||=` returns them unchanged.
113
+ def code_obj
114
+ @code_obj ||=
115
+ (Components::Code.parse(@code.to_s) unless @code.nil? || @code.to_s.empty?)
116
+ end
401
117
 
402
- # Check if adoption_part looks like an identifier (contains publisher or type keywords)
403
- # BUT exclude revision/amendment/supersedes notes
404
- # AND exclude Pattern 4 relationship types
405
- if main_part && adoption_part &&
406
- !adoption_part.match?(/^\s*(Revision|Revison|Amendment|Corrigendum|Corrigenda|incorporates|Incorporating|Incorporates|Adoption|Supplement|Draft Amendment|DRAFT Amendment|Draft Revision|Reaffirmation|Redesignation|redesignated as|Supersedes|Supercedes|Includes|Previously designated as|Notebooks|Standard Newspaper)/i) &&
407
- (adoption_part.match?(/\b(ANSI|ISO|IEC|IEEE|AIEE|IRE|ASA|ASTM|CSA|ASME|NACE|NSF|ASHRAE|NCTA|AESC)\s/) ||
408
- adoption_part.match?(/^\s*(ANSI|ISO|IEC|IEEE|AIEE|IRE|ASA|ASTM|CSA|ASME|NACE|NSF|ASHRAE|NCTA|AESC)\b/) ||
409
- adoption_part.match?(/\bStd\s+\d+/))
410
- # Parse the main IEEE identifier
411
- ieee_id = parse_single(main_part)
118
+ def draft_obj
119
+ @draft_obj ||=
120
+ (Components::Draft.parse(@draft.to_s) unless @draft.nil? || @draft.to_s.empty?)
121
+ end
412
122
 
413
- # Parse comma-separated adopted identifiers
414
- adopted_parts = adoption_part.split(",").map(&:strip)
415
- adopted_ids = adopted_parts.map do |part|
416
- if part.strip.start_with?("IEC")
417
- # Use IEC parser for IEC adoptions
418
- # Preprocess to convert "Edition X.Y" to IEC format
419
- # Pattern: "IEC 60255-24 Edition 2.0 2013-04" → "IEC 60255-24:2013-04 ED2.0"
420
- iec_part = part.dup
421
- # Replace " Edition X.Y YYYY-MM" (or similar) with ":YYYY-MM EDX.Y"
422
- iec_part.gsub!(/\s+Edition\s+([0-9.]+)\s+([0-9-]+)/,
423
- ':\2 ED\1')
424
- # Replace " Edition X.Y" at end (no date)
425
- iec_part.gsub!(/\s+Edition\s+([0-9.]+)\s*$/, ' ED\1')
426
- Pubid::Iec.parse(iec_part)
427
- elsif part.strip.start_with?("ANSI")
428
- # Use ANSI parser for ANSI adoptions
429
- Pubid::Ansi.parse(part)
430
- else
431
- # Use IEEE parser for other adoptions
432
- parse_single(part)
433
- end
434
- end
123
+ # Expose numeric month from draft if available
124
+ def draft_month
125
+ return nil unless draft_obj.is_a?(Components::Draft)
435
126
 
436
- return Identifiers::AdoptedStandard.new(
437
- ieee_identifier: ieee_id,
438
- adopted_identifiers: adopted_ids,
439
- )
440
- end
441
- # If it doesn't look like an identifier, let the parser handle it as additional_parameters
442
- end
127
+ draft_obj.numeric_month
128
+ end
443
129
 
444
- # Fall back to single identifier parsing
445
- parse_single(input)
130
+ # Parse IEEE identifier string.
131
+ #
132
+ # PreParser owns all regex/dispatch logic; this method is a thin
133
+ # orchestrator that consumes a PreParser::Result and routes to the
134
+ # correct builder.
135
+ def self.parse(input)
136
+ if input.length > Pubid::MAX_INPUT_LENGTH
137
+ raise ArgumentError, Pubid::INPUT_TOO_LONG_MESSAGE
446
138
  end
447
139
 
448
- # Parse a single IEEE identifier
449
- def self.parse_single(input)
450
- # Apply legacy update_codes normalization first, before Parser's extensive preprocessing
451
- normalized = Core::UpdateCodes.apply(input, :ieee)
452
- parsed = Parser.parse(normalized) # Use class method for preprocessing
453
- builder = Builder.new(Base)
454
- # Pass the original input string to builder for context
455
- builder.original_input = input
456
- builder.build(parsed)
140
+ result = PreParser.preprocess(input)
141
+
142
+ case result.dispatch
143
+ when :aiee_simple
144
+ return Aiee::Identifier.parse(result.input)
145
+ when :iec_ieee_copublished
146
+ return parse_single(result.input)
147
+ when :dual_semicolon
148
+ return build_dual(result.parts)
149
+ when :dual_reaffirmed
150
+ return build_reaffirmed(result)
151
+ when :dual_ire
152
+ return build_dual_with_reaffirmed(result)
153
+ when :dual_space_separated
154
+ return build_dual(result.parts)
155
+ when :dual_and
156
+ return build_dual(result.parts)
157
+ when :dual_ampersand
158
+ return build_dual(result.parts)
159
+ when :aiee_asa_adoption
160
+ return build_aiee_asa_adoption(result.parts)
161
+ when :adopted
162
+ return build_adopted(result.parts)
163
+ else
164
+ parse_single(result.input)
457
165
  end
166
+ rescue Parslet::ParseFailed
167
+ parse_single(input)
168
+ end
458
169
 
459
- def to_s
460
- parts = []
461
-
462
- # Publisher(s) - handle copublisher array properly
463
- if copublisher && !copublisher.empty?
464
- # Copublisher is an array, join all publishers with single slash
465
- parts << [publisher, *copublisher].join("/")
466
- else
467
- parts << publisher
468
- end
469
-
470
- # Draft status
471
- parts << draft_status if draft_status
472
-
473
- # Type - only render for IEEE/AIEE publishers, and only for non-projects
474
- # Must come BEFORE code
475
- should_render_type = publisher&.match?(/^(IEEE|AIEE)/)
476
-
477
- if should_render_type && !typed_stage&.project_status && type && !type.to_s.strip.empty? && type != "P"
478
- # Non-project with explicit type (Std, No, etc.)
479
- type_str = type.dup
480
- # Remove P prefix if somehow present
481
- type_str = type_str.sub(/^P/, "") if type_str.start_with?("P")
482
- parts << type_str unless type_str.strip.empty?
483
- end
484
-
485
- # Code - with P prefix for projects (concatenated, not separated)
486
- if code_obj
487
- result = code_obj.to_s
488
-
489
- # Prepend P if this is a project AND code doesn't already have P
490
- if typed_stage&.project_status && should_render_type && !result.start_with?("P")
491
- result = "P#{result}"
492
- end
493
-
494
- # Only attach year to code if there's no edition, no month, and no draft
495
- result += "-#{year}" if year && !draft_obj && !edition && !month
496
-
497
- # Append draft to code - with or without space based on original format
498
- if draft_obj
499
- result += space_before_draft ? " #{draft_obj}" : draft_obj.to_s
500
- end
501
-
502
- # Append interpretation notation (/INT)
503
- result += "/INT" if interpretation
504
-
505
- # Append conformance notation (/ConformanceNN-YYYY)
506
- if conf_number
507
- result += "/Conformance#{conf_number}"
508
- result += "-#{conf_year}" if conf_year
509
- end
510
-
511
- # Append ASHRAE joint publication (/ASHRAE Guideline NN-YYYY)
512
- if ashrae_number
513
- result += "/ASHRAE Guideline #{ashrae_number}"
514
- result += "-#{ashrae_year}" if ashrae_year
515
- end
516
-
517
- # Append IEEE cross-reference (/C62.22.1-1996)
518
- result += crossref if crossref
519
-
520
- parts << result
521
- elsif should_render_type && typed_stage&.project_status
522
- # No code but is a project - add standalone P
523
- parts << "P"
524
- end
525
-
526
- # Edition - with year if present (IEC style)
527
- if edition
528
- edition_str = "Edition #{edition}"
529
- if year
530
- edition_str += " #{year}"
531
- edition_str += "-#{edition_month}" if edition_month
532
- end
533
- parts << edition_str
534
- end
535
-
536
- # Build the main identifier (without month yet)
537
- result = parts.join(" ")
538
-
539
- # Month/Day - append directly to avoid extra space before comma
540
- if month
541
- # Format: ", Month Day, Year" or ", Month, Year"
542
- result += ", #{month}"
543
- result += " #{day}" if day
544
- if year && !edition
545
- # Add comma after month if year follows
546
- result += ", #{year}"
547
- end
548
- end
549
-
550
- # Add parenthetical content if present
551
- # Handle multiple parenthetical clauses (reaffirmed + relationships/revision)
552
- parentheticals = []
170
+ # ---- dispatch constructors (used by parse) ----
553
171
 
554
- reaff = reaffirmed
555
- if reaff && !reaff.to_s.strip.empty?
556
- parentheticals << "(R#{reaff})"
557
- end
172
+ def self.build_dual(parts)
173
+ first = parse_single(parts[0])
174
+ second = parse_single(parts[1])
175
+ Identifiers::DualPublished.new(
176
+ first_identifier: first,
177
+ second_identifier: second,
178
+ )
179
+ end
180
+ private_class_method :build_dual
558
181
 
559
- # Then add relationships/revision/amendment as second parenthetical
560
- if parenthetical_content
561
- parentheticals << "(#{parenthetical_content})"
562
- elsif relationships && !relationships.empty?
563
- # Render relationships (multiple separated by /)
564
- relationship_str = relationships.join(" / ")
565
- parentheticals << "(#{relationship_str})"
566
- elsif revision_of
567
- parentheticals << "(Revision of IEEE Std #{revision_of})"
568
- elsif amendment_to
569
- parentheticals << "(Amendment to IEEE Std #{amendment_to})"
570
- elsif adoption
571
- parentheticals << "(Adoption of #{adoption})"
572
- elsif note && !note.to_s.strip.empty?
573
- # Only add note if it doesn't duplicate other content
574
- parentheticals << "(#{note})"
182
+ def self.build_reaffirmed(result)
183
+ parsed = parse_single(result.input)
184
+ parsed.reaffirmed = result.metadata[:reaffirmed] if parsed.class.attributes.key?(:reaffirmed)
185
+ parsed
186
+ end
187
+ private_class_method :build_reaffirmed
188
+
189
+ def self.build_dual_with_reaffirmed(result)
190
+ ieee_id = parse_single(result.parts[0])
191
+ ieee_id.reaffirmed = result.metadata[:reaffirmed] if ieee_id.class.attributes.key?(:reaffirmed)
192
+ ire_id = parse_single(result.parts[1])
193
+ Identifiers::DualPublished.new(
194
+ first_identifier: ieee_id,
195
+ second_identifier: ire_id,
196
+ )
197
+ end
198
+ private_class_method :build_dual_with_reaffirmed
199
+
200
+ def self.build_aiee_asa_adoption(parts)
201
+ aiee_id = parse_single(parts[0])
202
+ asa_id = parse_single(parts[1])
203
+ Identifiers::AdoptedStandard.new(
204
+ ieee_identifier: aiee_id,
205
+ adopted_identifier: asa_id,
206
+ )
207
+ end
208
+ private_class_method :build_aiee_asa_adoption
209
+
210
+ def self.build_adopted(parts)
211
+ ieee_id = parse_single(parts[0])
212
+ adopted_parts = parts[1].split(",").map(&:strip)
213
+ adopted_ids = adopted_parts.map do |part|
214
+ if part.start_with?("IEC")
215
+ Pubid::Iec.parse(normalize_iec_adoption(part))
216
+ elsif part.start_with?("ANSI")
217
+ Pubid::Ansi.parse(part)
218
+ else
219
+ parse_single(part)
575
220
  end
576
-
577
- # Append all parentheticals with space separation
578
- result += " #{parentheticals.join(' ')}" unless parentheticals.empty?
579
-
580
- # Book nickname - outside parentheses in square brackets
581
- result += " [#{nickname}]" if nickname && !nickname.to_s.strip.empty?
582
-
583
- # Redline suffix
584
- result += " - Redline" if redline
585
-
586
- result
587
221
  end
222
+ Identifiers::AdoptedStandard.new(
223
+ ieee_identifier: ieee_id,
224
+ adopted_identifiers: adopted_ids,
225
+ )
226
+ end
227
+ private_class_method :build_adopted
228
+
229
+ # "IEC 60255-24 Edition 2.0 2013-04" → "IEC 60255-24:2013-04 ED2.0"
230
+ def self.normalize_iec_adoption(part)
231
+ iec_part = part.dup
232
+ iec_part.gsub!(/\s+Edition\s+([0-9.]+)\s+([0-9-]+)/, ':\2 ED\1')
233
+ iec_part.gsub!(/\s+Edition\s+([0-9.]+)\s*$/, ' ED\1')
234
+ iec_part
235
+ end
236
+ private_class_method :normalize_iec_adoption
237
+
238
+ # Parse a single IEEE identifier
239
+ def self.parse_single(input)
240
+ # Apply legacy update_codes normalization first, before Parser's extensive preprocessing
241
+ normalized = Core::UpdateCodes.apply(input, :ieee)
242
+ parsed = Parser.parse(normalized) # Use class method for preprocessing
243
+ builder = Builder.new(Identifier)
244
+ # Pass the original input string to builder for context
245
+ builder.original_input = input
246
+ builder.build(parsed)
588
247
  end
589
248
  end
249
+
250
+ module Identifiers
251
+ # Backward-compatible alias: IEEE's base class used to be
252
+ # Pubid::Ieee::Identifiers::Base. It is now Pubid::Ieee::Identifier.
253
+ Base = Pubid::Ieee::Identifier
254
+ end
590
255
  end
591
256
  end