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
@@ -6,8 +6,8 @@ module Pubid
6
6
  # Errata identifier (Err.)
7
7
  # Pattern: "ITU-T G.9701 (2014) Err. 1 (07/2016)"
8
8
  class Errata < Supplement
9
- def to_s
10
- render_supplement("Err.")
9
+ def to_s(**opts)
10
+ annotate_plain_render(render_supplement("Err."), **opts)
11
11
  end
12
12
  end
13
13
  end
@@ -115,8 +115,8 @@ module Pubid
115
115
  result
116
116
  end
117
117
 
118
- def to_s
119
- render_supplement("Suppl.")
118
+ def to_s(**opts)
119
+ annotate_plain_render(render_supplement("Suppl."), **opts)
120
120
  end
121
121
 
122
122
  # Shared by Amendment / Corrigendum / Errata.
@@ -11,6 +11,7 @@ module Pubid
11
11
  autoload :AppendixOfRecommendation,
12
12
  "#{__dir__}/identifiers/appendix_of_recommendation"
13
13
  autoload :CombinedIdentifier, "#{__dir__}/identifiers/combined_identifier"
14
+ autoload :Contribution, "#{__dir__}/identifiers/contribution"
14
15
  autoload :Corrigendum, "#{__dir__}/identifiers/corrigendum"
15
16
  autoload :Errata, "#{__dir__}/identifiers/errata"
16
17
  autoload :Handbook, "#{__dir__}/identifiers/handbook"
@@ -4,7 +4,7 @@ require "parslet"
4
4
 
5
5
  module Pubid
6
6
  module Itu
7
- class Parser < Parslet::Parser
7
+ class Parser < ::Pubid::Parser::Grammar
8
8
  # Basic elements
9
9
  rule(:digit) { match["0-9"] }
10
10
  rule(:digits) { digit.repeat(1) }
@@ -323,6 +323,22 @@ module Pubid
323
323
  (digits | letter.repeat(1)).as(:number) >> parts
324
324
  end
325
325
 
326
+ # ITU Contribution (Temporary Document) — "ITU-R SG17-C1000" (pubid#340).
327
+ # The "-C" marker before the number distinguishes it from every
328
+ # neighbouring shape: a series-code document's post-dash number starts
329
+ # with digits or is all letters (never "C"+digits), and with_series
330
+ # requires a dot after the series.
331
+ rule(:contribution) do
332
+ itu_prefix >>
333
+ sector >>
334
+ space >>
335
+ series >>
336
+ str("-C").as(:contribution_marker) >>
337
+ number >>
338
+ parts >>
339
+ language.maybe
340
+ end
341
+
326
342
  rule(:base_series_code) do
327
343
  itu_prefix >>
328
344
  sector >>
@@ -656,9 +672,10 @@ module Pubid
656
672
  numeric_question |
657
673
  letter_question |
658
674
  with_series |
675
+ contribution |
659
676
  # Unreachable earlier: special_publication needs the literal "OB",
660
- # handbook/numeric_question need leading digits, and letter_question
661
- # needs series >> dot. Before without_series, whose code needs digits.
677
+ # handbook/numeric_question need leading digits, letter_question
678
+ # needs series >> dot, and contribution needs "-C" after the series.
662
679
  series_code_identifier |
663
680
  without_series
664
681
  end
data/lib/pubid/itu.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "pubid"
3
4
  require "lutaml/model"
4
5
 
5
6
  module Pubid
@@ -69,7 +69,7 @@ module Pubid
69
69
  when :publisher
70
70
  Jcgm::Components::Publisher.new(publisher: value.to_s)
71
71
  when :number
72
- Pubid::Components::Code.new(value: value.to_s)
72
+ value.to_s
73
73
  when :date
74
74
  date_str = value.to_s
75
75
  if date_str.include?("-")
@@ -3,6 +3,16 @@
3
3
  module Pubid
4
4
  module Jcgm
5
5
  class Identifier < ::Pubid::Identifier
6
+ # `number`/`part`/`subpart` are declared here rather than inherited as a
7
+ # Components::Code: JCGM stores a bare string in `number` and uses neither
8
+ # of the other two. Declaring on this class is safe because its body lives
9
+ # in this one file and is never reopened, so every subclass body opens
10
+ # after it has run (lutaml deep-dups the parent attribute table at
11
+ # class-definition time).
12
+ attribute :number, :string
13
+ attribute :part, :string
14
+ attribute :subpart, :string
15
+
6
16
  def self.parse(string)
7
17
  Pubid::Jcgm.parse(string)
8
18
  end
@@ -39,7 +39,7 @@ module Pubid
39
39
  end
40
40
 
41
41
  def ordinal
42
- self.class.ordinal(number&.value)
42
+ self.class.ordinal(number)
43
43
  end
44
44
  end
45
45
  end
@@ -4,7 +4,7 @@ require "parslet"
4
4
 
5
5
  module Pubid
6
6
  module Jcgm
7
- class Parser < Parslet::Parser
7
+ class Parser < ::Pubid::Parser::Grammar
8
8
  include ::Pubid::Parser::CommonParseRules
9
9
  include ::Pubid::Parser::CommonParseMethods
10
10
 
@@ -53,10 +53,17 @@ module Pubid
53
53
  # wants the ordinal suffix), so ordered choice is unambiguous. Emits the
54
54
  # same tokens as a guide plus type_with_stage "Meeting", so the generic
55
55
  # builder path resolves it to Identifiers::Meeting (like "Amd").
56
+ #
57
+ # The trailing " (YYYY)" group is optional, so the partial reference
58
+ # "JCGM 11st Meeting" parses with `date` left nil. JCGM numbers its
59
+ # meetings in one sequence, so the ordinal alone names the event and the
60
+ # year is redundant detail. The whole group is `.maybe` (not the year
61
+ # inside it), so a dangling "(" or a truncated year still fails —
62
+ # the same shape as `date_portion.maybe` in `rule(:base)`.
56
63
  rule(:meeting_identifier) do
57
64
  publisher >> space >> digits.as(:number) >> ordinal_suffix >>
58
- space >> str("Meeting").as(:type_with_stage) >> space >>
59
- str("(") >> year_digits.as(:date) >> str(")")
65
+ space >> str("Meeting").as(:type_with_stage) >>
66
+ (space >> str("(") >> year_digits.as(:date) >> str(")")).maybe
60
67
  end
61
68
 
62
69
  rule(:ordinal_suffix) do
@@ -37,8 +37,15 @@ module Pubid
37
37
  id.date ? ":#{id.date}" : ""
38
38
  end
39
39
 
40
+ # Two surface forms:
41
+ # "JCGM 17th Meeting (2012)" — date present (published records)
42
+ # "JCGM 17th Meeting" — date absent (partial reference)
43
+ # The ordinal alone names the meeting, so the dateless form is a valid
44
+ # identifier and must stay re-parseable (the parser makes the " (YYYY)"
45
+ # group optional to match).
40
46
  def render_meeting(id)
41
- "JCGM #{id.ordinal} Meeting (#{id.date.year})"
47
+ base = "JCGM #{id.ordinal} Meeting"
48
+ id.date ? "#{base} (#{id.date.year})" : base
42
49
  end
43
50
 
44
51
  def render_single(id)
@@ -52,7 +59,7 @@ module Pubid
52
59
  def render_amendment(id, context)
53
60
  result = id.base.to_s if id.base
54
61
  result += "/Amd"
55
- result += " #{id.number.render(context:)}" if id.number
62
+ result += " #{id.number}" if id.number
56
63
  result += ":#{id.date}" if id.date
57
64
  result
58
65
  end
@@ -60,7 +67,7 @@ module Pubid
60
67
  def render_gum_guide(id, context)
61
68
  parts = []
62
69
  parts << id.publisher.publisher if id.publisher
63
- parts << "GUM-#{id.number.render(context:)}" if id.number
70
+ parts << "GUM-#{id.number}" if id.number
64
71
 
65
72
  result = parts.join(" ")
66
73
  result += ":#{id.date}" if id.date
@@ -7,7 +7,6 @@ module Pubid
7
7
  default: -> { self.class.default_publisher }
8
8
  attribute :typed_stage, Pubid::Components::TypedStage,
9
9
  default: -> { self.class.published_typed_stage }
10
- attribute :number, Pubid::Components::Code
11
10
  attribute :date, Pubid::Components::Date
12
11
  attribute :languages, Pubid::Components::Language, collection: true
13
12
  attribute :stage, Pubid::Components::Stage
@@ -18,7 +17,7 @@ module Pubid
18
17
  # (fully determined by the class, i.e. `_type`) are intentionally NOT
19
18
  # mapped — they are reconstructed from the class on load via the
20
19
  # attribute defaults above. Components collapse to bare scalars:
21
- # number -> "100" (not {value: "100"}); date -> year/month/day scalars.
20
+ # number is already a bare :string; date -> year/month/day scalars.
22
21
  # Mirrors ISO (lib/pubid/iso/identifier.rb) and OIML.
23
22
  key_value do
24
23
  map "_type", to: :_type
@@ -37,15 +36,29 @@ module Pubid
37
36
  # The class's published typed_stage (canonical surface form). Each JCGM
38
37
  # class registers exactly one, all :published; `_type` fixes the class,
39
38
  # so an omitted typed_stage reconstructs deterministically on from_hash.
39
+ #
40
+ # `original_abbr` is deliberately LEFT NIL. It records the spelling the
41
+ # input used and is not serialized, so it cannot survive a round trip —
42
+ # and this method is one of the two paths that fill `typed_stage`. The
43
+ # other is the parse path (`Builder#locate_typed_stage` ->
44
+ # `Jcgm.locate_stage`), which returns the registry entry untouched.
45
+ # Setting it here made the two disagree, so `from_hash(to_hash) != parse`
46
+ # for every type the grammar tags (Meeting, Corrigendum, Amendment) —
47
+ # and because `#matches?` is `exclude(...) == other.exclude(...)`, a
48
+ # relaton index lookup then silently matched nothing. Nothing in JCGM
49
+ # reads the field: the renderer hardcodes its surface words, the URN
50
+ # generator reads only `type_code`, and `TypedStage#abbreviation` chooses
51
+ # among long_abbr/short_abbr/abbr.first. Recording the *matched* token
52
+ # instead would not work either — "/Cor 1" would store "Cor" where
53
+ # from_hash rebuilds "Corrigendum".
40
54
  def self.published_typed_stage
41
55
  return nil unless const_defined?(:TYPED_STAGES)
42
56
 
43
57
  ts = self::TYPED_STAGES.find { |t| t.stage_code.to_s == "published" }
44
- return nil unless ts
45
-
46
- ts = ts.dup
47
- ts.original_abbr = ts.canonical_abbreviation
48
- ts
58
+ # dup: locate_stage hands out the shared object from the frozen
59
+ # TYPED_STAGES array (the array is frozen, its elements are not), so a
60
+ # future in-place mutation would otherwise leak globally.
61
+ ts&.dup
49
62
  end
50
63
 
51
64
  # type and stage are derived from typed_stage, never stored — so the
@@ -58,13 +71,9 @@ module Pubid
58
71
  typed_stage&.to_stage
59
72
  end
60
73
 
61
- # --- number (Code) flattened to a bare string ---
62
- def number_to_kv(model, doc) = emit_kv(doc, "number", model.number&.value)
63
- def number_from_kv(model, value) = model.number = build_code(value)
64
-
65
- def build_code(value)
66
- Pubid::Components::Code.new(value: value.to_s)
67
- end
74
+ # --- number ---
75
+ def number_to_kv(model, doc) = emit_kv(doc, "number", model.number)
76
+ def number_from_kv(model, value) = model.number = value.to_s
68
77
 
69
78
  # --- date flattened to top-level year/month/day scalars ---
70
79
  def year_to_kv(model, doc) = emit_kv(doc, "year", model.date&.year)
@@ -90,7 +99,7 @@ module Pubid
90
99
 
91
100
  def number_portion
92
101
  parts = []
93
- parts << number.value if number
102
+ parts << number if number
94
103
  parts << ":#{date.year}" if date
95
104
  parts.join
96
105
  end
@@ -18,7 +18,7 @@ module Pubid
18
18
  # urn:jcgm:meeting:<number>:<year>, e.g. urn:jcgm:meeting:17:2012
19
19
  def generate_meeting_urn
20
20
  parts = ["urn", "jcgm", "meeting"]
21
- parts << identifier.number.value if identifier.number
21
+ parts << identifier.number if identifier.number
22
22
  parts << identifier.date.year if identifier.date
23
23
  parts.join(":")
24
24
  end
@@ -4,31 +4,173 @@ module Pubid
4
4
  module Jcgm
5
5
  # Parses JCGM URNs back into identifiers.
6
6
  #
7
- # UrnGenerator emits `urn:jcgm:{number}:{year}` for guides and
8
- # `urn:jcgm:meeting:{number}:{year}` for committee/meeting records.
7
+ # Identifiers are rebuilt **directly from the URN segments** rather than
8
+ # rendered to a human-readable string and re-parsed (the BIPM UrnParser
9
+ # pattern). Round-tripping through the grammar flattened the segments into
10
+ # one string and silently lost everything the string form could not carry:
11
+ # a GUM guide (`urn:jcgm:gum.6:2020`) raised, and the language and
12
+ # supplement segments were dropped, so a corrigendum read back as the
13
+ # standard it amends — a different document, with no error.
9
14
  #
10
- # Examples:
11
- # - urn:jcgm:200:2012 → JCGM 200:2012
12
- # - urn:jcgm:100:2008 → JCGM 100:2008
13
- # - urn:jcgm:meeting:17:2012 → JCGM 17th Meeting (2012)
15
+ # Shapes emitted by UrnGenerator, and read back here:
16
+ # urn:jcgm:200:2008 → JCGM 200:2008
17
+ # urn:jcgm:100:2008:en → JCGM 100:2008(E)
18
+ # urn:jcgm:200:2012:en,fr → JCGM 200:2012(E/F)
19
+ # urn:jcgm:GUM → JCGM GUM
20
+ # urn:jcgm:gum.6:2020 → JCGM GUM-6:2020
21
+ # urn:jcgm:200:2008:corrigendum → JCGM 200:2008 Corrigendum
22
+ # urn:jcgm:101:2008:corrigendum:1:2009 → JCGM 101:2008/Cor 1:2009
23
+ # urn:jcgm:100:2008:amendment:1:2023 → JCGM 100:2008/Amd 1:2023
24
+ # urn:jcgm:meeting:17:2012 → JCGM 17th Meeting (2012)
25
+ # urn:jcgm:meeting:11 → JCGM 11st Meeting
26
+ #
27
+ # KNOWN GAP, on the generator side and unchanged here: a full date is
28
+ # rendered as its year alone (`JCGM GUM-1:2022-11-28` →
29
+ # `urn:jcgm:gum.1:2022`), so a URN cannot restore the month and day.
30
+ # Widening it would change already-published URNs.
14
31
  class UrnParser < Pubid::UrnParser::Base
32
+ MEETING_MARKER = "meeting"
33
+
34
+ # The GUM-guide number is written "gum.<n>" by UrnGenerator.
35
+ GUM_PREFIX = "gum."
36
+
37
+ # The supplement marker UrnGenerator writes is the typed stage's
38
+ # `type_code`; the abbreviation is what `Jcgm.locate_stage` matches on.
39
+ SUPPLEMENT_ABBRS = {
40
+ "corrigendum" => "Cor",
41
+ "amendment" => "Amd",
42
+ }.freeze
43
+
44
+ MEETING_ABBR = "Meeting"
45
+
46
+ # Mirrors `year_digits` in the shared parser rules.
47
+ YEAR_PATTERN = /\A(?:19|20)\d{2}\z/
48
+
49
+ # A language segment is one or more comma-joined ISO codes.
50
+ LANGUAGES_PATTERN = /\A[a-z]{2}(?:,[a-z]{2})*\z/
51
+
52
+ # Language codes back to the single-letter form the identifier prints;
53
+ # the builder maps the other way when parsing "(E)" / "(E/F)".
54
+ LANGUAGE_LETTERS = Pubid::Builder::Base::LANG_CHAR_MAP.invert.freeze
55
+
15
56
  def parse_urn(urn)
16
57
  parts = split_parts(strip_namespace(urn))
17
- return parse_meeting_urn(parts) if parts.first == "meeting"
58
+ return parse_meeting_urn(parts) if parts.first == MEETING_MARKER
18
59
 
19
- number, year = parts
20
- text = "JCGM #{number}"
21
- text += ":#{year}" if year
22
- flavor_parse(text)
60
+ parse_document_urn(parts)
23
61
  end
24
62
 
25
63
  private
26
64
 
27
- # urn:jcgm:meeting:<number>:<year> -> "JCGM <ordinal> Meeting (<year>)"
65
+ # urn:jcgm:meeting:<number>[:<year>] -> Identifiers::Meeting
66
+ #
67
+ # The reconstruction must agree with a parsed identifier attribute for
68
+ # attribute, so `typed_stage` comes from the same registry lookup
69
+ # `Builder#locate_typed_stage` uses. (The attribute default now agrees
70
+ # too — it no longer sets `original_abbr` — so this mirrors the builder
71
+ # for clarity rather than out of necessity.)
28
72
  def parse_meeting_urn(parts)
29
73
  _, number, year = parts
30
- ordinal = Identifiers::Meeting.ordinal(number)
31
- flavor_parse("JCGM #{ordinal} Meeting (#{year})")
74
+
75
+ Identifiers::Meeting.new(
76
+ number: meeting_number(number),
77
+ date: urn_date(year),
78
+ typed_stage: Jcgm.locate_stage(MEETING_ABBR),
79
+ )
80
+ end
81
+
82
+ # A guide, a GUM guide, or a supplement wrapping one of those. The
83
+ # supplement marker splits the segments: everything before it describes
84
+ # the base document, everything after is the supplement's own number and
85
+ # date.
86
+ def parse_document_urn(parts)
87
+ marker_at = parts.index { |segment| SUPPLEMENT_ABBRS.key?(segment) }
88
+ return build_document(parts) unless marker_at
89
+
90
+ build_supplement(
91
+ parts[marker_at],
92
+ build_document(parts[0...marker_at]),
93
+ parts[(marker_at + 1)..] || [],
94
+ )
95
+ end
96
+
97
+ # The class comes from the same registry the builder uses, so a URN and a
98
+ # parsed reference resolve to one class and one typed_stage.
99
+ def build_supplement(marker, base, tail)
100
+ typed_stage = Jcgm.locate_stage(SUPPLEMENT_ABBRS.fetch(marker))
101
+ number, year = tail
102
+
103
+ Jcgm.locate_type(typed_stage.type_code).new(
104
+ base: base,
105
+ number: (number.nil? || number.empty? ? nil : number.to_s),
106
+ date: urn_date(year),
107
+ typed_stage: typed_stage,
108
+ )
109
+ end
110
+
111
+ # <number>[:<year>][:<languages>] — the number segment carries the
112
+ # "gum." prefix for a GUM guide, which is what selects the class.
113
+ #
114
+ # Guide and GumGuide deliberately take their `typed_stage` from the
115
+ # attribute default: the grammar emits no type token for them, so
116
+ # `Builder#build` fills it from `published_typed_stage` too, and both
117
+ # paths agree.
118
+ def build_document(parts)
119
+ number, *rest = parts
120
+ require_number!(number)
121
+ gum = number.start_with?(GUM_PREFIX)
122
+
123
+ (gum ? Identifiers::GumGuide : Identifiers::Guide).new(
124
+ number: (gum ? number.delete_prefix(GUM_PREFIX) : number).to_s,
125
+ date: urn_date(segment_matching(rest, YEAR_PATTERN)),
126
+ languages: urn_languages(segment_matching(rest, LANGUAGES_PATTERN)),
127
+ )
128
+ end
129
+
130
+ def require_number!(segment)
131
+ return unless segment.nil? || segment.empty?
132
+
133
+ raise Pubid::UrnParser::Errors::ParseError,
134
+ "JCGM URN has no document number"
135
+ end
136
+
137
+ def segment_matching(segments, pattern)
138
+ segments.find { |segment| segment.match?(pattern) }
139
+ end
140
+
141
+ # The ordinal, normalized the way `Identifiers::Meeting.ordinal` does it
142
+ # (`number.to_i`), so "011" reads back as "11" and a missing or
143
+ # non-numeric segment as "0" — the behaviour of the previous
144
+ # render-and-re-parse implementation.
145
+ def meeting_number(segment)
146
+ segment.to_i.to_s
147
+ end
148
+
149
+ # nil for an absent year; a Date for a well-formed one. The grammar
150
+ # accepts only a 19xx/20xx year, so anything else is a malformed URN and
151
+ # must still be rejected now that no re-parse validates it.
152
+ def urn_date(segment)
153
+ return nil if segment.nil? || segment.empty?
154
+
155
+ unless segment.match?(YEAR_PATTERN)
156
+ raise Pubid::UrnParser::Errors::ParseError,
157
+ "Invalid year in JCGM URN: #{segment.inspect}"
158
+ end
159
+
160
+ Pubid::Components::Date.new(year: segment)
161
+ end
162
+
163
+ # "en" -> (E), "en,fr" -> (E/F). `original_code` is what the renderer
164
+ # prints, so it is restored from the code rather than left nil.
165
+ def urn_languages(segment)
166
+ return nil if segment.nil? || segment.empty?
167
+
168
+ segment.split(",").map do |code|
169
+ Pubid::Components::Language.new(
170
+ code: code,
171
+ original_code: LANGUAGE_LETTERS.fetch(code, code),
172
+ )
173
+ end
32
174
  end
33
175
  end
34
176
  end
data/lib/pubid/jcgm.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "pubid"
3
4
  module Pubid
4
5
  module Jcgm
5
6
  extend Pubid::PrefixesSupport
@@ -22,6 +23,15 @@ module Pubid
22
23
  # @param identifier [String] the identifier string to parse
23
24
  # @return [Identifier] the parsed identifier
24
25
  def self.parse(identifier)
26
+ unless identifier.is_a?(String)
27
+ raise Pubid::Errors::InvalidInputError,
28
+ Pubid::INPUT_NOT_A_STRING_MESSAGE
29
+ end
30
+
31
+ if identifier.length > Pubid::MAX_INPUT_LENGTH
32
+ raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
33
+ end
34
+
25
35
  parser = Parser.new
26
36
  builder = Builder.new
27
37
 
@@ -175,12 +175,19 @@ module Pubid
175
175
  # Parse a JIS identifier string into an identifier object
176
176
  # @param identifier [String] The JIS identifier string to parse
177
177
  # @return [Identifier] The appropriate identifier object
178
- # @raise [RuntimeError] If parsing fails
178
+ # @raise [Pubid::Errors::ParseError] If parsing fails
179
179
  def self.parse(identifier)
180
+ unless identifier.is_a?(String)
181
+ raise Pubid::Errors::InvalidInputError,
182
+ Pubid::INPUT_NOT_A_STRING_MESSAGE
183
+ end
184
+
185
+ if identifier.length > Pubid::MAX_INPUT_LENGTH
186
+ raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
187
+ end
188
+
180
189
  parsed = Parser.parse(identifier)
181
190
  Builder.build(parsed)
182
- rescue Parslet::ParseFailed => e
183
- raise "Failed to parse JIS identifier '#{identifier}': #{e.message}"
184
191
  end
185
192
  end
186
193
  end
@@ -4,7 +4,7 @@ require "parslet"
4
4
 
5
5
  module Pubid
6
6
  module Jis
7
- class Parser < Parslet::Parser
7
+ class Parser < ::Pubid::Parser::Grammar
8
8
  # Japanese character normalization
9
9
  rule(:jp_dash) { str("ー") } # Full-width dash
10
10
  rule(:jp_space) { str(" ") } # Full-width space
data/lib/pubid/jis.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "pubid"
3
4
  module Pubid
4
5
  module Jis
5
6
  extend Pubid::PrefixesSupport
@@ -253,7 +253,7 @@ module Pubid
253
253
  elsif decimal_num
254
254
  decimal_base = decimal_num[:decimal_base].to_s
255
255
  decimal_suffix = decimal_num[:decimal_suffix].to_s
256
- identifier.number = Components::Code.new(value: "#{first_num.value}-#{decimal_base}.#{decimal_suffix}")
256
+ identifier.number = "#{first_num.value}-#{decimal_base}.#{decimal_suffix}"
257
257
  # NEW: Handle letter number pattern (e.g., 1-1A, 1-3B for NCSTAR identifiers)
258
258
  # letter_num is {:letter_base => "1", :letter_suffix => "A"}
259
259
  # Also handles IR series "R" suffix: "79-1786R" -> "79-1786r1"
@@ -268,10 +268,10 @@ module Pubid
268
268
  # Series-specific handler took ownership (e.g., IR "R" → r1)
269
269
  elsif identifier.part
270
270
  # SpecialPublication pattern: letter_suffix is separate Part component
271
- identifier.number = Components::Code.new(value: "#{first_num.value}-#{letter_base}")
271
+ identifier.number = "#{first_num.value}-#{letter_base}"
272
272
  else
273
273
  # NCSTAR pattern: letter_suffix is part of the number
274
- identifier.number = Components::Code.new(value: "#{first_num.value}-#{letter_base}#{letter_suffix}")
274
+ identifier.number = "#{first_num.value}-#{letter_base}#{letter_suffix}"
275
275
  end
276
276
  elsif second_num
277
277
  # Check for special patterns first
@@ -285,7 +285,7 @@ module Pubid
285
285
  # Create Edition component
286
286
  edition_obj = Components::Edition.new(type: "r", id: edition_id)
287
287
 
288
- identifier.number = Components::Code.new(value: "#{first_num.value}-#{number_part}")
288
+ identifier.number = "#{first_num.value}-#{number_part}"
289
289
  identifier.edition = edition_obj
290
290
  # CS Emergency pattern: e104-43 -> number=104, edition_year=1943
291
291
  # Logic: e104-43 means "emergency 104 from 1943" (43 = 1943)
@@ -300,7 +300,7 @@ module Pubid
300
300
  # Create Edition component
301
301
  edition_obj = Components::Edition.new(type: "e", id: edition_year)
302
302
 
303
- identifier.number = Components::Code.new(value: number_part)
303
+ identifier.number = number_part
304
304
  identifier.edition = edition_obj
305
305
  elsif first_num.value.to_s.match?(/^(\d+)e(\d+)$/) &&
306
306
  second_num.value.to_s.match?(/^\d{2,4}$/)
@@ -314,7 +314,7 @@ module Pubid
314
314
  # Expand 2-digit year to 4-digit (50 -> 1950)
315
315
  year_part = "19#{year_part}" if year_part.length == 2
316
316
 
317
- identifier.number = Components::Code.new(value: number_part)
317
+ identifier.number = number_part
318
318
 
319
319
  # For edition+year patterns, handling depends on identifier type:
320
320
  # - CIRC: edition number + year as additional_text, rendered with dot ("11e2-1915" -> "11e2.1915")
@@ -329,7 +329,7 @@ module Pubid
329
329
  number_part = first_num.value.to_s.match(/^(\d+)supp?$/)[1]
330
330
  year_part = second_num.value.to_s
331
331
 
332
- identifier.number = Components::Code.new(value: number_part)
332
+ identifier.number = number_part
333
333
  supp[:value] = year_part
334
334
  supp[:present] = true
335
335
  elsif second_num.value.to_s.match?(/^(\d+)supp?$/)
@@ -337,14 +337,14 @@ module Pubid
337
337
  # second number. Strip it and isolate as supplement="" (single-p).
338
338
  second_part = second_num.value.to_s.match(/^(\d+)supp?$/)[1]
339
339
  compound = "#{first_num.value}-#{second_part}"
340
- identifier.number = Components::Code.new(value: compound)
340
+ identifier.number = compound
341
341
  supp[:value] = ""
342
342
  supp[:present] = true
343
343
  elsif identifier.is_a?(Identifiers::TechnicalNote) &&
344
344
  second_num.value.to_s.match?(/^(19|20)\d{2}$/)
345
345
  # SPECIAL CASE FOR TN: second_num is edition year
346
346
  # Following "date IS edition" rule: -1993 becomes Edition(type: "e", id: "1993")
347
- identifier.number = first_num
347
+ identifier.number = first_num.value.to_s
348
348
  edition_obj = Components::Edition.new(type: "e",
349
349
  id: second_num.value.to_s)
350
350
  identifier.edition = edition_obj
@@ -354,16 +354,16 @@ module Pubid
354
354
  # not folded into the compound number.
355
355
  identifier.part = Components::Part.new(type: "pt",
356
356
  value: part_num)
357
- identifier.number = Components::Code.new(value: "#{first_num.value}-#{second_num.value}")
357
+ identifier.number = "#{first_num.value}-#{second_num.value}"
358
358
  else
359
359
  # For GCR and others, include part number in compound number
360
360
  compound_value = "#{first_num.value}-#{second_num.value}"
361
361
  compound_value += "-#{part_num}" if part_num
362
- identifier.number = Components::Code.new(value: compound_value)
362
+ identifier.number = compound_value
363
363
  end
364
364
  else
365
365
  # No second_num, use first_num directly
366
- identifier.number = first_num
366
+ identifier.number = first_num.value.to_s
367
367
  end
368
368
  end
369
369
 
@@ -16,10 +16,10 @@ module Pubid
16
16
  attribute :id, :string # i, f, 1-9
17
17
  attribute :type, :string # pd, wd, prd
18
18
 
19
- # Load stages from V1 YAML config
19
+ # Load stages from data/nist/stages.yaml
20
20
  STAGES = YAML.load_file(
21
21
  File.join(File.dirname(__FILE__),
22
- "../../../../archived-gems/pubid-nist/stages.yaml"),
22
+ "../../../../data/nist/stages.yaml"),
23
23
  ).freeze
24
24
 
25
25
  # Render stage in specified format