pubid 2.0.0.pre.alpha.9 → 2.0.0.pre.alpha.12

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 (461) hide show
  1. checksums.yaml +4 -4
  2. data/README.adoc +33 -11
  3. data/conformance/pending.yaml +4 -0
  4. data/lib/pubid/adobe/identifier.rb +1 -3
  5. data/lib/pubid/adobe/parser.rb +10 -2
  6. data/lib/pubid/adobe.rb +1 -0
  7. data/lib/pubid/amca/builder.rb +2 -2
  8. data/lib/pubid/amca/identifiers/base.rb +49 -27
  9. data/lib/pubid/amca/identifiers/interpretation.rb +17 -9
  10. data/lib/pubid/amca/identifiers/publication.rb +13 -9
  11. data/lib/pubid/amca/identifiers/standard.rb +2 -0
  12. data/lib/pubid/amca/parser.rb +1 -1
  13. data/lib/pubid/amca/renderer.rb +4 -4
  14. data/lib/pubid/amca/urn_generator.rb +2 -2
  15. data/lib/pubid/amca.rb +2 -1
  16. data/lib/pubid/ansi/builder.rb +2 -2
  17. data/lib/pubid/ansi/identifier.rb +18 -0
  18. data/lib/pubid/ansi/parser.rb +1 -1
  19. data/lib/pubid/ansi/renderer.rb +2 -2
  20. data/lib/pubid/ansi.rb +5 -5
  21. data/lib/pubid/api/builder.rb +16 -2
  22. data/lib/pubid/api/identifier.rb +25 -4
  23. data/lib/pubid/api/identifiers/mpms.rb +10 -8
  24. data/lib/pubid/api/parser.rb +1 -1
  25. data/lib/pubid/api/renderer.rb +16 -7
  26. data/lib/pubid/api/single_identifier.rb +5 -12
  27. data/lib/pubid/api.rb +1 -0
  28. data/lib/pubid/ashrae/builder.rb +31 -10
  29. data/lib/pubid/ashrae/identifiers/addenda_package.rb +6 -0
  30. data/lib/pubid/ashrae/identifiers/addendum.rb +8 -0
  31. data/lib/pubid/ashrae/identifiers/base.rb +81 -5
  32. data/lib/pubid/ashrae/identifiers/combined_addenda.rb +7 -0
  33. data/lib/pubid/ashrae/identifiers/errata.rb +9 -0
  34. data/lib/pubid/ashrae/identifiers/guideline.rb +3 -0
  35. data/lib/pubid/ashrae/identifiers/interpretation.rb +16 -0
  36. data/lib/pubid/ashrae/identifiers/standard.rb +3 -0
  37. data/lib/pubid/ashrae/parser.rb +23 -8
  38. data/lib/pubid/ashrae/renderer.rb +6 -6
  39. data/lib/pubid/ashrae/supplement_identifier.rb +17 -2
  40. data/lib/pubid/ashrae/urn_generator.rb +8 -2
  41. data/lib/pubid/ashrae.rb +7 -1
  42. data/lib/pubid/asme/builder.rb +11 -2
  43. data/lib/pubid/asme/identifier.rb +9 -0
  44. data/lib/pubid/asme/identifiers/standard.rb +86 -2
  45. data/lib/pubid/asme/parser.rb +1 -1
  46. data/lib/pubid/asme/renderer.rb +3 -3
  47. data/lib/pubid/asme/single_identifier.rb +8 -1
  48. data/lib/pubid/asme/urn_generator.rb +2 -2
  49. data/lib/pubid/asme.rb +1 -0
  50. data/lib/pubid/astm/builder.rb +10 -3
  51. data/lib/pubid/astm/identifier.rb +9 -0
  52. data/lib/pubid/astm/identifiers/adjunct.rb +16 -1
  53. data/lib/pubid/astm/identifiers/code_number.rb +81 -0
  54. data/lib/pubid/astm/identifiers/data_series.rb +2 -0
  55. data/lib/pubid/astm/identifiers/iso_dual_published.rb +16 -0
  56. data/lib/pubid/astm/identifiers/manual.rb +10 -0
  57. data/lib/pubid/astm/identifiers/monograph.rb +2 -0
  58. data/lib/pubid/astm/identifiers/research_report.rb +10 -0
  59. data/lib/pubid/astm/identifiers/standard.rb +10 -0
  60. data/lib/pubid/astm/identifiers/technical_report.rb +2 -0
  61. data/lib/pubid/astm/identifiers/work_in_progress.rb +2 -0
  62. data/lib/pubid/astm/identifiers.rb +1 -0
  63. data/lib/pubid/astm/parser.rb +1 -1
  64. data/lib/pubid/astm/renderer.rb +1 -1
  65. data/lib/pubid/astm/single_identifier.rb +31 -1
  66. data/lib/pubid/astm/urn_generator.rb +7 -5
  67. data/lib/pubid/astm.rb +1 -0
  68. data/lib/pubid/bipm/builder.rb +66 -10
  69. data/lib/pubid/bipm/identifier.rb +134 -9
  70. data/lib/pubid/bipm/identifiers/committee_document.rb +12 -0
  71. data/lib/pubid/bipm/identifiers/guide.rb +11 -0
  72. data/lib/pubid/bipm/identifiers/meeting.rb +12 -0
  73. data/lib/pubid/bipm/identifiers/mep.rb +12 -0
  74. data/lib/pubid/bipm/identifiers/metrologia_article.rb +24 -0
  75. data/lib/pubid/bipm/identifiers/si_brochure.rb +13 -0
  76. data/lib/pubid/bipm/parser.rb +65 -12
  77. data/lib/pubid/bipm/renderer.rb +8 -0
  78. data/lib/pubid/bipm/urn_parser.rb +11 -3
  79. data/lib/pubid/bipm.rb +7 -1
  80. data/lib/pubid/bsi/builder.rb +48 -38
  81. data/lib/pubid/bsi/components/date.rb +10 -4
  82. data/lib/pubid/bsi/identifiers/adopted_european_norm.rb +36 -7
  83. data/lib/pubid/bsi/identifiers/adopted_international_standard.rb +7 -6
  84. data/lib/pubid/bsi/identifiers/british_industrial_practice.rb +1 -2
  85. data/lib/pubid/bsi/identifiers/bundled_identifier.rb +10 -0
  86. data/lib/pubid/bsi/identifiers/committee_document.rb +10 -1
  87. data/lib/pubid/bsi/identifiers/handbook.rb +1 -3
  88. data/lib/pubid/bsi/identifiers/practice_guide.rb +1 -2
  89. data/lib/pubid/bsi/identifiers/set.rb +11 -0
  90. data/lib/pubid/bsi/identifiers/standalone_amendment.rb +10 -1
  91. data/lib/pubid/bsi/parser.rb +1 -1
  92. data/lib/pubid/bsi/renderer.rb +63 -51
  93. data/lib/pubid/bsi/single_identifier.rb +27 -9
  94. data/lib/pubid/bsi/urn_generator.rb +11 -2
  95. data/lib/pubid/bsi.rb +7 -1
  96. data/lib/pubid/builder/base.rb +18 -8
  97. data/lib/pubid/bundled_identifier.rb +16 -6
  98. data/lib/pubid/calconnect/identifier.rb +9 -5
  99. data/lib/pubid/calconnect/parser.rb +1 -1
  100. data/lib/pubid/calconnect.rb +7 -1
  101. data/lib/pubid/ccsds/identifier.rb +13 -2
  102. data/lib/pubid/ccsds/identifiers/base.rb +4 -2
  103. data/lib/pubid/ccsds/identifiers/corrigendum.rb +2 -2
  104. data/lib/pubid/ccsds/parser.rb +1 -1
  105. data/lib/pubid/ccsds/single_identifier.rb +16 -12
  106. data/lib/pubid/ccsds.rb +1 -0
  107. data/lib/pubid/cen_cenelec/builder.rb +101 -76
  108. data/lib/pubid/cen_cenelec/identifier.rb +115 -3
  109. data/lib/pubid/cen_cenelec/identifiers/adopted_european_norm.rb +27 -16
  110. data/lib/pubid/cen_cenelec/identifiers/amendment.rb +46 -6
  111. data/lib/pubid/cen_cenelec/identifiers/base.rb +8 -9
  112. data/lib/pubid/cen_cenelec/identifiers/cen_report.rb +1 -1
  113. data/lib/pubid/cen_cenelec/identifiers/cen_workshop_agreement.rb +1 -1
  114. data/lib/pubid/cen_cenelec/identifiers/cenelec_harmonization_document.rb +1 -1
  115. data/lib/pubid/cen_cenelec/identifiers/consolidated_identifier.rb +29 -20
  116. data/lib/pubid/cen_cenelec/identifiers/corrigendum.rb +47 -8
  117. data/lib/pubid/cen_cenelec/identifiers/european_prestandard.rb +27 -2
  118. data/lib/pubid/cen_cenelec/identifiers/european_specification.rb +1 -1
  119. data/lib/pubid/cen_cenelec/identifiers/fragment.rb +15 -5
  120. data/lib/pubid/cen_cenelec/identifiers/guide.rb +1 -1
  121. data/lib/pubid/cen_cenelec/identifiers/harmonization_document.rb +1 -1
  122. data/lib/pubid/cen_cenelec/identifiers/technical_report.rb +1 -1
  123. data/lib/pubid/cen_cenelec/identifiers/technical_specification.rb +1 -1
  124. data/lib/pubid/cen_cenelec/parser.rb +1 -1
  125. data/lib/pubid/cen_cenelec/renderer.rb +55 -60
  126. data/lib/pubid/cen_cenelec/single_identifier.rb +20 -2
  127. data/lib/pubid/cen_cenelec/urn_generator.rb +66 -44
  128. data/lib/pubid/cen_cenelec.rb +28 -10
  129. data/lib/pubid/cie/builder.rb +10 -0
  130. data/lib/pubid/cie/identifier.rb +6 -1
  131. data/lib/pubid/cie/identifiers/bundle.rb +4 -2
  132. data/lib/pubid/cie/identifiers/conference.rb +2 -2
  133. data/lib/pubid/cie/identifiers/corrigendum.rb +2 -2
  134. data/lib/pubid/cie/identifiers/dual_published.rb +2 -2
  135. data/lib/pubid/cie/identifiers/identical.rb +2 -2
  136. data/lib/pubid/cie/identifiers/joint_published.rb +2 -2
  137. data/lib/pubid/cie/identifiers/proceedings.rb +8 -6
  138. data/lib/pubid/cie/identifiers/standard.rb +22 -13
  139. data/lib/pubid/cie/identifiers/supplement.rb +2 -2
  140. data/lib/pubid/cie/identifiers/tutorial_bundle.rb +2 -2
  141. data/lib/pubid/cie/parser.rb +1 -1
  142. data/lib/pubid/cie.rb +7 -1
  143. data/lib/pubid/conformance/checks.rb +72 -0
  144. data/lib/pubid/conformance/corpus/case.rb +55 -0
  145. data/lib/pubid/conformance/corpus.rb +36 -0
  146. data/lib/pubid/conformance/generator.rb +212 -0
  147. data/lib/pubid/conformance/pending.rb +63 -0
  148. data/lib/pubid/conformance/runner.rb +153 -0
  149. data/lib/pubid/conformance.rb +55 -0
  150. data/lib/pubid/core.rb +2 -0
  151. data/lib/pubid/csa/builder.rb +47 -47
  152. data/lib/pubid/csa/composite_identifier.rb +14 -19
  153. data/lib/pubid/csa/identifier.rb +143 -81
  154. data/lib/pubid/csa/identifiers/bundled.rb +23 -13
  155. data/lib/pubid/csa/identifiers/canadian_adopted.rb +13 -13
  156. data/lib/pubid/csa/identifiers/cec.rb +44 -4
  157. data/lib/pubid/csa/identifiers/combined.rb +95 -73
  158. data/lib/pubid/csa/identifiers/csa_adopted.rb +4 -4
  159. data/lib/pubid/csa/identifiers/package.rb +2 -2
  160. data/lib/pubid/csa/parser.rb +6 -2
  161. data/lib/pubid/csa/renderer.rb +13 -5
  162. data/lib/pubid/csa/single_identifier.rb +93 -4
  163. data/lib/pubid/csa/urn_generator.rb +9 -2
  164. data/lib/pubid/csa/wrapper_identifier.rb +39 -23
  165. data/lib/pubid/csa.rb +7 -1
  166. data/lib/pubid/doi/identifier.rb +6 -3
  167. data/lib/pubid/doi/parser.rb +1 -1
  168. data/lib/pubid/doi.rb +1 -0
  169. data/lib/pubid/easc/identifier.rb +0 -2
  170. data/lib/pubid/easc/parser.rb +10 -2
  171. data/lib/pubid/easc.rb +1 -0
  172. data/lib/pubid/ecma/builder.rb +23 -9
  173. data/lib/pubid/ecma/identifier.rb +81 -10
  174. data/lib/pubid/ecma/parser.rb +39 -4
  175. data/lib/pubid/ecma/renderer.rb +29 -8
  176. data/lib/pubid/ecma/urn_generator.rb +34 -13
  177. data/lib/pubid/ecma/urn_parser.rb +30 -14
  178. data/lib/pubid/ecma.rb +1 -0
  179. data/lib/pubid/errors.rb +80 -0
  180. data/lib/pubid/etsi/builder.rb +6 -1
  181. data/lib/pubid/etsi/identifiers/base.rb +34 -20
  182. data/lib/pubid/etsi/identifiers/etsi_standard.rb +120 -41
  183. data/lib/pubid/etsi/identifiers/supplement_identifier.rb +10 -0
  184. data/lib/pubid/etsi/parser.rb +1 -1
  185. data/lib/pubid/etsi.rb +1 -0
  186. data/lib/pubid/evs/builder.rb +26 -0
  187. data/lib/pubid/evs/identifier.rb +37 -0
  188. data/lib/pubid/evs/identifiers/national_adoption.rb +22 -0
  189. data/lib/pubid/evs/identifiers.rb +9 -0
  190. data/lib/pubid/evs/parser.rb +27 -0
  191. data/lib/pubid/evs/renderer.rb +30 -0
  192. data/lib/pubid/evs/urn_generator.rb +30 -0
  193. data/lib/pubid/evs/urn_parser.rb +80 -0
  194. data/lib/pubid/evs.rb +90 -0
  195. data/lib/pubid/export.rb +1 -0
  196. data/lib/pubid/format_detector.rb +1 -0
  197. data/lib/pubid/gb/builder.rb +14 -9
  198. data/lib/pubid/gb/identifier.rb +29 -7
  199. data/lib/pubid/gb/parser.rb +5 -1
  200. data/lib/pubid/gb/renderer.rb +4 -3
  201. data/lib/pubid/gb.rb +2 -0
  202. data/lib/pubid/gost/builder.rb +46 -9
  203. data/lib/pubid/gost/identifier.rb +0 -2
  204. data/lib/pubid/gost/identifiers/foreign_reference.rb +1 -1
  205. data/lib/pubid/gost/parser.rb +10 -2
  206. data/lib/pubid/gost.rb +1 -0
  207. data/lib/pubid/iala/identifier.rb +9 -6
  208. data/lib/pubid/iala/parser.rb +10 -2
  209. data/lib/pubid/iala/urn_parser.rb +6 -2
  210. data/lib/pubid/iala.rb +1 -0
  211. data/lib/pubid/iana/builder.rb +3 -1
  212. data/lib/pubid/iana/identifier.rb +74 -6
  213. data/lib/pubid/iana/identifiers/registry.rb +36 -0
  214. data/lib/pubid/iana/parser.rb +1 -1
  215. data/lib/pubid/iana/renderer.rb +3 -0
  216. data/lib/pubid/iana/urn_generator.rb +5 -1
  217. data/lib/pubid/iana.rb +1 -0
  218. data/lib/pubid/identifier.rb +605 -13
  219. data/lib/pubid/identifier_metadata.rb +1 -0
  220. data/lib/pubid/idf/builder.rb +3 -3
  221. data/lib/pubid/idf/identifier.rb +25 -9
  222. data/lib/pubid/idf/identifiers/amendment.rb +1 -1
  223. data/lib/pubid/idf/identifiers/corrigendum.rb +1 -1
  224. data/lib/pubid/idf/identifiers/international_standard.rb +1 -1
  225. data/lib/pubid/idf/identifiers/reviewed_method.rb +1 -1
  226. data/lib/pubid/idf/parser.rb +1 -1
  227. data/lib/pubid/idf/renderer.rb +4 -4
  228. data/lib/pubid/idf/single_identifier.rb +1 -1
  229. data/lib/pubid/idf/urn_generator.rb +1 -1
  230. data/lib/pubid/idf.rb +7 -1
  231. data/lib/pubid/iec/builder.rb +14 -7
  232. data/lib/pubid/iec/components.rb +0 -1
  233. data/lib/pubid/iec/identifier.rb +73 -83
  234. data/lib/pubid/iec/identifiers/amendment.rb +6 -2
  235. data/lib/pubid/iec/identifiers/component_specification.rb +0 -10
  236. data/lib/pubid/iec/identifiers/conformity_assessment.rb +0 -10
  237. data/lib/pubid/iec/identifiers/corrigendum.rb +2 -2
  238. data/lib/pubid/iec/identifiers/fragment_identifier.rb +2 -4
  239. data/lib/pubid/iec/identifiers/guide.rb +0 -16
  240. data/lib/pubid/iec/identifiers/international_standard.rb +15 -2
  241. data/lib/pubid/iec/identifiers/operational_document.rb +0 -10
  242. data/lib/pubid/iec/identifiers/publicly_available_specification.rb +0 -12
  243. data/lib/pubid/iec/identifiers/societal_technology_trend_report.rb +0 -10
  244. data/lib/pubid/iec/identifiers/systems_reference_document.rb +0 -10
  245. data/lib/pubid/iec/identifiers/technical_report.rb +0 -17
  246. data/lib/pubid/iec/identifiers/technical_specification.rb +0 -17
  247. data/lib/pubid/iec/identifiers/technology_report.rb +0 -10
  248. data/lib/pubid/iec/identifiers/test_report_form.rb +0 -11
  249. data/lib/pubid/iec/identifiers/white_paper.rb +0 -10
  250. data/lib/pubid/iec/identifiers/working_document.rb +24 -10
  251. data/lib/pubid/iec/parser.rb +2 -18
  252. data/lib/pubid/iec/renderer.rb +13 -10
  253. data/lib/pubid/iec/single_identifier.rb +33 -10
  254. data/lib/pubid/iec/urn_generator.rb +227 -61
  255. data/lib/pubid/iec/urn_parser.rb +42 -4
  256. data/lib/pubid/iec.rb +1 -0
  257. data/lib/pubid/ieee/aiee/identifier.rb +2 -2
  258. data/lib/pubid/ieee/aiee/parser.rb +1 -1
  259. data/lib/pubid/ieee/builder.rb +87 -3
  260. data/lib/pubid/ieee/identifiers/adopted_standard.rb +32 -0
  261. data/lib/pubid/ieee/identifiers/base.rb +42 -5
  262. data/lib/pubid/ieee/identifiers/csa_dual_published.rb +21 -0
  263. data/lib/pubid/ieee/identifiers/dual_published.rb +55 -0
  264. data/lib/pubid/ieee/identifiers/interpretation_identifier.rb +23 -0
  265. data/lib/pubid/ieee/identifiers/joint_development.rb +11 -7
  266. data/lib/pubid/ieee/identifiers/multi_numbered_identifier.rb +57 -5
  267. data/lib/pubid/ieee/identifiers/nesc/base.rb +2 -2
  268. data/lib/pubid/ieee/identifiers/nesc/draft.rb +2 -2
  269. data/lib/pubid/ieee/identifiers/nesc/handbook.rb +2 -2
  270. data/lib/pubid/ieee/identifiers/nesc/redline.rb +2 -2
  271. data/lib/pubid/ieee/identifiers/nesc/standard.rb +2 -2
  272. data/lib/pubid/ieee/identifiers/si_standard.rb +5 -2
  273. data/lib/pubid/ieee/ire/identifier.rb +2 -2
  274. data/lib/pubid/ieee/ire/parser.rb +20 -2
  275. data/lib/pubid/ieee/nesc/parser.rb +1 -1
  276. data/lib/pubid/ieee/parser.rb +208 -33
  277. data/lib/pubid/ieee/project_renderer.rb +46 -0
  278. data/lib/pubid/ieee/renderer.rb +11 -8
  279. data/lib/pubid/ieee/urn_generator.rb +32 -3
  280. data/lib/pubid/ieee.rb +9 -1
  281. data/lib/pubid/ieee_debug.rb +1 -0
  282. data/lib/pubid/ietf/builder.rb +24 -8
  283. data/lib/pubid/ietf/identifiers/base.rb +25 -28
  284. data/lib/pubid/ietf/identifiers/bcp.rb +11 -0
  285. data/lib/pubid/ietf/identifiers/fyi.rb +11 -0
  286. data/lib/pubid/ietf/identifiers/internet_draft.rb +18 -2
  287. data/lib/pubid/ietf/identifiers/rfc.rb +2 -0
  288. data/lib/pubid/ietf/identifiers/serialization.rb +51 -0
  289. data/lib/pubid/ietf/identifiers/std.rb +11 -0
  290. data/lib/pubid/ietf/identifiers.rb +1 -0
  291. data/lib/pubid/ietf/parser.rb +27 -9
  292. data/lib/pubid/ietf/renderer.rb +44 -6
  293. data/lib/pubid/ietf/urn_generator.rb +20 -6
  294. data/lib/pubid/ietf/urn_parser.rb +10 -2
  295. data/lib/pubid/ietf.rb +12 -2
  296. data/lib/pubid/iho/identifiers/base.rb +10 -3
  297. data/lib/pubid/iho/parser.rb +1 -1
  298. data/lib/pubid/iho.rb +1 -0
  299. data/lib/pubid/isbn/identifier.rb +25 -7
  300. data/lib/pubid/isbn/parser.rb +1 -1
  301. data/lib/pubid/isbn.rb +1 -0
  302. data/lib/pubid/iso/builder.rb +10 -11
  303. data/lib/pubid/iso/bundled_identifier.rb +5 -3
  304. data/lib/pubid/iso/combined_identifier.rb +5 -3
  305. data/lib/pubid/iso/components.rb +0 -1
  306. data/lib/pubid/iso/identifier.rb +23 -11
  307. data/lib/pubid/iso/identifiers/directives.rb +7 -4
  308. data/lib/pubid/iso/identifiers/directives_supplement.rb +4 -4
  309. data/lib/pubid/iso/identifiers/tc_document.rb +31 -46
  310. data/lib/pubid/iso/normalizer.rb +1 -1
  311. data/lib/pubid/iso/parser.rb +1 -1
  312. data/lib/pubid/iso/urn_generator.rb +10 -10
  313. data/lib/pubid/iso.rb +12 -5
  314. data/lib/pubid/itu/builder.rb +10 -0
  315. data/lib/pubid/itu/identifiers/addendum.rb +2 -2
  316. data/lib/pubid/itu/identifiers/amendment.rb +2 -2
  317. data/lib/pubid/itu/identifiers/base.rb +29 -5
  318. data/lib/pubid/itu/identifiers/contribution.rb +31 -0
  319. data/lib/pubid/itu/identifiers/corrigendum.rb +2 -2
  320. data/lib/pubid/itu/identifiers/errata.rb +2 -2
  321. data/lib/pubid/itu/identifiers/supplement.rb +2 -2
  322. data/lib/pubid/itu/identifiers.rb +1 -0
  323. data/lib/pubid/itu/parser.rb +20 -3
  324. data/lib/pubid/itu.rb +1 -0
  325. data/lib/pubid/jcgm/builder.rb +1 -1
  326. data/lib/pubid/jcgm/identifier.rb +10 -0
  327. data/lib/pubid/jcgm/identifiers/meeting.rb +1 -1
  328. data/lib/pubid/jcgm/parser.rb +10 -3
  329. data/lib/pubid/jcgm/renderer.rb +10 -3
  330. data/lib/pubid/jcgm/single_identifier.rb +24 -15
  331. data/lib/pubid/jcgm/urn_generator.rb +1 -1
  332. data/lib/pubid/jcgm/urn_parser.rb +156 -14
  333. data/lib/pubid/jcgm.rb +10 -0
  334. data/lib/pubid/jis/identifier.rb +10 -3
  335. data/lib/pubid/jis/parser.rb +1 -1
  336. data/lib/pubid/jis.rb +1 -0
  337. data/lib/pubid/nist/builder.rb +12 -12
  338. data/lib/pubid/nist/components/stage.rb +2 -2
  339. data/lib/pubid/nist/configuration.rb +6 -8
  340. data/lib/pubid/nist/identifiers/base.rb +22 -5
  341. data/lib/pubid/nist/identifiers/circular.rb +9 -2
  342. data/lib/pubid/nist/identifiers/circular_supplement.rb +14 -5
  343. data/lib/pubid/nist/identifiers/commercial_standard_emergency.rb +1 -1
  344. data/lib/pubid/nist/identifiers/commercial_standards_monthly.rb +7 -1
  345. data/lib/pubid/nist/identifiers/crpl_report.rb +12 -7
  346. data/lib/pubid/nist/identifiers/federal_information_processing_standards.rb +2 -2
  347. data/lib/pubid/nist/identifiers/handbook.rb +9 -2
  348. data/lib/pubid/nist/identifiers/internal_report.rb +12 -5
  349. data/lib/pubid/nist/identifiers/miscellaneous_publication.rb +16 -8
  350. data/lib/pubid/nist/identifiers/monograph.rb +14 -7
  351. data/lib/pubid/nist/identifiers/report.rb +14 -6
  352. data/lib/pubid/nist/parser.rb +1 -1
  353. data/lib/pubid/nist/series/ir.rb +3 -7
  354. data/lib/pubid/nist/supplement_identifier.rb +16 -1
  355. data/lib/pubid/nist.rb +7 -1
  356. data/lib/pubid/oasis/builder.rb +12 -10
  357. data/lib/pubid/oasis/identifier.rb +89 -11
  358. data/lib/pubid/oasis/parser.rb +1 -1
  359. data/lib/pubid/oasis.rb +7 -1
  360. data/lib/pubid/ogc/identifier.rb +9 -5
  361. data/lib/pubid/ogc/parser.rb +1 -1
  362. data/lib/pubid/ogc.rb +1 -0
  363. data/lib/pubid/oiml/builder.rb +7 -3
  364. data/lib/pubid/oiml/identifiers/basic_publication.rb +2 -0
  365. data/lib/pubid/oiml/identifiers/bulletin.rb +40 -10
  366. data/lib/pubid/oiml/identifiers/code_number.rb +90 -0
  367. data/lib/pubid/oiml/identifiers/document.rb +2 -0
  368. data/lib/pubid/oiml/identifiers/expert_report.rb +2 -0
  369. data/lib/pubid/oiml/identifiers/guide.rb +2 -0
  370. data/lib/pubid/oiml/identifiers/recommendation.rb +2 -0
  371. data/lib/pubid/oiml/identifiers/seminar_report.rb +2 -0
  372. data/lib/pubid/oiml/identifiers/vocabulary.rb +2 -0
  373. data/lib/pubid/oiml/identifiers.rb +1 -0
  374. data/lib/pubid/oiml/parser.rb +8 -3
  375. data/lib/pubid/oiml/renderer.rb +3 -3
  376. data/lib/pubid/oiml/single_identifier.rb +26 -42
  377. data/lib/pubid/oiml/supplement_identifier.rb +27 -0
  378. data/lib/pubid/oiml/urn_generator.rb +4 -4
  379. data/lib/pubid/oiml.rb +7 -1
  380. data/lib/pubid/omg/builder.rb +1 -0
  381. data/lib/pubid/omg/identifier.rb +20 -3
  382. data/lib/pubid/omg/parser.rb +64 -11
  383. data/lib/pubid/omg/renderer.rb +13 -1
  384. data/lib/pubid/omg.rb +2 -1
  385. data/lib/pubid/parser/grammar.rb +74 -0
  386. data/lib/pubid/parser.rb +2 -0
  387. data/lib/pubid/parsers/mr_string.rb +19 -0
  388. data/lib/pubid/plateau/parser.rb +1 -1
  389. data/lib/pubid/plateau.rb +10 -0
  390. data/lib/pubid/prefixes_support.rb +1 -0
  391. data/lib/pubid/renderers/annotator.rb +233 -0
  392. data/lib/pubid/renderers/base.rb +13 -0
  393. data/lib/pubid/renderers/directives_renderer.rb +6 -4
  394. data/lib/pubid/renderers/human_readable.rb +3 -3
  395. data/lib/pubid/renderers/mr_string.rb +15 -9
  396. data/lib/pubid/renderers.rb +1 -0
  397. data/lib/pubid/rendering/numbering.rb +25 -7
  398. data/lib/pubid/rendering.rb +1 -0
  399. data/lib/pubid/sae/builder.rb +1 -1
  400. data/lib/pubid/sae/identifiers/base.rb +13 -3
  401. data/lib/pubid/sae/parser.rb +1 -1
  402. data/lib/pubid/sae/urn_generator.rb +1 -1
  403. data/lib/pubid/sae.rb +1 -0
  404. data/lib/pubid/schema/declaration.rb +33 -0
  405. data/lib/pubid/schema/error.rb +14 -0
  406. data/lib/pubid/schema/identifier_type.rb +23 -0
  407. data/lib/pubid/schema/loader.rb +117 -0
  408. data/lib/pubid/schema/typed_stage.rb +24 -0
  409. data/lib/pubid/schema.rb +25 -0
  410. data/lib/pubid/tgpp/builder.rb +1 -1
  411. data/lib/pubid/tgpp/identifier.rb +21 -5
  412. data/lib/pubid/tgpp/identifiers/technical_report.rb +2 -2
  413. data/lib/pubid/tgpp/identifiers/technical_specification.rb +2 -2
  414. data/lib/pubid/tgpp/parser.rb +22 -6
  415. data/lib/pubid/tgpp/renderer.rb +21 -5
  416. data/lib/pubid/tgpp/urn_generator.rb +27 -8
  417. data/lib/pubid/tgpp/urn_parser.rb +8 -6
  418. data/lib/pubid/tgpp.rb +7 -1
  419. data/lib/pubid/type_resolver.rb +16 -1
  420. data/lib/pubid/un/identifier.rb +6 -3
  421. data/lib/pubid/un/parser.rb +1 -1
  422. data/lib/pubid/un.rb +1 -0
  423. data/lib/pubid/urn_parser/errors.rb +8 -1
  424. data/lib/pubid/urn_parser.rb +1 -0
  425. data/lib/pubid/utils.rb +1 -0
  426. data/lib/pubid/version.rb +1 -1
  427. data/lib/pubid/w3c/builder.rb +5 -5
  428. data/lib/pubid/w3c/identifier.rb +38 -9
  429. data/lib/pubid/w3c/parser.rb +1 -1
  430. data/lib/pubid/w3c/renderer.rb +1 -1
  431. data/lib/pubid/w3c/urn_generator.rb +2 -2
  432. data/lib/pubid/w3c.rb +1 -0
  433. data/lib/pubid/xsf/identifier.rb +9 -5
  434. data/lib/pubid/xsf/parser.rb +17 -4
  435. data/lib/pubid/xsf.rb +7 -1
  436. data/lib/pubid.rb +399 -21
  437. data/lib/tasks/conformance.rake +34 -0
  438. data/lib/tasks/docs.rake +16 -14
  439. data/lib/tasks/schema.rake +88 -0
  440. data/schema/core/joint_prefixes.yaml +24 -0
  441. data/schema/iec.yaml +1017 -0
  442. data/schema/iso.yaml +1586 -0
  443. data/schema/schema.schema.yaml +96 -0
  444. metadata +40 -19
  445. data/archived-gems/pubid-ccsds/update_codes.yaml +0 -1
  446. data/archived-gems/pubid-iec/stages.yaml +0 -129
  447. data/archived-gems/pubid-iec/update_codes.yaml +0 -67
  448. data/archived-gems/pubid-ieee/update_codes.yaml +0 -104
  449. data/archived-gems/pubid-iso/stages.yaml +0 -106
  450. data/archived-gems/pubid-iso/update_codes.yaml +0 -4
  451. data/archived-gems/pubid-itu/i18n.yaml +0 -13
  452. data/archived-gems/pubid-itu/series.yaml +0 -42
  453. data/archived-gems/pubid-nist/publishers.yaml +0 -6
  454. data/archived-gems/pubid-nist/series.yaml +0 -121
  455. data/archived-gems/pubid-nist/update_codes.yaml +0 -93
  456. data/archived-gems/pubid-plateau/update_codes.yaml +0 -6
  457. data/lib/pubid/ccsds/identifiers/base_BASE_88929.rb +0 -70
  458. data/lib/pubid/cen_cenelec/supplement_identifier.rb +0 -48
  459. data/lib/pubid/iec/components/code.rb +0 -36
  460. data/lib/pubid/iso/components/code.rb +0 -24
  461. /data/{archived-gems/pubid-nist → data/nist}/stages.yaml +0 -0
@@ -58,13 +58,7 @@ module Pubid
58
58
  private
59
59
 
60
60
  def default_series_path
61
- # Prefer data/nist/series.yaml (the canonical home in the monorepo);
62
- # fall back to archived-gems/pubid-nist/series.yaml for backward
63
- # compat with the v1 layout.
64
- monorepo = File.join(__dir__, "../../../data/nist/series.yaml")
65
- return monorepo if File.exist?(monorepo)
66
-
67
- File.join(__dir__, "../../../archived-gems/pubid-nist/series.yaml")
61
+ File.join(__dir__, "../../../data/nist/series.yaml")
68
62
  end
69
63
 
70
64
  def load_series
@@ -78,6 +72,10 @@ module Pubid
78
72
  end
79
73
  end
80
74
 
81
- class ConfigurationError < StandardError; end
75
+ # Raised when the NIST series YAML cannot be loaded. Included in the
76
+ # marker module so `rescue Pubid::Errors::Error` stays exhaustive.
77
+ class ConfigurationError < StandardError
78
+ include ::Pubid::Errors::Error
79
+ end
82
80
  end
83
81
  end
@@ -27,7 +27,7 @@ module Pubid
27
27
  # bare { "value" => … } with no part;
28
28
  # - drop the redundant build artifacts, decomposed pieces of the
29
29
  # canonical number that identity/rendering never read.
30
- COMPACT_FLAT_CODES = %w[series number subseries].freeze
30
+ COMPACT_FLAT_CODES = %w[series subseries].freeze
31
31
  COMPACT_FLAT_VALUES = %w[volume].freeze
32
32
  COMPACT_DROP_ATTRS = %w[first_number second_number].freeze
33
33
 
@@ -105,7 +105,14 @@ module Pubid
105
105
  # equality so a manually-built id (without subseries set) still equals a
106
106
  # parsed one.
107
107
  attribute :subseries, Components::Code
108
- attribute :number, Components::Code
108
+ # Plain :string. NIST never populated the Code beyond `value`: `subpart`
109
+ # is set nowhere, and `part` was a reader splitting `value` on "-" that
110
+ # no library code called. `series`, `subseries`, `first_number`,
111
+ # `second_number` and `parts` keep the component.
112
+ attribute :number, :string
113
+ # NIST never uses `subpart`; declare it so the inherited Components::Code
114
+ # does not disagree with this class (the determinism landmine).
115
+ attribute :subpart, :string
109
116
 
110
117
  # V2 COMPONENTS (Lutaml::Model objects) - PROPER SEPARATION
111
118
  # `edition` (Components::Edition: type + id + additional_text) is the
@@ -170,6 +177,10 @@ module Pubid
170
177
  end
171
178
 
172
179
  def initialize(**attributes)
180
+ # This override calls `super()` with no attributes and assigns through
181
+ # setters instead, so it bypasses the base constructor's coercion and
182
+ # unknown-key check. Apply them here to keep the cross-flavor contract.
183
+ attributes = self.class.normalize_init_attributes(attributes)
173
184
  super()
174
185
 
175
186
  attrs = self.class.attributes
@@ -304,13 +315,19 @@ module Pubid
304
315
  # @param format [:full, :long, :abbreviated, :short, :mr] output format
305
316
  def to_s(format = nil)
306
317
  # Handle both keyword argument (hash) and positional argument (symbol/string)
318
+ annotated = format.is_a?(Hash) ? format[:annotated] : nil
307
319
  format = format[:format] if format.is_a?(Hash)
308
320
 
309
321
  # Default to parsed_format if available (preserves input format on round-trip)
310
322
  # Falls back to :short format for output (normalization)
311
323
  # Explicit format parameter always overrides parsed_format
312
- render(format: :human,
313
- nist_format: format || parsed_format&.to_sym || :short)
324
+ rendered = render(format: :human,
325
+ nist_format: format || parsed_format&.to_sym || :short)
326
+
327
+ # NIST's `to_s` takes a POSITIONAL format and its `render` override
328
+ # builds no rendering context, so neither the flag nor the context
329
+ # reaches the shared hook. Apply the annotation fallback here.
330
+ annotate_plain_render(rendered, annotated: annotated)
314
331
  end
315
332
 
316
333
  # NIST has its own defined MR format (to_mr_style) used by relaton-nist
@@ -402,7 +419,7 @@ module Pubid
402
419
  # "National Institute of Standards and Technology Special Publication 800-27, Revision A"
403
420
  result = publisher_full_name
404
421
  result += " #{series_full_name}" if series
405
- result += " #{number.value}" if number
422
+ result += " #{number}" if number
406
423
  result += parts.map { |p| "-#{p}" }.join if parts&.any?
407
424
 
408
425
  # Render volume and issue number in long form: "Vol. 6, No. 12"
@@ -53,7 +53,14 @@ module Pubid
53
53
  # Override to_s for CIRC-specific edition+year rendering
54
54
  # CIRC uses dot notation: "NBS CIRC 11e2.1915" instead of "NBS CIRC 11e2-1915"
55
55
  def to_s(format = nil)
56
- result = super
56
+ # The rewrite below is `$`-anchored, so it has to run on the PLAIN
57
+ # string: with the flag set, `super` returns a string ending in
58
+ # "</span>" and the anchor no longer matches. Render plain, rewrite,
59
+ # then annotate.
60
+ annotated = format.is_a?(Hash) ? format[:annotated] : nil
61
+ format = format[:format] if format.is_a?(Hash)
62
+
63
+ result = super(format)
57
64
 
58
65
  # For CIRC edition patterns with additional_text (year), render with dot notation
59
66
  # "11e2-1915" format: edition.id="2", additional_text="1915" → render as "11e2.1915"
@@ -65,7 +72,7 @@ module Pubid
65
72
  "e#{edition.id || '?'}.#{edition.additional_text}")
66
73
  end
67
74
 
68
- result
75
+ annotate_plain_render(result, annotated: annotated)
69
76
  end
70
77
  end
71
78
  end
@@ -37,13 +37,22 @@ module Pubid
37
37
  attribute :implicit_supplement, :boolean # true for implicit supplements (e.g., "145r11/1925")
38
38
 
39
39
  def to_s(format = :short)
40
+ # A caller passing `annotated:` reaches this positional parameter as a
41
+ # Hash. Normalise it away before `super`, so the annotation is applied
42
+ # once, here, and covers the date-range branch too — that branch
43
+ # returns a hand-composed string and never reached `super` at all.
44
+ annotated = format.is_a?(Hash) ? format[:annotated] : nil
45
+ format = format[:format] || :short if format.is_a?(Hash)
46
+
40
47
  # Handle date range supplements (no base identifier)
41
- if supplement_date_range_start && supplement_date_range_end
42
- return "NBS CIRC sup#{supplement_date_range_start}-#{supplement_date_range_end}"
43
- end
48
+ result = if supplement_date_range_start && supplement_date_range_end
49
+ "NBS CIRC sup#{supplement_date_range_start}-#{supplement_date_range_end}"
50
+ else
51
+ # Use parent's rendering for base + supplement
52
+ super(format)
53
+ end
44
54
 
45
- # Use parent's rendering for base + supplement
46
- super
55
+ annotate_plain_render(result, annotated: annotated)
47
56
  end
48
57
  end
49
58
  end
@@ -44,7 +44,7 @@ module Pubid
44
44
  result += " CS-E"
45
45
 
46
46
  # Number (already extracted from e104 → 104 in builder)
47
- result += " #{number.value}" if number
47
+ result += " #{number}" if number
48
48
 
49
49
  # Edition (e1943 for e104-43 pattern) plus any other component the
50
50
  # parser attached (volume, supplement, version, ...).
@@ -41,6 +41,12 @@ module Pubid
41
41
  end
42
42
 
43
43
  def to_s(format = nil)
44
+ # A caller passing `annotated:` reaches this positional parameter as
45
+ # a Hash — Ruby converts keywords for a method that accepts none.
46
+ # The same idiom `Identifiers::Base#to_s` uses. This renderer
47
+ # ignores `format`, so only the flag is read.
48
+ annotated = format.is_a?(Hash) ? format[:annotated] : nil
49
+
44
50
  result = "#{publisher} #{series}"
45
51
 
46
52
  # Proper Volume and Part components
@@ -55,7 +61,7 @@ module Pubid
55
61
  result += " #{vol_str}n#{issue_number.number}"
56
62
  end
57
63
 
58
- result
64
+ annotate_plain_render(result, annotated: annotated)
59
65
  end
60
66
  end
61
67
  end
@@ -46,7 +46,7 @@ module Pubid
46
46
  def normalized_number
47
47
  return nil unless number
48
48
 
49
- num_value = number.value.to_s
49
+ num_value = number.to_s
50
50
 
51
51
  # Pattern: c4-4 → 4-4 (hide 'c' prefix)
52
52
  case num_value
@@ -64,12 +64,12 @@ module Pubid
64
64
  end
65
65
 
66
66
  # Override number to return normalized value
67
- # Tests expect number.value to be normalized (c4-4 → 4-4, 4-m-5 → 4-M-5)
67
+ # Tests expect number to be normalized (c4-4 → 4-4, 4-m-5 → 4-M-5)
68
68
  def number
69
69
  num = super
70
70
  return num unless num
71
71
 
72
- num_value = num.value.to_s
72
+ num_value = num.to_s
73
73
 
74
74
  # Pattern: c4-4 → 4-4 (hide 'c' prefix)
75
75
  case num_value
@@ -83,11 +83,16 @@ module Pubid
83
83
  num_value = num_value.gsub(/^m-/, "M-")
84
84
  end
85
85
 
86
- # Return new Code object with normalized value
87
- Components::Code.new(value: num_value)
86
+ num_value
88
87
  end
89
88
 
90
89
  def to_s(format = nil)
90
+ # A caller passing `annotated:` reaches this positional parameter as
91
+ # a Hash — Ruby converts keywords for a method that accepts none.
92
+ # The same idiom `Identifiers::Base#to_s` uses. This renderer
93
+ # ignores `format`, so only the flag is read.
94
+ annotated = format.is_a?(Hash) ? format[:annotated] : nil
95
+
91
96
  # Use actual series attribute if it contains subseries (e.g., "CRPL-F-B")
92
97
  # Otherwise use default series_code ("CRPL")
93
98
  series_to_render = if series&.value&.include?("CRPL-F-")
@@ -100,7 +105,7 @@ module Pubid
100
105
 
101
106
  if number
102
107
  # Normalize number value for CRPL patterns
103
- num_value = number.value.to_s
108
+ num_value = number.to_s
104
109
 
105
110
  # Pattern: c4-4 → 4-4 (hide 'c' prefix)
106
111
  case num_value
@@ -124,7 +129,7 @@ module Pubid
124
129
  result += append_short_components
125
130
 
126
131
  result += range_notation if range_notation
127
- result
132
+ annotate_plain_render(result, annotated: annotated)
128
133
  end
129
134
  end
130
135
  end
@@ -66,14 +66,14 @@ module Pubid
66
66
  # Convert dash-year patterns to edition format for FIPS
67
67
  # Pattern: "14-1971" → "14e1971" (edition year format)
68
68
  # But preserve dash for parts: "140-3" stays as-is
69
- if number.value =~ /^(\d{1,3})-(\d{4})$/
69
+ if number =~ /^(\d{1,3})-(\d{4})$/
70
70
  # This is a number-year pattern (e.g., "14-1971")
71
71
  # Convert to edition format: "14e1971"
72
72
  number_part = $1
73
73
  year_part = $2
74
74
  result += " #{number_part}e#{year_part}"
75
75
  else
76
- result += " #{number.value}"
76
+ result += " #{number}"
77
77
  end
78
78
  end
79
79
 
@@ -34,8 +34,15 @@ module Pubid
34
34
  # Override to_s to handle edition+year pattern with dash instead of dot
35
35
  # Pattern: "44e2-1955" (dash separator) not "44e2.1955" (dot separator)
36
36
  def to_s(format = nil)
37
+ # The rewrite below is `$`-anchored, so it has to run on the PLAIN
38
+ # string: with the flag set, `super` returns a string ending in
39
+ # "</span>" and the anchor no longer matches. Render plain, rewrite,
40
+ # then annotate.
41
+ annotated = format.is_a?(Hash) ? format[:annotated] : nil
42
+ format = format[:format] if format.is_a?(Hash)
43
+
37
44
  # Use the base to_s method first
38
- result = super
45
+ result = super(format)
39
46
 
40
47
  # If edition has additional_text (year), replace dot with dash
41
48
  if edition&.additional_text && !edition.additional_text.empty?
@@ -43,7 +50,7 @@ module Pubid
43
50
  "#{edition.type}#{edition.id}-#{edition.additional_text}")
44
51
  end
45
52
 
46
- result
53
+ annotate_plain_render(result, annotated: annotated)
47
54
  end
48
55
  end
49
56
  end
@@ -37,6 +37,11 @@ module Pubid
37
37
  # @param format [Symbol, Hash, nil] output format
38
38
  # @return [String] formatted identifier
39
39
  def to_s(format = nil)
40
+ # `super` is handed a SYMBOL below, so the base's annotation hook
41
+ # never sees the flag. Capture it here and wrap what comes back —
42
+ # exactly once, because the base returned plain text.
43
+ annotated = format.is_a?(Hash) ? format[:annotated] : nil
44
+
40
45
  # Handle both keyword argument (hash) and positional argument (symbol/string)
41
46
  effective_format = if format.is_a?(Hash)
42
47
  format[:format]
@@ -45,11 +50,13 @@ module Pubid
45
50
  end
46
51
 
47
52
  # If explicit format is specified, use it. Otherwise, default to short.
48
- if effective_format.nil?
49
- super(:short)
50
- else
51
- super(effective_format)
52
- end
53
+ rendered = if effective_format.nil?
54
+ super(:short)
55
+ else
56
+ super(effective_format)
57
+ end
58
+
59
+ annotate_plain_render(rendered, annotated: annotated)
53
60
  end
54
61
  end
55
62
  end
@@ -36,27 +36,35 @@ module Pubid
36
36
  "MP"
37
37
  end
38
38
 
39
+ # A caller passing `annotated:` reaches this positional parameter as a
40
+ # Hash — Ruby converts keywords for a method that accepts none. The same
41
+ # idiom `Identifiers::Base#to_s` uses.
39
42
  def to_s(format = :short)
40
- case format
41
- when :mr
42
- to_mr_style
43
- else
44
- to_short_style
45
- end
43
+ annotated = format.is_a?(Hash) ? format[:annotated] : nil
44
+ format = format[:format] || :short if format.is_a?(Hash)
45
+
46
+ rendered = case format
47
+ when :mr
48
+ to_mr_style
49
+ else
50
+ to_short_style
51
+ end
52
+
53
+ annotate_plain_render(rendered, annotated: annotated)
46
54
  end
47
55
 
48
56
  public
49
57
 
50
58
  def to_short_style
51
59
  result = "#{default_publisher} #{series_code}"
52
- result += " #{number.value}" if number
60
+ result += " #{number}" if number
53
61
  result += append_short_components
54
62
  result
55
63
  end
56
64
 
57
65
  def to_mr_style
58
66
  result = "#{default_publisher}.#{series_code}"
59
- result += ".#{number.value}" if number
67
+ result += ".#{number}" if number
60
68
  result += append_mr_components
61
69
  result
62
70
  end
@@ -35,6 +35,11 @@ module Pubid
35
35
  end
36
36
 
37
37
  def to_s(format = nil)
38
+ # `super` is handed a SYMBOL below, so the base's annotation hook
39
+ # never sees the flag. Capture it here and wrap what comes back —
40
+ # exactly once, because the base returned plain text.
41
+ annotated = format.is_a?(Hash) ? format[:annotated] : nil
42
+
38
43
  # Handle both keyword argument (hash) and positional argument (symbol/string)
39
44
  effective_format = if format.is_a?(Hash)
40
45
  format[:format]
@@ -43,13 +48,15 @@ module Pubid
43
48
  end
44
49
 
45
50
  # If explicit format is specified, use it. Otherwise, default to short.
46
- if effective_format.nil?
47
- super(:short)
48
- elsif effective_format == :mr
49
- to_mr_style
50
- else
51
- super(effective_format)
52
- end
51
+ rendered = if effective_format.nil?
52
+ super(:short)
53
+ elsif effective_format == :mr
54
+ to_mr_style
55
+ else
56
+ super(effective_format)
57
+ end
58
+
59
+ annotate_plain_render(rendered, annotated: annotated)
53
60
  end
54
61
 
55
62
  private
@@ -39,13 +39,21 @@ module Pubid
39
39
  "RPT"
40
40
  end
41
41
 
42
+ # A caller passing `annotated:` reaches this positional parameter as a
43
+ # Hash — Ruby converts keywords for a method that accepts none. The same
44
+ # idiom `Identifiers::Base#to_s` uses.
42
45
  def to_s(format = :short)
43
- case format
44
- when :mr
45
- to_mr_style
46
- else
47
- to_short_style
48
- end
46
+ annotated = format.is_a?(Hash) ? format[:annotated] : nil
47
+ format = format[:format] || :short if format.is_a?(Hash)
48
+
49
+ rendered = case format
50
+ when :mr
51
+ to_mr_style
52
+ else
53
+ to_short_style
54
+ end
55
+
56
+ annotate_plain_render(rendered, annotated: annotated)
49
57
  end
50
58
 
51
59
  private
@@ -6,7 +6,7 @@ module Pubid
6
6
  module Nist
7
7
  # Parser class for NIST identifiers
8
8
  # Single Responsibility: Parsing NIST identifier syntax
9
- class Parser < Parslet::Parser
9
+ class Parser < ::Pubid::Parser::Grammar
10
10
 
11
11
  # Class-level parse method with preprocessing.
12
12
  # Delegates all string normalization to Nist::Preprocessor, then
@@ -31,9 +31,7 @@ module Pubid
31
31
  letter_suffix:)
32
32
  return false unless letter_suffix == "R"
33
33
 
34
- identifier.number = Components::Code.new(
35
- value: "#{first_num.value}-#{letter_base}",
36
- )
34
+ identifier.number = "#{first_num.value}-#{letter_base}"
37
35
  edition_obj = Components::Edition.new(type: "r", id: "1")
38
36
  identifier.edition = edition_obj
39
37
  true
@@ -42,13 +40,11 @@ module Pubid
42
40
  def self.finalize_identifier(identifier, _parsed_hash)
43
41
  return unless identifier.number
44
42
 
45
- value = identifier.number.value.to_s
43
+ value = identifier.number.to_s
46
44
  match = value.match(/^(\d+)e(\d{4})$/)
47
45
  return unless match
48
46
 
49
- identifier.number = Components::Code.new(
50
- value: "#{match[1]}-#{match[2]}",
51
- )
47
+ identifier.number = "#{match[1]}-#{match[2]}"
52
48
  identifier.edition = nil
53
49
  end
54
50
  end
@@ -25,13 +25,28 @@ module Pubid
25
25
  base&.series
26
26
  end
27
27
 
28
+ # Four exits — a date range, a base-less delegation, an update form and
29
+ # the canonical one — so the composition stays whole in its own method
30
+ # and `to_s` wraps its single result. `super` is not reachable from a
31
+ # private method, so it is handed in as a block.
28
32
  def to_s(format = :short)
33
+ annotated = format.is_a?(Hash) ? format[:annotated] : nil
34
+ format = format[:format] || :short if format.is_a?(Hash)
35
+
36
+ rendered = render_plain(format) { super(format) }
37
+
38
+ annotate_plain_render(rendered, annotated: annotated)
39
+ end
40
+
41
+ private
42
+
43
+ def render_plain(format)
29
44
  # Handle date range supplements (no base identifier)
30
45
  if supplement_date_range_start && supplement_date_range_end
31
46
  return "NBS CIRC sup#{supplement_date_range_start}-#{supplement_date_range_end}"
32
47
  end
33
48
 
34
- return super unless base
49
+ return yield unless base
35
50
 
36
51
  result = base.to_s(format)
37
52
 
data/lib/pubid/nist.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "pubid"
3
4
  module Pubid
4
5
  module Nist
5
6
  extend Pubid::PrefixesSupport
@@ -67,8 +68,13 @@ module Pubid
67
68
  # @param identifier [String] the identifier string to parse
68
69
  # @return [Identifier] the parsed identifier
69
70
  def self.parse(identifier)
71
+ unless identifier.is_a?(String)
72
+ raise Pubid::Errors::InvalidInputError,
73
+ Pubid::INPUT_NOT_A_STRING_MESSAGE
74
+ end
75
+
70
76
  if identifier.length > Pubid::MAX_INPUT_LENGTH
71
- raise ArgumentError, Pubid::INPUT_TOO_LONG_MESSAGE
77
+ raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
72
78
  end
73
79
 
74
80
  # Use the Parser class's preprocessing method
@@ -4,15 +4,17 @@ module Pubid
4
4
  module Oasis
5
5
  # Turns the parse tree into an `Identifiers::Standard`: stores the slug
6
6
  # verbatim in `original`, then performs a best-effort, ORDER-INDEPENDENT
7
- # decomposition into spec / version / stage / part / label.
7
+ # decomposition into number / version / stage / part / label.
8
8
  #
9
9
  # Decomposition splits `original` on "-" and classifies each *whole*
10
10
  # fragment (so a stage-like substring inside a spec name — e.g. "CSDL" — is
11
- # never mistaken for a stage). `spec` is the run of leading name fragments
12
- # before the first recognized fragment; `version`/`stage`/`part` are the
13
- # first fragment of each recognized kind; `label` is any name fragment
14
- # appearing after the first recognized one. Anything the classifier cannot
15
- # place stays only in `original`, so the printed form always round-trips.
11
+ # never mistaken for a stage). `number` is the specification name: the run
12
+ # of leading name fragments before the first recognized fragment, and the
13
+ # key relaton-index sorts and binary-searches on. `version`/`stage`/`part`
14
+ # are the first fragment of each recognized kind; `label` is any name
15
+ # fragment appearing after the first recognized one. Anything the
16
+ # classifier cannot place stays only in `original`, so the printed form
17
+ # always round-trips.
16
18
  class Builder
17
19
  # A fragment is a version when it is `v?N(.N)+` (e.g. "3.0", "v1.2.1",
18
20
  # "V1.0"). Bare integers are deliberately NOT versions (too ambiguous
@@ -36,16 +38,16 @@ module Pubid
36
38
 
37
39
  private
38
40
 
39
- # @return [Hash] best-effort { spec:, version:, stage:, part:, label: }.
40
- # Each fragment is classified once into a [fragment, kind] pair. `spec` is
41
- # the leading run of name fragments; `label` is any name fragment after
41
+ # @return [Hash] best-effort { number:, version:, stage:, part:, label: }.
42
+ # Each fragment is classified once into a [fragment, kind] pair. `number`
43
+ # is the leading run of name fragments; `label` is any name fragment after
42
44
  # the first recognized one; version/stage/part are the first of each kind.
43
45
  def decompose(original)
44
46
  pairs = original.split("-").map { |f| [f, classify(f)] }
45
47
  lead = pairs.take_while { |_, kind| kind.nil? }
46
48
  rest = pairs.drop(lead.length)
47
49
  {
48
- spec: names(lead),
50
+ number: names(lead),
49
51
  version: first_of(rest, :version),
50
52
  stage: first_of(rest, :stage),
51
53
  part: first_of(rest, :part),