pubid 2.0.0.pre.alpha.21 → 2.0.0.pre.alpha.23

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.
@@ -158,13 +158,13 @@ module Pubid
158
158
  # the year on every round trip — the same collision
159
159
  # `fold_scalar_aliases` guards against on the constructor path.
160
160
  # `edition` keeps its own name, so it is never a collision.
161
- return if flat_key != attr_name.to_s && declared_attribute?(flat_key)
161
+ return if flat_key != attr_name.name && declared_attribute?(flat_key)
162
162
 
163
163
  key = data.key?(flat_key) ? flat_key : flat_key.to_sym
164
164
  value = data[key]
165
165
  return if value.nil? || value.is_a?(::Hash)
166
166
 
167
- field = flat_scalar_fields.fetch(attr_name).to_s
167
+ field = flat_scalar_fields.fetch(attr_name).name
168
168
  if value.is_a?(::Array)
169
169
  # A collection of components (`copublishers`) flattens to a list of
170
170
  # scalars; anything else in a list is not the flat form.
@@ -172,12 +172,12 @@ module Pubid
172
172
  return unless value.all? { |v| scalar_value?(v) }
173
173
 
174
174
  data.delete(key)
175
- data[attr_name.to_s] = value.map { |v| { field => v.to_s } }
175
+ data[attr_name.name] = value.map { |v| { field => v.to_s } }
176
176
  return
177
177
  end
178
178
 
179
179
  data.delete(key)
180
- data[attr_name.to_s] = { field => value.to_s }
180
+ data[attr_name.name] = { field => value.to_s }
181
181
  end
182
182
 
183
183
  # True when +name+ is declared as a collection on this class.
@@ -267,17 +267,19 @@ module Pubid
267
267
  # edition as a plain `year` attribute, and folding there would destroy it.
268
268
  def fold_scalar_aliases(attrs)
269
269
  SCALAR_ATTRIBUTE_ALIASES.each do |alias_name, (target, field)|
270
- key = attrs.key?(alias_name) ? alias_name : alias_name.to_s
270
+ alias_str = alias_name.name
271
+ key = attrs.key?(alias_name) ? alias_name : alias_str
271
272
  next unless attrs.key?(key)
272
- next if attributes.key?(alias_name) || attributes.key?(alias_name.to_s)
273
+ next if attributes.key?(alias_name) || attributes.key?(alias_str)
273
274
  next unless component_attribute?(target)
274
275
 
275
276
  value = attrs.delete(key)
276
277
  next if value.nil?
277
278
 
278
- existing = attrs[target] || attrs[target.to_s] || {}
279
+ target_str = target.name
280
+ existing = attrs[target] || attrs[target_str] || {}
279
281
  existing = component_to_hash(existing)
280
- attrs.delete(target.to_s)
282
+ attrs.delete(target_str)
281
283
  attrs[target] = existing.merge(field => value.to_s)
282
284
  end
283
285
  end
@@ -286,7 +288,7 @@ module Pubid
286
288
  # declares the attribute as a plain :string keeps the scalar untouched.
287
289
  def coerce_component_scalars(attrs)
288
290
  FLAT_SCALAR_FIELDS.each do |attr_name, field|
289
- key = attrs.key?(attr_name) ? attr_name : attr_name.to_s
291
+ key = attrs.key?(attr_name) ? attr_name : attr_name.name
290
292
  next unless attrs.key?(key)
291
293
  next unless component_attribute?(attr_name)
292
294
 
@@ -774,16 +776,23 @@ module Pubid
774
776
  super
775
777
  end
776
778
 
779
+ # Pure function of the class name; relaton resolves it per registry
780
+ # lookup and per cache-key build, so the kebab-case rebuild (two gsub
781
+ # passes + interpolations) ran on every call — a top string
782
+ # allocation site in metanorma's bibliography phase. Singleton ivars
783
+ # are not inherited, so each concrete class memoizes its own.
777
784
  def self.polymorphic_name
778
785
  return nil unless name
779
786
 
780
- parts = name.split("::")
781
- flavor = parts[1]&.downcase
782
- type_kebab = parts.last
783
- .gsub(/([A-Z]+)([A-Z][a-z])/, '\1-\2')
784
- .gsub(/([a-z\d])([A-Z])/, '\1-\2')
785
- .downcase
786
- "pubid:#{flavor}:#{type_kebab}"
787
+ @pubid_polymorphic_name ||= begin
788
+ parts = name.split("::")
789
+ flavor = parts[1]&.downcase
790
+ type_kebab = parts.last
791
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1-\2')
792
+ .gsub(/([a-z\d])([A-Z])/, '\1-\2')
793
+ .downcase
794
+ "pubid:#{flavor}:#{type_kebab}"
795
+ end
787
796
  end
788
797
 
789
798
  def root
@@ -1488,23 +1488,10 @@ module Pubid
1488
1488
  # which standards are actually IEEE-related.
1489
1489
  # ISO-only standards are still filtered as they have separate handling.
1490
1490
 
1491
- # Pattern 3: Replace underscore before ISO stage codes with slash
1492
- # These are joint development drafts that use underscore instead of slash
1493
- cleaned = cleaned.gsub(/_(FDIS|CDV|CD|DIS|WD|PWI|NP)/, '/\1')
1494
-
1495
1491
  # NEW: Normalize multiple spaces to single space
1496
1492
  # No valid IEEE identifier pattern needs more than 1 space
1497
1493
  cleaned = cleaned.gsub(/\s+/, " ")
1498
1494
 
1499
- # A joint ISO-led publisher list is sometimes crawled with a stray slash
1500
- # (or slash+space) before the ISO stage code — "ISO/IEC/IEEE/ FDIS …" or
1501
- # "ISO/IEC/IEEE/FDIS …". Restore the space separator so the stage parses
1502
- # (bucket 7).
1503
- cleaned = cleaned.gsub(
1504
- %r{\b(ISO/IEC/IEEE|IEEE/ISO/IEC|IEEE/IEC/ISO|ISO/IEEE|IEC/IEEE|IEEE/IEC|ISO/IEC)/ ?(FDIS|FCD|CDV|DIS\d?|CD\d?|WD|PWI|NP)\b},
1505
- '\1 \2',
1506
- )
1507
-
1508
1495
  # Rewrite the rawbib revision-notation dialects (REVa/REVd/glued) into
1509
1496
  # the canonical /R-<x> form before the suffix normalization below.
1510
1497
  cleaned = normalize_revision_notation(cleaned)
@@ -1520,17 +1507,9 @@ module Pubid
1520
1507
  cleaned = cleaned.gsub(/(\d)\s+-(\d{4})\b/, '\1-\2') # "C37.101 -2006" → "C37.101-2006"
1521
1508
  cleaned = cleaned.gsub(/(\d)-\s+(\d{4})\b/, '\1-\2') # "C62.35- 2010" → "C62.35-2010"
1522
1509
 
1523
- # NEW Session 171: HTML entity for en dash (&#x2013;)
1524
- # ONLY convert if not already followed by a dash (avoid creating --)
1525
- cleaned = cleaned.gsub(/&#x2013;(?!-)/, "-") # En dash → regular hyphen (if not followed by dash)
1526
- cleaned = cleaned.gsub("&#x2013;-", "-") # En-dash-dash → single dash
1527
-
1528
1510
  # NEW Session 171: Remove wrong ! prefix
1529
1511
  cleaned = cleaned.gsub(/^!IEEE /, "IEEE ")
1530
1512
 
1531
- # NEW Session 171: Fix "IEEE/ ASTM" spacing (extra space after slash)
1532
- cleaned = cleaned.gsub("IEEE/ ASTM", "IEEE/ASTM")
1533
-
1534
1513
  # NEW Phase 1: Handle HTML entities comprehensively
1535
1514
  cleaned = cleaned.gsub("&#x2122;", "™") # Trademark symbol
1536
1515
  cleaned = cleaned.gsub("&#x2019;", "'") # Smart apostrophe
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.21"
4
+ VERSION = "2.0.0.pre.alpha.23"
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.21
4
+ version: 2.0.0.pre.alpha.23
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-02 00:00:00.000000000 Z
11
+ date: 2026-10-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: lutaml-model