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.
- checksums.yaml +4 -4
- data/README.adoc +43 -1
- data/data/ieee/update_codes.yaml +17 -4
- data/data/nist/update_codes.yaml +7 -3
- data/data/parg/tables/bipm_groups.yaml +14 -0
- data/data/parg/tables/bipm_type_codes.yaml +5 -0
- data/data/parg/tables/bipm_type_names_en.yaml +6 -0
- data/data/parg/tables/bipm_type_names_fr.yaml +6 -0
- data/data/parg/tables/directives_supplements_typed_stages.yaml +3 -0
- data/data/parg/tables/directives_typed_stages.yaml +5 -0
- data/data/parg/tables/idf_typed_stages.yaml +27 -0
- data/data/parg/tables/idf_typed_stages_supplements.yaml +2 -0
- data/data/parg/tables/iec_typed_stages.yaml +130 -0
- data/data/parg/tables/iso_publishers.yaml +4 -0
- data/data/parg/tables/organizations.yaml +12 -0
- data/data/parg/tables/tc_types.yaml +42 -0
- data/data/parg/tables/typed_stages.yaml +114 -0
- data/data/parg/tables/typed_stages_supplements.yaml +64 -0
- data/data/parg/tables/wg_types.yaml +21 -0
- data/lib/pubid/adobe/builder.rb +2 -0
- data/lib/pubid/adobe/identifier.rb +11 -1
- data/lib/pubid/all_parts.rb +201 -0
- data/lib/pubid/all_parts_identifier.rb +19 -0
- data/lib/pubid/amca/CLAUDE.md +47 -0
- data/lib/pubid/amca/builder.rb +3 -5
- data/lib/pubid/amca/identifiers/base.rb +11 -1
- data/lib/pubid/amca/identifiers/publication.rb +13 -0
- data/lib/pubid/amca/parser.rb +2 -1
- data/lib/pubid/amca/renderer.rb +22 -33
- data/lib/pubid/amca/urn_generator.rb +21 -2
- data/lib/pubid/amca/urn_parser.rb +36 -10
- data/lib/pubid/ansi/builder.rb +6 -0
- data/lib/pubid/ansi/identifier.rb +1 -1
- data/lib/pubid/api/CLAUDE.md +23 -0
- data/lib/pubid/api/builder.rb +2 -0
- data/lib/pubid/api/identifier.rb +1 -1
- data/lib/pubid/api/parser.rb +8 -4
- data/lib/pubid/ashrae/CLAUDE.md +13 -0
- data/lib/pubid/ashrae/builder.rb +58 -14
- data/lib/pubid/ashrae/identifiers/base.rb +10 -1
- data/lib/pubid/ashrae/identifiers/errata.rb +14 -2
- data/lib/pubid/ashrae/identifiers/interpretation.rb +2 -10
- data/lib/pubid/ashrae/parser.rb +80 -39
- data/lib/pubid/ashrae/renderer.rb +32 -1
- data/lib/pubid/ashrae/urn_generator.rb +32 -9
- data/lib/pubid/asme/CLAUDE.md +25 -0
- data/lib/pubid/asme/builder.rb +16 -9
- data/lib/pubid/asme/components/code.rb +2 -0
- data/lib/pubid/asme/identifier.rb +1 -1
- data/lib/pubid/asme/identifiers/standard.rb +6 -1
- data/lib/pubid/asme/parser.rb +41 -14
- data/lib/pubid/astm/CLAUDE.md +9 -0
- data/lib/pubid/astm/builder.rb +2 -0
- data/lib/pubid/astm/components/code.rb +2 -0
- data/lib/pubid/astm/identifier.rb +1 -1
- data/lib/pubid/astm/parser.rb +4 -1
- data/lib/pubid/bipm/CLAUDE.md +11 -0
- data/lib/pubid/bipm/builder.rb +2 -0
- data/lib/pubid/bipm/identifier.rb +1 -1
- data/lib/pubid/bsi/CLAUDE.md +93 -0
- data/lib/pubid/bsi/builder.rb +13 -11
- data/lib/pubid/bsi/identifiers/addendum_document.rb +2 -0
- data/lib/pubid/bsi/identifiers/adopted_european_norm.rb +6 -54
- data/lib/pubid/bsi/identifiers/adopted_international_standard.rb +5 -22
- data/lib/pubid/bsi/identifiers/amendment.rb +36 -12
- data/lib/pubid/bsi/identifiers/bundled_identifier.rb +2 -0
- data/lib/pubid/bsi/identifiers/consolidated_identifier.rb +23 -26
- data/lib/pubid/bsi/identifiers/corrigendum.rb +29 -12
- data/lib/pubid/bsi/identifiers/expert_commentary.rb +6 -7
- data/lib/pubid/bsi/identifiers/national_annex.rb +18 -20
- data/lib/pubid/bsi/identifiers/root_identity.rb +31 -0
- data/lib/pubid/bsi/identifiers/set.rb +2 -0
- data/lib/pubid/bsi/identifiers/supplement_document.rb +2 -0
- data/lib/pubid/bsi/identifiers.rb +1 -0
- data/lib/pubid/bsi/parser.rb +8 -8
- data/lib/pubid/bsi/renderer.rb +20 -20
- data/lib/pubid/bsi/single_identifier.rb +1 -3
- data/lib/pubid/bsi/urn_generator.rb +28 -18
- data/lib/pubid/builder/base.rb +27 -0
- data/lib/pubid/calconnect/builder.rb +2 -0
- data/lib/pubid/calconnect/identifier.rb +5 -1
- data/lib/pubid/ccsds/builder.rb +2 -0
- data/lib/pubid/ccsds/identifier.rb +9 -1
- data/lib/pubid/cen_cenelec/CLAUDE.md +59 -0
- data/lib/pubid/cen_cenelec/builder.rb +6 -1
- data/lib/pubid/cen_cenelec/identifier.rb +12 -28
- data/lib/pubid/cen_cenelec/identifiers/amendment.rb +3 -10
- data/lib/pubid/cen_cenelec/identifiers/corrigendum.rb +3 -10
- data/lib/pubid/cen_cenelec/parser.rb +20 -5
- data/lib/pubid/cie/CLAUDE.md +58 -0
- data/lib/pubid/cie/builder.rb +2 -0
- data/lib/pubid/cie/components/language.rb +2 -0
- data/lib/pubid/cie/identifier.rb +1 -1
- data/lib/pubid/cie/parser.rb +9 -2
- data/lib/pubid/components/adoption.rb +2 -0
- data/lib/pubid/components/code.rb +2 -0
- data/lib/pubid/components/date.rb +8 -6
- data/lib/pubid/components/edition.rb +2 -0
- data/lib/pubid/components/iteration.rb +2 -0
- data/lib/pubid/components/language.rb +2 -0
- data/lib/pubid/components/locality.rb +2 -0
- data/lib/pubid/components/publisher.rb +2 -0
- data/lib/pubid/components/relationship.rb +2 -0
- data/lib/pubid/components/stage.rb +2 -0
- data/lib/pubid/components/supplement.rb +2 -0
- data/lib/pubid/components/type.rb +2 -0
- data/lib/pubid/components/typed_stage.rb +8 -0
- data/lib/pubid/conformance/checks.rb +1 -1
- data/lib/pubid/csa/CLAUDE.md +41 -0
- data/lib/pubid/csa/builder.rb +2 -0
- data/lib/pubid/csa/identifier.rb +19 -3
- data/lib/pubid/csa/parser.rb +25 -8
- data/lib/pubid/csa/renderer.rb +12 -12
- data/lib/pubid/csa/single_identifier.rb +17 -0
- data/lib/pubid/doi/builder.rb +2 -0
- data/lib/pubid/doi/identifier.rb +1 -1
- data/lib/pubid/easc/builder.rb +2 -0
- data/lib/pubid/easc/identifier.rb +10 -1
- data/lib/pubid/ecma/CLAUDE.md +28 -0
- data/lib/pubid/ecma/builder.rb +2 -0
- data/lib/pubid/ecma/identifier.rb +8 -1
- data/lib/pubid/etsi/CLAUDE.md +34 -0
- data/lib/pubid/etsi/builder.rb +2 -0
- data/lib/pubid/etsi/components/code.rb +6 -0
- data/lib/pubid/etsi/components/version.rb +2 -0
- data/lib/pubid/etsi/identifiers/base.rb +1 -1
- data/lib/pubid/etsi/identifiers/etsi_standard.rb +7 -0
- data/lib/pubid/evs/CLAUDE.md +58 -0
- data/lib/pubid/evs/builder.rb +2 -0
- data/lib/pubid/evs.rb +1 -1
- data/lib/pubid/gb/CLAUDE.md +140 -0
- data/lib/pubid/gb/builder.rb +7 -2
- data/lib/pubid/gb/identifier.rb +6 -4
- data/lib/pubid/gb/identifiers/all_parts.rb +17 -0
- data/lib/pubid/gb/identifiers.rb +1 -0
- data/lib/pubid/gb/renderer.rb +0 -1
- data/lib/pubid/gost/CLAUDE.md +64 -0
- data/lib/pubid/gost/builder.rb +3 -1
- data/lib/pubid/gost/identifier.rb +16 -1
- data/lib/pubid/gost/parser.rb +8 -1
- data/lib/pubid/iala/CLAUDE.md +82 -0
- data/lib/pubid/iala/builder.rb +2 -0
- data/lib/pubid/iala/identifier.rb +10 -1
- data/lib/pubid/iana/CLAUDE.md +7 -0
- data/lib/pubid/iana/builder.rb +2 -0
- data/lib/pubid/iana/identifier.rb +1 -1
- data/lib/pubid/identifier.rb +161 -17
- data/lib/pubid/idf/builder.rb +11 -1
- data/lib/pubid/idf/identifier.rb +5 -0
- data/lib/pubid/idf/identifiers/all_parts.rb +17 -0
- data/lib/pubid/idf/identifiers.rb +1 -0
- data/lib/pubid/iec/CLAUDE.md +31 -0
- data/lib/pubid/iec/builder.rb +7 -1
- data/lib/pubid/iec/components/consolidated_amendment.rb +4 -0
- data/lib/pubid/iec/components/sheet.rb +2 -0
- data/lib/pubid/iec/components/trf_info.rb +2 -0
- data/lib/pubid/iec/components/vap_suffix.rb +2 -0
- data/lib/pubid/iec/identifier.rb +8 -3
- data/lib/pubid/iec/identifiers/all_parts.rb +19 -0
- data/lib/pubid/iec/identifiers.rb +1 -0
- data/lib/pubid/iec/parser.rb +9 -4
- data/lib/pubid/iec/renderer.rb +0 -1
- data/lib/pubid/iec/urn_generator.rb +9 -1
- data/lib/pubid/iec/urn_parser.rb +3 -2
- data/lib/pubid/ieee/CLAUDE.md +97 -0
- data/lib/pubid/ieee/builder.rb +194 -27
- data/lib/pubid/ieee/components/code.rb +2 -0
- data/lib/pubid/ieee/components/draft.rb +35 -2
- data/lib/pubid/ieee/components/typed_stage.rb +2 -0
- data/lib/pubid/ieee/identifiers/base.rb +21 -1
- data/lib/pubid/ieee/identifiers/iec_ieee_copublished.rb +9 -0
- data/lib/pubid/ieee/identifiers/joint_development.rb +66 -23
- data/lib/pubid/ieee/identifiers/project_draft_identifier.rb +8 -1
- data/lib/pubid/ieee/parser.rb +156 -29
- data/lib/pubid/ieee/renderer.rb +42 -7
- data/lib/pubid/ieee/urn_generator.rb +31 -0
- data/lib/pubid/ietf/CLAUDE.md +7 -0
- data/lib/pubid/ietf/builder.rb +2 -0
- data/lib/pubid/ietf/identifiers/base.rb +1 -1
- data/lib/pubid/iho/builder.rb +2 -0
- data/lib/pubid/isbn/builder.rb +2 -0
- data/lib/pubid/isbn/identifier.rb +1 -1
- data/lib/pubid/iso/CLAUDE.md +47 -0
- data/lib/pubid/iso/builder.rb +19 -5
- data/lib/pubid/iso/components/publisher.rb +2 -0
- data/lib/pubid/iso/identifier.rb +10 -15
- data/lib/pubid/iso/identifiers/all_parts.rb +19 -0
- data/lib/pubid/iso/identifiers/directives_supplement.rb +4 -2
- data/lib/pubid/iso/identifiers.rb +1 -0
- data/lib/pubid/iso/normalizer.rb +4 -1
- data/lib/pubid/iso/rendering_style.rb +0 -1
- data/lib/pubid/itu/CLAUDE.md +115 -0
- data/lib/pubid/itu/builder.rb +26 -4
- data/lib/pubid/itu/components/code.rb +2 -0
- data/lib/pubid/itu/components/designation.rb +2 -0
- data/lib/pubid/itu/components/sector.rb +2 -0
- data/lib/pubid/itu/components/series.rb +2 -0
- data/lib/pubid/itu/identifiers/base.rb +11 -18
- data/lib/pubid/itu/identifiers/radio_regulations.rb +27 -0
- data/lib/pubid/itu/identifiers/special_publication.rb +48 -14
- data/lib/pubid/itu/identifiers/standard_serialization.rb +2 -0
- data/lib/pubid/itu/identifiers/supplement.rb +15 -0
- data/lib/pubid/itu/identifiers.rb +1 -0
- data/lib/pubid/itu/parser.rb +108 -22
- data/lib/pubid/itu/urn_generator.rb +9 -2
- data/lib/pubid/jcgm/CLAUDE.md +7 -0
- data/lib/pubid/jcgm/builder.rb +2 -0
- data/lib/pubid/jcgm/components/publisher.rb +2 -0
- data/lib/pubid/jcgm.rb +1 -1
- data/lib/pubid/jis/builder.rb +5 -1
- data/lib/pubid/jis/identifier.rb +6 -18
- data/lib/pubid/jis/identifiers/all_parts.rb +19 -0
- data/lib/pubid/jis/identifiers.rb +1 -0
- data/lib/pubid/jis/renderer.rb +0 -2
- data/lib/pubid/jis/urn_generator.rb +0 -1
- data/lib/pubid/nist/CLAUDE.md +56 -0
- data/lib/pubid/nist/builder.rb +3 -0
- data/lib/pubid/nist/components/edition.rb +2 -0
- data/lib/pubid/nist/components/issue_number.rb +2 -0
- data/lib/pubid/nist/components/part.rb +2 -0
- data/lib/pubid/nist/components/stage.rb +2 -0
- data/lib/pubid/nist/components/supplement.rb +2 -0
- data/lib/pubid/nist/components/translation.rb +2 -0
- data/lib/pubid/nist/components/update.rb +2 -0
- data/lib/pubid/nist/components/version.rb +2 -0
- data/lib/pubid/nist/components/volume.rb +2 -0
- data/lib/pubid/nist/identifiers/base.rb +39 -7
- data/lib/pubid/nist/parser.rb +24 -2
- data/lib/pubid/nist/preprocessor.rb +53 -2
- data/lib/pubid/nist/urn_parser.rb +10 -1
- data/lib/pubid/oasis/CLAUDE.md +19 -0
- data/lib/pubid/oasis/builder.rb +2 -0
- data/lib/pubid/oasis/identifier.rb +20 -1
- data/lib/pubid/ogc/CLAUDE.md +34 -0
- data/lib/pubid/ogc/builder.rb +2 -0
- data/lib/pubid/ogc/identifier.rb +12 -1
- data/lib/pubid/oiml/CLAUDE.md +189 -0
- data/lib/pubid/oiml/builder.rb +20 -0
- data/lib/pubid/oiml/components/code.rb +6 -0
- data/lib/pubid/oiml/identifier.rb +13 -0
- data/lib/pubid/oiml/identifiers/annex.rb +4 -0
- data/lib/pubid/oiml/identifiers/certification_system.rb +34 -0
- data/lib/pubid/oiml/identifiers/code_number.rb +8 -0
- data/lib/pubid/oiml/identifiers/dual_published.rb +174 -0
- data/lib/pubid/oiml/identifiers.rb +2 -0
- data/lib/pubid/oiml/parser.rb +35 -4
- data/lib/pubid/oiml/renderer.rb +23 -1
- data/lib/pubid/oiml/single_identifier.rb +4 -0
- data/lib/pubid/oiml/supplement_identifier.rb +7 -0
- data/lib/pubid/oiml/urn_generator.rb +28 -0
- data/lib/pubid/oiml.rb +6 -1
- data/lib/pubid/omg/CLAUDE.md +15 -0
- data/lib/pubid/omg/builder.rb +2 -0
- data/lib/pubid/omg/identifier.rb +1 -1
- data/lib/pubid/parg/artifact.rb +46 -0
- data/lib/pubid/parg/backend.rb +92 -0
- data/lib/pubid/parg.rb +8 -0
- data/lib/pubid/parser/grammar.rb +23 -0
- data/lib/pubid/pg.rb +8 -0
- data/lib/pubid/plateau/builder.rb +2 -0
- data/lib/pubid/plateau/identifiers/base.rb +4 -0
- data/lib/pubid/plateau/supplement_identifier.rb +14 -2
- data/lib/pubid/plateau/urn_generator.rb +7 -1
- data/lib/pubid/plateau.rb +1 -2
- data/lib/pubid/renderers/human_readable.rb +0 -1
- data/lib/pubid/sae/builder.rb +2 -0
- data/lib/pubid/sae/components/date.rb +2 -0
- data/lib/pubid/sae/components/type.rb +2 -0
- data/lib/pubid/sae/identifiers/base.rb +1 -1
- data/lib/pubid/subset_match.rb +197 -0
- data/lib/pubid/tgpp/CLAUDE.md +43 -0
- data/lib/pubid/tgpp/builder.rb +2 -0
- data/lib/pubid/tgpp/identifier.rb +15 -1
- data/lib/pubid/type_resolver.rb +14 -2
- data/lib/pubid/un/builder.rb +2 -0
- data/lib/pubid/un/identifier.rb +1 -1
- data/lib/pubid/version.rb +1 -1
- data/lib/pubid/w3c/CLAUDE.md +7 -0
- data/lib/pubid/w3c/builder.rb +2 -0
- data/lib/pubid/w3c/identifier.rb +1 -1
- data/lib/pubid/xsf/CLAUDE.md +11 -0
- data/lib/pubid/xsf/builder.rb +2 -0
- data/lib/pubid/xsf/identifier.rb +1 -1
- data/lib/pubid.rb +17 -3
- metadata +78 -2
data/lib/pubid/itu/builder.rb
CHANGED
|
@@ -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).
|
|
157
|
-
#
|
|
158
|
-
#
|
|
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:
|
|
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
|
|
|
@@ -15,7 +15,7 @@ module Pubid
|
|
|
15
15
|
raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
parsed =
|
|
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
|
|
8
|
-
# "ITU OB No.
|
|
9
|
-
#
|
|
10
|
-
#
|
|
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 =
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
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"
|
data/lib/pubid/itu/parser.rb
CHANGED
|
@@ -125,9 +125,48 @@ module Pubid
|
|
|
125
125
|
dash >> (letter.repeat(1, 3) >> dot >> digits).as(:range_end)
|
|
126
126
|
end
|
|
127
127
|
|
|
128
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
315
|
-
#
|
|
316
|
-
#
|
|
317
|
-
#
|
|
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:
|
|
369
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
565
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.)
|
data/lib/pubid/jcgm/builder.rb
CHANGED
data/lib/pubid/jcgm.rb
CHANGED
data/lib/pubid/jis/builder.rb
CHANGED
|
@@ -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)
|