pubid 2.0.17 → 2.0.19
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 +135 -0
- data/lib/pubid/adobe/identifier.rb +2 -0
- data/lib/pubid/amca/identifiers/base.rb +2 -0
- data/lib/pubid/ansi/identifier.rb +2 -0
- data/lib/pubid/api/identifier.rb +2 -0
- data/lib/pubid/ashrae/identifiers/base.rb +2 -0
- data/lib/pubid/ashrae.rb +2 -0
- data/lib/pubid/asme/identifier.rb +2 -0
- data/lib/pubid/astm/identifier.rb +2 -0
- data/lib/pubid/bipm/identifier.rb +2 -0
- data/lib/pubid/bipm.rb +2 -0
- data/lib/pubid/bsi/single_identifier.rb +2 -0
- data/lib/pubid/bsi.rb +2 -0
- data/lib/pubid/calconnect/identifier.rb +2 -0
- data/lib/pubid/calconnect.rb +2 -0
- data/lib/pubid/ccsds/identifier.rb +2 -0
- data/lib/pubid/cen_cenelec/identifier.rb +2 -0
- data/lib/pubid/cen_cenelec.rb +2 -0
- data/lib/pubid/cie/identifier.rb +2 -0
- data/lib/pubid/cie.rb +2 -0
- data/lib/pubid/components/typed_stage.rb +7 -6
- data/lib/pubid/csa/identifier.rb +2 -0
- data/lib/pubid/csa.rb +2 -0
- data/lib/pubid/doi/identifier.rb +2 -0
- data/lib/pubid/easc/identifier.rb +2 -0
- data/lib/pubid/ecma/identifier.rb +2 -0
- data/lib/pubid/etsi/identifiers/base.rb +2 -0
- data/lib/pubid/evs/identifier.rb +2 -0
- data/lib/pubid/evs.rb +2 -0
- data/lib/pubid/gb/identifier.rb +2 -0
- data/lib/pubid/gost/identifier.rb +2 -0
- data/lib/pubid/iala/identifier.rb +2 -0
- data/lib/pubid/iana/identifier.rb +2 -0
- data/lib/pubid/idf/identifier.rb +2 -0
- data/lib/pubid/idf.rb +2 -0
- data/lib/pubid/iec/identifier.rb +2 -0
- data/lib/pubid/iec/urn_parser.rb +5 -1
- data/lib/pubid/ieee/identifiers/base.rb +2 -0
- data/lib/pubid/ieee.rb +2 -0
- data/lib/pubid/ietf/identifiers/base.rb +2 -0
- data/lib/pubid/ietf.rb +2 -0
- data/lib/pubid/iho/identifiers/base.rb +2 -0
- data/lib/pubid/isbn/identifier.rb +3 -1
- data/lib/pubid/iso/CLAUDE.md +15 -0
- data/lib/pubid/iso/identifier.rb +2 -0
- data/lib/pubid/iso/urn_parser.rb +12 -2
- data/lib/pubid/iso.rb +2 -0
- data/lib/pubid/itu/identifiers/base.rb +2 -0
- data/lib/pubid/jcgm.rb +2 -0
- data/lib/pubid/jis/identifier.rb +2 -0
- data/lib/pubid/nist.rb +2 -0
- data/lib/pubid/oasis/identifier.rb +2 -0
- data/lib/pubid/oasis.rb +2 -0
- data/lib/pubid/ogc/identifier.rb +2 -0
- data/lib/pubid/oiml.rb +2 -0
- data/lib/pubid/omg/identifier.rb +2 -0
- data/lib/pubid/plateau.rb +2 -0
- data/lib/pubid/sae/identifiers/base.rb +2 -0
- data/lib/pubid/subset_match.rb +1 -3
- data/lib/pubid/tgpp/identifier.rb +2 -0
- data/lib/pubid/tgpp.rb +2 -0
- data/lib/pubid/un/identifier.rb +2 -0
- data/lib/pubid/urn_parser/base.rb +41 -1
- data/lib/pubid/version.rb +1 -1
- data/lib/pubid/w3c/identifier.rb +2 -0
- data/lib/pubid/xsf/identifier.rb +2 -0
- data/lib/pubid/xsf.rb +2 -0
- data/lib/pubid.rb +53 -9
- metadata +5 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d2bbeed2065311312a5f62f6315be2f0d29cf03245755bf5a95b741567e08a96
|
|
4
|
+
data.tar.gz: cd32eefaf7a17f10fae7b4a78055e3f241758e544848d4b1dac239a0d1ff30c1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9b543322085f97459a47b0ccd67541b9ae9106695e4779e4e385294460aa9c702f5247623d19762ee33c37810031c3d97e030d5d67e5859fde2e932b18f0e140
|
|
7
|
+
data.tar.gz: e147a8e6484951b7a31db7f013942aae7ebad11fe63b321c325fe71d72025e27415dce3cbcfa85f4159f6d2cedf2369da1a77dbce80702708333b7a4c2f62a0a
|
data/README.adoc
CHANGED
|
@@ -51,6 +51,141 @@ both humans and machines, in a round-trippable manner.
|
|
|
51
51
|
|
|
52
52
|
See <<v2-migration-status>> for detailed status and <<v2-usage-examples>> for examples.
|
|
53
53
|
|
|
54
|
+
== Documentation
|
|
55
|
+
|
|
56
|
+
[cols="1,2", options="header"]
|
|
57
|
+
|===
|
|
58
|
+
|Document |Contents
|
|
59
|
+
|
|
60
|
+
|link:docs/CONSTRUCTION.md[Constructing identifiers programmatically]
|
|
61
|
+
|The v2 replacement for the v1 `Identifier.create`: `.new` keyword
|
|
62
|
+
construction, `Pubid.from_hash`, supplement-with-base, copublishers, stages,
|
|
63
|
+
and the cross-flavor keyword conventions (spec-tested by
|
|
64
|
+
`spec/pubid/attribute_construction_spec.rb`).
|
|
65
|
+
|
|
66
|
+
|link:docs/flavor-guides/iso.md[ISO: usage, rendering formats and stage mapping]
|
|
67
|
+
|ISO rendering options (`lang_single`, `stage_format_long`, `with_date`,
|
|
68
|
+
`with_edition`), the DAM/FDAM and DCor/FDCor supplement-stage abbreviations,
|
|
69
|
+
the per-doctype harmonized-stage mapping tables, stage iteration, and typed
|
|
70
|
+
stage queries. Ported from the archived pubid-iso README, restated for v2.
|
|
71
|
+
|
|
72
|
+
|link:docs/flavor-guides/ieee.md[IEEE identifier patterns]
|
|
73
|
+
|The Type I / Type II pattern series (with the full historic regexes), the
|
|
74
|
+
filename generator (`to_mr_string`/`to_slug`), relationship clauses, and the
|
|
75
|
+
AIEE/IRE predecessor-organization forms. Ported from the archived pubid-ieee
|
|
76
|
+
README.
|
|
77
|
+
|
|
78
|
+
|link:docs/flavor-guides/iec.md[IEC: TRF format grammars]
|
|
79
|
+
|IECEE and IECEx Test Report Form patterns — the `&` part join, the
|
|
80
|
+
`F_II`-style sub-versions, the `_EMC`/`_PS`/`_SOF` test-type suffixes, and the
|
|
81
|
+
IECEx `_`/`v` separator. Ported from the archived pubid-iec README.
|
|
82
|
+
|
|
83
|
+
|link:docs/flavor-guides/plateau.md[PLATEAU: the Handbook #11 edition split]
|
|
84
|
+
|Why an edition-1–3 "Handbook #11" reference with a part suffix parses as
|
|
85
|
+
Handbook #06 / #04 (the rule encoded in `data/plateau/update_codes.yaml`).
|
|
86
|
+
Ported from the archived pubid-plateau README.
|
|
87
|
+
|
|
88
|
+
|link:docs/RENDERING_GUIDE.md[Advanced Rendering Styles Guide]
|
|
89
|
+
|ISO short/long supplement abbreviation preservation and IEC `AMD1` vs
|
|
90
|
+
`Amd 1` spacing.
|
|
91
|
+
|
|
92
|
+
|link:docs/identifier-patterns/README.md[Identifier pattern reference (generated)]
|
|
93
|
+
|Per-flavor reference docs (`docs/identifier-patterns/<flavor>.md`) generated
|
|
94
|
+
from the code by `rake docs:patterns` — identifier types, typed stages,
|
|
95
|
+
harmonized codes.
|
|
96
|
+
|
|
97
|
+
|link:docs/URN-GENERATION-GUIDE.adoc[URN Generation Guide]
|
|
98
|
+
|`#to_urn` and `#to_pubid_urn`, RFC 5141-bis.
|
|
99
|
+
|===
|
|
100
|
+
|
|
101
|
+
=== Flavor index
|
|
102
|
+
|
|
103
|
+
All flavors live in the one gem under `lib/pubid/<flavor>/`; each has a
|
|
104
|
+
publisher page on https://pubid.github.io/publishers[pubid.github.io].
|
|
105
|
+
|
|
106
|
+
// Compatibility aliases (:cen -> :cen_cenelec, :3gpp -> module Tgpp) are noted inline.
|
|
107
|
+
|===
|
|
108
|
+
|Flavor |Flavor |Flavor
|
|
109
|
+
|
|
110
|
+
|link:https://pubid.github.io/publishers/iso[ISO]
|
|
111
|
+
|link:https://pubid.github.io/publishers/iec[IEC]
|
|
112
|
+
|link:https://pubid.github.io/publishers/ieee[IEEE]
|
|
113
|
+
|
|
114
|
+
|link:https://pubid.github.io/publishers/nist[NIST]
|
|
115
|
+
|link:https://pubid.github.io/publishers/itu[ITU]
|
|
116
|
+
|link:https://pubid.github.io/publishers/bsi[BSI]
|
|
117
|
+
|
|
118
|
+
|link:https://pubid.github.io/publishers/cen-cenelec[CEN/CENELEC]
|
|
119
|
+
|link:https://pubid.github.io/publishers/etsi[ETSI]
|
|
120
|
+
|link:https://pubid.github.io/publishers/jis[JIS]
|
|
121
|
+
|
|
122
|
+
|link:https://pubid.github.io/publishers/tgpp[3GPP]
|
|
123
|
+
|link:https://pubid.github.io/publishers/ansi[ANSI]
|
|
124
|
+
|link:https://pubid.github.io/publishers/api[API]
|
|
125
|
+
|
|
126
|
+
|link:https://pubid.github.io/publishers/astm[ASTM]
|
|
127
|
+
|link:https://pubid.github.io/publishers/ashrae[ASHRAE]
|
|
128
|
+
|link:https://pubid.github.io/publishers/asme[ASME]
|
|
129
|
+
|
|
130
|
+
|link:https://pubid.github.io/publishers/bipm[BIPM]
|
|
131
|
+
|link:https://pubid.github.io/publishers/ccsds[CCSDS]
|
|
132
|
+
|link:https://pubid.github.io/publishers/cie[CIE]
|
|
133
|
+
|
|
134
|
+
|link:https://pubid.github.io/publishers/csa[CSA]
|
|
135
|
+
|link:https://pubid.github.io/publishers/doi[DOI]
|
|
136
|
+
|link:https://pubid.github.io/publishers/easc[EASC]
|
|
137
|
+
|
|
138
|
+
|link:https://pubid.github.io/publishers/ecma[ECMA]
|
|
139
|
+
|link:https://pubid.github.io/publishers/evs[EVS]
|
|
140
|
+
|link:https://pubid.github.io/publishers/gb[GB]
|
|
141
|
+
|
|
142
|
+
|link:https://pubid.github.io/publishers/gost[GOST]
|
|
143
|
+
|link:https://pubid.github.io/publishers/iala[IALA]
|
|
144
|
+
|link:https://pubid.github.io/publishers/iana[IANA]
|
|
145
|
+
|
|
146
|
+
|link:https://pubid.github.io/publishers/idf[IDF]
|
|
147
|
+
|link:https://pubid.github.io/publishers/ietf[IETF]
|
|
148
|
+
|link:https://pubid.github.io/publishers/iho[IHO]
|
|
149
|
+
|
|
150
|
+
|link:https://pubid.github.io/publishers/isbn[ISBN]
|
|
151
|
+
|link:https://pubid.github.io/publishers/jcgm[JCGM]
|
|
152
|
+
|link:https://pubid.github.io/publishers/oasis[OASIS]
|
|
153
|
+
|
|
154
|
+
|link:https://pubid.github.io/publishers/ogc[OGC]
|
|
155
|
+
|link:https://pubid.github.io/publishers/oiml[OIML]
|
|
156
|
+
|link:https://pubid.github.io/publishers/omg[OMG]
|
|
157
|
+
|
|
158
|
+
|link:https://pubid.github.io/publishers/plateau[PLATEAU]
|
|
159
|
+
|link:https://pubid.github.io/publishers/sae[SAE]
|
|
160
|
+
|link:https://pubid.github.io/publishers/un[UN]
|
|
161
|
+
|
|
162
|
+
|link:https://pubid.github.io/publishers/w3c[W3C]
|
|
163
|
+
|link:https://pubid.github.io/publishers/xsf[XSF]
|
|
164
|
+
|link:https://pubid.github.io/publishers/adobe[Adobe]
|
|
165
|
+
|
|
166
|
+
|link:https://pubid.github.io/publishers/amca[AMCA]
|
|
167
|
+
|link:https://pubid.github.io/publishers/calconnect[CalConnect]
|
|
168
|
+
|
|
|
169
|
+
|===
|
|
170
|
+
|
|
171
|
+
(The gem's 3GPP flavor key is `3gpp`; its publisher page is `tgpp`. CEN and
|
|
172
|
+
CENELEC share the `cen_cenelec` flavor, publisher page `cen-cenelec`.)
|
|
173
|
+
|
|
174
|
+
=== Historic identifier patterns
|
|
175
|
+
|
|
176
|
+
Some identifier eras predate the flavors and are not fully re-documented here;
|
|
177
|
+
their pattern records live in the archived per-gem READMEs
|
|
178
|
+
(`gh api repos/pubid/pubid-<flavor>/contents/README.adoc`):
|
|
179
|
+
|
|
180
|
+
* *NBS-era NIST* — the National Bureau of Standards series (1920s–1988:
|
|
181
|
+
`NBS CIRC`, `NBS LCIRC`, `NBS CSM`, `FIPS` before the NIST rename) are
|
|
182
|
+
described in the archived link:https://github.com/pubid/pubid-nist#readme[pubid-nist README];
|
|
183
|
+
the v2 parser accepts them and preserves the NBS publisher.
|
|
184
|
+
* *AIEE and IRE IEEE* — the predecessor organizations (1884–1963) are covered
|
|
185
|
+
in link:docs/flavor-guides/ieee.md[the IEEE patterns doc] and the archived
|
|
186
|
+
link:https://github.com/pubid/pubid-ieee#readme[pubid-ieee README].
|
|
187
|
+
|
|
188
|
+
|
|
54
189
|
== URN Generation (RFC 5141-bis)
|
|
55
190
|
|
|
56
191
|
PubID V2 implements RFC 5141-bis compliant URN generation for ISO and IEC identifiers with **full test coverage**.
|
|
@@ -27,6 +27,8 @@ module Pubid
|
|
|
27
27
|
raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
identifier = ::Pubid.utf8_input(identifier)
|
|
31
|
+
|
|
30
32
|
# R1 parser swap: the baked PG artifact is the parser of record.
|
|
31
33
|
parsed = Pubid::Parg::Model.bound(:adobe, identifier.strip)
|
|
32
34
|
Builder.build(parsed)
|
data/lib/pubid/api/identifier.rb
CHANGED
|
@@ -28,6 +28,8 @@ module Pubid
|
|
|
28
28
|
raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
+
input = ::Pubid.utf8_input(input)
|
|
32
|
+
|
|
31
33
|
# There used to be a `return nil if input.start_with?("#")` here, to
|
|
32
34
|
# skip the comment lines of a fixture file. A comment is not an
|
|
33
35
|
# identifier, so it must be rejected like any other unparseable string:
|
data/lib/pubid/ashrae.rb
CHANGED
|
@@ -281,6 +281,8 @@ module Pubid
|
|
|
281
281
|
raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
|
|
282
282
|
end
|
|
283
283
|
|
|
284
|
+
identifier = ::Pubid.utf8_input(identifier)
|
|
285
|
+
|
|
284
286
|
# Normalize legacy/docnumber-style spellings before parsing
|
|
285
287
|
# (data/bipm/update_codes.yaml), mirroring Pubid::Ccsds / Pubid::Plateau.
|
|
286
288
|
normalized = Core::UpdateCodes.apply(identifier, :bipm)
|
data/lib/pubid/bipm.rb
CHANGED
|
@@ -18,6 +18,8 @@ module Pubid
|
|
|
18
18
|
raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
+
string = ::Pubid.utf8_input(string)
|
|
22
|
+
|
|
21
23
|
# Delegate to IEC for bare IEC identifiers. This handles IEC-specific
|
|
22
24
|
# features like VAP suffixes (CSV, RLV, etc.) and consolidated
|
|
23
25
|
# supplements (+AMD1:2001).
|
data/lib/pubid/bsi.rb
CHANGED
data/lib/pubid/calconnect.rb
CHANGED
|
@@ -75,6 +75,8 @@ module Pubid
|
|
|
75
75
|
raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
|
|
76
76
|
end
|
|
77
77
|
|
|
78
|
+
identifier = ::Pubid.utf8_input(identifier)
|
|
79
|
+
|
|
78
80
|
# Apply legacy update_codes normalization first
|
|
79
81
|
normalized = Core::UpdateCodes.apply(identifier, :ccsds)
|
|
80
82
|
parsed = Pubid::Parg::Model.bound(:ccsds, normalized)
|
|
@@ -119,6 +119,8 @@ module Pubid
|
|
|
119
119
|
raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
|
|
120
120
|
end
|
|
121
121
|
|
|
122
|
+
identifier = ::Pubid.utf8_input(identifier)
|
|
123
|
+
|
|
122
124
|
normalized = Parser.normalize_input(identifier)
|
|
123
125
|
parsed = Pubid::Parg::Model.bound(:cen_cenelec, normalized)
|
|
124
126
|
Builder.new.build(parsed)
|
data/lib/pubid/cen_cenelec.rb
CHANGED
data/lib/pubid/cie/identifier.rb
CHANGED
|
@@ -28,6 +28,8 @@ module Pubid
|
|
|
28
28
|
raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
+
input = ::Pubid.utf8_input(input)
|
|
32
|
+
|
|
31
33
|
parsed = Pubid::Parg::Model.bound(:cie, Parser.normalize_input(input))
|
|
32
34
|
builder = Builder.new
|
|
33
35
|
builder.build(parsed, original_string: input)
|
data/lib/pubid/cie.rb
CHANGED
|
@@ -8,12 +8,7 @@ module Pubid
|
|
|
8
8
|
# URN render: stage code (RFC 5141-bis: stage-XX.XX format handled by urn_generator).
|
|
9
9
|
class TypedStage < Lutaml::Model::Serializable
|
|
10
10
|
include ::Pubid::SubsetMatch
|
|
11
|
-
|
|
12
|
-
# `original_abbr` records the input spelling ("Amd" or "AMD"), not the
|
|
13
|
-
# stage, so a subset match skips it.
|
|
14
|
-
def self.subset_ignored_attributes
|
|
15
|
-
%i[original_abbr]
|
|
16
|
-
end
|
|
11
|
+
include ::Pubid::EqualityIgnored
|
|
17
12
|
|
|
18
13
|
attribute :name, :string
|
|
19
14
|
attribute :code, :string
|
|
@@ -25,6 +20,12 @@ module Pubid
|
|
|
25
20
|
attribute :short_abbr, :string
|
|
26
21
|
attribute :long_abbr, :string
|
|
27
22
|
|
|
23
|
+
# `original_abbr` records the input spelling ("Amd.", "AMD", "DAmd"),
|
|
24
|
+
# not the stage, so `==`, `hash` and `===` skip it (issue #656). It is
|
|
25
|
+
# not serialized: the parse path keeps the input spelling and
|
|
26
|
+
# `from_hash` rebuilds the canonical one, and the two ids must be ==.
|
|
27
|
+
equality_ignored :original_abbr
|
|
28
|
+
|
|
28
29
|
def to_stage
|
|
29
30
|
Stage.new(
|
|
30
31
|
name: name,
|
data/lib/pubid/csa/identifier.rb
CHANGED
data/lib/pubid/csa.rb
CHANGED
data/lib/pubid/doi/identifier.rb
CHANGED
data/lib/pubid/evs/identifier.rb
CHANGED
data/lib/pubid/evs.rb
CHANGED
|
@@ -34,6 +34,8 @@ module Pubid
|
|
|
34
34
|
raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
+
identifier = ::Pubid.utf8_input(identifier)
|
|
38
|
+
|
|
37
39
|
# The trailing language code (" et" / " en" / " et-en") extracts
|
|
38
40
|
# before the grammar runs — the CEN adopted_string is greedy over
|
|
39
41
|
# plain text and would swallow it.
|
data/lib/pubid/gb/identifier.rb
CHANGED
data/lib/pubid/idf/identifier.rb
CHANGED
data/lib/pubid/idf.rb
CHANGED
data/lib/pubid/iec/identifier.rb
CHANGED
|
@@ -316,6 +316,8 @@ module Pubid
|
|
|
316
316
|
raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
|
|
317
317
|
end
|
|
318
318
|
|
|
319
|
+
string = ::Pubid.utf8_input(string)
|
|
320
|
+
|
|
319
321
|
# Route URN strings to the URN parser (mirrors Iso::Identifier.parse)
|
|
320
322
|
if Pubid::FormatDetector.detect(string) == :urn
|
|
321
323
|
return Pubid::Iec::UrnParser.parse(string)
|
data/lib/pubid/iec/urn_parser.rb
CHANGED
|
@@ -22,8 +22,12 @@ module Pubid
|
|
|
22
22
|
# Parse IEC URN string
|
|
23
23
|
# @param urn [String] URN string to parse
|
|
24
24
|
# @return [Identifier] parsed identifier
|
|
25
|
+
# @raise [Pubid::Errors::InvalidInputError] for a non-String
|
|
26
|
+
# @raise [Pubid::Errors::UrnParseError] for a URN that does not parse
|
|
25
27
|
def self.parse(urn)
|
|
26
|
-
|
|
28
|
+
::Pubid::UrnParser::Base.guarded_parse(urn) do |utf8|
|
|
29
|
+
new.parse_urn(utf8)
|
|
30
|
+
end
|
|
27
31
|
end
|
|
28
32
|
|
|
29
33
|
# Parse URN string into identifier
|
data/lib/pubid/ieee.rb
CHANGED
data/lib/pubid/ietf.rb
CHANGED
|
@@ -53,7 +53,9 @@ module Pubid
|
|
|
53
53
|
raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
identifier = ::Pubid.utf8_input(identifier)
|
|
57
|
+
|
|
58
|
+
# The guards above must stay OUTSIDE build_identifier: this method
|
|
57
59
|
# rescues ArgumentError to convert the Builder's check-digit and
|
|
58
60
|
# length validation into a parse failure, and that rescue used to
|
|
59
61
|
# swallow the guards' own ArgumentError and re-raise it as a
|
data/lib/pubid/iso/CLAUDE.md
CHANGED
|
@@ -143,3 +143,18 @@
|
|
|
143
143
|
(`data/parg/iso.parg`) must move together — the twin is the embedded
|
|
144
144
|
source's provenance copy.
|
|
145
145
|
|
|
146
|
+
- **`typed_stage.original_abbr` is spelling, so `==` skips it (issue #656).**
|
|
147
|
+
The parse path stores the input token (`Amd.`, `COR`, `DAmd`, `pDCOR`) and
|
|
148
|
+
`stage_from_kv` stores the canonical one (`Amd`, `Cor`, `DAM`); the value is
|
|
149
|
+
not serialized. Before the fix, 90 pass ids (supplements, Directives,
|
|
150
|
+
Guides, `ISO/NWIP`) were not `==` after `from_hash(to_hash)`, so
|
|
151
|
+
`#matches?` against an index row returned false with no error. ISO cannot
|
|
152
|
+
delete the write as JCGM and IEC did, because `detect_stage_format_long`
|
|
153
|
+
reads it. `Components::TypedStage` now declares
|
|
154
|
+
`equality_ignored :original_abbr`. Since `to_s` reads the value, two `==`
|
|
155
|
+
ids may render differently, which the #656 rule allows. Locked by
|
|
156
|
+
`spec/pubid/iso/typed_stage_round_trip_spec.rb`.
|
|
157
|
+
**Still open, and not covered by that spec**: 14 pass ids stay unequal for
|
|
158
|
+
another reason — `DirectivesSupplement#supplement_publisher` is an
|
|
159
|
+
`Iso::Components::Publisher` after `parse` and a `Pubid::Components::Publisher`
|
|
160
|
+
after `from_hash` (11 supplements, 3 bundles).
|
data/lib/pubid/iso/identifier.rb
CHANGED
data/lib/pubid/iso/urn_parser.rb
CHANGED
|
@@ -64,8 +64,12 @@ module Pubid
|
|
|
64
64
|
# Parse ISO URN string
|
|
65
65
|
# @param urn [String] URN string to parse
|
|
66
66
|
# @return [Identifier] parsed identifier
|
|
67
|
+
# @raise [Pubid::Errors::InvalidInputError] for a non-String
|
|
68
|
+
# @raise [Pubid::Errors::UrnParseError] for a URN that does not parse
|
|
67
69
|
def self.parse(urn)
|
|
68
|
-
|
|
70
|
+
::Pubid::UrnParser::Base.guarded_parse(urn) do |utf8|
|
|
71
|
+
new.parse_urn(utf8)
|
|
72
|
+
end
|
|
69
73
|
end
|
|
70
74
|
|
|
71
75
|
# Parse URN string into identifier
|
|
@@ -74,11 +78,17 @@ module Pubid
|
|
|
74
78
|
def parse_urn(urn)
|
|
75
79
|
# Remove urn:iso:std: prefix
|
|
76
80
|
unless urn.start_with?("urn:iso:std:")
|
|
77
|
-
raise
|
|
81
|
+
raise ::Pubid::Errors::UrnParseError,
|
|
78
82
|
"Invalid ISO URN: #{urn}"
|
|
79
83
|
end
|
|
80
84
|
|
|
81
85
|
parts = urn.sub("urn:iso:std:", "").split(":")
|
|
86
|
+
# "urn:iso:std:" has no originator; reading it as a publisher
|
|
87
|
+
# reached `split` on nil (issue #670).
|
|
88
|
+
if parts.empty?
|
|
89
|
+
raise ::Pubid::Errors::UrnParseError,
|
|
90
|
+
"missing originator: #{urn}"
|
|
91
|
+
end
|
|
82
92
|
|
|
83
93
|
# Series suffix: a trailing "ser" marks an all-parts identifier. Strip
|
|
84
94
|
# it before component parsing so it is not misread as a language or
|
data/lib/pubid/iso.rb
CHANGED
data/lib/pubid/jcgm.rb
CHANGED
data/lib/pubid/jis/identifier.rb
CHANGED
data/lib/pubid/nist.rb
CHANGED
|
@@ -78,6 +78,8 @@ module Pubid
|
|
|
78
78
|
raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
|
|
79
79
|
end
|
|
80
80
|
|
|
81
|
+
identifier = ::Pubid.utf8_input(identifier)
|
|
82
|
+
|
|
81
83
|
# Use the Parser class's preprocessing method
|
|
82
84
|
# Note: We call the class method directly to ensure preprocessing is applied
|
|
83
85
|
parsed = Parser.class_parse_with_preprocessing(identifier)
|
data/lib/pubid/oasis.rb
CHANGED
data/lib/pubid/ogc/identifier.rb
CHANGED
data/lib/pubid/oiml.rb
CHANGED
data/lib/pubid/omg/identifier.rb
CHANGED
data/lib/pubid/plateau.rb
CHANGED
|
@@ -29,6 +29,8 @@ module Pubid
|
|
|
29
29
|
raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
+
input = ::Pubid.utf8_input(input)
|
|
33
|
+
|
|
32
34
|
# Apply legacy update_codes normalization first
|
|
33
35
|
normalized = Core::UpdateCodes.apply(input, :plateau)
|
|
34
36
|
parsed = Pubid::Parg::Model.bound(:plateau, normalized)
|
data/lib/pubid/subset_match.rb
CHANGED
|
@@ -169,9 +169,7 @@ module Pubid
|
|
|
169
169
|
# Exactness composes rather than flattening: a nested value that
|
|
170
170
|
# includes this module is matched in BOTH directions with its own
|
|
171
171
|
# `===`, so the class that owns it keeps its rule. A plain `==` would
|
|
172
|
-
# override it —
|
|
173
|
-
# (the input spelling, `Amd` against `AMD`), and CEN/CENELEC declares
|
|
174
|
-
# `typed_stage` strict.
|
|
172
|
+
# override it — CEN/CENELEC declares `typed_stage` strict.
|
|
175
173
|
# @return [Boolean]
|
|
176
174
|
def self.exact_match?(mine, theirs)
|
|
177
175
|
return true if blank?(mine) && blank?(theirs)
|
data/lib/pubid/tgpp.rb
CHANGED
data/lib/pubid/un/identifier.rb
CHANGED
|
@@ -16,10 +16,50 @@ module Pubid
|
|
|
16
16
|
# {number}:{year}` shape.
|
|
17
17
|
class Base
|
|
18
18
|
# Parse a URN string into an Identifier.
|
|
19
|
+
#
|
|
19
20
|
# @param urn [String]
|
|
20
21
|
# @return [Pubid::Identifier]
|
|
22
|
+
# @raise [Pubid::Errors::InvalidInputError] for a non-String
|
|
23
|
+
# @raise [Pubid::Errors::UrnParseError] for a URN that does not parse
|
|
21
24
|
def self.parse(urn)
|
|
22
|
-
new.parse_urn(
|
|
25
|
+
guarded_parse(urn) { |utf8| new.parse_urn(utf8) }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Run a URN decode under the pubid error contract (issue #670). The
|
|
29
|
+
# input checks run first, before a parser reaches +downcase+ or a
|
|
30
|
+
# regex on the string. A decode then fails as a pubid error only:
|
|
31
|
+
# the raw classes a malformed body produced (+NoMethodError+ on a
|
|
32
|
+
# missing segment, +IndexError+ from +fetch+, +ArgumentError+) are
|
|
33
|
+
# wrapped as {Pubid::Errors::UrnParseError}, with the raw error kept
|
|
34
|
+
# as the cause so a parser defect stays visible. This is the same
|
|
35
|
+
# wrap +Pubid.parse_urn+ applies; here it also covers a direct call.
|
|
36
|
+
# ISO and IEC have their own parser classes and call this too.
|
|
37
|
+
#
|
|
38
|
+
# @param urn [Object]
|
|
39
|
+
# @yieldparam utf8 [String] the URN as a valid UTF-8 string
|
|
40
|
+
# @return [Pubid::Identifier] the block's result
|
|
41
|
+
def self.guarded_parse(urn)
|
|
42
|
+
yield validate_input!(urn)
|
|
43
|
+
rescue ::Pubid::Errors::Error
|
|
44
|
+
raise
|
|
45
|
+
rescue StandardError => e
|
|
46
|
+
raise ::Pubid::Errors::UrnParseError,
|
|
47
|
+
"#{name} cannot parse URN #{urn.inspect}: #{e.message}"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# The input checks every URN parser runs first.
|
|
51
|
+
# @param urn [Object]
|
|
52
|
+
# @return [String] the URN as a valid UTF-8 string (see
|
|
53
|
+
# {Pubid.utf8_input})
|
|
54
|
+
# @raise [Pubid::Errors::InvalidInputError] for a non-String
|
|
55
|
+
# @raise [Pubid::Errors::UrnParseError] for invalid encoding bytes
|
|
56
|
+
def self.validate_input!(urn)
|
|
57
|
+
unless urn.is_a?(String)
|
|
58
|
+
raise ::Pubid::Errors::InvalidInputError,
|
|
59
|
+
::Pubid::INPUT_NOT_A_STRING_MESSAGE
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
::Pubid.utf8_input(urn, ::Pubid::Errors::UrnParseError)
|
|
23
63
|
end
|
|
24
64
|
|
|
25
65
|
# Subclasses override.
|
data/lib/pubid/version.rb
CHANGED
data/lib/pubid/w3c/identifier.rb
CHANGED
data/lib/pubid/xsf/identifier.rb
CHANGED
data/lib/pubid/xsf.rb
CHANGED
data/lib/pubid.rb
CHANGED
|
@@ -39,6 +39,57 @@ module Pubid
|
|
|
39
39
|
# point rejects a non-String before it measures the length.
|
|
40
40
|
INPUT_NOT_A_STRING_MESSAGE = "identifier must be a String"
|
|
41
41
|
|
|
42
|
+
# Raised when an input string cannot be read as UTF-8.
|
|
43
|
+
INPUT_INVALID_ENCODING_MESSAGE = "input string has invalid encoding"
|
|
44
|
+
|
|
45
|
+
# Encodings whose strings are read as raw bytes: their bytes are taken as
|
|
46
|
+
# UTF-8 without a conversion.
|
|
47
|
+
BYTE_ENCODINGS = [Encoding::UTF_8, Encoding::BINARY,
|
|
48
|
+
Encoding::US_ASCII].freeze
|
|
49
|
+
|
|
50
|
+
# Return +string+ as a valid UTF-8 string, or raise a pubid error
|
|
51
|
+
# (issue #670). Invalid encoding is bad input, not a grammar failure:
|
|
52
|
+
# without this step the detectors, the normalization regexes and the
|
|
53
|
+
# parser raise raw `ArgumentError` (invalid byte sequence), `EncodingError`
|
|
54
|
+
# or `Encoding::CompatibilityError`, which are outside the {Errors::Error}
|
|
55
|
+
# marker.
|
|
56
|
+
#
|
|
57
|
+
# A UTF-8, binary or US-ASCII string is read as UTF-8 bytes, so ".b" of
|
|
58
|
+
# ASCII text passes and a stray high byte does not. A string labelled with
|
|
59
|
+
# another encoding (ISO-8859-1, Windows-1252, UTF-16LE …) is converted, so
|
|
60
|
+
# a valid "café" in Latin-1 parses as it does in UTF-8. A valid UTF-8
|
|
61
|
+
# string comes back as the same object.
|
|
62
|
+
#
|
|
63
|
+
# Every public +parse+ entry point assigns the result after its inline
|
|
64
|
+
# length guard. The length guard stays inline because it is the CodeQL
|
|
65
|
+
# barrier; this step is not one, so it can be a shared method.
|
|
66
|
+
#
|
|
67
|
+
# @param string [String]
|
|
68
|
+
# @param error_class [Class] the pubid error to raise
|
|
69
|
+
# @return [String] a valid UTF-8 string
|
|
70
|
+
# @raise [Errors::InvalidInputError] (or +error_class+) when the string
|
|
71
|
+
# cannot be read as UTF-8
|
|
72
|
+
def self.utf8_input(string, error_class = Errors::InvalidInputError)
|
|
73
|
+
utf8 = if BYTE_ENCODINGS.include?(string.encoding)
|
|
74
|
+
utf8_bytes(string)
|
|
75
|
+
else
|
|
76
|
+
string.encode(Encoding::UTF_8)
|
|
77
|
+
end
|
|
78
|
+
return utf8 if utf8.valid_encoding?
|
|
79
|
+
|
|
80
|
+
raise error_class, INPUT_INVALID_ENCODING_MESSAGE
|
|
81
|
+
rescue EncodingError
|
|
82
|
+
raise error_class, INPUT_INVALID_ENCODING_MESSAGE
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# The bytes of +string+ labelled UTF-8 (the same object when they are).
|
|
86
|
+
def self.utf8_bytes(string)
|
|
87
|
+
return string if string.encoding == Encoding::UTF_8
|
|
88
|
+
|
|
89
|
+
string.dup.force_encoding(Encoding::UTF_8)
|
|
90
|
+
end
|
|
91
|
+
private_class_method :utf8_bytes
|
|
92
|
+
|
|
42
93
|
# Registry for tracking all loaded flavors
|
|
43
94
|
class Registry
|
|
44
95
|
@flavors = {}
|
|
@@ -252,15 +303,7 @@ module Pubid
|
|
|
252
303
|
raise Pubid::Errors::InvalidInputError,
|
|
253
304
|
Pubid::INPUT_TOO_LONG_MESSAGE
|
|
254
305
|
end
|
|
255
|
-
|
|
256
|
-
# and normalization regexes raise raw `ArgumentError` (invalid byte
|
|
257
|
-
# sequence) or `Encoding::CompatibilityError` on it, which is the leak
|
|
258
|
-
# the contract closes (issue #670). The bytes are read as UTF-8 — a
|
|
259
|
-
# binary string of ASCII bytes (".b") is fine, high bytes are not.
|
|
260
|
-
unless string.dup.force_encoding(Encoding::UTF_8).valid_encoding?
|
|
261
|
-
raise Pubid::Errors::InvalidInputError,
|
|
262
|
-
"input string has invalid encoding"
|
|
263
|
-
end
|
|
306
|
+
string = utf8_input(string)
|
|
264
307
|
|
|
265
308
|
format = FormatDetector.detect(string) if format == :auto
|
|
266
309
|
|
|
@@ -308,6 +351,7 @@ module Pubid
|
|
|
308
351
|
raise Pubid::Errors::InvalidInputError,
|
|
309
352
|
Pubid::INPUT_NOT_A_STRING_MESSAGE
|
|
310
353
|
end
|
|
354
|
+
string = utf8_input(string, Errors::UrnParseError)
|
|
311
355
|
eager_load_flavors!
|
|
312
356
|
return PubidUrn.parse(string) if PubidUrn.pubid_urn?(string)
|
|
313
357
|
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pubid
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.0.
|
|
4
|
+
version: 2.0.19
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-11-
|
|
11
|
+
date: 2026-11-10 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: lutaml-model
|
|
@@ -1104,10 +1104,12 @@ files:
|
|
|
1104
1104
|
- schema/iec.yaml
|
|
1105
1105
|
- schema/iso.yaml
|
|
1106
1106
|
- schema/schema.schema.yaml
|
|
1107
|
-
homepage: https://github.com/
|
|
1107
|
+
homepage: https://github.com/pubid/pubid
|
|
1108
1108
|
licenses:
|
|
1109
1109
|
- BSD-2-Clause
|
|
1110
1110
|
metadata:
|
|
1111
|
+
changelog_uri: https://github.com/pubid/pubid/releases
|
|
1112
|
+
documentation_uri: https://rubydoc.info/gems/pubid
|
|
1111
1113
|
rubygems_mfa_required: 'true'
|
|
1112
1114
|
post_install_message:
|
|
1113
1115
|
rdoc_options: []
|