pubid 2.0.0.pre.alpha.11 → 2.0.0.pre.alpha.12

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 (74) hide show
  1. checksums.yaml +4 -4
  2. data/lib/pubid/bsi/components/date.rb +10 -4
  3. data/lib/pubid/bsi/identifiers/british_industrial_practice.rb +1 -1
  4. data/lib/pubid/bsi/identifiers/handbook.rb +1 -1
  5. data/lib/pubid/bsi/identifiers/practice_guide.rb +1 -1
  6. data/lib/pubid/bsi/single_identifier.rb +11 -2
  7. data/lib/pubid/ccsds/identifier.rb +4 -0
  8. data/lib/pubid/ccsds/single_identifier.rb +14 -10
  9. data/lib/pubid/csa/builder.rb +3 -3
  10. data/lib/pubid/csa/identifiers/bundled.rb +2 -2
  11. data/lib/pubid/csa/identifiers/cec.rb +1 -1
  12. data/lib/pubid/csa/single_identifier.rb +1 -1
  13. data/lib/pubid/csa/urn_generator.rb +1 -1
  14. data/lib/pubid/evs/builder.rb +1 -1
  15. data/lib/pubid/evs/identifier.rb +6 -6
  16. data/lib/pubid/evs/identifiers/national_adoption.rb +7 -2
  17. data/lib/pubid/evs/renderer.rb +1 -1
  18. data/lib/pubid/evs/urn_generator.rb +1 -1
  19. data/lib/pubid/evs/urn_parser.rb +1 -1
  20. data/lib/pubid/gb/builder.rb +14 -9
  21. data/lib/pubid/gb/identifier.rb +23 -4
  22. data/lib/pubid/gb/parser.rb +4 -0
  23. data/lib/pubid/gb/renderer.rb +4 -3
  24. data/lib/pubid/gb.rb +1 -0
  25. data/lib/pubid/identifier.rb +11 -3
  26. data/lib/pubid/idf/identifier.rb +7 -0
  27. data/lib/pubid/idf/identifiers/amendment.rb +1 -1
  28. data/lib/pubid/idf/identifiers/corrigendum.rb +1 -1
  29. data/lib/pubid/idf/identifiers/international_standard.rb +1 -1
  30. data/lib/pubid/idf/identifiers/reviewed_method.rb +1 -1
  31. data/lib/pubid/idf/single_identifier.rb +1 -1
  32. data/lib/pubid/ieee/builder.rb +42 -2
  33. data/lib/pubid/ieee/identifiers/si_standard.rb +5 -2
  34. data/lib/pubid/ieee/ire/parser.rb +19 -1
  35. data/lib/pubid/ieee/parser.rb +207 -32
  36. data/lib/pubid/ieee/project_renderer.rb +46 -0
  37. data/lib/pubid/ieee/renderer.rb +6 -7
  38. data/lib/pubid/ieee.rb +2 -0
  39. data/lib/pubid/iso/builder.rb +10 -11
  40. data/lib/pubid/iso/components.rb +0 -1
  41. data/lib/pubid/iso/identifier.rb +11 -4
  42. data/lib/pubid/iso/identifiers/directives.rb +7 -4
  43. data/lib/pubid/iso/identifiers/tc_document.rb +31 -46
  44. data/lib/pubid/iso/normalizer.rb +1 -1
  45. data/lib/pubid/iso/urn_generator.rb +10 -10
  46. data/lib/pubid/iso.rb +5 -4
  47. data/lib/pubid/itu/builder.rb +10 -0
  48. data/lib/pubid/itu/identifiers/base.rb +10 -0
  49. data/lib/pubid/itu/identifiers/contribution.rb +31 -0
  50. data/lib/pubid/itu/identifiers.rb +1 -0
  51. data/lib/pubid/itu/parser.rb +19 -2
  52. data/lib/pubid/nist/builder.rb +12 -12
  53. data/lib/pubid/nist/identifiers/base.rb +10 -3
  54. data/lib/pubid/nist/identifiers/commercial_standard_emergency.rb +1 -1
  55. data/lib/pubid/nist/identifiers/crpl_report.rb +5 -6
  56. data/lib/pubid/nist/identifiers/federal_information_processing_standards.rb +2 -2
  57. data/lib/pubid/nist/identifiers/miscellaneous_publication.rb +2 -2
  58. data/lib/pubid/nist/series/ir.rb +3 -7
  59. data/lib/pubid/oiml/builder.rb +2 -2
  60. data/lib/pubid/oiml/identifiers/bulletin.rb +30 -36
  61. data/lib/pubid/oiml/identifiers/code_number.rb +2 -2
  62. data/lib/pubid/oiml/parser.rb +7 -2
  63. data/lib/pubid/oiml/renderer.rb +3 -3
  64. data/lib/pubid/oiml/single_identifier.rb +1 -1
  65. data/lib/pubid/oiml/urn_generator.rb +4 -4
  66. data/lib/pubid/renderers/directives_renderer.rb +3 -1
  67. data/lib/pubid/sae/builder.rb +1 -1
  68. data/lib/pubid/sae/identifiers/base.rb +4 -1
  69. data/lib/pubid/sae/urn_generator.rb +1 -1
  70. data/lib/pubid/version.rb +1 -1
  71. data/lib/tasks/docs.rake +13 -11
  72. metadata +4 -4
  73. data/lib/pubid/ccsds/identifiers/base_BASE_88929.rb +0 -70
  74. data/lib/pubid/iso/components/code.rb +0 -24
@@ -6,18 +6,15 @@ module Pubid
6
6
  # Technical Committee Document
7
7
  # Format: TC 184/SC 4/WG 3 N 123, JTC 1 N 456, TC 184 N 100
8
8
  class TcDocument < Identifier
9
- # TC type (TC, JTC, PC, IT, etc.)
10
- attribute :tc_type, ::Pubid::Components::Code
11
- # TC number
12
- attribute :tc_number, ::Pubid::Components::Code
13
- # SC type
14
- attribute :sc_type, ::Pubid::Components::Code
15
- # SC number
16
- attribute :sc_number, ::Pubid::Components::Code
17
- # WG type
18
- attribute :wg_type, ::Pubid::Components::Code
19
- # WG number
20
- attribute :wg_number, ::Pubid::Components::Code
9
+ # The committee structure: plain strings ("TC", "184", "SC", "4").
10
+ # Each held a Components::Code carrying nothing but `value`, and the
11
+ # serialized form was already the bare scalar.
12
+ attribute :tc_type, :string
13
+ attribute :tc_number, :string
14
+ attribute :sc_type, :string
15
+ attribute :sc_number, :string
16
+ attribute :wg_type, :string
17
+ attribute :wg_number, :string
21
18
 
22
19
  # TC types from ISO system
23
20
  TC_TYPES = %w[TC JTC PC IT CAB CASCO COPOLCO COUNCIL CPSG CS DEVCO GA
@@ -33,33 +30,21 @@ module Pubid
33
30
  # TC documents don't use typed stages like other identifiers
34
31
  TYPED_STAGES = [].freeze
35
32
 
36
- # Serialize the committee structure (all plain-string Codes) on top of
37
- # the inherited ISO mapping; TC documents have no stage.
33
+ # Serialize the committee structure on top of the inherited ISO
34
+ # mapping; TC documents have no stage. The attributes are plain
35
+ # strings, so lutaml needs no converter (the ETSI/OIML shape).
38
36
  key_value do
39
- map "tc_type", with: { to: :tc_type_to_kv, from: :tc_type_from_kv }
40
- map "tc_number", with: { to: :tc_number_to_kv, from: :tc_number_from_kv }
41
- map "sc_type", with: { to: :sc_type_to_kv, from: :sc_type_from_kv }
42
- map "sc_number", with: { to: :sc_number_to_kv, from: :sc_number_from_kv }
43
- map "wg_type", with: { to: :wg_type_to_kv, from: :wg_type_from_kv }
44
- map "wg_number", with: { to: :wg_number_to_kv, from: :wg_number_from_kv }
37
+ map "tc_type", to: :tc_type
38
+ map "tc_number", to: :tc_number
39
+ map "sc_type", to: :sc_type
40
+ map "sc_number", to: :sc_number
41
+ map "wg_type", to: :wg_type
42
+ map "wg_number", to: :wg_number
45
43
  end
46
44
 
47
45
  # TC documents have no stage; suppress the inherited stage emission.
48
46
  def stage_to_kv(_model, _doc); end
49
47
 
50
- def tc_type_to_kv(m, doc) = emit_code(doc, "tc_type", m.tc_type)
51
- def tc_type_from_kv(m, v) = m.tc_type = build_code(v)
52
- def tc_number_to_kv(m, doc) = emit_code(doc, "tc_number", m.tc_number)
53
- def tc_number_from_kv(m, v) = m.tc_number = build_code(v)
54
- def sc_type_to_kv(m, doc) = emit_code(doc, "sc_type", m.sc_type)
55
- def sc_type_from_kv(m, v) = m.sc_type = build_code(v)
56
- def sc_number_to_kv(m, doc) = emit_code(doc, "sc_number", m.sc_number)
57
- def sc_number_from_kv(m, v) = m.sc_number = build_code(v)
58
- def wg_type_to_kv(m, doc) = emit_code(doc, "wg_type", m.wg_type)
59
- def wg_type_from_kv(m, v) = m.wg_type = build_code(v)
60
- def wg_number_to_kv(m, doc) = emit_code(doc, "wg_number", m.wg_number)
61
- def wg_number_from_kv(m, v) = m.wg_number = build_code(v)
62
-
63
48
  def self.type
64
49
  { key: :tc,
65
50
  web: :tc_document, title: "Technical Committee Document", short: "TC" }
@@ -73,23 +58,23 @@ module Pubid
73
58
  result = publisher.to_s if publisher
74
59
 
75
60
  # Add TC type and number
76
- result += "/#{tc_type.render} " if tc_type&.value
77
- result += tc_number.render.to_s if tc_number&.value
61
+ result += "/#{tc_type} " unless tc_type.to_s.empty?
62
+ result += tc_number.to_s unless tc_number.to_s.empty?
78
63
 
79
64
  # Add SC type and number
80
- if sc_type&.value && sc_number&.value
81
- result += "/#{sc_type.render} "
82
- result += sc_number.render.to_s
65
+ unless sc_type.to_s.empty? || sc_number.to_s.empty?
66
+ result += "/#{sc_type} "
67
+ result += sc_number.to_s
83
68
  end
84
69
 
85
70
  # Add WG type and number
86
- if wg_type&.value && wg_number&.value
87
- result += "/#{wg_type.render} "
88
- result += wg_number.render.to_s
71
+ unless wg_type.to_s.empty? || wg_number.to_s.empty?
72
+ result += "/#{wg_type} "
73
+ result += wg_number.to_s
89
74
  end
90
75
 
91
76
  # Add document number
92
- result += " N #{number.render}" if number&.value
77
+ result += " N #{number}" unless number.to_s.empty?
93
78
 
94
79
  # Add year if present
95
80
  result += ":#{date.render}" if date&.year
@@ -105,16 +90,16 @@ module Pubid
105
90
  parts << publisher.render(context: urn_ctx) if publisher
106
91
 
107
92
  # Add TC
108
- parts << "tc:#{tc_number.render(context: urn_ctx)}" if tc_number&.value
93
+ parts << "tc:#{tc_number}" unless tc_number.to_s.empty?
109
94
 
110
95
  # Add SC
111
- parts << "sc-#{sc_number.render(context: urn_ctx)}" if sc_number&.value
96
+ parts << "sc-#{sc_number}" unless sc_number.to_s.empty?
112
97
 
113
98
  # Add WG
114
- parts << "wg-#{wg_number.render(context: urn_ctx)}" if wg_number&.value
99
+ parts << "wg-#{wg_number}" unless wg_number.to_s.empty?
115
100
 
116
101
  # Add document number
117
- parts << number.render(context: urn_ctx) if number&.value
102
+ parts << number.to_s unless number.to_s.empty?
118
103
 
119
104
  parts.join(":")
120
105
  end
@@ -73,7 +73,7 @@ module Pubid
73
73
 
74
74
  addendum = Identifiers::Addendum.new
75
75
  addendum.base = base
76
- addendum.number = Iso::Components::Code.new(value: supplement_number)
76
+ addendum.number = supplement_number
77
77
  addendum.date = Pubid::Components::Date.new(year: supplement_year) if supplement_year
78
78
 
79
79
  typed_stage = Pubid::Iso.locate_stage(stage_abbr)
@@ -63,7 +63,7 @@ module Pubid
63
63
  parts << originator_component
64
64
  type_comp = type_component
65
65
  parts << type_comp if type_comp
66
- parts << identifier.number.render(context: URN_CONTEXT) if identifier.number
66
+ parts << identifier.number.to_s if identifier.number
67
67
  part_comp = part_component
68
68
  parts << part_comp if part_comp
69
69
  stage_comp = stage_component
@@ -95,7 +95,7 @@ module Pubid
95
95
  parts << base_gen.originator_component
96
96
  type_comp = base_gen.type_component
97
97
  parts << type_comp if type_comp
98
- parts << base_id.number.render(context: URN_CONTEXT) if base_id.number
98
+ parts << base_id.number.to_s if base_id.number
99
99
  part_comp = base_gen.part_component
100
100
  parts << part_comp if part_comp
101
101
 
@@ -138,13 +138,13 @@ module Pubid
138
138
  parts << supp.date.render(context: URN_CONTEXT)
139
139
  if supp.number
140
140
  if supp.stage_iteration && !supp.is_a?(Pubid::Iso::Identifiers::Supplement)
141
- parts << "v#{supp.number.render(context: URN_CONTEXT)}.#{supp.stage_iteration.render(context: URN_CONTEXT)}"
141
+ parts << "v#{supp.number}.#{supp.stage_iteration.render(context: URN_CONTEXT)}"
142
142
  else
143
- parts << "v#{supp.number.render(context: URN_CONTEXT)}"
143
+ parts << "v#{supp.number}"
144
144
  end
145
145
  end
146
146
  else
147
- parts << supp.number.render(context: URN_CONTEXT) if supp.number
147
+ parts << supp.number.to_s if supp.number
148
148
  if supp.stage_iteration && !supp.is_a?(Pubid::Iso::Identifiers::Supplement)
149
149
  parts << "v1.#{supp.stage_iteration.render(context: URN_CONTEXT)}"
150
150
  else
@@ -197,8 +197,8 @@ module Pubid
197
197
  def part_component
198
198
  return nil unless identifier.part
199
199
 
200
- result = "-#{identifier.part.render(context: URN_CONTEXT)}"
201
- result += "-#{identifier.subpart.render(context: URN_CONTEXT)}" if identifier.subpart
200
+ result = "-#{identifier.part}"
201
+ result += "-#{identifier.subpart}" if identifier.subpart
202
202
  result
203
203
  end
204
204
 
@@ -292,13 +292,13 @@ module Pubid
292
292
  parts << identifier.date.render(context: URN_CONTEXT)
293
293
  if identifier.number
294
294
  parts << if identifier.stage_iteration
295
- "v#{identifier.number.render(context: URN_CONTEXT)}.#{identifier.stage_iteration.render(context: URN_CONTEXT)}"
295
+ "v#{identifier.number}.#{identifier.stage_iteration.render(context: URN_CONTEXT)}"
296
296
  else
297
- "v#{identifier.number.render(context: URN_CONTEXT)}"
297
+ "v#{identifier.number}"
298
298
  end
299
299
  end
300
300
  else
301
- parts << identifier.number.render(context: URN_CONTEXT) if identifier.number
301
+ parts << identifier.number.to_s if identifier.number
302
302
  parts << if identifier.stage_iteration
303
303
  "v1.#{identifier.stage_iteration.render(context: URN_CONTEXT)}"
304
304
  else
data/lib/pubid/iso.rb CHANGED
@@ -66,7 +66,7 @@ module Pubid
66
66
  # @param type [Symbol] the type key (e.g. :is, :tr, :ts, :guide)
67
67
  # @param publisher [String, Pubid::Iso::Components::Publisher] primary publisher
68
68
  # @param copublishers [Array<String>, Array<Pubid::Components::Publisher>]
69
- # @param number [String, Integer, Pubid::Iso::Components::Code]
69
+ # @param number [String, Integer]
70
70
  # @param year [String, Integer, Pubid::Components::Date, nil]
71
71
  # @param attrs [Hash] additional identifier attributes
72
72
  # @return [Pubid::Iso::Identifier]
@@ -106,10 +106,11 @@ module Pubid
106
106
  private_class_method :build_copublishers
107
107
 
108
108
  # @!visibility private
109
+ # number/part/subpart are plain strings. A caller that still passes a
110
+ # Components::Code (another flavor's builder, a hand-built id) is read
111
+ # through its own #to_s rather than refused.
109
112
  def self.build_code(value)
110
- return value if value.is_a?(Components::Code)
111
-
112
- Components::Code.new(value: value.to_s)
113
+ value.to_s
113
114
  end
114
115
  private_class_method :build_code
115
116
 
@@ -68,6 +68,16 @@ module Pubid
68
68
  return build_question(data)
69
69
  end
70
70
 
71
+ # Contribution (Temporary Document) — "ITU-R SG17-C1000" (pubid#340).
72
+ if data[:contribution_marker]
73
+ return Identifiers::Contribution.new(
74
+ sector: Components::Sector.new(sector: data[:sector].to_s),
75
+ series: Components::Series.new(series: data[:series].to_s),
76
+ code: build_code(data),
77
+ language: data[:language]&.to_s,
78
+ )
79
+ end
80
+
71
81
  # Build basic recommendation or combined identifier
72
82
  sector = Components::Sector.new(sector: data[:sector].to_s)
73
83
  series = Components::Series.new(series: data[:series].to_s) if data[:series]
@@ -37,6 +37,16 @@ module Pubid
37
37
  end
38
38
  private_class_method :normalize_whitespace
39
39
 
40
+ # The type key used by Pubid::Itu.locate_type — the class name
41
+ # ("Contribution" => :contribution). No ITU leaf ever defined a
42
+ # `type` hash, so locate_type raised NoMethodError on every call;
43
+ # the derived default gives every concrete leaf a key for free
44
+ # (pubid#340's ask). metanorma-itu constructs via this lookup.
45
+ def self.type
46
+ leaf = name.split("::").last
47
+ { key: leaf.gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase.to_sym }
48
+ end
49
+
40
50
  # Long-form ↔ ITU single-letter language code map. The parser produces
41
51
  # single-letter codes (E/F/S/R/A/C); API callers (e.g. metanorma-itu)
42
52
  # pass long-form (en/fr/es/ru/ar/zh). Storage is normalized to the
@@ -0,0 +1,31 @@
1
+ # frozen_string: true
2
+
3
+ module Pubid
4
+ module Itu
5
+ module Identifiers
6
+ # ITU Contribution (Temporary Document) — the working documents a study
7
+ # group circulates, numbered per study group.
8
+ #
9
+ # Format: ITU-{SECTOR} {SERIES}-C{NUMBER} e.g. ITU-R SG17-C1000
10
+ #
11
+ # Mirrors pubid-itu 1.15's Pubid::Itu::Identifier::Contribution
12
+ # ("%{series}-C%{number}"), which metanorma-itu builds from
13
+ # `:doctype: contribution` documents (pubid#340).
14
+ class Contribution < Identifier
15
+ include StandardSerialization
16
+
17
+ def render_base(**_opts)
18
+ "#{publisher}-#{sector} #{series}-C#{code&.number}"
19
+ end
20
+
21
+ def ==(other)
22
+ return false unless other.is_a?(Contribution)
23
+
24
+ sector == other.sector &&
25
+ series == other.series &&
26
+ code == other.code
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -11,6 +11,7 @@ module Pubid
11
11
  autoload :AppendixOfRecommendation,
12
12
  "#{__dir__}/identifiers/appendix_of_recommendation"
13
13
  autoload :CombinedIdentifier, "#{__dir__}/identifiers/combined_identifier"
14
+ autoload :Contribution, "#{__dir__}/identifiers/contribution"
14
15
  autoload :Corrigendum, "#{__dir__}/identifiers/corrigendum"
15
16
  autoload :Errata, "#{__dir__}/identifiers/errata"
16
17
  autoload :Handbook, "#{__dir__}/identifiers/handbook"
@@ -323,6 +323,22 @@ module Pubid
323
323
  (digits | letter.repeat(1)).as(:number) >> parts
324
324
  end
325
325
 
326
+ # ITU Contribution (Temporary Document) — "ITU-R SG17-C1000" (pubid#340).
327
+ # The "-C" marker before the number distinguishes it from every
328
+ # neighbouring shape: a series-code document's post-dash number starts
329
+ # with digits or is all letters (never "C"+digits), and with_series
330
+ # requires a dot after the series.
331
+ rule(:contribution) do
332
+ itu_prefix >>
333
+ sector >>
334
+ space >>
335
+ series >>
336
+ str("-C").as(:contribution_marker) >>
337
+ number >>
338
+ parts >>
339
+ language.maybe
340
+ end
341
+
326
342
  rule(:base_series_code) do
327
343
  itu_prefix >>
328
344
  sector >>
@@ -656,9 +672,10 @@ module Pubid
656
672
  numeric_question |
657
673
  letter_question |
658
674
  with_series |
675
+ contribution |
659
676
  # Unreachable earlier: special_publication needs the literal "OB",
660
- # handbook/numeric_question need leading digits, and letter_question
661
- # needs series >> dot. Before without_series, whose code needs digits.
677
+ # handbook/numeric_question need leading digits, letter_question
678
+ # needs series >> dot, and contribution needs "-C" after the series.
662
679
  series_code_identifier |
663
680
  without_series
664
681
  end
@@ -253,7 +253,7 @@ module Pubid
253
253
  elsif decimal_num
254
254
  decimal_base = decimal_num[:decimal_base].to_s
255
255
  decimal_suffix = decimal_num[:decimal_suffix].to_s
256
- identifier.number = Components::Code.new(value: "#{first_num.value}-#{decimal_base}.#{decimal_suffix}")
256
+ identifier.number = "#{first_num.value}-#{decimal_base}.#{decimal_suffix}"
257
257
  # NEW: Handle letter number pattern (e.g., 1-1A, 1-3B for NCSTAR identifiers)
258
258
  # letter_num is {:letter_base => "1", :letter_suffix => "A"}
259
259
  # Also handles IR series "R" suffix: "79-1786R" -> "79-1786r1"
@@ -268,10 +268,10 @@ module Pubid
268
268
  # Series-specific handler took ownership (e.g., IR "R" → r1)
269
269
  elsif identifier.part
270
270
  # SpecialPublication pattern: letter_suffix is separate Part component
271
- identifier.number = Components::Code.new(value: "#{first_num.value}-#{letter_base}")
271
+ identifier.number = "#{first_num.value}-#{letter_base}"
272
272
  else
273
273
  # NCSTAR pattern: letter_suffix is part of the number
274
- identifier.number = Components::Code.new(value: "#{first_num.value}-#{letter_base}#{letter_suffix}")
274
+ identifier.number = "#{first_num.value}-#{letter_base}#{letter_suffix}"
275
275
  end
276
276
  elsif second_num
277
277
  # Check for special patterns first
@@ -285,7 +285,7 @@ module Pubid
285
285
  # Create Edition component
286
286
  edition_obj = Components::Edition.new(type: "r", id: edition_id)
287
287
 
288
- identifier.number = Components::Code.new(value: "#{first_num.value}-#{number_part}")
288
+ identifier.number = "#{first_num.value}-#{number_part}"
289
289
  identifier.edition = edition_obj
290
290
  # CS Emergency pattern: e104-43 -> number=104, edition_year=1943
291
291
  # Logic: e104-43 means "emergency 104 from 1943" (43 = 1943)
@@ -300,7 +300,7 @@ module Pubid
300
300
  # Create Edition component
301
301
  edition_obj = Components::Edition.new(type: "e", id: edition_year)
302
302
 
303
- identifier.number = Components::Code.new(value: number_part)
303
+ identifier.number = number_part
304
304
  identifier.edition = edition_obj
305
305
  elsif first_num.value.to_s.match?(/^(\d+)e(\d+)$/) &&
306
306
  second_num.value.to_s.match?(/^\d{2,4}$/)
@@ -314,7 +314,7 @@ module Pubid
314
314
  # Expand 2-digit year to 4-digit (50 -> 1950)
315
315
  year_part = "19#{year_part}" if year_part.length == 2
316
316
 
317
- identifier.number = Components::Code.new(value: number_part)
317
+ identifier.number = number_part
318
318
 
319
319
  # For edition+year patterns, handling depends on identifier type:
320
320
  # - CIRC: edition number + year as additional_text, rendered with dot ("11e2-1915" -> "11e2.1915")
@@ -329,7 +329,7 @@ module Pubid
329
329
  number_part = first_num.value.to_s.match(/^(\d+)supp?$/)[1]
330
330
  year_part = second_num.value.to_s
331
331
 
332
- identifier.number = Components::Code.new(value: number_part)
332
+ identifier.number = number_part
333
333
  supp[:value] = year_part
334
334
  supp[:present] = true
335
335
  elsif second_num.value.to_s.match?(/^(\d+)supp?$/)
@@ -337,14 +337,14 @@ module Pubid
337
337
  # second number. Strip it and isolate as supplement="" (single-p).
338
338
  second_part = second_num.value.to_s.match(/^(\d+)supp?$/)[1]
339
339
  compound = "#{first_num.value}-#{second_part}"
340
- identifier.number = Components::Code.new(value: compound)
340
+ identifier.number = compound
341
341
  supp[:value] = ""
342
342
  supp[:present] = true
343
343
  elsif identifier.is_a?(Identifiers::TechnicalNote) &&
344
344
  second_num.value.to_s.match?(/^(19|20)\d{2}$/)
345
345
  # SPECIAL CASE FOR TN: second_num is edition year
346
346
  # Following "date IS edition" rule: -1993 becomes Edition(type: "e", id: "1993")
347
- identifier.number = first_num
347
+ identifier.number = first_num.value.to_s
348
348
  edition_obj = Components::Edition.new(type: "e",
349
349
  id: second_num.value.to_s)
350
350
  identifier.edition = edition_obj
@@ -354,16 +354,16 @@ module Pubid
354
354
  # not folded into the compound number.
355
355
  identifier.part = Components::Part.new(type: "pt",
356
356
  value: part_num)
357
- identifier.number = Components::Code.new(value: "#{first_num.value}-#{second_num.value}")
357
+ identifier.number = "#{first_num.value}-#{second_num.value}"
358
358
  else
359
359
  # For GCR and others, include part number in compound number
360
360
  compound_value = "#{first_num.value}-#{second_num.value}"
361
361
  compound_value += "-#{part_num}" if part_num
362
- identifier.number = Components::Code.new(value: compound_value)
362
+ identifier.number = compound_value
363
363
  end
364
364
  else
365
365
  # No second_num, use first_num directly
366
- identifier.number = first_num
366
+ identifier.number = first_num.value.to_s
367
367
  end
368
368
  end
369
369
 
@@ -27,7 +27,7 @@ module Pubid
27
27
  # bare { "value" => … } with no part;
28
28
  # - drop the redundant build artifacts, decomposed pieces of the
29
29
  # canonical number that identity/rendering never read.
30
- COMPACT_FLAT_CODES = %w[series number subseries].freeze
30
+ COMPACT_FLAT_CODES = %w[series subseries].freeze
31
31
  COMPACT_FLAT_VALUES = %w[volume].freeze
32
32
  COMPACT_DROP_ATTRS = %w[first_number second_number].freeze
33
33
 
@@ -105,7 +105,14 @@ module Pubid
105
105
  # equality so a manually-built id (without subseries set) still equals a
106
106
  # parsed one.
107
107
  attribute :subseries, Components::Code
108
- attribute :number, Components::Code
108
+ # Plain :string. NIST never populated the Code beyond `value`: `subpart`
109
+ # is set nowhere, and `part` was a reader splitting `value` on "-" that
110
+ # no library code called. `series`, `subseries`, `first_number`,
111
+ # `second_number` and `parts` keep the component.
112
+ attribute :number, :string
113
+ # NIST never uses `subpart`; declare it so the inherited Components::Code
114
+ # does not disagree with this class (the determinism landmine).
115
+ attribute :subpart, :string
109
116
 
110
117
  # V2 COMPONENTS (Lutaml::Model objects) - PROPER SEPARATION
111
118
  # `edition` (Components::Edition: type + id + additional_text) is the
@@ -412,7 +419,7 @@ module Pubid
412
419
  # "National Institute of Standards and Technology Special Publication 800-27, Revision A"
413
420
  result = publisher_full_name
414
421
  result += " #{series_full_name}" if series
415
- result += " #{number.value}" if number
422
+ result += " #{number}" if number
416
423
  result += parts.map { |p| "-#{p}" }.join if parts&.any?
417
424
 
418
425
  # Render volume and issue number in long form: "Vol. 6, No. 12"
@@ -44,7 +44,7 @@ module Pubid
44
44
  result += " CS-E"
45
45
 
46
46
  # Number (already extracted from e104 → 104 in builder)
47
- result += " #{number.value}" if number
47
+ result += " #{number}" if number
48
48
 
49
49
  # Edition (e1943 for e104-43 pattern) plus any other component the
50
50
  # parser attached (volume, supplement, version, ...).
@@ -46,7 +46,7 @@ module Pubid
46
46
  def normalized_number
47
47
  return nil unless number
48
48
 
49
- num_value = number.value.to_s
49
+ num_value = number.to_s
50
50
 
51
51
  # Pattern: c4-4 → 4-4 (hide 'c' prefix)
52
52
  case num_value
@@ -64,12 +64,12 @@ module Pubid
64
64
  end
65
65
 
66
66
  # Override number to return normalized value
67
- # Tests expect number.value to be normalized (c4-4 → 4-4, 4-m-5 → 4-M-5)
67
+ # Tests expect number to be normalized (c4-4 → 4-4, 4-m-5 → 4-M-5)
68
68
  def number
69
69
  num = super
70
70
  return num unless num
71
71
 
72
- num_value = num.value.to_s
72
+ num_value = num.to_s
73
73
 
74
74
  # Pattern: c4-4 → 4-4 (hide 'c' prefix)
75
75
  case num_value
@@ -83,8 +83,7 @@ module Pubid
83
83
  num_value = num_value.gsub(/^m-/, "M-")
84
84
  end
85
85
 
86
- # Return new Code object with normalized value
87
- Components::Code.new(value: num_value)
86
+ num_value
88
87
  end
89
88
 
90
89
  def to_s(format = nil)
@@ -106,7 +105,7 @@ module Pubid
106
105
 
107
106
  if number
108
107
  # Normalize number value for CRPL patterns
109
- num_value = number.value.to_s
108
+ num_value = number.to_s
110
109
 
111
110
  # Pattern: c4-4 → 4-4 (hide 'c' prefix)
112
111
  case num_value
@@ -66,14 +66,14 @@ module Pubid
66
66
  # Convert dash-year patterns to edition format for FIPS
67
67
  # Pattern: "14-1971" → "14e1971" (edition year format)
68
68
  # But preserve dash for parts: "140-3" stays as-is
69
- if number.value =~ /^(\d{1,3})-(\d{4})$/
69
+ if number =~ /^(\d{1,3})-(\d{4})$/
70
70
  # This is a number-year pattern (e.g., "14-1971")
71
71
  # Convert to edition format: "14e1971"
72
72
  number_part = $1
73
73
  year_part = $2
74
74
  result += " #{number_part}e#{year_part}"
75
75
  else
76
- result += " #{number.value}"
76
+ result += " #{number}"
77
77
  end
78
78
  end
79
79
 
@@ -57,14 +57,14 @@ module Pubid
57
57
 
58
58
  def to_short_style
59
59
  result = "#{default_publisher} #{series_code}"
60
- result += " #{number.value}" if number
60
+ result += " #{number}" if number
61
61
  result += append_short_components
62
62
  result
63
63
  end
64
64
 
65
65
  def to_mr_style
66
66
  result = "#{default_publisher}.#{series_code}"
67
- result += ".#{number.value}" if number
67
+ result += ".#{number}" if number
68
68
  result += append_mr_components
69
69
  result
70
70
  end
@@ -31,9 +31,7 @@ module Pubid
31
31
  letter_suffix:)
32
32
  return false unless letter_suffix == "R"
33
33
 
34
- identifier.number = Components::Code.new(
35
- value: "#{first_num.value}-#{letter_base}",
36
- )
34
+ identifier.number = "#{first_num.value}-#{letter_base}"
37
35
  edition_obj = Components::Edition.new(type: "r", id: "1")
38
36
  identifier.edition = edition_obj
39
37
  true
@@ -42,13 +40,11 @@ module Pubid
42
40
  def self.finalize_identifier(identifier, _parsed_hash)
43
41
  return unless identifier.number
44
42
 
45
- value = identifier.number.value.to_s
43
+ value = identifier.number.to_s
46
44
  match = value.match(/^(\d+)e(\d{4})$/)
47
45
  return unless match
48
46
 
49
- identifier.number = Components::Code.new(
50
- value: "#{match[1]}-#{match[2]}",
51
- )
47
+ identifier.number = "#{match[1]}-#{match[2]}"
52
48
  identifier.edition = nil
53
49
  end
54
50
  end
@@ -228,13 +228,13 @@ module Pubid
228
228
  article_id = parsed_hash[:article_id].to_s
229
229
  identifier.date ||= Pubid::Components::Date.new
230
230
  identifier.date.year = article_id[0, 4]
231
- identifier.issue = article_id[4, 2]
231
+ identifier.number = article_id[4, 2]
232
232
  identifier.sequence = article_id[6, 2]
233
233
  warn_on_volume_mismatch(identifier, parsed_hash[:volume_roman])
234
234
  return
235
235
  end
236
236
 
237
- identifier.issue = parsed_hash[:issue].to_s if parsed_hash[:issue]
237
+ identifier.number = parsed_hash[:issue].to_s if parsed_hash[:issue]
238
238
  identifier.sequence = parsed_hash[:sequence].to_s if parsed_hash[:sequence]
239
239
  end
240
240