pubid 2.0.10 → 2.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/lib/pubid/adobe/pubid_urn_schema.rb +10 -3
  3. data/lib/pubid/amca/pubid_urn_schema.rb +25 -0
  4. data/lib/pubid/amca.rb +1 -0
  5. data/lib/pubid/bipm/CLAUDE.md +9 -0
  6. data/lib/pubid/bipm/pubid_urn_schema.rb +42 -0
  7. data/lib/pubid/bipm.rb +1 -0
  8. data/lib/pubid/bsi/CLAUDE.md +13 -0
  9. data/lib/pubid/bsi/pubid_urn_schema.rb +114 -0
  10. data/lib/pubid/bsi.rb +1 -0
  11. data/lib/pubid/cen_cenelec/CLAUDE.md +11 -0
  12. data/lib/pubid/cen_cenelec/pubid_urn_schema.rb +26 -8
  13. data/lib/pubid/cie/CLAUDE.md +9 -0
  14. data/lib/pubid/cie/pubid_urn_schema.rb +51 -0
  15. data/lib/pubid/cie.rb +1 -0
  16. data/lib/pubid/csa/CLAUDE.md +12 -0
  17. data/lib/pubid/csa/pubid_urn_schema.rb +58 -0
  18. data/lib/pubid/csa.rb +1 -0
  19. data/lib/pubid/doi/pubid_urn_schema.rb +19 -0
  20. data/lib/pubid/doi.rb +1 -0
  21. data/lib/pubid/evs/CLAUDE.md +10 -0
  22. data/lib/pubid/format_registry.rb +5 -1
  23. data/lib/pubid/gb/pubid_urn_schema.rb +23 -0
  24. data/lib/pubid/gb.rb +1 -0
  25. data/lib/pubid/idf/pubid_urn_schema.rb +44 -0
  26. data/lib/pubid/idf.rb +1 -0
  27. data/lib/pubid/iec/CLAUDE.md +13 -0
  28. data/lib/pubid/iec/pubid_urn_schema.rb +46 -8
  29. data/lib/pubid/ietf/pubid_urn_schema.rb +24 -0
  30. data/lib/pubid/ietf.rb +1 -0
  31. data/lib/pubid/iho/CLAUDE.md +11 -0
  32. data/lib/pubid/iho/pubid_urn_schema.rb +49 -0
  33. data/lib/pubid/iho.rb +1 -0
  34. data/lib/pubid/isbn/pubid_urn_schema.rb +20 -0
  35. data/lib/pubid/isbn.rb +1 -0
  36. data/lib/pubid/jis/pubid_urn_schema.rb +29 -0
  37. data/lib/pubid/jis.rb +1 -0
  38. data/lib/pubid/omg/pubid_urn_schema.rb +21 -0
  39. data/lib/pubid/omg.rb +1 -0
  40. data/lib/pubid/sae/pubid_urn_schema.rb +33 -0
  41. data/lib/pubid/sae.rb +1 -0
  42. data/lib/pubid/tgpp/pubid_urn_schema.rb +23 -0
  43. data/lib/pubid/tgpp.rb +1 -0
  44. data/lib/pubid/un/pubid_urn_schema.rb +19 -0
  45. data/lib/pubid/un.rb +1 -0
  46. data/lib/pubid/version.rb +1 -1
  47. data/lib/pubid.rb +28 -2
  48. metadata +18 -2
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pubid
4
+ module Idf
5
+ # The pubid-scheme URN of IDF (issue #670; docs/URN-SCHEMES.adoc).
6
+ #
7
+ # IDF 146:2003/AMD 1:2023 ->
8
+ # urn:pubid:idf:is:146:pubbody-IDF:year-2003:amd:1:year-2023
9
+ #
10
+ # The current urn:idf:… form of #to_urn is pubid-invented and stays
11
+ # there. `type`, `stage` and `typed_stage` serialize every registry
12
+ # field of their components, so each field is its own atom (the NIST
13
+ # ruling): reducing them to one identifying value would change to_hash
14
+ # and need a relaton-data re-crawl — a separate decision.
15
+ PubidUrnSchema = Pubid::PubidUrn.define("idf") do
16
+ atom "publisher.body", "pubbody"
17
+ atom "type.abbr", "typeabbr"
18
+ atom "type.name", "typename"
19
+ atom "type.type_code", "typetypecode"
20
+ atom "part", "part"
21
+ atom "year", "year"
22
+ atom "stage.abbr", "stageabbr"
23
+ atom "stage.name", "stagename"
24
+ atom "stage.stage_code", "stagestagecode"
25
+ atom "stage.harmonized_stages", "stageharmonizedstages", kind: :list
26
+ atom "typed_stage.abbr", "typedstageabbr", kind: :list
27
+ atom "typed_stage.name", "typedstagename"
28
+ atom "typed_stage.code", "typedstage"
29
+ atom "typed_stage.long_abbr", "typedstagelongabbr"
30
+ atom "typed_stage.short_abbr", "typedstageshortabbr"
31
+ atom "typed_stage.stage_code", "typedstagestagecode"
32
+ atom "typed_stage.type_code", "typedstagetypecode"
33
+ atom "typed_stage.harmonized_stages", "typedstageharmonizedstages",
34
+ kind: :list
35
+
36
+ type "pubid:idf:all-parts", "all"
37
+ type "pubid:idf:amendment", "amd"
38
+ type "pubid:idf:corrigendum", "cor"
39
+ type "pubid:idf:identifier", "id"
40
+ type "pubid:idf:international-standard", "is"
41
+ type "pubid:idf:reviewed-method", "rm"
42
+ end
43
+ end
44
+ end
data/lib/pubid/idf.rb CHANGED
@@ -17,6 +17,7 @@ module Pubid
17
17
  autoload :SupplementIdentifier, "#{__dir__}/idf/supplement_identifier"
18
18
  autoload :UrnGenerator, "#{__dir__}/idf/urn_generator"
19
19
  autoload :UrnParser, "#{__dir__}/idf/urn_parser"
20
+ autoload :PubidUrnSchema, "#{__dir__}/idf/pubid_urn_schema"
20
21
 
21
22
  def self.parse(identifier)
22
23
  unless identifier.is_a?(String)
@@ -31,3 +31,16 @@ These notes were part of the root `CLAUDE.md`. Read them before you change `lib/
31
31
  **Two parent-slot gaps recorded by the cross-flavor `base` survey** (`lib/pubid/bsi/CLAUDE.md` has the measured version). `Identifiers::TestReportForm#cispr_identifier` is a polymorphic `Identifier` parent slot with no `#root` override, so `root` returns the form itself. And `Identifiers::ConsolidatedIdentifier#base_document` returns `identifiers&.first` **without** the recursive `&.base_document` that its BSI, CSA and CEN counterparts use — a one-level peel where the others peel fully, so a consolidated edition of a supplement does not reduce to the bare standard. Neither is chased here; both are pinned by nothing, which is why they are written down.
32
32
 
33
33
  **Stage codes — the SSOT and what is settled.** The authoritative source for IEC stage abbreviations is committed in this repo: **`data/iec/stage-codes.md`** — the 52-row IEC/ISO stage-code correspondence table transcribed from IEC's official *IEC/ISO Harmonized APIs* documents (v0.0.8 2023-06, api-portal.iec.ch; v0.0.7 2021-05, iec-apiportal.apigee.io — the successors that replace the IEC Data Feeds), the legacy Data-Feeds field examples, and the IS-lifecycle ladder from `StageCodes_IS_v2.pdf` (2023-06). `www.iec.ch` itself is fully WAF-gated to non-browsers and the Wayback Machine holds no snapshots of its PDFs, so the extract — not the unreachable original — is the reference. **`Pubid::Iec.locate_stage` reads `TYPED_STAGES` only** (`lib/pubid/iec.rb`); `PROJECT_STAGES` (AFDIS, TCDV, PRVC…) are not queryable through it. Every one of metanorma-iec's 55 `STAGE_CODES` — the list quoted in pubid#237 — resolves, locked by `spec/pubid/iec/typed_stages_spec.rb` in the issue's own order; aliases are always **appended, never prepended**, so `abbr.first` (what renders) is unchanged. Notable rulings from that pass: **ADIS** is the legacy Data-Feeds spelling of AFDIS ("Approved for FDIS circulation", ISO_LEVEL 40.99) and is an alias on `cdv` (the typed stage claiming 40.99); **CDISH/CDPAS** are the 50.20 rows of the DISH/DPAS deliverables, not "committee draft ISH/PAS" (30.xx); **Reconciliation items recorded in the extract, deliberately not applied:** DELPUB sits at 99.60 there while the `is` ladder does not claim 99.60; CAN carries two codes (30.98 IS/TR/TS, 50.98 PAS/ISH/COR); the rows CDISH-table-siblings DECDISH/MERGED/SPLIT/PCC/PRVN/DEL/RPUB/TPUB and the per-deliverable translation/preparation families are not registered as typed-stage abbreviations.
34
+
35
+ ## pubid-scheme URN (issue #670)
36
+
37
+ `lib/pubid/iec/pubid_urn_schema.rb` completes the partial schema the IEEE
38
+ and GOST waves landed; rulings in `docs/URN-SCHEMES.adoc` (`[[urn-iec]]`).
39
+ Invariant: every atom added after those waves sits after `database-` and
40
+ before `lang-`, so a landed URN encodes identically (`database` became the
41
+ bool the corpus serializes before any URN carried it). The URN inherits
42
+ the unserialized-identity gaps pinned above: a WorkingDocument's URN is
43
+ the type-only `urn:pubid:iec:wd` (all WD rows share one hash — a shared
44
+ hash, not a collision), and a TestReportForm's CISPR document rides
45
+ `cispr_identifier`. Mapping them stays the wire-format decision it
46
+ already was.
@@ -2,15 +2,28 @@
2
2
 
3
3
  module Pubid
4
4
  module Iec
5
- # The pubid-scheme URN of IEC — PARTIAL (issue #656;
5
+ # The pubid-scheme URN of IEC (issue #670 completes the flavor;
6
6
  # docs/URN-SCHEMES.adoc).
7
7
  #
8
- # Only the types other flavors nest are declared: the International
9
- # Standard (IEC/IEEE co-publications, GOST adoptions), and the TS, TR,
10
- # amendment and consolidated identifier a GOST adoption can name
11
- # ("IEC 60079-0:2017+AMD1:2020"). IEC's own wave declares the rest of the
12
- # flavor; until then IEC stays pending in the contract spec. The official
13
- # RFC 5141-style urn:iec:std: form stays on #to_urn.
8
+ # CISPR 12:2007/AMD1:2009 ->
9
+ # urn:pubid:iec:is:12:pub-CISPR:year-2007:amd:1:pub-CISPR:year-2009
10
+ #
11
+ # The partial schema landed with IEEE named the one type IEEE nests; the
12
+ # GOST wave added the TS, TR, amendment and consolidated identifier a
13
+ # GOST adoption can name. This wave declares the rest of the flavor and
14
+ # the atoms its types serialize (`vap`, the sheet/fragment numbers);
15
+ # every new atom sits after the ones earlier waves placed, so the
16
+ # published URNs are unchanged. `database` was a string atom before any
17
+ # URN carried it — the corpus holds only `true`, so it is a bool.
18
+ #
19
+ # Known identity gaps the URN inherits, because it carries exactly what
20
+ # to_hash carries (pinned in lib/pubid/iec/CLAUDE.md): a WorkingDocument
21
+ # serializes only `_type`, and a TestReportForm's CISPR document sits in
22
+ # the unserialized `cispr_identifier` — such rows emit type-only URNs
23
+ # (`urn:pubid:iec:wd`) and are one hash, so they are not a collision.
24
+ # Mapping them is a wire-format change and stays the separate decision
25
+ # it already was. The official RFC 5141-style urn:iec:std: form stays
26
+ # on #to_urn.
14
27
  PubidUrnSchema = Pubid::PubidUrn.define("iec") do
15
28
  atom "publisher", "pub"
16
29
  atom "copublishers", "co", kind: :list
@@ -23,17 +36,42 @@ module Pubid
23
36
  atom "edition", "ed"
24
37
  atom "stage", "stage"
25
38
  atom "stage_iteration", "iter"
26
- atom "database", "database"
39
+ atom "database", "database", kind: :bool
40
+ atom "fragment_number", "frag"
41
+ atom "sheet_number", "sheet"
42
+ atom "sheet_year", "sheetyear"
43
+ atom "vap", "vap", kind: :list
27
44
  atom "languages.*.code", "lang"
28
45
  atom "identifiers", "ids", kind: :ids
29
46
 
30
47
  facet "languages.*.original_code", "langsrc"
31
48
 
49
+ type "pubid:iec:all-parts", "all"
32
50
  type "pubid:iec:amendment", "amd"
51
+ type "pubid:iec:base", "base"
52
+ type "pubid:iec:component-specification", "cs"
53
+ type "pubid:iec:conformity-assessment", "ca"
33
54
  type "pubid:iec:consolidated-identifier", "consol", core: []
55
+ type "pubid:iec:corrigendum", "cor"
56
+ type "pubid:iec:fragment-identifier", "fi", core: []
57
+ type "pubid:iec:guide", "guide"
58
+ type "pubid:iec:identifier", "id"
34
59
  type "pubid:iec:international-standard", "is"
60
+ type "pubid:iec:interpretation-sheet", "is2"
61
+ type "pubid:iec:operational-document", "od"
62
+ type "pubid:iec:publicly-available-specification", "pas"
63
+ type "pubid:iec:sheet-identifier", "si", core: []
64
+ type "pubid:iec:single-identifier", "si2"
65
+ type "pubid:iec:societal-technology-trend-report", "sttr"
66
+ type "pubid:iec:systems-reference-document", "srd"
67
+ type "pubid:iec:technical-group", "tg"
35
68
  type "pubid:iec:technical-report", "tr"
36
69
  type "pubid:iec:technical-specification", "ts"
70
+ type "pubid:iec:technology-report", "tr2"
71
+ type "pubid:iec:test-report-form", "trf"
72
+ type "pubid:iec:vap-identifier", "vi"
73
+ type "pubid:iec:white-paper", "wp"
74
+ type "pubid:iec:working-document", "wd"
37
75
  end
38
76
  end
39
77
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pubid
4
+ module Ietf
5
+ # The pubid-scheme URN of IETF (issue #670; docs/URN-SCHEMES.adoc).
6
+ #
7
+ # draft-3gpp-collaboration-01 ->
8
+ # urn:pubid:ietf:id2:3gpp-collaboration:v-01
9
+ #
10
+ # The official urn:ietf:rfc:… namespace (RFC 2648) stays on #to_urn.
11
+ # The internet-draft token is `id2`, because `id` names the flavor's
12
+ # bare identifier.
13
+ PubidUrnSchema = Pubid::PubidUrn.define("ietf") do
14
+ atom "version", "v"
15
+
16
+ type "pubid:ietf:bcp", "bcp"
17
+ type "pubid:ietf:fyi", "fyi"
18
+ type "pubid:ietf:identifier", "id"
19
+ type "pubid:ietf:internet-draft", "id2"
20
+ type "pubid:ietf:rfc", "rfc"
21
+ type "pubid:ietf:std", "std"
22
+ end
23
+ end
24
+ end
data/lib/pubid/ietf.rb CHANGED
@@ -24,6 +24,7 @@ module Pubid
24
24
  autoload :Renderer, "#{__dir__}/ietf/renderer"
25
25
  autoload :UrnGenerator, "#{__dir__}/ietf/urn_generator"
26
26
  autoload :UrnParser, "#{__dir__}/ietf/urn_parser"
27
+ autoload :PubidUrnSchema, "#{__dir__}/ietf/pubid_urn_schema"
27
28
 
28
29
  # Parse an IETF identifier string
29
30
  def self.parse(identifier)
@@ -172,3 +172,14 @@ edition rename (the serialized key moved `version` → `edition`) and
172
172
  the volume unification; relaton-cli's `MachineIndex::REFINABLE_KEYS`
173
173
  needs `edition`/`volume` beside the retired `version`/`year` keys; the
174
174
  pubid pin moves in step with the relaton-data-iho re-key.
175
+
176
+ ## pubid-scheme URN (issue #670)
177
+
178
+ `lib/pubid/iho/pubid_urn_schema.rb`; rulings in `docs/URN-SCHEMES.adoc`
179
+ (`[[urn-iho]]`). The atoms follow the containment order of
180
+ `UrnGenerator::SEGMENTS` — the document's `ed-`/`year-` first, then each
181
+ component with its own edition/year right after it (`sup-`, `part-`,
182
+ `parted-`, `appendix-`, `appsub-`, `vol-`, `issue-` …). Case is preserved
183
+ (`part-A`); the yearbook year is a volume (`vol-2024`, no `year-` value —
184
+ the legacy `y.` marker stays decodable in the official URN parser, never
185
+ emitted). The official `urn:mrn:iho:…` form stays on `#to_urn`.
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pubid
4
+ module Iho
5
+ # The pubid-scheme URN of IHO (issue #670; docs/URN-SCHEMES.adoc).
6
+ #
7
+ # IHO S-100 5.0.0 Part 2 (2024) ->
8
+ # urn:pubid:iho:std:100:ed-5.0.0:year-2024:part-2
9
+ #
10
+ # The atoms follow the containment order of the official URN
11
+ # (urn:iho:s:100:ed-5.2.0:part-2, Pubid::Iho::UrnGenerator): the
12
+ # document's edition and year first, then each component with its own
13
+ # edition/year right after it — supplement, part, annex, appendix (the
14
+ # two-level Appendix D-2 keeps the subnumber as its own atom), volume
15
+ # (the unified locator, yearbook volumes included: `vol-2024`), issue.
16
+ # Letters stay case-preserved (`part-A`). The legacy `y.` yearbook
17
+ # marker is decodable in the official URN parser, never emitted here:
18
+ # a periodical has no `year-` atom value, its volume carries the year.
19
+ # The official urn:mrn:iho:… form stays on #to_urn.
20
+ PubidUrnSchema = Pubid::PubidUrn.define("iho") do
21
+ atom "edition", "ed"
22
+ atom "year", "year"
23
+ atom "supplement", "sup"
24
+ atom "supplement_edition", "suped"
25
+ atom "supplement_year", "supyear"
26
+ atom "part", "part"
27
+ atom "part_edition", "parted"
28
+ atom "part_year", "party"
29
+ atom "annex", "annex"
30
+ atom "annex_edition", "anned"
31
+ atom "annex_year", "annyear"
32
+ atom "appendix", "appendix"
33
+ atom "appendix_subnumber", "appsub"
34
+ atom "appendix_edition", "apped"
35
+ atom "appendix_year", "appyear"
36
+ atom "volume", "vol"
37
+ atom "issue", "issue"
38
+ atom "volume_edition", "voled"
39
+ atom "volume_year", "voly"
40
+
41
+ type "pubid:iho:bibliographic", "b"
42
+ type "pubid:iho:circular-letter", "cl"
43
+ type "pubid:iho:identifier", "id"
44
+ type "pubid:iho:miscellaneous", "m"
45
+ type "pubid:iho:publication", "pub"
46
+ type "pubid:iho:standard", "std"
47
+ end
48
+ end
49
+ end
data/lib/pubid/iho.rb CHANGED
@@ -14,6 +14,7 @@ module Pubid
14
14
  autoload :Renderer, "#{__dir__}/iho/renderer"
15
15
  autoload :UrnGenerator, "#{__dir__}/iho/urn_generator"
16
16
  autoload :UrnParser, "#{__dir__}/iho/urn_parser"
17
+ autoload :PubidUrnSchema, "#{__dir__}/iho/pubid_urn_schema"
17
18
 
18
19
  # Parse an IHO identifier string into an identifier object
19
20
  # @param identifier [String] The IHO identifier string to parse
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pubid
4
+ module Isbn
5
+ # The pubid-scheme URN of ISBN (issue #670; docs/URN-SCHEMES.adoc).
6
+ #
7
+ # ISBN 0-306-40615-2 -> urn:pubid:isbn:book:hyphenated-0-306-40615-2:raw-0306406152
8
+ #
9
+ # There is no positional core: the hyphenated and raw spellings are both
10
+ # optional atoms (an ISBN given bare serializes only `raw`). The official
11
+ # urn:isbn: namespace (RFC 3187) stays on #to_urn.
12
+ PubidUrnSchema = Pubid::PubidUrn.define("isbn") do
13
+ atom "hyphenated", "hyphenated"
14
+ atom "raw", "raw"
15
+
16
+ type "pubid:isbn:book", "book", core: []
17
+ type "pubid:isbn:identifier", "id", core: []
18
+ end
19
+ end
20
+ end
data/lib/pubid/isbn.rb CHANGED
@@ -18,6 +18,7 @@ module Pubid
18
18
  autoload :Identifiers, "#{__dir__}/isbn/identifiers"
19
19
  autoload :Parser, "#{__dir__}/isbn/parser"
20
20
  autoload :Renderer, "#{__dir__}/isbn/renderer"
21
+ autoload :PubidUrnSchema, "#{__dir__}/isbn/pubid_urn_schema"
21
22
 
22
23
  def self.parse(identifier)
23
24
  Identifier.parse(identifier)
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pubid
4
+ module Jis
5
+ # The pubid-scheme URN of JIS (issue #670; docs/URN-SCHEMES.adoc).
6
+ #
7
+ # JIS A 0001:1999 -> urn:pubid:jis:jis:0001:series-A:year-1999
8
+ #
9
+ # The current urn:jis:… form of #to_urn is pubid-invented and stays
10
+ # there.
11
+ PubidUrnSchema = Pubid::PubidUrn.define("jis") do
12
+ atom "series", "series"
13
+ atom "parts", "part", kind: :list
14
+ atom "year", "year", kind: :int
15
+ atom "reaffirmed", "reaffirmed", kind: :bool
16
+
17
+ type "pubid:jis:all-parts", "all"
18
+ type "pubid:jis:amendment", "amd"
19
+ type "pubid:jis:corrigendum", "cor"
20
+ type "pubid:jis:explanation", "e"
21
+ type "pubid:jis:identifier", "id"
22
+ type "pubid:jis:japanese-industrial-standard", "jis"
23
+ type "pubid:jis:standard", "std"
24
+ type "pubid:jis:technical-report", "tr"
25
+ type "pubid:jis:technical-specification", "ts"
26
+ type "pubid:jis:welding-engineering-standard", "wes"
27
+ end
28
+ end
29
+ end
data/lib/pubid/jis.rb CHANGED
@@ -19,6 +19,7 @@ module Pubid
19
19
  autoload :SupplementIdentifier, "#{__dir__}/jis/supplement_identifier"
20
20
  autoload :UrnGenerator, "#{__dir__}/jis/urn_generator"
21
21
  autoload :UrnParser, "#{__dir__}/jis/urn_parser"
22
+ autoload :PubidUrnSchema, "#{__dir__}/jis/pubid_urn_schema"
22
23
 
23
24
  # Parse a JIS identifier string
24
25
  def self.parse(identifier)
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pubid
4
+ module Omg
5
+ # The pubid-scheme URN of OMG (issue #670; docs/URN-SCHEMES.adoc).
6
+ #
7
+ # OMG AMI4CCM 1.0 -> urn:pubid:omg:spec:acronym-AMI4CCM:v-1.0
8
+ #
9
+ # No positional core: an OMG specification names itself by its acronym.
10
+ # The current urn:omg:… form of #to_urn is pubid-invented and stays
11
+ # there.
12
+ PubidUrnSchema = Pubid::PubidUrn.define("omg") do
13
+ atom "part", "part"
14
+ atom "version", "v"
15
+ atom "acronym", "acronym"
16
+
17
+ type "pubid:omg:identifier", "id", core: []
18
+ type "pubid:omg:specification", "spec", core: []
19
+ end
20
+ end
21
+ end
data/lib/pubid/omg.rb CHANGED
@@ -17,6 +17,7 @@ module Pubid
17
17
  autoload :Identifiers, "#{__dir__}/omg/identifiers"
18
18
  autoload :Parser, "#{__dir__}/omg/parser"
19
19
  autoload :Renderer, "#{__dir__}/omg/renderer"
20
+ autoload :PubidUrnSchema, "#{__dir__}/omg/pubid_urn_schema"
20
21
 
21
22
  def self.parse(identifier)
22
23
  Identifier.parse(identifier)
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pubid
4
+ module Sae
5
+ # The pubid-scheme URN of SAE (issue #670; docs/URN-SCHEMES.adoc).
6
+ #
7
+ # SAE J3016_202104 -> urn:pubid:sae:id:3016:typeabbr-J:year-2021:month-4
8
+ # SAE ARP4754A:2010 -> urn:pubid:sae:id:4754:typeabbr-ARP:rev-A:yearflat-2010
9
+ #
10
+ # The current urn:sae:… form of #to_urn is pubid-invented and stays
11
+ # there. The type (`J`, `AS`, `ARP`, `AMS`) is an atom, because the
12
+ # identifier type is one class carrying every series code; the date
13
+ # fields are Integers.
14
+ #
15
+ # SAE's hash carries two date shapes (the canonical flattening keeps a
16
+ # year-alone date flat: pubid#360), so a year gets one prefix per shape
17
+ # — the ashrae ruling. `year-` stays the nested year+month form the
18
+ # first release emitted; `yearflat-` is the flat year-alone form the
19
+ # corpus round (pubid#669) added. A part is top-level in the hash
20
+ # (AMS-QQ-A-250/4B -> part-4).
21
+ PubidUrnSchema = Pubid::PubidUrn.define("sae") do
22
+ atom "type.abbr", "typeabbr"
23
+ atom "revision", "rev"
24
+ atom "amendment", "amd"
25
+ atom "part", "part"
26
+ atom "date.year", "year", kind: :int
27
+ atom "date.month", "month", kind: :int
28
+ atom "year", "yearflat"
29
+
30
+ type "pubid:sae:identifier", "id"
31
+ end
32
+ end
33
+ end
data/lib/pubid/sae.rb CHANGED
@@ -17,6 +17,7 @@ module Pubid
17
17
  autoload :Renderer, "#{__dir__}/sae/renderer"
18
18
  autoload :UrnGenerator, "#{__dir__}/sae/urn_generator"
19
19
  autoload :UrnParser, "#{__dir__}/sae/urn_parser"
20
+ autoload :PubidUrnSchema, "#{__dir__}/sae/pubid_urn_schema"
20
21
 
21
22
  def self.parse(input)
22
23
  Identifier.parse(input)
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pubid
4
+ module Tgpp
5
+ # The pubid-scheme URN of 3GPP (issue #670; docs/URN-SCHEMES.adoc).
6
+ #
7
+ # TS 01.01:REL-96/5.0.0 -> urn:pubid:3gpp:ts:01.01:part-01:release-96:v-5.0.0
8
+ #
9
+ # The official urn:3gpp: namespace (RFC 5279) stays on #to_urn. The
10
+ # parts ("01.01" splits to part "01" and part "01"…) are the list the
11
+ # flavor models; the release and the version close the URN.
12
+ PubidUrnSchema = Pubid::PubidUrn.define("3gpp") do
13
+ atom "parts", "part", kind: :list
14
+ atom "version", "v"
15
+ atom "release", "release"
16
+ atom "suffix", "suffix"
17
+
18
+ type "pubid:3gpp:identifier", "id"
19
+ type "pubid:3gpp:technical-report", "tr"
20
+ type "pubid:3gpp:technical-specification", "ts"
21
+ end
22
+ end
23
+ end
data/lib/pubid/tgpp.rb CHANGED
@@ -34,6 +34,7 @@ module Pubid
34
34
  autoload :Renderer, "#{__dir__}/tgpp/renderer"
35
35
  autoload :UrnGenerator, "#{__dir__}/tgpp/urn_generator"
36
36
  autoload :UrnParser, "#{__dir__}/tgpp/urn_parser"
37
+ autoload :PubidUrnSchema, "#{__dir__}/tgpp/pubid_urn_schema"
37
38
 
38
39
  # Parse a 3GPP identifier string
39
40
  def self.parse(identifier)
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pubid
4
+ module Un
5
+ # The pubid-scheme URN of UN (issue #670; docs/URN-SCHEMES.adoc).
6
+ #
7
+ # A/RES/78/1 -> urn:pubid:un:document:A,RES,78:1
8
+ #
9
+ # The path segments are the list before the final number; the number is
10
+ # the positional core. The current urn:un:… form of #to_urn is
11
+ # pubid-invented and stays there.
12
+ PubidUrnSchema = Pubid::PubidUrn.define("un") do
13
+ atom "path", "path", kind: :list
14
+
15
+ type "pubid:un:document", "document"
16
+ type "pubid:un:identifier", "id"
17
+ end
18
+ end
19
+ end
data/lib/pubid/un.rb CHANGED
@@ -12,6 +12,7 @@ module Pubid
12
12
  autoload :Identifiers, "#{__dir__}/un/identifiers"
13
13
  autoload :Parser, "#{__dir__}/un/parser"
14
14
  autoload :Renderer, "#{__dir__}/un/renderer"
15
+ autoload :PubidUrnSchema, "#{__dir__}/un/pubid_urn_schema"
15
16
 
16
17
  def self.parse(identifier)
17
18
  Identifier.parse(identifier)
data/lib/pubid/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Pubid
4
- VERSION = "2.0.10"
4
+ VERSION = "2.0.12"
5
5
  end
data/lib/pubid.rb CHANGED
@@ -238,6 +238,7 @@ module Pubid
238
238
  # @param format [Symbol] :auto, :human, :mr_string, or :urn
239
239
  # @return [Identifier] The parsed identifier
240
240
  # @raise [ArgumentError] for a non-String, or one over {MAX_INPUT_LENGTH}
241
+ # (as {Errors::InvalidInputError}, which is one)
241
242
  # @raise [Pubid::Errors::ParseError] when no flavor can parse the string — the
242
243
  # class every flavor's own +parse+ raises (see the parse-failure contract)
243
244
  def self.parse(string, format: :auto)
@@ -249,6 +250,15 @@ module Pubid
249
250
  raise Pubid::Errors::InvalidInputError,
250
251
  Pubid::INPUT_TOO_LONG_MESSAGE
251
252
  end
253
+ # Invalid encoding is bad input, not a grammar failure: the detectors
254
+ # and normalization regexes raise raw `ArgumentError` (invalid byte
255
+ # sequence) or `Encoding::CompatibilityError` on it, which is the leak
256
+ # the contract closes (issue #670). The bytes are read as UTF-8 — a
257
+ # binary string of ASCII bytes (".b") is fine, high bytes are not.
258
+ unless string.dup.force_encoding(Encoding::UTF_8).valid_encoding?
259
+ raise Pubid::Errors::InvalidInputError,
260
+ "input string has invalid encoding"
261
+ end
252
262
 
253
263
  format = FormatDetector.detect(string) if format == :auto
254
264
 
@@ -282,22 +292,38 @@ module Pubid
282
292
  #
283
293
  # @param string [String] the URN
284
294
  # @return [Identifier] the parsed identifier
295
+ # @raise [Pubid::Errors::InvalidInputError] for a non-String
285
296
  #
286
297
  # A pubid-scheme URN ("urn:pubid:…", see {PubidUrn}) is decoded by the
287
298
  # shared codec; every other namespace goes to its flavor's UrnParser.
299
+ # Every decode failure on this surface is a pubid error (issue #670): an
300
+ # empty string, a missing or unknown namespace, invalid encoding, and a
301
+ # URN a flavor's parser rejects all raise {Errors::UrnParseError} — the
302
+ # raw `ArgumentError` the detection and the flavor parsers raise is the
303
+ # wrapped failure's cause, never the raised class.
288
304
  def self.parse_urn(string)
305
+ unless string.is_a?(String)
306
+ raise Pubid::Errors::InvalidInputError,
307
+ Pubid::INPUT_NOT_A_STRING_MESSAGE
308
+ end
289
309
  eager_load_flavors!
290
310
  return PubidUrn.parse(string) if PubidUrn.pubid_urn?(string)
291
311
 
292
312
  flavor = detect_flavor_from_urn(string)
293
313
  flavor_module = Registry.get(flavor)
294
314
  unless flavor_module
295
- raise ArgumentError,
296
- "Unknown flavor in URN: #{flavor}"
315
+ raise Errors::UrnParseError,
316
+ "unknown URN namespace: #{flavor.inspect} (in #{string.inspect})"
297
317
  end
298
318
 
299
319
  urn_parser = flavor_module.const_get(:UrnParser)
300
320
  urn_parser.parse(string)
321
+ rescue Errors::Error
322
+ raise
323
+ rescue StandardError => e
324
+ raise Errors::UrnParseError,
325
+ "URN #{string.inspect} does not parse as #{flavor.inspect}: #{e.message}",
326
+ cause: e
301
327
  end
302
328
 
303
329
  # The slug form (relaton#441): the pubid-scheme URN of +source+ in