pubid 2.0.0.pre.alpha.30 → 2.0.0.pre.alpha.32

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.
data/lib/pubid/errors.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "pubid"
3
+ # no `require "pubid"` here: errors.rb loads from pubid.rb itself, and
4
+ # the umbrella self-require is a circular load whose failure order is
5
+ # platform-dependent (windows raises mid-cycle where linux tolerates it)
4
6
  require "parslet"
5
7
 
6
8
  module Pubid
@@ -316,7 +316,11 @@ module Pubid
316
316
  end
317
317
  end
318
318
  end
319
- if dash && parsed_hash[:year].nil?
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
320
324
  joined = drafts.filter_map { |d| d.is_a?(Hash) ? d[:draft_version] : nil }
321
325
  .map { |v| v.is_a?(Hash) ? v[:value] : v.to_s }.join
322
326
  bare_joint_draft = parsed_hash[:joint_publishers] &&
@@ -335,7 +339,7 @@ module Pubid
335
339
  [joined, dash[:dash_year], dash[:dash_month]].compact.join("-")
336
340
  dash.delete(:dash_year)
337
341
  dash.delete(:dash_month)
338
- elsif dash[:dash_month] || dash_led
342
+ elsif (dash[:dash_month] || dash_led) && parsed_hash[:year].nil?
339
343
  parsed_hash[:year] = dash[:dash_year]
340
344
  parsed_hash[:month] = dash[:dash_month]
341
345
  else
@@ -955,12 +959,23 @@ module Pubid
955
959
  # exempt: their canonical is the ISO face, which prints the
956
960
  # date from the identity slots (", March 2017", pubid#216).
957
961
  if parsed[:draft_month] && parsed[:iso_stage].nil?
958
- draft = Components::Draft.new(
959
- version: draft_ver,
960
- month: extract_value(parsed[:draft_month]),
961
- year: extract_value(parsed[:draft_year]))
962
- draft.comma_before_month = true
963
- attributes[:draft] = draft.to_s.sub(%r{\A/}, "")
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
964
979
  attributes.delete(:year) if parsed[:year].nil?
965
980
  attributes.delete(:month) if parsed[:month].nil?
966
981
  elsif parsed[:draft_year] && parsed[:iso_stage].nil?
@@ -971,6 +986,19 @@ module Pubid
971
986
  draft_year = extract_value(parsed[:draft_year]).to_s.sub(/\A[, ]+/, "")
972
987
  attributes[:draft] = "D#{draft_ver}, #{draft_year}"
973
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)
974
1002
  else
975
1003
  attributes[:ieee_draft] = "D#{draft_ver}"
976
1004
  end
@@ -1850,13 +1878,18 @@ module Pubid
1850
1878
  month_slice = draft_data[:month]
1851
1879
  month = extract_value(month_slice) if month_slice
1852
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/)
1853
1885
  day = extract_value(draft_data[:day]) if draft_data[:day]
1854
1886
 
1855
1887
  # A REV-carrying draft embeds its print date into the designator
1856
1888
  # with the month canonicalized numeric (pubid ruling 2026-10-14):
1857
1889
  # "P802.11-REVmc/D8.0, Aug 2016" renders ".../D8.0-2016-08".
1858
1890
  # Drafts without a REV keep the comma face.
1859
- if (attributes[:revision] || attributes[:rev_bare]) && month && year
1891
+ if (attributes[:revision] || attributes[:rev_bare] ||
1892
+ attributes[:cor_number] || attributes[:cor_year]) && month && year
1860
1893
  number = REV_MONTH_NUMBERS[month.to_s.delete(".")]
1861
1894
  if number && version
1862
1895
  version = "#{version}-#{year}-#{number}"
@@ -1991,8 +2024,17 @@ module Pubid
1991
2024
  end
1992
2025
 
1993
2026
  if param_data[:revision_of]
1994
- attributes[:revision_of] =
1995
- extract_value(param_data[:revision_of])
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
1996
2038
  end
1997
2039
  if param_data[:amendment_to]
1998
2040
  attributes[:amendment_to] =
@@ -1273,6 +1273,63 @@ module Pubid
1273
1273
  cleaned = cleaned.gsub("&amp;amp;", "&") # Double-encoded ampersand
1274
1274
  cleaned = cleaned.gsub("&amp;", "&") # 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
+ # The same redundant word rides the wider joint lists
1282
+ # ("IEEE//ISO/IEC Draft P29119-2/D=DIS-201112"); the crawl also
1283
+ # doubles the publisher slash (the 2026-10-14 ruling: the list
1284
+ # normalizes to single slashes). Drop the word and collapse the
1285
+ # slash before the joint route sees either.
1286
+ # Scoped to the D= stage-draft tail: the doubled-D ("DDIS2") and
1287
+ # stage-month ("DFDIS_March") tails route elsewhere until the
1288
+ # grammar carries them - dropping the word there would route
1289
+ # them to the generic standard face with a slashed copublisher.
1290
+ cleaned = cleaned.gsub(%r{((?:IEEE|ISO|IEC)(?:/(?:IEEE|ISO|IEC|CSA))+)\s+Draft\s+(P[A-Z0-9][A-Z0-9.-]*/D=[A-Z])}, '\1 \2')
1291
+ cleaned = cleaned.gsub(%r{(IEEE|ISO|IEC)//}, '\1/')
1292
+ # The lettered REV spellings glue to the number ("P1310Rev3",
1293
+ # "P1722Rev1") or carry an after-word separator ("REV.ma",
1294
+ # "REV-ma"); the rev rules read a space or a bare word
1295
+ # respectively, so the boundary normalizes (the render keeps the
1296
+ # glued face - "IEEE P1310 Rev3/D2-2011-02" parses to
1297
+ # "IEEE P1310Rev3/D2-2011-02"). The before-word separator the
1298
+ # Rev doctrine carries ("-REVmc") is untouched.
1299
+ cleaned = cleaned.gsub(%r{(REV|Rev|rev)[.\-](?=[A-Za-z])}, '\1')
1300
+ cleaned = cleaned.gsub(%r{(\d)(REV|Rev|rev)}, '\1 \2')
1301
+
1302
+ # The corrigendum's own draft follows the ordinal after a comma
1303
+ # ("P11073-10418-2011/Cor 1, D4, July 2015"): re-spell the comma as
1304
+ # the draft separator so the cor route's draft slot reaches it.
1305
+ cleaned = cleaned.gsub(%r{/Cor(?:rigendum)? (\d+), (D[A-Z0-9])}, '/Cor \1/\2')
1306
+
1307
+ # The corrigendum's own draft rides before the ordinal in the
1308
+ # house order ("PC37.101-2006/D2, 2007/Cor. 1"): the catalogue
1309
+ # spellings "…/Cor 1, D4, July 2015" and "…/Cor 1-201x/D5,
1310
+ # April 2012" re-order into it, with the draft's date glued
1311
+ # -YYYY-MM (pubid ruling 2026-10-14: no comma dates).
1312
+ cleaned = cleaned.gsub(
1313
+ %r{/Cor(?:rigendum)? (\d+(?:-[0-9xX]{4})?)/(D[A-Z0-9][^,]*), ([A-Za-z]+) (\d{4})}
1314
+ ) do
1315
+ month = ::Pubid::Ieee::Builder::REV_MONTH_NUMBERS[Regexp.last_match(3).capitalize]
1316
+ if month
1317
+ "/#{Regexp.last_match(2)}-#{Regexp.last_match(4)}-#{month}/Cor. #{Regexp.last_match(1)}"
1318
+ else
1319
+ Regexp.last_match(0)
1320
+ end
1321
+ end
1322
+ cleaned = cleaned.gsub(
1323
+ %r{/Cor(?:rigendum)? (\d+), (D[A-Z0-9][^,]*), ([A-Za-z]+) (\d{4})}
1324
+ ) do
1325
+ month = ::Pubid::Ieee::Builder::REV_MONTH_NUMBERS[Regexp.last_match(3).capitalize]
1326
+ if month
1327
+ "/#{Regexp.last_match(2)}-#{Regexp.last_match(4)}-#{month}/Cor. #{Regexp.last_match(1)}"
1328
+ else
1329
+ Regexp.last_match(0)
1330
+ end
1331
+ end
1332
+
1276
1333
  # NEW Phase 1: Fix number spacing issues (e.g., "C57.1 2.25" → "C57.12.25")
1277
1334
  # This handles cases where a space appears in the middle of a number
1278
1335
  cleaned = cleaned.gsub(/(\d+\.\d+)\s+(\d+\.)/, '\1\2')
@@ -195,7 +195,8 @@ 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
 
@@ -51,6 +51,13 @@ module Pubid
51
51
  COMPACT_FLAT_VALUES.each do |key|
52
52
  out[key] = { "value" => out[key] } if out[key].is_a?(::String)
53
53
  end
54
+ # Legacy index rows (the pre-flattening component era) carry the
55
+ # number as a {"value" => …} Hash; the attribute is a plain
56
+ # string now, so the Hash collapses to its scalar before the
57
+ # assignment dumps it (pubid#540).
58
+ if out["number"].is_a?(::Hash) && out["number"].key?("value")
59
+ out["number"] = out["number"]["value"]
60
+ end
54
61
  out
55
62
  end
56
63
 
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.0.pre.alpha.30"
4
+ VERSION = "2.0.0.pre.alpha.32"
5
5
  end
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.30
4
+ version: 2.0.0.pre.alpha.32
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-14 00:00:00.000000000 Z
11
+ date: 2026-10-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: lutaml-model