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,22 +2,38 @@
2
2
 
3
3
  module Pubid
4
4
  module Csa
5
- # Common base class for all CSA identifiers. SingleIdentifier (and the
6
- # concrete Identifiers::* types through it) descends from it, so every CSA
7
- # identifier is `is_a?(Pubid::Csa::Identifier)` natively and gets the shared
8
- # polymorphic `from_hash`. (The Wrapper/Composite container models are
9
- # separate Lutaml::Model types, not identifiers.)
5
+ # Common base class for EVERY CSA identifier — the single-document types
6
+ # through SingleIdentifier, and the container types (WrapperIdentifier,
7
+ # CompositeIdentifier, Bundled, Combined) directly. So every CSA identifier
8
+ # is `is_a?(Pubid::Csa::Identifier)` natively and gets the shared
9
+ # polymorphic `from_hash`, `#root`, `#exclude` and MR rendering. The
10
+ # containers used to be separate Lutaml::Model types, which is what made
11
+ # `root.number` raise rather than return the relaton index key.
10
12
  class Identifier < ::Pubid::Identifier
11
13
  def self.parse(input)
14
+ unless input.is_a?(String)
15
+ raise Pubid::Errors::InvalidInputError,
16
+ Pubid::INPUT_NOT_A_STRING_MESSAGE
17
+ end
18
+
12
19
  if input.length > Pubid::MAX_INPUT_LENGTH
13
- raise ArgumentError, Pubid::INPUT_TOO_LONG_MESSAGE
20
+ raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
14
21
  end
15
22
 
16
23
  # Filter out comments
17
- return nil if input.start_with?("#")
24
+ if input.start_with?("#")
25
+ raise Pubid::Errors::ParseError.new(
26
+ "Not a CSA identifier (comment): #{input}",
27
+ input: input, flavor: "csa",
28
+ )
29
+ end
18
30
 
19
- # Filter out non-standards
20
- return nil if input.match?(/^CSA (Communities|Group|Learning|OnDemand|Update)/)
31
+ # Filter out non-standards (memberships, courses, newsletters)
32
+ if input.match?(/^CSA (Communities|Group|Learning|OnDemand|Update)/)
33
+ raise Pubid::Errors::ParseError.new(
34
+ "Not a CSA standard: #{input}", input: input, flavor: "csa"
35
+ )
36
+ end
21
37
 
22
38
  # Preprocessing: normalize CEI to IEC (French name)
23
39
  input = input.gsub("CEI/IEC", "IEC").gsub(/\bCEI\b/, "IEC")
@@ -46,16 +62,14 @@ module Pubid
46
62
 
47
63
  # Parse normally (will create Bundled or Combined identifier)
48
64
  tree = Parser.new.parse(normalized)
49
- result = Builder.new.build(tree)
65
+ result = build!(tree, input)
50
66
 
51
67
  # Apply CAN/CSA- prefix to the appropriate parts
52
- if result
53
- set_publisher_prefix(result, "CAN/CSA-")
68
+ set_publisher_prefix(result, "CAN/CSA-")
54
69
 
55
- # Handle reaffirmation if present
56
- if (wrapped_input =~ /\(R(\d{4})\)/) && result.class.attributes.key?(:reaffirmation)
57
- result.reaffirmation = $1
58
- end
70
+ # Handle reaffirmation if present
71
+ if (wrapped_input =~ /\(R(\d{4})\)/) && result.class.attributes.key?(:reaffirmation)
72
+ result.reaffirmation = $1
59
73
  end
60
74
 
61
75
  return result
@@ -91,8 +105,7 @@ module Pubid
91
105
  wrapped_input = wrapped_input.sub(/^CSA-/, "CSA ")
92
106
 
93
107
  # Parse the wrapped identifier recursively
94
- wrapped_identifier = parse(wrapped_input)
95
- return nil unless wrapped_identifier
108
+ base = parse(wrapped_input)
96
109
 
97
110
  # NOTE: Series identifiers should be wrapped in CanadianAdopted when
98
111
  # they have CAN/CSA- prefix. The Series will handle rendering correctly.
@@ -101,32 +114,35 @@ module Pubid
101
114
  # Set publisher prefix on wrapped identifier
102
115
  # For Series identifiers with CAN/ wrapper, use full "CAN/CSA-" prefix
103
116
  # For other identifiers, use the detected original_prefix ("CSA-" or "CSA")
104
- if wrapped_identifier.class.attributes.key?(:publisher_prefix)
105
- if is_can_csa && wrapped_identifier.is_a?(Identifiers::Series)
117
+ if base.class.attributes.key?(:publisher_prefix)
118
+ if is_can_csa && base.is_a?(Identifiers::Series)
106
119
  # Series gets full "CAN/CSA-" prefix for proper rendering
107
- wrapped_identifier.publisher_prefix = "CAN/CSA-"
120
+ base.publisher_prefix = "CAN/CSA-"
108
121
  elsif original_prefix
109
- # For Combined identifiers, set on first identifier
110
- if wrapped_identifier.is_a?(Identifiers::Combined) && wrapped_identifier.first
111
- wrapped_identifier.first.publisher_prefix = original_prefix if wrapped_identifier.first.class.attributes.key?(:publisher_prefix)
122
+ # For Combined identifiers, set on the primary designation
123
+ if base.is_a?(Identifiers::Combined) && base.identifiers&.first
124
+ primary = base.identifiers.first
125
+ if primary.class.attributes.key?(:publisher_prefix)
126
+ primary.publisher_prefix = original_prefix
127
+ end
112
128
  else
113
129
  # For non-Combined identifiers, set directly
114
- wrapped_identifier.publisher_prefix = original_prefix
130
+ base.publisher_prefix = original_prefix
115
131
  end
116
132
  end
117
133
  end
118
134
 
119
- # Set reaffirmation on wrapped_identifier if it has the attribute
120
- if wrapped_identifier.class.attributes.key?(:reaffirmation) && reaffirm_year
121
- wrapped_identifier.reaffirmation = reaffirm_year
122
- if wrapped_identifier.class.attributes.key?(:original_reaffirmation_4digit)
123
- wrapped_identifier.original_reaffirmation_4digit = reaffirmation_was_4digit
135
+ # Set reaffirmation on base if it has the attribute
136
+ if base.class.attributes.key?(:reaffirmation) && reaffirm_year
137
+ base.reaffirmation = reaffirm_year
138
+ if base.class.attributes.key?(:original_reaffirmation_4digit)
139
+ base.original_reaffirmation_4digit = reaffirmation_was_4digit
124
140
  end
125
141
  end
126
142
 
127
143
  # Create CanadianAdoptedIdentifier wrapper
128
144
  result = Identifiers::CanadianAdopted.new
129
- result.wrapped_identifier = wrapped_identifier
145
+ result.base = base
130
146
  result.reaffirmation = reaffirm_year if reaffirm_year
131
147
 
132
148
  return result
@@ -157,44 +173,43 @@ module Pubid
157
173
  end
158
174
 
159
175
  # Parse the wrapped identifier recursively
160
- wrapped_identifier = parse(wrapped_input)
161
- return nil unless wrapped_identifier
176
+ base = parse(wrapped_input)
162
177
 
163
178
  # Set year_format for dash format identifiers (preserve original 2-digit year)
164
- if has_dash_year && wrapped_identifier.class.attributes.key?(:year_format)
165
- wrapped_identifier.year_format = "dash"
179
+ if has_dash_year && base.class.attributes.key?(:year_format)
180
+ base.year_format = "dash"
166
181
  # Mark original year as 2-digit so renderer converts back (1986 → 86)
167
- if wrapped_identifier.class.attributes.key?(:original_year_4digit)
168
- wrapped_identifier.original_year_4digit = false
182
+ if base.class.attributes.key?(:original_year_4digit)
183
+ base.original_year_4digit = false
169
184
  end
170
185
  end
171
186
 
172
187
  # Check if this is a Series identifier - return it directly with CAN3- prefix
173
188
  # Series identifiers are complete identifier types and handle the prefix themselves
174
189
  # They don't need to be wrapped in CanadianAdopted
175
- if wrapped_identifier.is_a?(Identifiers::Series)
176
- wrapped_identifier.publisher_prefix = "CAN3-"
177
- wrapped_identifier.reaffirmation = reaffirm_year if reaffirm_year
178
- wrapped_identifier.original_reaffirmation_4digit = reaffirmation_was_4digit
179
- return wrapped_identifier
190
+ if base.is_a?(Identifiers::Series)
191
+ base.publisher_prefix = "CAN3-"
192
+ base.reaffirmation = reaffirm_year if reaffirm_year
193
+ base.original_reaffirmation_4digit = reaffirmation_was_4digit
194
+ return base
180
195
  end
181
196
 
182
197
  # Set CAN3- as publisher prefix on wrapped identifier
183
- if wrapped_identifier.class.attributes.key?(:publisher_prefix)
184
- wrapped_identifier.publisher_prefix = "CAN3-"
198
+ if base.class.attributes.key?(:publisher_prefix)
199
+ base.publisher_prefix = "CAN3-"
185
200
  end
186
201
 
187
- # Set reaffirmation on wrapped_identifier if it has the attribute
188
- if wrapped_identifier.class.attributes.key?(:reaffirmation) && reaffirm_year
189
- wrapped_identifier.reaffirmation = reaffirm_year
190
- if wrapped_identifier.class.attributes.key?(:original_reaffirmation_4digit)
191
- wrapped_identifier.original_reaffirmation_4digit = reaffirmation_was_4digit
202
+ # Set reaffirmation on base if it has the attribute
203
+ if base.class.attributes.key?(:reaffirmation) && reaffirm_year
204
+ base.reaffirmation = reaffirm_year
205
+ if base.class.attributes.key?(:original_reaffirmation_4digit)
206
+ base.original_reaffirmation_4digit = reaffirmation_was_4digit
192
207
  end
193
208
  end
194
209
 
195
210
  # Create CanadianAdoptedIdentifier wrapper
196
211
  result = Identifiers::CanadianAdopted.new
197
- result.wrapped_identifier = wrapped_identifier
212
+ result.base = base
198
213
  result.reaffirmation = reaffirm_year if reaffirm_year
199
214
 
200
215
  return result
@@ -250,13 +265,20 @@ module Pubid
250
265
  "/Amd #{amendment_num}#{separator}#{amend_full_year}"
251
266
  end
252
267
 
253
- # Parse with appropriate flavor parser
254
- wrapped_identifier = parse_external_standard(wrapped_input)
255
- return nil unless wrapped_identifier
268
+ # Parse with appropriate flavor parser. The helper probes several
269
+ # flavors and reports failure with nil; the public contract is a
270
+ # raise, so translate here.
271
+ base = parse_external_standard(wrapped_input)
272
+ unless base
273
+ raise Pubid::Errors::ParseError.new(
274
+ "Unparseable adopted standard: #{input}",
275
+ input: input, flavor: "csa",
276
+ )
277
+ end
256
278
 
257
279
  # Create CsaAdoptedIdentifier wrapper
258
280
  result = Identifiers::CsaAdopted.new
259
- result.wrapped_identifier = wrapped_identifier
281
+ result.base = base
260
282
  result.reaffirmation = reaffirm_year if reaffirm_year
261
283
 
262
284
  return result
@@ -337,12 +359,9 @@ module Pubid
337
359
  break if token.match?(/^PACKAGE$/i)
338
360
 
339
361
  test_input = base_input.empty? ? token : "#{base_input} #{token}"
340
- parsed = parse(test_input)
341
- if parsed
342
- base_input = test_input
343
- else
344
- break
345
- end
362
+ break unless try_parse(test_input)
363
+
364
+ base_input = test_input
346
365
  end
347
366
 
348
367
  # Extract materials as everything between base and PACKAGE
@@ -354,13 +373,20 @@ module Pubid
354
373
  end
355
374
  end
356
375
 
357
- # Parse base identifier recursively
358
- base_identifier = base_input ? parse(base_input) : nil
359
- return nil unless base_identifier
376
+ # Parse the base identifier recursively. An unparseable base is a
377
+ # failed package, not a nil.
378
+ if base_input.nil? || base_input.empty?
379
+ raise Pubid::Errors::ParseError.new(
380
+ "Unparseable package base: #{input}",
381
+ input: input, flavor: "csa",
382
+ )
383
+ end
384
+
385
+ base = parse(base_input)
360
386
 
361
387
  # Create PackageIdentifier
362
388
  result = Identifiers::Package.new
363
- result.base_identifier = base_identifier
389
+ result.base = base
364
390
  # Set materials if present
365
391
  if package_materials && !package_materials.empty?
366
392
  result.package_materials = package_materials
@@ -393,39 +419,61 @@ module Pubid
393
419
  normalized = normalized.gsub("CAN3-", "CSA ")
394
420
 
395
421
  tree = Parser.new.parse(normalized)
396
- result = Builder.new.build(tree)
422
+ result = build!(tree, input)
397
423
 
398
424
  # Set publisher prefix if detected
399
- if result && publisher_prefix
400
- set_publisher_prefix(result, publisher_prefix)
401
- end
425
+ set_publisher_prefix(result, publisher_prefix) if publisher_prefix
402
426
 
403
427
  # Set year format if detected as dash and not already set
404
- if result && has_dash_year && result.year_format.nil?
405
- result.year_format = "dash"
406
- end
428
+ result.year_format = "dash" if has_dash_year && result.year_format.nil?
407
429
 
408
430
  result
409
431
  rescue Parslet::ParseFailed => e
410
432
  raise e
411
433
  end
412
434
 
435
+ # Build a parse tree into an identifier, or fail loudly.
436
+ #
437
+ # Builder#build can return nil for a tree it recognises but cannot map
438
+ # to a class. `parse` must return an identifier or raise — every flavor
439
+ # but `api` already honours that — because a nil surfaces in the caller
440
+ # as a NoMethodError far from the input that caused it, rather than as a
441
+ # catchable parse failure.
442
+ def self.build!(tree, input)
443
+ built = Builder.new.build(tree)
444
+ return built if built
445
+
446
+ raise Pubid::Errors::ParseError.new(
447
+ "Unparseable CSA identifier: #{input}",
448
+ input: input, flavor: "csa",
449
+ )
450
+ end
451
+
452
+ # Probe whether a string parses. The package base scan walks
453
+ # progressively longer prefixes and keeps the longest that succeeds, so
454
+ # it is the one caller that genuinely wants a nil rather than a raise.
455
+ def self.try_parse(input)
456
+ parse(input)
457
+ rescue Parslet::ParseFailed, ArgumentError
458
+ nil
459
+ end
460
+
413
461
  def self.set_publisher_prefix(obj, prefix)
414
462
  # Set on main object if it has the attribute
415
463
  if obj.class.attributes.key?(:publisher_prefix)
416
464
  obj.publisher_prefix = prefix
417
465
  end
418
466
 
419
- # Set on combined identifier parts
467
+ # Set on combined identifier parts. The primary designation always
468
+ # takes the prefix; a trailing one only when it printed a publisher of
469
+ # its own (a bare continuation must stay bare).
420
470
  if obj.is_a?(Identifiers::Combined)
421
- if obj.first&.class&.attributes&.key?(:publisher_prefix)
422
- obj.first.publisher_prefix = prefix
423
- end
424
- if obj.second && obj.second.class.attributes.key?(:has_publisher) && obj.second.has_publisher && obj.second.class.attributes.key?(:publisher_prefix)
425
- obj.second.publisher_prefix = prefix
426
- end
427
- if obj.third && obj.third.class.attributes.key?(:has_publisher) && obj.third.has_publisher && obj.third.class.attributes.key?(:publisher_prefix)
428
- obj.third.publisher_prefix = prefix
471
+ Array(obj.identifiers).each_with_index do |part, index|
472
+ next unless part.class.attributes.key?(:publisher_prefix)
473
+ next if index.positive? &&
474
+ !(part.class.attributes.key?(:has_publisher) && part.has_publisher)
475
+
476
+ part.publisher_prefix = prefix
429
477
  end
430
478
  end
431
479
 
@@ -458,6 +506,65 @@ module Pubid
458
506
 
459
507
  nil
460
508
  end
509
+
510
+ # CSA stores the publication year in its own plain `year` string
511
+ # attribute (not a `Components::Date`), so the base `#exclude`'s
512
+ # `:year`->`:date` remap nils the unused inherited `date` and leaves
513
+ # `year` intact. Override to nil `year` (and its formatting metadata)
514
+ # when `:year`/`:date` is excluded, so a partial (year-less) CSA
515
+ # reference is a year wildcard under `matches?(row, ignore: [:year])`.
516
+ # The metadata attrs (`year_format`, `year_prefix`,
517
+ # `original_year_4digit`, `french`) are year-derived — a year-less
518
+ # parse leaves them nil/false, so they must be reset here too or `==`
519
+ # would still differ. `french` in particular is only ever set from a
520
+ # `:F` year prefix (the French-edition form `CSA B149.1:F20`), so a
521
+ # bare `CSA B149.1` reference must wildcard it to match both the
522
+ # English and French editions of a document number. `super` preserves
523
+ # the base recursion into nested identifiers (adoption wrappers
524
+ # delegate their year to an inner id and lack a `year` accessor, hence
525
+ # the `respond_to?` guard). Mirrors BIPM.
526
+ def exclude(*args)
527
+ result = super
528
+ year_keys = args & %i[year date]
529
+ if !year_keys.empty? && result.respond_to?(:year=)
530
+ result.year = nil
531
+ result.year_format = nil
532
+ result.year_prefix = nil
533
+ result.original_year_4digit = false
534
+ result.french = nil
535
+ end
536
+ result
537
+ end
538
+
539
+ # CSA encodes its identity across many shape-specific attributes
540
+ # (`publisher_prefix`, `number`, `no_number`, `series`, `series_prefix`,
541
+ # `package`, `reaffirmation`, `year` with prefix/format/French markers,
542
+ # etc.) that the generic MrString renderer doesn't know about. The
543
+ # CSA `to_s` already round-trips losslessly through the CSA parser, so
544
+ # MR mirrors it: ` ` → `.`, `:` → `.` (so the year never looks like
545
+ # another code segment), `/` → `-` (CAN/CSA- prefix), then lowercased
546
+ # to match the all-lowercase MR convention (issue #142).
547
+ #
548
+ # Those three are the SEMANTIC mappings — they carry CSA's segment
549
+ # structure. Everything else is then neutralised by CHARSET, not by an
550
+ # enumerated escape list, so a character that appears in a future
551
+ # reference cannot leak into what `to_slug` hands a filesystem. CSA
552
+ # references really do carry `(`, `)`, `,`, `&` and `+` today
553
+ # (`CSA C22.2 NO. 100:14 (R2024)`, `… Code, Handbook & Training
554
+ # Package`, `… + A1:15`), and the old `tr` chain let all of them
555
+ # through — 601 of 816 slugs were not filename-safe. The BIPM `mr_slug`
556
+ # precedent.
557
+ def to_mr_string
558
+ to_s
559
+ .downcase
560
+ .tr(" ", ".").tr(":", ".").tr("/", "-")
561
+ .gsub(/[^a-z0-9._-]+/, "-")
562
+ .gsub(/\A[-.]+|[-.]+\z/, "")
563
+ end
564
+
565
+ def to_slug
566
+ to_mr_string
567
+ end
461
568
  end
462
569
  end
463
570
  end
@@ -5,17 +5,33 @@ require "lutaml/model"
5
5
  module Pubid
6
6
  module Csa
7
7
  module Identifiers
8
- class Bundled < Lutaml::Model::Serializable
9
- attribute :base, Standard
10
- attribute :bundled_with, Standard, collection: true
8
+ class Bundled < Identifier
9
+ # The consolidated base document and the amendments bundled with it.
10
+ # Both are typed cross-flavor and polymorphic — see
11
+ # WrapperIdentifier#base — because a bundle's base is routinely a Cec,
12
+ # which is not a Standard subclass. #root is inherited: it walks
13
+ # `base` to the origin document.
14
+ attribute :base, ::Pubid::Identifier, polymorphic: true
15
+ attribute :bundled_with, ::Pubid::Identifier, polymorphic: true,
16
+ collection: true
11
17
  attribute :reaffirmation, :string
12
18
  attribute :original_reaffirmation_4digit, :boolean, default: -> {
13
19
  false
14
20
  }
15
21
  attribute :year_format, :string # Add for compatibility with identifier.rb
16
22
 
17
- def to_s
18
- # For Cec identifiers, use normalized form (code instead of cec_part + NO.)
23
+ # A bundle is a base document consolidated with its amendments, so
24
+ # both matching primitives peel to that base.
25
+ def base_document
26
+ base ? base.base_document : self
27
+ end
28
+
29
+ def drop_supplements
30
+ base || self
31
+ end
32
+
33
+ def to_s(**opts)
34
+ # For Cec identifiers, use normalized form (number instead of cec_part + NO.)
19
35
  # This is used for "normalized form" rendering in bundled identifiers
20
36
  if base.is_a?(Cec)
21
37
  # Render base with normalized code format
@@ -31,7 +47,7 @@ module Pubid
31
47
  end
32
48
  end
33
49
  base_str = (needs_space ? "#{prefix} " : prefix) +
34
- base.code.value.to_s + # Use normalized code (e.g., "C22.2-1")
50
+ base.number.value.to_s + # Normalized code (e.g. "C22.2-1")
35
51
  ":#{year_display}"
36
52
  parts = [base_str]
37
53
  else
@@ -44,7 +60,7 @@ module Pubid
44
60
  bundled_with.each do |bundled|
45
61
  # For Cec identifiers, use normalized code format
46
62
  if bundled.is_a?(Cec)
47
- bundled_part = bundled.code.value.to_s # Normalized code (e.g., "C22.2-2")
63
+ bundled_part = bundled.number.value.to_s # e.g. "C22.2-2"
48
64
  if bundled.year
49
65
  # Use dash if year_format is dash, otherwise colon
50
66
  separator = bundled.year_format == "dash" ? "-" : ":"
@@ -62,7 +78,7 @@ module Pubid
62
78
  else
63
79
  # Standard rendering for other identifier types
64
80
  bundled_part = ""
65
- bundled_part += bundled.code.to_s if bundled.code
81
+ bundled_part += bundled.number.to_s if bundled.number
66
82
 
67
83
  if bundled.year
68
84
  # Use dash if year_format is dash, otherwise colon
@@ -117,7 +133,7 @@ module Pubid
117
133
  end
118
134
  end
119
135
 
120
- result
136
+ annotate_plain_render(result, **opts)
121
137
  end
122
138
  end
123
139
  end
@@ -12,40 +12,40 @@ module Pubid
12
12
  #
13
13
  # This is a wrapper pattern where:
14
14
  # - CAN/ indicates Canadian adoption
15
- # - The wrapped_identifier is the actual CSA standard being adopted
15
+ # - The base is the actual CSA standard being adopted
16
16
  #
17
17
  # This is semantically different from a string prefix because:
18
18
  # - CAN/ wraps an entire identifier (which may itself be complex)
19
19
  # - The wrapped identifier is recursively parsed as a full CSA identifier
20
20
  # - Proper object composition, not string manipulation
21
21
  class CanadianAdopted < WrapperIdentifier
22
- def to_s
22
+ def to_s(**opts)
23
23
  # For CAN3- identifiers, don't add CAN/ prefix (CAN3- is already complete)
24
24
  # For Series with CAN/CSA- prefix, don't add CAN/ (it's already complete)
25
25
  # For CAN/CSA- identifiers, CAN/ wraps CSA- part
26
- if wrapped_identifier.class.attributes.key?(:publisher_prefix) && wrapped_identifier.publisher_prefix
27
- prefix = wrapped_identifier.publisher_prefix
26
+ if base.class.attributes.key?(:publisher_prefix) && base.publisher_prefix
27
+ prefix = base.publisher_prefix
28
28
  # CAN3- is standalone, just render wrapped identifier
29
29
  # CAN/CSA- is already complete for Series identifiers
30
- if prefix == "CAN3-" || (prefix == "CAN/CSA-" && wrapped_identifier.is_a?(Identifiers::Series))
31
- result = wrapped_identifier.to_s
30
+ if prefix == "CAN3-" || (prefix == "CAN/CSA-" && base.is_a?(Identifiers::Series))
31
+ result = base.to_s
32
32
  else
33
33
  # For other cases, prepend CAN/ prefix
34
- result = "CAN/#{wrapped_identifier}"
34
+ result = "CAN/#{base}"
35
35
  end
36
36
  else
37
37
  # No publisher_prefix, prepend CAN/
38
- result = "CAN/#{wrapped_identifier}"
38
+ result = "CAN/#{base}"
39
39
  end
40
- # Only add reaffirmation from wrapper if wrapped_identifier doesn't have one
40
+ # Only add reaffirmation from wrapper if base doesn't have one
41
41
  # (to avoid duplicates - Identifier.parse sets it on both)
42
- if reaffirmation && !(wrapped_identifier.class.attributes.key?(:reaffirmation) && wrapped_identifier.reaffirmation)
42
+ if reaffirmation && !(base.class.attributes.key?(:reaffirmation) && base.reaffirmation)
43
43
  # Determine if space is needed before reaffirmation
44
44
  # Space needed if year is 2-digit and reaffirmation is 4-digit (original format)
45
45
  # No space if both year and reaffirmation are 2-digit
46
46
  # Check original_year_4digit flag - if false, year was originally 2-digit
47
- year_was_2digit = wrapped_identifier.class.attributes.key?(:original_year_4digit) &&
48
- !wrapped_identifier.original_year_4digit
47
+ year_was_2digit = base.class.attributes.key?(:original_year_4digit) &&
48
+ !base.original_year_4digit
49
49
  # Check original_reaffirmation_4digit flag
50
50
  reaffirmation_was_4digit = original_reaffirmation_4digit
51
51
 
@@ -74,7 +74,7 @@ module Pubid
74
74
  result += "(R#{reaffirmation_str})"
75
75
  end
76
76
  end
77
- result
77
+ annotate_plain_render(result, **opts)
78
78
  end
79
79
  end
80
80
  end
@@ -13,12 +13,52 @@ module Pubid
13
13
  attribute :cec_part, Components::Code # C22.2, C22.3, C22.4, C22.6
14
14
  attribute :no_number, Components::Code # Number after NO.
15
15
 
16
- # Combined code attribute for compatibility
17
- # Returns cec_part + "-" + no_number (e.g., "C22.2-1")
18
- def code
16
+ # `CSA C22.2 NO. 286:23` stores the joined "C22.2-286" in `number`, which
17
+ # appears nowhere in the printed form; the two halves it does print live
18
+ # in `cec_part` and `no_number`. Naming them gives the identifier its
19
+ # docnumber spans back.
20
+ def annotation_tokens
21
+ super + [
22
+ [:publisher_prefix, "publisher"],
23
+ [:cec_part, "docnumber"],
24
+ [:no_number, "docnumber"],
25
+ ]
26
+ end
27
+
28
+ # Merged with SingleIdentifier's block (lutaml combines an inherited
29
+ # key_value block with a subclass one), so this adds only the half of
30
+ # the code that Cec keeps separately.
31
+ key_value do
32
+ map "cec_part",
33
+ with: { to: :cec_part_to_kv, from: :cec_part_from_kv }
34
+ end
35
+
36
+ def cec_part_to_kv(model, doc)
37
+ emit_kv(doc, "cec_part", model.cec_part)
38
+ end
39
+
40
+ def cec_part_from_kv(model, value)
41
+ model.cec_part = Components::Code.new(value: value.to_s)
42
+ end
43
+
44
+ # The document code, synthesised from its two halves:
45
+ # cec_part + "-" + no_number (e.g. "C22.2-1"). This is the index key.
46
+ #
47
+ # It shadows the reader lutaml generates for the inherited `number`
48
+ # attribute, which is safe because Cec declares no `number` of its own
49
+ # — the ITU precedent (itu/identifiers/base.rb). `cec_part` and
50
+ # `no_number` are themselves serialized, so from_hash reproduces the
51
+ # synthesised value.
52
+ #
53
+ # Deliberately NOT memoised: `@number` is the very ivar lutaml uses to
54
+ # store the inherited attribute, so caching there would both discard a
55
+ # value assigned through the generated writer and go stale if
56
+ # `cec_part`/`no_number` were reassigned — and `number` is the relaton
57
+ # index key. The concatenation is trivial, so there is nothing to save.
58
+ def number
19
59
  return nil unless cec_part && no_number
20
60
 
21
- @code ||= Components::Code.new(value: "#{cec_part.value}-#{no_number.value}")
61
+ Components::Code.new(value: "#{cec_part.value}-#{no_number.value}")
22
62
  end
23
63
  end
24
64
  end