pubid 2.0.0.pre.alpha.12 → 2.0.0.pre.alpha.14

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 (285) hide show
  1. checksums.yaml +4 -4
  2. data/README.adoc +43 -1
  3. data/data/ieee/update_codes.yaml +17 -4
  4. data/data/nist/update_codes.yaml +7 -3
  5. data/data/parg/tables/bipm_groups.yaml +14 -0
  6. data/data/parg/tables/bipm_type_codes.yaml +5 -0
  7. data/data/parg/tables/bipm_type_names_en.yaml +6 -0
  8. data/data/parg/tables/bipm_type_names_fr.yaml +6 -0
  9. data/data/parg/tables/directives_supplements_typed_stages.yaml +3 -0
  10. data/data/parg/tables/directives_typed_stages.yaml +5 -0
  11. data/data/parg/tables/idf_typed_stages.yaml +27 -0
  12. data/data/parg/tables/idf_typed_stages_supplements.yaml +2 -0
  13. data/data/parg/tables/iec_typed_stages.yaml +130 -0
  14. data/data/parg/tables/iso_publishers.yaml +4 -0
  15. data/data/parg/tables/organizations.yaml +12 -0
  16. data/data/parg/tables/tc_types.yaml +42 -0
  17. data/data/parg/tables/typed_stages.yaml +114 -0
  18. data/data/parg/tables/typed_stages_supplements.yaml +64 -0
  19. data/data/parg/tables/wg_types.yaml +21 -0
  20. data/lib/pubid/adobe/builder.rb +2 -0
  21. data/lib/pubid/adobe/identifier.rb +11 -1
  22. data/lib/pubid/all_parts.rb +201 -0
  23. data/lib/pubid/all_parts_identifier.rb +19 -0
  24. data/lib/pubid/amca/CLAUDE.md +47 -0
  25. data/lib/pubid/amca/builder.rb +3 -5
  26. data/lib/pubid/amca/identifiers/base.rb +11 -1
  27. data/lib/pubid/amca/identifiers/publication.rb +13 -0
  28. data/lib/pubid/amca/parser.rb +2 -1
  29. data/lib/pubid/amca/renderer.rb +22 -33
  30. data/lib/pubid/amca/urn_generator.rb +21 -2
  31. data/lib/pubid/amca/urn_parser.rb +36 -10
  32. data/lib/pubid/ansi/builder.rb +6 -0
  33. data/lib/pubid/ansi/identifier.rb +1 -1
  34. data/lib/pubid/api/CLAUDE.md +23 -0
  35. data/lib/pubid/api/builder.rb +2 -0
  36. data/lib/pubid/api/identifier.rb +1 -1
  37. data/lib/pubid/api/parser.rb +8 -4
  38. data/lib/pubid/ashrae/CLAUDE.md +13 -0
  39. data/lib/pubid/ashrae/builder.rb +58 -14
  40. data/lib/pubid/ashrae/identifiers/base.rb +10 -1
  41. data/lib/pubid/ashrae/identifiers/errata.rb +14 -2
  42. data/lib/pubid/ashrae/identifiers/interpretation.rb +2 -10
  43. data/lib/pubid/ashrae/parser.rb +80 -39
  44. data/lib/pubid/ashrae/renderer.rb +32 -1
  45. data/lib/pubid/ashrae/urn_generator.rb +32 -9
  46. data/lib/pubid/asme/CLAUDE.md +25 -0
  47. data/lib/pubid/asme/builder.rb +16 -9
  48. data/lib/pubid/asme/components/code.rb +2 -0
  49. data/lib/pubid/asme/identifier.rb +1 -1
  50. data/lib/pubid/asme/identifiers/standard.rb +6 -1
  51. data/lib/pubid/asme/parser.rb +41 -14
  52. data/lib/pubid/astm/CLAUDE.md +9 -0
  53. data/lib/pubid/astm/builder.rb +2 -0
  54. data/lib/pubid/astm/components/code.rb +2 -0
  55. data/lib/pubid/astm/identifier.rb +1 -1
  56. data/lib/pubid/astm/parser.rb +4 -1
  57. data/lib/pubid/bipm/CLAUDE.md +11 -0
  58. data/lib/pubid/bipm/builder.rb +2 -0
  59. data/lib/pubid/bipm/identifier.rb +1 -1
  60. data/lib/pubid/bsi/CLAUDE.md +93 -0
  61. data/lib/pubid/bsi/builder.rb +13 -11
  62. data/lib/pubid/bsi/identifiers/addendum_document.rb +2 -0
  63. data/lib/pubid/bsi/identifiers/adopted_european_norm.rb +6 -54
  64. data/lib/pubid/bsi/identifiers/adopted_international_standard.rb +5 -22
  65. data/lib/pubid/bsi/identifiers/amendment.rb +36 -12
  66. data/lib/pubid/bsi/identifiers/bundled_identifier.rb +2 -0
  67. data/lib/pubid/bsi/identifiers/consolidated_identifier.rb +23 -26
  68. data/lib/pubid/bsi/identifiers/corrigendum.rb +29 -12
  69. data/lib/pubid/bsi/identifiers/expert_commentary.rb +6 -7
  70. data/lib/pubid/bsi/identifiers/national_annex.rb +18 -20
  71. data/lib/pubid/bsi/identifiers/root_identity.rb +31 -0
  72. data/lib/pubid/bsi/identifiers/set.rb +2 -0
  73. data/lib/pubid/bsi/identifiers/supplement_document.rb +2 -0
  74. data/lib/pubid/bsi/identifiers.rb +1 -0
  75. data/lib/pubid/bsi/parser.rb +8 -8
  76. data/lib/pubid/bsi/renderer.rb +20 -20
  77. data/lib/pubid/bsi/single_identifier.rb +1 -3
  78. data/lib/pubid/bsi/urn_generator.rb +28 -18
  79. data/lib/pubid/builder/base.rb +27 -0
  80. data/lib/pubid/calconnect/builder.rb +2 -0
  81. data/lib/pubid/calconnect/identifier.rb +5 -1
  82. data/lib/pubid/ccsds/builder.rb +2 -0
  83. data/lib/pubid/ccsds/identifier.rb +9 -1
  84. data/lib/pubid/cen_cenelec/CLAUDE.md +59 -0
  85. data/lib/pubid/cen_cenelec/builder.rb +6 -1
  86. data/lib/pubid/cen_cenelec/identifier.rb +12 -28
  87. data/lib/pubid/cen_cenelec/identifiers/amendment.rb +3 -10
  88. data/lib/pubid/cen_cenelec/identifiers/corrigendum.rb +3 -10
  89. data/lib/pubid/cen_cenelec/parser.rb +20 -5
  90. data/lib/pubid/cie/CLAUDE.md +58 -0
  91. data/lib/pubid/cie/builder.rb +2 -0
  92. data/lib/pubid/cie/components/language.rb +2 -0
  93. data/lib/pubid/cie/identifier.rb +1 -1
  94. data/lib/pubid/cie/parser.rb +9 -2
  95. data/lib/pubid/components/adoption.rb +2 -0
  96. data/lib/pubid/components/code.rb +2 -0
  97. data/lib/pubid/components/date.rb +8 -6
  98. data/lib/pubid/components/edition.rb +2 -0
  99. data/lib/pubid/components/iteration.rb +2 -0
  100. data/lib/pubid/components/language.rb +2 -0
  101. data/lib/pubid/components/locality.rb +2 -0
  102. data/lib/pubid/components/publisher.rb +2 -0
  103. data/lib/pubid/components/relationship.rb +2 -0
  104. data/lib/pubid/components/stage.rb +2 -0
  105. data/lib/pubid/components/supplement.rb +2 -0
  106. data/lib/pubid/components/type.rb +2 -0
  107. data/lib/pubid/components/typed_stage.rb +8 -0
  108. data/lib/pubid/conformance/checks.rb +1 -1
  109. data/lib/pubid/csa/CLAUDE.md +41 -0
  110. data/lib/pubid/csa/builder.rb +2 -0
  111. data/lib/pubid/csa/identifier.rb +19 -3
  112. data/lib/pubid/csa/parser.rb +25 -8
  113. data/lib/pubid/csa/renderer.rb +12 -12
  114. data/lib/pubid/csa/single_identifier.rb +17 -0
  115. data/lib/pubid/doi/builder.rb +2 -0
  116. data/lib/pubid/doi/identifier.rb +1 -1
  117. data/lib/pubid/easc/builder.rb +2 -0
  118. data/lib/pubid/easc/identifier.rb +10 -1
  119. data/lib/pubid/ecma/CLAUDE.md +28 -0
  120. data/lib/pubid/ecma/builder.rb +2 -0
  121. data/lib/pubid/ecma/identifier.rb +8 -1
  122. data/lib/pubid/etsi/CLAUDE.md +34 -0
  123. data/lib/pubid/etsi/builder.rb +2 -0
  124. data/lib/pubid/etsi/components/code.rb +6 -0
  125. data/lib/pubid/etsi/components/version.rb +2 -0
  126. data/lib/pubid/etsi/identifiers/base.rb +1 -1
  127. data/lib/pubid/etsi/identifiers/etsi_standard.rb +7 -0
  128. data/lib/pubid/evs/CLAUDE.md +58 -0
  129. data/lib/pubid/evs/builder.rb +2 -0
  130. data/lib/pubid/evs.rb +1 -1
  131. data/lib/pubid/gb/CLAUDE.md +140 -0
  132. data/lib/pubid/gb/builder.rb +7 -2
  133. data/lib/pubid/gb/identifier.rb +6 -4
  134. data/lib/pubid/gb/identifiers/all_parts.rb +17 -0
  135. data/lib/pubid/gb/identifiers.rb +1 -0
  136. data/lib/pubid/gb/renderer.rb +0 -1
  137. data/lib/pubid/gost/CLAUDE.md +64 -0
  138. data/lib/pubid/gost/builder.rb +3 -1
  139. data/lib/pubid/gost/identifier.rb +16 -1
  140. data/lib/pubid/gost/parser.rb +8 -1
  141. data/lib/pubid/iala/CLAUDE.md +82 -0
  142. data/lib/pubid/iala/builder.rb +2 -0
  143. data/lib/pubid/iala/identifier.rb +10 -1
  144. data/lib/pubid/iana/CLAUDE.md +7 -0
  145. data/lib/pubid/iana/builder.rb +2 -0
  146. data/lib/pubid/iana/identifier.rb +1 -1
  147. data/lib/pubid/identifier.rb +161 -17
  148. data/lib/pubid/idf/builder.rb +11 -1
  149. data/lib/pubid/idf/identifier.rb +5 -0
  150. data/lib/pubid/idf/identifiers/all_parts.rb +17 -0
  151. data/lib/pubid/idf/identifiers.rb +1 -0
  152. data/lib/pubid/iec/CLAUDE.md +31 -0
  153. data/lib/pubid/iec/builder.rb +7 -1
  154. data/lib/pubid/iec/components/consolidated_amendment.rb +4 -0
  155. data/lib/pubid/iec/components/sheet.rb +2 -0
  156. data/lib/pubid/iec/components/trf_info.rb +2 -0
  157. data/lib/pubid/iec/components/vap_suffix.rb +2 -0
  158. data/lib/pubid/iec/identifier.rb +8 -3
  159. data/lib/pubid/iec/identifiers/all_parts.rb +19 -0
  160. data/lib/pubid/iec/identifiers.rb +1 -0
  161. data/lib/pubid/iec/parser.rb +9 -4
  162. data/lib/pubid/iec/renderer.rb +0 -1
  163. data/lib/pubid/iec/urn_generator.rb +9 -1
  164. data/lib/pubid/iec/urn_parser.rb +3 -2
  165. data/lib/pubid/ieee/CLAUDE.md +97 -0
  166. data/lib/pubid/ieee/builder.rb +194 -27
  167. data/lib/pubid/ieee/components/code.rb +2 -0
  168. data/lib/pubid/ieee/components/draft.rb +35 -2
  169. data/lib/pubid/ieee/components/typed_stage.rb +2 -0
  170. data/lib/pubid/ieee/identifiers/base.rb +21 -1
  171. data/lib/pubid/ieee/identifiers/iec_ieee_copublished.rb +9 -0
  172. data/lib/pubid/ieee/identifiers/joint_development.rb +66 -23
  173. data/lib/pubid/ieee/identifiers/project_draft_identifier.rb +8 -1
  174. data/lib/pubid/ieee/parser.rb +156 -29
  175. data/lib/pubid/ieee/renderer.rb +42 -7
  176. data/lib/pubid/ieee/urn_generator.rb +31 -0
  177. data/lib/pubid/ietf/CLAUDE.md +7 -0
  178. data/lib/pubid/ietf/builder.rb +2 -0
  179. data/lib/pubid/ietf/identifiers/base.rb +1 -1
  180. data/lib/pubid/iho/builder.rb +2 -0
  181. data/lib/pubid/isbn/builder.rb +2 -0
  182. data/lib/pubid/isbn/identifier.rb +1 -1
  183. data/lib/pubid/iso/CLAUDE.md +47 -0
  184. data/lib/pubid/iso/builder.rb +19 -5
  185. data/lib/pubid/iso/components/publisher.rb +2 -0
  186. data/lib/pubid/iso/identifier.rb +10 -15
  187. data/lib/pubid/iso/identifiers/all_parts.rb +19 -0
  188. data/lib/pubid/iso/identifiers/directives_supplement.rb +4 -2
  189. data/lib/pubid/iso/identifiers.rb +1 -0
  190. data/lib/pubid/iso/normalizer.rb +4 -1
  191. data/lib/pubid/iso/rendering_style.rb +0 -1
  192. data/lib/pubid/itu/CLAUDE.md +115 -0
  193. data/lib/pubid/itu/builder.rb +26 -4
  194. data/lib/pubid/itu/components/code.rb +2 -0
  195. data/lib/pubid/itu/components/designation.rb +2 -0
  196. data/lib/pubid/itu/components/sector.rb +2 -0
  197. data/lib/pubid/itu/components/series.rb +2 -0
  198. data/lib/pubid/itu/identifiers/base.rb +11 -18
  199. data/lib/pubid/itu/identifiers/radio_regulations.rb +27 -0
  200. data/lib/pubid/itu/identifiers/special_publication.rb +48 -14
  201. data/lib/pubid/itu/identifiers/standard_serialization.rb +2 -0
  202. data/lib/pubid/itu/identifiers/supplement.rb +15 -0
  203. data/lib/pubid/itu/identifiers.rb +1 -0
  204. data/lib/pubid/itu/parser.rb +108 -22
  205. data/lib/pubid/itu/urn_generator.rb +9 -2
  206. data/lib/pubid/jcgm/CLAUDE.md +7 -0
  207. data/lib/pubid/jcgm/builder.rb +2 -0
  208. data/lib/pubid/jcgm/components/publisher.rb +2 -0
  209. data/lib/pubid/jcgm.rb +1 -1
  210. data/lib/pubid/jis/builder.rb +5 -1
  211. data/lib/pubid/jis/identifier.rb +6 -18
  212. data/lib/pubid/jis/identifiers/all_parts.rb +19 -0
  213. data/lib/pubid/jis/identifiers.rb +1 -0
  214. data/lib/pubid/jis/renderer.rb +0 -2
  215. data/lib/pubid/jis/urn_generator.rb +0 -1
  216. data/lib/pubid/nist/CLAUDE.md +56 -0
  217. data/lib/pubid/nist/builder.rb +3 -0
  218. data/lib/pubid/nist/components/edition.rb +2 -0
  219. data/lib/pubid/nist/components/issue_number.rb +2 -0
  220. data/lib/pubid/nist/components/part.rb +2 -0
  221. data/lib/pubid/nist/components/stage.rb +2 -0
  222. data/lib/pubid/nist/components/supplement.rb +2 -0
  223. data/lib/pubid/nist/components/translation.rb +2 -0
  224. data/lib/pubid/nist/components/update.rb +2 -0
  225. data/lib/pubid/nist/components/version.rb +2 -0
  226. data/lib/pubid/nist/components/volume.rb +2 -0
  227. data/lib/pubid/nist/identifiers/base.rb +39 -7
  228. data/lib/pubid/nist/parser.rb +24 -2
  229. data/lib/pubid/nist/preprocessor.rb +53 -2
  230. data/lib/pubid/nist/urn_parser.rb +10 -1
  231. data/lib/pubid/oasis/CLAUDE.md +19 -0
  232. data/lib/pubid/oasis/builder.rb +2 -0
  233. data/lib/pubid/oasis/identifier.rb +20 -1
  234. data/lib/pubid/ogc/CLAUDE.md +34 -0
  235. data/lib/pubid/ogc/builder.rb +2 -0
  236. data/lib/pubid/ogc/identifier.rb +12 -1
  237. data/lib/pubid/oiml/CLAUDE.md +189 -0
  238. data/lib/pubid/oiml/builder.rb +20 -0
  239. data/lib/pubid/oiml/components/code.rb +6 -0
  240. data/lib/pubid/oiml/identifier.rb +13 -0
  241. data/lib/pubid/oiml/identifiers/annex.rb +4 -0
  242. data/lib/pubid/oiml/identifiers/certification_system.rb +34 -0
  243. data/lib/pubid/oiml/identifiers/code_number.rb +8 -0
  244. data/lib/pubid/oiml/identifiers/dual_published.rb +174 -0
  245. data/lib/pubid/oiml/identifiers.rb +2 -0
  246. data/lib/pubid/oiml/parser.rb +35 -4
  247. data/lib/pubid/oiml/renderer.rb +23 -1
  248. data/lib/pubid/oiml/single_identifier.rb +4 -0
  249. data/lib/pubid/oiml/supplement_identifier.rb +7 -0
  250. data/lib/pubid/oiml/urn_generator.rb +28 -0
  251. data/lib/pubid/oiml.rb +6 -1
  252. data/lib/pubid/omg/CLAUDE.md +15 -0
  253. data/lib/pubid/omg/builder.rb +2 -0
  254. data/lib/pubid/omg/identifier.rb +1 -1
  255. data/lib/pubid/parg/artifact.rb +46 -0
  256. data/lib/pubid/parg/backend.rb +92 -0
  257. data/lib/pubid/parg.rb +8 -0
  258. data/lib/pubid/parser/grammar.rb +23 -0
  259. data/lib/pubid/pg.rb +8 -0
  260. data/lib/pubid/plateau/builder.rb +2 -0
  261. data/lib/pubid/plateau/identifiers/base.rb +4 -0
  262. data/lib/pubid/plateau/supplement_identifier.rb +14 -2
  263. data/lib/pubid/plateau/urn_generator.rb +7 -1
  264. data/lib/pubid/plateau.rb +1 -2
  265. data/lib/pubid/renderers/human_readable.rb +0 -1
  266. data/lib/pubid/sae/builder.rb +2 -0
  267. data/lib/pubid/sae/components/date.rb +2 -0
  268. data/lib/pubid/sae/components/type.rb +2 -0
  269. data/lib/pubid/sae/identifiers/base.rb +1 -1
  270. data/lib/pubid/subset_match.rb +197 -0
  271. data/lib/pubid/tgpp/CLAUDE.md +43 -0
  272. data/lib/pubid/tgpp/builder.rb +2 -0
  273. data/lib/pubid/tgpp/identifier.rb +15 -1
  274. data/lib/pubid/type_resolver.rb +14 -2
  275. data/lib/pubid/un/builder.rb +2 -0
  276. data/lib/pubid/un/identifier.rb +1 -1
  277. data/lib/pubid/version.rb +1 -1
  278. data/lib/pubid/w3c/CLAUDE.md +7 -0
  279. data/lib/pubid/w3c/builder.rb +2 -0
  280. data/lib/pubid/w3c/identifier.rb +1 -1
  281. data/lib/pubid/xsf/CLAUDE.md +11 -0
  282. data/lib/pubid/xsf/builder.rb +2 -0
  283. data/lib/pubid/xsf/identifier.rb +1 -1
  284. data/lib/pubid.rb +17 -3
  285. metadata +78 -2
@@ -50,6 +50,16 @@ module Pubid
50
50
  return sp
51
51
  end
52
52
 
53
+ # Radio Regulations — "ITU-R RR (2020)"
54
+ if data[:radio_regulations]
55
+ return Identifiers::RadioRegulations.new(
56
+ sector: Components::Sector.new(sector: data[:sector].to_s),
57
+ series: Components::Series.new(series: "RR"),
58
+ date: data[:year] ? build_date(data) : nil,
59
+ language: data[:language]&.to_s,
60
+ )
61
+ end
62
+
53
63
  # Check if this is a supplement identifier
54
64
  if data[:supplement_type]
55
65
  supp = build_supplement(data)
@@ -153,11 +163,12 @@ module Pubid
153
163
  nil
154
164
  end
155
165
 
156
- # Build Special Publication (OB). Sector is silently dropped — OB is a
157
- # cross-bureau publication and `Identifier` rejects sector+OB
158
- # in its constructor.
166
+ # Build Special Publication (OB). The sector of the TSB spelling
167
+ # ("ITU-T OB.1096") is kept so the bulletin renders back as it was cited;
168
+ # SpecialPublication#== ignores it, since OB is cross-bureau.
159
169
  def build_special_publication(data)
160
170
  Identifiers::SpecialPublication.new(
171
+ sector: (Components::Sector.new(sector: data[:sector].to_s) if data[:sector]),
161
172
  series: Components::Series.new(series: "OB"),
162
173
  code: data[:number] ? build_code(data) : nil,
163
174
  date: data[:year] ? build_date(data) : nil,
@@ -344,10 +355,19 @@ module Pubid
344
355
  )
345
356
  end
346
357
 
358
+ # The Roman month of a bulletin date ("15.III.2016") is stored as the
359
+ # two-digit month every other ITU date uses; the day marks the spelling.
347
360
  def build_date(data)
361
+ month = if data[:roman_month]
362
+ (Identifiers::SpecialPublication::ROMAN_MONTHS.index(data[:roman_month].to_s) + 1)
363
+ .to_s.rjust(2, "0")
364
+ else
365
+ data[:month]&.to_s
366
+ end
348
367
  Pubid::Components::Date.new(
349
368
  year: data[:year].to_s,
350
- month: data[:month]&.to_s,
369
+ month: month,
370
+ day: data[:day]&.to_s,
351
371
  )
352
372
  end
353
373
 
@@ -368,3 +388,5 @@ module Pubid
368
388
  end
369
389
  end
370
390
  end
391
+
392
+ Pubid::Itu::Builder.prepend(Pubid::Builder::AllPartsWrap)
@@ -20,6 +20,8 @@ module Pubid
20
20
  # +subseries+ (dot-separated, flavor-specific) and +parts+
21
21
  # (dash-separated).
22
22
  class Code < Lutaml::Model::Serializable
23
+ include ::Pubid::SubsetMatch
24
+
23
25
  attribute :imp_marker, :string
24
26
  attribute :number, :string
25
27
  attribute :series_suffix, :string
@@ -13,6 +13,8 @@ module Pubid
13
13
  #
14
14
  # Format: SERIES.CODE (e.g. "Y.1351", "Y.1362-2")
15
15
  class Designation < Lutaml::Model::Serializable
16
+ include ::Pubid::SubsetMatch
17
+
16
18
  attribute :series, Pubid::Itu::Components::Series
17
19
  attribute :code, Pubid::Itu::Components::Code
18
20
 
@@ -8,6 +8,8 @@ module Pubid
8
8
  # ITU Sector component
9
9
  # Values: R (Radio), T (Telecommunications), D (Development)
10
10
  class Sector < Lutaml::Model::Serializable
11
+ include ::Pubid::SubsetMatch
12
+
11
13
  attribute :sector, :string # R, T, or D
12
14
 
13
15
  VALID_SECTORS = %w[R T D].freeze
@@ -8,6 +8,8 @@ module Pubid
8
8
  # ITU Series component
9
9
  # Examples: BO, V, X, A, etc.
10
10
  class Series < Lutaml::Model::Serializable
11
+ include ::Pubid::SubsetMatch
12
+
11
13
  attribute :series, :string # e.g., BO, V, X, R, SG1, OB
12
14
 
13
15
  def to_s
@@ -15,7 +15,7 @@ module Pubid
15
15
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
16
16
  end
17
17
 
18
- parsed = Parser.parse(normalize_whitespace(identifier))
18
+ parsed = Pubid::Parg::Backend.parse(:itu, normalize_whitespace(identifier))
19
19
  Builder.build(parsed)
20
20
  end
21
21
 
@@ -101,8 +101,6 @@ module Pubid
101
101
  end
102
102
 
103
103
  super
104
-
105
- validate_ob_no_sector!
106
104
  end
107
105
 
108
106
  # The document number lives on the `code` component for ITU; surface it at
@@ -462,6 +460,16 @@ module Pubid
462
460
  model.code ||= Components::Code.new
463
461
  end
464
462
 
463
+ # The day is set only by the printed bulletin date ("15.III.2016"), so
464
+ # it emits only there and no existing index row gains a key.
465
+ def day_to_kv(model, doc)
466
+ emit_kv(doc, "day", model.date&.day)
467
+ end
468
+
469
+ def day_from_kv(model, value)
470
+ date_for(model).day = value.to_s
471
+ end
472
+
465
473
  def date_for(model)
466
474
  model.date ||= Pubid::Components::Date.new
467
475
  end
@@ -472,21 +480,6 @@ module Pubid
472
480
  str = value.to_s
473
481
  LANGUAGES[str] || str
474
482
  end
475
-
476
- # OB (Operational Bulletin) is a cross-bureau ITU publication and
477
- # must not have a sector. Direct construction with both raises;
478
- # the parser silently drops sector for legacy strings like
479
- # "ITU-T OB.X" (handled in Builder).
480
- def validate_ob_no_sector!
481
- return unless series&.series == "OB"
482
- return if sector.nil?
483
- return if sector.is_a?(Components::Sector) && (sector.sector.nil? || sector.sector.to_s.empty?)
484
-
485
- raise ArgumentError,
486
- "OB (Operational Bulletin) is a cross-bureau ITU publication; " \
487
- "sector must not be set"
488
- end
489
483
  end
490
-
491
484
  end
492
485
  end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pubid
4
+ module Itu
5
+ module Identifiers
6
+ # The ITU Radio Regulations — the treaty text revised by each World
7
+ # Radiocommunication Conference.
8
+ # Format: ITU-R RR [(YYYY)]
9
+ # Example: ITU-R RR (2020)
10
+ #
11
+ # It has no document number: "RR" is the whole designation, stored as
12
+ # the series. `#number` returns it, so `root.number` (the relaton-index
13
+ # key) is not empty.
14
+ class RadioRegulations < Identifier
15
+ include StandardSerialization
16
+
17
+ def number
18
+ series&.series
19
+ end
20
+
21
+ def render_base(**_opts)
22
+ "#{publisher}-#{sector} #{series}#{render_date_suffix}"
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -4,26 +4,60 @@ module Pubid
4
4
  module Itu
5
5
  module Identifiers
6
6
  # ITU Special Publication — currently models the Operational Bulletin (OB).
7
- # OB is a cross-bureau publication (no sector) rendered as
8
- # "ITU OB No. {number}" with optional date.
9
- #
10
- # Pattern: "ITU OB No. 1283 (01/2024)"
7
+ # OB is a cross-bureau publication. ITU prints it without a sector
8
+ # ("ITU OB No. 1283 (01/2024)"); the TSB spelling carries one
9
+ # ("ITU-T OB.1096 (2016)"), which is kept and rendered back. The sector
10
+ # is not part of the identity: `==`, the URN and the MR slug ignore it,
11
+ # so both spellings name one bulletin.
11
12
  class SpecialPublication < Identifier
12
13
  include StandardSerialization
13
14
 
15
+ ROMAN_MONTHS = %w[I II III IV V VI VII VIII IX X XI XII].freeze
16
+
14
17
  def render_base(**_opts)
15
18
  number = code&.number
16
- result = "#{publisher} #{series} No. #{number}"
17
-
18
- if date
19
- result += if date.month
20
- " (#{date.month.to_s.rjust(2, '0')}/#{date.year})"
21
- else
22
- " (#{date.year})"
23
- end
24
- end
19
+ result = if sector
20
+ "#{publisher}-#{sector} #{series}.#{number}"
21
+ else
22
+ "#{publisher} #{series} No. #{number}"
23
+ end
24
+
25
+ result + render_ob_date
26
+ end
27
+
28
+ # Cross-bureau: the sector is how one bureau cites the bulletin, not
29
+ # which bulletin it is.
30
+ def ==(other)
31
+ return false unless other.instance_of?(self.class)
32
+
33
+ series == other.series &&
34
+ code == other.code &&
35
+ date == other.date &&
36
+ language == other.language &&
37
+ common_text_twin == other.common_text_twin
38
+ end
39
+
40
+ # Keep the MR slug sector-free, like `==` (it was always so, because
41
+ # the sector used to be dropped).
42
+ def mr_type
43
+ nil
44
+ end
25
45
 
26
- result
46
+ private
47
+
48
+ # " (MM/YYYY)" or " (YYYY)"; the day-bearing date is the printed
49
+ # bulletin form " - 15.III.2016", the only spelling that sets a day.
50
+ def render_ob_date
51
+ return "" unless date
52
+
53
+ if date.day && date.month
54
+ roman = ROMAN_MONTHS[date.month.to_i - 1]
55
+ " - #{date.day.to_s.rjust(2, '0')}.#{roman}.#{date.year}"
56
+ elsif date.month
57
+ " (#{date.month.to_s.rjust(2, '0')}/#{date.year})"
58
+ else
59
+ " (#{date.year})"
60
+ end
27
61
  end
28
62
  end
29
63
  end
@@ -46,6 +46,8 @@ module Pubid
46
46
  with: { to: :year_to_kv, from: :year_from_kv }
47
47
  map "month",
48
48
  with: { to: :month_to_kv, from: :month_from_kv }
49
+ map "day",
50
+ with: { to: :day_to_kv, from: :day_from_kv }
49
51
  map "language", to: :language
50
52
  map "common_text_twin",
51
53
  with: { to: :common_text_twin_to_kv,
@@ -131,6 +131,21 @@ module Pubid
131
131
  # Builder#build_supplement) and are deliberately not serialized (see
132
132
  # supplement_sector_to_kv), so comparing them would make a parsed
133
133
  # identifier unequal to the same identifier rebuilt via from_hash.
134
+ # A subset match follows the same rules as `==` below: the two
135
+ # rendering flags are not identity, and when a base is present the
136
+ # sector/series/code copies of the base are not serialized.
137
+ SUBSET_BASE_COPIES = %i[sector series code series_word].freeze
138
+
139
+ def self.subset_ignored_attributes
140
+ %i[number_glued slash_joined]
141
+ end
142
+
143
+ def subset_attribute_match?(name, mine, theirs)
144
+ return true if base && SUBSET_BASE_COPIES.include?(name)
145
+
146
+ super
147
+ end
148
+
134
149
  def ==(other)
135
150
  return false unless other.instance_of?(self.class)
136
151
 
@@ -16,6 +16,7 @@ module Pubid
16
16
  autoload :Errata, "#{__dir__}/identifiers/errata"
17
17
  autoload :Handbook, "#{__dir__}/identifiers/handbook"
18
18
  autoload :Question, "#{__dir__}/identifiers/question"
19
+ autoload :RadioRegulations, "#{__dir__}/identifiers/radio_regulations"
19
20
  autoload :Recommendation, "#{__dir__}/identifiers/recommendation"
20
21
  autoload :Report, "#{__dir__}/identifiers/report"
21
22
  autoload :SpecialPublication, "#{__dir__}/identifiers/special_publication"
@@ -125,9 +125,48 @@ module Pubid
125
125
  dash >> (letter.repeat(1, 3) >> dot >> digits).as(:range_end)
126
126
  end
127
127
 
128
- # Parts
128
+ # A "-YYYYMM" approval date — the "200307" of "T-REC-T.4-200307-I" and
129
+ # "ITU-T T.4-200307". ITU's own edition suffix is short ("-5"), so six
130
+ # digits that read as a plausible year (19xx/20xx) and month (01-12) are
131
+ # the date, never a part. A six-digit run that fails either test
132
+ # ("-200313", "-180001") is still a part, as it was before.
133
+ rule(:yyyymm_year) { (str("19") | str("20")) >> digit >> digit }
134
+ rule(:yyyymm_month) do
135
+ (str("0") >> match["1-9"]) | (str("1") >> match["0-2"])
136
+ end
137
+ rule(:yyyymm_shape) { yyyymm_year >> yyyymm_month >> digit.absent? }
138
+
139
+ # The status letter that trails the date in a publication id — "I" (in
140
+ # force) or "S" (superseded). It names the state of the edition, not the
141
+ # edition, so it is parsed and dropped. "S" is also the Spanish language
142
+ # suffix, so it is a status ONLY in the full "T-REC-…" id, where ITU
143
+ # always writes one; after an "ITU-T …-YYYYMM" print form only "I" is,
144
+ # and "-S" stays the language ("ITU-T Z.100-199911-S").
145
+ rule(:id_status) do
146
+ dash >> match["IS"] >> match["A-Za-z0-9"].absent?
147
+ end
148
+
149
+ rule(:print_id_status) do
150
+ dash >> str("I") >> match["A-Za-z0-9"].absent?
151
+ end
152
+
153
+ rule(:yyyymm_date) do
154
+ dash >> yyyymm_year.as(:year) >> yyyymm_month.as(:month) >>
155
+ digit.absent?
156
+ end
157
+
158
+ rule(:id_date) { yyyymm_date >> print_id_status.maybe }
159
+
160
+ # Either date spelling of a Recommendation.
161
+ rule(:document_date) { date_part | id_date }
162
+
163
+ # "ITU-T REC T.4", "ITU-T REC-T.4" — the redundant type word of ITU's
164
+ # own URLs. Not captured: a Recommendation is the default type.
165
+ rule(:rec_word) { str("REC") >> (space | dash) }
166
+
167
+ # Parts. The yyyymm guard keeps the approval date out of the part list.
129
168
  rule(:part) do
130
- dash >> digits.as(:part)
169
+ dash >> yyyymm_shape.absent? >> digits.as(:part)
131
170
  end
132
171
 
133
172
  rule(:parts) { part.repeat(0).as(:parts) }
@@ -273,6 +312,7 @@ module Pubid
273
312
  itu_prefix >>
274
313
  sector >>
275
314
  space >>
315
+ rec_word.maybe >>
276
316
  series >> dot >>
277
317
  code >>
278
318
  range_end.maybe >>
@@ -281,7 +321,7 @@ module Pubid
281
321
  series_word.maybe >>
282
322
  attachment.maybe >>
283
323
  version_part.maybe >>
284
- date_part.maybe
324
+ document_date.maybe
285
325
  end
286
326
 
287
327
  rule(:base_without_series) do
@@ -292,7 +332,7 @@ module Pubid
292
332
  code_suffixes >>
293
333
  attachment.maybe >>
294
334
  version_part.maybe >>
295
- date_part.maybe
335
+ document_date.maybe
296
336
  end
297
337
 
298
338
  # A series-code document — "EMC-5", "MES-2", "QOS-2", "IMPL-8",
@@ -311,12 +351,10 @@ module Pubid
311
351
  # The number stays in `code.number`, so `root.number` — the field
312
352
  # relaton-index bsearches on — is "5" for EMC-5 and "QKD" for SEC-QKD
313
353
  # rather than nil.
314
- # The OB guard keeps "ITU-T OB-1" a clean parse failure. Without it the
315
- # string reaches Builder#build's Recommendation fallback, whose
316
- # validate_ob_no_sector! raises an ArgumentError that escapes
317
- # Identifier.parse's Parslet::ParseFailed rescue — turning a rejected
318
- # input into a crash for callers. It guards "OB" + dash specifically, so
319
- # a genuine two-letter mnemonic starting "OB" would still parse.
354
+ # The OB guard keeps "ITU-T OB-1" a clean parse failure rather than a
355
+ # Recommendation of a series "OB" — the Operational Bulletin's series
356
+ # name. It guards "OB" + dash specifically, so a genuine two-letter
357
+ # mnemonic starting "OB" would still parse.
320
358
  rule(:series_code_body) do
321
359
  (str("OB") >> dash).absent? >>
322
360
  letter.repeat(2).as(:series) >> dash.as(:series_dash) >>
@@ -365,10 +403,8 @@ module Pubid
365
403
  # Builder#build's `combined` branch, which builds a CombinedIdentifier and
366
404
  # would silently drop the marker — a clean parse failure is better than a
367
405
  # Report that comes back as a Recommendation. The OB guard mirrors
368
- # series_code_body's: an Operational Bulletin is cross-bureau, and
369
- # "Report ITU-T OB.1" would otherwise route to SpecialPublication (marker
370
- # dropped) or hit validate_ob_no_sector!, whose ArgumentError escapes
371
- # Identifier.parse's Parslet::ParseFailed rescue.
406
+ # series_code_body's: "Report ITU-T OB.1" would otherwise route to
407
+ # SpecialPublication with the marker dropped.
372
408
  rule(:report_body) do
373
409
  (str("OB") >> dot).absent? >>
374
410
  (series >> dot).maybe >>
@@ -535,6 +571,7 @@ module Pubid
535
571
  itu_prefix >>
536
572
  sector >>
537
573
  space >>
574
+ rec_word.maybe >>
538
575
  series >> dot >>
539
576
  code >>
540
577
  range_end.maybe >>
@@ -543,7 +580,7 @@ module Pubid
543
580
  series_word.maybe >>
544
581
  attachment.maybe >>
545
582
  version_part.maybe >>
546
- date_part.maybe >>
583
+ document_date.maybe >>
547
584
  language.maybe
548
585
  end
549
586
 
@@ -556,24 +593,71 @@ module Pubid
556
593
  code_suffixes >>
557
594
  attachment.maybe >>
558
595
  version_part.maybe >>
559
- date_part.maybe >>
596
+ document_date.maybe >>
597
+ language.maybe
598
+ end
599
+
600
+ # ITU's publication id — "T-REC-T.4-200307-I",
601
+ # "R-REC-BO.1130-5-202602-I": <sector>-REC-<number>[-<edition>]-<YYYYMM>
602
+ # [-<status>], the name ITU gives each edition in its URLs and PDF
603
+ # files. It builds the plain Recommendation it names and renders in the
604
+ # print form ("ITU-T T.4 (07/2003)"). The date is required: without it
605
+ # the string names no edition. No other rule starts with a bare sector
606
+ # letter, so the slot is free.
607
+ rule(:publication_id) do
608
+ sector >> dash >> str("REC") >> dash >>
609
+ series >> dot >> code >> yyyymm_date >> id_status.maybe >>
560
610
  language.maybe
561
611
  end
562
612
 
613
+ # The Radio Regulations — "ITU-R RR", "ITU-R RR (2020)", and the URL
614
+ # spelling "ITU-R RR-2020". Always ITU-R. The trailing any.absent? is
615
+ # load-bearing: PEG ordered choice never re-enters the alternation once
616
+ # an alternative succeeds, so a partial match on "ITU-R RR.1" must fail
617
+ # here and fall through to with_series.
618
+ rule(:radio_regulations) do
619
+ itu_prefix >> str("R").as(:sector) >> space >>
620
+ str("RR").as(:radio_regulations) >>
621
+ (date_part | (dash >> digit.repeat(4, 4).as(:year))).maybe >>
622
+ language.maybe >> any.absent?
623
+ end
624
+
563
625
  # OB (Operational Bulletin) — Special Publication.
564
- # OB is a cross-bureau ITU publication; sector, when present in legacy
565
- # strings like "ITU-T OB.1096", is silently dropped by the builder.
626
+ # OB is a cross-bureau ITU publication. The TSB spelling carries a
627
+ # sector ("ITU-T OB.1096 (2016)"); the builder keeps it, and it renders
628
+ # back, but it is not part of the bulletin's identity.
566
629
  rule(:ob_series) { str("OB").as(:series) }
567
630
 
568
631
  rule(:ob_dot_body) { dot >> number }
569
632
  rule(:ob_no_body) { space >> str("No.") >> space >> number }
633
+ # "ITU OB 1000" — metanorma-itu's docidentifier ("Annex to ITU OB %").
634
+ # Accepted as an input spelling only; it renders "ITU OB No. 1000", the
635
+ # form ITU's own bulletin site uses.
636
+ rule(:ob_bare_body) { space >> number }
637
+
638
+ # The date as a bulletin prints it — "ITU-T OB.1096 - 15.III.2016": day,
639
+ # Roman month, year. The months are tried longest first, because PEG
640
+ # takes the first alternative that matches and "I" would otherwise win
641
+ # on "III"; "XIII" matches "XII", then fails on the required dot.
642
+ rule(:roman_month) do
643
+ %w[XII XI X IX VIII VII VI V IV III II I]
644
+ .map { |m| str(m) }.reduce(:|)
645
+ end
646
+
647
+ rule(:ob_roman_date) do
648
+ str(" - ") >> digit.repeat(2, 2).as(:day) >> dot >>
649
+ roman_month.as(:roman_month) >> dot >>
650
+ digit.repeat(4, 4).as(:year)
651
+ end
652
+
653
+ rule(:ob_date) { date_part | ob_roman_date }
570
654
 
571
655
  rule(:ob_with_sector) do
572
656
  itu_prefix >>
573
657
  (sector >> space).maybe >>
574
658
  ob_series >>
575
- (ob_dot_body | ob_no_body) >>
576
- date_part.maybe >>
659
+ (ob_dot_body | ob_no_body | ob_bare_body) >>
660
+ ob_date.maybe >>
577
661
  language.maybe
578
662
  end
579
663
 
@@ -585,7 +669,7 @@ module Pubid
585
669
  str("Operational Bulletin").as(:_op_bull) >>
586
670
  space >> str("No.") >> space >>
587
671
  number >>
588
- date_part.maybe >>
672
+ ob_date.maybe >>
589
673
  language.maybe
590
674
  end
591
675
 
@@ -671,13 +755,15 @@ module Pubid
671
755
  handbook |
672
756
  numeric_question |
673
757
  letter_question |
758
+ radio_regulations |
674
759
  with_series |
675
760
  contribution |
676
761
  # Unreachable earlier: special_publication needs the literal "OB",
677
762
  # handbook/numeric_question need leading digits, letter_question
678
763
  # needs series >> dot, and contribution needs "-C" after the series.
679
764
  series_code_identifier |
680
- without_series
765
+ without_series |
766
+ publication_id
681
767
  end
682
768
 
683
769
  # Common-text form: an ITU identifier followed by "| ISO/IEC ...".
@@ -16,7 +16,10 @@ module Pubid
16
16
  def generate_base_urn
17
17
  parts = ["urn", "itu"]
18
18
 
19
- if identifier.sector
19
+ # An Operational Bulletin is cross-bureau: its sector is a spelling,
20
+ # not identity (see SpecialPublication#==), so it stays out of the URN.
21
+ if identifier.sector &&
22
+ !identifier.is_a?(Identifiers::SpecialPublication)
20
23
  sector = identifier.sector.to_s
21
24
  parts << sector.to_s.downcase
22
25
  else
@@ -56,7 +59,11 @@ module Pubid
56
59
 
57
60
  if identifier.date
58
61
  date = identifier.date
59
- if date&.year && date.month
62
+ # Only an Operational Bulletin's printed date carries a day
63
+ # ("15.III.2016"); it is in `==`, so it must reach the URN too.
64
+ if date&.year && date.month && date.day
65
+ parts << "#{date.day}/#{date.month}/#{date.year}"
66
+ elsif date&.year && date.month
60
67
  parts << "#{date.month}/#{date.year}"
61
68
  elsif date&.year
62
69
  parts << date.year.to_s
@@ -0,0 +1,7 @@
1
+ # JCGM flavor notes
2
+
3
+ JCGM meetings and URN parsing.
4
+
5
+ These notes were part of the root `CLAUDE.md`. Read them before you change `lib/pubid/jcgm/` or `spec/pubid/jcgm/`. The root file keeps the cross-flavor contract that every flavor obeys.
6
+
7
+ - **JCGM meeting year is optional (`JCGM 11st Meeting`)**: a meeting's trailing ` (YYYY)` group is a *separable* component, not part of its identity — JCGM numbers its meetings in one sequence, so the ordinal alone names the event. `rule(:meeting_identifier)` wraps the **whole** group in `.maybe` (not the year inside it, so a dangling `(`, an empty `()` or a truncated `(200)` still fails), mirroring `date_portion.maybe` in `rule(:base)`; `render_meeting` appends the group only when `date` is present. No builder or serialization change was needed: `Builder::Base#assign_attributes` iterates the parsed hash, so an absent `:date` key is simply never assigned, and `SingleIdentifier#emit_kv` already drops a nil value — a dateless meeting is `{_type, number}` and a dated one is byte-identical to before, so **no index migration follows**. **The load-bearing third surface is `UrnParser#parse_meeting_urn`**: `UrnGenerator#generate_meeting_urn` had *always* guarded the date and emitted the short `urn:jcgm:meeting:11`, but the parser rebuilt the ungrammatical `"JCGM 11st Meeting ()"` from it — so the dateless form must be made optional on the URN read-back too, or the round-trip the relaton index depends on breaks. **The whole `UrnParser` was converted to build identifiers directly from the URN segments** (the BIPM pattern; 5 of 38 flavors construct directly, the rest use the shared `flavor_parse` helper, which stays for them). Rendering a string and re-parsing it flattens the segments and silently loses whatever the string form cannot carry, which is what three **pre-existing** defects were: `urn:jcgm:gum.6:2020` **raised** (`"JCGM gum.6:2020"` is not a grammar form); the language segment was dropped (`urn:jcgm:100:2008:en` → `JCGM 100:2008`); and `number, year = parts` discarded every segment past the second, so a supplement decayed into **the standard it amends** — `urn:jcgm:200:2008:corrigendum` → `JCGM 200:2008`, a different document, with no error. The supplement marker (`corrigendum`/`amendment`, written by the generator as the `type_code`) splits the segments: everything before it is the base document, everything after is the supplement's own number and date. Four things the string round-trip used to supply for free are now explicit, and each is load-bearing: (1) **`typed_stage` comes from `Jcgm.locate_stage`, the same registry lookup `Builder#locate_typed_stage` uses — NOT from the attribute default** — because `SingleIdentifier.published_typed_stage` additionally sets `original_abbr`, which would make a URN-reconstructed id **unequal** to a parsed one; the marker is mapped to the abbreviation the registry indexes (`amendment` → `Amd`, since `locate_stage` matches `abbr`, not `type_code`), and the class then comes from `Jcgm.locate_type(typed_stage.type_code)` so one lookup fixes both. **Guide and GumGuide deliberately take the default instead**, because the grammar emits no type token for them and `Builder#build` fills theirs from `published_typed_stage` too — matching the builder means matching *which* of the two paths it took. (2) The meeting number is normalized with **`to_i`**, matching `Identifiers::Meeting.ordinal`, so `011` still reads back as `11` and a missing or non-numeric segment as `0`. (3) The year is **validated against the grammar's `19xx|20xx`** and rejected with `Pubid::UrnParser::Errors::ParseError` (the easc/gost convention) — with no re-parse, nothing else would catch a malformed segment. (4) A language code is mapped back through the inverse of `Builder::Base::LANG_CHAR_MAP` to restore `original_code`, which is what the renderer prints (`en,fr` → `(E/F)`). **KNOWN GAP, generator-side and deliberately unchanged**: a full date renders as its year alone (`JCGM GUM-1:2022-11-28` → `urn:jcgm:gum.1:2022`), so a URN cannot restore month and day; widening it would change already-published URNs. 30 of the 32 corpus ids now round-trip through the URN byte-exactly, and those 2 are the truncated-date pair. (That `original_abbr` asymmetry is **since fixed** — see `lib/pubid/iec/CLAUDE.md` — so the default now agrees too and the lookup mirrors the builder for clarity rather than necessity.) **Deliberately not a typed error**: the earlier reading of this defect was to raise on a nil date; an undated meeting reference is unambiguous and legitimate, and raising would leave relaton's `Bib::ItemData#to_most_recent_reference` with nothing to return. **Do not add the English teens exception to `Identifiers::Meeting.ordinal`** — the real records print `11st`/`12nd`/`13rd`, and three published documents round-trip through that naive rule. Locked by `spec/pubid/jcgm/meeting_partial_spec.rb` plus a bare-form row in `urn_parser_spec.rb`. **Still open, separate call**: `Meeting.new(number: "11").to_s` raises `NoMethodError` (`number` is a `Components::Code`; a bare String is accepted at construction and only explodes at render), and `Meeting#ordinal` renders `"0th"` for a nil `number`. (hand-off: jcgm-meeting-render-nil-date.)
@@ -104,3 +104,5 @@ module Pubid
104
104
  end
105
105
  end
106
106
  end
107
+
108
+ Pubid::Jcgm::Builder.prepend(Pubid::Builder::AllPartsWrap)
@@ -6,6 +6,8 @@ module Pubid
6
6
  module Jcgm
7
7
  module Components
8
8
  class Publisher < Lutaml::Model::Serializable
9
+ include ::Pubid::SubsetMatch
10
+
9
11
  attribute :publisher, :string
10
12
 
11
13
  def to_s
data/lib/pubid/jcgm.rb CHANGED
@@ -35,7 +35,7 @@ module Pubid
35
35
  parser = Parser.new
36
36
  builder = Builder.new
37
37
 
38
- parsed = parser.parse(identifier)
38
+ parsed = Pubid::Parg::Backend.parse(:jcgm, identifier)
39
39
  builder.build(parsed)
40
40
  end
41
41
 
@@ -20,6 +20,9 @@ module Pubid
20
20
  build_single_identifier(data)
21
21
  end
22
22
  attach_symbol(identifier, data)
23
+ # "(all parts)" names every part of the document, so it wraps the
24
+ # document, which holds no mark itself.
25
+ data[:all_parts] ? identifier.to_all_parts : identifier
23
26
  end
24
27
 
25
28
  private
@@ -37,7 +40,6 @@ module Pubid
37
40
  parts: extract_part_strings(data[:parts]),
38
41
  year: data[:year]&.to_i,
39
42
  language: data[:language]&.to_s,
40
- all_parts: (true if data[:all_parts]),
41
43
  reaffirmed: (true if data[:reaffirmed]),
42
44
  }
43
45
 
@@ -114,3 +116,5 @@ module Pubid
114
116
  end
115
117
  end
116
118
  end
119
+
120
+ Pubid::Jis::Builder.prepend(Pubid::Builder::AllPartsWrap)