pubid 2.0.0.pre.alpha.9 → 2.0.0.pre.alpha.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (432) 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/identifiers/adopted_european_norm.rb +36 -7
  82. data/lib/pubid/bsi/identifiers/adopted_international_standard.rb +7 -6
  83. data/lib/pubid/bsi/identifiers/british_industrial_practice.rb +0 -1
  84. data/lib/pubid/bsi/identifiers/bundled_identifier.rb +10 -0
  85. data/lib/pubid/bsi/identifiers/committee_document.rb +10 -1
  86. data/lib/pubid/bsi/identifiers/handbook.rb +0 -2
  87. data/lib/pubid/bsi/identifiers/practice_guide.rb +0 -1
  88. data/lib/pubid/bsi/identifiers/set.rb +11 -0
  89. data/lib/pubid/bsi/identifiers/standalone_amendment.rb +10 -1
  90. data/lib/pubid/bsi/parser.rb +1 -1
  91. data/lib/pubid/bsi/renderer.rb +63 -51
  92. data/lib/pubid/bsi/single_identifier.rb +16 -7
  93. data/lib/pubid/bsi/urn_generator.rb +11 -2
  94. data/lib/pubid/bsi.rb +7 -1
  95. data/lib/pubid/builder/base.rb +18 -8
  96. data/lib/pubid/bundled_identifier.rb +16 -6
  97. data/lib/pubid/calconnect/identifier.rb +9 -5
  98. data/lib/pubid/calconnect/parser.rb +1 -1
  99. data/lib/pubid/calconnect.rb +7 -1
  100. data/lib/pubid/ccsds/identifier.rb +9 -2
  101. data/lib/pubid/ccsds/identifiers/base.rb +4 -2
  102. data/lib/pubid/ccsds/identifiers/corrigendum.rb +2 -2
  103. data/lib/pubid/ccsds/parser.rb +1 -1
  104. data/lib/pubid/ccsds/single_identifier.rb +2 -2
  105. data/lib/pubid/ccsds.rb +1 -0
  106. data/lib/pubid/cen_cenelec/builder.rb +101 -76
  107. data/lib/pubid/cen_cenelec/identifier.rb +115 -3
  108. data/lib/pubid/cen_cenelec/identifiers/adopted_european_norm.rb +27 -16
  109. data/lib/pubid/cen_cenelec/identifiers/amendment.rb +46 -6
  110. data/lib/pubid/cen_cenelec/identifiers/base.rb +8 -9
  111. data/lib/pubid/cen_cenelec/identifiers/cen_report.rb +1 -1
  112. data/lib/pubid/cen_cenelec/identifiers/cen_workshop_agreement.rb +1 -1
  113. data/lib/pubid/cen_cenelec/identifiers/cenelec_harmonization_document.rb +1 -1
  114. data/lib/pubid/cen_cenelec/identifiers/consolidated_identifier.rb +29 -20
  115. data/lib/pubid/cen_cenelec/identifiers/corrigendum.rb +47 -8
  116. data/lib/pubid/cen_cenelec/identifiers/european_prestandard.rb +27 -2
  117. data/lib/pubid/cen_cenelec/identifiers/european_specification.rb +1 -1
  118. data/lib/pubid/cen_cenelec/identifiers/fragment.rb +15 -5
  119. data/lib/pubid/cen_cenelec/identifiers/guide.rb +1 -1
  120. data/lib/pubid/cen_cenelec/identifiers/harmonization_document.rb +1 -1
  121. data/lib/pubid/cen_cenelec/identifiers/technical_report.rb +1 -1
  122. data/lib/pubid/cen_cenelec/identifiers/technical_specification.rb +1 -1
  123. data/lib/pubid/cen_cenelec/parser.rb +1 -1
  124. data/lib/pubid/cen_cenelec/renderer.rb +55 -60
  125. data/lib/pubid/cen_cenelec/single_identifier.rb +20 -2
  126. data/lib/pubid/cen_cenelec/urn_generator.rb +66 -44
  127. data/lib/pubid/cen_cenelec.rb +28 -10
  128. data/lib/pubid/cie/builder.rb +10 -0
  129. data/lib/pubid/cie/identifier.rb +6 -1
  130. data/lib/pubid/cie/identifiers/bundle.rb +4 -2
  131. data/lib/pubid/cie/identifiers/conference.rb +2 -2
  132. data/lib/pubid/cie/identifiers/corrigendum.rb +2 -2
  133. data/lib/pubid/cie/identifiers/dual_published.rb +2 -2
  134. data/lib/pubid/cie/identifiers/identical.rb +2 -2
  135. data/lib/pubid/cie/identifiers/joint_published.rb +2 -2
  136. data/lib/pubid/cie/identifiers/proceedings.rb +8 -6
  137. data/lib/pubid/cie/identifiers/standard.rb +22 -13
  138. data/lib/pubid/cie/identifiers/supplement.rb +2 -2
  139. data/lib/pubid/cie/identifiers/tutorial_bundle.rb +2 -2
  140. data/lib/pubid/cie/parser.rb +1 -1
  141. data/lib/pubid/cie.rb +7 -1
  142. data/lib/pubid/conformance/checks.rb +72 -0
  143. data/lib/pubid/conformance/corpus/case.rb +55 -0
  144. data/lib/pubid/conformance/corpus.rb +36 -0
  145. data/lib/pubid/conformance/generator.rb +212 -0
  146. data/lib/pubid/conformance/pending.rb +63 -0
  147. data/lib/pubid/conformance/runner.rb +153 -0
  148. data/lib/pubid/conformance.rb +55 -0
  149. data/lib/pubid/core.rb +2 -0
  150. data/lib/pubid/csa/builder.rb +47 -47
  151. data/lib/pubid/csa/composite_identifier.rb +14 -19
  152. data/lib/pubid/csa/identifier.rb +143 -81
  153. data/lib/pubid/csa/identifiers/bundled.rb +23 -13
  154. data/lib/pubid/csa/identifiers/canadian_adopted.rb +13 -13
  155. data/lib/pubid/csa/identifiers/cec.rb +44 -4
  156. data/lib/pubid/csa/identifiers/combined.rb +95 -73
  157. data/lib/pubid/csa/identifiers/csa_adopted.rb +4 -4
  158. data/lib/pubid/csa/identifiers/package.rb +2 -2
  159. data/lib/pubid/csa/parser.rb +6 -2
  160. data/lib/pubid/csa/renderer.rb +13 -5
  161. data/lib/pubid/csa/single_identifier.rb +93 -4
  162. data/lib/pubid/csa/urn_generator.rb +9 -2
  163. data/lib/pubid/csa/wrapper_identifier.rb +39 -23
  164. data/lib/pubid/csa.rb +7 -1
  165. data/lib/pubid/doi/identifier.rb +6 -3
  166. data/lib/pubid/doi/parser.rb +1 -1
  167. data/lib/pubid/doi.rb +1 -0
  168. data/lib/pubid/easc/identifier.rb +0 -2
  169. data/lib/pubid/easc/parser.rb +10 -2
  170. data/lib/pubid/easc.rb +1 -0
  171. data/lib/pubid/ecma/builder.rb +23 -9
  172. data/lib/pubid/ecma/identifier.rb +81 -10
  173. data/lib/pubid/ecma/parser.rb +39 -4
  174. data/lib/pubid/ecma/renderer.rb +29 -8
  175. data/lib/pubid/ecma/urn_generator.rb +34 -13
  176. data/lib/pubid/ecma/urn_parser.rb +30 -14
  177. data/lib/pubid/ecma.rb +1 -0
  178. data/lib/pubid/errors.rb +80 -0
  179. data/lib/pubid/etsi/builder.rb +6 -1
  180. data/lib/pubid/etsi/identifiers/base.rb +34 -20
  181. data/lib/pubid/etsi/identifiers/etsi_standard.rb +120 -41
  182. data/lib/pubid/etsi/identifiers/supplement_identifier.rb +10 -0
  183. data/lib/pubid/etsi/parser.rb +1 -1
  184. data/lib/pubid/etsi.rb +1 -0
  185. data/lib/pubid/evs/builder.rb +26 -0
  186. data/lib/pubid/evs/identifier.rb +37 -0
  187. data/lib/pubid/evs/identifiers/national_adoption.rb +17 -0
  188. data/lib/pubid/evs/identifiers.rb +9 -0
  189. data/lib/pubid/evs/parser.rb +27 -0
  190. data/lib/pubid/evs/renderer.rb +30 -0
  191. data/lib/pubid/evs/urn_generator.rb +30 -0
  192. data/lib/pubid/evs/urn_parser.rb +80 -0
  193. data/lib/pubid/evs.rb +90 -0
  194. data/lib/pubid/export.rb +1 -0
  195. data/lib/pubid/format_detector.rb +1 -0
  196. data/lib/pubid/gb/identifier.rb +6 -3
  197. data/lib/pubid/gb/parser.rb +1 -1
  198. data/lib/pubid/gb.rb +1 -0
  199. data/lib/pubid/gost/builder.rb +46 -9
  200. data/lib/pubid/gost/identifier.rb +0 -2
  201. data/lib/pubid/gost/identifiers/foreign_reference.rb +1 -1
  202. data/lib/pubid/gost/parser.rb +10 -2
  203. data/lib/pubid/gost.rb +1 -0
  204. data/lib/pubid/iala/identifier.rb +9 -6
  205. data/lib/pubid/iala/parser.rb +10 -2
  206. data/lib/pubid/iala/urn_parser.rb +6 -2
  207. data/lib/pubid/iala.rb +1 -0
  208. data/lib/pubid/iana/builder.rb +3 -1
  209. data/lib/pubid/iana/identifier.rb +74 -6
  210. data/lib/pubid/iana/identifiers/registry.rb +36 -0
  211. data/lib/pubid/iana/parser.rb +1 -1
  212. data/lib/pubid/iana/renderer.rb +3 -0
  213. data/lib/pubid/iana/urn_generator.rb +5 -1
  214. data/lib/pubid/iana.rb +1 -0
  215. data/lib/pubid/identifier.rb +594 -10
  216. data/lib/pubid/identifier_metadata.rb +1 -0
  217. data/lib/pubid/idf/builder.rb +3 -3
  218. data/lib/pubid/idf/identifier.rb +18 -9
  219. data/lib/pubid/idf/parser.rb +1 -1
  220. data/lib/pubid/idf/renderer.rb +4 -4
  221. data/lib/pubid/idf/urn_generator.rb +1 -1
  222. data/lib/pubid/idf.rb +7 -1
  223. data/lib/pubid/iec/builder.rb +14 -7
  224. data/lib/pubid/iec/components.rb +0 -1
  225. data/lib/pubid/iec/identifier.rb +73 -83
  226. data/lib/pubid/iec/identifiers/amendment.rb +6 -2
  227. data/lib/pubid/iec/identifiers/component_specification.rb +0 -10
  228. data/lib/pubid/iec/identifiers/conformity_assessment.rb +0 -10
  229. data/lib/pubid/iec/identifiers/corrigendum.rb +2 -2
  230. data/lib/pubid/iec/identifiers/fragment_identifier.rb +2 -4
  231. data/lib/pubid/iec/identifiers/guide.rb +0 -16
  232. data/lib/pubid/iec/identifiers/international_standard.rb +15 -2
  233. data/lib/pubid/iec/identifiers/operational_document.rb +0 -10
  234. data/lib/pubid/iec/identifiers/publicly_available_specification.rb +0 -12
  235. data/lib/pubid/iec/identifiers/societal_technology_trend_report.rb +0 -10
  236. data/lib/pubid/iec/identifiers/systems_reference_document.rb +0 -10
  237. data/lib/pubid/iec/identifiers/technical_report.rb +0 -17
  238. data/lib/pubid/iec/identifiers/technical_specification.rb +0 -17
  239. data/lib/pubid/iec/identifiers/technology_report.rb +0 -10
  240. data/lib/pubid/iec/identifiers/test_report_form.rb +0 -11
  241. data/lib/pubid/iec/identifiers/white_paper.rb +0 -10
  242. data/lib/pubid/iec/identifiers/working_document.rb +24 -10
  243. data/lib/pubid/iec/parser.rb +2 -18
  244. data/lib/pubid/iec/renderer.rb +13 -10
  245. data/lib/pubid/iec/single_identifier.rb +33 -10
  246. data/lib/pubid/iec/urn_generator.rb +227 -61
  247. data/lib/pubid/iec/urn_parser.rb +42 -4
  248. data/lib/pubid/iec.rb +1 -0
  249. data/lib/pubid/ieee/aiee/identifier.rb +2 -2
  250. data/lib/pubid/ieee/aiee/parser.rb +1 -1
  251. data/lib/pubid/ieee/builder.rb +45 -1
  252. data/lib/pubid/ieee/identifiers/adopted_standard.rb +32 -0
  253. data/lib/pubid/ieee/identifiers/base.rb +42 -5
  254. data/lib/pubid/ieee/identifiers/csa_dual_published.rb +21 -0
  255. data/lib/pubid/ieee/identifiers/dual_published.rb +55 -0
  256. data/lib/pubid/ieee/identifiers/interpretation_identifier.rb +23 -0
  257. data/lib/pubid/ieee/identifiers/joint_development.rb +11 -7
  258. data/lib/pubid/ieee/identifiers/multi_numbered_identifier.rb +57 -5
  259. data/lib/pubid/ieee/identifiers/nesc/base.rb +2 -2
  260. data/lib/pubid/ieee/identifiers/nesc/draft.rb +2 -2
  261. data/lib/pubid/ieee/identifiers/nesc/handbook.rb +2 -2
  262. data/lib/pubid/ieee/identifiers/nesc/redline.rb +2 -2
  263. data/lib/pubid/ieee/identifiers/nesc/standard.rb +2 -2
  264. data/lib/pubid/ieee/ire/identifier.rb +2 -2
  265. data/lib/pubid/ieee/ire/parser.rb +1 -1
  266. data/lib/pubid/ieee/nesc/parser.rb +1 -1
  267. data/lib/pubid/ieee/parser.rb +1 -1
  268. data/lib/pubid/ieee/renderer.rb +5 -1
  269. data/lib/pubid/ieee/urn_generator.rb +32 -3
  270. data/lib/pubid/ieee.rb +7 -1
  271. data/lib/pubid/ieee_debug.rb +1 -0
  272. data/lib/pubid/ietf/builder.rb +24 -8
  273. data/lib/pubid/ietf/identifiers/base.rb +25 -28
  274. data/lib/pubid/ietf/identifiers/bcp.rb +11 -0
  275. data/lib/pubid/ietf/identifiers/fyi.rb +11 -0
  276. data/lib/pubid/ietf/identifiers/internet_draft.rb +18 -2
  277. data/lib/pubid/ietf/identifiers/rfc.rb +2 -0
  278. data/lib/pubid/ietf/identifiers/serialization.rb +51 -0
  279. data/lib/pubid/ietf/identifiers/std.rb +11 -0
  280. data/lib/pubid/ietf/identifiers.rb +1 -0
  281. data/lib/pubid/ietf/parser.rb +27 -9
  282. data/lib/pubid/ietf/renderer.rb +44 -6
  283. data/lib/pubid/ietf/urn_generator.rb +20 -6
  284. data/lib/pubid/ietf/urn_parser.rb +10 -2
  285. data/lib/pubid/ietf.rb +12 -2
  286. data/lib/pubid/iho/identifiers/base.rb +10 -3
  287. data/lib/pubid/iho/parser.rb +1 -1
  288. data/lib/pubid/iho.rb +1 -0
  289. data/lib/pubid/isbn/identifier.rb +25 -7
  290. data/lib/pubid/isbn/parser.rb +1 -1
  291. data/lib/pubid/isbn.rb +1 -0
  292. data/lib/pubid/iso/bundled_identifier.rb +5 -3
  293. data/lib/pubid/iso/combined_identifier.rb +5 -3
  294. data/lib/pubid/iso/identifier.rb +12 -7
  295. data/lib/pubid/iso/identifiers/directives_supplement.rb +4 -4
  296. data/lib/pubid/iso/parser.rb +1 -1
  297. data/lib/pubid/iso.rb +7 -1
  298. data/lib/pubid/itu/identifiers/addendum.rb +2 -2
  299. data/lib/pubid/itu/identifiers/amendment.rb +2 -2
  300. data/lib/pubid/itu/identifiers/base.rb +19 -5
  301. data/lib/pubid/itu/identifiers/corrigendum.rb +2 -2
  302. data/lib/pubid/itu/identifiers/errata.rb +2 -2
  303. data/lib/pubid/itu/identifiers/supplement.rb +2 -2
  304. data/lib/pubid/itu/parser.rb +1 -1
  305. data/lib/pubid/itu.rb +1 -0
  306. data/lib/pubid/jcgm/builder.rb +1 -1
  307. data/lib/pubid/jcgm/identifier.rb +10 -0
  308. data/lib/pubid/jcgm/identifiers/meeting.rb +1 -1
  309. data/lib/pubid/jcgm/parser.rb +10 -3
  310. data/lib/pubid/jcgm/renderer.rb +10 -3
  311. data/lib/pubid/jcgm/single_identifier.rb +24 -15
  312. data/lib/pubid/jcgm/urn_generator.rb +1 -1
  313. data/lib/pubid/jcgm/urn_parser.rb +156 -14
  314. data/lib/pubid/jcgm.rb +10 -0
  315. data/lib/pubid/jis/identifier.rb +10 -3
  316. data/lib/pubid/jis/parser.rb +1 -1
  317. data/lib/pubid/jis.rb +1 -0
  318. data/lib/pubid/nist/components/stage.rb +2 -2
  319. data/lib/pubid/nist/configuration.rb +6 -8
  320. data/lib/pubid/nist/identifiers/base.rb +12 -2
  321. data/lib/pubid/nist/identifiers/circular.rb +9 -2
  322. data/lib/pubid/nist/identifiers/circular_supplement.rb +14 -5
  323. data/lib/pubid/nist/identifiers/commercial_standards_monthly.rb +7 -1
  324. data/lib/pubid/nist/identifiers/crpl_report.rb +7 -1
  325. data/lib/pubid/nist/identifiers/handbook.rb +9 -2
  326. data/lib/pubid/nist/identifiers/internal_report.rb +12 -5
  327. data/lib/pubid/nist/identifiers/miscellaneous_publication.rb +14 -6
  328. data/lib/pubid/nist/identifiers/monograph.rb +14 -7
  329. data/lib/pubid/nist/identifiers/report.rb +14 -6
  330. data/lib/pubid/nist/parser.rb +1 -1
  331. data/lib/pubid/nist/supplement_identifier.rb +16 -1
  332. data/lib/pubid/nist.rb +7 -1
  333. data/lib/pubid/oasis/builder.rb +12 -10
  334. data/lib/pubid/oasis/identifier.rb +89 -11
  335. data/lib/pubid/oasis/parser.rb +1 -1
  336. data/lib/pubid/oasis.rb +7 -1
  337. data/lib/pubid/ogc/identifier.rb +9 -5
  338. data/lib/pubid/ogc/parser.rb +1 -1
  339. data/lib/pubid/ogc.rb +1 -0
  340. data/lib/pubid/oiml/builder.rb +5 -1
  341. data/lib/pubid/oiml/identifiers/basic_publication.rb +2 -0
  342. data/lib/pubid/oiml/identifiers/bulletin.rb +36 -0
  343. data/lib/pubid/oiml/identifiers/code_number.rb +90 -0
  344. data/lib/pubid/oiml/identifiers/document.rb +2 -0
  345. data/lib/pubid/oiml/identifiers/expert_report.rb +2 -0
  346. data/lib/pubid/oiml/identifiers/guide.rb +2 -0
  347. data/lib/pubid/oiml/identifiers/recommendation.rb +2 -0
  348. data/lib/pubid/oiml/identifiers/seminar_report.rb +2 -0
  349. data/lib/pubid/oiml/identifiers/vocabulary.rb +2 -0
  350. data/lib/pubid/oiml/identifiers.rb +1 -0
  351. data/lib/pubid/oiml/parser.rb +1 -1
  352. data/lib/pubid/oiml/single_identifier.rb +26 -42
  353. data/lib/pubid/oiml/supplement_identifier.rb +27 -0
  354. data/lib/pubid/oiml.rb +7 -1
  355. data/lib/pubid/omg/builder.rb +1 -0
  356. data/lib/pubid/omg/identifier.rb +20 -3
  357. data/lib/pubid/omg/parser.rb +64 -11
  358. data/lib/pubid/omg/renderer.rb +13 -1
  359. data/lib/pubid/omg.rb +2 -1
  360. data/lib/pubid/parser/grammar.rb +74 -0
  361. data/lib/pubid/parser.rb +2 -0
  362. data/lib/pubid/parsers/mr_string.rb +19 -0
  363. data/lib/pubid/plateau/parser.rb +1 -1
  364. data/lib/pubid/plateau.rb +10 -0
  365. data/lib/pubid/prefixes_support.rb +1 -0
  366. data/lib/pubid/renderers/annotator.rb +233 -0
  367. data/lib/pubid/renderers/base.rb +13 -0
  368. data/lib/pubid/renderers/directives_renderer.rb +3 -3
  369. data/lib/pubid/renderers/human_readable.rb +3 -3
  370. data/lib/pubid/renderers/mr_string.rb +15 -9
  371. data/lib/pubid/renderers.rb +1 -0
  372. data/lib/pubid/rendering/numbering.rb +25 -7
  373. data/lib/pubid/rendering.rb +1 -0
  374. data/lib/pubid/sae/identifiers/base.rb +9 -2
  375. data/lib/pubid/sae/parser.rb +1 -1
  376. data/lib/pubid/sae.rb +1 -0
  377. data/lib/pubid/schema/declaration.rb +33 -0
  378. data/lib/pubid/schema/error.rb +14 -0
  379. data/lib/pubid/schema/identifier_type.rb +23 -0
  380. data/lib/pubid/schema/loader.rb +117 -0
  381. data/lib/pubid/schema/typed_stage.rb +24 -0
  382. data/lib/pubid/schema.rb +25 -0
  383. data/lib/pubid/tgpp/builder.rb +1 -1
  384. data/lib/pubid/tgpp/identifier.rb +21 -5
  385. data/lib/pubid/tgpp/identifiers/technical_report.rb +2 -2
  386. data/lib/pubid/tgpp/identifiers/technical_specification.rb +2 -2
  387. data/lib/pubid/tgpp/parser.rb +22 -6
  388. data/lib/pubid/tgpp/renderer.rb +21 -5
  389. data/lib/pubid/tgpp/urn_generator.rb +27 -8
  390. data/lib/pubid/tgpp/urn_parser.rb +8 -6
  391. data/lib/pubid/tgpp.rb +7 -1
  392. data/lib/pubid/type_resolver.rb +16 -1
  393. data/lib/pubid/un/identifier.rb +6 -3
  394. data/lib/pubid/un/parser.rb +1 -1
  395. data/lib/pubid/un.rb +1 -0
  396. data/lib/pubid/urn_parser/errors.rb +8 -1
  397. data/lib/pubid/urn_parser.rb +1 -0
  398. data/lib/pubid/utils.rb +1 -0
  399. data/lib/pubid/version.rb +1 -1
  400. data/lib/pubid/w3c/builder.rb +5 -5
  401. data/lib/pubid/w3c/identifier.rb +38 -9
  402. data/lib/pubid/w3c/parser.rb +1 -1
  403. data/lib/pubid/w3c/renderer.rb +1 -1
  404. data/lib/pubid/w3c/urn_generator.rb +2 -2
  405. data/lib/pubid/w3c.rb +1 -0
  406. data/lib/pubid/xsf/identifier.rb +9 -5
  407. data/lib/pubid/xsf/parser.rb +17 -4
  408. data/lib/pubid/xsf.rb +7 -1
  409. data/lib/pubid.rb +399 -21
  410. data/lib/tasks/conformance.rake +34 -0
  411. data/lib/tasks/docs.rake +3 -3
  412. data/lib/tasks/schema.rake +88 -0
  413. data/schema/core/joint_prefixes.yaml +24 -0
  414. data/schema/iec.yaml +1017 -0
  415. data/schema/iso.yaml +1586 -0
  416. data/schema/schema.schema.yaml +96 -0
  417. metadata +38 -17
  418. data/archived-gems/pubid-ccsds/update_codes.yaml +0 -1
  419. data/archived-gems/pubid-iec/stages.yaml +0 -129
  420. data/archived-gems/pubid-iec/update_codes.yaml +0 -67
  421. data/archived-gems/pubid-ieee/update_codes.yaml +0 -104
  422. data/archived-gems/pubid-iso/stages.yaml +0 -106
  423. data/archived-gems/pubid-iso/update_codes.yaml +0 -4
  424. data/archived-gems/pubid-itu/i18n.yaml +0 -13
  425. data/archived-gems/pubid-itu/series.yaml +0 -42
  426. data/archived-gems/pubid-nist/publishers.yaml +0 -6
  427. data/archived-gems/pubid-nist/series.yaml +0 -121
  428. data/archived-gems/pubid-nist/update_codes.yaml +0 -93
  429. data/archived-gems/pubid-plateau/update_codes.yaml +0 -6
  430. data/lib/pubid/cen_cenelec/supplement_identifier.rb +0 -48
  431. data/lib/pubid/iec/components/code.rb +0 -36
  432. /data/{archived-gems/pubid-nist → data/nist}/stages.yaml +0 -0
@@ -30,10 +30,11 @@ module Pubid
30
30
  def render_default(id)
31
31
  parts = ["API"]
32
32
 
33
- # Add type if available
34
- if id.class.attributes.key?(:type_string) && id.type_string
35
- parts << id.type_string
36
- end
33
+ # Add the type token (RP, STD, BULL, PUBL, SPEC, TR ...) if the leaf
34
+ # defines one. `type_string` is a plain METHOD on each leaf, never a
35
+ # lutaml attribute, so the old `attributes.key?(:type_string)` guard
36
+ # was always false and the token was dropped from every rendering.
37
+ parts << id.type_string if id.respond_to?(:type_string) && id.type_string
37
38
 
38
39
  # Add code/number with part as one token
39
40
  parts << code_portion(id) if code_portion(id)
@@ -48,7 +49,7 @@ module Pubid
48
49
  parts = ["API", "MPMS"]
49
50
 
50
51
  # Add chapter
51
- parts << "CH #{id.chapter}" if id.chapter
52
+ parts << "CH #{id.number}" if id.number
52
53
 
53
54
  # Add section/subsection
54
55
  if id.section
@@ -77,10 +78,18 @@ module Pubid
77
78
  parts.join(" ")
78
79
  end
79
80
 
81
+ # The document number plus its part, as one token.
82
+ #
83
+ # This used to read a `code` attribute that NOTHING ever assigned — the
84
+ # parser emits no `:code` key and the builder never sets one — so it
85
+ # always returned nil and 163 of API's 193 corpus identifiers rendered
86
+ # as the bare publisher "API". That string is both the document number
87
+ # and the output filename, so they all collapsed onto each other.
88
+ # `number` is what Builder#cast actually populates.
80
89
  def code_portion(id)
81
- return nil unless id.code
90
+ return nil unless id.number
82
91
 
83
- code_str = id.code.to_s
92
+ code_str = id.number.to_s
84
93
  code_str += "-#{id.part}" if id.part
85
94
  code_str
86
95
  end
@@ -12,20 +12,13 @@ module Pubid
12
12
  # serialize a String against the Components::Publisher attribute.
13
13
  attribute :publisher, :string, default: -> { "API" }
14
14
 
15
- attribute :code, Components::Code
16
- attribute :part, :string
15
+ # `number` and `part` are inherited as :string from
16
+ # Pubid::Api::Identifier. There is deliberately no `code` attribute: one
17
+ # existed here typed Components::Code, but nothing ever assigned it
18
+ # (the parser emits no `:code` key), so it only misdirected
19
+ # Renderer#code_portion away from the `number` the builder populates.
17
20
  attribute :year, :string
18
21
  attribute :reaffirmation, :string
19
-
20
- private
21
-
22
- def code_portion
23
- return nil unless code
24
-
25
- code_str = code.to_s
26
- code_str += "-#{part}" if part
27
- code_str
28
- end
29
22
  end
30
23
  end
31
24
  end
data/lib/pubid/api.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "pubid"
3
4
  module Pubid
4
5
  module Api
5
6
  extend Pubid::PrefixesSupport
@@ -39,6 +39,18 @@ module Pubid
39
39
  return build_addendum_no_type(parsed_hash[:addendum_no_type])
40
40
  elsif parsed_hash[:standard_addendum]
41
41
  return build_standard_addendum(parsed_hash[:standard_addendum])
42
+ # These two grammar rules had NO dispatch branch, so they fell through
43
+ # to the plain-identifier path below. That path looks for
44
+ # publisher/type/code at the TOP of the hash, finds only the wrapper
45
+ # key, and builds an empty Standard — 123 corpus ids rendered as the
46
+ # bare string "ASHRAE Standard ". Both subtrees are flat and carry
47
+ # exactly the keys #build_addendum_from_identifier already reads.
48
+ elsif parsed_hash[:addendum]
49
+ return build_addendum_from_identifier(parsed_hash[:addendum])
50
+ elsif parsed_hash[:publisher_base_addendum]
51
+ return build_addendum_from_identifier(
52
+ parsed_hash[:publisher_base_addendum],
53
+ )
42
54
  end
43
55
 
44
56
  # Handle base identifier
@@ -89,7 +101,7 @@ module Pubid
89
101
  # code_with_year contains both code and year
90
102
  code_year_data = parsed[:code_with_year]
91
103
  if code_year_data && code_year_data[:code]
92
- attributes[:code] =
104
+ attributes[:number] =
93
105
  extract_value(code_year_data[:code])
94
106
  end
95
107
  if code_year_data && code_year_data[:year]
@@ -97,7 +109,7 @@ module Pubid
97
109
  extract_value(code_year_data[:year])
98
110
  end
99
111
  elsif parsed[:code]
100
- attributes[:code] = extract_value(parsed[:code])
112
+ attributes[:number] = extract_value(parsed[:code])
101
113
  end
102
114
 
103
115
  # Extract year if not already extracted from code_with_year
@@ -205,7 +217,7 @@ module Pubid
205
217
  def build_publisher_addendum(parsed)
206
218
  base_attrs = {
207
219
  type: extract_value(parsed[:type]),
208
- code: extract_value(parsed[:code]),
220
+ number: extract_value(parsed[:code]),
209
221
  year: extract_value(parsed[:year]),
210
222
  }
211
223
  base_class = determine_identifier_class(base_attrs)
@@ -228,7 +240,7 @@ module Pubid
228
240
  base_attrs = {
229
241
  copublisher: extract_value(parsed[:copublisher]),
230
242
  type: extract_value(parsed[:type]) || "Standard",
231
- code: extract_value(parsed[:code]),
243
+ number: extract_value(parsed[:code]),
232
244
  year: extract_value(parsed[:year]),
233
245
  }
234
246
  base_class = determine_identifier_class(base_attrs)
@@ -260,7 +272,7 @@ module Pubid
260
272
  if parsed[:code_with_year]
261
273
  code_year_data = parsed[:code_with_year]
262
274
  if code_year_data && code_year_data[:code]
263
- base_attrs[:code] =
275
+ base_attrs[:number] =
264
276
  extract_value(code_year_data[:code])
265
277
  end
266
278
  if code_year_data && code_year_data[:year]
@@ -298,7 +310,7 @@ module Pubid
298
310
  if parsed[:code_with_year]
299
311
  code_year_data = parsed[:code_with_year]
300
312
  if code_year_data && code_year_data[:code]
301
- base_attrs[:code] =
313
+ base_attrs[:number] =
302
314
  extract_value(code_year_data[:code])
303
315
  end
304
316
  if code_year_data && code_year_data[:year]
@@ -349,7 +361,7 @@ module Pubid
349
361
  if parsed[:code_with_year]
350
362
  code_year_data = parsed[:code_with_year]
351
363
  if code_year_data && code_year_data[:code]
352
- base_attrs[:code] =
364
+ base_attrs[:number] =
353
365
  extract_value(code_year_data[:code])
354
366
  end
355
367
  if code_year_data && code_year_data[:year]
@@ -358,7 +370,7 @@ module Pubid
358
370
  end
359
371
  else
360
372
  base_attrs[:type] = extract_value(parsed[:type]) || "Standard"
361
- base_attrs[:code] = extract_value(parsed[:code])
373
+ base_attrs[:number] = extract_value(parsed[:code])
362
374
  base_attrs[:year] = extract_value(parsed[:year])
363
375
  end
364
376
 
@@ -431,6 +443,15 @@ module Pubid
431
443
  # @param parsed [Hash] the parsed data
432
444
  # @return [Hash] attributes for base identifier
433
445
  def extract_base_attributes(parsed)
446
+ # Unwrap the `:base` subtree the supplement grammars nest the base
447
+ # document under. Builder#build does this for the plain-identifier
448
+ # path, but the five supplement builders that call this method all
449
+ # passed the WHOLE parse hash — so every lookup below missed and the
450
+ # base was built with nothing. 318 Errata rendered as the identical
451
+ # string "ASHRAE Standard Errata"; doing it here fixes all five call
452
+ # sites at once, rather than repeating the unwrap in each.
453
+ parsed = parsed[:base] if parsed.is_a?(Hash) && parsed[:base]
454
+
434
455
  attributes = {}
435
456
 
436
457
  if parsed[:publisher]
@@ -448,7 +469,7 @@ module Pubid
448
469
  if parsed[:code_with_year]
449
470
  code_year_data = parsed[:code_with_year]
450
471
  if code_year_data && code_year_data[:code]
451
- attributes[:code] =
472
+ attributes[:number] =
452
473
  extract_value(code_year_data[:code])
453
474
  end
454
475
  if code_year_data && code_year_data[:year]
@@ -456,7 +477,7 @@ module Pubid
456
477
  extract_value(code_year_data[:year])
457
478
  end
458
479
  else
459
- attributes[:code] = extract_value(parsed[:code]) if parsed[:code]
480
+ attributes[:number] = extract_value(parsed[:code]) if parsed[:code]
460
481
  attributes[:year] = extract_value(parsed[:year]) if parsed[:year]
461
482
  end
462
483
 
@@ -14,6 +14,12 @@ module Pubid
14
14
  class AddendaPackage < SupplementIdentifier
15
15
  attribute :package_description, :string # e.g., "Addenda Supplement Package"
16
16
 
17
+ # See Errata.
18
+ def mr_supplement_suffix
19
+ ["pkg", mr_sanitize(package_description)]
20
+ .compact.reject(&:empty?).join(".")
21
+ end
22
+
17
23
  TYPED_STAGES = [
18
24
  Components::TypedStage.new(
19
25
  abbr: ["Addenda Supplement Package", "Addenda Package"],
@@ -15,6 +15,14 @@ module Pubid
15
15
  attribute :addendum_code, :string # a, b, c, ..., aa, ab, ..., ba, etc.
16
16
  attribute :addendum_date, :string # Optional date (January 22, 2019)
17
17
 
18
+ # See Errata: the renderer must recurse into `base`. The addendum
19
+ # letter is what distinguishes addendum a from addendum b of one
20
+ # standard, so it belongs in the slug as well as in `==`.
21
+ def mr_supplement_suffix
22
+ ["add", addendum_code&.to_s&.downcase]
23
+ .compact.reject(&:empty?).join(".")
24
+ end
25
+
18
26
  TYPED_STAGES = [
19
27
  Components::TypedStage.new(
20
28
  abbr: ["Addendum", "Addenda"],
@@ -8,16 +8,25 @@ module Pubid
8
8
  # Parse an ASHRAE identifier string into an identifier object
9
9
  # @param identifier [String] The ASHRAE identifier string to parse
10
10
  # @return [Pubid::Ashrae::Identifier] The appropriate identifier object
11
- # @raise [Parslet::ParseFailed] If parsing fails
11
+ # @raise [Pubid::Errors::ParseError] If parsing fails
12
12
  def self.parse(identifier)
13
13
  parsed = Parser.parse(identifier)
14
14
  Builder.build(parsed)
15
- rescue Parslet::ParseFailed => e
16
- raise "Failed to parse ASHRAE identifier '#{identifier}': #{e.message}"
17
15
  end
18
16
 
19
17
  attribute :publisher, :string, default: "ASHRAE"
20
- attribute :code, :string
18
+ # The document number used to live here as `attribute :code, :string`,
19
+ # leaving the `number` inherited from ::Pubid::Identifier nil — so
20
+ # relaton-index, which sorts and bsearches on `id.root.number.to_s`,
21
+ # keyed all 3,619 ASHRAE rows on "".
22
+ #
23
+ # It is now `attribute :number, :string` on the two single-document
24
+ # LEAVES (Identifiers::Standard and Identifiers::Guideline), never here:
25
+ # this class is inherited by BOTH SingleIdentifier and
26
+ # SupplementIdentifier, and redefining the parent's Components::Code-typed
27
+ # `number` on an inherited-from class resolves nondeterministically under
28
+ # multi-flavor load. The supplement types carry no number of their own —
29
+ # they reach it through #root, which walks `base`.
21
30
  attribute :year, :string
22
31
  attribute :type, :string
23
32
  attribute :suffix, :string # R (revision), P (proposed), etc.
@@ -25,6 +34,74 @@ module Pubid
25
34
  attribute :reaffirmed, :string
26
35
  attribute :copublisher, :string
27
36
 
37
+ # --- MR slug hooks -------------------------------------------------
38
+ #
39
+ # ASHRAE supplied none, and every base hook looks for something ASHRAE
40
+ # does not use: `mr_type` reads a typed_stage, `mr_year` reads a
41
+ # Components::Date. With `number` nil too, ALL 3,619 ids collapsed onto
42
+ # the single slug "ashrae" — the worst collapse measured in the gem, and
43
+ # `to_slug` is what consumers use as an output FILENAME.
44
+ #
45
+ # `type` distinguishes a Guideline from a Standard of the same number
46
+ # (Guideline 0 and Standard 0 are different documents), so it has to
47
+ # reach the slug and not only `==`.
48
+ def mr_type
49
+ type&.to_s&.downcase
50
+ end
51
+
52
+ # ASHRAE keeps its edition in a plain `year` string, not the inherited
53
+ # `date`, so the base hook read nil and every edition of one standard
54
+ # shared a slug.
55
+ def mr_year
56
+ year&.to_s
57
+ end
58
+
59
+ # `copublisher` is in `==` and in `to_s`, so by the project rule it has to
60
+ # reach the slug too: "ASHRAE Standard 15-2019 Addendum a" and
61
+ # "ANSI/ASHRAE Addendum a to ANSI/ASHRAE Standard 15-2019" have different
62
+ # hashes and would otherwise share a filename. (Whether those two strings
63
+ # ought to normalise to ONE identifier is a separate question, and not one
64
+ # the slug should paper over.)
65
+ def mr_publisher
66
+ mr_sanitize(copublisher || publisher)
67
+ end
68
+
69
+ # Both trailing markers are identity-bearing and must reach the slug, not
70
+ # only `==`: `reaffirmed` ("RA 2017") separates "ANSI/ASHRAE Standard
71
+ # 206-2013 (RA 2017)" from the plain "…206-2013", and `suffix` ("R" for
72
+ # revision, "P" for proposed) separates "ASHRAE Guideline 27-2019" from
73
+ # "…27-2019R". Their hashes differ, so their filenames must too.
74
+ # Uses the shared #mr_join rather than a bare `compact.join("-")`, so it
75
+ # returns nil instead of "" when nothing survives: MrString's own
76
+ # `compact` drops nil but not the empty string, and a blank segment
77
+ # yields a double dot in the joined slug.
78
+ def mr_number_with_part
79
+ mr_join(mr_sanitize(number),
80
+ (reaffirmed ? "ra#{mr_sanitize(reaffirmed)}" : nil),
81
+ mr_sanitize(suffix))
82
+ end
83
+
84
+ # An ASHRAE value can carry characters outside the [a-z0-9.-] charset
85
+ # Renderers::MrString documents: a comma in the code "90A,B,C", a slash
86
+ # in the copublisher "ANSI/ASHRAE", spaces in a package description.
87
+ # Filter BY CHARSET (the BIPM `mr_slug` precedent) rather than by an
88
+ # enumerated escape list, so a character introduced later cannot reach a
89
+ # filename.
90
+ #
91
+ # Public alongside the hooks above rather than tucked behind `private`,
92
+ # since the whole MR surface here is public, and the supplement types
93
+ # call it from their own mr_supplement_suffix. The output emptiness check
94
+ # matters: a value made only of out-of-charset characters collapses to
95
+ # "", which is truthy in Ruby and would reach the renderer as a blank
96
+ # segment, producing a double dot in the joined slug.
97
+ def mr_sanitize(value)
98
+ return nil if value.nil? || value.to_s.empty?
99
+
100
+ sanitized = value.to_s.downcase
101
+ .gsub(%r{[^a-z0-9.-]+}, "-").gsub(%r{\A-+|-+\z}, "")
102
+ sanitized.empty? ? nil : sanitized
103
+ end
104
+
28
105
  # ASHRAE stores the publication year in a plain :string scalar, not a
29
106
  # Components::Date, so the base #exclude's :year->:date remap cannot nil
30
107
  # it. Reset the scalar directly (mirroring CSA/BIPM) so a year-less
@@ -38,6 +115,5 @@ module Pubid
38
115
  result
39
116
  end
40
117
  end
41
-
42
118
  end
43
119
  end
@@ -14,6 +14,13 @@ module Pubid
14
14
  attribute :addendum_codes, :string # Multiple codes like "c and d"
15
15
  attribute :connector, :string # "and" or ","
16
16
 
17
+ # See Errata. The whole letter list is the identity here — "Addenda a,
18
+ # b" and "Addenda c, d" of one standard are different documents.
19
+ def mr_supplement_suffix
20
+ ["adds", mr_sanitize(addendum_codes)]
21
+ .compact.reject(&:empty?).join(".")
22
+ end
23
+
17
24
  TYPED_STAGES = [
18
25
  Components::TypedStage.new(
19
26
  abbr: ["Addenda", "Combined Addenda"],
@@ -13,6 +13,15 @@ module Pubid
13
13
  class Errata < SupplementIdentifier
14
14
  attribute :errata_date, :string
15
15
 
16
+ # Make Renderers::MrString recurse into `base` instead of slugging the
17
+ # supplement flat off attributes it does not have. Without it every
18
+ # erratum of every standard shared one filename, and `to_slug` is what
19
+ # consumers use as an output filename.
20
+ def mr_supplement_suffix
21
+ ["errata", mr_sanitize(errata_date)]
22
+ .compact.reject(&:empty?).join(".")
23
+ end
24
+
16
25
  TYPED_STAGES = [
17
26
  Components::TypedStage.new(
18
27
  abbr: ["Errata"],
@@ -8,6 +8,9 @@ module Pubid
8
8
  # ASHRAE Guideline identifier
9
9
  # Example: ASHRAE Guideline 0-2019, ASHRAE Guideline 1.5
10
10
  class Guideline < SingleIdentifier
11
+ # The relaton index key. Declared on the LEAF, not on the shared
12
+ # Identifier that SupplementIdentifier also inherits — see base.rb.
13
+ attribute :number, :string
11
14
  attribute :type, :string, default: "Guideline"
12
15
 
13
16
  TYPED_STAGES = [
@@ -11,6 +11,22 @@ module Pubid
11
11
  # - Interpretations for Standard 15.2-2022
12
12
  # - Interpretations for Standard 52.1-1992
13
13
  class Interpretation < SupplementIdentifier
14
+ # Mirrors its four sibling supplement types: without it
15
+ # Renderers::MrString slugs the interpretation FLAT off attributes it
16
+ # does not have, instead of recursing into `base`.
17
+ #
18
+ # Currently unreachable, and NOT because of alternation ordering:
19
+ # rule(:interpretation_identifier) is reached and does match, but it
20
+ # never wraps its output in `.as(:interpretation_identifier)`, so
21
+ # Builder#build's branch for that key is dead code and the tree falls
22
+ # through to the plain-Standard path (pre-existing; pinned in
23
+ # spec/pubid/ashrae/root_number_spec.rb, hand-off
24
+ # ashrae-interpretation-collapses-onto-base). Added anyway so that
25
+ # fixing the dispatch does not silently ship malformed filenames.
26
+ def mr_supplement_suffix
27
+ "interp"
28
+ end
29
+
14
30
  TYPED_STAGES = [
15
31
  Components::TypedStage.new(
16
32
  abbr: ["Interpretations"],
@@ -8,6 +8,9 @@ module Pubid
8
8
  # ASHRAE Standard identifier
9
9
  # Example: ASHRAE Standard 15-2024, ASHRAE Standard 90.1-2022
10
10
  class Standard < SingleIdentifier
11
+ # The relaton index key. Declared on the LEAF, not on the shared
12
+ # Identifier that SupplementIdentifier also inherits — see base.rb.
13
+ attribute :number, :string
11
14
  attribute :type, :string, default: "Standard"
12
15
 
13
16
  TYPED_STAGES = [
@@ -6,7 +6,7 @@ module Pubid
6
6
  module Ashrae
7
7
  # Parser class for ASHRAE identifiers
8
8
  # Single Responsibility: Parsing ASHRAE identifier syntax
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 }
@@ -54,15 +54,25 @@ module Pubid
54
54
  end
55
55
 
56
56
  # Code with year pattern (e.g., 34-2024, 62.1-2022, 90A-2010) - used when type is missing
57
+ #
58
+ # The code portion MUST be captured with `.as(:code)`. Without it the
59
+ # digits were matched and then discarded — only the year was named — so
60
+ # every identifier reaching this rule lost its document number: 48 corpus
61
+ # ids rendered as "ANSI/ASHRAE Addendum a to ASHRAE Standard -2024",
62
+ # number gone and a dangling dash left behind. Builder#extract_base_attributes
63
+ # already read `code_with_year[:code]`; it was waiting for a value the
64
+ # grammar never produced.
57
65
  rule(:code_with_year) do
58
66
  (
59
- digits >> # First part (e.g., 34, 62)
60
- (dot >> digits).repeat(0, 2) >> # Optional dotted parts (e.g., .1)
61
- # Special pattern for codes like "90A,B,C-2010"
62
67
  (
63
- letter >> # Single letter after digits
64
- (comma >> letter).repeat(0, 10) # Optional comma-separated letters
65
- ).maybe >>
68
+ digits >> # First part (e.g., 34, 62)
69
+ (dot >> digits).repeat(0, 2) >> # Optional dotted parts (e.g., .1)
70
+ # Special pattern for codes like "90A,B,C-2010"
71
+ (
72
+ letter >> # Single letter after digits
73
+ (comma >> letter).repeat(0, 10) # Optional comma-separated letters
74
+ ).maybe
75
+ ).as(:code) >>
66
76
  space?.maybe >> dash >>
67
77
  year_digits.as(:year)
68
78
  ).as(:code_with_year)
@@ -655,8 +665,13 @@ module Pubid
655
665
  root(:identifier)
656
666
 
657
667
  def self.parse(string)
668
+ unless string.is_a?(String)
669
+ raise Pubid::Errors::InvalidInputError,
670
+ Pubid::INPUT_NOT_A_STRING_MESSAGE
671
+ end
672
+
658
673
  if string.length > Pubid::MAX_INPUT_LENGTH
659
- raise ArgumentError, Pubid::INPUT_TOO_LONG_MESSAGE
674
+ raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
660
675
  end
661
676
 
662
677
  # Strip leading/trailing whitespace
@@ -46,7 +46,7 @@ module Pubid
46
46
  parts << id.type.to_s if id.type
47
47
  result = parts.join(" ")
48
48
  result += " " if result.length.positive?
49
- result += id.code.to_s
49
+ result += id.number.to_s
50
50
  result += "-#{id.year}" if id.year
51
51
  result += " (#{id.amendment})" if id.amendment
52
52
  result += id.suffix if id.suffix
@@ -63,7 +63,7 @@ module Pubid
63
63
  if id.copublisher
64
64
  "#{id.copublisher} Addendum #{id.addendum_code} to #{id.base}"
65
65
  else
66
- result = "ASHRAE Addendum #{id.addendum_code} to #{base_type} #{id.base.code}"
66
+ result = "ASHRAE Addendum #{id.addendum_code} to #{base_type} #{id.base.number}"
67
67
  result += "-#{id.base.year}" if id.base.year
68
68
  result
69
69
  end.tap { |r| r << " (#{id.addendum_date})" if id.addendum_date }
@@ -73,7 +73,7 @@ module Pubid
73
73
  def render_addenda_package(id)
74
74
  return id.base.to_s unless id.base
75
75
 
76
- result = "ASHRAE #{id.base.type || 'Standard'} #{id.base.code}"
76
+ result = "ASHRAE #{id.base.type || 'Standard'} #{id.base.number}"
77
77
  result += "-#{id.base.year}" if id.base.year
78
78
  result += ": Addenda #{id.package_description}" if id.package_description
79
79
  result
@@ -85,9 +85,9 @@ module Pubid
85
85
 
86
86
  base_type = id.base.type || "Standard"
87
87
  if id.addendum_codes
88
- result = "ASHRAE Addenda #{id.addendum_codes} to #{base_type} #{id.base.code}"
88
+ result = "ASHRAE Addenda #{id.addendum_codes} to #{base_type} #{id.base.number}"
89
89
  else
90
- result = "ASHRAE Addenda to #{base_type} #{id.base.code}"
90
+ result = "ASHRAE Addenda to #{base_type} #{id.base.number}"
91
91
  end
92
92
  result += "-#{id.base.year}" if id.base.year
93
93
  result
@@ -108,7 +108,7 @@ module Pubid
108
108
  return id.base.to_s unless id.base
109
109
 
110
110
  base_type = id.base.type || "Standard"
111
- result = "Interpretations for #{base_type} #{id.base.code}"
111
+ result = "Interpretations for #{base_type} #{id.base.number}"
112
112
  result += "-#{id.base.year}" if id.base.year
113
113
  result
114
114
  end
@@ -14,9 +14,24 @@ module Pubid
14
14
  base&.publisher
15
15
  end
16
16
 
17
- # Delegate code to base
17
+ # Delegate the document code to the base standard.
18
+ #
19
+ # The BODY had to move with the `code` -> `number` rename — it was
20
+ # `base&.code`, and `code` no longer exists anywhere in the hierarchy, so
21
+ # it raised NoMethodError on every Errata/Addendum/CombinedAddenda/
22
+ # AddendaPackage/Interpretation. Nothing inside lib/pubid/ashrae calls
23
+ # it, which is exactly why the suite stayed green while a public method
24
+ # on five types was broken.
25
+ #
26
+ # The NAME deliberately stays `code`. Renaming it to `number` looks
27
+ # tidier and is wrong twice over: `number` is an inherited lutaml
28
+ # attribute typed Components::Code, so a String-returning method of that
29
+ # name makes `to_hash` raise IncorrectModelError on every supplement —
30
+ # and even typed correctly it would serialize a duplicate of the base's
31
+ # number onto the wrapper. `code` is not an attribute anywhere, so it is
32
+ # free to be a plain reader. Use `#root.number` for the index key.
18
33
  def code
19
- base&.code
34
+ base&.number
20
35
  end
21
36
  end
22
37
  end
@@ -3,10 +3,16 @@
3
3
  module Pubid
4
4
  module Ashrae
5
5
  class UrnGenerator < Pubid::UrnGenerator::Base
6
+ # A supplement carries no number of its own — only the two
7
+ # single-document leaves declare one — so reach it through #root, which
8
+ # walks `base` to the standard the supplement attaches to. Without the
9
+ # fallback every Errata/Addendum URN lost its number when `code` moved
10
+ # off the shared base onto the leaves.
6
11
  def urn_number
7
- return nil unless identifier.code
12
+ num = identifier.number || identifier.root.number
13
+ return nil if num.nil? || num.to_s.empty?
8
14
 
9
- identifier.code.to_s
15
+ num.to_s
10
16
  end
11
17
 
12
18
  def urn_suffix
data/lib/pubid/ashrae.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "pubid"
3
4
  module Pubid
4
5
  module Ashrae
5
6
  extend Pubid::PrefixesSupport
@@ -19,8 +20,13 @@ module Pubid
19
20
  autoload :UrnParser, "#{__dir__}/ashrae/urn_parser"
20
21
 
21
22
  def self.parse(identifier)
23
+ unless identifier.is_a?(String)
24
+ raise Pubid::Errors::InvalidInputError,
25
+ Pubid::INPUT_NOT_A_STRING_MESSAGE
26
+ end
27
+
22
28
  if identifier.length > Pubid::MAX_INPUT_LENGTH
23
- raise ArgumentError, Pubid::INPUT_TOO_LONG_MESSAGE
29
+ raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
24
30
  end
25
31
 
26
32
  Identifier.parse(identifier)
@@ -21,9 +21,18 @@ module Pubid
21
21
  identifier.publisher = "ASME"
22
22
  end
23
23
 
24
- # Build code component
24
+ # The code is stored as flat index columns on the leaf (see
25
+ # Identifiers::Standard) — `number` is what relaton-index keys on — so
26
+ # the Code built below is unpacked rather than assigned.
25
27
  if parsed_hash[:designator]
26
- identifier.code = build_code(parsed_hash)
28
+ identifier.number = build_code(parsed_hash).to_s
29
+ elsif parsed_hash[:number]
30
+ # A joint-published reference ("ISO/ASME 14414-2015") parses to a bare
31
+ # `number` with no designator. The designator-only guard above dropped
32
+ # it, so the document number was lost from BOTH the identifier and its
33
+ # rendering: `to_s` was "ISO/ASME-2015". Pre-existing; recovering it
34
+ # is what gives these ids an index key.
35
+ identifier.number = parsed_hash[:number].to_s
27
36
  end
28
37
 
29
38
  # Set PTC suffix
@@ -4,6 +4,15 @@ module Pubid
4
4
  module Asme
5
5
  class Identifier < Pubid::Identifier
6
6
  def self.parse(str)
7
+ unless str.is_a?(String)
8
+ raise Pubid::Errors::InvalidInputError,
9
+ Pubid::INPUT_NOT_A_STRING_MESSAGE
10
+ end
11
+
12
+ if str.length > Pubid::MAX_INPUT_LENGTH
13
+ raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
14
+ end
15
+
7
16
  parser = Parser.new
8
17
  builder = Builder.new
9
18