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
@@ -33,49 +33,43 @@ module Pubid
33
33
  # carried in the structured form and the roman volume in citations.
34
34
  BASE_YEAR_OFFSET = 1959
35
35
 
36
- # Zero-padded issue number within the year ("01".."04", plus "07"/"10"
37
- # for the online-bulletin series). Always 2 digits.
38
- attribute :issue, :string
36
+ # The issue: the zero-padded number within the volume ("01".."04",
37
+ # plus "07"/"10" for the online-bulletin series). Always 2 digits. The
38
+ # year is the volume and the issue is the number, as in the citation
39
+ # form "LXVII(2)".
40
+ #
41
+ # Declared as `number`, so it is the relaton index key
42
+ # (`id.root.number.to_s`) and every index row with an issue shows a
43
+ # `number`. The key puts the same issue number of all volumes in one
44
+ # bucket; the MR slug keeps each article distinct.
45
+ #
46
+ # Declared on this LEAF, which has no subclasses. It redefines the
47
+ # `Components::Code number` of ::Pubid::Identifier as a :string, and a
48
+ # redefinition on a class that other classes inherit is the
49
+ # multi-flavor determinism landmine (see Identifiers::CodeNumber).
50
+ #
51
+ # nil for a volume ("OIML Bulletin 1960") and for the bare periodical
52
+ # reference "OIML Bulletin", which name no issue.
53
+ attribute :number, :string
39
54
  # Zero-padded sequence within the issue ("00" = editorial, "01"+ for
40
55
  # articles). Always 2 digits.
41
56
  attribute :sequence, :string
42
57
 
43
58
  key_value do
44
- map "issue", to: :issue
59
+ map "number", to: :number
45
60
  map "sequence", to: :sequence
46
61
  end
47
62
 
48
- # relaton-index keys on `id.root.number.to_s`. Bulletin is the one OIML
49
- # leaf with no code (it does not include Identifiers::CodeNumber), so
50
- # it derives the key from the year it already stores: every issue and
51
- # article of a volume clusters into one bucket, with per-article
52
- # distinctness coming from the MR slug instead. That is the BIPM
53
- # Metrologia-volume precedent recorded in CLAUDE.md.
54
- #
55
- # A DERIVED READER, deliberately not an attribute: storing it would
56
- # duplicate `year` in every row, and the key_value block above maps no
57
- # "number", so nothing is added to the wire format. Safe as a plain
58
- # method because Bulletin declares no `number` attribute of its own
59
- # (the ITU itu/identifiers/base.rb precedent).
60
- #
61
- # nil for the bare periodical reference "OIML Bulletin", which names no
62
- # year — a deliberate gap, pinned in spec/pubid/oiml/root_number_spec.rb.
63
- def number
64
- date&.year&.to_s
65
- end
66
-
67
- # The index key above is deliberately coarse — a whole volume in one
68
- # bucket — which is only defensible because the MR slug stays
69
- # per-article. The inherited hook reads through `code`, which Bulletin
70
- # does not have, so it would return nil and collapse every article of a
71
- # year onto `oiml.bulletin.<year>`; `to_slug` is an output FILENAME, so
72
- # that is an overwrite. Emit the rest of the locator instead.
63
+ # The inherited hook reads through `code`, which Bulletin does not
64
+ # have, so it would return nil and collapse every article of a year
65
+ # onto `oiml.bulletin.<year>`; `to_slug` is an output FILENAME, so that
66
+ # is an overwrite. Emit the rest of the locator instead.
73
67
  #
74
68
  # The year is deliberately NOT included: Renderers::MrString already
75
69
  # gives it its own segment (`mr_year`), so repeating it here would
76
70
  # render `oiml.bulletin.1960-03-01.1960`.
77
71
  def mr_number_with_part
78
- segments = [issue, sequence].compact
72
+ segments = [number, sequence].compact
79
73
  return nil if segments.empty?
80
74
 
81
75
  segments.join("-")
@@ -96,18 +90,18 @@ module Pubid
96
90
  # Volume as a roman-numeral string ("LXVII"), derived from the year.
97
91
  # nil when the year is absent.
98
92
  def volume_roman
99
- number = volume_arabic&.to_i
100
- return nil unless number&.positive?
93
+ volume = volume_arabic&.to_i
94
+ return nil unless volume&.positive?
101
95
 
102
- self.class.to_roman(number)
96
+ self.class.to_roman(volume)
103
97
  end
104
98
 
105
99
  # 8-digit oiml.org article id ("20260211"). Composed by concatenating
106
- # year + issue + sequence. nil unless all three are present.
100
+ # year + issue (number) + sequence. nil unless all three are present.
107
101
  def article_id
108
- return nil unless date&.year && issue && sequence
102
+ return nil unless date&.year && number && sequence
109
103
 
110
- "#{date.year}#{issue}#{sequence}"
104
+ "#{date.year}#{number}#{sequence}"
111
105
  end
112
106
 
113
107
  class << self
@@ -28,8 +28,8 @@ module Pubid
28
28
  #
29
29
  # Bulletin deliberately does NOT include this module. It carries no code
30
30
  # at all (its locator is the year/issue/sequence tuple), so it would only
31
- # gain a permanently-nil `number` and a `number` key that never appears.
32
- # It derives its index key from the year instead; see bulletin.rb.
31
+ # gain permanently-nil part/subpart/suffix columns. It declares its own
32
+ # `number` (the issue) instead; see bulletin.rb.
33
33
  module CodeNumber
34
34
  def self.included(base)
35
35
  install_attributes(base)
@@ -122,9 +122,14 @@ module Pubid
122
122
  ).as(:edition_format) # Wrap entire match to capture that Edition was used
123
123
  end
124
124
 
125
- # Date - year after colon OR edition portion (with optional space before year)
125
+ # Date - year after colon, the VIM's paren-year form ("OIML V 2-200
126
+ # (2000)" — space + (YYYY) instead of :YYYY, the shape #342 flags as
127
+ # the one real-world spelling that did not parse), OR edition portion
128
+ # (with optional space before year)
126
129
  rule(:date) do
127
- edition_portion | (colon >> space.maybe >> year_digits.as(:year))
130
+ edition_portion |
131
+ (colon >> space.maybe >> year_digits.as(:year)) |
132
+ (space.maybe >> lparen >> year_digits.as(:year) >> rparen)
128
133
  end
129
134
 
130
135
  # Draft stage - WD or CD with optional iteration
@@ -65,7 +65,7 @@ module Pubid
65
65
  result = "#{id.publisher} Bulletin"
66
66
  if id.date&.year
67
67
  result += " #{id.date.year}"
68
- result += "-#{id.issue}" if id.issue
68
+ result += "-#{id.number}" if id.number
69
69
  result += "-#{id.sequence}" if id.sequence
70
70
  end
71
71
  result += " (#{id.language})" if id.language
@@ -77,9 +77,9 @@ module Pubid
77
77
  # structured since OIML's citation format doesn't define volume-only
78
78
  # or issue-only variants.
79
79
  def render_bulletin_citation(id)
80
- return render_bulletin_structured(id) unless id.date&.year && id.issue && id.sequence
80
+ return render_bulletin_structured(id) unless id.date&.year && id.number && id.sequence
81
81
 
82
- "#{id.publisher} Bulletin #{id.volume_roman}(#{id.issue.to_i}) #{id.article_id}"
82
+ "#{id.publisher} Bulletin #{id.volume_roman}(#{id.number.to_i}) #{id.article_id}"
83
83
  end
84
84
 
85
85
  def effective_format(id)
@@ -24,7 +24,7 @@ module Pubid
24
24
  # concrete leaf, because redefining the inherited `number` on this class
25
25
  # — which every leaf inherits — is the multi-flavor determinism landmine.
26
26
  # Keeping the mapping with the attribute is also what lets Bulletin, the
27
- # one leaf with no code, omit both.
27
+ # one leaf with no code, declare its own `number` (the issue) and map it.
28
28
  key_value do
29
29
  map "publisher", to: :publisher
30
30
  map "year", with: { to: :year_to_kv, from: :year_from_kv }
@@ -64,12 +64,12 @@ module Pubid
64
64
  end
65
65
 
66
66
  # Structured locator "YYYY-II-SS" (or "YYYY-II" / "YYYY") for a Bulletin.
67
- # Built directly from the date year and the issue/sequence attributes so
68
- # the URN is identical regardless of whether the input was the structured
69
- # or citation form.
67
+ # Built directly from the date year and the number (the issue) and
68
+ # sequence attributes so the URN is identical regardless of whether the
69
+ # input was the structured or citation form.
70
70
  def bulletin_locator
71
71
  locator = identifier.date.year.to_s
72
- locator += "-#{identifier.issue}" if identifier.issue
72
+ locator += "-#{identifier.number}" if identifier.number
73
73
  locator += "-#{identifier.sequence}" if identifier.sequence
74
74
  locator
75
75
  end
@@ -37,7 +37,9 @@ module Pubid
37
37
  abbr = annotate(abbr, typed_stage_css(@id.typed_stage),
38
38
  annotated: ann)
39
39
  end
40
- subgroup_str = @id.subgroup.render(context:) if @id.subgroup
40
+ if @id.subgroup
41
+ subgroup_str = render_component(@id.subgroup, context)
42
+ end
41
43
 
42
44
  [
43
45
  pub_str,
@@ -20,7 +20,7 @@ module Pubid
20
20
  when :type
21
21
  Components::Type.new(abbr: value.to_s)
22
22
  when :number
23
- Components::Code.new(value: value.to_s)
23
+ value.to_s
24
24
  when :year
25
25
  { date: Components::Date.new(year: value.to_i) }
26
26
  else
@@ -22,7 +22,10 @@ module Pubid
22
22
 
23
23
  attribute :publisher, :string, default: -> { "SAE" }
24
24
  attribute :type, Sae::Components::Type
25
- attribute :number, Sae::Components::Code
25
+ # A plain :string, like every converted flavor. Sae::Components::Code was
26
+ # a bare alias of the shared component and never carried more than
27
+ # `value`, so this is a representation change only.
28
+ attribute :number, :string
26
29
  attribute :date, Sae::Components::Date
27
30
 
28
31
  def self.type
@@ -12,7 +12,7 @@ module Pubid
12
12
  def urn_number
13
13
  return nil unless identifier.number
14
14
 
15
- identifier.number.render(context: URN_CONTEXT)
15
+ identifier.number.to_s
16
16
  end
17
17
 
18
18
  def generate
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.11"
4
+ VERSION = "2.0.0.pre.alpha.12"
5
5
  end
data/lib/tasks/docs.rake CHANGED
@@ -8,17 +8,19 @@ namespace :docs do
8
8
  output_dir = File.join(__dir__, "..", "..", "docs", "identifier-patterns")
9
9
  FileUtils.mkdir_p(output_dir)
10
10
 
11
- flavors = Dir[File.join(__dir__, "..", "..", "lib", "pubid",
12
- "*")].select do |d|
13
- File.directory?(d)
14
- end
15
- .map do |d|
16
- File.basename(d)
17
- end
18
- .reject do |f|
19
- %w[core components rendering
20
- parser serializable utils].include?(f)
21
- end
11
+ lib_root = File.join(__dir__, "..", "..", "lib", "pubid")
12
+ # The registry is the authoritative flavor list. This replaces a
13
+ # filesystem glob over lib/pubid/* directories, whose reject list kept
14
+ # going stale and swept internal modules (parsers, renderers, lutaml,
15
+ # builder, utils, ...) in as "flavors". The module-derived directory
16
+ # (CenCenelec -> cen_cenelec, W3c -> w3c) must exist on disk.
17
+ Pubid.eager_load_flavors!
18
+ flavors = Pubid::Registry.flavor_names.filter_map do |name|
19
+ mod = Pubid::Registry.get(name)
20
+ dir = mod.name.split("::").last
21
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase
22
+ dir if File.directory?(File.join(lib_root, dir))
23
+ end.uniq.sort
22
24
 
23
25
  flavors.each do |flavor|
24
26
  puts "Generating docs for #{flavor}..."
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.11
4
+ version: 2.0.0.pre.alpha.12
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-09-15 00:00:00.000000000 Z
11
+ date: 2026-09-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: lutaml-model
@@ -245,7 +245,6 @@ files:
245
245
  - lib/pubid/ccsds/identifier.rb
246
246
  - lib/pubid/ccsds/identifiers.rb
247
247
  - lib/pubid/ccsds/identifiers/base.rb
248
- - lib/pubid/ccsds/identifiers/base_BASE_88929.rb
249
248
  - lib/pubid/ccsds/identifiers/corrigendum.rb
250
249
  - lib/pubid/ccsds/parser.rb
251
250
  - lib/pubid/ccsds/single_identifier.rb
@@ -557,6 +556,7 @@ files:
557
556
  - lib/pubid/ieee/nesc/parser.rb
558
557
  - lib/pubid/ieee/parser.rb
559
558
  - lib/pubid/ieee/pre_parser.rb
559
+ - lib/pubid/ieee/project_renderer.rb
560
560
  - lib/pubid/ieee/renderer.rb
561
561
  - lib/pubid/ieee/typed_stages.rb
562
562
  - lib/pubid/ieee/urn_generator.rb
@@ -604,7 +604,6 @@ files:
604
604
  - lib/pubid/iso/bundled_identifier.rb
605
605
  - lib/pubid/iso/combined_identifier.rb
606
606
  - lib/pubid/iso/components.rb
607
- - lib/pubid/iso/components/code.rb
608
607
  - lib/pubid/iso/components/publisher.rb
609
608
  - lib/pubid/iso/format_resolver.rb
610
609
  - lib/pubid/iso/identifier.rb
@@ -653,6 +652,7 @@ files:
653
652
  - lib/pubid/itu/identifiers/appendix_of_recommendation.rb
654
653
  - lib/pubid/itu/identifiers/base.rb
655
654
  - lib/pubid/itu/identifiers/combined_identifier.rb
655
+ - lib/pubid/itu/identifiers/contribution.rb
656
656
  - lib/pubid/itu/identifiers/corrigendum.rb
657
657
  - lib/pubid/itu/identifiers/errata.rb
658
658
  - lib/pubid/itu/identifiers/handbook.rb
@@ -1,70 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "lutaml/model"
4
- require_relative "../../serializable"
5
- require_relative "../urn_generator"
6
-
7
- module Pubid
8
- module Ccsds
9
- module Identifiers
10
- # CCSDS identifier
11
- # Format: CCSDS NUMBER.PART-TYPE-EDITION[-SUFFIX]
12
- # Example: CCSDS 120.0-G-4, CCSDS 100.0-G-1-S
13
- class Base < Lutaml::Model::Serializable
14
- include Pubid::Serializable
15
-
16
- # Generate URN for this identifier
17
- #
18
- # @return [String] URN representation
19
- def to_urn
20
- UrnGenerator.new(self).generate
21
- end
22
-
23
- # Include CCSDS-specific attributes in serialization
24
- def base_hash
25
- hash = super
26
- # CCSDS uses plain strings for type, edition, suffix
27
- hash[:type] = type if type
28
- hash[:suffix] = suffix if suffix
29
- hash[:language] = language if language
30
- hash
31
- end
32
-
33
- attribute :number, :string
34
- attribute :part, :string
35
- attribute :type, :string # B, G, M, R, Y, O, etc.
36
- attribute :edition, :string
37
- attribute :suffix, :string # Optional suffix like -S
38
- attribute :language, :string # Language metadata (e.g., "French", "Russian")
39
-
40
- def publisher
41
- "CCSDS"
42
- end
43
-
44
- def to_s
45
- result = "#{publisher} #{number}"
46
- result += ".#{part}" if part
47
- result += "-#{type}" if type
48
- result += "-#{edition}" if edition
49
- result += "-#{suffix}" if suffix
50
-
51
- # Add language metadata
52
- result += " - #{language} Translated" if language
53
-
54
- result
55
- end
56
-
57
- def ==(other)
58
- return false unless other.is_a?(Base)
59
-
60
- number == other.number &&
61
- part == other.part &&
62
- type == other.type &&
63
- edition == other.edition &&
64
- suffix == other.suffix &&
65
- language == other.language
66
- end
67
- end
68
- end
69
- end
70
- end
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Pubid
4
- module Iso
5
- module Components
6
- # Code component for ISO identifiers.
7
- #
8
- # Inherits +value+ and +parts+ from the shared Code; overrides
9
- # rendering to join parts with "-" (ISO convention). Equality and
10
- # hash are inherited from the shared Code (compare all fields).
11
- class Code < ::Pubid::Components::Code
12
- def to_s
13
- result = value.to_s
14
- result += parts.map { |p| "-#{p}" }.join if parts&.any?
15
- result
16
- end
17
-
18
- def render(context: nil)
19
- to_s
20
- end
21
- end
22
- end
23
- end
24
- end