pubid 2.0.0.pre.alpha.7 → 2.0.0.pre.alpha.9

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 (485) hide show
  1. checksums.yaml +4 -4
  2. data/README.adoc +9 -50
  3. data/data/bipm/update_codes.yaml +9 -0
  4. data/data/iec/update_codes.yaml +3 -0
  5. data/data/ieee/update_codes.yaml +107 -1
  6. data/data/nist/series.yaml +155 -0
  7. data/data/nist/update_codes.yaml +8 -0
  8. data/lib/pubid/adobe/builder.rb +52 -0
  9. data/lib/pubid/adobe/identifier.rb +49 -0
  10. data/lib/pubid/adobe/identifiers/publication.rb +31 -0
  11. data/lib/pubid/adobe/identifiers/tech_note.rb +32 -0
  12. data/lib/pubid/adobe/identifiers.rb +10 -0
  13. data/lib/pubid/adobe/parser.rb +128 -0
  14. data/lib/pubid/adobe/renderer.rb +39 -0
  15. data/lib/pubid/adobe/urn_generator.rb +42 -0
  16. data/lib/pubid/adobe/urn_parser.rb +69 -0
  17. data/lib/pubid/adobe.rb +60 -0
  18. data/lib/pubid/amca/builder.rb +2 -2
  19. data/lib/pubid/amca/identifier.rb +1 -3
  20. data/lib/pubid/amca/identifiers/base.rb +1 -6
  21. data/lib/pubid/amca/identifiers/interpretation.rb +1 -1
  22. data/lib/pubid/amca/identifiers/publication.rb +1 -1
  23. data/lib/pubid/amca/identifiers/standard.rb +1 -1
  24. data/lib/pubid/amca/identifiers.rb +0 -1
  25. data/lib/pubid/amca/single_identifier.rb +1 -1
  26. data/lib/pubid/amca.rb +3 -3
  27. data/lib/pubid/ashrae/builder.rb +22 -22
  28. data/lib/pubid/ashrae/identifier.rb +3 -4
  29. data/lib/pubid/ashrae/identifiers/addenda_package.rb +1 -1
  30. data/lib/pubid/ashrae/identifiers/addendum.rb +1 -1
  31. data/lib/pubid/ashrae/identifiers/base.rb +14 -8
  32. data/lib/pubid/ashrae/identifiers/combined_addenda.rb +1 -1
  33. data/lib/pubid/ashrae/identifiers.rb +0 -1
  34. data/lib/pubid/ashrae/parser.rb +14 -9
  35. data/lib/pubid/ashrae/renderer.rb +19 -19
  36. data/lib/pubid/ashrae/single_identifier.rb +1 -1
  37. data/lib/pubid/ashrae/supplement_identifier.rb +6 -6
  38. data/lib/pubid/ashrae.rb +3 -3
  39. data/lib/pubid/bipm/builder.rb +107 -0
  40. data/lib/pubid/bipm/identifier.rb +160 -0
  41. data/lib/pubid/bipm/identifiers/committee_document.rb +24 -0
  42. data/lib/pubid/bipm/identifiers/guide.rb +24 -0
  43. data/lib/pubid/bipm/identifiers/meeting.rb +41 -0
  44. data/lib/pubid/bipm/identifiers/mep.rb +21 -0
  45. data/lib/pubid/bipm/identifiers/metrologia_article.rb +24 -0
  46. data/lib/pubid/bipm/identifiers/si_brochure.rb +20 -0
  47. data/lib/pubid/bipm/identifiers.rb +15 -0
  48. data/lib/pubid/bipm/parser.rb +184 -0
  49. data/lib/pubid/bipm/renderer.rb +110 -0
  50. data/lib/pubid/bipm/urn_generator.rb +58 -0
  51. data/lib/pubid/bipm/urn_parser.rb +52 -0
  52. data/lib/pubid/bipm.rb +86 -0
  53. data/lib/pubid/bsi/builder.rb +14 -14
  54. data/lib/pubid/bsi/identifier.rb +0 -1
  55. data/lib/pubid/bsi/identifiers/addendum_document.rb +2 -2
  56. data/lib/pubid/bsi/identifiers/amendment.rb +4 -4
  57. data/lib/pubid/bsi/identifiers/consolidated_identifier.rb +6 -0
  58. data/lib/pubid/bsi/identifiers/corrigendum.rb +4 -4
  59. data/lib/pubid/bsi/identifiers/expert_commentary.rb +5 -5
  60. data/lib/pubid/bsi/identifiers/supplement_document.rb +2 -2
  61. data/lib/pubid/bsi/identifiers/value_added_publication.rb +6 -6
  62. data/lib/pubid/bsi/renderer.rb +8 -8
  63. data/lib/pubid/bsi/single_identifier.rb +48 -0
  64. data/lib/pubid/builder/base.rb +5 -3
  65. data/lib/pubid/bundled_identifier.rb +3 -3
  66. data/lib/pubid/calconnect/builder.rb +38 -0
  67. data/lib/pubid/calconnect/identifier.rb +117 -0
  68. data/lib/pubid/calconnect/identifiers/standard.rb +29 -0
  69. data/lib/pubid/calconnect/identifiers.rb +9 -0
  70. data/lib/pubid/calconnect/parser.rb +54 -0
  71. data/lib/pubid/calconnect/renderer.rb +36 -0
  72. data/lib/pubid/calconnect/urn_generator.rb +33 -0
  73. data/lib/pubid/calconnect/urn_parser.rb +34 -0
  74. data/lib/pubid/calconnect.rb +77 -0
  75. data/lib/pubid/ccsds/builder.rb +1 -1
  76. data/lib/pubid/ccsds/identifier.rb +1 -1
  77. data/lib/pubid/ccsds/identifiers/corrigendum.rb +2 -2
  78. data/lib/pubid/ccsds/supplement_identifier.rb +6 -6
  79. data/lib/pubid/ccsds/urn_generator.rb +3 -3
  80. data/lib/pubid/cen_cenelec/builder.rb +46 -12
  81. data/lib/pubid/cen_cenelec/identifiers/amendment.rb +2 -2
  82. data/lib/pubid/cen_cenelec/identifiers/consolidated_identifier.rb +6 -0
  83. data/lib/pubid/cen_cenelec/identifiers/corrigendum.rb +2 -2
  84. data/lib/pubid/cen_cenelec/identifiers/european_norm.rb +60 -3
  85. data/lib/pubid/cen_cenelec/identifiers/fragment.rb +2 -2
  86. data/lib/pubid/cen_cenelec/renderer.rb +5 -5
  87. data/lib/pubid/cen_cenelec/supplement_identifier.rb +5 -5
  88. data/lib/pubid/cen_cenelec/urn_generator.rb +1 -1
  89. data/lib/pubid/cen_cenelec.rb +76 -72
  90. data/lib/pubid/cie/builder.rb +139 -24
  91. data/lib/pubid/cie/identifier.rb +7 -3
  92. data/lib/pubid/cie/identifiers/bundle.rb +32 -4
  93. data/lib/pubid/cie/identifiers/code_attributes.rb +52 -0
  94. data/lib/pubid/cie/identifiers/conference.rb +9 -4
  95. data/lib/pubid/cie/identifiers/corrigendum.rb +17 -23
  96. data/lib/pubid/cie/identifiers/dual_published.rb +4 -4
  97. data/lib/pubid/cie/identifiers/identical.rb +7 -7
  98. data/lib/pubid/cie/identifiers/joint_published.rb +8 -8
  99. data/lib/pubid/cie/identifiers/proceedings.rb +39 -0
  100. data/lib/pubid/cie/identifiers/standard.rb +33 -7
  101. data/lib/pubid/cie/identifiers/supplement.rb +24 -22
  102. data/lib/pubid/cie/identifiers/tutorial_bundle.rb +6 -2
  103. data/lib/pubid/cie/identifiers.rb +2 -0
  104. data/lib/pubid/cie/parser.rb +54 -5
  105. data/lib/pubid/cie/single_identifier.rb +40 -1
  106. data/lib/pubid/cie/supplement_identifier.rb +29 -16
  107. data/lib/pubid/cie/urn_generator.rb +11 -8
  108. data/lib/pubid/cie.rb +0 -1
  109. data/lib/pubid/components/date.rb +21 -5
  110. data/lib/pubid/csa/builder.rb +1 -1
  111. data/lib/pubid/csa/composite_identifier.rb +17 -1
  112. data/lib/pubid/csa/identifier.rb +48 -3
  113. data/lib/pubid/csa/identifiers/bundled.rb +6 -0
  114. data/lib/pubid/csa/identifiers/combined.rb +6 -0
  115. data/lib/pubid/csa/identifiers/package.rb +2 -2
  116. data/lib/pubid/csa/parser.rb +6 -6
  117. data/lib/pubid/csa/wrapper_identifier.rb +17 -0
  118. data/lib/pubid/doi/builder.rb +18 -0
  119. data/lib/pubid/doi/identifier.rb +46 -0
  120. data/lib/pubid/doi/identifiers/resource.rb +30 -0
  121. data/lib/pubid/doi/identifiers.rb +9 -0
  122. data/lib/pubid/doi/parser.rb +52 -0
  123. data/lib/pubid/doi/renderer.rb +13 -0
  124. data/lib/pubid/doi.rb +55 -0
  125. data/lib/pubid/easc/builder.rb +55 -0
  126. data/lib/pubid/easc/identifier.rb +61 -0
  127. data/lib/pubid/easc/identifiers/pmg.rb +20 -0
  128. data/lib/pubid/easc/identifiers/rmg.rb +19 -0
  129. data/lib/pubid/easc/identifiers.rb +10 -0
  130. data/lib/pubid/easc/parser.rb +80 -0
  131. data/lib/pubid/easc/renderer.rb +48 -0
  132. data/lib/pubid/easc/urn_generator.rb +46 -0
  133. data/lib/pubid/easc/urn_parser.rb +57 -0
  134. data/lib/pubid/easc.rb +52 -0
  135. data/lib/pubid/ecma/builder.rb +31 -0
  136. data/lib/pubid/ecma/identifier.rb +76 -0
  137. data/lib/pubid/ecma/identifiers/memento.rb +30 -0
  138. data/lib/pubid/ecma/identifiers/standard.rb +31 -0
  139. data/lib/pubid/ecma/identifiers/technical_report.rb +31 -0
  140. data/lib/pubid/ecma/identifiers.rb +11 -0
  141. data/lib/pubid/ecma/parser.rb +41 -0
  142. data/lib/pubid/ecma/renderer.rb +46 -0
  143. data/lib/pubid/ecma/urn_generator.rb +25 -0
  144. data/lib/pubid/ecma/urn_parser.rb +35 -0
  145. data/lib/pubid/ecma.rb +67 -0
  146. data/lib/pubid/etsi/builder.rb +7 -3
  147. data/lib/pubid/etsi/identifier.rb +1 -3
  148. data/lib/pubid/etsi/identifiers/base.rb +28 -10
  149. data/lib/pubid/etsi/identifiers/etsi_standard.rb +128 -4
  150. data/lib/pubid/etsi/identifiers/supplement_identifier.rb +35 -3
  151. data/lib/pubid/etsi/identifiers.rb +0 -1
  152. data/lib/pubid/etsi/parser.rb +8 -2
  153. data/lib/pubid/etsi/renderer.rb +8 -2
  154. data/lib/pubid/etsi.rb +5 -5
  155. data/lib/pubid/export/exporter.rb +2 -1
  156. data/lib/pubid/export/flavor_exporter.rb +18 -3
  157. data/lib/pubid/gb/builder.rb +45 -0
  158. data/lib/pubid/gb/identifier.rb +64 -0
  159. data/lib/pubid/gb/identifiers/standard.rb +34 -0
  160. data/lib/pubid/gb/identifiers.rb +9 -0
  161. data/lib/pubid/gb/parser.rb +63 -0
  162. data/lib/pubid/gb/renderer.rb +35 -0
  163. data/lib/pubid/gb.rb +87 -0
  164. data/lib/pubid/gost/builder.rb +148 -0
  165. data/lib/pubid/gost/identifier.rb +49 -0
  166. data/lib/pubid/gost/identifiers/foreign_reference.rb +25 -0
  167. data/lib/pubid/gost/identifiers/harmonized.rb +39 -0
  168. data/lib/pubid/gost/identifiers/identical_adoption.rb +39 -0
  169. data/lib/pubid/gost/identifiers/interstate_standard.rb +20 -0
  170. data/lib/pubid/gost/identifiers/national_standard.rb +19 -0
  171. data/lib/pubid/gost/identifiers.rb +13 -0
  172. data/lib/pubid/gost/parser.rb +97 -0
  173. data/lib/pubid/gost/renderer.rb +48 -0
  174. data/lib/pubid/gost/urn_generator.rb +46 -0
  175. data/lib/pubid/gost/urn_parser.rb +52 -0
  176. data/lib/pubid/gost.rb +49 -0
  177. data/lib/pubid/iala/builder.rb +22 -27
  178. data/lib/pubid/iala/identifier.rb +27 -2
  179. data/lib/pubid/iala/identifiers/advice.rb +1 -1
  180. data/lib/pubid/iala/identifiers/annex.rb +14 -12
  181. data/lib/pubid/iala/identifiers/general_assembly.rb +5 -2
  182. data/lib/pubid/iala/identifiers/guideline.rb +3 -1
  183. data/lib/pubid/iala/identifiers/letter.rb +1 -1
  184. data/lib/pubid/iala/identifiers/manual.rb +3 -1
  185. data/lib/pubid/iala/identifiers/model_course.rb +3 -1
  186. data/lib/pubid/iala/identifiers/recommendation.rb +3 -1
  187. data/lib/pubid/iala/identifiers/report.rb +1 -1
  188. data/lib/pubid/iala/identifiers/resolution.rb +1 -1
  189. data/lib/pubid/iala/identifiers/standard.rb +3 -1
  190. data/lib/pubid/iala/identifiers.rb +0 -1
  191. data/lib/pubid/iala/renderer.rb +1 -1
  192. data/lib/pubid/iala/urn_generator.rb +1 -1
  193. data/lib/pubid/iala/urn_parser.rb +19 -2
  194. data/lib/pubid/iala.rb +4 -4
  195. data/lib/pubid/iana/builder.rb +20 -0
  196. data/lib/pubid/iana/identifier.rb +72 -0
  197. data/lib/pubid/iana/identifiers/registry.rb +33 -0
  198. data/lib/pubid/iana/identifiers.rb +9 -0
  199. data/lib/pubid/iana/parser.rb +38 -0
  200. data/lib/pubid/iana/renderer.rb +29 -0
  201. data/lib/pubid/iana/urn_generator.rb +15 -0
  202. data/lib/pubid/iana/urn_parser.rb +24 -0
  203. data/lib/pubid/iana.rb +69 -0
  204. data/lib/pubid/identifier.rb +210 -23
  205. data/lib/pubid/idf/builder.rb +1 -1
  206. data/lib/pubid/idf/parser.rb +2 -2
  207. data/lib/pubid/idf/renderer.rb +1 -1
  208. data/lib/pubid/idf/supplement_identifier.rb +3 -3
  209. data/lib/pubid/iec/builder.rb +35 -25
  210. data/lib/pubid/iec/identifier.rb +47 -2
  211. data/lib/pubid/iec/identifiers/base.rb +2 -2
  212. data/lib/pubid/iec/identifiers/consolidated_identifier.rb +6 -0
  213. data/lib/pubid/iec/identifiers/fragment_identifier.rb +11 -11
  214. data/lib/pubid/iec/identifiers/international_standard.rb +8 -5
  215. data/lib/pubid/iec/identifiers/sheet_identifier.rb +12 -12
  216. data/lib/pubid/iec/identifiers/technical_group.rb +30 -0
  217. data/lib/pubid/iec/identifiers/technical_report.rb +1 -1
  218. data/lib/pubid/iec/identifiers/technical_specification.rb +1 -1
  219. data/lib/pubid/iec/identifiers/vap_identifier.rb +12 -12
  220. data/lib/pubid/iec/identifiers.rb +1 -0
  221. data/lib/pubid/iec/parser.rb +51 -10
  222. data/lib/pubid/iec/renderer.rb +18 -8
  223. data/lib/pubid/iec/single_identifier.rb +1 -1
  224. data/lib/pubid/iec/supplement_identifier.rb +32 -19
  225. data/lib/pubid/iec.rb +1 -6
  226. data/lib/pubid/ieee/aiee/builder.rb +17 -3
  227. data/lib/pubid/ieee/aiee/identifier.rb +45 -61
  228. data/lib/pubid/ieee/builder.rb +341 -68
  229. data/lib/pubid/ieee/compaction.rb +121 -0
  230. data/lib/pubid/ieee/components/draft.rb +47 -7
  231. data/lib/pubid/ieee/identifier.rb +1 -3
  232. data/lib/pubid/ieee/identifiers/adopted_standard.rb +30 -5
  233. data/lib/pubid/ieee/identifiers/amendment.rb +40 -0
  234. data/lib/pubid/ieee/identifiers/base.rb +106 -13
  235. data/lib/pubid/ieee/identifiers/code_number.rb +82 -0
  236. data/lib/pubid/ieee/identifiers/conformance_identifier.rb +6 -2
  237. data/lib/pubid/ieee/identifiers/corrigendum.rb +17 -2
  238. data/lib/pubid/ieee/identifiers/csa_dual_published.rb +3 -3
  239. data/lib/pubid/ieee/identifiers/dual_identifier.rb +3 -3
  240. data/lib/pubid/ieee/identifiers/dual_published.rb +3 -3
  241. data/lib/pubid/ieee/identifiers/iec_ieee_copublished.rb +102 -5
  242. data/lib/pubid/ieee/identifiers/interpretation_identifier.rb +2 -1
  243. data/lib/pubid/ieee/identifiers/joint_development.rb +56 -11
  244. data/lib/pubid/ieee/identifiers/multi_numbered_identifier.rb +1 -1
  245. data/lib/pubid/ieee/identifiers/nesc/base.rb +90 -19
  246. data/lib/pubid/ieee/identifiers/nesc/draft.rb +20 -5
  247. data/lib/pubid/ieee/identifiers/nesc/edition.rb +32 -0
  248. data/lib/pubid/ieee/identifiers/nesc/handbook.rb +16 -5
  249. data/lib/pubid/ieee/identifiers/nesc/redline.rb +11 -2
  250. data/lib/pubid/ieee/identifiers/nesc/standard.rb +18 -3
  251. data/lib/pubid/ieee/identifiers/nesc.rb +1 -0
  252. data/lib/pubid/ieee/identifiers/parenthetical_identifier.rb +3 -3
  253. data/lib/pubid/ieee/identifiers/project_draft_identifier.rb +14 -6
  254. data/lib/pubid/ieee/identifiers/redlined_standard.rb +4 -4
  255. data/lib/pubid/ieee/identifiers/si_standard.rb +4 -1
  256. data/lib/pubid/ieee/identifiers/standard.rb +4 -1
  257. data/lib/pubid/ieee/identifiers/supplement_identifier.rb +26 -7
  258. data/lib/pubid/ieee/identifiers.rb +2 -1
  259. data/lib/pubid/ieee/ire/builder.rb +7 -6
  260. data/lib/pubid/ieee/ire/identifier.rb +48 -33
  261. data/lib/pubid/ieee/nesc/builder.rb +33 -20
  262. data/lib/pubid/ieee/nesc/parser.rb +15 -14
  263. data/lib/pubid/ieee/parser.rb +461 -43
  264. data/lib/pubid/ieee/pre_parser.rb +1 -1
  265. data/lib/pubid/ieee/renderer.rb +154 -57
  266. data/lib/pubid/ieee/typed_stages.rb +12 -1
  267. data/lib/pubid/ieee/urn_generator.rb +3 -5
  268. data/lib/pubid/ieee.rb +56 -3
  269. data/lib/pubid/ietf/builder.rb +62 -0
  270. data/lib/pubid/ietf/identifier.rb +5 -0
  271. data/lib/pubid/ietf/identifiers/base.rb +75 -0
  272. data/lib/pubid/ietf/identifiers/bcp.rb +25 -0
  273. data/lib/pubid/ietf/identifiers/fyi.rb +25 -0
  274. data/lib/pubid/ietf/identifiers/internet_draft.rb +29 -0
  275. data/lib/pubid/ietf/identifiers/rfc.rb +25 -0
  276. data/lib/pubid/ietf/identifiers/std.rb +25 -0
  277. data/lib/pubid/ietf/identifiers.rb +13 -0
  278. data/lib/pubid/ietf/parser.rb +43 -0
  279. data/lib/pubid/ietf/renderer.rb +26 -0
  280. data/lib/pubid/ietf/urn_generator.rb +23 -0
  281. data/lib/pubid/ietf/urn_parser.rb +35 -0
  282. data/lib/pubid/ietf.rb +76 -0
  283. data/lib/pubid/iho/identifier.rb +1 -3
  284. data/lib/pubid/iho/identifiers/base.rb +1 -7
  285. data/lib/pubid/iho/identifiers/bibliographic.rb +1 -1
  286. data/lib/pubid/iho/identifiers/circular_letter.rb +1 -1
  287. data/lib/pubid/iho/identifiers/miscellaneous.rb +1 -1
  288. data/lib/pubid/iho/identifiers/publication.rb +1 -1
  289. data/lib/pubid/iho/identifiers/standard.rb +1 -1
  290. data/lib/pubid/iho/identifiers.rb +0 -1
  291. data/lib/pubid/iho.rb +7 -7
  292. data/lib/pubid/isbn/builder.rb +44 -0
  293. data/lib/pubid/isbn/check_digit.rb +53 -0
  294. data/lib/pubid/isbn/identifier.rb +63 -0
  295. data/lib/pubid/isbn/identifiers/book.rb +30 -0
  296. data/lib/pubid/isbn/identifiers.rb +9 -0
  297. data/lib/pubid/isbn/parser.rb +45 -0
  298. data/lib/pubid/isbn/renderer.rb +16 -0
  299. data/lib/pubid/isbn.rb +52 -0
  300. data/lib/pubid/iso/builder.rb +9 -4
  301. data/lib/pubid/iso/combined_identifier.rb +2 -2
  302. data/lib/pubid/iso/identifier.rb +17 -1
  303. data/lib/pubid/iso/identifiers/directives_supplement.rb +8 -8
  304. data/lib/pubid/iso/normalizer.rb +2 -2
  305. data/lib/pubid/iso/parser.rb +16 -12
  306. data/lib/pubid/iso/supplement_identifier.rb +19 -6
  307. data/lib/pubid/iso/urn_generator.rb +4 -1
  308. data/lib/pubid/iso/urn_parser.rb +1 -1
  309. data/lib/pubid/itu/builder.rb +230 -29
  310. data/lib/pubid/itu/components/code.rb +48 -5
  311. data/lib/pubid/itu/components/designation.rb +35 -0
  312. data/lib/pubid/itu/components.rb +1 -0
  313. data/lib/pubid/itu/identifier.rb +1 -3
  314. data/lib/pubid/itu/identifiers/addendum.rb +15 -0
  315. data/lib/pubid/itu/identifiers/amendment.rb +6 -28
  316. data/lib/pubid/itu/identifiers/annex.rb +13 -2
  317. data/lib/pubid/itu/identifiers/annex_of_recommendation.rb +87 -0
  318. data/lib/pubid/itu/identifiers/appendix_of_recommendation.rb +92 -0
  319. data/lib/pubid/itu/identifiers/base.rb +341 -23
  320. data/lib/pubid/itu/identifiers/combined_identifier.rb +99 -23
  321. data/lib/pubid/itu/identifiers/corrigendum.rb +16 -23
  322. data/lib/pubid/itu/identifiers/errata.rb +15 -0
  323. data/lib/pubid/itu/identifiers/handbook.rb +38 -0
  324. data/lib/pubid/itu/identifiers/question.rb +63 -0
  325. data/lib/pubid/itu/identifiers/recommendation.rb +3 -2
  326. data/lib/pubid/itu/identifiers/report.rb +39 -0
  327. data/lib/pubid/itu/identifiers/special_publication.rb +3 -1
  328. data/lib/pubid/itu/identifiers/standard_serialization.rb +58 -0
  329. data/lib/pubid/itu/identifiers/supplement.rb +119 -14
  330. data/lib/pubid/itu/identifiers.rb +10 -1
  331. data/lib/pubid/itu/model.rb +1 -1
  332. data/lib/pubid/itu/parser.rb +480 -26
  333. data/lib/pubid/itu/urn_generator.rb +36 -6
  334. data/lib/pubid/itu/urn_parser.rb +8 -0
  335. data/lib/pubid/itu.rb +11 -3
  336. data/lib/pubid/jcgm/builder.rb +23 -13
  337. data/lib/pubid/jcgm/identifiers/amendment.rb +0 -2
  338. data/lib/pubid/jcgm/identifiers/corrigendum.rb +34 -0
  339. data/lib/pubid/jcgm/identifiers/gum_guide.rb +3 -3
  340. data/lib/pubid/jcgm/identifiers/meeting.rb +47 -0
  341. data/lib/pubid/jcgm/identifiers.rb +2 -0
  342. data/lib/pubid/jcgm/parser.rb +66 -6
  343. data/lib/pubid/jcgm/renderer.rb +26 -12
  344. data/lib/pubid/jcgm/single_identifier.rb +74 -5
  345. data/lib/pubid/jcgm/supplement_identifier.rb +39 -4
  346. data/lib/pubid/jcgm/urn_generator.rb +21 -13
  347. data/lib/pubid/jcgm/urn_parser.rb +15 -3
  348. data/lib/pubid/jis/identifier.rb +52 -0
  349. data/lib/pubid/nist/builder.rb +29 -9
  350. data/lib/pubid/nist/caster.rb +7 -45
  351. data/lib/pubid/nist/circular_supplement_builder.rb +7 -5
  352. data/lib/pubid/nist/components/supplement.rb +5 -2
  353. data/lib/pubid/nist/configuration.rb +7 -1
  354. data/lib/pubid/nist/identifier.rb +1 -3
  355. data/lib/pubid/nist/identifiers/base.rb +121 -37
  356. data/lib/pubid/nist/identifiers/circular.rb +1 -1
  357. data/lib/pubid/nist/identifiers/commercial_standard.rb +1 -1
  358. data/lib/pubid/nist/identifiers/commercial_standard_emergency.rb +1 -1
  359. data/lib/pubid/nist/identifiers/commercial_standards_monthly.rb +1 -1
  360. data/lib/pubid/nist/identifiers/crpl_report.rb +1 -1
  361. data/lib/pubid/nist/identifiers/dated_document.rb +1 -1
  362. data/lib/pubid/nist/identifiers/federal_information_processing_standards.rb +1 -1
  363. data/lib/pubid/nist/identifiers/grant_contractor_report.rb +1 -1
  364. data/lib/pubid/nist/identifiers/handbook.rb +1 -1
  365. data/lib/pubid/nist/identifiers/internal_report.rb +1 -1
  366. data/lib/pubid/nist/identifiers/letter_circular.rb +1 -1
  367. data/lib/pubid/nist/identifiers/miscellaneous_publication.rb +1 -1
  368. data/lib/pubid/nist/identifiers/monograph.rb +1 -1
  369. data/lib/pubid/nist/identifiers/ncstar.rb +2 -2
  370. data/lib/pubid/nist/identifiers/nsrds.rb +1 -1
  371. data/lib/pubid/nist/identifiers/owmwp.rb +1 -1
  372. data/lib/pubid/nist/identifiers/report.rb +1 -1
  373. data/lib/pubid/nist/identifiers/special_publication.rb +10 -1
  374. data/lib/pubid/nist/identifiers/technical_note.rb +1 -1
  375. data/lib/pubid/nist/identifiers.rb +0 -1
  376. data/lib/pubid/nist/parser.rb +29 -4
  377. data/lib/pubid/nist/preprocessor.rb +15 -0
  378. data/lib/pubid/nist/router.rb +3 -2
  379. data/lib/pubid/nist/series/ir.rb +0 -3
  380. data/lib/pubid/nist/supplement_identifier.rb +9 -9
  381. data/lib/pubid/nist.rb +13 -12
  382. data/lib/pubid/oasis/builder.rb +77 -0
  383. data/lib/pubid/oasis/identifier.rb +82 -0
  384. data/lib/pubid/oasis/identifiers/standard.rb +29 -0
  385. data/lib/pubid/oasis/identifiers.rb +9 -0
  386. data/lib/pubid/oasis/parser.rb +34 -0
  387. data/lib/pubid/oasis/renderer.rb +29 -0
  388. data/lib/pubid/oasis/urn_generator.rb +25 -0
  389. data/lib/pubid/oasis/urn_parser.rb +22 -0
  390. data/lib/pubid/oasis.rb +74 -0
  391. data/lib/pubid/ogc/builder.rb +33 -0
  392. data/lib/pubid/ogc/identifier.rb +77 -0
  393. data/lib/pubid/ogc/identifiers/document.rb +26 -0
  394. data/lib/pubid/ogc/identifiers.rb +10 -0
  395. data/lib/pubid/ogc/parser.rb +40 -0
  396. data/lib/pubid/ogc/renderer.rb +33 -0
  397. data/lib/pubid/ogc/urn_generator.rb +14 -0
  398. data/lib/pubid/ogc/urn_parser.rb +22 -0
  399. data/lib/pubid/ogc.rb +71 -0
  400. data/lib/pubid/oiml/builder.rb +31 -7
  401. data/lib/pubid/oiml/identifier.rb +9 -1
  402. data/lib/pubid/oiml/parser.rb +11 -12
  403. data/lib/pubid/oiml/renderer.rb +13 -13
  404. data/lib/pubid/oiml/single_identifier.rb +20 -0
  405. data/lib/pubid/oiml/supplement_identifier.rb +9 -9
  406. data/lib/pubid/oiml/urn_parser.rb +23 -6
  407. data/lib/pubid/oiml.rb +4 -0
  408. data/lib/pubid/omg/builder.rb +19 -0
  409. data/lib/pubid/omg/identifier.rb +44 -0
  410. data/lib/pubid/omg/identifiers/specification.rb +32 -0
  411. data/lib/pubid/omg/identifiers.rb +9 -0
  412. data/lib/pubid/omg/parser.rb +40 -0
  413. data/lib/pubid/omg/renderer.rb +19 -0
  414. data/lib/pubid/omg.rb +51 -0
  415. data/lib/pubid/parsers/mr_string.rb +101 -7
  416. data/lib/pubid/plateau/builder.rb +8 -4
  417. data/lib/pubid/plateau/identifier.rb +1 -3
  418. data/lib/pubid/plateau/identifiers/base.rb +1 -6
  419. data/lib/pubid/plateau/identifiers/handbook.rb +1 -1
  420. data/lib/pubid/plateau/identifiers/technical_report.rb +1 -1
  421. data/lib/pubid/plateau/identifiers.rb +0 -1
  422. data/lib/pubid/plateau/parser.rb +15 -5
  423. data/lib/pubid/plateau/renderer.rb +1 -1
  424. data/lib/pubid/plateau/supplement_identifier.rb +11 -11
  425. data/lib/pubid/plateau.rb +4 -4
  426. data/lib/pubid/renderers/human_readable.rb +3 -1
  427. data/lib/pubid/renderers/mr_string.rb +46 -4
  428. data/lib/pubid/renderers/supplement_renderer.rb +1 -1
  429. data/lib/pubid/rendering/supplement.rb +4 -4
  430. data/lib/pubid/sae/builder.rb +1 -1
  431. data/lib/pubid/sae/identifier.rb +1 -3
  432. data/lib/pubid/sae/identifiers/base.rb +10 -7
  433. data/lib/pubid/sae/identifiers.rb +0 -1
  434. data/lib/pubid/sae.rb +3 -3
  435. data/lib/pubid/tgpp/builder.rb +41 -0
  436. data/lib/pubid/tgpp/identifier.rb +91 -0
  437. data/lib/pubid/tgpp/identifiers/technical_report.rb +32 -0
  438. data/lib/pubid/tgpp/identifiers/technical_specification.rb +33 -0
  439. data/lib/pubid/tgpp/identifiers.rb +11 -0
  440. data/lib/pubid/tgpp/parser.rb +62 -0
  441. data/lib/pubid/tgpp/renderer.rb +36 -0
  442. data/lib/pubid/tgpp/urn_generator.rb +20 -0
  443. data/lib/pubid/tgpp/urn_parser.rb +28 -0
  444. data/lib/pubid/tgpp.rb +78 -0
  445. data/lib/pubid/type_resolver.rb +59 -0
  446. data/lib/pubid/un/builder.rb +42 -0
  447. data/lib/pubid/un/identifier.rb +37 -0
  448. data/lib/pubid/un/identifiers/document.rb +24 -0
  449. data/lib/pubid/un/identifiers.rb +9 -0
  450. data/lib/pubid/un/parser.rb +25 -0
  451. data/lib/pubid/un/renderer.rb +11 -0
  452. data/lib/pubid/un.rb +46 -0
  453. data/lib/pubid/version.rb +1 -1
  454. data/lib/pubid/w3c/builder.rb +55 -0
  455. data/lib/pubid/w3c/identifier.rb +101 -0
  456. data/lib/pubid/w3c/identifiers/candidate_recommendation.rb +31 -0
  457. data/lib/pubid/w3c/identifiers/candidate_recommendation_draft.rb +31 -0
  458. data/lib/pubid/w3c/identifiers/draft_note.rb +31 -0
  459. data/lib/pubid/w3c/identifiers/note.rb +30 -0
  460. data/lib/pubid/w3c/identifiers/obsolete_recommendation.rb +31 -0
  461. data/lib/pubid/w3c/identifiers/proposed_edited_recommendation.rb +31 -0
  462. data/lib/pubid/w3c/identifiers/proposed_recommendation.rb +31 -0
  463. data/lib/pubid/w3c/identifiers/recommendation.rb +31 -0
  464. data/lib/pubid/w3c/identifiers/standard.rb +12 -0
  465. data/lib/pubid/w3c/identifiers/superseded_recommendation.rb +31 -0
  466. data/lib/pubid/w3c/identifiers/working_draft.rb +30 -0
  467. data/lib/pubid/w3c/identifiers.rb +25 -0
  468. data/lib/pubid/w3c/parser.rb +43 -0
  469. data/lib/pubid/w3c/renderer.rb +35 -0
  470. data/lib/pubid/w3c/urn_generator.rb +18 -0
  471. data/lib/pubid/w3c/urn_parser.rb +36 -0
  472. data/lib/pubid/w3c.rb +67 -0
  473. data/lib/pubid/xsf/builder.rb +17 -0
  474. data/lib/pubid/xsf/identifier.rb +62 -0
  475. data/lib/pubid/xsf/identifiers/xep.rb +31 -0
  476. data/lib/pubid/xsf/identifiers.rb +9 -0
  477. data/lib/pubid/xsf/parser.rb +25 -0
  478. data/lib/pubid/xsf/renderer.rb +23 -0
  479. data/lib/pubid/xsf/urn_generator.rb +13 -0
  480. data/lib/pubid/xsf/urn_parser.rb +18 -0
  481. data/lib/pubid/xsf.rb +76 -0
  482. data/lib/pubid.rb +25 -1
  483. metadata +205 -4
  484. data/lib/pubid/cie/components/code.rb +0 -80
  485. data/lib/pubid/iala/identifiers/base.rb +0 -14
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pubid
4
+ module Iana
5
+ # Human-readable renderer for IANA registry identifiers.
6
+ #
7
+ # Produces the authoritative printed form:
8
+ # "IANA _6lowpan-parameters"
9
+ # "IANA _6lowpan-parameters/lowpan_nhc"
10
+ #
11
+ # With `with_publisher: false` on the identifier it renders the bare
12
+ # index-key slug instead (no "IANA " token).
13
+ class Renderer < ::Pubid::Renderers::Base
14
+ PUBLISHER = "IANA"
15
+
16
+ def render(**_opts)
17
+ id = @id
18
+ code = id.code
19
+ with_publisher?(id) ? "#{PUBLISHER} #{code}" : code
20
+ end
21
+
22
+ private
23
+
24
+ def with_publisher?(id)
25
+ id.with_publisher != false
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pubid
4
+ module Iana
5
+ # Emits `urn:iana:<registry>[:<sub_registry>]`. Slugs contain no ":"
6
+ # (charset is [a-zA-Z0-9._-]), so the two levels split unambiguously.
7
+ class UrnGenerator < Pubid::UrnGenerator::Base
8
+ def generate
9
+ parts = ["urn", "iana", identifier.registry]
10
+ parts << identifier.sub_registry if identifier.sub_registry
11
+ parts.join(":")
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pubid
4
+ module Iana
5
+ # Parses IANA URNs back into identifiers.
6
+ #
7
+ # UrnGenerator emits `urn:iana:<registry>[:<sub_registry>]`; this rebuilds
8
+ # the bare slug and delegates to the flavor's text parser (which normalizes
9
+ # it back to the "IANA "-prefixed printed form).
10
+ #
11
+ # Examples:
12
+ # - urn:iana:calipso -> IANA calipso
13
+ # - urn:iana:_6lowpan-parameters:lowpan_nhc
14
+ # -> IANA _6lowpan-parameters/lowpan_nhc
15
+ class UrnParser < Pubid::UrnParser::Base
16
+ def parse_urn(urn)
17
+ body = strip_namespace(urn)
18
+ registry, sub_registry = split_parts(body)
19
+ slug = sub_registry ? "#{registry}/#{sub_registry}" : registry
20
+ flavor_parse(slug)
21
+ end
22
+ end
23
+ end
24
+ end
data/lib/pubid/iana.rb ADDED
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pubid
4
+ module Iana
5
+ extend Pubid::PrefixesSupport
6
+
7
+ # Sole IANA publisher token (see the parser's optional `IANA ` prefix).
8
+ # IANA entries are protocol registries whose printed form always leads with
9
+ # the "IANA" token; routing relies on it since the raw slug carries no
10
+ # publisher.
11
+ PREFIXES = ["IANA"].freeze
12
+
13
+ autoload :Builder, "#{__dir__}/iana/builder"
14
+ autoload :Identifier, "#{__dir__}/iana/identifier"
15
+ autoload :Identifiers, "#{__dir__}/iana/identifiers"
16
+ autoload :Parser, "#{__dir__}/iana/parser"
17
+ autoload :Renderer, "#{__dir__}/iana/renderer"
18
+ autoload :UrnGenerator, "#{__dir__}/iana/urn_generator"
19
+ autoload :UrnParser, "#{__dir__}/iana/urn_parser"
20
+
21
+ # Parse an IANA identifier string
22
+ def self.parse(identifier)
23
+ Identifier.parse(identifier)
24
+ end
25
+
26
+ # Per-flavor format registry: inherits global formats, overrides :human
27
+ Identifier.format_registry = FormatRegistry.new(parent: Identifier.format_registry)
28
+ Identifier.format_registry.register(:human, renderer: Iana::Renderer)
29
+
30
+ # Auto-discover all identifier types from the Identifiers namespace
31
+ # @return [Array<Class>] identifier classes that define a self.type Hash
32
+ def self.identifier_types
33
+ @identifier_types ||= Identifiers.constants
34
+ .filter_map { |c| begin; Identifiers.const_get(c); rescue NameError; nil; end }
35
+ .select { |c| c.is_a?(Class) && c.singleton_methods(false).include?(:type) }
36
+ .select { |c| c.type.is_a?(Hash) }
37
+ end
38
+
39
+ # Build typed stage index from identifier types
40
+ # @return [Array<Pubid::Components::TypedStage>] all typed stages
41
+ def self.all_typed_stages
42
+ @all_typed_stages ||= identifier_types.flat_map do |klass|
43
+ if klass.const_defined?(:TYPED_STAGES)
44
+ klass.const_get(:TYPED_STAGES)
45
+ else
46
+ []
47
+ end
48
+ end
49
+ end
50
+
51
+ # Lookup: type code -> identifier class
52
+ # @param code [String, Symbol] the type key to find
53
+ # @return [Class, nil] the matching identifier class
54
+ def self.locate_type(code)
55
+ identifier_types.find { |t| t.type[:key].to_s == code.to_s }
56
+ end
57
+
58
+ # Lookup: abbreviation -> typed stage
59
+ # @param abbr [String, Symbol] the abbreviation to find
60
+ # @return [Pubid::Components::TypedStage, nil] the matching typed stage
61
+ def self.locate_stage(abbr)
62
+ abbr_str = abbr.to_s.upcase
63
+ all_typed_stages.find { |s| s.abbr.any? { |a| a.to_s.upcase == abbr_str } }
64
+ end
65
+ end
66
+ end
67
+
68
+ # Register IANA flavor with the registry
69
+ Pubid::Registry.register(:iana, Pubid::Iana)
@@ -13,27 +13,53 @@ module Pubid
13
13
  # `key_value` polymorphic_map reads `_type` only to VALIDATE; it does not
14
14
  # re-instantiate the concrete subclass. So a base-class `from_hash` would
15
15
  # return a bare base object and drop subtype-specific attributes (e.g. a
16
- # supplement's base_identifier). Route by `_type` to the concrete class
16
+ # supplement's base). Route by `_type` to the concrete class
17
17
  # named in `polymorphic_type_map`, then let its inherited from_hash (this
18
18
  # method again, where klass == self) fall through to lutaml's real work.
19
19
  #
20
20
  # This generalizes the per-flavor `*_TYPE_MAP` dispatch that ISO, JIS,
21
21
  # IEC, CCSDS and IHO each hand-rolled — one implementation, inherited by
22
- # every flavor base.
22
+ # every flavor base. Cross-flavor dispatch (a nested adopted identifier
23
+ # whose _type belongs to a different flavor) is delegated to
24
+ # {TypeResolver}, which knows about every registered flavor.
23
25
  def from_hash(data, options = {})
24
- type = data && (data["_type"] || data[:_type])
25
- klass = polymorphic_type_map[type]
26
+ klass = concrete_class_for(data)
26
27
  return klass.from_hash(data, options) if klass && klass != self
27
-
28
28
  super
29
29
  end
30
30
 
31
+ # lutaml's nested polymorphic cast (Attribute#cast → apply_mappings)
32
+ # bypasses {from_hash}, so cross-flavor dispatch wouldn't fire for a
33
+ # nested attribute that carries another flavor's _type. Intercept
34
+ # apply_mappings and route to {from_hash} so the concrete class's own
35
+ # mappings (with their flavor-specific custom cast methods, e.g. ISO's
36
+ # number_from_kv) drive deserialization.
37
+ def apply_mappings(doc, format, options = {})
38
+ return super unless hash_with_type?(doc, format)
39
+
40
+ klass = concrete_class_for(doc)
41
+ return super unless klass && klass != self
42
+
43
+ klass.from_hash(doc, options)
44
+ end
45
+
46
+ # Resolve a polymorphic _type to the concrete class that owns it:
47
+ # this flavor's own map first, then any registered flavor's map via
48
+ # {TypeResolver}. Returns nil for blank or unknown types.
49
+ def concrete_class_for(data)
50
+ type = data && (data["_type"] || data[:_type])
51
+ return nil unless type
52
+
53
+ polymorphic_type_map[type] || ::Pubid::TypeResolver.resolve(type)
54
+ end
55
+
31
56
  # Map of polymorphic_name ("pubid:iso:corrigendum") => concrete class for
32
57
  # the flavor this base belongs to. Built once by scanning the flavor's
33
58
  # `Identifiers` namespace for Pubid::Identifier descendants and unioning
34
59
  # the flavor's `identifier_types` registry (which may register classes
35
60
  # living outside that namespace, e.g. ISO's BundledIdentifier). Memoized
36
- # per class.
61
+ # per class. Public so {TypeResolver} can read another flavor's map for
62
+ # cross-flavor polymorphic dispatch.
37
63
  def polymorphic_type_map
38
64
  @polymorphic_type_map ||=
39
65
  identifier_registry_classes.each_with_object({}) do |klass, map|
@@ -44,6 +70,12 @@ module Pubid
44
70
 
45
71
  private
46
72
 
73
+ def hash_with_type?(doc, format)
74
+ format == :hash && doc.is_a?(Hash) && (doc["_type"] || doc[:_type])
75
+ end
76
+
77
+ private
78
+
47
79
  # The flavor module namespacing this base, e.g. Pubid::Iso for
48
80
  # Pubid::Iso::Identifier (and for the inherited Pubid::Iso::Identifiers::
49
81
  # Corrigendum). Always the first two name components, so it is stable
@@ -115,8 +147,8 @@ module Pubid
115
147
  attribute :locality, Components::Locality
116
148
  attribute :typed_stage, Components::TypedStage
117
149
  attribute :all_parts, Lutaml::Model::Type::Boolean, default: false
118
- # base_identifier is declared by supplement subclasses with proper type
119
- def base_identifier
150
+ # base is declared by supplement subclasses with proper type
151
+ def base
120
152
  nil
121
153
  end
122
154
 
@@ -229,7 +261,7 @@ module Pubid
229
261
  end
230
262
 
231
263
  def root
232
- return base_identifier.root if base_identifier
264
+ return base.root if base
233
265
 
234
266
  self
235
267
  end
@@ -246,8 +278,13 @@ module Pubid
246
278
  raise ArgumentError, "No renderer registered for format: #{format}"
247
279
  end
248
280
 
249
- context = build_rendering_context(renderer, format:, **opts)
250
- renderer.new(self).render(context:, **opts.slice(:with_edition))
281
+ # `:trademark` is a render-time flag consumed by the renderer, not the
282
+ # rendering context — and some flavors override build_rendering_context
283
+ # with a strict signature, so strip it here.
284
+ ctx_opts = opts.except(:trademark)
285
+ context = build_rendering_context(renderer, format:, **ctx_opts)
286
+ render_opts = opts.slice(:with_edition, :trademark)
287
+ renderer.new(self).render(context:, **render_opts)
251
288
  end
252
289
 
253
290
  def to_s(**opts)
@@ -258,38 +295,103 @@ module Pubid
258
295
  render(format: :mr_string)
259
296
  end
260
297
 
261
- # MR string template methods — flavors override as needed
298
+ # Filesystem-/URL-safe slug derived from the MR string. Defaults to
299
+ # `to_mr_string` because every flavor except NIST already emits an
300
+ # all-lowercase, filename-safe MR (only `[a-z0-9.-]` characters, plus
301
+ # `_` as the supplement separator and `-` as the copublisher separator).
302
+ # Flavors whose MR is not slug-safe (notably NIST, whose MR format is
303
+ # fixed by the pubid standard) override this to project the MR into a
304
+ # slug-safe form.
305
+ def to_slug
306
+ to_mr_string
307
+ end
308
+
309
+ # MR string template methods — flavors override as needed.
310
+ #
311
+ # The MR format is a lossless, dot-separated, all-lowercase, filename-safe
312
+ # slug mirroring `to_s`'s structure:
313
+ #
314
+ # {publisher}[-{copublisher}…][.{type}].{number}[-{part}[-{subpart}…]]
315
+ # [.{year}[-{month}[-{day}]]|’--’}][.{edition}][.{language}-{language}…]
316
+ # [.all-parts][_supplements…]
317
+ #
318
+ # Supplements append `_{type}.{number}.{year}` recursively, so a chained
319
+ # supplement like `…/Amd 3:2016/Cor 1:2017` round-trips as
320
+ # `…_amd.3.2016_cor.1.2017`. Copublishers join with `-`
321
+ # (`iso-iec.17031-1.2020`) so the slug never contains a path separator.
322
+ # Distinct identifiers never collide on `to_mr_string` (issue #142).
262
323
  def mr_publisher
263
- publisher&.to_s
324
+ publisher&.to_s&.downcase&.tr("/", "-")
264
325
  end
265
326
 
266
327
  def mr_type
267
328
  return nil unless typed_stage
268
329
 
269
330
  code = typed_stage.type_code
270
- return nil if code.nil? || code.empty? || code == "is"
331
+ return nil if code.nil? || code.empty? || code.to_s == "is"
271
332
 
272
- code
333
+ code.to_s.downcase
273
334
  end
274
335
 
275
336
  def mr_number_with_part
276
337
  num = mr_number
277
338
  return nil unless num
278
339
 
279
- prt = mr_part
280
- prt ? "#{num}-#{prt}" : num
340
+ segments = [num]
341
+ segments << mr_part if part
342
+ segments << mr_subpart if subpart
343
+ segments.compact.join("-").downcase
281
344
  end
282
345
 
283
346
  def mr_number
284
- number&.to_s
347
+ number&.to_s&.downcase
285
348
  end
286
349
 
287
350
  def mr_part
288
- part&.to_s
351
+ part&.to_s&.downcase
352
+ end
353
+
354
+ def mr_subpart
355
+ subpart&.to_s&.downcase
289
356
  end
290
357
 
291
358
  def mr_year
292
- date&.year&.to_s
359
+ return nil unless date
360
+ return "--" if date.respond_to?(:undated?) && date.undated?
361
+ return nil unless date.year
362
+
363
+ result = date.year.to_s
364
+ result += "-#{date.month}" if date.respond_to?(:month) && date.month
365
+ result += "-#{date.day}" if date.respond_to?(:day) && date.day
366
+ result
367
+ end
368
+
369
+ def mr_edition
370
+ return nil unless edition&.number
371
+
372
+ "ed#{edition.number}"
373
+ end
374
+
375
+ def mr_languages
376
+ return nil unless languages&.any?
377
+
378
+ # Hyphen-joined, no parens — parens are shell-/URL-unsafe.
379
+ languages.map { |l| l.code.to_s.downcase }.join("-")
380
+ end
381
+
382
+ def mr_all_parts
383
+ return nil unless all_parts
384
+
385
+ "all-parts"
386
+ end
387
+
388
+ # Hook: supplement / wrapper subclasses return the `{type}.{number}.{year}`
389
+ # suffix that distinguishes them from their base. `nil` for non-supplements.
390
+ # The base MrString renderer recurses into `base` whenever this returns
391
+ # a non-nil value, so chained supplements (Cor → Amd → IS) render fully.
392
+ # The suffix is appended with `_` (not `/`) so the MR stays filename-safe.
393
+ def mr_supplement_suffix
394
+ nil
293
395
  end
294
396
 
295
397
  # URN template methods — flavors override as needed
@@ -332,14 +434,21 @@ module Pubid
332
434
  end
333
435
 
334
436
  excluded_args = args.dup
335
- # Map :year to :date since identifiers store years inside date
336
- excluded_args << :date if excluded_args.delete(:year)
437
+ # Map :year to :date (year-in-date flavors), but ALSO keep :year so a
438
+ # flavor that models the edition as a plain `year` attribute (e.g. GOST)
439
+ # has it excluded too. The loop below nils whichever name the flavor has.
440
+ excluded_args << :date if excluded_args.include?(:year)
337
441
 
338
442
  attrs = self.class.attributes.each_with_object({}) do |(name, _), h|
339
443
  value = excluded_args.include?(name) ? nil : public_send(name)
340
444
  h[name] = exclude_from_nested(value, args)
341
445
  end
342
- self.class.new(attrs)
446
+ # Splat the rebuilt attributes as keywords. Flavors whose identifier
447
+ # #initialize is keyword-only (ITU, IEEE, …) would raise ArgumentError on
448
+ # a positional hash under Ruby 3 kwarg separation; the base
449
+ # initialize(attrs = {}, options = {}) still accepts **attrs because Ruby
450
+ # passes keywords to a no-keyword method as a trailing positional Hash.
451
+ self.class.new(**attrs)
343
452
  end
344
453
 
345
454
  # The standard this identifier supplements, dropping its own supplement
@@ -388,6 +497,84 @@ module Pubid
388
497
  false
389
498
  end
390
499
 
500
+ # --- Relational algebra (pubid/pubid#247) ---
501
+ # Each predicate returns true / false. Predicates that don't apply to
502
+ # this identifier type (e.g. supplement checks on a non-wrapper) return
503
+ # false rather than raising. This makes them safe for chain-of-checks
504
+ # patterns like `id.related_to?(other)`.
505
+
506
+ # Self is a supplement (amendment / corrigendum / errata / supplement)
507
+ # of +other+. Self must be a wrapper (has a +base+ attribute) whose
508
+ # inner base equals +other+.
509
+ def supplement_of?(other)
510
+ return false unless other.is_a?(::Pubid::Identifier)
511
+ return false unless self.class.attributes.key?(:base)
512
+
513
+ base == other
514
+ end
515
+
516
+ # Inverse of #supplement_of? — self is the base document and +other+
517
+ # is a supplement attached to it.
518
+ def has_supplement?(other)
519
+ other.is_a?(::Pubid::Identifier) && other.supplement_of?(self)
520
+ end
521
+
522
+ # Self and +other+ refer to the same document but differ only in the
523
+ # trailing date / year — the "dated vs undated" reference pattern.
524
+ def dated_version_of?(other)
525
+ return false unless other.is_a?(::Pubid::Identifier)
526
+
527
+ matches?(other, ignore: [:date, :year]) && self != other
528
+ end
529
+
530
+ # Self and +other+ have the same publisher + number but differ in
531
+ # part, subpart, or edition. This is the "sibling documents" pattern:
532
+ # same base standard, different sub-component.
533
+ def sibling_of?(other)
534
+ return false unless other.is_a?(::Pubid::Identifier)
535
+
536
+ publisher == other.publisher &&
537
+ number == other.number &&
538
+ self != other
539
+ end
540
+
541
+ # Self is an all-parts collection that covers +other+.
542
+ def includes?(other)
543
+ return false unless other.is_a?(::Pubid::Identifier)
544
+ return false unless all_parts
545
+
546
+ root == other.root
547
+ end
548
+
549
+ # Self is a draft version of +other+ (the published identifier).
550
+ # IEEE uses draft_status + draft; ISO/IEC use typed_stage.
551
+ def draft_of?(other)
552
+ return false unless other.is_a?(::Pubid::Identifier)
553
+
554
+ root == other.root &&
555
+ self != other &&
556
+ matches?(other, ignore: [:date, :year, :stage, :typed_stage])
557
+ end
558
+
559
+ # Self and +other+ have the same publisher + number + part but differ
560
+ # in edition / revision marker.
561
+ def edition_of?(other)
562
+ return false unless other.is_a?(::Pubid::Identifier)
563
+
564
+ publisher == other.publisher &&
565
+ number == other.number &&
566
+ part == other.part &&
567
+ self != other
568
+ end
569
+
570
+ # Any relational predicate holds.
571
+ def related_to?(other)
572
+ %i[supplement_of? has_supplement? dated_version_of? edition_of?
573
+ sibling_of? includes? draft_of?].any? do |m|
574
+ public_send(m, other)
575
+ end
576
+ end
577
+
391
578
  def hash
392
579
  @hash ||= compute_hash
393
580
  end
@@ -32,7 +32,7 @@ module Pubid
32
32
 
33
33
  def cast(type, value)
34
34
  case type
35
- when :base_identifier
35
+ when :base
36
36
  build(value)
37
37
  when :publisher
38
38
  Components::Publisher.new(body: value)
@@ -117,7 +117,7 @@ module Pubid
117
117
  # IDF 148-1:2008 / COR 1:2009
118
118
  # IDF 140-1:2007 / AMD1:2012 (no space between AMD and 1)
119
119
  rule(:supplement_identifier_no_third) do
120
- identifier_publisher_no_third.as(:base_identifier) >>
120
+ identifier_publisher_no_third.as(:base) >>
121
121
  space? >> str("/") >> space? >>
122
122
  supplement_type_with_stage >>
123
123
  space.maybe >> second_part
@@ -130,7 +130,7 @@ module Pubid
130
130
  # # ISO/IEC 13818-1:2015/Amd 3:2016/Cor 1:2017
131
131
  # # ISO/IEC 19794-7:2014/Amd 1:2015/CD Cor 1
132
132
  # rule(:supplement_supplement_identifier) do
133
- # supplement_identifier_no_third.as(:base_identifier) >>
133
+ # supplement_identifier_no_third.as(:base) >>
134
134
  # str("/") >> supplement_type_with_stage >>
135
135
  # # digits.as(:stage_iteration).maybe >>
136
136
  # space? >> second_part >> third_part
@@ -70,7 +70,7 @@ module Pubid
70
70
  with_edition = opts[:with_edition] || false
71
71
 
72
72
  [
73
- id.base_identifier.to_s(lang: lang, lang_single: lang_single,
73
+ id.base.to_s(lang: lang, lang_single: lang_single,
74
74
  with_edition: with_edition),
75
75
  "/",
76
76
  id.typed_stage.abbreviation,
@@ -12,15 +12,15 @@ module Pubid
12
12
  # SingleIdentifier (which is for base documents only).
13
13
  #
14
14
  # Architecture Note:
15
- # - Supplements have a base_identifier attribute (recursive structure)
15
+ # - Supplements have a base attribute (recursive structure)
16
16
  # - Supplements share the to_s rendering pattern
17
17
  # - Supplements inherit typed_stage from base Identifier class
18
18
  class SupplementIdentifier < Identifier
19
- attribute :base_identifier, Identifier
19
+ attribute :base, Identifier
20
20
 
21
21
  # Render supplement identifier as string.
22
22
  #
23
- # Format: "base_identifier/ABBREVIATION number:year"
23
+ # Format: "base/ABBREVIATION number:year"
24
24
  # Example: "IDF 125:1988/AMD 1:2023"
25
25
  #
26
26
  # @param lang [Symbol] Language (default: :en)