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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (672) hide show
  1. checksums.yaml +4 -4
  2. data/README.adoc +33 -11
  3. data/conformance/pending.yaml +4 -0
  4. data/data/bipm/update_codes.yaml +9 -0
  5. data/data/iec/update_codes.yaml +3 -0
  6. data/data/ieee/update_codes.yaml +107 -1
  7. data/{archived-gems/pubid-nist → data/nist}/series.yaml +34 -0
  8. data/data/nist/update_codes.yaml +8 -0
  9. data/lib/pubid/adobe/builder.rb +52 -0
  10. data/lib/pubid/adobe/identifier.rb +47 -0
  11. data/lib/pubid/adobe/identifiers/publication.rb +31 -0
  12. data/lib/pubid/adobe/identifiers/tech_note.rb +32 -0
  13. data/lib/pubid/adobe/identifiers.rb +10 -0
  14. data/lib/pubid/adobe/parser.rb +136 -0
  15. data/lib/pubid/adobe/renderer.rb +39 -0
  16. data/lib/pubid/adobe/urn_generator.rb +42 -0
  17. data/lib/pubid/adobe/urn_parser.rb +69 -0
  18. data/lib/pubid/adobe.rb +61 -0
  19. data/lib/pubid/amca/builder.rb +4 -4
  20. data/lib/pubid/amca/identifier.rb +1 -3
  21. data/lib/pubid/amca/identifiers/base.rb +50 -33
  22. data/lib/pubid/amca/identifiers/interpretation.rb +18 -10
  23. data/lib/pubid/amca/identifiers/publication.rb +14 -10
  24. data/lib/pubid/amca/identifiers/standard.rb +3 -1
  25. data/lib/pubid/amca/identifiers.rb +0 -1
  26. data/lib/pubid/amca/parser.rb +1 -1
  27. data/lib/pubid/amca/renderer.rb +4 -4
  28. data/lib/pubid/amca/single_identifier.rb +1 -1
  29. data/lib/pubid/amca/urn_generator.rb +2 -2
  30. data/lib/pubid/amca.rb +5 -4
  31. data/lib/pubid/ansi/builder.rb +2 -2
  32. data/lib/pubid/ansi/identifier.rb +18 -0
  33. data/lib/pubid/ansi/parser.rb +1 -1
  34. data/lib/pubid/ansi/renderer.rb +2 -2
  35. data/lib/pubid/ansi.rb +5 -5
  36. data/lib/pubid/api/builder.rb +16 -2
  37. data/lib/pubid/api/identifier.rb +25 -4
  38. data/lib/pubid/api/identifiers/mpms.rb +10 -8
  39. data/lib/pubid/api/parser.rb +1 -1
  40. data/lib/pubid/api/renderer.rb +16 -7
  41. data/lib/pubid/api/single_identifier.rb +5 -12
  42. data/lib/pubid/api.rb +1 -0
  43. data/lib/pubid/ashrae/builder.rb +53 -32
  44. data/lib/pubid/ashrae/identifier.rb +3 -4
  45. data/lib/pubid/ashrae/identifiers/addenda_package.rb +7 -1
  46. data/lib/pubid/ashrae/identifiers/addendum.rb +9 -1
  47. data/lib/pubid/ashrae/identifiers/base.rb +94 -12
  48. data/lib/pubid/ashrae/identifiers/combined_addenda.rb +8 -1
  49. data/lib/pubid/ashrae/identifiers/errata.rb +9 -0
  50. data/lib/pubid/ashrae/identifiers/guideline.rb +3 -0
  51. data/lib/pubid/ashrae/identifiers/interpretation.rb +16 -0
  52. data/lib/pubid/ashrae/identifiers/standard.rb +3 -0
  53. data/lib/pubid/ashrae/identifiers.rb +0 -1
  54. data/lib/pubid/ashrae/parser.rb +37 -17
  55. data/lib/pubid/ashrae/renderer.rb +20 -20
  56. data/lib/pubid/ashrae/single_identifier.rb +1 -1
  57. data/lib/pubid/ashrae/supplement_identifier.rb +21 -6
  58. data/lib/pubid/ashrae/urn_generator.rb +8 -2
  59. data/lib/pubid/ashrae.rb +10 -4
  60. data/lib/pubid/asme/builder.rb +11 -2
  61. data/lib/pubid/asme/identifier.rb +9 -0
  62. data/lib/pubid/asme/identifiers/standard.rb +86 -2
  63. data/lib/pubid/asme/parser.rb +1 -1
  64. data/lib/pubid/asme/renderer.rb +3 -3
  65. data/lib/pubid/asme/single_identifier.rb +8 -1
  66. data/lib/pubid/asme/urn_generator.rb +2 -2
  67. data/lib/pubid/asme.rb +1 -0
  68. data/lib/pubid/astm/builder.rb +10 -3
  69. data/lib/pubid/astm/identifier.rb +9 -0
  70. data/lib/pubid/astm/identifiers/adjunct.rb +16 -1
  71. data/lib/pubid/astm/identifiers/code_number.rb +81 -0
  72. data/lib/pubid/astm/identifiers/data_series.rb +2 -0
  73. data/lib/pubid/astm/identifiers/iso_dual_published.rb +16 -0
  74. data/lib/pubid/astm/identifiers/manual.rb +10 -0
  75. data/lib/pubid/astm/identifiers/monograph.rb +2 -0
  76. data/lib/pubid/astm/identifiers/research_report.rb +10 -0
  77. data/lib/pubid/astm/identifiers/standard.rb +10 -0
  78. data/lib/pubid/astm/identifiers/technical_report.rb +2 -0
  79. data/lib/pubid/astm/identifiers/work_in_progress.rb +2 -0
  80. data/lib/pubid/astm/identifiers.rb +1 -0
  81. data/lib/pubid/astm/parser.rb +1 -1
  82. data/lib/pubid/astm/renderer.rb +1 -1
  83. data/lib/pubid/astm/single_identifier.rb +31 -1
  84. data/lib/pubid/astm/urn_generator.rb +7 -5
  85. data/lib/pubid/astm.rb +1 -0
  86. data/lib/pubid/bipm/builder.rb +163 -0
  87. data/lib/pubid/bipm/identifier.rb +285 -0
  88. data/lib/pubid/bipm/identifiers/committee_document.rb +36 -0
  89. data/lib/pubid/bipm/identifiers/guide.rb +35 -0
  90. data/lib/pubid/bipm/identifiers/meeting.rb +53 -0
  91. data/lib/pubid/bipm/identifiers/mep.rb +33 -0
  92. data/lib/pubid/bipm/identifiers/metrologia_article.rb +48 -0
  93. data/lib/pubid/bipm/identifiers/si_brochure.rb +33 -0
  94. data/lib/pubid/bipm/identifiers.rb +15 -0
  95. data/lib/pubid/bipm/parser.rb +237 -0
  96. data/lib/pubid/bipm/renderer.rb +118 -0
  97. data/lib/pubid/bipm/urn_generator.rb +58 -0
  98. data/lib/pubid/bipm/urn_parser.rb +60 -0
  99. data/lib/pubid/bipm.rb +92 -0
  100. data/lib/pubid/bsi/builder.rb +62 -52
  101. data/lib/pubid/bsi/identifier.rb +0 -1
  102. data/lib/pubid/bsi/identifiers/addendum_document.rb +2 -2
  103. data/lib/pubid/bsi/identifiers/adopted_european_norm.rb +36 -7
  104. data/lib/pubid/bsi/identifiers/adopted_international_standard.rb +7 -6
  105. data/lib/pubid/bsi/identifiers/amendment.rb +4 -4
  106. data/lib/pubid/bsi/identifiers/british_industrial_practice.rb +0 -1
  107. data/lib/pubid/bsi/identifiers/bundled_identifier.rb +10 -0
  108. data/lib/pubid/bsi/identifiers/committee_document.rb +10 -1
  109. data/lib/pubid/bsi/identifiers/consolidated_identifier.rb +6 -0
  110. data/lib/pubid/bsi/identifiers/corrigendum.rb +4 -4
  111. data/lib/pubid/bsi/identifiers/expert_commentary.rb +5 -5
  112. data/lib/pubid/bsi/identifiers/handbook.rb +0 -2
  113. data/lib/pubid/bsi/identifiers/practice_guide.rb +0 -1
  114. data/lib/pubid/bsi/identifiers/set.rb +11 -0
  115. data/lib/pubid/bsi/identifiers/standalone_amendment.rb +10 -1
  116. data/lib/pubid/bsi/identifiers/supplement_document.rb +2 -2
  117. data/lib/pubid/bsi/identifiers/value_added_publication.rb +6 -6
  118. data/lib/pubid/bsi/parser.rb +1 -1
  119. data/lib/pubid/bsi/renderer.rb +71 -59
  120. data/lib/pubid/bsi/single_identifier.rb +64 -7
  121. data/lib/pubid/bsi/urn_generator.rb +11 -2
  122. data/lib/pubid/bsi.rb +7 -1
  123. data/lib/pubid/builder/base.rb +23 -11
  124. data/lib/pubid/bundled_identifier.rb +19 -9
  125. data/lib/pubid/calconnect/builder.rb +38 -0
  126. data/lib/pubid/calconnect/identifier.rb +121 -0
  127. data/lib/pubid/calconnect/identifiers/standard.rb +29 -0
  128. data/lib/pubid/calconnect/identifiers.rb +9 -0
  129. data/lib/pubid/calconnect/parser.rb +54 -0
  130. data/lib/pubid/calconnect/renderer.rb +36 -0
  131. data/lib/pubid/calconnect/urn_generator.rb +33 -0
  132. data/lib/pubid/calconnect/urn_parser.rb +34 -0
  133. data/lib/pubid/calconnect.rb +83 -0
  134. data/lib/pubid/ccsds/builder.rb +1 -1
  135. data/lib/pubid/ccsds/identifier.rb +10 -3
  136. data/lib/pubid/ccsds/identifiers/base.rb +4 -2
  137. data/lib/pubid/ccsds/identifiers/corrigendum.rb +4 -4
  138. data/lib/pubid/ccsds/parser.rb +1 -1
  139. data/lib/pubid/ccsds/single_identifier.rb +2 -2
  140. data/lib/pubid/ccsds/supplement_identifier.rb +6 -6
  141. data/lib/pubid/ccsds/urn_generator.rb +3 -3
  142. data/lib/pubid/ccsds.rb +1 -0
  143. data/lib/pubid/cen_cenelec/builder.rb +131 -72
  144. data/lib/pubid/cen_cenelec/identifier.rb +115 -3
  145. data/lib/pubid/cen_cenelec/identifiers/adopted_european_norm.rb +27 -16
  146. data/lib/pubid/cen_cenelec/identifiers/amendment.rb +46 -6
  147. data/lib/pubid/cen_cenelec/identifiers/base.rb +8 -9
  148. data/lib/pubid/cen_cenelec/identifiers/cen_report.rb +1 -1
  149. data/lib/pubid/cen_cenelec/identifiers/cen_workshop_agreement.rb +1 -1
  150. data/lib/pubid/cen_cenelec/identifiers/cenelec_harmonization_document.rb +1 -1
  151. data/lib/pubid/cen_cenelec/identifiers/consolidated_identifier.rb +29 -14
  152. data/lib/pubid/cen_cenelec/identifiers/corrigendum.rb +47 -8
  153. data/lib/pubid/cen_cenelec/identifiers/european_norm.rb +60 -3
  154. data/lib/pubid/cen_cenelec/identifiers/european_prestandard.rb +27 -2
  155. data/lib/pubid/cen_cenelec/identifiers/european_specification.rb +1 -1
  156. data/lib/pubid/cen_cenelec/identifiers/fragment.rb +15 -5
  157. data/lib/pubid/cen_cenelec/identifiers/guide.rb +1 -1
  158. data/lib/pubid/cen_cenelec/identifiers/harmonization_document.rb +1 -1
  159. data/lib/pubid/cen_cenelec/identifiers/technical_report.rb +1 -1
  160. data/lib/pubid/cen_cenelec/identifiers/technical_specification.rb +1 -1
  161. data/lib/pubid/cen_cenelec/parser.rb +1 -1
  162. data/lib/pubid/cen_cenelec/renderer.rb +55 -60
  163. data/lib/pubid/cen_cenelec/single_identifier.rb +20 -2
  164. data/lib/pubid/cen_cenelec/urn_generator.rb +66 -44
  165. data/lib/pubid/cen_cenelec.rb +104 -82
  166. data/lib/pubid/cie/builder.rb +149 -24
  167. data/lib/pubid/cie/identifier.rb +13 -4
  168. data/lib/pubid/cie/identifiers/bundle.rb +35 -5
  169. data/lib/pubid/cie/identifiers/code_attributes.rb +52 -0
  170. data/lib/pubid/cie/identifiers/conference.rb +11 -6
  171. data/lib/pubid/cie/identifiers/corrigendum.rb +17 -23
  172. data/lib/pubid/cie/identifiers/dual_published.rb +6 -6
  173. data/lib/pubid/cie/identifiers/identical.rb +9 -9
  174. data/lib/pubid/cie/identifiers/joint_published.rb +10 -10
  175. data/lib/pubid/cie/identifiers/proceedings.rb +41 -0
  176. data/lib/pubid/cie/identifiers/standard.rb +43 -8
  177. data/lib/pubid/cie/identifiers/supplement.rb +27 -25
  178. data/lib/pubid/cie/identifiers/tutorial_bundle.rb +7 -3
  179. data/lib/pubid/cie/identifiers.rb +2 -0
  180. data/lib/pubid/cie/parser.rb +55 -6
  181. data/lib/pubid/cie/single_identifier.rb +40 -1
  182. data/lib/pubid/cie/supplement_identifier.rb +29 -16
  183. data/lib/pubid/cie/urn_generator.rb +11 -8
  184. data/lib/pubid/cie.rb +7 -2
  185. data/lib/pubid/components/date.rb +21 -5
  186. data/lib/pubid/conformance/checks.rb +72 -0
  187. data/lib/pubid/conformance/corpus/case.rb +55 -0
  188. data/lib/pubid/conformance/corpus.rb +36 -0
  189. data/lib/pubid/conformance/generator.rb +212 -0
  190. data/lib/pubid/conformance/pending.rb +63 -0
  191. data/lib/pubid/conformance/runner.rb +153 -0
  192. data/lib/pubid/conformance.rb +55 -0
  193. data/lib/pubid/core.rb +2 -0
  194. data/lib/pubid/csa/builder.rb +48 -48
  195. data/lib/pubid/csa/composite_identifier.rb +18 -7
  196. data/lib/pubid/csa/identifier.rb +187 -80
  197. data/lib/pubid/csa/identifiers/bundled.rb +25 -9
  198. data/lib/pubid/csa/identifiers/canadian_adopted.rb +13 -13
  199. data/lib/pubid/csa/identifiers/cec.rb +44 -4
  200. data/lib/pubid/csa/identifiers/combined.rb +97 -69
  201. data/lib/pubid/csa/identifiers/csa_adopted.rb +4 -4
  202. data/lib/pubid/csa/identifiers/package.rb +4 -4
  203. data/lib/pubid/csa/parser.rb +12 -8
  204. data/lib/pubid/csa/renderer.rb +13 -5
  205. data/lib/pubid/csa/single_identifier.rb +93 -4
  206. data/lib/pubid/csa/urn_generator.rb +9 -2
  207. data/lib/pubid/csa/wrapper_identifier.rb +43 -10
  208. data/lib/pubid/csa.rb +7 -1
  209. data/lib/pubid/doi/builder.rb +18 -0
  210. data/lib/pubid/doi/identifier.rb +49 -0
  211. data/lib/pubid/doi/identifiers/resource.rb +30 -0
  212. data/lib/pubid/doi/identifiers.rb +9 -0
  213. data/lib/pubid/doi/parser.rb +52 -0
  214. data/lib/pubid/doi/renderer.rb +13 -0
  215. data/lib/pubid/doi.rb +56 -0
  216. data/lib/pubid/easc/builder.rb +55 -0
  217. data/lib/pubid/easc/identifier.rb +59 -0
  218. data/lib/pubid/easc/identifiers/pmg.rb +20 -0
  219. data/lib/pubid/easc/identifiers/rmg.rb +19 -0
  220. data/lib/pubid/easc/identifiers.rb +10 -0
  221. data/lib/pubid/easc/parser.rb +88 -0
  222. data/lib/pubid/easc/renderer.rb +48 -0
  223. data/lib/pubid/easc/urn_generator.rb +46 -0
  224. data/lib/pubid/easc/urn_parser.rb +57 -0
  225. data/lib/pubid/easc.rb +53 -0
  226. data/lib/pubid/ecma/builder.rb +45 -0
  227. data/lib/pubid/ecma/identifier.rb +147 -0
  228. data/lib/pubid/ecma/identifiers/memento.rb +30 -0
  229. data/lib/pubid/ecma/identifiers/standard.rb +31 -0
  230. data/lib/pubid/ecma/identifiers/technical_report.rb +31 -0
  231. data/lib/pubid/ecma/identifiers.rb +11 -0
  232. data/lib/pubid/ecma/parser.rb +76 -0
  233. data/lib/pubid/ecma/renderer.rb +67 -0
  234. data/lib/pubid/ecma/urn_generator.rb +46 -0
  235. data/lib/pubid/ecma/urn_parser.rb +51 -0
  236. data/lib/pubid/ecma.rb +68 -0
  237. data/lib/pubid/errors.rb +80 -0
  238. data/lib/pubid/etsi/builder.rb +13 -4
  239. data/lib/pubid/etsi/identifier.rb +1 -3
  240. data/lib/pubid/etsi/identifiers/base.rb +43 -11
  241. data/lib/pubid/etsi/identifiers/etsi_standard.rb +207 -4
  242. data/lib/pubid/etsi/identifiers/supplement_identifier.rb +45 -3
  243. data/lib/pubid/etsi/identifiers.rb +0 -1
  244. data/lib/pubid/etsi/parser.rb +9 -3
  245. data/lib/pubid/etsi/renderer.rb +8 -2
  246. data/lib/pubid/etsi.rb +6 -5
  247. data/lib/pubid/evs/builder.rb +26 -0
  248. data/lib/pubid/evs/identifier.rb +37 -0
  249. data/lib/pubid/evs/identifiers/national_adoption.rb +17 -0
  250. data/lib/pubid/evs/identifiers.rb +9 -0
  251. data/lib/pubid/evs/parser.rb +27 -0
  252. data/lib/pubid/evs/renderer.rb +30 -0
  253. data/lib/pubid/evs/urn_generator.rb +30 -0
  254. data/lib/pubid/evs/urn_parser.rb +80 -0
  255. data/lib/pubid/evs.rb +90 -0
  256. data/lib/pubid/export/exporter.rb +2 -1
  257. data/lib/pubid/export/flavor_exporter.rb +18 -3
  258. data/lib/pubid/export.rb +1 -0
  259. data/lib/pubid/format_detector.rb +1 -0
  260. data/lib/pubid/gb/builder.rb +45 -0
  261. data/lib/pubid/gb/identifier.rb +67 -0
  262. data/lib/pubid/gb/identifiers/standard.rb +34 -0
  263. data/lib/pubid/gb/identifiers.rb +9 -0
  264. data/lib/pubid/gb/parser.rb +63 -0
  265. data/lib/pubid/gb/renderer.rb +35 -0
  266. data/lib/pubid/gb.rb +88 -0
  267. data/lib/pubid/gost/builder.rb +185 -0
  268. data/lib/pubid/gost/identifier.rb +47 -0
  269. data/lib/pubid/gost/identifiers/foreign_reference.rb +25 -0
  270. data/lib/pubid/gost/identifiers/harmonized.rb +39 -0
  271. data/lib/pubid/gost/identifiers/identical_adoption.rb +39 -0
  272. data/lib/pubid/gost/identifiers/interstate_standard.rb +20 -0
  273. data/lib/pubid/gost/identifiers/national_standard.rb +19 -0
  274. data/lib/pubid/gost/identifiers.rb +13 -0
  275. data/lib/pubid/gost/parser.rb +105 -0
  276. data/lib/pubid/gost/renderer.rb +48 -0
  277. data/lib/pubid/gost/urn_generator.rb +46 -0
  278. data/lib/pubid/gost/urn_parser.rb +52 -0
  279. data/lib/pubid/gost.rb +50 -0
  280. data/lib/pubid/iala/builder.rb +22 -27
  281. data/lib/pubid/iala/identifier.rb +36 -8
  282. data/lib/pubid/iala/identifiers/advice.rb +1 -1
  283. data/lib/pubid/iala/identifiers/annex.rb +10 -10
  284. data/lib/pubid/iala/identifiers/general_assembly.rb +5 -2
  285. data/lib/pubid/iala/identifiers/guideline.rb +3 -1
  286. data/lib/pubid/iala/identifiers/letter.rb +1 -1
  287. data/lib/pubid/iala/identifiers/manual.rb +3 -1
  288. data/lib/pubid/iala/identifiers/model_course.rb +3 -1
  289. data/lib/pubid/iala/identifiers/recommendation.rb +3 -1
  290. data/lib/pubid/iala/identifiers/report.rb +1 -1
  291. data/lib/pubid/iala/identifiers/resolution.rb +1 -1
  292. data/lib/pubid/iala/identifiers/standard.rb +3 -1
  293. data/lib/pubid/iala/identifiers.rb +0 -1
  294. data/lib/pubid/iala/parser.rb +10 -2
  295. data/lib/pubid/iala/renderer.rb +1 -1
  296. data/lib/pubid/iala/urn_generator.rb +1 -1
  297. data/lib/pubid/iala/urn_parser.rb +24 -3
  298. data/lib/pubid/iala.rb +5 -4
  299. data/lib/pubid/iana/builder.rb +22 -0
  300. data/lib/pubid/iana/identifier.rb +140 -0
  301. data/lib/pubid/iana/identifiers/registry.rb +69 -0
  302. data/lib/pubid/iana/identifiers.rb +9 -0
  303. data/lib/pubid/iana/parser.rb +38 -0
  304. data/lib/pubid/iana/renderer.rb +32 -0
  305. data/lib/pubid/iana/urn_generator.rb +19 -0
  306. data/lib/pubid/iana/urn_parser.rb +24 -0
  307. data/lib/pubid/iana.rb +70 -0
  308. data/lib/pubid/identifier.rb +797 -26
  309. data/lib/pubid/identifier_metadata.rb +1 -0
  310. data/lib/pubid/idf/builder.rb +4 -4
  311. data/lib/pubid/idf/identifier.rb +18 -9
  312. data/lib/pubid/idf/parser.rb +3 -3
  313. data/lib/pubid/idf/renderer.rb +5 -5
  314. data/lib/pubid/idf/supplement_identifier.rb +3 -3
  315. data/lib/pubid/idf/urn_generator.rb +1 -1
  316. data/lib/pubid/idf.rb +7 -1
  317. data/lib/pubid/iec/builder.rb +49 -32
  318. data/lib/pubid/iec/components.rb +0 -1
  319. data/lib/pubid/iec/identifier.rb +112 -77
  320. data/lib/pubid/iec/identifiers/amendment.rb +6 -2
  321. data/lib/pubid/iec/identifiers/base.rb +2 -2
  322. data/lib/pubid/iec/identifiers/component_specification.rb +0 -10
  323. data/lib/pubid/iec/identifiers/conformity_assessment.rb +0 -10
  324. data/lib/pubid/iec/identifiers/consolidated_identifier.rb +6 -0
  325. data/lib/pubid/iec/identifiers/corrigendum.rb +2 -2
  326. data/lib/pubid/iec/identifiers/fragment_identifier.rb +13 -15
  327. data/lib/pubid/iec/identifiers/guide.rb +0 -16
  328. data/lib/pubid/iec/identifiers/international_standard.rb +22 -6
  329. data/lib/pubid/iec/identifiers/operational_document.rb +0 -10
  330. data/lib/pubid/iec/identifiers/publicly_available_specification.rb +0 -12
  331. data/lib/pubid/iec/identifiers/sheet_identifier.rb +12 -12
  332. data/lib/pubid/iec/identifiers/societal_technology_trend_report.rb +0 -10
  333. data/lib/pubid/iec/identifiers/systems_reference_document.rb +0 -10
  334. data/lib/pubid/iec/identifiers/technical_group.rb +30 -0
  335. data/lib/pubid/iec/identifiers/technical_report.rb +1 -18
  336. data/lib/pubid/iec/identifiers/technical_specification.rb +1 -18
  337. data/lib/pubid/iec/identifiers/technology_report.rb +0 -10
  338. data/lib/pubid/iec/identifiers/test_report_form.rb +0 -11
  339. data/lib/pubid/iec/identifiers/vap_identifier.rb +12 -12
  340. data/lib/pubid/iec/identifiers/white_paper.rb +0 -10
  341. data/lib/pubid/iec/identifiers/working_document.rb +24 -10
  342. data/lib/pubid/iec/identifiers.rb +1 -0
  343. data/lib/pubid/iec/parser.rb +53 -28
  344. data/lib/pubid/iec/renderer.rb +31 -18
  345. data/lib/pubid/iec/single_identifier.rb +34 -11
  346. data/lib/pubid/iec/supplement_identifier.rb +32 -19
  347. data/lib/pubid/iec/urn_generator.rb +227 -61
  348. data/lib/pubid/iec/urn_parser.rb +42 -4
  349. data/lib/pubid/iec.rb +2 -6
  350. data/lib/pubid/ieee/aiee/builder.rb +17 -3
  351. data/lib/pubid/ieee/aiee/identifier.rb +46 -62
  352. data/lib/pubid/ieee/aiee/parser.rb +1 -1
  353. data/lib/pubid/ieee/builder.rb +386 -69
  354. data/lib/pubid/ieee/compaction.rb +121 -0
  355. data/lib/pubid/ieee/components/draft.rb +47 -7
  356. data/lib/pubid/ieee/identifier.rb +1 -3
  357. data/lib/pubid/ieee/identifiers/adopted_standard.rb +62 -5
  358. data/lib/pubid/ieee/identifiers/amendment.rb +40 -0
  359. data/lib/pubid/ieee/identifiers/base.rb +148 -18
  360. data/lib/pubid/ieee/identifiers/code_number.rb +82 -0
  361. data/lib/pubid/ieee/identifiers/conformance_identifier.rb +6 -2
  362. data/lib/pubid/ieee/identifiers/corrigendum.rb +17 -2
  363. data/lib/pubid/ieee/identifiers/csa_dual_published.rb +24 -3
  364. data/lib/pubid/ieee/identifiers/dual_identifier.rb +3 -3
  365. data/lib/pubid/ieee/identifiers/dual_published.rb +58 -3
  366. data/lib/pubid/ieee/identifiers/iec_ieee_copublished.rb +102 -5
  367. data/lib/pubid/ieee/identifiers/interpretation_identifier.rb +25 -1
  368. data/lib/pubid/ieee/identifiers/joint_development.rb +64 -15
  369. data/lib/pubid/ieee/identifiers/multi_numbered_identifier.rb +58 -6
  370. data/lib/pubid/ieee/identifiers/nesc/base.rb +90 -19
  371. data/lib/pubid/ieee/identifiers/nesc/draft.rb +20 -5
  372. data/lib/pubid/ieee/identifiers/nesc/edition.rb +32 -0
  373. data/lib/pubid/ieee/identifiers/nesc/handbook.rb +16 -5
  374. data/lib/pubid/ieee/identifiers/nesc/redline.rb +11 -2
  375. data/lib/pubid/ieee/identifiers/nesc/standard.rb +18 -3
  376. data/lib/pubid/ieee/identifiers/nesc.rb +1 -0
  377. data/lib/pubid/ieee/identifiers/parenthetical_identifier.rb +3 -3
  378. data/lib/pubid/ieee/identifiers/project_draft_identifier.rb +14 -6
  379. data/lib/pubid/ieee/identifiers/redlined_standard.rb +4 -4
  380. data/lib/pubid/ieee/identifiers/si_standard.rb +4 -1
  381. data/lib/pubid/ieee/identifiers/standard.rb +4 -1
  382. data/lib/pubid/ieee/identifiers/supplement_identifier.rb +26 -7
  383. data/lib/pubid/ieee/identifiers.rb +2 -1
  384. data/lib/pubid/ieee/ire/builder.rb +7 -6
  385. data/lib/pubid/ieee/ire/identifier.rb +48 -33
  386. data/lib/pubid/ieee/ire/parser.rb +1 -1
  387. data/lib/pubid/ieee/nesc/builder.rb +33 -20
  388. data/lib/pubid/ieee/nesc/parser.rb +16 -15
  389. data/lib/pubid/ieee/parser.rb +462 -44
  390. data/lib/pubid/ieee/pre_parser.rb +1 -1
  391. data/lib/pubid/ieee/renderer.rb +158 -57
  392. data/lib/pubid/ieee/typed_stages.rb +12 -1
  393. data/lib/pubid/ieee/urn_generator.rb +35 -8
  394. data/lib/pubid/ieee.rb +63 -4
  395. data/lib/pubid/ieee_debug.rb +1 -0
  396. data/lib/pubid/ietf/builder.rb +78 -0
  397. data/lib/pubid/ietf/identifier.rb +5 -0
  398. data/lib/pubid/ietf/identifiers/base.rb +72 -0
  399. data/lib/pubid/ietf/identifiers/bcp.rb +36 -0
  400. data/lib/pubid/ietf/identifiers/fyi.rb +36 -0
  401. data/lib/pubid/ietf/identifiers/internet_draft.rb +45 -0
  402. data/lib/pubid/ietf/identifiers/rfc.rb +27 -0
  403. data/lib/pubid/ietf/identifiers/serialization.rb +51 -0
  404. data/lib/pubid/ietf/identifiers/std.rb +36 -0
  405. data/lib/pubid/ietf/identifiers.rb +14 -0
  406. data/lib/pubid/ietf/parser.rb +61 -0
  407. data/lib/pubid/ietf/renderer.rb +64 -0
  408. data/lib/pubid/ietf/urn_generator.rb +37 -0
  409. data/lib/pubid/ietf/urn_parser.rb +43 -0
  410. data/lib/pubid/ietf.rb +86 -0
  411. data/lib/pubid/iho/identifier.rb +1 -3
  412. data/lib/pubid/iho/identifiers/base.rb +11 -10
  413. data/lib/pubid/iho/identifiers/bibliographic.rb +1 -1
  414. data/lib/pubid/iho/identifiers/circular_letter.rb +1 -1
  415. data/lib/pubid/iho/identifiers/miscellaneous.rb +1 -1
  416. data/lib/pubid/iho/identifiers/publication.rb +1 -1
  417. data/lib/pubid/iho/identifiers/standard.rb +1 -1
  418. data/lib/pubid/iho/identifiers.rb +0 -1
  419. data/lib/pubid/iho/parser.rb +1 -1
  420. data/lib/pubid/iho.rb +8 -7
  421. data/lib/pubid/isbn/builder.rb +44 -0
  422. data/lib/pubid/isbn/check_digit.rb +53 -0
  423. data/lib/pubid/isbn/identifier.rb +81 -0
  424. data/lib/pubid/isbn/identifiers/book.rb +30 -0
  425. data/lib/pubid/isbn/identifiers.rb +9 -0
  426. data/lib/pubid/isbn/parser.rb +45 -0
  427. data/lib/pubid/isbn/renderer.rb +16 -0
  428. data/lib/pubid/isbn.rb +53 -0
  429. data/lib/pubid/iso/builder.rb +9 -4
  430. data/lib/pubid/iso/bundled_identifier.rb +5 -3
  431. data/lib/pubid/iso/combined_identifier.rb +6 -4
  432. data/lib/pubid/iso/identifier.rb +29 -8
  433. data/lib/pubid/iso/identifiers/directives_supplement.rb +12 -12
  434. data/lib/pubid/iso/normalizer.rb +2 -2
  435. data/lib/pubid/iso/parser.rb +17 -13
  436. data/lib/pubid/iso/supplement_identifier.rb +19 -6
  437. data/lib/pubid/iso/urn_generator.rb +4 -1
  438. data/lib/pubid/iso/urn_parser.rb +1 -1
  439. data/lib/pubid/iso.rb +7 -1
  440. data/lib/pubid/itu/builder.rb +230 -29
  441. data/lib/pubid/itu/components/code.rb +48 -5
  442. data/lib/pubid/itu/components/designation.rb +35 -0
  443. data/lib/pubid/itu/components.rb +1 -0
  444. data/lib/pubid/itu/identifier.rb +1 -3
  445. data/lib/pubid/itu/identifiers/addendum.rb +15 -0
  446. data/lib/pubid/itu/identifiers/amendment.rb +7 -29
  447. data/lib/pubid/itu/identifiers/annex.rb +13 -2
  448. data/lib/pubid/itu/identifiers/annex_of_recommendation.rb +87 -0
  449. data/lib/pubid/itu/identifiers/appendix_of_recommendation.rb +92 -0
  450. data/lib/pubid/itu/identifiers/base.rb +357 -25
  451. data/lib/pubid/itu/identifiers/combined_identifier.rb +99 -23
  452. data/lib/pubid/itu/identifiers/corrigendum.rb +16 -23
  453. data/lib/pubid/itu/identifiers/errata.rb +15 -0
  454. data/lib/pubid/itu/identifiers/handbook.rb +38 -0
  455. data/lib/pubid/itu/identifiers/question.rb +63 -0
  456. data/lib/pubid/itu/identifiers/recommendation.rb +3 -2
  457. data/lib/pubid/itu/identifiers/report.rb +39 -0
  458. data/lib/pubid/itu/identifiers/special_publication.rb +3 -1
  459. data/lib/pubid/itu/identifiers/standard_serialization.rb +58 -0
  460. data/lib/pubid/itu/identifiers/supplement.rb +119 -14
  461. data/lib/pubid/itu/identifiers.rb +10 -1
  462. data/lib/pubid/itu/model.rb +1 -1
  463. data/lib/pubid/itu/parser.rb +481 -27
  464. data/lib/pubid/itu/urn_generator.rb +36 -6
  465. data/lib/pubid/itu/urn_parser.rb +8 -0
  466. data/lib/pubid/itu.rb +12 -3
  467. data/lib/pubid/jcgm/builder.rb +24 -14
  468. data/lib/pubid/jcgm/identifier.rb +10 -0
  469. data/lib/pubid/jcgm/identifiers/amendment.rb +0 -2
  470. data/lib/pubid/jcgm/identifiers/corrigendum.rb +34 -0
  471. data/lib/pubid/jcgm/identifiers/gum_guide.rb +3 -3
  472. data/lib/pubid/jcgm/identifiers/meeting.rb +47 -0
  473. data/lib/pubid/jcgm/identifiers.rb +2 -0
  474. data/lib/pubid/jcgm/parser.rb +74 -7
  475. data/lib/pubid/jcgm/renderer.rb +33 -12
  476. data/lib/pubid/jcgm/single_identifier.rb +84 -6
  477. data/lib/pubid/jcgm/supplement_identifier.rb +39 -4
  478. data/lib/pubid/jcgm/urn_generator.rb +21 -13
  479. data/lib/pubid/jcgm/urn_parser.rb +164 -10
  480. data/lib/pubid/jcgm.rb +10 -0
  481. data/lib/pubid/jis/identifier.rb +62 -3
  482. data/lib/pubid/jis/parser.rb +1 -1
  483. data/lib/pubid/jis.rb +1 -0
  484. data/lib/pubid/nist/builder.rb +29 -9
  485. data/lib/pubid/nist/caster.rb +7 -45
  486. data/lib/pubid/nist/circular_supplement_builder.rb +7 -5
  487. data/lib/pubid/nist/components/stage.rb +2 -2
  488. data/lib/pubid/nist/components/supplement.rb +5 -2
  489. data/lib/pubid/nist/configuration.rb +6 -2
  490. data/lib/pubid/nist/identifier.rb +1 -3
  491. data/lib/pubid/nist/identifiers/base.rb +133 -39
  492. data/lib/pubid/nist/identifiers/circular.rb +10 -3
  493. data/lib/pubid/nist/identifiers/circular_supplement.rb +14 -5
  494. data/lib/pubid/nist/identifiers/commercial_standard.rb +1 -1
  495. data/lib/pubid/nist/identifiers/commercial_standard_emergency.rb +1 -1
  496. data/lib/pubid/nist/identifiers/commercial_standards_monthly.rb +8 -2
  497. data/lib/pubid/nist/identifiers/crpl_report.rb +8 -2
  498. data/lib/pubid/nist/identifiers/dated_document.rb +1 -1
  499. data/lib/pubid/nist/identifiers/federal_information_processing_standards.rb +1 -1
  500. data/lib/pubid/nist/identifiers/grant_contractor_report.rb +1 -1
  501. data/lib/pubid/nist/identifiers/handbook.rb +10 -3
  502. data/lib/pubid/nist/identifiers/internal_report.rb +13 -6
  503. data/lib/pubid/nist/identifiers/letter_circular.rb +1 -1
  504. data/lib/pubid/nist/identifiers/miscellaneous_publication.rb +15 -7
  505. data/lib/pubid/nist/identifiers/monograph.rb +15 -8
  506. data/lib/pubid/nist/identifiers/ncstar.rb +2 -2
  507. data/lib/pubid/nist/identifiers/nsrds.rb +1 -1
  508. data/lib/pubid/nist/identifiers/owmwp.rb +1 -1
  509. data/lib/pubid/nist/identifiers/report.rb +15 -7
  510. data/lib/pubid/nist/identifiers/special_publication.rb +10 -1
  511. data/lib/pubid/nist/identifiers/technical_note.rb +1 -1
  512. data/lib/pubid/nist/identifiers.rb +0 -1
  513. data/lib/pubid/nist/parser.rb +27 -4
  514. data/lib/pubid/nist/preprocessor.rb +15 -0
  515. data/lib/pubid/nist/router.rb +2 -2
  516. data/lib/pubid/nist/series/ir.rb +0 -3
  517. data/lib/pubid/nist/supplement_identifier.rb +24 -9
  518. data/lib/pubid/nist.rb +19 -13
  519. data/lib/pubid/oasis/builder.rb +79 -0
  520. data/lib/pubid/oasis/identifier.rb +160 -0
  521. data/lib/pubid/oasis/identifiers/standard.rb +29 -0
  522. data/lib/pubid/oasis/identifiers.rb +9 -0
  523. data/lib/pubid/oasis/parser.rb +34 -0
  524. data/lib/pubid/oasis/renderer.rb +29 -0
  525. data/lib/pubid/oasis/urn_generator.rb +25 -0
  526. data/lib/pubid/oasis/urn_parser.rb +22 -0
  527. data/lib/pubid/oasis.rb +80 -0
  528. data/lib/pubid/ogc/builder.rb +33 -0
  529. data/lib/pubid/ogc/identifier.rb +81 -0
  530. data/lib/pubid/ogc/identifiers/document.rb +26 -0
  531. data/lib/pubid/ogc/identifiers.rb +10 -0
  532. data/lib/pubid/ogc/parser.rb +40 -0
  533. data/lib/pubid/ogc/renderer.rb +33 -0
  534. data/lib/pubid/ogc/urn_generator.rb +14 -0
  535. data/lib/pubid/ogc/urn_parser.rb +22 -0
  536. data/lib/pubid/ogc.rb +72 -0
  537. data/lib/pubid/oiml/builder.rb +36 -8
  538. data/lib/pubid/oiml/identifier.rb +9 -1
  539. data/lib/pubid/oiml/identifiers/basic_publication.rb +2 -0
  540. data/lib/pubid/oiml/identifiers/bulletin.rb +36 -0
  541. data/lib/pubid/oiml/identifiers/code_number.rb +90 -0
  542. data/lib/pubid/oiml/identifiers/document.rb +2 -0
  543. data/lib/pubid/oiml/identifiers/expert_report.rb +2 -0
  544. data/lib/pubid/oiml/identifiers/guide.rb +2 -0
  545. data/lib/pubid/oiml/identifiers/recommendation.rb +2 -0
  546. data/lib/pubid/oiml/identifiers/seminar_report.rb +2 -0
  547. data/lib/pubid/oiml/identifiers/vocabulary.rb +2 -0
  548. data/lib/pubid/oiml/identifiers.rb +1 -0
  549. data/lib/pubid/oiml/parser.rb +12 -13
  550. data/lib/pubid/oiml/renderer.rb +13 -13
  551. data/lib/pubid/oiml/single_identifier.rb +41 -37
  552. data/lib/pubid/oiml/supplement_identifier.rb +36 -9
  553. data/lib/pubid/oiml/urn_parser.rb +23 -6
  554. data/lib/pubid/oiml.rb +10 -0
  555. data/lib/pubid/omg/builder.rb +20 -0
  556. data/lib/pubid/omg/identifier.rb +61 -0
  557. data/lib/pubid/omg/identifiers/specification.rb +32 -0
  558. data/lib/pubid/omg/identifiers.rb +9 -0
  559. data/lib/pubid/omg/parser.rb +93 -0
  560. data/lib/pubid/omg/renderer.rb +31 -0
  561. data/lib/pubid/omg.rb +52 -0
  562. data/lib/pubid/parser/grammar.rb +74 -0
  563. data/lib/pubid/parser.rb +2 -0
  564. data/lib/pubid/parsers/mr_string.rb +120 -7
  565. data/lib/pubid/plateau/builder.rb +8 -4
  566. data/lib/pubid/plateau/identifier.rb +1 -3
  567. data/lib/pubid/plateau/identifiers/base.rb +1 -6
  568. data/lib/pubid/plateau/identifiers/handbook.rb +1 -1
  569. data/lib/pubid/plateau/identifiers/technical_report.rb +1 -1
  570. data/lib/pubid/plateau/identifiers.rb +0 -1
  571. data/lib/pubid/plateau/parser.rb +16 -6
  572. data/lib/pubid/plateau/renderer.rb +1 -1
  573. data/lib/pubid/plateau/supplement_identifier.rb +11 -11
  574. data/lib/pubid/plateau.rb +14 -4
  575. data/lib/pubid/prefixes_support.rb +1 -0
  576. data/lib/pubid/renderers/annotator.rb +233 -0
  577. data/lib/pubid/renderers/base.rb +13 -0
  578. data/lib/pubid/renderers/directives_renderer.rb +3 -3
  579. data/lib/pubid/renderers/human_readable.rb +6 -4
  580. data/lib/pubid/renderers/mr_string.rb +54 -6
  581. data/lib/pubid/renderers/supplement_renderer.rb +1 -1
  582. data/lib/pubid/renderers.rb +1 -0
  583. data/lib/pubid/rendering/numbering.rb +25 -7
  584. data/lib/pubid/rendering/supplement.rb +4 -4
  585. data/lib/pubid/rendering.rb +1 -0
  586. data/lib/pubid/sae/builder.rb +1 -1
  587. data/lib/pubid/sae/identifier.rb +1 -3
  588. data/lib/pubid/sae/identifiers/base.rb +19 -9
  589. data/lib/pubid/sae/identifiers.rb +0 -1
  590. data/lib/pubid/sae/parser.rb +1 -1
  591. data/lib/pubid/sae.rb +4 -3
  592. data/lib/pubid/schema/declaration.rb +33 -0
  593. data/lib/pubid/schema/error.rb +14 -0
  594. data/lib/pubid/schema/identifier_type.rb +23 -0
  595. data/lib/pubid/schema/loader.rb +117 -0
  596. data/lib/pubid/schema/typed_stage.rb +24 -0
  597. data/lib/pubid/schema.rb +25 -0
  598. data/lib/pubid/tgpp/builder.rb +41 -0
  599. data/lib/pubid/tgpp/identifier.rb +107 -0
  600. data/lib/pubid/tgpp/identifiers/technical_report.rb +32 -0
  601. data/lib/pubid/tgpp/identifiers/technical_specification.rb +33 -0
  602. data/lib/pubid/tgpp/identifiers.rb +11 -0
  603. data/lib/pubid/tgpp/parser.rb +78 -0
  604. data/lib/pubid/tgpp/renderer.rb +52 -0
  605. data/lib/pubid/tgpp/urn_generator.rb +39 -0
  606. data/lib/pubid/tgpp/urn_parser.rb +30 -0
  607. data/lib/pubid/tgpp.rb +84 -0
  608. data/lib/pubid/type_resolver.rb +74 -0
  609. data/lib/pubid/un/builder.rb +42 -0
  610. data/lib/pubid/un/identifier.rb +40 -0
  611. data/lib/pubid/un/identifiers/document.rb +24 -0
  612. data/lib/pubid/un/identifiers.rb +9 -0
  613. data/lib/pubid/un/parser.rb +25 -0
  614. data/lib/pubid/un/renderer.rb +11 -0
  615. data/lib/pubid/un.rb +47 -0
  616. data/lib/pubid/urn_parser/errors.rb +8 -1
  617. data/lib/pubid/urn_parser.rb +1 -0
  618. data/lib/pubid/utils.rb +1 -0
  619. data/lib/pubid/version.rb +1 -1
  620. data/lib/pubid/w3c/builder.rb +55 -0
  621. data/lib/pubid/w3c/identifier.rb +130 -0
  622. data/lib/pubid/w3c/identifiers/candidate_recommendation.rb +31 -0
  623. data/lib/pubid/w3c/identifiers/candidate_recommendation_draft.rb +31 -0
  624. data/lib/pubid/w3c/identifiers/draft_note.rb +31 -0
  625. data/lib/pubid/w3c/identifiers/note.rb +30 -0
  626. data/lib/pubid/w3c/identifiers/obsolete_recommendation.rb +31 -0
  627. data/lib/pubid/w3c/identifiers/proposed_edited_recommendation.rb +31 -0
  628. data/lib/pubid/w3c/identifiers/proposed_recommendation.rb +31 -0
  629. data/lib/pubid/w3c/identifiers/recommendation.rb +31 -0
  630. data/lib/pubid/w3c/identifiers/standard.rb +12 -0
  631. data/lib/pubid/w3c/identifiers/superseded_recommendation.rb +31 -0
  632. data/lib/pubid/w3c/identifiers/working_draft.rb +30 -0
  633. data/lib/pubid/w3c/identifiers.rb +25 -0
  634. data/lib/pubid/w3c/parser.rb +43 -0
  635. data/lib/pubid/w3c/renderer.rb +35 -0
  636. data/lib/pubid/w3c/urn_generator.rb +18 -0
  637. data/lib/pubid/w3c/urn_parser.rb +36 -0
  638. data/lib/pubid/w3c.rb +68 -0
  639. data/lib/pubid/xsf/builder.rb +17 -0
  640. data/lib/pubid/xsf/identifier.rb +66 -0
  641. data/lib/pubid/xsf/identifiers/xep.rb +31 -0
  642. data/lib/pubid/xsf/identifiers.rb +9 -0
  643. data/lib/pubid/xsf/parser.rb +38 -0
  644. data/lib/pubid/xsf/renderer.rb +23 -0
  645. data/lib/pubid/xsf/urn_generator.rb +13 -0
  646. data/lib/pubid/xsf/urn_parser.rb +18 -0
  647. data/lib/pubid/xsf.rb +82 -0
  648. data/lib/pubid.rb +424 -22
  649. data/lib/tasks/conformance.rake +34 -0
  650. data/lib/tasks/docs.rake +3 -3
  651. data/lib/tasks/schema.rake +88 -0
  652. data/schema/core/joint_prefixes.yaml +24 -0
  653. data/schema/iec.yaml +1017 -0
  654. data/schema/iso.yaml +1586 -0
  655. data/schema/schema.schema.yaml +96 -0
  656. metadata +241 -19
  657. data/archived-gems/pubid-ccsds/update_codes.yaml +0 -1
  658. data/archived-gems/pubid-iec/stages.yaml +0 -129
  659. data/archived-gems/pubid-iec/update_codes.yaml +0 -67
  660. data/archived-gems/pubid-ieee/update_codes.yaml +0 -104
  661. data/archived-gems/pubid-iso/stages.yaml +0 -106
  662. data/archived-gems/pubid-iso/update_codes.yaml +0 -4
  663. data/archived-gems/pubid-itu/i18n.yaml +0 -13
  664. data/archived-gems/pubid-itu/series.yaml +0 -42
  665. data/archived-gems/pubid-nist/publishers.yaml +0 -6
  666. data/archived-gems/pubid-nist/update_codes.yaml +0 -93
  667. data/archived-gems/pubid-plateau/update_codes.yaml +0 -6
  668. data/lib/pubid/cen_cenelec/supplement_identifier.rb +0 -48
  669. data/lib/pubid/cie/components/code.rb +0 -80
  670. data/lib/pubid/iala/identifiers/base.rb +0 -14
  671. data/lib/pubid/iec/components/code.rb +0 -36
  672. /data/{archived-gems/pubid-nist → data/nist}/stages.yaml +0 -0
@@ -2,6 +2,28 @@
2
2
 
3
3
  module Pubid
4
4
  module Parsers
5
+ # Parses a machine-readable (MR) identifier string back into an Identifier.
6
+ #
7
+ # The MR format produced by `Renderers::MrString` mirrors the human form,
8
+ # lowercased and filename-safe:
9
+ #
10
+ # iso.9001.2015 → ISO 9001:2015
11
+ # iso-iec.17031-1.2020 → ISO/IEC 17031-1:2020
12
+ # iso.1234-1-2-3.2020 → ISO 1234-1-2-3:2020
13
+ # iso.tr.14627.2017 → ISO/TR 14627:2017
14
+ # iso.16634.-- → ISO 16634:--
15
+ # iso.9001.2015_amd.1.2020 → ISO 9001:2015/Amd 1:2020
16
+ # iso-iec.13818-1.2015_amd.3.2016_cor.1.2017 → ISO/IEC 13818-1:2015/Amd 3:2016/Cor 1:2017
17
+ #
18
+ # Copublishers join with `-` (`iso-iec`), supplements append with `_`
19
+ # (`iso.9001.2015_amd.1.2020`). Both round-trip back to `/` here. The
20
+ # flavor's own parser is case-insensitive on the type/publisher tokens
21
+ # we emit, so the lowercased slug re-parses cleanly.
22
+ #
23
+ # Note: NIST, CSA, IEEE, JIS, and SAE emit their own MR shapes that this
24
+ # parser does not attempt to reverse (their `to_s` already round-trips
25
+ # through the flavor's own parser, so callers should use that path for
26
+ # those flavors).
5
27
  class MrString < Base
6
28
  FLAVOR_MAP = {
7
29
  "ISO" => :iso,
@@ -59,6 +81,14 @@ module Pubid
59
81
  ccsds: :Ccsds,
60
82
  }.freeze
61
83
 
84
+ # Lower-case type codes the renderer emits as a separate MR segment
85
+ # (e.g. `iso.tr.14627`). When the parser sees one of these in the
86
+ # second position, it splits it back out as `/TYPE`.
87
+ TYPE_CODES = %w[
88
+ tr ts pas is amd cor suppl add ext guide spec
89
+ standard corrigendum amendment
90
+ ].freeze
91
+
62
92
  def self.parse(mr_string)
63
93
  flavor = detect_flavor(mr_string)
64
94
 
@@ -71,23 +101,106 @@ module Pubid
71
101
  raise ArgumentError, "Unknown flavor: #{flavor}" unless flavor_module
72
102
 
73
103
  identifier_string = convert_to_human_readable(mr_string)
104
+
105
+ # Refuse to hand the flavor a string this parser did not change.
106
+ #
107
+ # `detect_flavor` falls back to `:iso` for any publisher it does not
108
+ # know, and `Pubid::Iso.parse` sends an MR-shaped string straight back
109
+ # here — so a string that survives conversion unchanged recurses
110
+ # forever. `ECMA-426 ed1` is the standing example: "ECMA" is absent
111
+ # from FLAVOR_MAP, the string matches the MR shape heuristic
112
+ # (`/\A[A-Z]{2,}[.-]/`), and conversion is a fixed point.
113
+ # `Pubid::Iso.parse("ECMA-426 ed1")` raised SystemStackError.
114
+ #
115
+ # A fixed point means "this was never an MR string", which is a parse
116
+ # failure — the class the cross-flavor contract requires, and the one
117
+ # `Pubid.parse` treats as "try the next flavor".
118
+ if identifier_string == mr_string && flavor_module == Pubid::Iso
119
+ raise Parslet::ParseFailed,
120
+ "#{mr_string.inspect} is not an MR string"
121
+ end
122
+
74
123
  flavor_module.parse(identifier_string)
75
124
  end
76
125
 
77
126
  def self.detect_flavor(mr_string)
78
- publisher = mr_string.split(".").first.upcase
127
+ publisher = mr_string.split(/[._-]/).first.to_s.upcase
79
128
  FLAVOR_MAP[publisher] || :iso
80
129
  end
81
130
 
131
+ # Convert the MR slug back to a human-readable identifier string the
132
+ # flavor's own parser will accept. Splits on `_` first so each segment
133
+ # (head + each supplement) is converted independently, then re-joined
134
+ # with `/`. Within the head, the copublisher separator `-` (first
135
+ # segment only) is restored to `/`.
82
136
  def self.convert_to_human_readable(mr_string)
83
- parts = mr_string.split(".")
84
- if parts.size > 2
85
- year_part = parts.pop
86
- parts.join(" ") + ":#{year_part}"
87
- else
88
- parts.join(" ")
137
+ head, *supplements = mr_string.split("_")
138
+ human = convert_head(head)
139
+ return human if supplements.empty?
140
+
141
+ supplements.inject(human) do |acc, supp|
142
+ "#{acc}/#{convert_supplement(supp)}"
89
143
  end
90
144
  end
145
+
146
+ # Converts a head segment like `iso-iec.13818-1.2015` to
147
+ # `ISO/IEC 13818-1:2015`.
148
+ def self.convert_head(head)
149
+ parts = head.split(".")
150
+ return parts.join(" ").upcase if parts.length <= 1
151
+
152
+ publisher, *rest = parts
153
+
154
+ # Optional type code segment (iso.tr.14627.2017 → ISO/TR 14627:2017).
155
+ type_segment = nil
156
+ if rest.first && TYPE_CODES.include?(rest.first.downcase)
157
+ type_segment = rest.shift.upcase
158
+ end
159
+
160
+ # Trailing language segment — bare language code(s) with no parens.
161
+ # Detect by length <= 5 and all-alpha (en, fr, en-fr, ru).
162
+ language_segment = nil
163
+ if rest.last&.match?(/\A[a-z]{2}(-[a-z]{2})*\z/)
164
+ language_segment = rest.pop
165
+ end
166
+
167
+ # Trailing year segment (may be `--` for undated, or YYYY[-MM[-DD]]).
168
+ year_segment = nil
169
+ if rest.any? && rest.last.match?(/\A(?:\d{4}(?:-\d{2}){0,2}|--)\z/)
170
+ year_segment = rest.pop
171
+ end
172
+
173
+ # Copublishers in the first segment: `iso-iec` → `ISO/IEC`. Multi-word
174
+ # publisher bodies (e.g. `cen-cenelec`) round-trip the same way.
175
+ publisher_part = publisher.split("-").map(&:upcase).join("/")
176
+ publisher_part = "#{publisher_part}/#{type_segment}" if type_segment
177
+
178
+ number_part = rest.join(" ")
179
+ result = publisher_part
180
+ result += " #{number_part}" unless number_part.empty?
181
+ result += ":#{year_segment}" if year_segment
182
+ if language_segment
183
+ langs = language_segment.split("-").join(",")
184
+ result += "(#{langs})"
185
+ end
186
+ result
187
+ end
188
+
189
+ # Converts a supplement segment like `amd.1.2020` to `Amd 1:2020`.
190
+ # The first token is the lowercase type code; remaining tokens are
191
+ # number and optional year.
192
+ def self.convert_supplement(supp)
193
+ tokens = supp.split(".")
194
+ type = tokens.shift&.upcase
195
+ year = tokens.last&.match?(/\A\d{4}\z/) ? tokens.pop : nil
196
+ number = tokens.join(" ")
197
+
198
+ result = type.to_s
199
+ result += " #{number}" unless number.empty?
200
+ result += ":#{year}" if year
201
+ result
202
+ end
91
203
  end
92
204
  end
93
205
  end
206
+
@@ -9,7 +9,7 @@ module Pubid
9
9
 
10
10
  def build(parsed)
11
11
  # Check for annex supplement first
12
- if parsed[:base_identifier] && parsed[:annex_letter]
12
+ if parsed[:base] && parsed[:annex_letter]
13
13
  return build_annex(parsed)
14
14
  end
15
15
 
@@ -29,7 +29,11 @@ module Pubid
29
29
  }
30
30
 
31
31
  params[:annex] = parsed[:annex].to_s.to_i if parsed[:annex]
32
- params[:edition] = parsed[:edition].to_s if parsed[:edition]
32
+ # Only Handbook carries an edition; a legacy Latin TR captures the
33
+ # edition slice via the shared handbook rule, so drop it here.
34
+ if parsed[:edition] && klass == Identifiers::Handbook
35
+ params[:edition] = parsed[:edition].to_s
36
+ end
33
37
 
34
38
  klass.new(**params)
35
39
  end
@@ -38,10 +42,10 @@ module Pubid
38
42
 
39
43
  def build_annex(parsed)
40
44
  # Recursively parse base identifier
41
- base = build(parsed[:base_identifier])
45
+ base = build(parsed[:base])
42
46
 
43
47
  Identifiers::Annex.new(
44
- base_identifier: base,
48
+ base: base,
45
49
  letter: parsed[:annex_letter].to_s,
46
50
  )
47
51
  end
@@ -1,6 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Pubid::Plateau::Identifier is the PLATEAU base class (a real Pubid::Identifier
4
- # subclass); its body, `.parse`, and the Identifiers::Base back-compat alias
5
- # live in identifiers/base.rb. This file just ensures it is loaded.
6
- require_relative "identifiers/base"
4
+ # subclass); its body and `.parse` live in identifiers/base.rb. This file just ensures it is loaded.
@@ -3,7 +3,7 @@
3
3
  module Pubid
4
4
  module Plateau
5
5
  # Base class for all PLATEAU identifiers. Canonical name
6
- # Pubid::Plateau::Identifier (Identifiers::Base is a back-compat alias).
6
+ # Pubid::Plateau::Identifier.
7
7
  class Identifier < ::Pubid::Identifier
8
8
  # Delegate to the flavor module so callers can use
9
9
  # `Pubid::Plateau::Identifier.parse` consistently with other flavors.
@@ -51,10 +51,5 @@ module Pubid
51
51
  end
52
52
  end
53
53
 
54
- module Identifiers
55
- # Backward-compatible alias: PLATEAU's base class used to be
56
- # Pubid::Plateau::Identifiers::Base. It is now Pubid::Plateau::Identifier.
57
- Base = Pubid::Plateau::Identifier
58
- end
59
54
  end
60
55
  end
@@ -6,7 +6,7 @@ module Pubid
6
6
  # PLATEAU Handbook
7
7
  # Format: PLATEAU Handbook #NN[-annex] [第X.Y版]
8
8
  # Example: PLATEAU Handbook #00 第1.0版
9
- class Handbook < Base
9
+ class Handbook < Identifier
10
10
  attribute :edition, :string, default: -> {}
11
11
 
12
12
  def type_string
@@ -6,7 +6,7 @@ module Pubid
6
6
  # PLATEAU Technical Report
7
7
  # Format: PLATEAU Technical Report #NN[-annex]
8
8
  # Example: PLATEAU Technical Report #01
9
- class TechnicalReport < Base
9
+ class TechnicalReport < Identifier
10
10
  def type_string
11
11
  "Technical Report"
12
12
  end
@@ -4,7 +4,6 @@ module Pubid
4
4
  module Plateau
5
5
  module Identifiers
6
6
  autoload :Annex, "#{__dir__}/identifiers/annex"
7
- autoload :Base, "#{__dir__}/identifiers/base"
8
7
  autoload :Handbook, "#{__dir__}/identifiers/handbook"
9
8
  autoload :TechnicalReport, "#{__dir__}/identifiers/technical_report"
10
9
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Pubid
4
4
  module Plateau
5
- class Parser < Parslet::Parser
5
+ class Parser < ::Pubid::Parser::Grammar
6
6
  rule(:space) { str(" ") }
7
7
  rule(:dash) { str("-") }
8
8
  rule(:hash) { str("#") }
@@ -21,17 +21,23 @@ module Pubid
21
21
  # Number: #00, #01, #46, etc.
22
22
  rule(:number) { hash >> digits.as(:number) }
23
23
 
24
- # Annex: -1, -2, etc.
25
- rule(:annex) { dash >> digits.as(:annex) }
24
+ # Annex: -1, -2, etc. Legacy Latin references use "_" as the sub-number
25
+ # separator (e.g. "#46_1"); both render as the canonical dash.
26
+ rule(:annex) { (dash | str("_")) >> digits.as(:annex) }
26
27
 
27
- # Edition: 第1.0版, 第2.3版, etc. (only for Handbook)
28
+ # Edition (Handbook only). Canonical form is 第X.Y版; legacy Latin
29
+ # references write the bare X.Y (e.g. "PLATEAU Handbook #00 1.0"). Both
30
+ # capture the same numeric :edition slice, so they build the same object
31
+ # and render to the canonical 第X.Y版.
28
32
  rule(:edition_part) do
29
33
  str("第") >>
30
34
  (digits >> str(".") >> digits).as(:edition) >>
31
35
  str("版")
32
36
  end
33
37
 
34
- rule(:edition) { space >> edition_part }
38
+ rule(:edition_latin) { (digits >> str(".") >> digits).as(:edition) }
39
+
40
+ rule(:edition) { space >> (edition_part | edition_latin) }
35
41
 
36
42
  # Annex supplement: "Annex A", "Annex B", etc.
37
43
  rule(:annex_letter) { match["A-Z"].as(:annex_letter) }
@@ -45,6 +51,10 @@ module Pubid
45
51
  number >> annex.maybe >> edition.maybe
46
52
  end
47
53
 
54
+ # Technical Report has no edition. A legacy Latin TR reference carrying a
55
+ # trailing edition (e.g. "#00 1.0") is matched by the `handbook` rule
56
+ # (doc_type matches either keyword, tried first); the builder dispatches
57
+ # on :type and drops the captured edition for Technical Report.
48
58
  rule(:technical_report) do
49
59
  publisher >> space >> doc_type >> space >>
50
60
  number >> annex.maybe
@@ -52,7 +62,7 @@ module Pubid
52
62
 
53
63
  # Annex identifier (supplement)
54
64
  rule(:annex_identifier) do
55
- (handbook | technical_report).as(:base_identifier) >> annex_supplement
65
+ (handbook | technical_report).as(:base) >> annex_supplement
56
66
  end
57
67
 
58
68
  rule(:identifier) { annex_identifier | handbook | technical_report }
@@ -44,7 +44,7 @@ module Pubid
44
44
  end
45
45
 
46
46
  def render_supplement(id)
47
- "#{id.base_identifier} #{id.supplement_string}"
47
+ "#{id.base} #{id.supplement_string}"
48
48
  end
49
49
  end
50
50
  end
@@ -7,7 +7,7 @@ module Pubid
7
7
  # Base class for PLATEAU supplements (Annex)
8
8
  # Supplements reference a base identifier
9
9
  class SupplementIdentifier < Pubid::Identifier
10
- attribute :base_identifier, Identifiers::Base
10
+ attribute :base, Identifier
11
11
  attribute :letter, :string, default: -> {}
12
12
 
13
13
  def publisher
@@ -19,20 +19,20 @@ module Pubid
19
19
  raise NotImplementedError, "Subclasses must implement supplement_string"
20
20
  end
21
21
 
22
- # Override base_hash to extract edition, type, and annex from base_identifier
22
+ # Override base_hash to extract edition, type, and annex from base
23
23
  def base_hash
24
24
  hash = super
25
25
  # For Plateau supplements, edition comes from the base identifier
26
- if base_identifier.class.attributes.key?(:edition) && base_identifier.edition
27
- hash[:edition] = base_identifier.edition
26
+ if base.class.attributes.key?(:edition) && base.edition
27
+ hash[:edition] = base.edition
28
28
  end
29
- # Include type from base_identifier
30
- if base_identifier.class.attributes.key?(:type_string) && base_identifier.type_string
31
- hash[:type] = base_identifier.type_string
29
+ # Include type from base
30
+ if base.class.attributes.key?(:type_string) && base.type_string
31
+ hash[:type] = base.type_string
32
32
  end
33
- # Include annex from base_identifier
34
- if base_identifier.class.attributes.key?(:annex) && base_identifier.annex
35
- hash[:annex] = base_identifier.annex
33
+ # Include annex from base
34
+ if base.class.attributes.key?(:annex) && base.annex
35
+ hash[:annex] = base.annex
36
36
  end
37
37
  hash
38
38
  end
@@ -60,7 +60,7 @@ module Pubid
60
60
  def ==(other)
61
61
  return false unless other.class == self.class
62
62
 
63
- base_identifier == other.base_identifier &&
63
+ base == other.base &&
64
64
  letter == other.letter
65
65
  end
66
66
  end
data/lib/pubid/plateau.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "pubid"
3
4
  require "parslet"
4
5
 
5
6
  module Pubid
@@ -10,7 +11,7 @@ module Pubid
10
11
  PREFIXES = ["PLATEAU"].freeze
11
12
 
12
13
  autoload :Builder, "#{__dir__}/plateau/builder"
13
- autoload :Identifier, "#{__dir__}/plateau/identifier"
14
+ autoload :Identifier, "#{__dir__}/plateau/identifiers/base"
14
15
  autoload :Identifiers, "#{__dir__}/plateau/identifiers"
15
16
  autoload :Parser, "#{__dir__}/plateau/parser"
16
17
  autoload :Renderer, "#{__dir__}/plateau/renderer"
@@ -19,6 +20,15 @@ module Pubid
19
20
  autoload :UrnParser, "#{__dir__}/plateau/urn_parser"
20
21
 
21
22
  def self.parse(input)
23
+ unless input.is_a?(String)
24
+ raise Pubid::Errors::InvalidInputError,
25
+ Pubid::INPUT_NOT_A_STRING_MESSAGE
26
+ end
27
+
28
+ if input.length > Pubid::MAX_INPUT_LENGTH
29
+ raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
30
+ end
31
+
22
32
  # Apply legacy update_codes normalization first
23
33
  normalized = Core::UpdateCodes.apply(input, :plateau)
24
34
  parser = Parser.new
@@ -27,9 +37,9 @@ module Pubid
27
37
  end
28
38
 
29
39
  # Per-flavor format registry: inherits global formats, overrides :human
30
- Identifiers::Base.format_registry = FormatRegistry.new(parent: ::Pubid::Identifier.format_registry)
31
- Identifiers::Base.format_registry.register(:human, renderer: Plateau::Renderer)
32
- SupplementIdentifier.format_registry = Identifiers::Base.format_registry
40
+ Identifier.format_registry = FormatRegistry.new(parent: ::Pubid::Identifier.format_registry)
41
+ Identifier.format_registry.register(:human, renderer: Plateau::Renderer)
42
+ SupplementIdentifier.format_registry = Identifier.format_registry
33
43
 
34
44
  # Auto-discover all identifier types from the Identifiers namespace
35
45
  # @return [Array<Class>] identifier classes that define a self.type Hash
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "pubid"
3
4
  module Pubid
4
5
  # Mixin providing the uniform, static +prefixes+ class method that every
5
6
  # registered flavor exposes. relaton uses this to build a global prefix
@@ -0,0 +1,233 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pubid
4
+ module Renderers
5
+ # Adds semantic <span class="..."> markers to an already-rendered
6
+ # identifier string.
7
+ #
8
+ # In pubid 1.x annotation was applied in ONE place — `prerender_params`
9
+ # wrapped every value in the param hash — so all 40-odd flavors got it for
10
+ # free. pubid 2 replaced that hash with typed components and per-flavor
11
+ # renderers, and the annotation went with it: `Renderers::Base#annotate`
12
+ # exists, but only the HumanReadable family calls it. All 39 flavor
13
+ # renderers receive `context.annotated` and ignore it, so
14
+ # `to_s(annotated: true)` returned plain text for every flavor but ISO.
15
+ #
16
+ # Re-annotating inside 39 renderers would mean 39 chances to drift. This
17
+ # class recovers the markup from the OUTSIDE instead: it asks the
18
+ # identifier for its own component values and wraps each one where it
19
+ # appears in the rendered string. That is one implementation, and a flavor
20
+ # added tomorrow is covered without touching its renderer.
21
+ #
22
+ # It is a fallback, not a replacement. `Identifier#render` uses it only
23
+ # when the renderer produced no span of its own, so ISO's exact,
24
+ # render-time placement still wins.
25
+ #
26
+ # Deliberate limits:
27
+ #
28
+ # * A token that does not appear verbatim in the output is skipped. A
29
+ # renderer may transform a value (abbreviate it, change its case), and a
30
+ # missing span is a far better outcome than a wrong one or a crash.
31
+ # * Matching walks left to right behind a cursor, so a later token can
32
+ # never match text an earlier one already claimed — that is what stops
33
+ # the part "1" of `ISO 1234-1` from matching inside "1234".
34
+ class Annotator
35
+ # Semantic class for each token, in the order tokens appear in a printed
36
+ # identifier. Order matters: it is the sequence the cursor walks.
37
+ TOKENS = [
38
+ [:publisher, "publisher"],
39
+ [:copublishers, "publisher"],
40
+ %i[typed_stage typed_stage_css],
41
+ [:type, "doctype"],
42
+ [:stage, "stage"],
43
+ [:number, "docnumber"],
44
+ [:part, "part"],
45
+ [:subpart, "part"],
46
+ [:stage_iteration, "iteration"],
47
+ [:year, "year"],
48
+ [:edition, "edition"],
49
+ [:languages, "language"],
50
+ ].freeze
51
+
52
+ # Characters that may not sit directly against a match, so a token never
53
+ # binds to the middle of a longer run of the same character class.
54
+ WORD_CHAR = /[A-Za-z0-9]/
55
+
56
+ # How deep to follow nested identifiers. A wrapper around a wrapper is
57
+ # real (BSI adopts a CEN prestandard that adopts an ISO standard); four
58
+ # levels is past anything the corpus holds, and the cap is here so a
59
+ # cyclic `base` cannot hang a rendering call.
60
+ MAX_NESTING = 4
61
+
62
+ def initialize(identifier, context = nil)
63
+ @id = identifier
64
+ @context = context
65
+ end
66
+
67
+ # @param rendered [String] the plain rendering of {@id}
68
+ # @return [String] the same string with semantic spans inserted
69
+ def annotate(rendered)
70
+ return rendered unless rendered.is_a?(String) && !rendered.empty?
71
+
72
+ cursor = 0
73
+ result = +""
74
+
75
+ ordered_tokens(rendered).each do |text, css_class|
76
+ index = find_token(rendered, text, cursor)
77
+ next if index.nil?
78
+
79
+ result << rendered[cursor...index]
80
+ result << %(<span class="#{css_class}">#{text}</span>)
81
+ cursor = index + text.length
82
+ end
83
+
84
+ result << rendered[cursor..]
85
+ result
86
+ end
87
+
88
+ private
89
+
90
+ # Tokens sorted by where they actually appear, not by the order
91
+ # {TOKENS} lists them.
92
+ #
93
+ # Flavors disagree about layout: ISO prints the number before the type
94
+ # ("ISO/IEC TR 2131"), CCSDS prints it after ("CCSDS 121.0-B-2"). Walking
95
+ # in declaration order pushed the cursor past the number for CCSDS, so
96
+ # "121" was never annotated. Sorting by first occurrence makes the walk
97
+ # follow the printed identifier instead of a fixed idea of one.
98
+ def ordered_tokens(rendered)
99
+ tokens = []
100
+ each_token { |text, css_class| tokens << [text, css_class] }
101
+
102
+ decorated = tokens.each_with_index.map do |token, i|
103
+ position = find_token(rendered, token.first, 0) || rendered.length
104
+ [position, i, token]
105
+ end
106
+
107
+ decorated.sort_by { |position, i, _| [position, i] }.map(&:last)
108
+ end
109
+
110
+ # Yields [text, css_class] for every annotatable token this identifier
111
+ # actually carries, then every one its nested identifiers carry.
112
+ def each_token(&)
113
+ emit_tokens(@id, 0, &)
114
+ end
115
+
116
+ # A wrapper — an adoption, a supplement, a bundle — carries no number of
117
+ # its own and prints the document it wraps, so the annotatable tokens in
118
+ # its string belong to `base`. Reading only the wrapper's own attributes
119
+ # is why every CSA container and CIE's supplement rendered plain while
120
+ # accepting the flag.
121
+ #
122
+ # Order does not matter here: `ordered_tokens` sorts by first occurrence,
123
+ # so a nested token lands where it actually appears in the string.
124
+ def emit_tokens(id, depth, &)
125
+ return if depth > MAX_NESTING
126
+
127
+ tokens_for(id).each do |attr_name, css_class|
128
+ Array(token_values(id, attr_name)).each do |value|
129
+ text = token_text(value)
130
+ next if text.nil? || text.empty?
131
+
132
+ yield text, resolve_class(css_class, value)
133
+ end
134
+ end
135
+
136
+ nested_identifiers(id).each { |nested| emit_tokens(nested, depth + 1, &) }
137
+ end
138
+
139
+ # {TOKENS} unless the identifier names more of its own — see
140
+ # `Pubid::Identifier#annotation_tokens`.
141
+ def tokens_for(id)
142
+ id.respond_to?(:annotation_tokens) ? id.annotation_tokens : TOKENS
143
+ end
144
+
145
+ # The identifiers this one wraps, under any of the four names the gem
146
+ # uses: `base` (the uniform parent accessor), the `ids` / `identifiers`
147
+ # collections that bundles and consolidated identifiers hold instead, and
148
+ # CSA `Bundled`'s `bundled_with` — which holds the amendments a
149
+ # consolidation prints, and whose values it composes into the string from
150
+ # their components rather than from their own `to_s`.
151
+ def nested_identifiers(id)
152
+ %i[base ids identifiers bundled_with].flat_map do |name|
153
+ next [] unless id.respond_to?(name)
154
+
155
+ begin
156
+ Array(id.public_send(name))
157
+ rescue StandardError
158
+ []
159
+ end
160
+ end.grep(::Pubid::Identifier)
161
+ end
162
+
163
+ def token_values(id, attr_name)
164
+ return nil unless id.respond_to?(attr_name)
165
+
166
+ id.public_send(attr_name)
167
+ rescue StandardError
168
+ # A derived reader may assume state a partial identifier lacks. A
169
+ # missing span is not worth an exception on a rendering path.
170
+ nil
171
+ end
172
+
173
+ # The printed form of one component. Components render themselves through
174
+ # the context; a bare scalar is already its own text.
175
+ def token_text(value)
176
+ return nil if value.nil?
177
+
178
+ text = if value.respond_to?(:render)
179
+ value.render(context: @context)
180
+ else
181
+ value
182
+ end
183
+ text.to_s.strip
184
+ rescue StandardError
185
+ nil
186
+ end
187
+
188
+ def resolve_class(css_class, value)
189
+ return css_class unless css_class == :typed_stage_css
190
+
191
+ TypedStageClass.for(value)
192
+ end
193
+
194
+ # First occurrence of +text+ at or after +cursor+ that is not embedded in
195
+ # a longer word — so "1" matches the part in "ISO 1234-1", never the "1"
196
+ # inside "1234".
197
+ def find_token(rendered, text, cursor)
198
+ at = cursor
199
+ while (index = rendered.index(text, at))
200
+ return index if standalone?(rendered, index, text.length)
201
+
202
+ at = index + 1
203
+ end
204
+ nil
205
+ end
206
+
207
+ def standalone?(rendered, index, length)
208
+ before = index.zero? ? nil : rendered[index - 1]
209
+ after = rendered[index + length]
210
+
211
+ !WORD_CHAR.match?(before.to_s) && !WORD_CHAR.match?(after.to_s)
212
+ end
213
+
214
+ # The typed-stage class depends on the stage's type code, the same
215
+ # mapping `Renderers::Base#typed_stage_css` applies. Kept here rather
216
+ # than reached for through a private method on another object.
217
+ module TypedStageClass
218
+ MAP = {
219
+ "amd" => "amendment",
220
+ "cor" => "corrigendum",
221
+ "add" => "addendum",
222
+ }.freeze
223
+
224
+ def self.for(typed_stage)
225
+ code = typed_stage.respond_to?(:type_code) ? typed_stage.type_code.to_s : ""
226
+ return "stage" if code.empty? || code == "is"
227
+
228
+ MAP[code] || "doctype"
229
+ end
230
+ end
231
+ end
232
+ end
233
+ end
@@ -41,6 +41,19 @@ module Pubid
41
41
  %(#{lead}<span class="#{css_class}">#{core}</span>#{trail})
42
42
  end
43
43
 
44
+ # Render a value that may be a component or a bare scalar.
45
+ #
46
+ # `number`, `part` and `subpart` are `Components::Code` on
47
+ # ::Pubid::Identifier but a plain `:string` in a growing number of
48
+ # flavors, so a shared renderer cannot assume the format-aware
49
+ # `#render(context:)` seam is there. This keeps both shapes working while
50
+ # the flavors convert one tranche at a time.
51
+ def render_component(value, context)
52
+ return nil if value.nil?
53
+
54
+ value.respond_to?(:render) ? value.render(context: context) : value.to_s
55
+ end
56
+
44
57
  # Choose between "stage" and a type/supplement class for a typed stage.
45
58
  def typed_stage_css(typed_stage)
46
59
  code = typed_stage&.type_code.to_s