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
@@ -4,14 +4,76 @@ module Pubid
4
4
  module CenCenelec
5
5
  class UrnGenerator < Pubid::UrnGenerator::Base
6
6
  def generate
7
- if identifier.is_a?(CenCenelec::SupplementIdentifier)
8
- generate_supplement_urn
7
+ urn_for(identifier)
8
+ end
9
+
10
+ protected
11
+
12
+ # The URN of +id+, walking the wrapper structure:
13
+ # EN 13250:2000/A1:2005 urn:cen:en:13250:2000:amd:1:2005
14
+ # EN 285:2015+A1:2021 urn:cen:en:285:2015:plus:amd:1:2021
15
+ # EN 60038 AMD1 FRAG2 urn:cen:en:60038:amd:1:frag:2
16
+ # CEN ISO/TS 21003-7:2019 urn:cen:cen:iso:ts:21003-7:2019
17
+ # ENV ISO 11079:1999 urn:cen:env:iso:11079:1999
18
+ # prEN ISO 1234:2020 urn:cen:en:iso:1234:2020:stage.proposal
19
+ # The "plus" marker keeps a consolidated identifier apart from the
20
+ # standalone amendment with the same number and year.
21
+ def urn_for(id)
22
+ case id
23
+ when nil
24
+ "urn:cen"
25
+ when Identifiers::Amendment, Identifiers::Corrigendum,
26
+ Identifiers::Fragment
27
+ [urn_for(id.base), *supplement_segments(id)].join(":")
28
+ when Identifiers::ConsolidatedIdentifier
29
+ base, *supplements = id.identifiers
30
+ [urn_for(base), *supplements.flat_map do |s|
31
+ ["plus", *supplement_segments(s)]
32
+ end].join(":")
9
33
  else
10
- generate_base_urn
34
+ if adoption?(id)
35
+ # The publisher, then the adopted document's MR string with its
36
+ # dot and underscore separators as colons, then a draft stage as
37
+ # the base URN writes it.
38
+ body = [id.mr_publisher, id.adopted&.to_mr_string]
39
+ .compact.reject(&:empty?).join(".").tr("._", "::")
40
+ ["urn:cen:#{body}", *stage_segment(id)].join(":")
41
+ else
42
+ self.class.new(id).generate_base_urn
43
+ end
11
44
  end
12
45
  end
13
46
 
14
- protected
47
+ # "EN ISO 8601:2019" or "ENV ISO 11079:1999": the document identity is
48
+ # on the adopted ISO/IEC identifier, not on this one.
49
+ def adoption?(id)
50
+ id.is_a?(Identifiers::AdoptedEuropeanNorm) ||
51
+ (id.is_a?(Identifiers::EuropeanPrestandard) && id.adopted)
52
+ end
53
+
54
+ # ["stage.proposal"] for a draft stage, [] for a published document.
55
+ def stage_segment(id)
56
+ code = id.typed_stage&.stage_code
57
+ return [] if code.nil? || code.to_s == "published"
58
+
59
+ ["stage.#{code}"]
60
+ end
61
+
62
+ # The segments that a supplement adds to its base: type, number (none
63
+ # for an unnumbered "/AC"), then the date.
64
+ def supplement_segments(id)
65
+ segments = case id
66
+ when Identifiers::Amendment
67
+ ["amd", id.number, id.year]
68
+ when Identifiers::Corrigendum
69
+ ["cor", id.number, id.supplement_date]
70
+ when Identifiers::Fragment
71
+ ["frag", id.number]
72
+ else
73
+ []
74
+ end
75
+ segments.map(&:to_s).reject(&:empty?)
76
+ end
15
77
 
16
78
  def generate_base_urn
17
79
  parts = ["urn", "cen"]
@@ -75,46 +137,6 @@ module Pubid
75
137
  parts.join(":")
76
138
  end
77
139
 
78
- def generate_supplement_urn
79
- parts = ["urn", "cen"]
80
-
81
- base_id = maybe(:base)
82
- if base_id
83
- base_gen = self.class.new(base_id)
84
- base_urn = base_gen.generate_base_urn
85
-
86
- base_part = base_urn.sub(/^urn:cen:/, "")
87
- base_parts = base_part.split(":")
88
-
89
- parts.concat(base_parts)
90
-
91
- typed_stage = maybe(:typed_stage)
92
- if typed_stage
93
- supp_type = typed_stage.type_code.to_s
94
- parts << supp_type if supp_type
95
- end
96
-
97
- amendment_number = maybe(:amendment_number)
98
- if amendment_number
99
- parts << amendment_number.to_s
100
- elsif identifier.number
101
- number = identifier.number.to_s
102
- parts << number
103
- end
104
-
105
- amendment_year = maybe(:amendment_year)
106
- if amendment_year
107
- parts << amendment_year.to_s
108
- elsif identifier.date&.is_a?(::Pubid::Components::Date) && identifier.date.present?
109
- parts << identifier.date.render(context: URN_CONTEXT)
110
- end
111
- else
112
- parts << "unknown"
113
- end
114
-
115
- parts.join(":")
116
- end
117
-
118
140
  def type_component
119
141
  typed_stage = maybe(:typed_stage)
120
142
  return nil unless typed_stage
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "pubid"
3
4
  module Pubid
4
5
  module CenCenelec
5
6
  extend Pubid::PrefixesSupport
@@ -9,14 +10,17 @@ module Pubid
9
10
  # are excluded — they route to their own SDOs, not to CEN.
10
11
  PREFIXES = %w[EN CEN CLC CWA HD ES CR ENV].freeze
11
12
 
13
+ # Publisher tokens that are also the document type. "CWA 14050" has no
14
+ # separate type token, so the builder picks the class from the publisher
15
+ # and the renderer prints the token once, not as "CWA/CWA 14050".
16
+ PUBLISHER_TYPES = %w[CWA HD ES CR ENV].freeze
17
+
12
18
  autoload :Builder, "#{__dir__}/cen_cenelec/builder"
13
19
  autoload :Identifier, "#{__dir__}/cen_cenelec/identifier"
14
20
  autoload :Identifiers, "#{__dir__}/cen_cenelec/identifiers"
15
21
  autoload :Parser, "#{__dir__}/cen_cenelec/parser"
16
22
  autoload :Renderer, "#{__dir__}/cen_cenelec/renderer"
17
23
  autoload :SingleIdentifier, "#{__dir__}/cen_cenelec/single_identifier"
18
- autoload :SupplementIdentifier,
19
- "#{__dir__}/cen_cenelec/supplement_identifier"
20
24
  autoload :UrnGenerator, "#{__dir__}/cen_cenelec/urn_generator"
21
25
  autoload :UrnParser, "#{__dir__}/cen_cenelec/urn_parser"
22
26
 
@@ -152,8 +156,13 @@ module Pubid
152
156
  ).freeze
153
157
 
154
158
  def self.parse(identifier)
159
+ unless identifier.is_a?(String)
160
+ raise Pubid::Errors::InvalidInputError,
161
+ Pubid::INPUT_NOT_A_STRING_MESSAGE
162
+ end
163
+
155
164
  if identifier.length > Pubid::MAX_INPUT_LENGTH
156
- raise ArgumentError, Pubid::INPUT_TOO_LONG_MESSAGE
165
+ raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
157
166
  end
158
167
 
159
168
  CenCenelec::Identifier.parse(identifier)
@@ -188,16 +197,25 @@ module Pubid
188
197
  abbr_str = abbr.to_s.upcase
189
198
  all_typed_stages.find { |s| s.abbr.any? { |a| a.to_s.upcase == abbr_str } }
190
199
  end
200
+
201
+ # Lookup: typed-stage code -> typed stage ("pren" -> the prEN entry).
202
+ # The serialized form of a draft stage is this code.
203
+ # @param code [String, Symbol] the typed-stage code to find
204
+ # @return [Pubid::Components::TypedStage, nil] the matching typed stage
205
+ def self.locate_stage_by_code(code)
206
+ all_typed_stages.find { |s| s.code.to_s == code.to_s }
207
+ end
191
208
  end
192
209
  end
193
210
 
194
211
  Pubid::Registry.register(:cen_cenelec, Pubid::CenCenelec)
195
212
  Pubid::Registry.register(:cen, Pubid::CenCenelec)
196
213
 
197
- # Per-flavor format registry: inherits global formats, overrides :human
198
- # Register on both root hierarchies: SingleIdentifier and Identifiers::Base
199
- cen_renderer = Pubid::CenCenelec::Renderer
200
- Pubid::CenCenelec::SingleIdentifier.format_registry = Pubid::FormatRegistry.new(parent: Pubid::Identifier.format_registry)
201
- Pubid::CenCenelec::SingleIdentifier.format_registry.register(:human, renderer: cen_renderer)
202
- Pubid::CenCenelec::Identifiers::Base.format_registry = Pubid::FormatRegistry.new(parent: Pubid::Identifier.format_registry)
203
- Pubid::CenCenelec::Identifiers::Base.format_registry.register(:human, renderer: cen_renderer)
214
+ # Per-flavor format registry: inherits global formats, overrides :human.
215
+ # Registered on the shared parent of every CEN/CENELEC class, so it covers
216
+ # SingleIdentifier, Identifiers::Base and the supplement wrappers, which
217
+ # descend from neither.
218
+ Pubid::CenCenelec::Identifier.format_registry =
219
+ Pubid::FormatRegistry.new(parent: Pubid::Identifier.format_registry)
220
+ Pubid::CenCenelec::Identifier.format_registry
221
+ .register(:human, renderer: Pubid::CenCenelec::Renderer)
@@ -30,6 +30,16 @@ module Pubid
30
30
  attributes.merge!(build_bundle(original_string))
31
31
  end
32
32
 
33
+ # `extract_attributes` builds one attribute hash for every CIE type,
34
+ # but the types do not share one attribute set — only Standard,
35
+ # DualPublished and Identical declare `s_prefix`, while Conference,
36
+ # Bundle, Proceedings and TutorialBundle do not. lutaml used to drop
37
+ # the surplus key without a word; the constructor now refuses it, so
38
+ # the builder has to stop offering it.
39
+ attributes = attributes.select do |key, _|
40
+ identifier_class.attributes.key?(key)
41
+ end
42
+
33
43
  # Construct identifier
34
44
  identifier_class.new(**attributes)
35
45
  end
@@ -15,8 +15,13 @@ module Pubid
15
15
  attribute :style, :string, default: -> { "current" }
16
16
 
17
17
  def self.parse(input)
18
+ unless input.is_a?(String)
19
+ raise Pubid::Errors::InvalidInputError,
20
+ Pubid::INPUT_NOT_A_STRING_MESSAGE
21
+ end
22
+
18
23
  if input.length > Pubid::MAX_INPUT_LENGTH
19
- raise ArgumentError, Pubid::INPUT_TOO_LONG_MESSAGE
24
+ raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
20
25
  end
21
26
 
22
27
  parsed = Parser.parse(input)
@@ -27,13 +27,15 @@ module Pubid
27
27
  # The printed form writes the publisher once: the first member keeps its
28
28
  # "CIE " prefix, the rest are bare. A base-less member is re-based onto
29
29
  # the bundle's shared base for rendering.
30
- def to_s
30
+ def to_s(**opts)
31
31
  return "" unless ids&.any?
32
32
 
33
- ids.each_with_index.map do |id, i|
33
+ result = ids.each_with_index.map do |id, i|
34
34
  member = id.base ? id : rebased(id)
35
35
  i.zero? ? member.to_s : member.to_s.delete_prefix("CIE ")
36
36
  end.join(",")
37
+
38
+ annotate_plain_render(result, **opts)
37
39
  end
38
40
 
39
41
  private
@@ -15,7 +15,7 @@ module Pubid
15
15
  # Techstreet opaque /slug variant (no default -> round-trips cleanly)
16
16
  attribute :variant, :string
17
17
 
18
- def to_s
18
+ def to_s(**opts)
19
19
  result = "CIE x#{number}"
20
20
 
21
21
  # Date with separator
@@ -29,7 +29,7 @@ module Pubid
29
29
  # Opaque /slug variant if present
30
30
  result += "/#{variant}" if variant
31
31
 
32
- result
32
+ annotate_plain_render(result, **opts)
33
33
  end
34
34
  end
35
35
  end
@@ -25,8 +25,8 @@ module Pubid
25
25
  year
26
26
  end
27
27
 
28
- def to_s
29
- "#{base}/Cor#{number}:#{year}"
28
+ def to_s(**opts)
29
+ annotate_plain_render("#{base}/Cor#{number}:#{year}", **opts)
30
30
  end
31
31
  end
32
32
  end
@@ -14,7 +14,7 @@ module Pubid
14
14
  attribute :s_prefix, :boolean, default: -> { false }
15
15
  attribute :iec_identifier, :string # IEC portion as string
16
16
 
17
- def to_s
17
+ def to_s(**opts)
18
18
  parts = ["CIE"]
19
19
 
20
20
  # S prefix
@@ -33,7 +33,7 @@ module Pubid
33
33
  # IEC portion with slash separator
34
34
  result += "/IEC #{iec_identifier}" if iec_identifier
35
35
 
36
- result
36
+ annotate_plain_render(result, **opts)
37
37
  end
38
38
  end
39
39
  end
@@ -18,7 +18,7 @@ module Pubid
18
18
  attribute :language, Components::Language
19
19
  attribute :iso_reference, :string # The ISO identifier in parentheses
20
20
 
21
- def to_s
21
+ def to_s(**opts)
22
22
  parts = ["CIE"]
23
23
 
24
24
  # S prefix
@@ -56,7 +56,7 @@ module Pubid
56
56
  # ISO reference in parentheses
57
57
  result += " (ISO #{iso_reference})" if iso_reference
58
58
 
59
- result
59
+ annotate_plain_render(result, **opts)
60
60
  end
61
61
  end
62
62
  end
@@ -15,7 +15,7 @@ module Pubid
15
15
  attribute :doc_type, :string # "TR" for Technical Report
16
16
  attribute :stage, :string # "DIS" for draft stage
17
17
 
18
- def to_s
18
+ def to_s(**opts)
19
19
  parts = ["CIE", copublisher]
20
20
 
21
21
  # Document type (TR)
@@ -44,7 +44,7 @@ module Pubid
44
44
  # Language (always parenthetical for joint published)
45
45
  result += language.to_s if language
46
46
 
47
- result
47
+ annotate_plain_render(result, **opts)
48
48
  end
49
49
  end
50
50
  end
@@ -26,12 +26,14 @@ module Pubid
26
26
  # Page range "1-5" (standalone form only).
27
27
  attribute :page, :string
28
28
 
29
- def to_s
30
- if conference
31
- "CIE x#{conference}-#{number}"
32
- else
33
- "CIE #{number} #{page}"
34
- end
29
+ def to_s(**opts)
30
+ result = if conference
31
+ "CIE x#{conference}-#{number}"
32
+ else
33
+ "CIE #{number} #{page}"
34
+ end
35
+
36
+ annotate_plain_render(result, **opts)
35
37
  end
36
38
  end
37
39
  end
@@ -17,7 +17,28 @@ module Pubid
17
17
  attribute :language, Components::Language
18
18
  attribute :stage, :string # DIS, DS
19
19
 
20
- def to_s
20
+ def to_s(**opts)
21
+ annotate_plain_render(render_plain, **opts)
22
+ end
23
+
24
+ # CIE Standard distinguishes S-prefixed standards (`CIE S 017`) from
25
+ # plain (`CIE 015`), D-prefixed (`CIE D001`), and DIS-staged. The
26
+ # generic mr_type looks at typed_stage, which CIE doesn't use, so
27
+ # without this override the `S`/`D`/stage marker would be lost in MR.
28
+ def mr_type
29
+ return stage.downcase if stage
30
+ return "d" if d_prefix
31
+ return "s" if s_prefix
32
+
33
+ nil
34
+ end
35
+
36
+ private
37
+
38
+ # The plain composition, extracted verbatim from `to_s`. It has two
39
+ # early returns (the slash_colon language form and the legacy
40
+ # bare slash-year form), and `to_s` needs a single exit to wrap.
41
+ def render_plain
21
42
  parts = ["CIE"]
22
43
 
23
44
  # Stage (DIS/DS) before code if present
@@ -66,18 +87,6 @@ module Pubid
66
87
 
67
88
  result
68
89
  end
69
-
70
- # CIE Standard distinguishes S-prefixed standards (`CIE S 017`) from
71
- # plain (`CIE 015`), D-prefixed (`CIE D001`), and DIS-staged. The
72
- # generic mr_type looks at typed_stage, which CIE doesn't use, so
73
- # without this override the `S`/`D`/stage marker would be lost in MR.
74
- def mr_type
75
- return stage.downcase if stage
76
- return "d" if d_prefix
77
- return "s" if s_prefix
78
-
79
- nil
80
- end
81
90
  end
82
91
  end
83
92
  end
@@ -22,7 +22,7 @@ module Pubid
22
22
  base&.year
23
23
  end
24
24
 
25
- def to_s
25
+ def to_s(**opts)
26
26
  b = base
27
27
  parts = ["CIE"]
28
28
  parts << b.stage if b.respond_to?(:stage) && b.stage
@@ -39,7 +39,7 @@ module Pubid
39
39
 
40
40
  # Supplements always use the current (colon) style.
41
41
  result += ":#{b.year}" if b.year
42
- result
42
+ annotate_plain_render(result, **opts)
43
43
  end
44
44
  end
45
45
  end
@@ -15,8 +15,8 @@ module Pubid
15
15
  # :string overrides the base ::Pubid::Identifier Components::Code type.
16
16
  attribute :number, :string
17
17
 
18
- def to_s
19
- "CIE Tutorials Bundle #{number}"
18
+ def to_s(**opts)
19
+ annotate_plain_render("CIE Tutorials Bundle #{number}", **opts)
20
20
  end
21
21
  end
22
22
  end
@@ -6,7 +6,7 @@ module Pubid
6
6
  module Cie
7
7
  # Parser for CIE identifiers
8
8
  # Handles dual-style system (legacy vs current)
9
- class Parser < Parslet::Parser
9
+ class Parser < ::Pubid::Parser::Grammar
10
10
  # Basic building blocks
11
11
  rule(:space) { str(" ") }
12
12
  rule(:space?) { space.maybe }
data/lib/pubid/cie.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "pubid"
3
4
  module Pubid
4
5
  module Cie
5
6
  extend Pubid::PrefixesSupport
@@ -23,8 +24,13 @@ module Pubid
23
24
  # Main entry point for CIE identifiers
24
25
  # Delegates to Identifier.parse
25
26
  def self.parse(input)
27
+ unless input.is_a?(String)
28
+ raise Pubid::Errors::InvalidInputError,
29
+ Pubid::INPUT_NOT_A_STRING_MESSAGE
30
+ end
31
+
26
32
  if input.length > Pubid::MAX_INPUT_LENGTH
27
- raise ArgumentError, Pubid::INPUT_TOO_LONG_MESSAGE
33
+ raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
28
34
  end
29
35
 
30
36
  Identifier.parse(input)
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pubid
4
+ module Conformance
5
+ # The single check core shared by the conformance runner and the rspec
6
+ # corpus gate. One corpus case in, its mismatch list out (empty ==
7
+ # passing). The list shape is load-bearing: RSpec's aggregate_failures
8
+ # does not raise on a failed expectation, so exception-based control
9
+ # flow would misread real failures as passes - and the runner needs to
10
+ # accumulate across cases rather than abort.
11
+ module Checks
12
+ class << self
13
+ # @param test_case [Pubid::Conformance::Corpus::Case]
14
+ # @param flavor_module [Module] the flavor owning the case
15
+ # @return [Array<String>] mismatch descriptions, empty when passing
16
+ def check_case(test_case, flavor_module)
17
+ identifier = flavor_module.parse(test_case.representations.human)
18
+ mismatchers(identifier, test_case, flavor_module)
19
+ .flat_map(&:call)
20
+ rescue StandardError => e
21
+ ["#{test_case.id} raised #{e.class}"]
22
+ end
23
+
24
+ # Each checker returns the case's mismatch list for its concern.
25
+ def mismatchers(identifier, test_case, flavor_module)
26
+ label = test_case.id.to_s
27
+ [
28
+ -> { canonical_mismatches(identifier, test_case, label) },
29
+ -> { representation_mismatches(identifier, test_case, label) },
30
+ -> { alias_mismatches(test_case, flavor_module, label) },
31
+ -> { deserialize_mismatches(identifier, flavor_module, label) },
32
+ ]
33
+ end
34
+
35
+ def canonical_mismatches(identifier, test_case, label)
36
+ actual = Conformance.plainify(identifier.to_hash)
37
+ actual == test_case.identifier ? [] : ["#{label} canonical hash"]
38
+ end
39
+
40
+ private
41
+
42
+ def representation_mismatches(identifier, test_case, label)
43
+ mismatches = []
44
+ reps = test_case.representations
45
+ mismatches << "#{label} human" if identifier.to_s != reps.human
46
+ if reps.urn && identifier.to_urn != reps.urn
47
+ mismatches << "#{label} urn"
48
+ end
49
+ mismatches
50
+ end
51
+
52
+ def alias_mismatches(test_case, flavor_module, label)
53
+ human = test_case.representations.human
54
+ test_case.non_normalized_aliases.filter_map do |entry|
55
+ aliased = flavor_module.parse(entry.spelling)
56
+ "#{label} alias #{entry.spelling}" if aliased.to_s != human
57
+ rescue StandardError
58
+ "#{label} alias #{entry.spelling} raised"
59
+ end
60
+ end
61
+
62
+ def deserialize_mismatches(identifier, flavor_module, label)
63
+ hash = identifier.to_hash
64
+ ok = flavor_module::Identifier.from_hash(hash).to_hash == hash
65
+ ok ? [] : ["#{label} deserialize"]
66
+ rescue StandardError
67
+ ["#{label} deserialize raised"]
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pubid
4
+ module Conformance
5
+ module Corpus
6
+ # Every proper rendering of the case's identifier.
7
+ class Representations < Lutaml::Model::Serializable
8
+ attribute :human, :string
9
+ attribute :urn, :string
10
+ end
11
+
12
+ # One old spelling that must normalize to the human form.
13
+ class Spelling < Lutaml::Model::Serializable
14
+ attribute :spelling, :string
15
+ attribute :style, :string
16
+ end
17
+
18
+ # Neutral rejection expectation - never an implementation class.
19
+ class Error < Lutaml::Model::Serializable
20
+ attribute :code, :string
21
+ end
22
+
23
+ class Expectation < Lutaml::Model::Serializable
24
+ attribute :error, Error
25
+ end
26
+
27
+ # One corpus case (schema/test.schema.yaml is the wire contract).
28
+ # `identifier` stays an opaque hash on purpose: it is the flavor's
29
+ # own canonical to_hash carried as DATA to compare against, not a
30
+ # structure the reader interprets.
31
+ class Case < Lutaml::Model::Serializable
32
+ attribute :id, :string
33
+ attribute :style, :string
34
+ attribute :identifier, :hash
35
+ attribute :representations, Representations
36
+ attribute :non_normalized_aliases, Spelling, collection: true,
37
+ default: -> { [] }
38
+ attribute :roundtrip, :boolean
39
+ attribute :expect, Expectation
40
+ attribute :input, :string
41
+ attribute :notes, :string
42
+ attribute :review, :string
43
+
44
+ def error_case? = !expect.nil? && !expect.error.nil?
45
+
46
+ # Reference-bug debt: nothing to gate on.
47
+ def quarantined? = identifier.nil? && !error_case?
48
+
49
+ def review? = !review.to_s.empty?
50
+
51
+ def roundtrip_failure_expected? = roundtrip == false
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pubid
4
+ module Conformance
5
+ # Typed reading layer for pubid-testsuite payloads. The YAML schema
6
+ # (schema/test.schema.yaml in pubid-testsuite) is the wire contract;
7
+ # these models are the shared typed reader for every implementation -
8
+ # the Ruby gem now, pubid-ts mirroring the same shape later. No
9
+ # consumer pokes payload hashes directly.
10
+ module Corpus
11
+ autoload :Case, "pubid/conformance/corpus/case"
12
+
13
+ class << self
14
+ # Cases from one payload file (underscore payloads excluded).
15
+ def load_file(path)
16
+ Array(YAML.safe_load_file(path)).map { |doc| Case.from_hash(doc) }
17
+ end
18
+
19
+ def case_files(flavor, corpus_dir)
20
+ Dir[File.join(corpus_dir, flavor, "*.yaml")]
21
+ .reject { |p| File.basename(p).start_with?("_") }.sort
22
+ end
23
+
24
+ def cases(flavor, corpus_dir)
25
+ case_files(flavor, corpus_dir).flat_map { |p| load_file(p) }
26
+ end
27
+
28
+ # The must-fail payload (fail fixtures decoded by the exporter).
29
+ def negative_file(flavor, corpus_dir)
30
+ path = File.join(corpus_dir, flavor, "_negative.yaml")
31
+ File.exist?(path) ? [path] : []
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end