pubid 2.0.0.pre.alpha.29 → 2.0.0.pre.alpha.31
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/data/parg/cen_cenelec.json +1 -1
- data/data/parg/cen_cenelec.parg +12 -4
- data/data/parg/evs.json +1 -1
- data/data/parg/ieee.json +1 -1
- data/lib/pubid/cen_cenelec/builder.rb +10 -0
- data/lib/pubid/cen_cenelec/identifier.rb +2 -0
- data/lib/pubid/cen_cenelec/renderer.rb +4 -1
- data/lib/pubid/evs/builder.rb +77 -9
- data/lib/pubid/evs/identifier.rb +29 -7
- data/lib/pubid/evs/identifiers/estonian_standard.rb +15 -0
- data/lib/pubid/evs/identifiers/international_adoption.rb +16 -0
- data/lib/pubid/evs/identifiers.rb +2 -0
- data/lib/pubid/evs/renderer.rb +43 -10
- data/lib/pubid/evs/urn_generator.rb +46 -6
- data/lib/pubid/evs/urn_parser.rb +94 -37
- data/lib/pubid/evs.rb +13 -2
- data/lib/pubid/ieee/builder.rb +115 -13
- data/lib/pubid/ieee/identifiers/base.rb +7 -1
- data/lib/pubid/ieee/parser.rb +37 -0
- data/lib/pubid/ieee/renderer.rb +8 -3
- data/lib/pubid/version.rb +1 -1
- metadata +4 -2
data/lib/pubid/ieee/builder.rb
CHANGED
|
@@ -5,6 +5,17 @@ module Pubid
|
|
|
5
5
|
# Builder class for constructing IEEE identifier scheme from parsed data
|
|
6
6
|
# Single Responsibility: Transform parsed data into identifier objects
|
|
7
7
|
class Builder
|
|
8
|
+
# The month_name vocabulary the REV date embed canonicalizes numeric
|
|
9
|
+
# (the parg rule's exact spellings, dots already stripped).
|
|
10
|
+
REV_MONTH_NUMBERS = {
|
|
11
|
+
"January" => "01", "February" => "02", "March" => "03",
|
|
12
|
+
"April" => "04", "May" => "05", "June" => "06", "July" => "07",
|
|
13
|
+
"August" => "08", "September" => "09", "Sept" => "09",
|
|
14
|
+
"October" => "10", "November" => "11", "December" => "12",
|
|
15
|
+
"Jan" => "01", "Feb" => "02", "Mar" => "03", "Apr" => "04",
|
|
16
|
+
"Jun" => "06", "Jul" => "07", "Aug" => "08", "Sep" => "09",
|
|
17
|
+
"Oct" => "10", "Nov" => "11", "Dec" => "12",
|
|
18
|
+
}.freeze
|
|
8
19
|
attr_accessor :original_input
|
|
9
20
|
attr_reader :identifier_class
|
|
10
21
|
|
|
@@ -287,7 +298,29 @@ module Pubid
|
|
|
287
298
|
end
|
|
288
299
|
dash = parsed_hash[:dash_year] ? parsed_hash : nil
|
|
289
300
|
dash ||= drafts.find { |d| d.is_a?(Hash) && d[:dash_year] }
|
|
290
|
-
if dash && parsed_hash[:
|
|
301
|
+
if dash.nil? && parsed_hash[:draft]
|
|
302
|
+
# A numeric month trailing a draft is the draft's own date: it
|
|
303
|
+
# embeds in the designator ("D09, 04 2011" -> "D09-2011-04")
|
|
304
|
+
# rather than printing the long-format comma face (a month name
|
|
305
|
+
# keeps the comma, "D7.2, Nov 2006") — pubid ruling 2026-10-13.
|
|
306
|
+
m = parsed_hash[:month] || parsed_hash[:trailing_month]
|
|
307
|
+
y = parsed_hash[:year] || parsed_hash[:trailing_year]
|
|
308
|
+
if m.to_s.match?(/\A\d{1,2}\z/) && y
|
|
309
|
+
host = drafts.find { |d| d.is_a?(Hash) && d[:draft_version] }
|
|
310
|
+
if host
|
|
311
|
+
host[:draft_version] = [host[:draft_version], y, m].compact.join("-")
|
|
312
|
+
parsed_hash.delete(:month)
|
|
313
|
+
parsed_hash.delete(:year)
|
|
314
|
+
parsed_hash.delete(:trailing_month)
|
|
315
|
+
parsed_hash.delete(:trailing_year)
|
|
316
|
+
end
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
# The draft's own dash date embeds onto the draft even when a
|
|
320
|
+
# base identity year is present ("…-2011/D4-2015-07": the 2011
|
|
321
|
+
# is the base's year, the 2015-07 the draft's); only the
|
|
322
|
+
# root-year assignment waits for an unoccupied year slot.
|
|
323
|
+
if dash
|
|
291
324
|
joined = drafts.filter_map { |d| d.is_a?(Hash) ? d[:draft_version] : nil }
|
|
292
325
|
.map { |v| v.is_a?(Hash) ? v[:value] : v.to_s }.join
|
|
293
326
|
bare_joint_draft = parsed_hash[:joint_publishers] &&
|
|
@@ -297,7 +330,16 @@ module Pubid
|
|
|
297
330
|
dash_led = joined.start_with?("-")
|
|
298
331
|
if bare_joint_draft && dash_led
|
|
299
332
|
parsed_hash[:part] = { value: dash[:dash_year] }
|
|
300
|
-
elsif dash[:dash_month]
|
|
333
|
+
elsif dash[:dash_month] &&
|
|
334
|
+
(host = drafts.find { |d| d.is_a?(Hash) && d[:draft_version] })
|
|
335
|
+
# The draft's own numeric date embeds in the designator
|
|
336
|
+
# ("D2-2015-04") — the "D2, 04 2015" comma spelling
|
|
337
|
+
# normalizes onto it (pubid ruling 2026-10-13).
|
|
338
|
+
host[:draft_version] =
|
|
339
|
+
[joined, dash[:dash_year], dash[:dash_month]].compact.join("-")
|
|
340
|
+
dash.delete(:dash_year)
|
|
341
|
+
dash.delete(:dash_month)
|
|
342
|
+
elsif (dash[:dash_month] || dash_led) && parsed_hash[:year].nil?
|
|
301
343
|
parsed_hash[:year] = dash[:dash_year]
|
|
302
344
|
parsed_hash[:month] = dash[:dash_month]
|
|
303
345
|
else
|
|
@@ -307,9 +349,16 @@ module Pubid
|
|
|
307
349
|
# draft version misses the D-registry), no separate year.
|
|
308
350
|
host = drafts.find { |d| d.is_a?(Hash) && d[:draft_version] }
|
|
309
351
|
if host
|
|
310
|
-
|
|
352
|
+
# The draft's own numeric date embeds in the designator
|
|
353
|
+
# ("D9-2006", and with its month "D2-2015-04") — the
|
|
354
|
+
# "D2, 04 2015" comma spelling normalizes onto it.
|
|
355
|
+
host[:draft_version] =
|
|
356
|
+
[joined, dash[:dash_year], dash[:dash_month]].compact.join("-")
|
|
311
357
|
dash.delete(:dash_year)
|
|
312
358
|
dash.delete(:dash_month)
|
|
359
|
+
# the hyphenated "/D-2-2007" spelling's year the spine
|
|
360
|
+
# captured at top level is the draft's own — vacate it
|
|
361
|
+
parsed_hash.delete(:year) if parsed_hash[:year] == dash[:dash_year]
|
|
313
362
|
else
|
|
314
363
|
parsed_hash[:year] = dash[:dash_year]
|
|
315
364
|
parsed_hash[:month] = dash[:dash_month]
|
|
@@ -910,12 +959,23 @@ module Pubid
|
|
|
910
959
|
# exempt: their canonical is the ISO face, which prints the
|
|
911
960
|
# date from the identity slots (", March 2017", pubid#216).
|
|
912
961
|
if parsed[:draft_month] && parsed[:iso_stage].nil?
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
962
|
+
dm = extract_value(parsed[:draft_month]).to_s
|
|
963
|
+
dy = extract_value(parsed[:draft_year]).to_s
|
|
964
|
+
num = REV_MONTH_NUMBERS[dm.delete(".")]
|
|
965
|
+
if attributes[:publishers] == %w[IEEE CSA] && num && !dy.empty?
|
|
966
|
+
# The joint IEEE/CSA drafts glue their date into the
|
|
967
|
+
# designator with the month numeric (pubid ruling
|
|
968
|
+
# 2026-10-14: no comma dates on these rows).
|
|
969
|
+
draft = Components::Draft.new(version: "#{draft_ver}-#{dy}-#{num}")
|
|
970
|
+
attributes[:draft] = draft.to_s.sub(%r{\A/}, "")
|
|
971
|
+
else
|
|
972
|
+
draft = Components::Draft.new(
|
|
973
|
+
version: draft_ver,
|
|
974
|
+
month: dm,
|
|
975
|
+
year: dy)
|
|
976
|
+
draft.comma_before_month = true
|
|
977
|
+
attributes[:draft] = draft.to_s.sub(%r{\A/}, "")
|
|
978
|
+
end
|
|
919
979
|
attributes.delete(:year) if parsed[:year].nil?
|
|
920
980
|
attributes.delete(:month) if parsed[:month].nil?
|
|
921
981
|
elsif parsed[:draft_year] && parsed[:iso_stage].nil?
|
|
@@ -926,6 +986,19 @@ module Pubid
|
|
|
926
986
|
draft_year = extract_value(parsed[:draft_year]).to_s.sub(/\A[, ]+/, "")
|
|
927
987
|
attributes[:draft] = "D#{draft_ver}, #{draft_year}"
|
|
928
988
|
attributes.delete(:year) if parsed[:year].nil?
|
|
989
|
+
elsif attributes[:publishers] == %w[IEEE CSA] &&
|
|
990
|
+
parsed[:iso_stage].nil? &&
|
|
991
|
+
extract_value(parsed[:year]).to_s.match?(/\A(19|20)\d\d\z/) &&
|
|
992
|
+
extract_value(parsed[:month]).to_s.match?(/\A\d{1,2}\z/)
|
|
993
|
+
# The glued face re-parses with the date in the identity
|
|
994
|
+
# slots ("D2-2016-09" -> year/month + a bare D2): it
|
|
995
|
+
# normalizes back onto the draft-embedded date so the
|
|
996
|
+
# round trip closes on the CSA canonical.
|
|
997
|
+
draft = Components::Draft.new(
|
|
998
|
+
version: "#{draft_ver}-#{extract_value(parsed[:year])}-#{extract_value(parsed[:month])}")
|
|
999
|
+
attributes[:draft] = draft.to_s.sub(%r{\A/}, "")
|
|
1000
|
+
attributes.delete(:year)
|
|
1001
|
+
attributes.delete(:month)
|
|
929
1002
|
else
|
|
930
1003
|
attributes[:ieee_draft] = "D#{draft_ver}"
|
|
931
1004
|
end
|
|
@@ -1548,6 +1621,8 @@ module Pubid
|
|
|
1548
1621
|
# Optional attributes (excluding part/subpart since they're in code)
|
|
1549
1622
|
extract_optional(parsed, attributes, :edition)
|
|
1550
1623
|
extract_optional(parsed, attributes, :revision)
|
|
1624
|
+
extract_optional(parsed, attributes, :rev_sep)
|
|
1625
|
+
attributes[:rev_bare] = true if parsed[:rev_bare]
|
|
1551
1626
|
|
|
1552
1627
|
# Month/day - always extract if present (but not if already extracted from year)
|
|
1553
1628
|
unless attributes[:edition_month]
|
|
@@ -1663,8 +1738,9 @@ module Pubid
|
|
|
1663
1738
|
|
|
1664
1739
|
if version
|
|
1665
1740
|
# relaton's hyphenated "/D-2" spelling captures the dash inside
|
|
1666
|
-
# draft_version; the stage registry keys on "D2".
|
|
1667
|
-
|
|
1741
|
+
# draft_version; the stage registry keys on "D2". A date-embedded
|
|
1742
|
+
# version ("3-2017-07") keys on its leading ordinal.
|
|
1743
|
+
draft_abbr = "D#{version.to_s.sub(/\A-/, "").sub(/-\d{4}(-\d{2})?\z/, "")}"
|
|
1668
1744
|
# Check if this specific draft stage is in registry
|
|
1669
1745
|
stage = Pubid::Ieee.locate_stage(draft_abbr)
|
|
1670
1746
|
return draft_abbr if stage&.abbr&.include?(draft_abbr)
|
|
@@ -1802,8 +1878,25 @@ module Pubid
|
|
|
1802
1878
|
month_slice = draft_data[:month]
|
|
1803
1879
|
month = extract_value(month_slice) if month_slice
|
|
1804
1880
|
year = extract_value(draft_data[:year]) if draft_data[:year]
|
|
1881
|
+
# The apostrophe-year catalogue convention ("P1484.20.1/D7,
|
|
1882
|
+
# May 07"): the 2-digit year expands with the <30 pivot so the
|
|
1883
|
+
# hash carries an unambiguous year.
|
|
1884
|
+
year = "#{year.to_i < 30 ? 20 : 19}#{year}" if year.to_s.match?(/\A\d{2}\z/)
|
|
1805
1885
|
day = extract_value(draft_data[:day]) if draft_data[:day]
|
|
1806
1886
|
|
|
1887
|
+
# A REV-carrying draft embeds its print date into the designator
|
|
1888
|
+
# with the month canonicalized numeric (pubid ruling 2026-10-14):
|
|
1889
|
+
# "P802.11-REVmc/D8.0, Aug 2016" renders ".../D8.0-2016-08".
|
|
1890
|
+
# Drafts without a REV keep the comma face.
|
|
1891
|
+
if (attributes[:revision] || attributes[:rev_bare] ||
|
|
1892
|
+
attributes[:cor_number] || attributes[:cor_year]) && month && year
|
|
1893
|
+
number = REV_MONTH_NUMBERS[month.to_s.delete(".")]
|
|
1894
|
+
if number && version
|
|
1895
|
+
version = "#{version}-#{year}-#{number}"
|
|
1896
|
+
month = year = day = nil
|
|
1897
|
+
end
|
|
1898
|
+
end
|
|
1899
|
+
|
|
1807
1900
|
# Detect comma before month and space before draft by checking the original input
|
|
1808
1901
|
if original_input
|
|
1809
1902
|
if month
|
|
@@ -1931,8 +2024,17 @@ module Pubid
|
|
|
1931
2024
|
end
|
|
1932
2025
|
|
|
1933
2026
|
if param_data[:revision_of]
|
|
1934
|
-
|
|
1935
|
-
|
|
2027
|
+
target = extract_value(param_data[:revision_of]).to_s.strip
|
|
2028
|
+
begin
|
|
2029
|
+
# The model types revision_of as an Identifier (the printed
|
|
2030
|
+
# target parses as one); feeding the raw string crashes
|
|
2031
|
+
# to_hash with Lutaml::Model::IncorrectModelError.
|
|
2032
|
+
attributes[:revision_of] = Identifier.parse(target)
|
|
2033
|
+
rescue Pubid::Errors::ParseError, StandardError
|
|
2034
|
+
# An unparseable printed target stays on the parenthetical
|
|
2035
|
+
# surface instead of poisoning the hash.
|
|
2036
|
+
attributes[:parenthetical_content] ||= target
|
|
2037
|
+
end
|
|
1936
2038
|
end
|
|
1937
2039
|
if param_data[:amendment_to]
|
|
1938
2040
|
attributes[:amendment_to] =
|
|
@@ -36,6 +36,12 @@ module Pubid
|
|
|
36
36
|
# "Rev<id>". `::Pubid::Identifier` has no `revision` attribute, so this is
|
|
37
37
|
# free of the number/stage multi-flavor collision landmine.
|
|
38
38
|
attribute :revision, :string
|
|
39
|
+
# The lettered revision's printed separator ("P802.11-REVmc" keeps its
|
|
40
|
+
# dash; a glued "REVi" stays glued) and the bare-word marker (no id
|
|
41
|
+
# after "Rev" — "802.1AR-Rev/D2.2" keeps "Rev"). pubid ruling
|
|
42
|
+
# 2026-10-14.
|
|
43
|
+
attribute :rev_sep, :string
|
|
44
|
+
attribute :rev_bare, :boolean
|
|
39
45
|
attribute :edition, :string # Edition 1.0
|
|
40
46
|
attribute :month, :string
|
|
41
47
|
# Provenance flag (pubid#517): the month arrived glued to the identity
|
|
@@ -216,7 +222,7 @@ module Pubid
|
|
|
216
222
|
# (renderer.rb's `"Edition #{edition} #{year}-#{edition_month}"`),
|
|
217
223
|
# which survived even though its sibling `year` is already stripped.
|
|
218
224
|
def self.all_parts_edition_keys
|
|
219
|
-
super + %i[revision reaffirmed edition_month]
|
|
225
|
+
super + %i[revision rev_sep rev_bare reaffirmed edition_month]
|
|
220
226
|
end
|
|
221
227
|
|
|
222
228
|
# Inverse of the to_hash compaction: expand a scalar `stage` back into the
|
data/lib/pubid/ieee/parser.rb
CHANGED
|
@@ -1273,6 +1273,43 @@ module Pubid
|
|
|
1273
1273
|
cleaned = cleaned.gsub("&amp;", "&") # Double-encoded ampersand
|
|
1274
1274
|
cleaned = cleaned.gsub("&", "&") # Single-encoded ampersand
|
|
1275
1275
|
|
|
1276
|
+
# The joint IEEE/CSA catalogue spells the type word before the
|
|
1277
|
+
# project number ("IEEE/CSA Draft P844.1/D2, Sept, 2016"): the P
|
|
1278
|
+
# already carries the project semantics (docs/IEEE-DRAFT-STAGES.md
|
|
1279
|
+
# §1.1), so the redundant word drops and the joint route parses.
|
|
1280
|
+
cleaned = cleaned.gsub(%r{IEEE/CSA Draft (P[A-Z0-9])}, 'IEEE/CSA \1')
|
|
1281
|
+
|
|
1282
|
+
# The corrigendum's own draft follows the ordinal after a comma
|
|
1283
|
+
# ("P11073-10418-2011/Cor 1, D4, July 2015"): re-spell the comma as
|
|
1284
|
+
# the draft separator so the cor route's draft slot reaches it.
|
|
1285
|
+
cleaned = cleaned.gsub(%r{/Cor(?:rigendum)? (\d+), (D[A-Z0-9])}, '/Cor \1/\2')
|
|
1286
|
+
|
|
1287
|
+
# The corrigendum's own draft rides before the ordinal in the
|
|
1288
|
+
# house order ("PC37.101-2006/D2, 2007/Cor. 1"): the catalogue
|
|
1289
|
+
# spellings "…/Cor 1, D4, July 2015" and "…/Cor 1-201x/D5,
|
|
1290
|
+
# April 2012" re-order into it, with the draft's date glued
|
|
1291
|
+
# -YYYY-MM (pubid ruling 2026-10-14: no comma dates).
|
|
1292
|
+
cleaned = cleaned.gsub(
|
|
1293
|
+
%r{/Cor(?:rigendum)? (\d+(?:-[0-9xX]{4})?)/(D[A-Z0-9][^,]*), ([A-Za-z]+) (\d{4})}
|
|
1294
|
+
) do
|
|
1295
|
+
month = ::Pubid::Ieee::Builder::REV_MONTH_NUMBERS[Regexp.last_match(3).capitalize]
|
|
1296
|
+
if month
|
|
1297
|
+
"/#{Regexp.last_match(2)}-#{Regexp.last_match(4)}-#{month}/Cor. #{Regexp.last_match(1)}"
|
|
1298
|
+
else
|
|
1299
|
+
Regexp.last_match(0)
|
|
1300
|
+
end
|
|
1301
|
+
end
|
|
1302
|
+
cleaned = cleaned.gsub(
|
|
1303
|
+
%r{/Cor(?:rigendum)? (\d+), (D[A-Z0-9][^,]*), ([A-Za-z]+) (\d{4})}
|
|
1304
|
+
) do
|
|
1305
|
+
month = ::Pubid::Ieee::Builder::REV_MONTH_NUMBERS[Regexp.last_match(3).capitalize]
|
|
1306
|
+
if month
|
|
1307
|
+
"/#{Regexp.last_match(2)}-#{Regexp.last_match(4)}-#{month}/Cor. #{Regexp.last_match(1)}"
|
|
1308
|
+
else
|
|
1309
|
+
Regexp.last_match(0)
|
|
1310
|
+
end
|
|
1311
|
+
end
|
|
1312
|
+
|
|
1276
1313
|
# NEW Phase 1: Fix number spacing issues (e.g., "C57.1 2.25" → "C57.12.25")
|
|
1277
1314
|
# This handles cases where a space appears in the middle of a number
|
|
1278
1315
|
cleaned = cleaned.gsub(/(\d+\.\d+)\s+(\d+\.)/, '\1\2')
|
data/lib/pubid/ieee/renderer.rb
CHANGED
|
@@ -195,14 +195,19 @@ module Pubid
|
|
|
195
195
|
glued_month = id.month.to_s.match?(/\A\d{1,2}\z/)
|
|
196
196
|
year_on_code = id.year && !id.edition &&
|
|
197
197
|
(!id.month || glued_month) &&
|
|
198
|
-
(!id.draft_obj || id.draft_obj&.year
|
|
198
|
+
(!id.draft_obj || id.draft_obj&.year ||
|
|
199
|
+
id.draft_obj&.version.to_s.match?(/-\d{4}-\d{2}\z/))
|
|
199
200
|
result += "-#{id.year}" if year_on_code
|
|
200
201
|
result += "-#{id.month}" if glued_month
|
|
201
202
|
|
|
202
203
|
# Append the numbered/lettered revision inline, in IEEE's native
|
|
203
204
|
# position — right after the code number and before the draft
|
|
204
|
-
# ("P802.16Rev2/D3"). Keeps a revision distinct from its base
|
|
205
|
-
|
|
205
|
+
# ("P802.16Rev2/D3"). Keeps a revision distinct from its base
|
|
206
|
+
# standard. The lettered id keeps its printed separator
|
|
207
|
+
# ("P802.11-REVmc" renders "P802.11-Revmc"); the bare word with
|
|
208
|
+
# no id still prints "Rev" (pubid ruling 2026-10-14).
|
|
209
|
+
result += "#{id.rev_sep}Rev#{id.revision}" if id.revision
|
|
210
|
+
result += "#{id.rev_sep}Rev" if id.rev_bare
|
|
206
211
|
|
|
207
212
|
# Append draft to code - with or without space based on original
|
|
208
213
|
# format. An exactly-IEC/IEEE co-published reference prints only the
|
data/lib/pubid/version.rb
CHANGED
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.0.pre.alpha.
|
|
4
|
+
version: 2.0.0.pre.alpha.31
|
|
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-10-
|
|
11
|
+
date: 2026-10-15 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: lutaml-model
|
|
@@ -484,6 +484,8 @@ files:
|
|
|
484
484
|
- lib/pubid/evs/builder.rb
|
|
485
485
|
- lib/pubid/evs/identifier.rb
|
|
486
486
|
- lib/pubid/evs/identifiers.rb
|
|
487
|
+
- lib/pubid/evs/identifiers/estonian_standard.rb
|
|
488
|
+
- lib/pubid/evs/identifiers/international_adoption.rb
|
|
487
489
|
- lib/pubid/evs/identifiers/national_adoption.rb
|
|
488
490
|
- lib/pubid/evs/parser.rb
|
|
489
491
|
- lib/pubid/evs/renderer.rb
|