pubid 2.0.17 → 2.0.18

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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/README.adoc +135 -0
  3. data/lib/pubid/adobe/identifier.rb +2 -0
  4. data/lib/pubid/amca/identifiers/base.rb +2 -0
  5. data/lib/pubid/ansi/identifier.rb +2 -0
  6. data/lib/pubid/api/identifier.rb +2 -0
  7. data/lib/pubid/ashrae/identifiers/base.rb +2 -0
  8. data/lib/pubid/ashrae.rb +2 -0
  9. data/lib/pubid/asme/identifier.rb +2 -0
  10. data/lib/pubid/astm/identifier.rb +2 -0
  11. data/lib/pubid/bipm/identifier.rb +2 -0
  12. data/lib/pubid/bipm.rb +2 -0
  13. data/lib/pubid/bsi/single_identifier.rb +2 -0
  14. data/lib/pubid/bsi.rb +2 -0
  15. data/lib/pubid/calconnect/identifier.rb +2 -0
  16. data/lib/pubid/calconnect.rb +2 -0
  17. data/lib/pubid/ccsds/identifier.rb +2 -0
  18. data/lib/pubid/cen_cenelec/identifier.rb +2 -0
  19. data/lib/pubid/cen_cenelec.rb +2 -0
  20. data/lib/pubid/cie/identifier.rb +2 -0
  21. data/lib/pubid/cie.rb +2 -0
  22. data/lib/pubid/csa/identifier.rb +2 -0
  23. data/lib/pubid/csa.rb +2 -0
  24. data/lib/pubid/doi/identifier.rb +2 -0
  25. data/lib/pubid/easc/identifier.rb +2 -0
  26. data/lib/pubid/ecma/identifier.rb +2 -0
  27. data/lib/pubid/etsi/identifiers/base.rb +2 -0
  28. data/lib/pubid/evs/identifier.rb +2 -0
  29. data/lib/pubid/evs.rb +2 -0
  30. data/lib/pubid/gb/identifier.rb +2 -0
  31. data/lib/pubid/gost/identifier.rb +2 -0
  32. data/lib/pubid/iala/identifier.rb +2 -0
  33. data/lib/pubid/iana/identifier.rb +2 -0
  34. data/lib/pubid/idf/identifier.rb +2 -0
  35. data/lib/pubid/idf.rb +2 -0
  36. data/lib/pubid/iec/identifier.rb +2 -0
  37. data/lib/pubid/iec/urn_parser.rb +5 -1
  38. data/lib/pubid/ieee/identifiers/base.rb +2 -0
  39. data/lib/pubid/ieee.rb +2 -0
  40. data/lib/pubid/ietf/identifiers/base.rb +2 -0
  41. data/lib/pubid/ietf.rb +2 -0
  42. data/lib/pubid/iho/identifiers/base.rb +2 -0
  43. data/lib/pubid/isbn/identifier.rb +3 -1
  44. data/lib/pubid/iso/identifier.rb +2 -0
  45. data/lib/pubid/iso/urn_parser.rb +12 -2
  46. data/lib/pubid/iso.rb +2 -0
  47. data/lib/pubid/itu/identifiers/base.rb +2 -0
  48. data/lib/pubid/jcgm.rb +2 -0
  49. data/lib/pubid/jis/identifier.rb +2 -0
  50. data/lib/pubid/nist.rb +2 -0
  51. data/lib/pubid/oasis/identifier.rb +2 -0
  52. data/lib/pubid/oasis.rb +2 -0
  53. data/lib/pubid/ogc/identifier.rb +2 -0
  54. data/lib/pubid/oiml.rb +2 -0
  55. data/lib/pubid/omg/identifier.rb +2 -0
  56. data/lib/pubid/plateau.rb +2 -0
  57. data/lib/pubid/sae/identifiers/base.rb +2 -0
  58. data/lib/pubid/tgpp/identifier.rb +2 -0
  59. data/lib/pubid/tgpp.rb +2 -0
  60. data/lib/pubid/un/identifier.rb +2 -0
  61. data/lib/pubid/urn_parser/base.rb +41 -1
  62. data/lib/pubid/version.rb +1 -1
  63. data/lib/pubid/w3c/identifier.rb +2 -0
  64. data/lib/pubid/xsf/identifier.rb +2 -0
  65. data/lib/pubid/xsf.rb +2 -0
  66. data/lib/pubid.rb +53 -9
  67. metadata +5 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0da3c4f8d7e991cfcee13325c148d4a58d3f9819521e24e96d6c9847dd0c49f1
4
- data.tar.gz: 54c6aa151ee3c7ea3769b8ce83f153767ee48b804e967a02b805c097e71f8b31
3
+ metadata.gz: 185124a260b71738ef7c333b0f9ceb23f867cd3886cece12810ed6420aff7a46
4
+ data.tar.gz: '081f7312e5c76b76bdd7fc8c61d4a7e5c70c1de4a70f2491c69227f1fa7b8f70'
5
5
  SHA512:
6
- metadata.gz: 80f63afedfd49021008da385957f9ea5c4dfb858e941467be476fb41027888a802c16c1c6542c86a0ef427cc507b9a419912bcb8529f3a2fe24dfd1939e0362a
7
- data.tar.gz: a47f5d38518d79b0443f1cb9903f5b82d6875031be8f6b579f4b6b5fb0da7dab2e68f25c3ec4ecc008554a45dcacebc17c89d69edc606dcf34ec866c3c774bd3
6
+ metadata.gz: 07d685cf60518b38c66d21e65186aa942df8a279536ddf1907edd4bb5fb0eb2b52eb9de627c44994aee7a520d51aeedf05de9f917992c194cfc829883eef587d
7
+ data.tar.gz: 9c153f3a31750860224084b31802eb65654af9948b9688b5493ca51cb946a18da5601710d3c74b18c7250d1edba33d855020bead796558c087a772ba16a99174
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)
@@ -17,6 +17,8 @@ module Pubid
17
17
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
18
18
  end
19
19
 
20
+ identifier = ::Pubid.utf8_input(identifier)
21
+
20
22
  parsed = Pubid::Parg::Model.bound(:amca, identifier)
21
23
  Builder.build(parsed)
22
24
  end
@@ -23,6 +23,8 @@ module Pubid
23
23
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
24
24
  end
25
25
 
26
+ string = ::Pubid.utf8_input(string)
27
+
26
28
  parsed = Pubid::Parg::Model.bound(:ansi, string)
27
29
  Pubid::Ansi::Builder.new.build(parsed)
28
30
  end
@@ -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:
@@ -19,6 +19,8 @@ module Pubid
19
19
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
20
20
  end
21
21
 
22
+ identifier = ::Pubid.utf8_input(identifier)
23
+
22
24
  parsed = Pubid::Parg::Model.bound(:ashrae, Parser.normalize_input(identifier))
23
25
  Builder.build(parsed)
24
26
  end
data/lib/pubid/ashrae.rb CHANGED
@@ -30,6 +30,8 @@ module Pubid
30
30
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
31
31
  end
32
32
 
33
+ identifier = ::Pubid.utf8_input(identifier)
34
+
33
35
  Identifier.parse(identifier)
34
36
  end
35
37
 
@@ -13,6 +13,8 @@ module Pubid
13
13
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
14
14
  end
15
15
 
16
+ str = ::Pubid.utf8_input(str)
17
+
16
18
  builder = Builder.new
17
19
 
18
20
  parsed = Pubid::Parg::Model.bound(:asme, str)
@@ -13,6 +13,8 @@ module Pubid
13
13
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
14
14
  end
15
15
 
16
+ str = ::Pubid.utf8_input(str)
17
+
16
18
  builder = Builder.new
17
19
 
18
20
  parsed = Pubid::Parg::Model.bound(:astm, str)
@@ -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
@@ -44,6 +44,8 @@ module Pubid
44
44
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
45
45
  end
46
46
 
47
+ identifier = ::Pubid.utf8_input(identifier)
48
+
47
49
  Identifier.parse(identifier)
48
50
  end
49
51
 
@@ -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
@@ -232,6 +232,8 @@ module Pubid
232
232
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
233
233
  end
234
234
 
235
+ string = ::Pubid.utf8_input(string)
236
+
235
237
  Identifier.parse(string)
236
238
  end
237
239
 
@@ -118,6 +118,8 @@ module Pubid
118
118
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
119
119
  end
120
120
 
121
+ identifier = ::Pubid.utf8_input(identifier)
122
+
121
123
  Pubid::Parg::Model.parse(:calconnect, Identifiers::Standard, identifier)
122
124
  end
123
125
  end
@@ -35,6 +35,8 @@ module Pubid
35
35
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
36
36
  end
37
37
 
38
+ identifier = ::Pubid.utf8_input(identifier)
39
+
38
40
  Identifier.parse(identifier)
39
41
  end
40
42
 
@@ -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)
@@ -166,6 +166,8 @@ module Pubid
166
166
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
167
167
  end
168
168
 
169
+ identifier = ::Pubid.utf8_input(identifier)
170
+
169
171
  CenCenelec::Identifier.parse(identifier)
170
172
  end
171
173
 
@@ -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
@@ -34,6 +34,8 @@ module Pubid
34
34
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
35
35
  end
36
36
 
37
+ input = ::Pubid.utf8_input(input)
38
+
37
39
  Identifier.parse(input)
38
40
  end
39
41
 
@@ -26,6 +26,8 @@ module Pubid
26
26
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
27
27
  end
28
28
 
29
+ input = ::Pubid.utf8_input(input)
30
+
29
31
  # Filter out comments
30
32
  if input.start_with?("#")
31
33
  raise Pubid::Errors::ParseError.new(
data/lib/pubid/csa.rb CHANGED
@@ -31,6 +31,8 @@ module Pubid
31
31
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
32
32
  end
33
33
 
34
+ identifier_string = ::Pubid.utf8_input(identifier_string)
35
+
34
36
  Identifier.parse(identifier_string)
35
37
  end
36
38
 
@@ -41,6 +41,8 @@ module Pubid
41
41
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
42
42
  end
43
43
 
44
+ identifier = ::Pubid.utf8_input(identifier)
45
+
44
46
  parsed = Pubid::Parg::Model.bound(:doi, identifier)
45
47
  Builder.build(parsed)
46
48
  end
@@ -37,6 +37,8 @@ module Pubid
37
37
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
38
38
  end
39
39
 
40
+ identifier = ::Pubid.utf8_input(identifier)
41
+
40
42
  parsed = Pubid::Parg::Model.bound(:easc, identifier)
41
43
  Builder.build(parsed)
42
44
  end
@@ -134,6 +134,8 @@ module Pubid
134
134
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
135
135
  end
136
136
 
137
+ identifier = ::Pubid.utf8_input(identifier)
138
+
137
139
  parsed = Pubid::Parg::Model.bound(:ecma, identifier)
138
140
  Builder.build(parsed)
139
141
  end
@@ -18,6 +18,8 @@ module Pubid
18
18
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
19
19
  end
20
20
 
21
+ identifier = ::Pubid.utf8_input(identifier)
22
+
21
23
  parsed = Pubid::Parg::Model.bound(:etsi, identifier)
22
24
  Builder.build(parsed)
23
25
  end
@@ -64,6 +64,8 @@ module Pubid
64
64
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
65
65
  end
66
66
 
67
+ string = ::Pubid.utf8_input(string)
68
+
67
69
  Pubid::Evs.parse(string)
68
70
  end
69
71
  end
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.
@@ -86,6 +86,8 @@ module Pubid
86
86
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
87
87
  end
88
88
 
89
+ identifier = ::Pubid.utf8_input(identifier)
90
+
89
91
  parsed = Pubid::Parg::Model.bound(:gb, identifier)
90
92
  Builder.build(parsed)
91
93
  end
@@ -23,6 +23,8 @@ module Pubid
23
23
  ::Pubid::INPUT_TOO_LONG_MESSAGE
24
24
  end
25
25
 
26
+ identifier = ::Pubid.utf8_input(identifier)
27
+
26
28
  parsed = Pubid::Parg::Model.bound(:gost, Parser.normalize_input(identifier))
27
29
  Builder.build(parsed)
28
30
  end
@@ -30,6 +30,8 @@ module Pubid
30
30
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
31
31
  end
32
32
 
33
+ identifier = ::Pubid.utf8_input(identifier)
34
+
33
35
  if FormatDetector.detect(identifier) == :urn
34
36
  UrnParser.parse(identifier)
35
37
  else
@@ -132,6 +132,8 @@ module Pubid
132
132
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
133
133
  end
134
134
 
135
+ identifier = ::Pubid.utf8_input(identifier)
136
+
135
137
  parsed = Pubid::Parg::Model.bound(:iana, identifier)
136
138
  Builder.build(parsed)
137
139
  end
@@ -83,6 +83,8 @@ module Pubid
83
83
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
84
84
  end
85
85
 
86
+ string = ::Pubid.utf8_input(string)
87
+
86
88
  parsed = Pubid::Parg::Model.bound(:idf, string)
87
89
  Pubid::Idf::Builder.new.build(parsed)
88
90
  end
data/lib/pubid/idf.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
+ identifier = ::Pubid.utf8_input(identifier)
33
+
32
34
  builder = Builder.new
33
35
 
34
36
  parsed = Pubid::Parg::Model.bound(:idf, identifier)
@@ -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)
@@ -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
- new.parse_urn(urn)
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
@@ -389,6 +389,8 @@ module Pubid
389
389
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
390
390
  end
391
391
 
392
+ input = ::Pubid.utf8_input(input)
393
+
392
394
  result = PreParser.preprocess(input)
393
395
 
394
396
  case result.dispatch
data/lib/pubid/ieee.rb CHANGED
@@ -55,6 +55,8 @@ module Pubid
55
55
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
56
56
  end
57
57
 
58
+ input = ::Pubid.utf8_input(input)
59
+
58
60
  Identifier.parse(input)
59
61
  end
60
62
 
@@ -64,6 +64,8 @@ module Pubid
64
64
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
65
65
  end
66
66
 
67
+ identifier = ::Pubid.utf8_input(identifier)
68
+
67
69
  parsed = Pubid::Parg::Model.bound(:ietf, strip_prefix(identifier))
68
70
  Builder.build(parsed)
69
71
  end
data/lib/pubid/ietf.rb CHANGED
@@ -41,6 +41,8 @@ module Pubid
41
41
  Pubid::INPUT_TOO_LONG_MESSAGE
42
42
  end
43
43
 
44
+ identifier = ::Pubid.utf8_input(identifier)
45
+
44
46
  Identifier.parse(identifier)
45
47
  end
46
48
 
@@ -32,6 +32,8 @@ module Pubid
32
32
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
33
33
  end
34
34
 
35
+ identifier = ::Pubid.utf8_input(identifier)
36
+
35
37
  parsed = Pubid::Parg::Model.bound(:iho, identifier.strip)
36
38
  Builder.build(parsed)
37
39
  end
@@ -53,7 +53,9 @@ module Pubid
53
53
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
54
54
  end
55
55
 
56
- # The two guards above must stay OUTSIDE build_identifier: this method
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
@@ -306,6 +306,8 @@ module Pubid
306
306
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
307
307
  end
308
308
 
309
+ string = ::Pubid.utf8_input(string)
310
+
309
311
  format = Pubid::FormatDetector.detect(string) if format == :auto
310
312
 
311
313
  case format
@@ -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
- new.parse_urn(urn)
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 ArgumentError,
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
@@ -42,6 +42,8 @@ module Pubid
42
42
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
43
43
  end
44
44
 
45
+ identifier = ::Pubid.utf8_input(identifier)
46
+
45
47
  format = Pubid::FormatDetector.detect(identifier) if format == :auto
46
48
 
47
49
  case format
@@ -19,6 +19,8 @@ module Pubid
19
19
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
20
20
  end
21
21
 
22
+ identifier = ::Pubid.utf8_input(identifier)
23
+
22
24
  parsed = Pubid::Parg::Model.bound(:itu, normalize_whitespace(identifier))
23
25
  Builder.build(parsed)
24
26
  end
data/lib/pubid/jcgm.rb CHANGED
@@ -33,6 +33,8 @@ module Pubid
33
33
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
34
34
  end
35
35
 
36
+ identifier = ::Pubid.utf8_input(identifier)
37
+
36
38
  builder = Builder.new
37
39
 
38
40
  parsed = Pubid::Parg::Model.bound(:jcgm, identifier)
@@ -184,6 +184,8 @@ module Pubid
184
184
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
185
185
  end
186
186
 
187
+ identifier = ::Pubid.utf8_input(identifier)
188
+
187
189
  parsed = Pubid::Parg::Model.bound(:jis, identifier)
188
190
  Builder.build(parsed)
189
191
  end
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)
@@ -146,6 +146,8 @@ module Pubid
146
146
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
147
147
  end
148
148
 
149
+ identifier = ::Pubid.utf8_input(identifier)
150
+
149
151
  parsed = Pubid::Parg::Model.bound(:oasis, identifier)
150
152
  Builder.build(parsed)
151
153
  end
data/lib/pubid/oasis.rb CHANGED
@@ -32,6 +32,8 @@ module Pubid
32
32
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
33
33
  end
34
34
 
35
+ identifier = ::Pubid.utf8_input(identifier)
36
+
35
37
  Identifier.parse(identifier)
36
38
  end
37
39
 
@@ -96,6 +96,8 @@ module Pubid
96
96
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
97
97
  end
98
98
 
99
+ identifier = ::Pubid.utf8_input(identifier)
100
+
99
101
  Builder.build(Pubid::Parg::Model.bound(:ogc, identifier))
100
102
  end
101
103
  end
data/lib/pubid/oiml.rb CHANGED
@@ -30,6 +30,8 @@ module Pubid
30
30
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
31
31
  end
32
32
 
33
+ identifier = ::Pubid.utf8_input(identifier)
34
+
33
35
  if identifier.include?("|")
34
36
  dual = Identifiers::DualPublished.build(identifier)
35
37
  return dual if dual
@@ -58,6 +58,8 @@ module Pubid
58
58
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
59
59
  end
60
60
 
61
+ identifier = ::Pubid.utf8_input(identifier)
62
+
61
63
  parsed = Pubid::Parg::Model.bound(:omg, identifier)
62
64
  Builder.build(parsed)
63
65
  end
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)
@@ -16,6 +16,8 @@ module Pubid
16
16
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
17
17
  end
18
18
 
19
+ input = ::Pubid.utf8_input(input)
20
+
19
21
  parsed = Pubid::Parg::Model.bound(:sae, input)
20
22
  Builder.build(parsed)
21
23
  end
@@ -113,6 +113,8 @@ module Pubid
113
113
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
114
114
  end
115
115
 
116
+ identifier = ::Pubid.utf8_input(identifier)
117
+
116
118
  parsed = Pubid::Parg::Model.bound(:tgpp, identifier)
117
119
  Builder.build(parsed)
118
120
  end
data/lib/pubid/tgpp.rb CHANGED
@@ -47,6 +47,8 @@ module Pubid
47
47
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
48
48
  end
49
49
 
50
+ identifier = ::Pubid.utf8_input(identifier)
51
+
50
52
  Identifier.parse(identifier)
51
53
  end
52
54
 
@@ -32,6 +32,8 @@ module Pubid
32
32
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
33
33
  end
34
34
 
35
+ identifier = ::Pubid.utf8_input(identifier)
36
+
35
37
  parsed = Pubid::Parg::Model.bound(:un, identifier)
36
38
  Builder.build(parsed)
37
39
  end
@@ -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(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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Pubid
4
- VERSION = "2.0.17"
4
+ VERSION = "2.0.18"
5
5
  end
@@ -122,6 +122,8 @@ module Pubid
122
122
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
123
123
  end
124
124
 
125
+ identifier = ::Pubid.utf8_input(identifier)
126
+
125
127
  parsed = Pubid::Parg::Model.bound(:w3c, identifier)
126
128
  Builder.build(parsed)
127
129
  end
@@ -62,6 +62,8 @@ module Pubid
62
62
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
63
63
  end
64
64
 
65
+ identifier = ::Pubid.utf8_input(identifier)
66
+
65
67
  parsed = Pubid::Parg::Model.bound(:xsf, identifier)
66
68
  Builder.build(parsed)
67
69
  end
data/lib/pubid/xsf.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
  Identifier.parse(identifier)
38
40
  end
39
41
 
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
- # Invalid encoding is bad input, not a grammar failure: the detectors
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.17
4
+ version: 2.0.18
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-08 00:00:00.000000000 Z
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/metanorma/pubid
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: []