pubid 2.0.0.pre.alpha.2 → 2.0.0.pre.alpha.3

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 (458) 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 +7 -39
  6. data/lib/pubid/amca/identifiers/base.rb +0 -26
  7. data/lib/pubid/amca/identifiers/interpretation.rb +0 -17
  8. data/lib/pubid/amca/identifiers/publication.rb +0 -13
  9. data/lib/pubid/amca/renderer.rb +82 -0
  10. data/lib/pubid/amca/single_identifier.rb +0 -23
  11. data/lib/pubid/amca/urn_parser.rb +28 -0
  12. data/lib/pubid/amca.rb +42 -1
  13. data/lib/pubid/ansi/builder.rb +5 -3
  14. data/lib/pubid/ansi/identifier.rb +1 -43
  15. data/lib/pubid/ansi/identifiers/american_national_standard.rb +2 -1
  16. data/lib/pubid/ansi/identifiers/standard.rb +2 -3
  17. data/lib/pubid/ansi/renderer.rb +53 -0
  18. data/lib/pubid/ansi/single_identifier.rb +2 -31
  19. data/lib/pubid/ansi/urn_generator.rb +3 -38
  20. data/lib/pubid/ansi/urn_parser.rb +23 -0
  21. data/lib/pubid/ansi.rb +38 -3
  22. data/lib/pubid/api/builder.rb +29 -74
  23. data/lib/pubid/api/identifier.rb +0 -51
  24. data/lib/pubid/api/identifiers/base.rb +0 -2
  25. data/lib/pubid/api/identifiers/bulletin.rb +0 -2
  26. data/lib/pubid/api/identifiers/continuous_operations_standard.rb +0 -2
  27. data/lib/pubid/api/identifiers/mpms.rb +1 -17
  28. data/lib/pubid/api/identifiers/publication.rb +0 -2
  29. data/lib/pubid/api/identifiers/recommended_practice.rb +0 -2
  30. data/lib/pubid/api/identifiers/specification.rb +0 -2
  31. data/lib/pubid/api/identifiers/standard.rb +0 -2
  32. data/lib/pubid/api/identifiers/technical_report.rb +0 -2
  33. data/lib/pubid/api/identifiers/typeless_standard.rb +1 -14
  34. data/lib/pubid/api/identifiers.rb +18 -0
  35. data/lib/pubid/api/renderer.rb +89 -0
  36. data/lib/pubid/api/single_identifier.rb +1 -13
  37. data/lib/pubid/api/urn_generator.rb +0 -18
  38. data/lib/pubid/api/urn_parser.rb +35 -0
  39. data/lib/pubid/api.rb +51 -5
  40. data/lib/pubid/ashrae/builder.rb +3 -3
  41. data/lib/pubid/ashrae/identifier.rb +6 -39
  42. data/lib/pubid/ashrae/identifiers/addenda_package.rb +0 -10
  43. data/lib/pubid/ashrae/identifiers/addendum.rb +0 -19
  44. data/lib/pubid/ashrae/identifiers/base.rb +3 -0
  45. data/lib/pubid/ashrae/identifiers/combined_addenda.rb +0 -15
  46. data/lib/pubid/ashrae/identifiers/errata.rb +0 -10
  47. data/lib/pubid/ashrae/identifiers/interpretation.rb +0 -10
  48. data/lib/pubid/ashrae/renderer.rb +117 -0
  49. data/lib/pubid/ashrae/single_identifier.rb +0 -13
  50. data/lib/pubid/ashrae/urn_generator.rb +0 -8
  51. data/lib/pubid/ashrae/urn_parser.rb +27 -0
  52. data/lib/pubid/ashrae.rb +42 -1
  53. data/lib/pubid/asme/components/code.rb +10 -2
  54. data/lib/pubid/asme/identifier.rb +0 -46
  55. data/lib/pubid/asme/identifiers/base.rb +0 -60
  56. data/lib/pubid/asme/renderer.rb +66 -0
  57. data/lib/pubid/asme/urn_parser.rb +31 -0
  58. data/lib/pubid/asme.rb +42 -1
  59. data/lib/pubid/astm/components/code.rb +9 -0
  60. data/lib/pubid/{jis → astm}/components.rb +1 -1
  61. data/lib/pubid/astm/identifier.rb +0 -77
  62. data/lib/pubid/astm/identifiers/adjunct.rb +0 -8
  63. data/lib/pubid/astm/identifiers/data_series.rb +0 -14
  64. data/lib/pubid/astm/identifiers/iso_dual_published.rb +9 -34
  65. data/lib/pubid/astm/identifiers/manual.rb +0 -27
  66. data/lib/pubid/astm/identifiers/monograph.rb +0 -14
  67. data/lib/pubid/astm/identifiers/research_report.rb +0 -7
  68. data/lib/pubid/astm/identifiers/standard.rb +0 -39
  69. data/lib/pubid/astm/identifiers/technical_report.rb +0 -13
  70. data/lib/pubid/astm/identifiers/work_in_progress.rb +0 -11
  71. data/lib/pubid/astm/identifiers.rb +18 -0
  72. data/lib/pubid/astm/renderer.rb +172 -0
  73. data/lib/pubid/astm/single_identifier.rb +0 -10
  74. data/lib/pubid/astm/urn_parser.rb +30 -0
  75. data/lib/pubid/astm.rb +39 -27
  76. data/lib/pubid/bsi/builder.rb +21 -12
  77. data/lib/pubid/bsi/identifier.rb +8 -62
  78. data/lib/pubid/bsi/identifiers/addendum_document.rb +3 -33
  79. data/lib/pubid/bsi/identifiers/adopted_european_norm.rb +11 -47
  80. data/lib/pubid/bsi/identifiers/adopted_international_standard.rb +11 -38
  81. data/lib/pubid/bsi/identifiers/aerospace_standard.rb +3 -53
  82. data/lib/pubid/bsi/identifiers/amendment.rb +3 -19
  83. data/lib/pubid/bsi/identifiers/british_industrial_practice.rb +2 -4
  84. data/lib/pubid/bsi/identifiers/british_standard.rb +2 -1
  85. data/lib/pubid/bsi/identifiers/bundled_identifier.rb +3 -84
  86. data/lib/pubid/bsi/identifiers/committee_document.rb +1 -14
  87. data/lib/pubid/bsi/identifiers/consolidated_identifier.rb +3 -84
  88. data/lib/pubid/bsi/identifiers/corrigendum.rb +3 -7
  89. data/lib/pubid/bsi/identifiers/detailed_specification.rb +1 -34
  90. data/lib/pubid/bsi/identifiers/disc.rb +1 -27
  91. data/lib/pubid/bsi/identifiers/draft_document.rb +3 -44
  92. data/lib/pubid/bsi/identifiers/electronic_book.rb +3 -36
  93. data/lib/pubid/bsi/identifiers/expert_commentary.rb +3 -15
  94. data/lib/pubid/bsi/identifiers/explanatory_supplement.rb +1 -45
  95. data/lib/pubid/bsi/identifiers/flex.rb +1 -33
  96. data/lib/pubid/bsi/identifiers/handbook.rb +2 -13
  97. data/lib/pubid/bsi/identifiers/index.rb +1 -30
  98. data/lib/pubid/bsi/identifiers/method.rb +1 -39
  99. data/lib/pubid/bsi/identifiers/national_annex.rb +5 -27
  100. data/lib/pubid/bsi/identifiers/practice_guide.rb +2 -4
  101. data/lib/pubid/bsi/identifiers/publicly_available_specification.rb +3 -52
  102. data/lib/pubid/bsi/identifiers/published_document.rb +3 -52
  103. data/lib/pubid/bsi/identifiers/section.rb +1 -28
  104. data/lib/pubid/bsi/identifiers/set.rb +3 -17
  105. data/lib/pubid/bsi/identifiers/standalone_amendment.rb +1 -7
  106. data/lib/pubid/bsi/identifiers/supplement_document.rb +3 -21
  107. data/lib/pubid/bsi/identifiers/supplementary_index.rb +1 -44
  108. data/lib/pubid/bsi/identifiers/technical_specification.rb +3 -45
  109. data/lib/pubid/bsi/identifiers/test_method.rb +1 -30
  110. data/lib/pubid/bsi/identifiers/value_added_publication.rb +3 -14
  111. data/lib/pubid/bsi/identifiers.rb +0 -1
  112. data/lib/pubid/bsi/renderer.rb +1050 -0
  113. data/lib/pubid/bsi/single_identifier.rb +6 -70
  114. data/lib/pubid/bsi/urn_generator.rb +2 -3
  115. data/lib/pubid/bsi/urn_parser.rb +52 -0
  116. data/lib/pubid/bsi.rb +224 -1
  117. data/lib/pubid/builder/base.rb +57 -10
  118. data/lib/pubid/bundled_identifier.rb +0 -1
  119. data/lib/pubid/ccsds/builder.rb +4 -3
  120. data/lib/pubid/ccsds/identifier.rb +63 -66
  121. data/lib/pubid/ccsds/identifiers/base.rb +11 -61
  122. data/lib/pubid/ccsds/identifiers/corrigendum.rb +7 -6
  123. data/lib/pubid/ccsds/parser.rb +4 -2
  124. data/lib/pubid/ccsds/supplement_identifier.rb +15 -11
  125. data/lib/pubid/ccsds/urn_generator.rb +3 -3
  126. data/lib/pubid/ccsds/urn_parser.rb +20 -0
  127. data/lib/pubid/ccsds.rb +39 -1
  128. data/lib/pubid/cen_cenelec/builder.rb +12 -14
  129. data/lib/pubid/cen_cenelec/identifier.rb +7 -38
  130. data/lib/pubid/cen_cenelec/identifiers/adopted_european_norm.rb +13 -4
  131. data/lib/pubid/cen_cenelec/identifiers/amendment.rb +2 -8
  132. data/lib/pubid/cen_cenelec/identifiers/base.rb +5 -41
  133. data/lib/pubid/cen_cenelec/identifiers/cen_report.rb +2 -1
  134. data/lib/pubid/cen_cenelec/identifiers/cen_workshop_agreement.rb +2 -1
  135. data/lib/pubid/cen_cenelec/identifiers/consolidated_identifier.rb +2 -25
  136. data/lib/pubid/cen_cenelec/identifiers/corrigendum.rb +2 -13
  137. data/lib/pubid/cen_cenelec/identifiers/european_norm.rb +2 -1
  138. data/lib/pubid/cen_cenelec/identifiers/european_prestandard.rb +4 -7
  139. data/lib/pubid/cen_cenelec/identifiers/european_specification.rb +2 -1
  140. data/lib/pubid/cen_cenelec/identifiers/fragment.rb +2 -2
  141. data/lib/pubid/cen_cenelec/identifiers/harmonization_document.rb +2 -1
  142. data/lib/pubid/cen_cenelec/identifiers/technical_report.rb +2 -1
  143. data/lib/pubid/cen_cenelec/identifiers/technical_specification.rb +2 -1
  144. data/lib/pubid/cen_cenelec/renderer.rb +261 -0
  145. data/lib/pubid/cen_cenelec/single_identifier.rb +11 -89
  146. data/lib/pubid/cen_cenelec/urn_generator.rb +6 -6
  147. data/lib/pubid/cen_cenelec/urn_parser.rb +28 -0
  148. data/lib/pubid/cen_cenelec.rb +168 -1
  149. data/lib/pubid/cie/components/code.rb +8 -0
  150. data/lib/pubid/cie/identifier.rb +6 -57
  151. data/lib/pubid/cie/urn_parser.rb +28 -0
  152. data/lib/pubid/cie.rb +43 -1
  153. data/lib/pubid/components/adoption.rb +104 -0
  154. data/lib/pubid/components/code.rb +22 -8
  155. data/lib/pubid/components/date.rb +23 -16
  156. data/lib/pubid/components/edition.rb +9 -6
  157. data/lib/pubid/components/iteration.rb +32 -0
  158. data/lib/pubid/components/language.rb +6 -4
  159. data/lib/pubid/components/locality.rb +10 -1
  160. data/lib/pubid/components/publisher.rb +9 -6
  161. data/lib/pubid/components/relationship.rb +151 -0
  162. data/lib/pubid/components/stage.rb +5 -14
  163. data/lib/pubid/components/supplement.rb +184 -0
  164. data/lib/pubid/components/type.rb +5 -15
  165. data/lib/pubid/components/typed_stage.rb +10 -11
  166. data/lib/pubid/components.rb +4 -1
  167. data/lib/pubid/core/update_codes.rb +28 -7
  168. data/lib/pubid/csa/identifier.rb +0 -59
  169. data/lib/pubid/csa/identifiers/base.rb +2 -122
  170. data/lib/pubid/csa/identifiers/cec.rb +2 -101
  171. data/lib/pubid/csa/identifiers/series.rb +2 -102
  172. data/lib/pubid/csa/renderer.rb +292 -0
  173. data/lib/pubid/csa/urn_generator.rb +1 -1
  174. data/lib/pubid/csa/urn_parser.rb +33 -0
  175. data/lib/pubid/csa.rb +42 -1
  176. data/lib/pubid/etsi/components/code.rb +9 -2
  177. data/lib/pubid/etsi/identifier.rb +0 -43
  178. data/lib/pubid/etsi/identifiers/base.rb +1 -4
  179. data/lib/pubid/etsi/identifiers/supplement_identifier.rb +2 -9
  180. data/lib/pubid/etsi/renderer.rb +42 -0
  181. data/lib/pubid/etsi/urn_parser.rb +34 -0
  182. data/lib/pubid/etsi.rb +42 -1
  183. data/lib/pubid/export/exporter.rb +4 -46
  184. data/lib/pubid/export/flavor_exporter.rb +111 -278
  185. data/lib/pubid/export.rb +0 -6
  186. data/lib/pubid/identifier.rb +2 -17
  187. data/lib/pubid/identifier_facade.rb +114 -0
  188. data/lib/pubid/identifier_metadata.rb +1 -1
  189. data/lib/pubid/idf/builder.rb +3 -3
  190. data/lib/pubid/idf/identifier.rb +3 -66
  191. data/lib/pubid/idf/identifiers/amendment.rb +2 -1
  192. data/lib/pubid/idf/identifiers/corrigendum.rb +2 -1
  193. data/lib/pubid/idf/identifiers/international_standard.rb +2 -1
  194. data/lib/pubid/idf/identifiers/reviewed_method.rb +2 -1
  195. data/lib/pubid/idf/parser.rb +3 -2
  196. data/lib/pubid/idf/renderer.rb +84 -0
  197. data/lib/pubid/idf/supplement_identifier.rb +2 -10
  198. data/lib/pubid/idf/urn_generator.rb +4 -39
  199. data/lib/pubid/idf/urn_parser.rb +25 -0
  200. data/lib/pubid/idf.rb +51 -1
  201. data/lib/pubid/iec/builder.rb +46 -64
  202. data/lib/pubid/iec/components/code.rb +8 -32
  203. data/lib/pubid/iec/components/publisher.rb +0 -1
  204. data/lib/pubid/iec/components.rb +14 -0
  205. data/lib/pubid/iec/identifier.rb +251 -213
  206. data/lib/pubid/iec/identifiers/amendment.rb +2 -3
  207. data/lib/pubid/iec/identifiers/base.rb +8 -32
  208. data/lib/pubid/iec/identifiers/component_specification.rb +3 -3
  209. data/lib/pubid/iec/identifiers/conformity_assessment.rb +1 -2
  210. data/lib/pubid/iec/identifiers/consolidated_identifier.rb +27 -26
  211. data/lib/pubid/iec/identifiers/corrigendum.rb +2 -3
  212. data/lib/pubid/iec/identifiers/fragment_identifier.rb +37 -22
  213. data/lib/pubid/iec/identifiers/guide.rb +0 -2
  214. data/lib/pubid/iec/identifiers/international_standard.rb +2 -3
  215. data/lib/pubid/iec/identifiers/interpretation_sheet.rb +2 -3
  216. data/lib/pubid/iec/identifiers/operational_document.rb +3 -3
  217. data/lib/pubid/iec/identifiers/publicly_available_specification.rb +2 -3
  218. data/lib/pubid/iec/identifiers/sheet_identifier.rb +21 -11
  219. data/lib/pubid/iec/identifiers/societal_technology_trend_report.rb +3 -3
  220. data/lib/pubid/iec/identifiers/systems_reference_document.rb +2 -3
  221. data/lib/pubid/iec/identifiers/technical_report.rb +2 -3
  222. data/lib/pubid/iec/identifiers/technical_specification.rb +2 -3
  223. data/lib/pubid/iec/identifiers/technology_report.rb +1 -2
  224. data/lib/pubid/iec/identifiers/test_report_form.rb +5 -34
  225. data/lib/pubid/iec/identifiers/vap_identifier.rb +26 -19
  226. data/lib/pubid/iec/identifiers/white_paper.rb +3 -3
  227. data/lib/pubid/iec/identifiers/working_document.rb +4 -48
  228. data/lib/pubid/iec/identifiers.rb +30 -0
  229. data/lib/pubid/iec/parser.rb +13 -12
  230. data/lib/pubid/iec/renderer.rb +254 -0
  231. data/lib/pubid/iec/single_identifier.rb +6 -12
  232. data/lib/pubid/iec/supplement_identifier.rb +58 -54
  233. data/lib/pubid/iec/urn_generator.rb +3 -3
  234. data/lib/pubid/iec/urn_parser.rb +3 -3
  235. data/lib/pubid/iec.rb +40 -68
  236. data/lib/pubid/ieee/builder.rb +12 -12
  237. data/lib/pubid/ieee/components/code.rb +8 -0
  238. data/lib/pubid/ieee/components/draft.rb +14 -0
  239. data/lib/pubid/ieee/components/relationship.rb +5 -149
  240. data/lib/pubid/ieee/identifier.rb +6 -41
  241. data/lib/pubid/ieee/identifiers/adopted_standard.rb +1 -6
  242. data/lib/pubid/ieee/identifiers/base.rb +101 -458
  243. data/lib/pubid/ieee/identifiers/conformance_identifier.rb +1 -7
  244. data/lib/pubid/ieee/identifiers/corrigendum.rb +1 -9
  245. data/lib/pubid/ieee/identifiers/csa_dual_published.rb +1 -7
  246. data/lib/pubid/ieee/identifiers/dual_identifier.rb +1 -1
  247. data/lib/pubid/ieee/identifiers/dual_published.rb +1 -1
  248. data/lib/pubid/ieee/identifiers/iec_ieee_copublished.rb +1 -6
  249. data/lib/pubid/ieee/identifiers/interpretation_identifier.rb +1 -7
  250. data/lib/pubid/ieee/identifiers/joint_development.rb +2 -0
  251. data/lib/pubid/ieee/identifiers/multi_numbered_identifier.rb +1 -15
  252. data/lib/pubid/ieee/identifiers/parenthetical_identifier.rb +1 -3
  253. data/lib/pubid/ieee/identifiers/project_draft_identifier.rb +15 -0
  254. data/lib/pubid/ieee/identifiers/redlined_standard.rb +1 -4
  255. data/lib/pubid/ieee/identifiers/si_standard.rb +1 -35
  256. data/lib/pubid/ieee/identifiers/standard.rb +1 -1
  257. data/lib/pubid/ieee/pre_parser.rb +301 -0
  258. data/lib/pubid/ieee/renderer.rb +307 -0
  259. data/lib/pubid/ieee/urn_parser.rb +34 -0
  260. data/lib/pubid/ieee.rb +62 -1
  261. data/lib/pubid/ieee_debug.rb +0 -1
  262. data/lib/pubid/iho/builder.rb +2 -2
  263. data/lib/pubid/iho/identifier.rb +8 -42
  264. data/lib/pubid/iho/identifiers/base.rb +49 -10
  265. data/lib/pubid/iho/parser.rb +3 -3
  266. data/lib/pubid/iho/renderer.rb +30 -0
  267. data/lib/pubid/iho/urn_generator.rb +2 -2
  268. data/lib/pubid/iho/urn_parser.rb +58 -0
  269. data/lib/pubid/iho.rb +50 -1
  270. data/lib/pubid/iso/builder.rb +55 -53
  271. data/lib/pubid/iso/bundled_identifier.rb +51 -0
  272. data/lib/pubid/iso/components/code.rb +7 -19
  273. data/lib/pubid/iso/components/publisher.rb +10 -8
  274. data/lib/pubid/iso/components.rb +2 -4
  275. data/lib/pubid/iso/identifier.rb +218 -252
  276. data/lib/pubid/iso/identifiers/addendum.rb +9 -6
  277. data/lib/pubid/iso/identifiers/amendment.rb +8 -4
  278. data/lib/pubid/iso/identifiers/corrigendum.rb +4 -4
  279. data/lib/pubid/iso/identifiers/data.rb +0 -1
  280. data/lib/pubid/iso/identifiers/directives.rb +8 -2
  281. data/lib/pubid/iso/identifiers/directives_supplement.rb +43 -14
  282. data/lib/pubid/iso/identifiers/extract.rb +2 -2
  283. data/lib/pubid/iso/identifiers/guide.rb +0 -1
  284. data/lib/pubid/iso/identifiers/international_standard.rb +4 -4
  285. data/lib/pubid/iso/identifiers/international_standardized_profile.rb +4 -4
  286. data/lib/pubid/iso/identifiers/international_workshop_agreement.rb +10 -4
  287. data/lib/pubid/iso/identifiers/pas.rb +2 -2
  288. data/lib/pubid/iso/identifiers/recommendation.rb +2 -2
  289. data/lib/pubid/iso/identifiers/supplement.rb +11 -3
  290. data/lib/pubid/iso/identifiers/tc_document.rb +44 -15
  291. data/lib/pubid/iso/identifiers/technical_report.rb +4 -4
  292. data/lib/pubid/iso/identifiers/technical_specification.rb +2 -2
  293. data/lib/pubid/iso/identifiers/technology_trends_assessments.rb +2 -2
  294. data/lib/pubid/iso/identifiers.rb +0 -1
  295. data/lib/pubid/iso/normalizer.rb +89 -0
  296. data/lib/pubid/iso/parser.rb +22 -4
  297. data/lib/pubid/iso/supplement_identifier.rb +15 -2
  298. data/lib/pubid/iso/urn_generator.rb +66 -182
  299. data/lib/pubid/iso/urn_parser.rb +12 -7
  300. data/lib/pubid/iso.rb +173 -2
  301. data/lib/pubid/itu/builder.rb +0 -12
  302. data/lib/pubid/itu/components/code.rb +8 -0
  303. data/lib/pubid/itu/components.rb +11 -0
  304. data/lib/pubid/itu/identifier.rb +6 -104
  305. data/lib/pubid/itu/identifiers/amendment.rb +0 -2
  306. data/lib/pubid/itu/identifiers/annex.rb +0 -2
  307. data/lib/pubid/itu/identifiers/base.rb +0 -6
  308. data/lib/pubid/itu/identifiers/combined_identifier.rb +0 -2
  309. data/lib/pubid/itu/identifiers/corrigendum.rb +0 -2
  310. data/lib/pubid/itu/identifiers/recommendation.rb +0 -2
  311. data/lib/pubid/itu/identifiers/special_publication.rb +0 -2
  312. data/lib/pubid/itu/identifiers/supplement.rb +0 -2
  313. data/lib/pubid/itu/urn_parser.rb +23 -0
  314. data/lib/pubid/itu.rb +42 -1
  315. data/lib/pubid/jcgm/builder.rb +16 -8
  316. data/lib/pubid/jcgm/identifier.rb +0 -43
  317. data/lib/pubid/jcgm/identifiers/amendment.rb +2 -7
  318. data/lib/pubid/jcgm/identifiers/gum_guide.rb +2 -10
  319. data/lib/pubid/jcgm/renderer.rb +68 -0
  320. data/lib/pubid/jcgm/single_identifier.rb +1 -5
  321. data/lib/pubid/jcgm/urn_generator.rb +4 -6
  322. data/lib/pubid/jcgm/urn_parser.rb +23 -0
  323. data/lib/pubid/jcgm.rb +43 -2
  324. data/lib/pubid/jis/builder.rb +44 -52
  325. data/lib/pubid/jis/identifier.rb +132 -46
  326. data/lib/pubid/jis/identifiers/amendment.rb +1 -1
  327. data/lib/pubid/jis/identifiers/corrigendum.rb +16 -0
  328. data/lib/pubid/jis/identifiers/standard.rb +2 -1
  329. data/lib/pubid/jis/identifiers/technical_report.rb +2 -1
  330. data/lib/pubid/jis/identifiers/technical_specification.rb +2 -1
  331. data/lib/pubid/jis/identifiers.rb +1 -1
  332. data/lib/pubid/jis/parser.rb +31 -5
  333. data/lib/pubid/jis/renderer.rb +69 -0
  334. data/lib/pubid/jis/single_identifier.rb +6 -12
  335. data/lib/pubid/jis/supplement_identifier.rb +17 -14
  336. data/lib/pubid/jis/urn_parser.rb +23 -0
  337. data/lib/pubid/jis.rb +42 -2
  338. data/lib/pubid/nist/builder.rb +63 -1871
  339. data/lib/pubid/nist/caster.rb +1272 -0
  340. data/lib/pubid/nist/circular_supplement_builder.rb +291 -0
  341. data/lib/pubid/nist/components/code.rb +9 -20
  342. data/lib/pubid/nist/components/supplement.rb +2 -2
  343. data/lib/pubid/nist/components.rb +0 -1
  344. data/lib/pubid/nist/identifier.rb +11 -48
  345. data/lib/pubid/nist/identifiers/base.rb +110 -47
  346. data/lib/pubid/nist/identifiers/circular.rb +7 -2
  347. data/lib/pubid/nist/identifiers/circular_supplement.rb +2 -1
  348. data/lib/pubid/nist/identifiers/commercial_standard.rb +2 -1
  349. data/lib/pubid/nist/identifiers/commercial_standard_emergency.rb +6 -4
  350. data/lib/pubid/nist/identifiers/commercial_standards_monthly.rb +10 -3
  351. data/lib/pubid/nist/identifiers/crpl_report.rb +8 -8
  352. data/lib/pubid/nist/identifiers/dated_document.rb +49 -0
  353. data/lib/pubid/nist/identifiers/federal_information_processing_standards.rb +15 -24
  354. data/lib/pubid/nist/identifiers/grant_contractor_report.rb +2 -1
  355. data/lib/pubid/nist/identifiers/handbook.rb +2 -1
  356. data/lib/pubid/nist/identifiers/internal_report.rb +2 -1
  357. data/lib/pubid/nist/identifiers/letter_circular.rb +2 -1
  358. data/lib/pubid/nist/identifiers/miscellaneous_publication.rb +5 -4
  359. data/lib/pubid/nist/identifiers/monograph.rb +7 -3
  360. data/lib/pubid/nist/identifiers/report.rb +4 -2
  361. data/lib/pubid/nist/identifiers/special_publication.rb +2 -1
  362. data/lib/pubid/nist/identifiers/technical_note.rb +3 -2
  363. data/lib/pubid/nist/identifiers.rb +1 -0
  364. data/lib/pubid/nist/parser.rb +62 -452
  365. data/lib/pubid/nist/parser_output_normalizer.rb +233 -0
  366. data/lib/pubid/nist/preprocessor.rb +416 -0
  367. data/lib/pubid/nist/renderer.rb +43 -0
  368. data/lib/pubid/nist/router.rb +148 -0
  369. data/lib/pubid/nist/series/base.rb +58 -0
  370. data/lib/pubid/nist/series/crpl.rb +13 -0
  371. data/lib/pubid/nist/series/fips.rb +14 -0
  372. data/lib/pubid/nist/series/ir.rb +60 -0
  373. data/lib/pubid/nist/series/letter_preserving.rb +15 -0
  374. data/lib/pubid/nist/series/mono.rb +19 -0
  375. data/lib/pubid/nist/series/ncstar.rb +20 -0
  376. data/lib/pubid/nist/series.rb +49 -0
  377. data/lib/pubid/nist/supplement_identifier.rb +3 -1
  378. data/lib/pubid/nist/urn_parser.rb +67 -0
  379. data/lib/pubid/nist.rb +82 -4
  380. data/lib/pubid/oiml/components/code.rb +10 -0
  381. data/lib/pubid/oiml/identifier.rb +0 -50
  382. data/lib/pubid/oiml/identifiers/annex.rb +3 -45
  383. data/lib/pubid/oiml/identifiers/base.rb +2 -17
  384. data/lib/pubid/oiml/renderer.rb +161 -0
  385. data/lib/pubid/oiml/single_identifier.rb +6 -45
  386. data/lib/pubid/oiml/supplement_identifier.rb +4 -19
  387. data/lib/pubid/oiml/urn_generator.rb +0 -8
  388. data/lib/pubid/oiml/urn_parser.rb +22 -0
  389. data/lib/pubid/oiml.rb +42 -1
  390. data/lib/pubid/plateau/identifier.rb +7 -41
  391. data/lib/pubid/plateau/identifiers/handbook.rb +1 -3
  392. data/lib/pubid/plateau/identifiers/technical_report.rb +1 -1
  393. data/lib/pubid/plateau/renderer.rb +51 -0
  394. data/lib/pubid/plateau/supplement_identifier.rb +1 -1
  395. data/lib/pubid/plateau/urn_parser.rb +43 -0
  396. data/lib/pubid/plateau.rb +43 -1
  397. data/lib/pubid/renderers/directives_renderer.rb +22 -8
  398. data/lib/pubid/renderers/guide_renderer.rb +4 -2
  399. data/lib/pubid/renderers/human_readable.rb +18 -7
  400. data/lib/pubid/rendering/context.rb +28 -19
  401. data/lib/pubid/rendering.rb +0 -3
  402. data/lib/pubid/sae/components/date.rb +8 -0
  403. data/lib/pubid/sae/components/type.rb +5 -1
  404. data/lib/pubid/sae/identifier.rb +0 -23
  405. data/lib/pubid/sae/identifiers/base.rb +2 -16
  406. data/lib/pubid/sae/renderer.rb +36 -0
  407. data/lib/pubid/sae/urn_generator.rb +2 -10
  408. data/lib/pubid/sae/urn_parser.rb +36 -0
  409. data/lib/pubid/sae.rb +42 -1
  410. data/lib/pubid/urn_generator/base.rb +12 -12
  411. data/lib/pubid/urn_parser/base.rb +81 -0
  412. data/lib/pubid/urn_parser/errors.rb +9 -0
  413. data/lib/pubid/urn_parser.rb +14 -0
  414. data/lib/pubid/version.rb +1 -1
  415. data/lib/pubid.rb +29 -7
  416. data/lib/tasks/website-data.json +1940 -1882
  417. metadata +75 -44
  418. data/lib/pubid/amca/scheme.rb +0 -16
  419. data/lib/pubid/ansi/scheme.rb +0 -15
  420. data/lib/pubid/api/scheme.rb +0 -66
  421. data/lib/pubid/ashrae/scheme.rb +0 -53
  422. data/lib/pubid/asme/scheme.rb +0 -37
  423. data/lib/pubid/astm/scheme.rb +0 -55
  424. data/lib/pubid/bsi/identifiers/base.rb +0 -11
  425. data/lib/pubid/bsi/scheme.rb +0 -243
  426. data/lib/pubid/ccsds/scheme.rb +0 -57
  427. data/lib/pubid/cen_cenelec/scheme.rb +0 -164
  428. data/lib/pubid/cie/scheme.rb +0 -64
  429. data/lib/pubid/components/factory.rb +0 -50
  430. data/lib/pubid/csa/scheme.rb +0 -44
  431. data/lib/pubid/etsi/scheme.rb +0 -42
  432. data/lib/pubid/export/data_class_exporter.rb +0 -59
  433. data/lib/pubid/export/ieee_exporter.rb +0 -78
  434. data/lib/pubid/export/itu_exporter.rb +0 -66
  435. data/lib/pubid/export/nist_exporter.rb +0 -64
  436. data/lib/pubid/export/registry_exporter.rb +0 -90
  437. data/lib/pubid/export/scheme_exporter.rb +0 -70
  438. data/lib/pubid/identifier_registry.rb +0 -198
  439. data/lib/pubid/idf/scheme.rb +0 -61
  440. data/lib/pubid/iec/scheme.rb +0 -71
  441. data/lib/pubid/ieee/scheme.rb +0 -90
  442. data/lib/pubid/iho/scheme.rb +0 -29
  443. data/lib/pubid/iso/identifiers/base.rb +0 -115
  444. data/lib/pubid/iso/scheme.rb +0 -193
  445. data/lib/pubid/itu/scheme.rb +0 -174
  446. data/lib/pubid/jcgm/scheme.rb +0 -60
  447. data/lib/pubid/jis/components/code.rb +0 -59
  448. data/lib/pubid/jis/identifiers/base.rb +0 -72
  449. data/lib/pubid/jis/scheme.rb +0 -49
  450. data/lib/pubid/nist/components/publisher.rb +0 -24
  451. data/lib/pubid/nist/scheme.rb +0 -199
  452. data/lib/pubid/oiml/scheme.rb +0 -46
  453. data/lib/pubid/plateau/scheme.rb +0 -45
  454. data/lib/pubid/rendering/base.rb +0 -73
  455. data/lib/pubid/rendering/common.rb +0 -211
  456. data/lib/pubid/rendering/format.rb +0 -25
  457. data/lib/pubid/sae/scheme.rb +0 -47
  458. data/lib/pubid/scheme.rb +0 -219
@@ -3,6 +3,11 @@
3
3
  module Pubid
4
4
  module Bsi
5
5
  class SingleIdentifier < Pubid::Identifier
6
+ # Identity for the Pubid::Bsi::Identifier facade: SingleIdentifier is the
7
+ # common ancestor of every concrete BSI identifier, so including the
8
+ # facade module here makes them all `is_a?(Pubid::Bsi::Identifier)`.
9
+ include Pubid::Bsi::Identifier
10
+
6
11
  # Generate URN for this identifier
7
12
  #
8
13
  # @return [String] URN representation
@@ -30,76 +35,7 @@ module Pubid
30
35
  attribute :space_separated_part, :boolean, default: -> { false }
31
36
 
32
37
  def to_s(lang: :en, lang_single: false)
33
- # Build string representation
34
- parts = []
35
-
36
- # For supplement/addendum base identifiers, flex_prefix replaces publisher+prefix
37
- parts << publisher.to_s if publisher
38
- if flex_prefix
39
- # Flex prefix is the full prefix including BS (e.g., "BS CECC" becomes "BS" + space + "CECC")
40
- parts << flex_prefix
41
- elsif prefix
42
- # Standard publisher
43
-
44
- # Prefix if present (specialized prefix like A, AU, C, etc.)
45
- parts << prefix.to_s
46
- end
47
-
48
- # Number with iteration, part, and subpart
49
- if number
50
- number_str = number.is_a?(Components::Code) ? number.value.to_s : number.to_s
51
-
52
- # Collection (second number with slash)
53
- if second_number
54
- second_val = second_number.is_a?(Components::Code) ? second_number.value : second_number
55
- number_str += "/#{second_val}"
56
- end
57
-
58
- # Part and subpart - check if space-separated
59
- space_separated = space_separated_part
60
- if part
61
- part_val = part.is_a?(Components::Code) ? part.value : part
62
- # Trim part value to remove leading/trailing spaces from parser
63
- part_str = part_val.to_s.strip
64
- # Use space for space-separated parts, dash otherwise
65
- separator = space_separated ? " " : "-"
66
- number_str += "#{separator}#{part_str}"
67
- end
68
- if subpart
69
- subpart_val = subpart.is_a?(Components::Code) ? subpart.value : subpart
70
- subpart_str = subpart_val.to_s.strip
71
- number_str += "-#{subpart_str}"
72
- end
73
-
74
- # Iteration (bracket notation like 1000[9])
75
- if iteration && !iteration.empty?
76
- number_str += "[#{iteration}]"
77
- end
78
-
79
- parts << number_str
80
- end
81
-
82
- result = parts.join(" ")
83
-
84
- # Date
85
- if date
86
- year_val = date.is_a?(Components::Date) ? date.year : date.to_i
87
- result += ":#{year_val}"
88
- # Month if present
89
- result += "-#{format('%02d', month)}" if month
90
- end
91
-
92
- # Edition
93
- result += " v#{edition}" if edition
94
-
95
- # Translation
96
- if translation_lang
97
- result += " (#{translation_lang})"
98
- elsif translation_upper
99
- result += " (#{translation_upper})"
100
- end
101
-
102
- result
38
+ render(format: :human, lang: lang, lang_single: lang_single)
103
39
  end
104
40
 
105
41
  def <=>(other)
@@ -44,9 +44,8 @@ module Pubid
44
44
  parts << "/#{second}"
45
45
  end
46
46
 
47
- if identifier.date
48
- year = identifier.date.year
49
- parts << year.to_s
47
+ if identifier.date&.is_a?(::Pubid::Components::Date) && identifier.date.present?
48
+ parts << identifier.date.render(context: URN_CONTEXT)
50
49
  elsif identifier.year
51
50
  parts << identifier.year.to_s
52
51
  end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pubid
4
+ module Bsi
5
+ # Parses BSI URNs back into identifiers.
6
+ #
7
+ # UrnGenerator emits:
8
+ # urn:bsi:{publisher}:{number}:{year}[:{supp_type}:{supp_number}:{supp_year}]
9
+ #
10
+ # publisher is the lowercase publisher (e.g., "bs", "pd"). supplement
11
+ # triples (`amd`/`cor`/`add`) follow the base identifier.
12
+ #
13
+ # Examples:
14
+ # - urn:bsi:bs:9001:2015 → BS 9001:2015
15
+ # - urn:bsi:bs:9001:2015:amd:1:2020 → BS 9001:2015/A1:2020
16
+ class UrnParser < Pubid::UrnParser::Base
17
+ SUPPLEMENT_ABBR = {
18
+ "amd" => "Amd",
19
+ "cor" => "Cor",
20
+ "add" => "Add",
21
+ }.freeze
22
+
23
+ def parse_urn(urn)
24
+ body = strip_namespace(urn)
25
+ parts = split_parts(body)
26
+
27
+ publisher_token, number, year, *supplement_parts = parts
28
+ publisher = publisher_token.upcase
29
+ text = "#{publisher} #{number}"
30
+ text += ":#{year}" if year
31
+ text += build_supplements(supplement_parts)
32
+ flavor_parse(text)
33
+ end
34
+
35
+ private
36
+
37
+ def build_supplements(parts)
38
+ return "" if parts.empty?
39
+
40
+ chunks = parts.each_slice(3).to_a
41
+ chunks.map { |type, num, supp_year| render_supplement(type, num, supp_year) }.join
42
+ end
43
+
44
+ def render_supplement(type, num, supp_year)
45
+ label = SUPPLEMENT_ABBR.fetch(type.downcase) { type.capitalize }
46
+ suffix = num ? " #{num}" : ""
47
+ suffix += ":#{supp_year}" if supp_year
48
+ "/#{label}#{suffix}"
49
+ end
50
+ end
51
+ end
52
+ end
data/lib/pubid/bsi.rb CHANGED
@@ -14,9 +14,193 @@ module Pubid
14
14
  autoload :Identifiers, "#{__dir__}/bsi/identifiers"
15
15
  autoload :Model, "#{__dir__}/bsi/model"
16
16
  autoload :Parser, "#{__dir__}/bsi/parser"
17
- autoload :Scheme, "#{__dir__}/bsi/scheme"
17
+ autoload :Renderer, "#{__dir__}/bsi/renderer"
18
18
  autoload :SingleIdentifier, "#{__dir__}/bsi/single_identifier"
19
19
  autoload :UrnGenerator, "#{__dir__}/bsi/urn_generator"
20
+ autoload :UrnParser, "#{__dir__}/bsi/urn_parser"
21
+
22
+ # TYPED_STAGES_REGISTRY for native BSI types. Used by Pubid::Bsi.locate_stage
23
+ # to map abbreviations (e.g. "BS", "PD", "Handbook") to typed stages.
24
+ TYPED_STAGES_REGISTRY = [
25
+ # British Standard (BS)
26
+ Pubid::Components::TypedStage.new(
27
+ code: :pubbs,
28
+ stage_code: :published,
29
+ type_code: :bs,
30
+ abbr: ["BS"],
31
+ name: "British Standard",
32
+ harmonized_stages: %w[60.00 60.60],
33
+ ),
34
+ Pubid::Components::TypedStage.new(
35
+ code: :drbs,
36
+ stage_code: :draft,
37
+ type_code: :bs,
38
+ abbr: ["Draft BS", "DBS"],
39
+ name: "Draft British Standard",
40
+ harmonized_stages: %w[30.00 30.20 30.60 40.00 40.20 40.60],
41
+ ),
42
+
43
+ # Published Document (PD)
44
+ Pubid::Components::TypedStage.new(
45
+ code: :pubpd,
46
+ stage_code: :published,
47
+ type_code: :pd,
48
+ abbr: ["PD"],
49
+ name: "Published Document",
50
+ harmonized_stages: %w[60.00 60.60],
51
+ ),
52
+
53
+ # Publicly Available Specification (PAS)
54
+ Pubid::Components::TypedStage.new(
55
+ code: :pubpas,
56
+ stage_code: :published,
57
+ type_code: :pas,
58
+ abbr: ["PAS"],
59
+ name: "Publicly Available Specification",
60
+ harmonized_stages: %w[60.00 60.60],
61
+ ),
62
+
63
+ # National Annex (NA)
64
+ Pubid::Components::TypedStage.new(
65
+ code: :pubna,
66
+ stage_code: :published,
67
+ type_code: :na,
68
+ abbr: ["NA"],
69
+ name: "National Annex",
70
+ harmonized_stages: %w[60.00 60.60],
71
+ ),
72
+
73
+ # Draft Document (DD)
74
+ Pubid::Components::TypedStage.new(
75
+ code: :pubdd,
76
+ stage_code: :published,
77
+ type_code: :dd,
78
+ abbr: ["DD"],
79
+ name: "Draft Document",
80
+ harmonized_stages: %w[60.00 60.60],
81
+ ),
82
+
83
+ # Flex Document
84
+ Pubid::Components::TypedStage.new(
85
+ code: :pubflex,
86
+ stage_code: :published,
87
+ type_code: :flex,
88
+ abbr: ["Flex", "BSI Flex"],
89
+ name: "BSI Flex",
90
+ harmonized_stages: %w[60.00 60.60],
91
+ ),
92
+
93
+ # Handbook
94
+ Pubid::Components::TypedStage.new(
95
+ code: :pubhandbook,
96
+ stage_code: :published,
97
+ type_code: :handbook,
98
+ abbr: ["Handbook", "HB"],
99
+ name: "BSI Handbook",
100
+ harmonized_stages: %w[60.00 60.60],
101
+ ),
102
+
103
+ # Practice Guide (PP)
104
+ Pubid::Components::TypedStage.new(
105
+ code: :pubpp,
106
+ stage_code: :published,
107
+ type_code: :pp,
108
+ abbr: ["PP"],
109
+ name: "Published Practice",
110
+ harmonized_stages: %w[60.00 60.60],
111
+ ),
112
+
113
+ # British Industrial Practice (BIP)
114
+ Pubid::Components::TypedStage.new(
115
+ code: :pubbip,
116
+ stage_code: :published,
117
+ type_code: :bip,
118
+ abbr: ["BIP"],
119
+ name: "British Industrial Practice",
120
+ harmonized_stages: %w[60.00 60.60],
121
+ ),
122
+
123
+ # Aerospace/Specialized British Standard (with letter prefix)
124
+ Pubid::Components::TypedStage.new(
125
+ code: :pubaerospace,
126
+ stage_code: :published,
127
+ type_code: :aerospace,
128
+ abbr: ["BS A", "BS AU", "BS C", "BS M", "BS S", "BS L", "BS TA",
129
+ "BS MA", "BS PL", "BS QC", "BS G", "BS HC", "BS F", "BS X", "BS B"],
130
+ name: "Aerospace/Specialized British Standard",
131
+ harmonized_stages: %w[60.00 60.60],
132
+ ),
133
+
134
+ # Index
135
+ Pubid::Components::TypedStage.new(
136
+ code: :pubindex,
137
+ stage_code: :published,
138
+ type_code: :index,
139
+ abbr: ["Index"],
140
+ name: "BSI Index",
141
+ harmonized_stages: %w[60.00 60.60],
142
+ ),
143
+
144
+ # Method
145
+ Pubid::Components::TypedStage.new(
146
+ code: :pubmethod,
147
+ stage_code: :published,
148
+ type_code: :method,
149
+ abbr: ["Method", "Methods"],
150
+ name: "BSI Method",
151
+ harmonized_stages: %w[60.00 60.60],
152
+ ),
153
+
154
+ # Section
155
+ Pubid::Components::TypedStage.new(
156
+ code: :pubsection,
157
+ stage_code: :published,
158
+ type_code: :section,
159
+ abbr: ["Section"],
160
+ name: "BSI Section",
161
+ harmonized_stages: %w[60.00 60.60],
162
+ ),
163
+
164
+ # DISC (Delivering Information Solutions to Customers)
165
+ Pubid::Components::TypedStage.new(
166
+ code: :pubdisc,
167
+ stage_code: :published,
168
+ type_code: :disc,
169
+ abbr: ["DISC"],
170
+ name: "DISC",
171
+ harmonized_stages: %w[60.00 60.60],
172
+ ),
173
+
174
+ # Detailed Specification (with N or C notation)
175
+ Pubid::Components::TypedStage.new(
176
+ code: :pubdetailed_spec,
177
+ stage_code: :published,
178
+ type_code: :detailed_specification,
179
+ abbr: ["DETAILED SPEC"],
180
+ name: "Detailed Specification",
181
+ harmonized_stages: %w[60.00 60.60],
182
+ ),
183
+
184
+ # Standalone Amendment
185
+ Pubid::Components::TypedStage.new(
186
+ code: :standalone_amendment,
187
+ stage_code: :published,
188
+ type_code: :amendment,
189
+ abbr: ["AMD"],
190
+ name: "Amendment",
191
+ harmonized_stages: %w[60.00 60.60],
192
+ ),
193
+
194
+ # Technical Specification
195
+ Pubid::Components::TypedStage.new(
196
+ code: :pubts,
197
+ stage_code: :published,
198
+ type_code: :ts,
199
+ abbr: ["TS"],
200
+ name: "Technical Specification",
201
+ harmonized_stages: %w[60.00 60.60],
202
+ ),
203
+ ].freeze
20
204
 
21
205
  def self.parse(string)
22
206
  Identifier.parse(string)
@@ -25,8 +209,47 @@ module Pubid
25
209
  def self.transform(data)
26
210
  Builder.build(data)
27
211
  end
212
+
213
+ # Auto-discover all identifier types from the Identifiers namespace.
214
+ # Includes classes with explicit type declarations (Hash or nil).
215
+ # @return [Array<Class>] identifier classes (Pubid::Identifier subclasses)
216
+ def self.identifier_types
217
+ @identifier_types ||= Identifiers.constants
218
+ .filter_map { |c| begin; Identifiers.const_get(c); rescue NameError; nil; end }
219
+ .select { |c| c.is_a?(Class) && c < Pubid::Identifier }
220
+ .select { |c| c.singleton_methods(false).include?(:type) }
221
+ .reject { |c| c.name&.split("::")&.last == "Base" }
222
+ end
223
+
224
+ # Build typed stage index from the module's TYPED_STAGES_REGISTRY
225
+ # @return [Array<Pubid::Components::TypedStage>] all typed stages
226
+ def self.all_typed_stages
227
+ @all_typed_stages ||= TYPED_STAGES_REGISTRY
228
+ end
229
+
230
+ # Lookup: type code -> identifier class
231
+ # @param code [String, Symbol] the type key to find
232
+ # @return [Class, nil] the matching identifier class
233
+ def self.locate_type(code)
234
+ identifier_types.find { |t| t.type&.dig(:key)&.to_s == code.to_s }
235
+ end
236
+
237
+ # Lookup: abbreviation -> typed stage
238
+ # @param abbr [String, Symbol] the abbreviation to find
239
+ # @return [Pubid::Components::TypedStage, nil] the matching typed stage
240
+ def self.locate_stage(abbr)
241
+ abbr_str = abbr.to_s.upcase
242
+ all_typed_stages.find { |s| s.abbr.any? { |a| a.to_s.upcase == abbr_str } }
243
+ end
28
244
  end
29
245
  end
30
246
 
31
247
  # Register Ubsi flavor with the registry
32
248
  Pubid::Registry.register(:bsi, Pubid::Bsi)
249
+
250
+ # Per-flavor format registry: inherits global formats, overrides :human.
251
+ # SingleIdentifier is the abstract base for every concrete BSI identifier
252
+ # (including Amendment and Corrigendum), so wiring the registry here covers
253
+ # the whole flavor in one shot.
254
+ Pubid::Bsi::SingleIdentifier.format_registry = Pubid::FormatRegistry.new(parent: Pubid::Identifier.format_registry)
255
+ Pubid::Bsi::SingleIdentifier.format_registry.register(:human, renderer: Pubid::Bsi::Renderer)
@@ -5,31 +5,43 @@ module Pubid
5
5
  # Base class for all flavor-specific builders.
6
6
  #
7
7
  # Provides the shared build loop that converts parsed data into identifier
8
- # objects. Flavors subclass and override only `cast` and optionally
9
- # `default_identifier_class`.
8
+ # objects. Flavors subclass and override:
9
+ # - +select_class(data)+ to dispatch by data shape (default returns
10
+ # +default_identifier_class+).
11
+ # - +cast(key, value)+ to transform raw parsed values into components.
12
+ # - +handle_key(identifier, key, value)+ for non-attribute side effects.
10
13
  #
11
14
  # The build loop:
12
15
  # 1. Flattens array-of-hashes input (from Parslet transforms)
13
- # 2. Iterates each key-value pair
14
- # 3. Calls `cast(key, value)` to convert raw values to component objects
15
- # 4. Assigns the result to the identifier via `send`
16
- # 5. Silently skips unknown attributes (rescue NoMethodError)
16
+ # 2. Asks +select_class(data)+ for the identifier class
17
+ # 3. Iterates each key-value pair
18
+ # 4. Calls +cast(key, value)+ to convert raw values to component objects
19
+ # 5. Assigns the result to the identifier via setter
20
+ # 6. Silently skips unknown attributes
17
21
  class Base
18
22
  attr_reader :identifier
19
23
 
20
- def initialize(identifier_class = default_identifier_class)
24
+ def initialize(identifier_class = nil)
21
25
  @identifier_class = identifier_class
22
26
  end
23
27
 
24
28
  def build(data)
25
29
  data = flatten_array(data) if data.is_a?(Array)
26
- identifier = @identifier_class.new
30
+ identifier_class = @identifier_class || select_class(data)
31
+ identifier = identifier_class.new
27
32
  assign_attributes(identifier, data)
28
33
  identifier
29
34
  end
30
35
 
31
36
  private
32
37
 
38
+ # Override in subclasses to dispatch to a specific identifier class
39
+ # based on the shape of the parsed data (type code, supplement marker,
40
+ # etc.). Defaults to +default_identifier_class+.
41
+ def select_class(_data)
42
+ default_identifier_class
43
+ end
44
+
33
45
  # Override in subclasses to convert raw parsed values to component objects.
34
46
  # Return a Hash to assign multiple attributes, or a single value.
35
47
  # Return nil to skip the assignment.
@@ -87,8 +99,9 @@ module Pubid
87
99
  }.freeze
88
100
 
89
101
  def locate_typed_stage(typed_stage_string)
90
- typed_stage_string = "" if typed_stage_string.nil?
91
- @scheme.locate_typed_stage_by_abbr(typed_stage_string)
102
+ raise NotImplementedError,
103
+ "#{self.class} must implement locate_typed_stage " \
104
+ "(rely on the flavor module's locate_stage method)"
92
105
  end
93
106
 
94
107
  def convert_roman_to_integer(roman_numeral)
@@ -122,6 +135,40 @@ module Pubid
122
135
  end
123
136
  end
124
137
 
138
+ # Splits a compound document number ("1234", "1234-1", "1234-1-2")
139
+ # into number/part/subpart Code components. Flavor-specific dash/slash
140
+ # normalization lives in +normalize_number_with_part+; legacy year
141
+ # extraction (e.g. ISO "4037-1979") lives in +extract_legacy_year+.
142
+ def parse_number_with_part(value, code_class:)
143
+ normalized = normalize_number_with_part(value)
144
+ parts = normalized.split("-").reject(&:empty?)
145
+ number = parts.shift
146
+ part = parts.shift&.strip
147
+ subpart = parts.any? ? parts.join("-") : nil
148
+
149
+ legacy = extract_legacy_year(number, part, code_class)
150
+ return legacy if legacy
151
+
152
+ part = convert_roman_to_integer(part) if part
153
+
154
+ code_hash = { number: code_class.new(value: number) }
155
+ code_hash[:part] = code_class.new(value: part) if part
156
+ code_hash[:subpart] = code_class.new(value: subpart) if subpart
157
+ code_hash
158
+ end
159
+
160
+ # Hook: normalize the raw number_with_part string before splitting.
161
+ # Override in subclasses for flavor-specific dash/slash/space handling.
162
+ def normalize_number_with_part(value)
163
+ value.to_s
164
+ end
165
+
166
+ # Hook: detect a year masquerading as a part (e.g. "4037-1979") and
167
+ # return a number+date hash, or nil to continue normal splitting.
168
+ def extract_legacy_year(_number, _part, _code_class)
169
+ nil
170
+ end
171
+
125
172
  def parse_languages(value)
126
173
  original_value = value.to_s
127
174
  normalized_value = original_value.gsub("/", ",")
@@ -1,4 +1,3 @@
1
- require_relative "identifier"
2
1
  # frozen_string_literal: true
3
2
 
4
3
  module Pubid
@@ -34,20 +34,21 @@ module Pubid
34
34
  # Extract corrigenda numbers
35
35
  corr_array = data[:corrigenda]
36
36
  corr_array = [corr_array] unless corr_array.is_a?(Array)
37
- corr_numbers = corr_array.map { |c| c[:cor_number].to_i }
37
+ corr_numbers = corr_array.map { |c| c[:cor_number].to_s }
38
38
 
39
39
  # Build base identifier (without corrigenda)
40
40
  base_data = data.dup
41
41
  base_data.delete(:corrigenda)
42
42
  base = build_base(base_data)
43
43
 
44
- # Build Corrigendum object for each corrigendum number
44
+ # Build Corrigendum object for each corrigendum number. The corrigendum
45
+ # sequence is stored in the inherited `number` attribute (mirroring ISO).
45
46
  # Note: CCSDS uses "Cor. 1" format (single corrigendum per identifier)
46
47
  # If multiple corrigenda exist, we build the last one wrapping the base
47
48
  corr_numbers.reduce(base) do |current_base, cor_num|
48
49
  Identifiers::Corrigendum.new(
49
50
  base_identifier: current_base,
50
- cor_number: cor_num,
51
+ number: cor_num,
51
52
  )
52
53
  end
53
54
  end
@@ -2,7 +2,57 @@
2
2
 
3
3
  module Pubid
4
4
  module Ccsds
5
- module Identifier
5
+ # Abstract root for every CCSDS identifier AND the flavor's parse entry
6
+ # point — mirrors Pubid::Iso::Identifier / Pubid::Jis::Identifier. Concrete
7
+ # identifiers (Identifiers::Base, Identifiers::Corrigendum) descend from this
8
+ # class, so a parsed CCSDS id is an instance of Pubid::Ccsds::Identifier and
9
+ # relaton-index can pass this class as `pubid_class` for from_hash/to_hash.
10
+ #
11
+ # Keeping the root abstract (it is never itself a serialized type, so it is
12
+ # absent from CCSDS_TYPE_MAP) is what breaks the polymorphic recursion a
13
+ # supplement's `base_identifier` would otherwise hit if it pointed at a
14
+ # concrete type that is also its own superclass.
15
+ class Identifier < ::Pubid::Identifier
16
+ # CCSDS stores every code component as a plain string (the Builder passes
17
+ # strings, never Components::Code/Edition objects), so override the
18
+ # inherited Components::Code/Edition attribute types with :string. This
19
+ # both matches the runtime values and yields a lean, round-trippable
20
+ # key_value (`to_hash`) serialization instead of nested component hashes.
21
+ # `type` (single book-color letter B/G/M/R/Y/O), `suffix`, and `language`
22
+ # are likewise CCSDS-specific plain strings.
23
+ attribute :number, :string
24
+ attribute :part, :string
25
+ attribute :edition, :string
26
+ attribute :type, :string
27
+ attribute :suffix, :string
28
+ attribute :language, :string
29
+
30
+ # Polymorphic type map for lutaml::Model key_value (de)serialization,
31
+ # mapping each concrete class's polymorphic_name to its class name.
32
+ CCSDS_TYPE_MAP = {
33
+ "pubid:ccsds:base" => "Pubid::Ccsds::Identifiers::Base",
34
+ "pubid:ccsds:corrigendum" => "Pubid::Ccsds::Identifiers::Corrigendum",
35
+ }.freeze
36
+
37
+ # The shared Pubid::Identifier no longer auto-maps attributes, so the
38
+ # flavor's root declares the full key_value mapping; subclasses
39
+ # (SupplementIdentifier/Corrigendum) merge their own blocks on top. Every
40
+ # value is a plain string, so a direct `to:` map round-trips.
41
+ key_value do
42
+ map "_type", to: :_type, polymorphic_map: CCSDS_TYPE_MAP
43
+ map "number", to: :number
44
+ map "part", to: :part
45
+ map "edition", to: :edition
46
+ map "type", to: :type
47
+ map "suffix", to: :suffix
48
+ map "language", to: :language
49
+ end
50
+
51
+ def publisher
52
+ "CCSDS"
53
+ end
54
+
55
+ # Parse a CCSDS identifier string into the appropriate identifier object.
6
56
  def self.parse(identifier)
7
57
  # Apply legacy update_codes normalization first
8
58
  normalized = Core::UpdateCodes.apply(identifier, :ccsds)
@@ -12,73 +62,20 @@ module Pubid
12
62
  raise "Failed to parse CCSDS identifier '#{identifier}': #{e.message}"
13
63
  end
14
64
 
15
- # Factory that builds a CCSDS identifier from a hash of primitives.
16
- #
17
- # Accepts the field shape used by relaton-data-ccsds index entries:
18
- # `:publisher`, `:number`, `:series`, `:part`, `:book_color`,
19
- # `:retired`, `:edition`, plus the parsed-shape fields `:type`,
20
- # `:suffix`, `:language`. CCSDS 2.x maps these as:
21
- #
22
- # * `:series` + `:number` → combined number ("A" + "20" → "A20")
23
- # * `:book_color` → `:type` (B/G/M/R/Y/O)
24
- # * `:publisher` → ignored (hardcoded "CCSDS")
25
- # * `:retired` → ignored (no 2.x field)
26
- #
27
- # Supplement subclasses (currently just Corrigendum, via `type: :cor`)
28
- # raise ArgumentError until `base:` kwarg is wired through.
29
- def self.create(type: nil, **opts)
30
- if type
31
- type_sym = type.to_sym
32
- base_type_key = Identifiers::Base.type[:key].to_sym
33
-
34
- if type_sym != base_type_key
35
- supplement = supplement_klass_for(type_sym)
36
- if supplement
37
- # TODO(create-shim): supplement subclasses (Corrigendum)
38
- # require a base_identifier. Wire `base:` kwarg through once
39
- # a caller needs it.
40
- raise ArgumentError, "#{supplement} requires a " \
41
- "base_identifier; Identifier.create " \
42
- "cannot build supplements yet"
43
- end
44
-
45
- # Not a class-dispatch key — treat as book_color data
46
- # (e.g. `type: "G"` for Green Book) and merge back into opts.
47
- opts = opts.merge(type: type)
48
- end
49
- end
50
-
51
- Identifiers::Base.new(**coerce_create_attrs(opts))
52
- end
53
-
54
- def self.supplement_klass_for(type_sym)
55
- Scheme.supplement_identifiers.find do |k|
56
- k.type[:key].to_sym == type_sym
57
- end
58
- end
59
-
60
- def self.coerce_create_attrs(opts)
61
- attrs = {}
62
-
63
- if opts[:number]
64
- num = opts[:number].to_s
65
- attrs[:number] =
66
- opts[:series] ? "#{opts[:series]}#{num}" : num
65
+ # Dispatch deserialization to the concrete class named by the stored
66
+ # `_type`, so `from_hash` rebuilds a Corrigendum rather than a bare root
67
+ # (lutaml resolves `_type` only for validation, not instantiation).
68
+ # Mirrors Pubid::Jis::Identifier.from_hash. The `klass == self` guard lets
69
+ # the matched subclass fall through to lutaml's inherited from_hash.
70
+ def self.from_hash(data, options = {})
71
+ type = data["_type"] || data[:_type]
72
+ klass_name = CCSDS_TYPE_MAP[type]
73
+ if klass_name
74
+ klass = Object.const_get(klass_name)
75
+ return klass.from_hash(data, options) unless klass == self
67
76
  end
68
-
69
- attrs[:type] = opts[:book_color].to_s if opts[:book_color]
70
- # Allow caller to pass :type as data when no :book_color given
71
- # (parsed identifiers expose it via Base#type attribute).
72
- attrs[:type] = opts[:type].to_s if opts[:type] && !attrs[:type]
73
-
74
- %i[part edition suffix language].each do |k|
75
- v = opts[k]
76
- attrs[k] = v.to_s unless v.nil?
77
- end
78
-
79
- attrs
77
+ super
80
78
  end
81
- private_class_method :supplement_klass_for, :coerce_create_attrs
82
79
  end
83
80
  end
84
81
  end