pubid 2.0.5 → 2.0.7
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 +16 -0
- data/data/parg/bipm.json +1 -1
- data/data/parg/bipm.parg +290 -0
- data/lib/pubid/ansi/pubid_urn_schema.rb +44 -0
- data/lib/pubid/ansi.rb +1 -0
- data/lib/pubid/astm/CLAUDE.md +8 -0
- data/lib/pubid/astm/pubid_urn_schema.rb +47 -0
- data/lib/pubid/astm.rb +1 -0
- data/lib/pubid/bipm/CLAUDE.md +2 -0
- data/lib/pubid/bipm/builder.rb +26 -1
- data/lib/pubid/bipm/identifiers/committee_document.rb +13 -1
- data/lib/pubid/bipm/renderer.rb +18 -3
- data/lib/pubid/bipm/urn_generator.rb +14 -1
- data/lib/pubid/bipm/urn_parser.rb +14 -1
- data/lib/pubid/identifier.rb +15 -0
- data/lib/pubid/iec/pubid_urn_schema.rb +32 -0
- data/lib/pubid/iec.rb +1 -0
- data/lib/pubid/ieee/CLAUDE.md +2 -0
- data/lib/pubid/ieee/pubid_urn_schema.rb +150 -0
- data/lib/pubid/ieee.rb +1 -0
- data/lib/pubid/iso/pubid_urn_schema.rb +61 -0
- data/lib/pubid/iso.rb +1 -0
- data/lib/pubid/itu/CLAUDE.md +15 -0
- data/lib/pubid/itu/pubid_urn_schema.rb +82 -0
- data/lib/pubid/itu.rb +1 -0
- data/lib/pubid/nist/CLAUDE.md +13 -0
- data/lib/pubid/nist/pubid_urn_schema.rb +108 -0
- data/lib/pubid/nist.rb +1 -0
- data/lib/pubid/pubid_urn/decoder.rb +156 -0
- data/lib/pubid/pubid_urn/encoder.rb +175 -0
- data/lib/pubid/pubid_urn/escape.rb +60 -0
- data/lib/pubid/pubid_urn/paths.rb +87 -0
- data/lib/pubid/pubid_urn/schema.rb +184 -0
- data/lib/pubid/pubid_urn.rb +226 -0
- data/lib/pubid/type_resolver.rb +12 -0
- data/lib/pubid/version.rb +1 -1
- data/lib/pubid.rb +18 -0
- metadata +16 -2
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pubid
|
|
4
|
+
module Astm
|
|
5
|
+
# The pubid-scheme URN of ASTM (issue #656; docs/URN-SCHEMES.adoc).
|
|
6
|
+
#
|
|
7
|
+
# ASTM E2938-15(2023)
|
|
8
|
+
# urn:pubid:astm:std:2938:letter-E:year-2015:reapproval-2023
|
|
9
|
+
#
|
|
10
|
+
# The core is the document number; every other value is an optional
|
|
11
|
+
# prefixed atom. Each suffix marker ("-EB", "-EA", "DVD", the metric
|
|
12
|
+
# "M", the "TP" of a manual) names a different catalogue record, so all
|
|
13
|
+
# are atoms and none is a facet. The current urn:astm: form of #to_urn
|
|
14
|
+
# is pubid-invented and stays as it is.
|
|
15
|
+
PubidUrnSchema = Pubid::PubidUrn.define("astm") do
|
|
16
|
+
atom "publisher", "pub"
|
|
17
|
+
atom "committee", "committee"
|
|
18
|
+
atom "letter", "letter"
|
|
19
|
+
atom "subseries", "subseries"
|
|
20
|
+
atom "suffix", "suffix"
|
|
21
|
+
atom "dual_m", "metric", kind: :bool
|
|
22
|
+
atom "year", "year"
|
|
23
|
+
atom "sub_year", "subyear"
|
|
24
|
+
atom "reapproval", "reapproval"
|
|
25
|
+
atom "edition", "ed"
|
|
26
|
+
atom "supplement", "sup", kind: :bool
|
|
27
|
+
atom "tp_designation", "tp"
|
|
28
|
+
atom "format_suffix", "format"
|
|
29
|
+
atom "ea_suffix", "ea", kind: :bool
|
|
30
|
+
atom "dvd_suffix", "dvd", kind: :bool
|
|
31
|
+
atom "hol_suffix", "hol", kind: :bool
|
|
32
|
+
|
|
33
|
+
type "pubid:astm:identifier", "id"
|
|
34
|
+
type "pubid:astm:single-identifier", "single"
|
|
35
|
+
type "pubid:astm:base", "base"
|
|
36
|
+
type "pubid:astm:standard", "std"
|
|
37
|
+
type "pubid:astm:iso-dual-published", "isodual"
|
|
38
|
+
type "pubid:astm:adjunct", "adj"
|
|
39
|
+
type "pubid:astm:data-series", "ds"
|
|
40
|
+
type "pubid:astm:manual", "mnl"
|
|
41
|
+
type "pubid:astm:monograph", "mono"
|
|
42
|
+
type "pubid:astm:research-report", "rr"
|
|
43
|
+
type "pubid:astm:technical-report", "tr"
|
|
44
|
+
type "pubid:astm:work-in-progress", "wk"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
data/lib/pubid/astm.rb
CHANGED
|
@@ -13,6 +13,7 @@ module Pubid
|
|
|
13
13
|
autoload :Identifiers, "#{__dir__}/astm/identifiers"
|
|
14
14
|
autoload :SingleIdentifier, "#{__dir__}/astm/single_identifier"
|
|
15
15
|
autoload :Parser, "#{__dir__}/astm/parser"
|
|
16
|
+
autoload :PubidUrnSchema, "#{__dir__}/astm/pubid_urn_schema"
|
|
16
17
|
autoload :Builder, "#{__dir__}/astm/builder"
|
|
17
18
|
autoload :Renderer, "#{__dir__}/astm/renderer"
|
|
18
19
|
autoload :UrnGenerator, "#{__dir__}/astm/urn_generator"
|
data/lib/pubid/bipm/CLAUDE.md
CHANGED
|
@@ -11,3 +11,5 @@ These notes were part of the root `CLAUDE.md`. Read them before you change `lib/
|
|
|
11
11
|
- **BIPM loose consumer forms (retiring `Relaton::Bipm::Id`)**: `Relaton::Bipm::Bibliography` could not use pubid as its **query** parser — it kept a bespoke regex grammar (`Relaton::Bipm::Id`) because `Pubid::Bipm.parse` rejected the spellings real references use. The grammar now accepts them, so relaton can match pubid-to-pubid like every other flavor. **All of these are NORMALIZING parses — the identifier renders back in BIPM's canonical spelling, never the input** — which is the whole point (a loose query must produce an identifier equal to the index row's), and which is exactly why they must **never** go into the byte-exact `spec/fixtures/bipm/identifiers/pass/` (the `CIPM/2005-06(REV)` precedent); their expectations live in `spec/pubid/bipm/loose_consumer_forms_spec.rb`. Six constructs: **(1) Meeting word order** — `meeting_en` gained a `<group> Meeting <n>` branch and `meeting_fr` gained a plain-`e` ordinal branch (`CIPM 111e Réunion (2022)`) and a `<group> Réunion <n>` branch, all normalizing to `CCTF 14th Meeting (1999)` / `CIPM 111<sup>e</sup> réunion (2022)`. **(2) `committee_group_fr`** — a French type name in the English (group-leading) word order (`CIPM Décision 101-1 (2012)` → `Décision 101-1 du CIPM (2012)`). Its slot in `group_leading` is **after `committee_long_en`**, which is load-bearing: `Action` and `Statement` are in *both* `TYPE_NAME_EN` and `TYPE_NAME_FR`, so the English rule must claim them first or `CIPM Action 5 (2010)` would flip to French. Like the existing French rules it sets `language: "F"` — information the user typed, which a consumer can always widen away but could never recover. **(3) `GROUP_ALIASES`** (`CCDS`→`CCTF`) and **(4) `LANGUAGE_ALIASES`** (`EN`→`E`, `FR`→`F`, longest-first in the `lang` rule so `EN` is never read as a bare `E`): both are resolved in the **builder**, so an alias never reaches an attribute and the index stores only the current name and the one-letter code. `PARSEABLE_GROUPS` (= `GROUPS + GROUP_ALIASES.keys`) is what the `group` rule alternates over; `GROUPS` itself is unchanged, so nothing else sees the alias. **(5) `si_brochure_section`** — the bare `SI Brochure` and `SI Brochure Part 1`. Neither names an edition, so **both are partial references with a nil `number`** that wildcard every edition (the date-less `CCTF REC 2` reading). `Part N` points at a section *inside* the brochure — there is no `si-brochure-part-1.yaml` upstream and relaton's `Id` deletes `:part` before comparing — so it rides in the shared `part` attribute and stays **out** of the index key, exactly as the MEP/guide `Part N.M` tail does. **A Parslet fact worth recording, because it contradicts what several comments in this repo imply**: Parslet threads `consume_all` down through an alternation, so an alternative matching only a **prefix** counts as a failure and the next one is tried — verified by reordering. `si_brochure_section` therefore cannot truncate `SI Brochure Concise` whatever its slot, and `committee_bare` could not shadow a typed form even if it came first. Alternation order decides **only** between two alternatives that both consume the *whole* input — which is exactly the `committee_long_en`-before-`committee_group_fr` case above, and the only ordering here that is genuinely load-bearing (verified: flipping it renders `CIPM Action 5 (2010)` as the French `Action 5 du CIPM (2010)`, a different record). The `BIPM ` prefix was also made **optional on the main `si_brochure` rule** (it already was on the variant rule): `si-brochure.yaml` stores its docnumber *without* it, and `Relaton::Bipm::Bibliography.search` strips a leading `BIPM ` before pubid ever sees the string — so `SI Brochure 9e v3.01 (2019/2024, E)` used to be a hard parse failure. **(6) `TYPE_WORD_ALIASES`** — a **data-corruption fix**: `Declaration`/`Déclaration` were in the `type_name_en`/`type_name_fr` alternations but **not** in `TYPE_WORD_TO_CODE` (which is built from `TYPE_NAME_EN`/`TYPE_NAME_FR`, where DECL's name is `Statement` in both languages), so `CGPM Declaration 1 (1971)` parsed with `type_code` **nil** and rendered as the corrupt `"CGPM 1 (1971)"` (double space), `Déclaration 1 du CGPM (1971)` as `" 1 de la CGPM (1971)"`. Both now map to DECL and render `Statement`. **The matching contract relaton needs** (pinned by the spec against real `index-v2` rows): committee-document and meeting rows are stored **language- and form-neutral** — verified, all 1348 + 367 — so the BIPM stem is **`pubid.exclude(:language, :form)`**, and every loose form reduces under it to the identifier rebuilt from its row. The SI Brochure is the exception: a nil-`edition` query must match *any* `SiBrochure` row, which is what `Id#id_hash`'s `{group: "SI", type: "Brochure"}` collapse already meant. **Also fixed here**: `UrnParser#build_metrologia`/`#build_si_brochure` never set the derived `number`, so a URN round-trip silently emptied the relaton index key (the shared `"flavor URN round-trip"` example only compares `to_urn`, so it was invisible) — now set from the same source the builder uses, guarded by a `presence` helper because an unfilled URN segment is `""`, not nil. **Verified**: all 7,922 published `relaton-data-bipm` rows re-parse from their own `to_s` with an identical hash, class and MR slug (0 mismatches, 0 MR collisions), and all 90 pass fixtures stay byte-exact. **Deliberately NOT changed:** `CCTF Recommendation 2009-02` still reads as the literal number `2009-02` while the index keys that document under `2` — genuinely ambiguous, since `NNNN-NN` is a real BIPM number form (`CIPM 2005-06`), so the consumer keeps a fallback. **Known pre-existing gap, deliberately untouched:** the SI Brochure URN carries only language/edition/version/years, so `Concise`, `FAQ` and `Appendix 3` — three distinct documents — already share `urn:bipm:si-brochure::::`, and the new bare/section forms join that bucket (defensibly, being partial references rather than distinct documents). Fixing it needs a URN-shape decision plus a matching `UrnParser` branch. `Pubid.parse` auto-routing does not resolve **any** BIPM form today (also pre-existing), so `PREFIXES` was left alone — adding `CCDS` there would change nothing observable. (hand-off: bipm-loose-consumer-forms; consumer follow-up: relaton__relaton__bipm-drop-id-parser-for-pubid.)
|
|
12
12
|
|
|
13
13
|
- **Routing reaches BIPM (`Pubid.parse`, pubid#644)**: the note above says "`Pubid.parse` auto-routing does not resolve **any** BIPM form today … so `PREFIXES` was left alone" — that is no longer true, in two halves. **(1) `PREFIXES` gained the two SI-family series tokens** `SI Brochure` and `SI MEP`: both lead real printed references on their own (`SI Brochure 9e v3.01 (2019/2024, E)` — the spelling relaton's `Bibliography#search` strips the `BIPM ` off, per the note above — and `SI MEP 10`), and before this both parsed **directly** but the router could not route them (no prefix owner ⇒ no non-exact fallback). "Rapport" stays excluded per the ambiguous-token policy (generic French word). **(2) The quoted-title form** `BIPM "The International System of Units"` — the brochure IS titled that — is mapped onto the parseable `BIPM SI Brochure` section form by **`data/bipm/update_codes.yaml`** (the CIPM-MRA precedent), so it parses as a **partial reference** that wildcards every edition, exactly like `SI Brochure Part 1`; the bare `"The International System of Units"` maps to `SI Brochure`. Both are normalizing parses and live with the loose-forms expectations, never in the byte-exact fixtures. The **French title is deliberately not mapped**: it names the F-language record specifically, and mapping it would silently drop typed language information (the loose-forms rule above). Also note the router's prefix-owner mechanism is what makes the normalizing parses routable: `Pubid::Bipm.parse("BIPM SI Brochure")` happens to round-trip exactly, but `SI Brochure 9e …` renders with the leading `BIPM ` the input lacked — without prefix-owner status the router would reject it. (hand-off: router-printed-forms-644.)
|
|
14
|
+
|
|
15
|
+
- **Corpus-cited forms + guide printings (pubid#657)**: the metanorma-bipm fetch queue cites two spellings the grammar rejected, and the 2026-11 guide update adds printings of its own. All are **parse-in forms over the existing canonical identities** — the canonical render follows the guide, nothing that already parsed changed. **(1) The em-dash meeting variant** `CGPM -- Meeting 1 (1889)`: `meeting_en` gained `<group> -- Meeting <n>` and `<group> -- <n><ord> Meeting` branches (the corpus cites only the first; the second is the same reordering over the other word order). They build through the existing `Meeting` path, so the canonical render is `CGPM 1st Meeting (1889)`. **(2) The publisher-prefixed decision** `BIPM DECN CIPM/101-1 (2012, EN)` (and `, FR`): new `committee_prefixed` rule — a **literal `BIPM`** lead (the constant publisher, deliberately NOT the `group` table, which contains BIPM and would collide with the second `group` capture), `type_abbrev`, then the body-qualified id `{group}/{number}`. Builds through `build_committee` short-form, so it equals `CIPM DECN 101-1 (2012, E)` — verified against real `index-v2` rows in `loose_consumer_forms_spec` (the corpus's citeas comes out as the BIPM-long style, which is the consumer's rendering choice, not pubid's). **(3) The guide's French body-prefixed long form** `Décision CIPM/111-10 (2022)` / `Recommandation JCRB/43-1 (2021)`: an alternative inside `committee_long_fr` (same capture name, so the same builder path — long form, language F). **The canonical French long form stays the connective spelling** (`Décision 101-1 du CIPM (2012)`) even though the guide's language-independent example prefers the body-prefixed one — the corpus freezes the connective render for what already parsed, and flipping the canonical would move frozen cases; that flip is an owner decision if the guide is adopted wholesale. **(4) SI Brochure guide forms**: the language code and the version are now OPTIONAL (`SI Brochure 9e v3.01 (2019/2024)` language-independent, `SI Brochure 9e (2019, E)` version-less — both byte round-trips with the canonical `BIPM SI Brochure ` lead), and the guide's French lead `Brochure sur le SI 9e …` parses as an alias of the `BIPM SI Brochure sur le SI …` canonical (note the lead alternation is `"BIPM SI Brochure" / "SI Brochure" / "Brochure sur le SI"` with the `["sur le SI" space]` phrase still optional after the first two — it just fails cleanly after the third). **(5) The corpus-cited ordinal-edition spelling** `SI Brochure 9th edition (2019)`: its own `si_brochure_ordinal_edition` rule (a separate branch, because the `edition` capture would otherwise bind `"9th"`); the builder strips the ordinal and appends `e`, so it normalizes onto `BIPM SI Brochure 9e (2019)`. **(6) Part numbers** (the guide's resource-specific-id numbering, `partnumbers` proposal, bipm-si-brochure#301): parts/subparts are dash forms inside `number` (`CIPM DECN 111-10-2`) and always parsed; the appendix/annex complement `CIPM DECN 112 A3[.5]` is the new optional ` A<n>[.<m>]` tail on `committee_short`, stored in the shared `appendix`/`annex` attributes (the MEP/guide tail's home), printed back byte-exactly by `appendix_segment`, folded into the URN identifier segment as `-a<n>[.<m>]` (`urn:bipm:cipm:decn:112-a3.5:2022` — unambiguous, because a bare number never carries letters; `UrnParser#split_appendix` splits it back), and joined into the MR number segment (`mr_slug` collapses the annex dot). It requires a number before it (`CIPM DECN A3` still fails). **URN deviations from the guide, deliberate**: the guide's own committee-example URNs (`urn:bipm:v1:cipm:resolution:95-2006:1`) carry a `v1` version segment and venue-qualified shapes the flavor's URNs never adopted (`urn:bipm:cipm:res:101-1:2012`, no `v1`, no venue segments — green for years, kept byte-identical); the new forms emit URNs under the EXISTING shape for the same reason, and the same applies to the brochure (`urn:bipm:si-brochure:<lang>:<edition>:<version>:<years>` vs the guide's `urn:bipm:v1:bipm:si-brochure:9:3.01:2024` — the guide strips the `e`/`v` and drops the language segment; adopting that is an index-affecting owner decision, not taken here). **One Ruby trap the URN fold exposed**: `id.number.to_s` on a String **attribute** returns the attribute itself, so building the folded segment by `<<` mutated `number` in place — every later `to_s`/`to_hash` of the same object then carried `-a3`; the fold is now interpolation, and `urn_spec` pins that a URN emission leaves `number` untouched. **The unmerged `si-brochure-id` proposal** (`SI Brochure 9`, `SI Brochure 9 (E)`, `SI Brochure 9 (2024) (F)` — bare-number edition) is NOT implemented: it conflicts with the merged guide's `9e` spelling. (hand-off: bipm-657-corpus-forms.)
|
data/lib/pubid/bipm/builder.rb
CHANGED
|
@@ -13,6 +13,11 @@ module Pubid
|
|
|
13
13
|
def build(data)
|
|
14
14
|
if data[:committee_short]
|
|
15
15
|
build_committee(data[:committee_short], form: "short")
|
|
16
|
+
elsif data[:committee_prefixed]
|
|
17
|
+
# The corpus's "BIPM DECN CIPM/101-1 (2012, EN)" publisher-prefixed
|
|
18
|
+
# spelling names the same document as the unprefixed short form; the
|
|
19
|
+
# BIPM lead is the constant publisher, so it carries no attribute.
|
|
20
|
+
build_committee(data[:committee_prefixed], form: "short")
|
|
16
21
|
elsif data[:committee_long_en]
|
|
17
22
|
build_committee(data[:committee_long_en], form: "long", language: "E")
|
|
18
23
|
elsif data[:committee_bare]
|
|
@@ -32,6 +37,8 @@ module Pubid
|
|
|
32
37
|
build_si_brochure_variant(data[:si_brochure_variant])
|
|
33
38
|
elsif data.key?(:si_brochure_section)
|
|
34
39
|
build_si_brochure_section(data[:si_brochure_section])
|
|
40
|
+
elsif data.key?(:si_brochure_ordinal_edition)
|
|
41
|
+
build_si_brochure_ordinal_edition(data[:si_brochure_ordinal_edition])
|
|
35
42
|
elsif data[:mep]
|
|
36
43
|
build_mep(data[:mep])
|
|
37
44
|
elsif data[:guide]
|
|
@@ -55,6 +62,8 @@ module Pubid
|
|
|
55
62
|
year: node[:year]&.to_s&.to_i,
|
|
56
63
|
language: Identifier.normalize_language(language || node[:language]),
|
|
57
64
|
form: form,
|
|
65
|
+
appendix: node[:appendix]&.to_s,
|
|
66
|
+
annex: node[:annex]&.to_s,
|
|
58
67
|
)
|
|
59
68
|
end
|
|
60
69
|
|
|
@@ -103,11 +112,13 @@ module Pubid
|
|
|
103
112
|
# The SI Brochure has no document number of its own, so the edition is
|
|
104
113
|
# the index key — it clusters the English and French records of one
|
|
105
114
|
# edition. The grammar makes `edition` mandatory here, so it is never nil.
|
|
115
|
+
# The version and the language code are optional (the guide's
|
|
116
|
+
# language-independent form), so they may be nil.
|
|
106
117
|
def build_si_brochure(node)
|
|
107
118
|
Identifiers::SiBrochure.new(
|
|
108
119
|
number: node[:edition].to_s,
|
|
109
120
|
edition: node[:edition].to_s,
|
|
110
|
-
version: node[:version]
|
|
121
|
+
version: node[:version]&.to_s,
|
|
111
122
|
years: node[:years].to_s,
|
|
112
123
|
language: Identifier.normalize_language(node[:language]),
|
|
113
124
|
)
|
|
@@ -128,6 +139,20 @@ module Pubid
|
|
|
128
139
|
Identifiers::SiBrochure.new(part: node[:part]&.to_s)
|
|
129
140
|
end
|
|
130
141
|
|
|
142
|
+
# The guide prints the edition "9e"; the corpus-cited "9th edition"
|
|
143
|
+
# spelling is the English ordinal form of the same edition, normalized
|
|
144
|
+
# onto the canonical "9e" value. The version is absent from the input, so
|
|
145
|
+
# it stays nil and drops from the serialized hash (a partial reference).
|
|
146
|
+
def build_si_brochure_ordinal_edition(node)
|
|
147
|
+
edition = "#{node[:ordinal_edition].to_s.sub(/(st|nd|rd|th)\z/, "")}e"
|
|
148
|
+
Identifiers::SiBrochure.new(
|
|
149
|
+
number: edition,
|
|
150
|
+
edition: edition,
|
|
151
|
+
years: node[:year]&.to_s,
|
|
152
|
+
language: Identifier.normalize_language(node[:language]),
|
|
153
|
+
)
|
|
154
|
+
end
|
|
155
|
+
|
|
131
156
|
# The Appendix/Annex/Part tail is the full-content spelling of the SAME
|
|
132
157
|
# document, so it stays out of the key.
|
|
133
158
|
def build_mep(node)
|
|
@@ -28,7 +28,19 @@ module Pubid
|
|
|
28
28
|
# The group has to be in the number segment or `CCTF REC 2` and
|
|
29
29
|
# `CCEM REC 2` would share a slug.
|
|
30
30
|
def mr_number_with_part
|
|
31
|
-
mr_slug(group, number)
|
|
31
|
+
mr_slug(group, number, appendix_tail)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
# The appendix/annex complement distinguishes the separately published
|
|
37
|
+
# subresources, so it joins the number segment (the language rule: what
|
|
38
|
+
# upstream stores as distinct files must slug distinctly). `mr_slug`
|
|
39
|
+
# collapses the annex dot, as it does the brochure's "v3.01".
|
|
40
|
+
def appendix_tail
|
|
41
|
+
return nil unless appendix
|
|
42
|
+
|
|
43
|
+
annex ? "a#{appendix}.#{annex}" : "a#{appendix}"
|
|
32
44
|
end
|
|
33
45
|
end
|
|
34
46
|
end
|
data/lib/pubid/bipm/renderer.rb
CHANGED
|
@@ -40,10 +40,20 @@ module Pubid
|
|
|
40
40
|
# leading space) rather than emit a double space.
|
|
41
41
|
lang = id.language ? ", #{id.language}" : ""
|
|
42
42
|
type = id.type_code ? " #{id.type_code}" : ""
|
|
43
|
-
"#{id.group}#{type}#{number_segment(id)}" \
|
|
43
|
+
"#{id.group}#{type}#{number_segment(id)}#{appendix_segment(id)}" \
|
|
44
44
|
"#{year_segment(id, lang: lang)}"
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
+
# The complement tail "{identifier} A{appendix}[.{annex}]" of the guide's
|
|
48
|
+
# resource-specific-id numbering (parts/subparts are dash forms inside
|
|
49
|
+
# `number` itself; appendices/annexes are this separate tail).
|
|
50
|
+
def appendix_segment(id)
|
|
51
|
+
return "" unless id.appendix
|
|
52
|
+
|
|
53
|
+
annex = id.annex ? ".#{id.annex}" : ""
|
|
54
|
+
" A#{id.appendix}#{annex}"
|
|
55
|
+
end
|
|
56
|
+
|
|
47
57
|
def render_committee_long(id)
|
|
48
58
|
if id.language == "F"
|
|
49
59
|
name = Identifier::TYPE_NAME_FR[id.type_code]
|
|
@@ -79,10 +89,15 @@ module Pubid
|
|
|
79
89
|
# to print either — just the brochure, optionally down to one part.
|
|
80
90
|
return "BIPM SI Brochure#{part_segment(id)}" unless id.edition
|
|
81
91
|
|
|
92
|
+
# The version is optional (the guide's "SI Brochure 9e (2019, E)" form)
|
|
93
|
+
# and the language code too (the language-independent form), so both
|
|
94
|
+
# segments are conditional.
|
|
82
95
|
phrase = id.language == "F" ? "sur le SI " : ""
|
|
83
96
|
lang = id.language ? ", #{id.language}" : ""
|
|
84
|
-
"BIPM SI Brochure #{phrase}#{id.edition}
|
|
85
|
-
|
|
97
|
+
result = +"BIPM SI Brochure #{phrase}#{id.edition}"
|
|
98
|
+
result << " #{id.version}" if id.version
|
|
99
|
+
result << " (#{id.years}#{lang})"
|
|
100
|
+
result
|
|
86
101
|
end
|
|
87
102
|
|
|
88
103
|
# Optional " Part <n>" segment of an SI Brochure section reference.
|
|
@@ -28,13 +28,26 @@ module Pubid
|
|
|
28
28
|
# type_code is nil for the bare MRA form → empty type segment, mirroring
|
|
29
29
|
# the empty-number-segment convention for number-less documents.
|
|
30
30
|
parts = ["urn", "bipm", id.group.downcase,
|
|
31
|
-
id.type_code.to_s.downcase, id
|
|
31
|
+
id.type_code.to_s.downcase, committee_number_segment(id)]
|
|
32
32
|
# A partial (date-less) reference drops the trailing year segment; a
|
|
33
33
|
# number-less-but-dated doc keeps its empty number segment.
|
|
34
34
|
parts << id.year if id.year
|
|
35
35
|
parts.join(":")
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
+
# The appendix/annex complement folds into the identifier segment as
|
|
39
|
+
# "-a{appendix}[.{annex}]". A bare number is digits and dashes only, so
|
|
40
|
+
# the letter-prefixed tail is unambiguous on the way back (UrnParser
|
|
41
|
+
# splits it). Built by interpolation, never by appending to
|
|
42
|
+
# `id.number.to_s` — on a String attribute that returns the attribute
|
|
43
|
+
# itself, so a `<<` would mutate the identifier's `number` in place.
|
|
44
|
+
def committee_number_segment(id)
|
|
45
|
+
return id.number.to_s unless id.appendix
|
|
46
|
+
|
|
47
|
+
tail = id.annex ? ".#{id.annex}" : ""
|
|
48
|
+
"#{id.number}-a#{id.appendix}#{tail}"
|
|
49
|
+
end
|
|
50
|
+
|
|
38
51
|
def meeting
|
|
39
52
|
id = identifier
|
|
40
53
|
parts = ["urn", "bipm", id.group.downcase, "meeting", id.number.to_s]
|
|
@@ -27,14 +27,27 @@ module Pubid
|
|
|
27
27
|
group: group, number: parts[2].to_s, year: parts[3]&.to_i,
|
|
28
28
|
)
|
|
29
29
|
else
|
|
30
|
+
number, appendix, annex = split_appendix(parts[2])
|
|
30
31
|
Identifiers::CommitteeDocument.new(
|
|
31
32
|
group: group,
|
|
32
33
|
type_code: (parts[1].empty? ? nil : parts[1].upcase),
|
|
33
|
-
number: (
|
|
34
|
+
number: (number.empty? ? nil : number), year: parts[3]&.to_i,
|
|
35
|
+
appendix: appendix, annex: annex,
|
|
34
36
|
)
|
|
35
37
|
end
|
|
36
38
|
end
|
|
37
39
|
|
|
40
|
+
# Inverse of UrnGenerator#committee_number_segment: splits the folded
|
|
41
|
+
# "-a{appendix}[.{annex}]" tail back out of the identifier segment. A
|
|
42
|
+
# bare number is digits and dashes only, so the letter-prefixed tail is
|
|
43
|
+
# unambiguous.
|
|
44
|
+
def split_appendix(segment)
|
|
45
|
+
match = segment.to_s.match(/\A(.+?)-a(\d+)(?:\.(\d+))?\z/)
|
|
46
|
+
return [segment, nil, nil] unless match
|
|
47
|
+
|
|
48
|
+
[match[1], match[2], match[3]]
|
|
49
|
+
end
|
|
50
|
+
|
|
38
51
|
# `number` is the relaton index key and is derived, not printed, so it has
|
|
39
52
|
# to be set here exactly as `Builder` sets it — a URN-reconstructed
|
|
40
53
|
# identifier must agree with a parsed one attribute for attribute.
|
data/lib/pubid/identifier.rb
CHANGED
|
@@ -1114,6 +1114,21 @@ module Pubid
|
|
|
1114
1114
|
resolve_urn_generator.new(self).generate
|
|
1115
1115
|
end
|
|
1116
1116
|
|
|
1117
|
+
# The lossless pubid-scheme URN ("urn:pubid:…").
|
|
1118
|
+
#
|
|
1119
|
+
# It is in pubid's own namespace (issue #656), carries every value of
|
|
1120
|
+
# #to_hash and always round-trips through Pubid.parse_urn. Not the
|
|
1121
|
+
# same as #to_urn, the official, possibly lossy URN of the
|
|
1122
|
+
# organization. Defined once, here, from #to_hash and the flavor's
|
|
1123
|
+
# PubidUrnSchema; flavors do not override it.
|
|
1124
|
+
#
|
|
1125
|
+
# @return [String]
|
|
1126
|
+
# @raise [Pubid::Errors::InvalidInputError] when the flavor has no
|
|
1127
|
+
# pubid-scheme schema yet
|
|
1128
|
+
def to_pubid_urn
|
|
1129
|
+
Pubid::PubidUrn.generate(self)
|
|
1130
|
+
end
|
|
1131
|
+
|
|
1117
1132
|
def resolve_urn_generator
|
|
1118
1133
|
flavor = self.class.name.split("::")[1]
|
|
1119
1134
|
Object.const_get("Pubid::#{flavor}::UrnGenerator")
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pubid
|
|
4
|
+
module Iec
|
|
5
|
+
# The pubid-scheme URN of IEC — PARTIAL (issue #656;
|
|
6
|
+
# docs/URN-SCHEMES.adoc).
|
|
7
|
+
#
|
|
8
|
+
# Only +pubid:iec:international-standard+ is declared, because IEEE
|
|
9
|
+
# identifiers nest it (IEC/IEEE co-publications). IEC's own wave declares
|
|
10
|
+
# the rest of the flavor; until then IEC stays pending in the contract
|
|
11
|
+
# spec. The official RFC 5141-style urn:iec:std: form stays on #to_urn.
|
|
12
|
+
PubidUrnSchema = Pubid::PubidUrn.define("iec") do
|
|
13
|
+
atom "publisher", "pub"
|
|
14
|
+
atom "copublishers", "co", kind: :list
|
|
15
|
+
atom "part", "part"
|
|
16
|
+
atom "subpart", "subpart"
|
|
17
|
+
atom "year", "year"
|
|
18
|
+
atom "month", "month"
|
|
19
|
+
atom "day", "day"
|
|
20
|
+
atom "undated", "undated", kind: :bool
|
|
21
|
+
atom "edition", "ed"
|
|
22
|
+
atom "stage", "stage"
|
|
23
|
+
atom "stage_iteration", "iter"
|
|
24
|
+
atom "database", "database"
|
|
25
|
+
atom "languages.*.code", "lang"
|
|
26
|
+
|
|
27
|
+
facet "languages.*.original_code", "langsrc"
|
|
28
|
+
|
|
29
|
+
type "pubid:iec:international-standard", "is"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
data/lib/pubid/iec.rb
CHANGED
|
@@ -17,6 +17,7 @@ module Pubid
|
|
|
17
17
|
autoload :SingleIdentifier, "#{__dir__}/iec/single_identifier"
|
|
18
18
|
autoload :SupplementIdentifier, "#{__dir__}/iec/supplement_identifier"
|
|
19
19
|
autoload :Parser, "#{__dir__}/iec/parser"
|
|
20
|
+
autoload :PubidUrnSchema, "#{__dir__}/iec/pubid_urn_schema"
|
|
20
21
|
autoload :Builder, "#{__dir__}/iec/builder"
|
|
21
22
|
autoload :UrnParser, "#{__dir__}/iec/urn_parser"
|
|
22
23
|
autoload :UrnGenerator, "#{__dir__}/iec/urn_generator"
|
data/lib/pubid/ieee/CLAUDE.md
CHANGED
|
@@ -99,3 +99,5 @@ The lesson BSI paid for: a wrapper's hand-written `#number` delegation makes the
|
|
|
99
99
|
- **Joint stage-first forms are lead ISO, and the ISO face is lossless (pubid#477)**: a stage word printed in the ISO position (`IEEE FCD 15026.3:2010`, `IEEE/IEC CD2 P62582-2017-05`) parses as printed (#469): `Builder#build_joint_development` sets `lead_party "ISO"` whatever the publisher order, so `canonical_format` is `:iso`. PR #472 had made the lead the first printed publisher and rendered an IEEE-first form in the `D=` face — but the grammar artifact cannot parse most of those renders (`D=FCD`, `D=CD.2`, a non-P number, a month), and `D=CD-2020` parses into a *different* id (`ieee_draft`, no `iso_stage`), so 19 published `relaton-data-ieee` rows stopped matching themselves. The `D=` face now prints only for an id that carries a `D=` designator, or on an explicit `to_s(format: :ieee)`. `JointDevelopment#to_iso_format` prints `iso_stage` as stored (`DIS2`, `CD4` — #472's bare `CD` lost the iteration) and the date in the spelling that parses back (`#iso_date_suffix`: `:2018`, `-2018-05`, `, April 2019`). **The test is `parse(to_s) == id`, not a stable `to_s`**: a lossy render reaches a fixed point and round-trips its *hash* (`iso_stage_spellings_spec` held both while dropping `CD4`→`CD`), yet the reparse is a different identifier. Measured over the 118 joint stage rows of `relaton-data-ieee`: `to_s` stable 100 → 118, `==` 51 → 106; the other 12 carry an `ieee_draft` or an edition that the ISO face still drops. **PARG separator-in-capture, again (#471)**: the draft `revision` capture is `".12"`, and `Draft#to_s` adds its own dot, so `handle_draft` strips the leading dot (`D2022.10.12` rendered `D2022.10..12`). Check every capture a renderer re-joins with a separator. Locked by `spec/pubid/ieee/joint_stage_roundtrip_spec.rb` (stored index hashes → `parse(to_s) == row`).
|
|
100
100
|
|
|
101
101
|
- **IEEE parses through the edition-2 artifact's typed model bindings (the last converter wave)**: the three `Backend.parse` sites (`identifiers/base.rb` `parse_single`, `Aiee::Identifier.parse`, `Ire::Identifier.parse`) route through `Pubid::Parg::Model.bound` — the grammar's `bindings` block produces the builder-ready hash at the engine, replacing the tree normalization. The grammar side (pubid-grammar `grammars/{ieee,nesc}.parg` edition 2 + bindings, plus a class-ref inlining pass over the merged `aiee.parg`/`ire.parg`) keeps every capture the builder reads: leaf scalars bind `(string)`; the branch/wrapper captures the builder dispatches on (`draft`, `parameters`, `publishers`, `base`, `relationship_type`, `related_ids`, the aiee/ire/nesc wrappers, …) bind `(raw)` — raw subtrees carry `Parsanol::Slice` leaves whose `to_s`/`==` behave like strings, so `extract_value` and the `.is_a?(Hash)` probes are unchanged. **Load-bearing grammar deviations** (documented in the `.parg` headers): the zero-width markers are re-spelled as real-token captures (`printed_dash_year`/`printed_month_year` capture their branch's leading separator, `iec_label_dash` the dash, `iso_published` the whole joint head choice — every read sits behind a nil-`iso_stage` gate, so the wider presence set is inert exactly where v1's was narrow); the `([dash]) as draft_dash` / `([rev_sep] as rev_sep)` capture-over-OPTIONAL landmines are `[ (x) as y ]` (parsanol-ruby#394); nested-only captures that collide with a top-level name rename with the builder (`draft_date_month/year/day`, `draft_version_dash_*`, `fdraft_month/year`, and `reaffirmed_year` — unbound, because binding it would have surfaced the reaffirmation year v1 kept absent). **Class RULE refs (`digit`/`lower`/`upper`, and multi-char ones like `alnum_char`/`identifier_string`'s `non_nl`) materialize per-token named nodes inside captures when the runtime recompiles the `use`-importing source — the vendored `data/parg/{aiee,ire,nesc}.parg` copies are the live import sources, so they must be re-vendored together with the artifacts** (a stale `.parg` beside a fresh `.json` silently parses through the old grammar). **Builder accommodations** (all dual-key with the v1 spellings as fallback, so HEAD trees and bound maps read alike): the renamed nested keys; and the multi-segment draft-version normalization at the top of `build_single_identifier` — edition-2 trees materialize a "D2.1.1" designator as per-token nodes with no `:draft_version`, where v1 degraded to a whole-clause leaf (reconstructed: the leaf string → `Draft.parse` inverse) or to a date-only hash when a draft date follows (no draft built). **Measured**: fixture-corpus parity (old artifact + tree backend + builder vs new artifact + bound + builder) over all 9,054 pass-fixture inputs → 1 diff, and standalone aiee/ire/nesc sweeps → 0 diffs each. **The one residue is an engine wall, documented not chased**: `IEC/IEEE PC37.60/P62271-111_D6.1` (testsuite case `ieee.project_draft_identifier.0946`, one corpus input, one `draft_compact_convergence` example) — v1's edition-1 tree degraded its whole draft clause ("DP62271-111_D6.1, Aug 2016", two P-numbers plus date) into a single leaf, and edition-2's fully-named trees cannot reproduce that collapse at any binding spelling (isolated rule probes + programmatic branch bisect); the row is one of the two-P-number joint one-offs already recorded as deferred above. The conversion also **fixed** two testsuite rows (`ieee.project_draft_identifier.9263` "ISO/IEC/IEEE P42010.2019/D4" / "P42010.WD4:2019(E)" — the lettered `draft_iso_iteration` capture now binds, so the D= face composes), at the cost of the 0946 residue above.
|
|
102
|
+
|
|
103
|
+
- **The pubid-scheme URN (`#to_pubid_urn`, issue #656) — IEEE wave.** Schema: `lib/pubid/ieee/pubid_urn_schema.rb`; design and rulings: the `[[urn-ieee]]` section of `docs/URN-SCHEMES.adoc`. All 9,049 parseable pass ids emit, round-trip by `to_hash`, are path-safe and collision-free; the contract row is `spec/pubid/pubid_urn_contract_spec.rb`, the per-family examples `spec/pubid/ieee/pubid_urn_spec.rb`. **No `to_hash` change, so no `relaton-data-ieee` re-crawl.** Three things to know before editing: **(1)** the wrappers whose parent slot is not `base` (`ieee_identifier`, `first_identifier`, `primary_identifier` …, see "The parent slot is never called `base` in IEEE" above) write that document as a nested atom, so they do NOT share its slug prefix — renaming a slot to `base` moves every such URN; **(2)** IEEE nests ANSI and IEC ids, so ANSI landed whole with this wave and `lib/pubid/iec/pubid_urn_schema.rb` is a partial schema for the one IEC type the corpus nests — a new nested foreign type (an adopted `IEC TR`, `TS`, `AMD`, `CSV`) makes `#to_pubid_urn` raise until its flavor's schema declares it; **(3)** the schema must place every `to_hash` key, so a new IEEE attribute fails the contract row until its atom is declared — add it in the shared-vocabulary group it belongs to, never at the end out of habit, because atom order is part of every published slug. **The URN inherits two `to_hash` gaps, pinned rather than fixed**: `csa_identifier` is a plain accessor, so the CSA half of a `CsaDualPublished` is lost (4 corpus ids, and two such documents with the same IEEE half share a hash and a URN); and one `separator` serves every part, so `IEEE Std 7-4.3.2-2016` comes back as `7.4.3.2` (14 ids). A third: `SupplementIdentifier#to_hash` drops the delegated `publisher` only on a top-level wrapper, so for a non-IEEE supplement nested in an adoption (`IEEE Std 1234-2010 (ISO 1234/Amd 1:2001)`) `from_hash(to_hash)` is not idempotent and `parse_urn` refuses the emitted URN as not canonical (none in the corpus). Fixing any of the three changes `to_hash`. Separately, `IEC/IEEE P62582-6, FDIS May 2019` and `ISO/IEC/IEEE P21840, DIS-2019` raise `Parsanol::PARG::BindError` from `parse` — the parse-error contract expects `Pubid::Errors::ParseError`.
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pubid
|
|
4
|
+
module Ieee
|
|
5
|
+
# The pubid-scheme URN of IEEE (issue #656; docs/URN-SCHEMES.adoc).
|
|
6
|
+
#
|
|
7
|
+
# IEEE Std 802.11-2016
|
|
8
|
+
# urn:pubid:ieee:std:802:stage-Std:part-11:year-2016:f:0.sep-.
|
|
9
|
+
#
|
|
10
|
+
# The core is the document number. Every other value is an optional
|
|
11
|
+
# prefixed atom, in the order below: the shared vocabulary first (who,
|
|
12
|
+
# what, which part, when, which edition, which state), then the IEEE
|
|
13
|
+
# flags, then the identifiers an IEEE id nests.
|
|
14
|
+
#
|
|
15
|
+
# The wrappers that name their wrapped document something other than
|
|
16
|
+
# +base+ (+ieee_identifier+, +first_identifier+, +primary_identifier+ …)
|
|
17
|
+
# write it as a nested atom, not as a root-first chain node, so they do
|
|
18
|
+
# not share the slug prefix of that document. Renaming them to +base+
|
|
19
|
+
# would change #to_hash and needs a relaton-data-ieee re-crawl.
|
|
20
|
+
#
|
|
21
|
+
# The official urn:ieee:std: form (RFC 8069) stays on #to_urn.
|
|
22
|
+
PubidUrnSchema = Pubid::PubidUrn.define("ieee") do
|
|
23
|
+
# Who.
|
|
24
|
+
atom "publisher", "pub"
|
|
25
|
+
atom "publishers", "pubs", kind: :list
|
|
26
|
+
atom "lead_party", "lead"
|
|
27
|
+
atom "copublisher", "co", kind: :list
|
|
28
|
+
atom "copublishers.*.body", "copubs"
|
|
29
|
+
# What.
|
|
30
|
+
atom "prefix", "prefix"
|
|
31
|
+
atom "type", "type"
|
|
32
|
+
atom "variant", "variant"
|
|
33
|
+
# Which part.
|
|
34
|
+
atom "parts", "part", kind: :list
|
|
35
|
+
atom "part", "pt"
|
|
36
|
+
atom "subpart", "subpart"
|
|
37
|
+
# When.
|
|
38
|
+
atom "year", "year"
|
|
39
|
+
atom "month", "month"
|
|
40
|
+
atom "day", "day"
|
|
41
|
+
atom "date.year", "dateyear"
|
|
42
|
+
atom "date.month", "datemonth"
|
|
43
|
+
atom "date.day", "dateday"
|
|
44
|
+
atom "date.undated", "undated", kind: :bool
|
|
45
|
+
atom "date_info", "dateinfo"
|
|
46
|
+
atom "iec_year", "iecyear"
|
|
47
|
+
# Which edition or revision.
|
|
48
|
+
atom "edition", "ed"
|
|
49
|
+
atom "edition_month", "edmonth"
|
|
50
|
+
atom "revision", "rev"
|
|
51
|
+
atom "reaffirmed", "reaff"
|
|
52
|
+
# Which state.
|
|
53
|
+
atom "stage", "stage"
|
|
54
|
+
atom "stage_iteration", "iter"
|
|
55
|
+
atom "iso_stage", "isostage"
|
|
56
|
+
atom "ieee_draft", "ieeedraft"
|
|
57
|
+
atom "draft", "draft"
|
|
58
|
+
atom "draft_info", "draftinfo"
|
|
59
|
+
atom "draft_status", "draftstatus"
|
|
60
|
+
atom "typed_stage.abbr", "tsabbr", kind: :list
|
|
61
|
+
atom "typed_stage.stage_code", "tsstage"
|
|
62
|
+
atom "typed_stage.type_code", "tstype"
|
|
63
|
+
atom "typed_stage.iso_stage_equivalent", "tsiso"
|
|
64
|
+
atom "typed_stage.ieee_draft_equivalent", "tsieee"
|
|
65
|
+
atom "typed_stage.approval_status", "tsapproval"
|
|
66
|
+
atom "typed_stage.project_status", "tsproject", kind: :bool
|
|
67
|
+
# Which language and locality.
|
|
68
|
+
atom "languages.*.code", "lang"
|
|
69
|
+
atom "locality.value", "loc"
|
|
70
|
+
# IEEE flags and free-text designations.
|
|
71
|
+
atom "project_marker", "project", kind: :bool
|
|
72
|
+
atom "redline", "redline", kind: :bool
|
|
73
|
+
atom "interpretation", "interp", kind: :bool
|
|
74
|
+
atom "registered", "registered", kind: :bool
|
|
75
|
+
atom "abbr_suffix", "abbrsuffix", kind: :bool
|
|
76
|
+
atom "abbr_suffix_registered", "abbrsuffixreg", kind: :bool
|
|
77
|
+
atom "rev_bare", "revbare", kind: :bool
|
|
78
|
+
atom "space_before_draft", "spacedraft", kind: :bool
|
|
79
|
+
atom "month_glued", "monthglued", kind: :bool
|
|
80
|
+
atom "ashrae_number", "ashrae"
|
|
81
|
+
atom "ashrae_year", "ashraeyear"
|
|
82
|
+
atom "iso_identifier", "isoid"
|
|
83
|
+
atom "adoption", "adoption"
|
|
84
|
+
atom "amendment_to", "amendmentto"
|
|
85
|
+
atom "crossref", "crossref"
|
|
86
|
+
atom "nickname", "nick"
|
|
87
|
+
atom "note", "note"
|
|
88
|
+
atom "parenthetical_content", "paren"
|
|
89
|
+
atom "relationships.*.relationship_type", "rel"
|
|
90
|
+
# Identifiers an IEEE id nests.
|
|
91
|
+
atom "ieee_identifier", "ieeeid", kind: :id
|
|
92
|
+
atom "iec_identifier", "iecid", kind: :id
|
|
93
|
+
atom "first_identifier", "first", kind: :id
|
|
94
|
+
atom "second_identifier", "second", kind: :id
|
|
95
|
+
atom "primary_identifier", "primary", kind: :id
|
|
96
|
+
atom "secondary_identifier", "secondary", kind: :id
|
|
97
|
+
atom "parenthetical_identifier", "parenid", kind: :id
|
|
98
|
+
atom "adopted_identifiers", "adopted", kind: :ids
|
|
99
|
+
atom "revision_of", "revof", kind: :id
|
|
100
|
+
atom "supplement_to", "supto", kind: :id
|
|
101
|
+
atom "amendments", "amds", kind: :ids
|
|
102
|
+
atom "corrigenda", "cors", kind: :ids
|
|
103
|
+
atom "incorporates", "incl", kind: :ids
|
|
104
|
+
atom "supersedes", "supersedes", kind: :ids
|
|
105
|
+
|
|
106
|
+
# Spelling only.
|
|
107
|
+
facet "separator", "sep"
|
|
108
|
+
facet "separators.*", "seps"
|
|
109
|
+
facet "rev_sep", "revsep"
|
|
110
|
+
facet "year_sep", "yearsep"
|
|
111
|
+
facet "date_separator", "datesep"
|
|
112
|
+
facet "original_format", "format"
|
|
113
|
+
facet "languages.*.original_code", "langsrc"
|
|
114
|
+
|
|
115
|
+
type "pubid:ieee:standard", "std"
|
|
116
|
+
type "pubid:ieee:project-draft-identifier", "proj"
|
|
117
|
+
type "pubid:ieee:si-standard", "si"
|
|
118
|
+
type "pubid:ieee:redlined-standard", "redlined"
|
|
119
|
+
type "pubid:ieee:joint-development", "joint"
|
|
120
|
+
type "pubid:ieee:iec-ieee-copublished", "iecieee"
|
|
121
|
+
type "pubid:ieee:dual-identifier", "dual"
|
|
122
|
+
type "pubid:ieee:dual-published", "dualpub"
|
|
123
|
+
type "pubid:ieee:csa-dual-published", "csadual"
|
|
124
|
+
type "pubid:ieee:multi-numbered-identifier", "multi"
|
|
125
|
+
type "pubid:ieee:adopted-standard", "adopted"
|
|
126
|
+
type "pubid:ieee:parenthetical-identifier", "paren"
|
|
127
|
+
|
|
128
|
+
type "pubid:ieee:amendment", "amd"
|
|
129
|
+
type "pubid:ieee:corrigendum", "cor"
|
|
130
|
+
type "pubid:ieee:interpretation-identifier", "int"
|
|
131
|
+
type "pubid:ieee:conformance-identifier", "conf"
|
|
132
|
+
type "pubid:ieee:supplement-identifier", "sup"
|
|
133
|
+
|
|
134
|
+
type "pubid:ieee:aiee", "aiee"
|
|
135
|
+
type "pubid:ieee:ire", "ire"
|
|
136
|
+
|
|
137
|
+
type "pubid:ieee:nesc-standard", "nesc"
|
|
138
|
+
type "pubid:ieee:nesc-edition", "nescedition"
|
|
139
|
+
type "pubid:ieee:nesc-handbook", "nescbook"
|
|
140
|
+
type "pubid:ieee:nesc-draft", "nescdraft"
|
|
141
|
+
type "pubid:ieee:nesc-preprint", "nescpre"
|
|
142
|
+
type "pubid:ieee:nesc-redline", "nescredline"
|
|
143
|
+
|
|
144
|
+
# The base class. The builder makes one for a parenthetical inside a
|
|
145
|
+
# relationship, whose related ids #to_hash does not serialize today;
|
|
146
|
+
# declared so that one in any other slot still emits.
|
|
147
|
+
type "pubid:ieee:identifier", "id"
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
data/lib/pubid/ieee.rb
CHANGED
|
@@ -18,6 +18,7 @@ module Pubid
|
|
|
18
18
|
autoload :Ire, "#{__dir__}/ieee/ire"
|
|
19
19
|
autoload :Nesc, "#{__dir__}/ieee/nesc"
|
|
20
20
|
autoload :Parser, "#{__dir__}/ieee/parser"
|
|
21
|
+
autoload :PubidUrnSchema, "#{__dir__}/ieee/pubid_urn_schema"
|
|
21
22
|
autoload :PreParser, "#{__dir__}/ieee/pre_parser"
|
|
22
23
|
autoload :Compaction, "#{__dir__}/ieee/compaction"
|
|
23
24
|
autoload :ProjectRenderer, "#{__dir__}/ieee/project_renderer"
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pubid
|
|
4
|
+
module Iso
|
|
5
|
+
# The pubid-scheme URN of ISO (issue #656; docs/URN-SCHEMES.adoc).
|
|
6
|
+
#
|
|
7
|
+
# ISO/IEC 13818-1:2015/Amd 3:2016
|
|
8
|
+
# urn:pubid:iso:is:13818:co-IEC:part-1:year-2015:amd:3:co-IEC:year-2016
|
|
9
|
+
#
|
|
10
|
+
# The type tokens follow the RFC 5141 type codes where one exists. The
|
|
11
|
+
# core is the document number; every other value is an optional prefixed
|
|
12
|
+
# atom, in the order below. The official urn:iso:std: form stays on
|
|
13
|
+
# #to_urn (Pubid::Iso::UrnGenerator).
|
|
14
|
+
PubidUrnSchema = Pubid::PubidUrn.define("iso") do
|
|
15
|
+
atom "publisher", "pub"
|
|
16
|
+
atom "copublishers", "co", kind: :list
|
|
17
|
+
atom "part", "part"
|
|
18
|
+
atom "subpart", "subpart"
|
|
19
|
+
atom "year", "year"
|
|
20
|
+
atom "month", "month"
|
|
21
|
+
atom "day", "day"
|
|
22
|
+
atom "undated", "undated", kind: :bool
|
|
23
|
+
atom "edition.number", "ed"
|
|
24
|
+
atom "stage", "stage"
|
|
25
|
+
atom "stage_iteration", "iter"
|
|
26
|
+
atom "supplement_version", "v"
|
|
27
|
+
atom "languages.*.code", "lang"
|
|
28
|
+
atom "locality.value", "loc"
|
|
29
|
+
|
|
30
|
+
facet "languages.*.original_code", "langsrc"
|
|
31
|
+
facet "edition.original_text", "edsrc"
|
|
32
|
+
|
|
33
|
+
type "pubid:iso:international-standard", "is"
|
|
34
|
+
type "pubid:iso:technical-report", "tr"
|
|
35
|
+
type "pubid:iso:technical-specification", "ts"
|
|
36
|
+
type "pubid:iso:guide", "guide"
|
|
37
|
+
type "pubid:iso:pas", "pas"
|
|
38
|
+
type "pubid:iso:recommendation", "r"
|
|
39
|
+
type "pubid:iso:international-standardized-profile", "isp"
|
|
40
|
+
type "pubid:iso:international-workshop-agreement", "iwa"
|
|
41
|
+
type "pubid:iso:technology-trends-assessments", "tta"
|
|
42
|
+
type "pubid:iso:data", "data"
|
|
43
|
+
type "pubid:iso:tc-document", "tcdoc",
|
|
44
|
+
atoms: [%w[tc_type tctype], %w[tc_number tc],
|
|
45
|
+
%w[sc_type sctype], %w[sc_number sc],
|
|
46
|
+
%w[wg_type wgtype], %w[wg_number wg]]
|
|
47
|
+
type "pubid:iso:directives", "dir", atoms: [%w[subgroup subgroup]]
|
|
48
|
+
type "pubid:iso:directives-supplement", "dirsup"
|
|
49
|
+
|
|
50
|
+
type "pubid:iso:amendment", "amd"
|
|
51
|
+
type "pubid:iso:corrigendum", "cor"
|
|
52
|
+
type "pubid:iso:addendum", "add"
|
|
53
|
+
type "pubid:iso:extract", "ext"
|
|
54
|
+
type "pubid:iso:supplement", "sup"
|
|
55
|
+
|
|
56
|
+
type "pubid:iso:all-parts", "all", atoms: [["identifiers", "ids", :ids]]
|
|
57
|
+
type "pubid:iso:bundled-identifier", "bundle",
|
|
58
|
+
atoms: [["supplements", "sups", :ids]]
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
data/lib/pubid/iso.rb
CHANGED
|
@@ -20,6 +20,7 @@ module Pubid
|
|
|
20
20
|
autoload :Identifiers, "#{__dir__}/iso/identifiers"
|
|
21
21
|
autoload :Normalizer, "#{__dir__}/iso/normalizer"
|
|
22
22
|
autoload :Parser, "#{__dir__}/iso/parser"
|
|
23
|
+
autoload :PubidUrnSchema, "#{__dir__}/iso/pubid_urn_schema"
|
|
23
24
|
autoload :RenderingStyle, "#{__dir__}/iso/rendering_style"
|
|
24
25
|
autoload :SingleIdentifier, "#{__dir__}/iso/single_identifier"
|
|
25
26
|
autoload :SupplementIdentifier, "#{__dir__}/iso/supplement_identifier"
|
data/lib/pubid/itu/CLAUDE.md
CHANGED
|
@@ -113,3 +113,18 @@ a replay of all 24,382 rows and every ITU pass fixture showed 0 changes.
|
|
|
113
113
|
**Not done:** an RR supplement (`ITU-R RR (2020) Amd 1` fails; the
|
|
114
114
|
base-less `ITU-R RR Amd 1` still builds an Amendment on series `RR`), and
|
|
115
115
|
`ITU-R RR-E` is still a Recommendation numbered `E` — both as on `main`.
|
|
116
|
+
|
|
117
|
+
## pubid-scheme URN (issue #656)
|
|
118
|
+
|
|
119
|
+
`lib/pubid/itu/pubid_urn_schema.rb` covers all 17 types; design and rulings
|
|
120
|
+
in `docs/URN-SCHEMES.adoc` (`[[urn-itu]]`). **A new rendering flag needs a
|
|
121
|
+
place in that schema, or `#to_pubid_urn` raises for every id that sets it.**
|
|
122
|
+
Declare it as a `:bool` *facet* when it is spelling only (the
|
|
123
|
+
`number_glued`/`series_suffix_spaced` kind — "the same document") and as a
|
|
124
|
+
`:bool` *atom* when it names a different document (`technical`,
|
|
125
|
+
`attachment`, `series_word`). The facet/atom choice is what decides whether
|
|
126
|
+
a slug without its `f` group keeps two ids apart. Not placed today:
|
|
127
|
+
`combined.*.parts` (a list inside a list; no pass fixture has one).
|
|
128
|
+
`imp_marker` (`G.Imp712`) and `common_text_twin` (`| ISO/IEC …`, an `:id`
|
|
129
|
+
atom) are in no pass fixture either; the contract spec reads only `pass/`,
|
|
130
|
+
so sweep `full/` and the spec literals as well after a schema change.
|