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
@@ -27,14 +27,14 @@ module Pubid
27
27
  #
28
28
  # @param trademark [Boolean] append the IEEE trademark symbol (™/®)
29
29
  # @return [String] YYYY NESC Handbook format with optional edition
30
- def to_s(trademark: false)
30
+ def to_s(trademark: false, **opts)
31
31
  abbr = "NESC"
32
32
  abbr += "(R)" if registered
33
33
  parts = [["IEEE Std", year, "#{abbr} Handbook"].compact.join(" ")]
34
34
  parts << ", #{edition}" if edition
35
35
  result = parts.join
36
36
  result += trademark_symbol if trademark
37
- result
37
+ annotate_plain_render(result, **opts)
38
38
  end
39
39
  end
40
40
  end
@@ -23,10 +23,10 @@ module Pubid
23
23
  #
24
24
  # @param trademark [Boolean] append the IEEE trademark symbol (™/®)
25
25
  # @return [String] YYYY NESC Redline format
26
- def to_s(trademark: false)
26
+ def to_s(trademark: false, **opts)
27
27
  result = [year, "NESC Redline"].compact.join(" ")
28
28
  result += trademark_symbol if trademark
29
- result
29
+ annotate_plain_render(result, **opts)
30
30
  end
31
31
  end
32
32
  end
@@ -26,13 +26,13 @@ module Pubid
26
26
  # @param trademark [Boolean] append the IEEE trademark symbol (™/®)
27
27
  # @return [String] C2-YYYY format (bare "C2" when the year is absent,
28
28
  # e.g. a partial reference produced by `#exclude(:year)`)
29
- def to_s(trademark: false)
29
+ def to_s(trademark: false, **opts)
30
30
  # Unlike the year-first NESC editions, this form renders a code, so
31
31
  # the mark goes after it and before the year — as everywhere else.
32
32
  code_part = "C2"
33
33
  code_part += trademark_symbol if trademark
34
34
  code_part += "-#{year}" if year
35
- "#{code_part} National Electrical Safety Code"
35
+ annotate_plain_render("#{code_part} National Electrical Safety Code", **opts)
36
36
  end
37
37
  end
38
38
  end
@@ -30,8 +30,11 @@ module Pubid
30
30
  # Format: "IEEE/ASTM SI 10-1997" (published)
31
31
  # or: "IEEE/ASTM PSI 10/D2, October 2015" (draft)
32
32
 
33
- # Use proper Draft component (Lutaml::Model object)
34
- attr_accessor :draft_obj
33
+ # NOTE: no `attr_accessor :draft_obj` here — it would shadow the
34
+ # base's lazy `draft_obj` reader (base.rb), which rebuilds the
35
+ # component from the `draft` attribute after from_hash. The plain
36
+ # accessor made every deserialized PSI draft render (and re-emit
37
+ # in to_hash) without its "/D<n>".
35
38
  end
36
39
  end
37
40
  end
@@ -44,7 +44,7 @@ module Pubid
44
44
  builder.build(parsed)
45
45
  end
46
46
 
47
- def to_s(trademark: false)
47
+ def to_s(trademark: false, **opts)
48
48
  # Year comes FIRST in IRE format - render as 2-digit short year
49
49
  result = [(short_year if year), publisher, type, code&.to_s]
50
50
  .compact.join(" ")
@@ -56,7 +56,7 @@ module Pubid
56
56
  code_obj&.prefix,
57
57
  publishers: [publisher])
58
58
  end
59
- result
59
+ annotate_plain_render(result, **opts)
60
60
  end
61
61
 
62
62
  private
@@ -7,7 +7,7 @@ module Pubid
7
7
  module Ire
8
8
  # Parser for IRE (Institute of Radio Engineers) identifiers
9
9
  # Historical period: 1912-1963 (merged into IEEE in 1963)
10
- class Parser < Parslet::Parser
10
+ class Parser < ::Pubid::Parser::Grammar
11
11
  # Basic building blocks
12
12
  rule(:space) { str(" ") }
13
13
  rule(:space?) { space.maybe }
@@ -73,7 +73,7 @@ module Pubid
73
73
  end
74
74
 
75
75
  # Complete IRE identifier - YEAR FIRST format
76
- rule(:ire_identifier) do
76
+ rule(:ire_identifier_year_first) do
77
77
  # Format: "52 IRE 7.S2" or "1952 IRE 7.S2"
78
78
  year.as(:year) >>
79
79
  space >>
@@ -84,6 +84,24 @@ module Pubid
84
84
  (space? >> (dash | str(",")).maybe >> space? >> year_full.as(:full_year)).maybe
85
85
  end
86
86
 
87
+ # Prefix-first format: "IRE 7.S2-1952" (pubid#316 family 2) — the
88
+ # year trails the designation instead of leading the identifier.
89
+ # The renderer always emits the year-first short-year form, so this
90
+ # spelling canonicalizes onto it ("IRE 7.S2-1952" → "52 IRE 7.S2").
91
+ rule(:ire_identifier_prefix_first) do
92
+ ire_prefix >>
93
+ space >>
94
+ (ire_type >> space).maybe >>
95
+ number >>
96
+ date
97
+ end
98
+
99
+ # Year-first is tried first so the original spellings keep their
100
+ # match; prefix-first claims only what year-first cannot.
101
+ rule(:ire_identifier) do
102
+ ire_identifier_year_first | ire_identifier_prefix_first
103
+ end
104
+
87
105
  root(:ire_identifier)
88
106
  end
89
107
  end
@@ -20,7 +20,7 @@ module Pubid
20
20
  # @example Parse handbook
21
21
  # parser = Parser.new
22
22
  # result = parser.parse("2017 NESC Handbook, Premier Edition")
23
- class Parser < Parslet::Parser
23
+ class Parser < ::Pubid::Parser::Grammar
24
24
  root(:nesc_identifier)
25
25
 
26
26
  rule(:space) { str(" ") }
@@ -7,7 +7,7 @@ module Pubid
7
7
  # Parser class for IEEE identifiers
8
8
  # Single Responsibility: Parsing IEEE identifier syntax
9
9
  # Note: IEEE is extremely complex with many edge cases
10
- class Parser < Parslet::Parser
10
+ class Parser < ::Pubid::Parser::Grammar
11
11
  # Basic building blocks
12
12
  rule(:space) { str(" ") }
13
13
  rule(:space?) { space.maybe }
@@ -611,19 +611,31 @@ module Pubid
611
611
  # "-YYYY[-MM]" date (instead of ":YYYY"), multi-digit committee-draft
612
612
  # stage codes (CD1..CD4) plus CDV, and a wider set of joint publishers.
613
613
  # (roadmap items 2/3, phase 1). longest publisher token first.
614
- (str("ISO/IEC/IEEE") | str("IEEE/ISO/IEC") | str("IEEE/IEC/ISO") |
615
- str("ISO/IEEE") | str("IEC/IEEE") | str("IEEE/IEC") | str("ISO/IEC") |
616
- str("IEEE")).as(:joint_publishers) >>
617
- space >>
618
- # ISO stage codes: FDIS, FCD, CDV; DIS/CD with an optional round digit
619
- # (DIS2, CD1..CD4); WD/PWI/NP. (FCD before FDIS is fine — distinct.)
620
- (str("FDIS") | str("FCD") | str("CDV") |
621
- (str("DIS") >> digit.maybe) |
622
- (str("CD") >> digit.maybe) |
623
- str("WD") | str("PWI") | str("NP")).as(:iso_stage) >>
624
- # optional " Std" noise word after the stage (e.g. "FDIS Std P15288")
625
- (space >> str("Std")).maybe >>
626
- space >>
614
+ # The stage is OPTIONAL ONLY for ISO-led joint prefixes - the
615
+ # stage-less PUBLISHED form (pubid#317): "ISO/IEC/IEEE 26511:2018",
616
+ # "ISO/IEEE 11073-20101:2004(E)". Every other prefix keeps the
617
+ # stage REQUIREMENT so this rule cannot steal what belongs to
618
+ # iec_ieee_copublished ("IEC/IEEE 60076-2016"), the bare-IEEE
619
+ # project rules ("IEEE P802.16/D-3") or the ISO flavor
620
+ # ("ISO/IEC <n>:<year>").
621
+ iso_led = (str("ISO/IEC/IEEE") | str("IEEE/ISO/IEC") |
622
+ str("IEEE/IEC/ISO") | str("ISO/IEEE"))
623
+ staged_only = (str("IEC/IEEE") | str("IEEE/IEC") |
624
+ str("ISO/IEC") | str("IEEE"))
625
+ # ISO stage codes: FDIS, FCD, CDV; DIS/CD with an optional round
626
+ # digit (DIS2, CD1..CD4); WD/PWI/NP.
627
+ iso_stage = (str("FDIS") | str("FCD") | str("CDV") |
628
+ (str("DIS") >> digit.maybe) |
629
+ (str("CD") >> digit.maybe) |
630
+ str("WD") | str("PWI") | str("NP")).as(:iso_stage)
631
+ std_noise = (space >> str("Std")).maybe
632
+ # The empty-string marker distinguishes the stage-less PUBLISHED
633
+ # tree in the builder (absence of :iso_stage alone means IEEE format).
634
+ ((iso_led.as(:joint_publishers) >> space >>
635
+ (iso_stage >> std_noise >> space |
636
+ str("").as(:iso_published) >> space.maybe)) |
637
+ (staged_only.as(:joint_publishers) >> space >>
638
+ iso_stage >> std_noise >> space)) >>
627
639
  str("P").maybe >> # optional project marker on the number
628
640
  digits.as(:number) >>
629
641
  # part must not swallow the trailing year (year_digits.absent?)
@@ -641,12 +653,33 @@ module Pubid
641
653
  # historical "…/D-3-2017" onto the number as "…-2017/D3", so by the
642
654
  # time this rule runs the draft usually trails the date (bucket 5);
643
655
  # a date-less "/D-4" keeps its hyphen (bucket 7), hence dash.maybe.
656
+ # A text date may trail the DRAFT itself (pubid#216:
657
+ # "CD P26515/D1, March 2017", "FDIS P15289/D3, 2017") — distinct
658
+ # keys (:draft_month/:draft_year), because a plain :month/:year
659
+ # here collides with the date clause above and parslet drops the
660
+ # subtree with a "Duplicate subtrees" warning.
644
661
  (slash >> str("D") >> dash.maybe >>
645
- match('[0-9.]').repeat(1).as(:draft_version)).maybe >>
662
+ match('[0-9.]').repeat(1).as(:draft_version) >>
663
+ (((comma | space) >> month_name.as(:draft_month) >> space >>
664
+ year_digits.as(:draft_year)) |
665
+ (comma >> year_digits.as(:draft_year))).maybe).maybe >>
666
+ # Optional amendment tail (pubid#317:
667
+ # "8802-11:2012/Amd.1:2014(E)") - the flat tree keys reuse
668
+ # build_flat_amendment.
669
+ (slash >> str("Amd") >> (dot | space).maybe >>
670
+ digits.as(:amd_number) >>
671
+ ((str(":") | dash) >> year_digits.as(:amd_year)).maybe).maybe >>
646
672
  # Optional edition, from relaton's "/E-<n>" suffix normalized to
647
673
  # "Edition <n>.0[ YYYY]" (nil-residue hand-off item 1).
648
674
  edition.maybe >>
649
- revision_suffix.maybe
675
+ revision_suffix.maybe >>
676
+ # Published joint docs print a language marker, after either the
677
+ # year or the amendment tail: "9945:2009(E)", "...Amd.1:2014(E)".
678
+ # The crawl sometimes spaces it ("…(E), January 2017" family,
679
+ # pubid#216), and a relationship parenthetical may follow
680
+ # ("…CD2 P15288-2013-09 (Revision of …)").
681
+ space.maybe >> (str("(E)") | str("(F)")).maybe >>
682
+ parenthetical.maybe
650
683
  end
651
684
 
652
685
  # Embedded (stage-LAST) ISO-led designations: the corpus writes the ISO
@@ -701,9 +734,13 @@ module Pubid
701
734
  end
702
735
 
703
736
  # IEEE P pattern (without Std): "IEEE P1003.1..." OR just "P1003.1..." (prefix optional)
737
+ # `P` is not consumed here: the `number` rule carries its own optional
738
+ # leading P and captures it into :number ("P1003.1"), so Code.parse
739
+ # peels it as code.prefix and the project marker survives the parse —
740
+ # a bare `str("P")` swallowed it and "P1201/D0.3" came back without
741
+ # its P at all (pubid#18's data-loss complaint).
704
742
  rule(:ieee_p_identifier) do
705
743
  (str("IEEE").as(:publisher) >> space).maybe >> # Make IEEE prefix optional
706
- str("P") >> space.maybe >> # Make space after P optional
707
744
  number >>
708
745
  (part_subpart_year | edition).maybe >>
709
746
  # Pattern for /08 style drafts (digits without D prefix) - MUST come before corrigendum
@@ -759,15 +796,17 @@ module Pubid
759
796
  # status is captured (draft_status ends in a space, so "Approved Draft"
760
797
  # splits cleanly) and round-trips as the draft_status attribute; the literal
761
798
  # "Draft" stays a bare marker (dropped on render, like the plain form).
799
+ # `P` is not consumed here: the `number` rule carries its own optional
800
+ # leading P and captures it into :number ("P802.11"), so Code.parse peels
801
+ # it as code.prefix and the project marker round-trips (pubid#318) — a
802
+ # bare `str("P").maybe` here swallowed it and the render lost the P.
762
803
  rule(:ieee_draft_p_identifier) do
763
804
  (str("IEEE").as(:publisher) >> space).maybe >> # Make IEEE prefix optional
764
805
  draft_status.as(:draft_status).maybe >>
765
806
  str("Draft") >> space >>
766
- # `P` is optional — a status-word draft may carry a bare number
767
- # ("IEEE Unapproved Draft 802.1ah/D4.2"), mirroring
768
- # ieee_approved_draft_identifier's str("P").maybe. The `number` rule
807
+ # A status-word draft may carry a bare number
808
+ # ("IEEE Unapproved Draft 802.1ah/D4.2"). The `number` rule
769
809
  # already accepts the bare forms (802.1ah, C57.15, 11073-10471).
770
- str("P").maybe >>
771
810
  number >>
772
811
  (part_subpart_year | edition).maybe >>
773
812
  # Trailing "Month YYYY" date under distinct keys so it never collides
@@ -836,6 +875,11 @@ module Pubid
836
875
  # Year-first pattern: "52 IRE 7.S2" or "60 IRE 28 PS7"
837
876
  ((match("[1-6]") >> digit >> space >> str("IRE")) | # 2-digit year format
838
877
  (str("19") >> digit.repeat(2, 2) >> space >> str("IRE"))) |
878
+ # Prefix-first pattern: "IRE 7.S2-1952" (pubid#316 family 2) —
879
+ # IRE then a designation (digits or a type word). Loose on
880
+ # purpose: the sub-parser is strict, and a failed delegation just
881
+ # falls through to the remaining alternatives.
882
+ (str("IRE") >> space >> (digits | str("Standard") | str("Std") | str("Trans"))) |
839
883
  # IEEE-IRE transitional pattern
840
884
  (str("IEEE-IRE") >> space)
841
885
  ).present? >>
@@ -861,23 +905,47 @@ module Pubid
861
905
  Nesc::Parser.new.nesc_identifier.as(:nesc)
862
906
  end
863
907
 
908
+ # Draft notation for PSI (e.g., /D2, /D3, rawbib's /D-2)
909
+ rule(:psi_draft) do
910
+ slash >> str("D") >> dash.maybe >> digits.as(:draft_version)
911
+ end
912
+
913
+ # Date on an SI/PSI identifier: ", Month Year" or "-YEAR[-MM]". The
914
+ # comma branch also accepts a NUMERIC month and a DASH before the
915
+ # year — the renderer emits ", 05 2010" for a numeric-month draft,
916
+ # and the preprocessing gsub rewrites a trailing " <digits> <year>"
917
+ # to "<digits>-<year>" (the same load-bearing coupling draft_date
918
+ # documents), so the re-parse must read its own rendering back.
919
+ rule(:psi_date) do
920
+ (comma >> (month_name | month_numeric).as(:month) >> (space | dash) >>
921
+ year_digits.as(:year)) |
922
+ (dash >> year_digits.as(:year) >>
923
+ (dash >> digit.repeat(2, 2).as(:month)).maybe)
924
+ end
925
+
864
926
  # IEEE/ASTM SI/PSI (Système International) patterns
865
927
  # SI = Published metric system standard
866
928
  # PSI = Proposed SI (draft)
929
+ # Rawbib spellings (pubid#316 family 3): a bare "IEEE" publisher, a dot
930
+ # separator after the type ("PSI.10", "SI 10.1997"), relaton's
931
+ # hyphenated "/D-<n>" draft, and a dash year-month ("-2010-05").
867
932
  rule(:ieee_astm_si_psi) do
868
- str("IEEE/ASTM").as(:publishers) >>
933
+ (str("IEEE/ASTM") | str("IEEE")).as(:publishers) >>
869
934
  space >>
870
935
  (str("PSI") | str("SI")).as(:si_type) >>
871
- space >>
936
+ (space | str(".")) >>
872
937
  digits.as(:number) >>
873
- # Draft notation for PSI (e.g., /D2, /D3)
874
- (slash >> str("D") >> digits.as(:draft_version)).maybe >>
875
- # Year with optional month
938
+ # Glued ".YEAR" edition date ("IEEE/ASTM SI 10.1997")
939
+ (str(".") >> year_digits.as(:year)).maybe >>
940
+ # The date and the draft appear in EITHER order: the legacy
941
+ # spelling puts the draft first ("PSI 10/D2, October 2015"),
942
+ # while normalize_relaton_suffixes repositions the rawbib
943
+ # hyphenated form onto the number ("PSI 10/D-3-2010" →
944
+ # "PSI 10-2010/D3"). Draft-first is tried first so the legacy
945
+ # comma-date keeps its original match; each side is optional so
946
+ # a date-only or draft-less form still parses.
876
947
  (
877
- # Format: ", Month Year"
878
- (comma >> month_name.as(:month) >> space >> year_digits.as(:year)) |
879
- # Format: "-YEAR"
880
- (dash >> year_digits.as(:year))
948
+ (psi_draft >> psi_date.maybe) | (psi_date >> psi_draft.maybe)
881
949
  ).maybe >>
882
950
  # Optional parenthetical (revision relationships)
883
951
  parenthetical.maybe
@@ -1159,12 +1227,15 @@ module Pubid
1159
1227
  # A "\d+" right after "Rev" both selects the numbered subset and keeps
1160
1228
  # these off the English word "Revision". Three source positions:
1161
1229
  # after a draft : "PC37.30.2/D043 Rev 18" -> ".../D043/R-18"
1230
+ # The draft captures accept relaton's hyphenated "/D-<n>" spelling
1231
+ # (pubid#316): without the "-?" the before-draft regex matched only
1232
+ # "/D" of "/D-3" and emitted the garbage "/D/R-2-3-2008-02".
1162
1233
  cleaned = cleaned.sub(
1163
- %r{(/D[0-9A-Za-z.]*)\s+[Rr][Ee][Vv]\s*(\d+)}, '\1/R-\2'
1234
+ %r{(/D-?[0-9A-Za-z.]+)\s+[Rr][Ee][Vv]\s*(\d+)}, '\1/R-\2'
1164
1235
  )
1165
1236
  # before a draft: "P802.16Rev2/D3" -> "P802.16/D3/R-2"
1166
1237
  cleaned = cleaned.sub(
1167
- %r{[-/_.]?\s?[Rr][Ee][Vv][-\s]?(\d+)(/D[0-9A-Za-z.]*)}, '\2/R-\1'
1238
+ %r{[-/_.]?\s?[Rr][Ee][Vv][-\s]?(\d+)(/D-?[0-9A-Za-z.]+)}, '\2/R-\1'
1168
1239
  )
1169
1240
  # no draft, trailing: "P1722-rev1" -> "P1722/R-1"
1170
1241
  cleaned = cleaned.sub(
@@ -1176,7 +1247,7 @@ module Pubid
1176
1247
  # became "/R-<n>", so these regexes only see the lettered residue.
1177
1248
  # Revision token that PRECEDES a draft: drop it (keep the /D…).
1178
1249
  cleaned = cleaned.sub(
1179
- %r{[-/_.]?\s?[Rr][Ee][Vv][-\s]?[A-Za-z0-9]+(?=/D[0-9])},
1250
+ %r{[-/_.]?\s?[Rr][Ee][Vv][-\s]?[A-Za-z0-9]+(?=/D-?[0-9A-Za-z])},
1180
1251
  "",
1181
1252
  )
1182
1253
  # Trailing revision glued to the number with no draft ("P802.11REVmb");
@@ -1185,6 +1256,105 @@ module Pubid
1185
1256
  cleaned.sub(%r{(\d)[Rr][Ee][Vv][A-Za-z0-9]+\s*\z}, '\1')
1186
1257
  end
1187
1258
 
1259
+ # Rewrite the mechanical spellings of the joint ISO-stage forms that no
1260
+ # grammar branch reaches (pubid#216 residuals). Two safety rules keep
1261
+ # this from stealing inputs other rules already parse:
1262
+ # 1. only BROKEN separators fire — underscore/space glue between
1263
+ # number, draft and stage, a slash-SPACE before the stage, a
1264
+ # dash/underscore month after it. A plain "/FDIS" tail is
1265
+ # natively accepted by ieee_p_identifier (fdraft) and the
1266
+ # stage-LAST embedded rule, so it is never rewritten.
1267
+ # 2. the number carries at most ONE part — joint_development_iso_format
1268
+ # reads a single optional part, while ieee_p_identifier takes
1269
+ # multi-part numbers ("P62271-37-013"); a rewrite that moved a
1270
+ # multi-part number onto the joint rule would break it.
1271
+ def self.normalize_joint_stage_spellings(cleaned)
1272
+ # The separator (slash-optional-space or a space) is INSIDE the
1273
+ # capture so a rewrite re-emits it — an uncaptured separator was
1274
+ # silently eaten, gluing the publisher to the stage.
1275
+ pubs = %r{((?:ISO/IEC/IEEE|IEEE/ISO/IEC|IEEE/IEC/ISO|ISO/IEEE|IEC/IEEE|IEEE/IEC|ISO/IEC|IEEE)(?:/ ?| ))}
1276
+ stage = /(FDIS|FCD|CDV|DIS\d?|CD\d?|WD|PWI|NP)/
1277
+ num = /(P?\d+(?:[.-]\d+)?)/
1278
+ # What the joint grammar can finish reading AFTER the stage: the end
1279
+ # of the string, a (comma-)month-year date, or a dash-year[-month].
1280
+ # Captured and re-emitted so a rewrite never drops the date.
1281
+ tail = /(,? (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Sept|Oct|Nov|Dec)[a-z]* \d{4}|\z|-\d{4}(?:-\d\d)?)/
1282
+
1283
+ # Draft glued to the number with an underscore: "P15289_D3" →
1284
+ # "P15289/D3" (a digit must precede the underscore; "_FDIS" is the
1285
+ # stage, not a draft, and is already slash-rewritten earlier).
1286
+ cleaned = cleaned.gsub(%r{(\d)_D(\d)}, '\1/D\2')
1287
+
1288
+ # Crawl typo: a comma with no space before a month (",March 2021").
1289
+ # In these identifiers a comma is always a date separator, so
1290
+ # spacing it is safe.
1291
+ cleaned = cleaned.sub(/,(?=[A-Za-z])/, ", ")
1292
+
1293
+ # Stage AFTER the draft, both glued with underscores (the early
1294
+ # stage gsub already turned "_CD" into "/CD"):
1295
+ # "…P24748-3/D3/FDIS, April 2020 (E)" → "…FDIS P24748-3/D3, …"
1296
+ cleaned = cleaned.sub(
1297
+ %r{\A#{pubs}#{num}/(D[\d.]+)/ ?#{stage}\b},
1298
+ '\1\4 \2/\3',
1299
+ )
1300
+
1301
+ # Slash-SPACE before the stage ("…P16085/ FDIS, August 2020" —
1302
+ # ieee_p's fdraft has no inner space, so this spelling has no other
1303
+ # home): → "…FDIS P16085, August 2020"
1304
+ cleaned = cleaned.sub(
1305
+ %r{\A#{pubs}#{num}/ #{stage}#{tail}},
1306
+ '\1\3 \2\4',
1307
+ )
1308
+
1309
+ # A space-year after a slash-stage ("…P26511.2/FDIS 2018" — no rule
1310
+ # reads "/FDIS 2018"): → "…FDIS P26511.2-2018" (dash-year clause).
1311
+ cleaned = cleaned.sub(
1312
+ %r{\A#{pubs}#{num}/#{stage} (\d{4})\b},
1313
+ '\1\3 \2-\4',
1314
+ )
1315
+
1316
+ # Stage separated from the number by a SPACE ("IEC/IEEE P63113 CD4,
1317
+ # April 2019"): → "IEC/IEEE CD4 P63113, April 2019". The tail guard
1318
+ # keeps "…FDIS March 2019" (space-month-year, readable after the
1319
+ # swap) while leaving prose suffixes alone.
1320
+ cleaned = cleaned.sub(
1321
+ %r{\A#{pubs}#{num} #{stage}#{tail}},
1322
+ '\1\3 \2\4',
1323
+ )
1324
+
1325
+ # The same space-stage with a space-DRAFT chained after it
1326
+ # ("IEC/IEEE P60980-344 CDV D1, June 2019"): → stage-first with a
1327
+ # slash draft ("…CDV P60980-344/D1, June 2019").
1328
+ cleaned = cleaned.sub(
1329
+ %r{\A#{pubs}#{num} #{stage} (D\d+)\b},
1330
+ '\1\3 \2/\4',
1331
+ )
1332
+
1333
+ # Slash-stage carrying a dash-date ("…P15288/CD2-2013-09 …" — the
1334
+ # dash-year tail is what the joint grammar reads after the number, so
1335
+ # this cannot steal the plain "/FDIS, June 2021" spellings
1336
+ # ieee_p_identifier finishes): → "…CD2 P15288-2013-09 …"
1337
+ cleaned = cleaned.sub(
1338
+ %r{\A#{pubs}#{num}/ ?#{stage}(-\d{4}(?:-\d\d)?)},
1339
+ '\1\3 \2\4',
1340
+ )
1341
+
1342
+ # Draft separated from a stage-first number by a SPACE:
1343
+ # "…DIS P11073-10418 D13, January 2011" → "…DIS P11073-10418/D13, …"
1344
+ cleaned = cleaned.sub(
1345
+ %r{\A#{pubs}#{stage} #{num}/? ?(D\d+)\b},
1346
+ '\1\2 \3/\4',
1347
+ )
1348
+
1349
+ # NOT rewritten: a month glued to the stage with a dash/underscore
1350
+ # ("…/FDIS_Dec 2012", "…/FDIS-Dec 2015"). Even routed onto the joint
1351
+ # grammar, the joint render of a month+draft is itself not yet
1352
+ # idempotent, and the bare-IEEE fdraft path drops the stage and the
1353
+ # date from to_hash — rewriting would trade a visible parse failure
1354
+ # for silent garbage. Deferred with the pubid#216 semantic residue.
1355
+ cleaned
1356
+ end
1357
+
1188
1358
  def self.parse(string)
1189
1359
  # Strip .pdf extension if present (Pattern 3: File Extensions)
1190
1360
  cleaned = string.sub(/\.pdf$/i, "")
@@ -1265,6 +1435,11 @@ module Pubid
1265
1435
  /\b(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Sept|Oct|Nov|Dec)(\d{4})\b/, '\1 \2'
1266
1436
  )
1267
1437
 
1438
+ # Rewrite the joint ISO-stage glued spellings (pubid#216) AFTER the
1439
+ # month unglue above, so "Dec2015" is already spaced when the
1440
+ # dash-month rewrite looks for it.
1441
+ cleaned = normalize_joint_stage_spellings(cleaned)
1442
+
1268
1443
  # NEW: Convert IEC/IEEE space-separated to semicolon format
1269
1444
  # Pattern: "IEC 61523-3 First edition 2004-09; IEEE 1497" → already semicolon
1270
1445
  # Pattern: "IEC 62539 First Edition 2007-07 IEEE 930" → needs semicolon
@@ -0,0 +1,46 @@
1
+ # frozen_string: true
2
+
3
+ module Pubid
4
+ module Ieee
5
+ # Renders the IEEE PROJECT DESIGNATOR — the short form IEEE itself uses
6
+ # for draft documents ("P1201/D0.3", "P10000/D1.2"): the project marker,
7
+ # the number with its parts, and the draft. No publisher, no type word,
8
+ # no date (pubid#18: metanorma-ieee builds these for drafts and had to
9
+ # post-process the full rendering).
10
+ class ProjectRenderer
11
+ def initialize(id)
12
+ @id = id
13
+ end
14
+
15
+ def render(context: nil, **_opts)
16
+ id = @id
17
+ body = if id.respond_to?(:code_obj) && id.code_obj
18
+ id.code_obj.to_s
19
+ else
20
+ id.number.to_s
21
+ end
22
+ body = "P#{body}" if project?(id) && !body.start_with?("P")
23
+
24
+ draft = id.draft_obj if id.respond_to?(:draft_obj)
25
+ body += "/D#{draft.version}" if draft&.version
26
+
27
+ body
28
+ end
29
+
30
+ private
31
+
32
+ # A project identifier carries its marker in one of three places
33
+ # depending on which grammar path built it: the code prefix (the bare
34
+ # "P1201/D0.3" spelling), the "P" type code ("IEEE P802.16/D-3"), or a
35
+ # typed stage with project_status (the D1-D6 draft stages).
36
+ def project?(id)
37
+ return true if id.code_obj&.prefix.to_s == "P"
38
+ return true if id.type.to_s == "P"
39
+
40
+ id.respond_to?(:typed_stage) &&
41
+ id.typed_stage&.respond_to?(:project_status) &&
42
+ id.typed_stage.project_status
43
+ end
44
+ end
45
+ end
46
+ end
@@ -115,17 +115,16 @@ module Pubid
115
115
  parts << id.draft_status if id.draft_status
116
116
 
117
117
  # Type - only render for IEEE/AIEE publishers, and only for non-projects.
118
- # An unapproved draft is not yet a standard, so per IEEE guidance the
119
- # "Std" token is dropped from the type when draft_status is "Unapproved"
120
- # (e.g. "Draft Std" -> "Draft", "Std" -> "").
118
+ # A status word ("Unapproved") already marks the document as a draft,
119
+ # so the WHOLE type word is dropped — not just "Std" (pubid#318: the
120
+ # half-strip left "Draft" in the render, the re-parse dropped it, and
121
+ # to_s needed three rounds to converge while losing the type).
121
122
  should_render_type = id.publisher&.match?(/^(IEEE|AIEE)/)
122
123
 
123
- if should_render_type && !id.typed_stage&.project_status && id.type && !id.type.to_s.strip.empty? && id.type != "P"
124
+ if should_render_type && !id.typed_stage&.project_status && id.type && !id.type.to_s.strip.empty? && id.type != "P" &&
125
+ !id.draft_status.to_s.match?(/unapproved/i)
124
126
  type_str = id.type.dup
125
127
  type_str = type_str.sub(/^P/, "") if type_str.start_with?("P")
126
- if id.draft_status.to_s.match?(/unapproved/i)
127
- type_str = type_str.gsub(/\bStd\b/i, "").squeeze(" ").strip
128
- end
129
128
  parts << type_str unless type_str.strip.empty?
130
129
  end
131
130
 
@@ -320,7 +319,11 @@ module Pubid
320
319
 
321
320
  secondary_code = id.secondary_identifier.code.to_s
322
321
  if secondary_code.start_with?("C") && secondary_code.match?(/^C\d+\./)
323
- "#{primary}/#{secondary}"
322
+ # A cross-reference prints the bare second designation after the
323
+ # slash — "IEEE Std 1299/C62.22.1-1996" — not a second full
324
+ # reference, so this branch must not reuse `secondary`, which
325
+ # carries its own "IEEE Std " prefix.
326
+ "#{primary}/#{secondary_code}"
324
327
  else
325
328
  "#{primary} and #{secondary}"
326
329
  end
@@ -17,7 +17,8 @@ module Pubid
17
17
  cc = code_component
18
18
  parts << cc if cc
19
19
 
20
- parts << identifier.year if identifier.year
20
+ yc = year_component
21
+ parts << yc if yc
21
22
 
22
23
  dc = draft_component
23
24
  parts << dc if dc
@@ -126,8 +127,7 @@ module Pubid
126
127
  end
127
128
 
128
129
  def publisher_component
129
- pub = identifier.publisher || "IEEE"
130
- pub = pub.to_s.downcase
130
+ pub = normalized_publisher
131
131
 
132
132
  if identifier.copublisher&.any?
133
133
  copubs = identifier.copublisher.map(&:to_s).map(&:downcase)
@@ -137,6 +137,18 @@ module Pubid
137
137
  pub
138
138
  end
139
139
 
140
+ # A dual-published document has TWO publishers, and
141
+ # DualPublished#publisher returns them as an Array — which `to_s`
142
+ # rendered into the URN as a literal Ruby array literal, brackets and
143
+ # quotes included: `urn:ieee:["ieee", "asme"]`. Join them the same way a
144
+ # copublisher list is joined above.
145
+ def normalized_publisher
146
+ names = Array(identifier.publisher)
147
+ .map { |p| p.to_s.downcase }.reject(&:empty?)
148
+
149
+ names.empty? ? "ieee" : names.join("-")
150
+ end
151
+
140
152
  def type_component
141
153
  return nil unless identifier.type
142
154
 
@@ -152,6 +164,23 @@ module Pubid
152
164
  identifier.code_obj.to_s
153
165
  end
154
166
 
167
+ # A multi-designation wrapper (AdoptedStandard, DualPublished,
168
+ # MultiNumberedIdentifier) carries no year of its own — the year sits on
169
+ # the designation it wraps — so the URN dropped it and two editions of
170
+ # one document produced the SAME urn: "AIEE No 15-1944" and
171
+ # "AIEE No 15-1959" both gave urn:ieee:aiee:15.
172
+ #
173
+ # Fixed here, in the one place that reads the year, rather than by
174
+ # defining a `#year` method on each wrapper: `year` is a lutaml attribute
175
+ # on the base and a method of that name would shadow its generated
176
+ # accessor. `#root` already walks to the wrapped designation.
177
+ def year_component
178
+ return identifier.year if identifier.year
179
+
180
+ root = identifier.root
181
+ root.equal?(identifier) ? nil : root.year
182
+ end
183
+
155
184
  def draft_component
156
185
  return nil unless identifier.draft_obj
157
186
 
data/lib/pubid/ieee.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "pubid"
3
4
  module Pubid
4
5
  module Ieee
5
6
  extend Pubid::PrefixesSupport
@@ -19,6 +20,7 @@ module Pubid
19
20
  autoload :Parser, "#{__dir__}/ieee/parser"
20
21
  autoload :PreParser, "#{__dir__}/ieee/pre_parser"
21
22
  autoload :Compaction, "#{__dir__}/ieee/compaction"
23
+ autoload :ProjectRenderer, "#{__dir__}/ieee/project_renderer"
22
24
  autoload :Renderer, "#{__dir__}/ieee/renderer"
23
25
  autoload :TypedStages, "#{__dir__}/ieee/typed_stages"
24
26
  autoload :UrnGenerator, "#{__dir__}/ieee/urn_generator"
@@ -42,8 +44,13 @@ module Pubid
42
44
 
43
45
  class << self
44
46
  def parse(input)
47
+ unless input.is_a?(String)
48
+ raise Pubid::Errors::InvalidInputError,
49
+ Pubid::INPUT_NOT_A_STRING_MESSAGE
50
+ end
51
+
45
52
  if input.length > Pubid::MAX_INPUT_LENGTH
46
- raise ArgumentError, Pubid::INPUT_TOO_LONG_MESSAGE
53
+ raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
47
54
  end
48
55
 
49
56
  Identifier.parse(input)
@@ -156,3 +163,4 @@ Pubid::Registry.register(:ieee, Pubid::Ieee)
156
163
  # Per-flavor format registry: inherits global formats, overrides :human
157
164
  Pubid::Ieee::Identifier.format_registry = Pubid::FormatRegistry.new(parent: Pubid::Identifier.format_registry)
158
165
  Pubid::Ieee::Identifier.format_registry.register(:human, renderer: Pubid::Ieee::Renderer)
166
+ Pubid::Ieee::Identifier.format_registry.register(:project, renderer: Pubid::Ieee::ProjectRenderer)