pubid 2.0.4 → 2.0.6
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/bipm/update_codes.yaml +14 -0
- data/data/parg/bipm.json +1 -1
- data/data/parg/bipm.parg +290 -0
- data/data/parg/iho.json +1 -1
- data/data/parg/iho.parg +358 -0
- data/data/parg/jis.json +1 -1
- data/data/parg/parg.json +1 -1
- data/data/parg/parg.parg +118 -0
- data/lib/pubid/bipm/CLAUDE.md +4 -0
- data/lib/pubid/bipm/builder.rb +26 -1
- data/lib/pubid/bipm/identifiers/committee_document.rb +13 -1
- data/lib/pubid/bipm/renderer.rb +18 -3
- data/lib/pubid/bipm/urn_generator.rb +14 -1
- data/lib/pubid/bipm/urn_parser.rb +14 -1
- data/lib/pubid/bipm.rb +10 -4
- data/lib/pubid/bsi/CLAUDE.md +4 -0
- data/lib/pubid/bsi/single_identifier.rb +10 -1
- data/lib/pubid/ieee/builder.rb +6 -1
- data/lib/pubid/ietf/CLAUDE.md +2 -0
- data/lib/pubid/ietf.rb +5 -2
- data/lib/pubid/iho/CLAUDE.md +157 -81
- data/lib/pubid/iho/builder.rb +61 -16
- data/lib/pubid/iho/identifier.rb +2 -2
- data/lib/pubid/iho/identifiers/base.rb +134 -37
- data/lib/pubid/iho/renderer.rb +44 -22
- data/lib/pubid/iho/urn_generator.rb +56 -19
- data/lib/pubid/iho/urn_parser.rb +211 -40
- data/lib/pubid/iso/CLAUDE.md +32 -0
- data/lib/pubid/iso/urn_parser.rb +63 -6
- data/lib/pubid/jis/CLAUDE.md +9 -4
- data/lib/pubid/parg/backend.rb +1 -1
- data/lib/pubid/parg/model.rb +7 -2
- data/lib/pubid/parg.rb +13 -0
- data/lib/pubid/tgpp/CLAUDE.md +2 -0
- data/lib/pubid/tgpp.rb +11 -0
- data/lib/pubid/version.rb +1 -1
- metadata +5 -2
|
@@ -6,10 +6,17 @@ module Pubid
|
|
|
6
6
|
# every concrete IHO identifier (Identifiers::*) descends from it.
|
|
7
7
|
#
|
|
8
8
|
# IHO identifiers have the form:
|
|
9
|
-
# IHO {S|P|M|B|C}-{number}[
|
|
9
|
+
# IHO {S|P|M|B|C}-{number}[ {edition}| Vol. N [(issue)]]
|
|
10
|
+
# [ Sup N][ Part X][ Annex A][ Appendix B[-2]] [(2024)]
|
|
10
11
|
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
12
|
+
# Every tail component is a first-class semantic component: a typed
|
|
13
|
+
# attribute, independently addressable, URN-encodable and matchable.
|
|
14
|
+
# The canonical faces order the edition BEFORE the part
|
|
15
|
+
# (`IHO S-100 1.0.0 Part 2`), print the supplement as "Sup" and the
|
|
16
|
+
# appendix as "Appendix" (with the subnumber after a dash), the
|
|
17
|
+
# volume issue parenthesized (`IHO P-1 Vol. 30 (2)`), and the
|
|
18
|
+
# with-year qualifier parenthesized (`IHO S-100 5.0.0 Part 2 (2024)`
|
|
19
|
+
# — the only year face: a periodical year IS a volume).
|
|
13
20
|
class Identifier < ::Pubid::Identifier
|
|
14
21
|
# Parse an IHO identifier string into an identifier object
|
|
15
22
|
# @param identifier [String] The IHO identifier string to parse
|
|
@@ -25,32 +32,55 @@ module Pubid
|
|
|
25
32
|
raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
|
|
26
33
|
end
|
|
27
34
|
|
|
28
|
-
parsed = Pubid::Parg::
|
|
35
|
+
parsed = Pubid::Parg::Model.bound(:iho, identifier.strip)
|
|
29
36
|
Builder.build(parsed)
|
|
30
37
|
end
|
|
31
38
|
|
|
32
39
|
# `number` overrides the generic Components::Code attribute with a plain
|
|
33
|
-
# string (same pattern as
|
|
34
|
-
# number (e.g. "100", "1/21"
|
|
35
|
-
#
|
|
40
|
+
# string (same pattern as the other string components); it holds the IHO
|
|
41
|
+
# document number (e.g. "100", "1/21" until the slash-chain volume
|
|
42
|
+
# parse, "158:100"). Kept as the generic `number` so the relaton index
|
|
43
|
+
# sorts/binary-searches on it natively via `id.number`.
|
|
36
44
|
attribute :number, :string
|
|
37
45
|
attribute :publisher, :string, default: "IHO"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
attribute :
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
# (P-
|
|
46
|
-
#
|
|
47
|
-
# scalars, like every other IHO attribute; the grammar's tail
|
|
48
|
-
# picks exactly one discriminator, so only one of
|
|
49
|
-
# version/year/volume is ever present, with issue only
|
|
50
|
-
# alongside volume.
|
|
46
|
+
# The official IHO edition is the dotted x.y.z (a plain string — the
|
|
47
|
+
# ~16 string-edition flavors precede this). `IHO S-100 1.0.0` is
|
|
48
|
+
# edition-specific, `IHO S-100` edition-non-specific; a year beside an
|
|
49
|
+
# edition is the with-year qualifier, never a year-keyed docidentifier.
|
|
50
|
+
attribute :edition, :string
|
|
51
|
+
# The with-year qualifier on standards (`(2024)` beside an edition) —
|
|
52
|
+
# the only year component. A periodical year IS a volume: the bare
|
|
53
|
+
# legacy year input ("IHO P-5 2024") binds into `volume`, so this
|
|
54
|
+
# attribute never carries a serial locator.
|
|
51
55
|
attribute :year, :string
|
|
56
|
+
# Supplement, part, annex, appendix and volume are independently
|
|
57
|
+
# publishable (owner ruling: "this can happen with any independently
|
|
58
|
+
# publishable artifacts"), so each carries an optional edition (+ year
|
|
59
|
+
# qualifier) of its own, mutually exclusive with the base edition
|
|
60
|
+
# context: `IHO P-6 1.0.0 Vol. 2` (within-edition) vs
|
|
61
|
+
# `IHO P-6 Vol. 2 1.0.1` (separately-published). The URN attaches
|
|
62
|
+
# ed- to the node it follows, so one positional slot records both.
|
|
63
|
+
attribute :supplement, :string
|
|
64
|
+
attribute :supplement_edition, :string
|
|
65
|
+
attribute :supplement_year, :string
|
|
66
|
+
attribute :part, :string
|
|
67
|
+
attribute :part_edition, :string
|
|
68
|
+
attribute :part_year, :string
|
|
69
|
+
attribute :annex, :string
|
|
70
|
+
attribute :annex_edition, :string
|
|
71
|
+
attribute :annex_year, :string
|
|
72
|
+
# The appendix is a letter or a number; a subnumbered appendix
|
|
73
|
+
# ("Appendix D-2") keeps the letter in `appendix` and the "2" in
|
|
74
|
+
# `appendix_subnumber` — two independently addressable components,
|
|
75
|
+
# not one string.
|
|
76
|
+
attribute :appendix, :string
|
|
77
|
+
attribute :appendix_subnumber, :string
|
|
78
|
+
attribute :appendix_edition, :string
|
|
79
|
+
attribute :appendix_year, :string
|
|
52
80
|
attribute :volume, :string
|
|
53
81
|
attribute :issue, :string
|
|
82
|
+
attribute :volume_edition, :string
|
|
83
|
+
attribute :volume_year, :string
|
|
54
84
|
|
|
55
85
|
# Polymorphic type map for lutaml::Model key_value serialization: maps
|
|
56
86
|
# each subclass's polymorphic_name to its class name so `from_hash` can
|
|
@@ -71,31 +101,98 @@ module Pubid
|
|
|
71
101
|
key_value do
|
|
72
102
|
map "_type", to: :_type, polymorphic_map: IHO_TYPE_MAP
|
|
73
103
|
map "number", to: :number
|
|
74
|
-
map "
|
|
75
|
-
map "part", to: :part
|
|
76
|
-
map "annex", to: :annex
|
|
77
|
-
map "supplement", to: :supplement
|
|
78
|
-
map "version", to: :version
|
|
104
|
+
map "edition", to: :edition
|
|
79
105
|
map "year", to: :year
|
|
80
|
-
map "
|
|
81
|
-
map "
|
|
106
|
+
map "supplement", to: :supplement
|
|
107
|
+
map "supplement_edition", to: :supplement_edition
|
|
108
|
+
map "supplement_year", to: :supplement_year
|
|
109
|
+
map "part", to: :part
|
|
110
|
+
map "part_edition", to: :part_edition
|
|
111
|
+
map "part_year", to: :part_year
|
|
112
|
+
map "annex", to: :annex
|
|
113
|
+
map "annex_edition", to: :annex_edition
|
|
114
|
+
map "annex_year", to: :annex_year
|
|
115
|
+
map "appendix", to: :appendix
|
|
116
|
+
map "appendix_subnumber", to: :appendix_subnumber
|
|
117
|
+
map "appendix_edition", to: :appendix_edition
|
|
118
|
+
map "appendix_year", to: :appendix_year
|
|
119
|
+
map "volume", to: :volume
|
|
120
|
+
map "volume_edition", to: :volume_edition
|
|
121
|
+
map "volume_year", to: :volume_year
|
|
122
|
+
map "issue", to: :issue
|
|
82
123
|
end
|
|
83
124
|
|
|
84
125
|
# from_hash is the shared polymorphic dispatch on Pubid::Identifier.
|
|
85
|
-
# IHO_TYPE_MAP remains
|
|
86
|
-
|
|
87
|
-
# The volume/issue
|
|
88
|
-
# the
|
|
89
|
-
# vs "P-1 Vol. 30 (2)"), so excluding
|
|
90
|
-
#
|
|
91
|
-
# :year -> :date. A lone
|
|
92
|
-
#
|
|
93
|
-
# alias nils whichever name the
|
|
126
|
+
# IHO_TYPE_MAP remains the key_value polymorphic_map.
|
|
127
|
+
|
|
128
|
+
# The volume/issue and appendix/subnumber pairs are scalar clusters:
|
|
129
|
+
# a reference that omits the finer component must still match a hit
|
|
130
|
+
# that carries one ("P-1 Vol. 30" vs "P-1 Vol. 30 (2)"), so excluding
|
|
131
|
+
# the coarser component nils the finer one with it — the same
|
|
132
|
+
# additive-alias shape the base applies to :year -> :date. A lone
|
|
133
|
+
# scalar :year needs no override here: the base nils a declared :year
|
|
134
|
+
# directly (the additive :year -> :date alias nils whichever name the
|
|
135
|
+
# flavor declares).
|
|
94
136
|
def exclude(*args)
|
|
137
|
+
args = args.dup
|
|
95
138
|
args << :issue if args.include?(:volume)
|
|
139
|
+
args << :appendix_subnumber if args.include?(:appendix)
|
|
96
140
|
super
|
|
97
141
|
end
|
|
98
|
-
end
|
|
99
142
|
|
|
143
|
+
# Render the compact face: the code-adjacent components folded onto
|
|
144
|
+
# the code with separators — the supplement and the part hyphenated
|
|
145
|
+
# (`IHO M-2-2` for `IHO M-2 Sup 2`, `IHO S-5-B` for `IHO S-5 Part B`,
|
|
146
|
+
# `IHO S-100-1` for `IHO S-100 Part 1`) and the volume as the slash
|
|
147
|
+
# chain (`IHO P-5/2024` for `IHO P-5 Vol. 2024`). The canonical
|
|
148
|
+
# render stays expanded; compact is opt-in. The legacy faces (glued
|
|
149
|
+
# lettered bases, Suppl, bare years) parse but are never rendered in
|
|
150
|
+
# any form.
|
|
151
|
+
def render(format: :human, **opts)
|
|
152
|
+
compact = opts.delete(:compact)
|
|
153
|
+
result = super
|
|
154
|
+
compact && format == :human ? compact_face(result) : result
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# The shared mr_string path reads `edition&.number` (the base
|
|
158
|
+
# declares edition as Components::Edition); IHO's edition is a plain
|
|
159
|
+
# string, so the segment is built from it directly.
|
|
160
|
+
def mr_edition
|
|
161
|
+
return nil unless edition
|
|
162
|
+
|
|
163
|
+
"ed#{edition}"
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
private
|
|
167
|
+
|
|
168
|
+
# Fold " Sup N" / " Part X" / " Vol. N" into the code as "-N" /
|
|
169
|
+
# "-X" / "/N", keeping everything else in place (the edition
|
|
170
|
+
# follows the compact code, as in the legacy "S-5A 1.0.2" print
|
|
171
|
+
# order). The issue keeps its parenthesized print.
|
|
172
|
+
def compact_face(rendered)
|
|
173
|
+
result = rendered.sub(/\A#{publisher} #{base_code}(?= |$)/,
|
|
174
|
+
"#{publisher} #{compact_code}")
|
|
175
|
+
folded_segments.each { |segment| result = result.sub(segment, "") }
|
|
176
|
+
result
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def base_code
|
|
180
|
+
"#{self.class.type[:short]}-#{number}"
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def compact_code
|
|
184
|
+
code = base_code.dup
|
|
185
|
+
code << "-#{supplement}" if supplement
|
|
186
|
+
code << "-#{part}" if part
|
|
187
|
+
code << "/#{volume}" if volume
|
|
188
|
+
code
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def folded_segments
|
|
192
|
+
[supplement && " Sup #{supplement}",
|
|
193
|
+
part && " Part #{part}",
|
|
194
|
+
volume && " Vol. #{volume}"].compact
|
|
195
|
+
end
|
|
196
|
+
end
|
|
100
197
|
end
|
|
101
198
|
end
|
data/lib/pubid/iho/renderer.rb
CHANGED
|
@@ -4,33 +4,55 @@ module Pubid
|
|
|
4
4
|
module Iho
|
|
5
5
|
# Human-readable renderer for IHO identifiers.
|
|
6
6
|
#
|
|
7
|
-
#
|
|
8
|
-
# "IHO S-
|
|
9
|
-
# "IHO
|
|
10
|
-
# "IHO S-100
|
|
11
|
-
# "IHO S-
|
|
12
|
-
# "IHO
|
|
13
|
-
#
|
|
7
|
+
# Canonical faces (owner rulings, 2026-11-03):
|
|
8
|
+
# "IHO S-100" undated (edition-non-specific)
|
|
9
|
+
# "IHO S-100 1.0.0" dated (edition-specific)
|
|
10
|
+
# "IHO S-100 5.0.0 Part 2" edition BEFORE the part
|
|
11
|
+
# "IHO S-98 1.0.0 Part A Annex B"
|
|
12
|
+
# "IHO S-122 1.0.0 Appendix D-2" canonical Appendix, subnumber
|
|
13
|
+
# joined by a dash
|
|
14
|
+
# "IHO M-2 Sup 1" canonical Sup (Suppl is an input
|
|
15
|
+
# alias)
|
|
16
|
+
# "IHO P-1 Vol. 30 (2)" volume-keyed series (P-1, P-6)
|
|
17
|
+
# "IHO P-5 Vol. 2024" the yearbook year IS a volume
|
|
18
|
+
# "IHO S-100 5.0.0 Part 2 (2024)" the with-year qualifier: a year
|
|
19
|
+
# beside an edition is the date
|
|
20
|
+
# qualifier on standards,
|
|
21
|
+
# parenthesized — the only year face
|
|
14
22
|
#
|
|
15
23
|
# The renderer is registered as the `:human` format in the IHO format
|
|
16
|
-
# registry and invoked via `render(format: :human)
|
|
24
|
+
# registry and invoked via `render(format: :human)`; the compact face
|
|
25
|
+
# (`IHO S-100-1 5.0.0`, `IHO M-2-2`, `IHO P-5/2024`) is the opt-in
|
|
26
|
+
# `compact: true` flag, and `with_publisher: false` drops the leading
|
|
27
|
+
# publisher as everywhere else. The legacy faces (glued lettered
|
|
28
|
+
# bases, Suppl, bare years) parse but are never rendered.
|
|
17
29
|
class Renderer < ::Pubid::Renderers::Base
|
|
30
|
+
# The canonical component faces in print order: edition BEFORE the
|
|
31
|
+
# part; each independently-publishable component carries its own
|
|
32
|
+
# edition (+ parenthesized year) right after it; the appendix joins
|
|
33
|
+
# its subnumber; the issue rides parenthesized after its volume;
|
|
34
|
+
# the base year prints only as the parenthesized with-year
|
|
35
|
+
# qualifier.
|
|
36
|
+
FACES = [
|
|
37
|
+
[:edition, " %s"],
|
|
38
|
+
[:supplement, " Sup %s"], [:supplement_edition, " %s"],
|
|
39
|
+
[:supplement_year, " (%s)"],
|
|
40
|
+
[:part, " Part %s"], [:part_edition, " %s"], [:part_year, " (%s)"],
|
|
41
|
+
[:annex, " Annex %s"], [:annex_edition, " %s"], [:annex_year, " (%s)"],
|
|
42
|
+
[:appendix, " Appendix %s"], [:appendix_subnumber, "-%s"],
|
|
43
|
+
[:appendix_edition, " %s"], [:appendix_year, " (%s)"],
|
|
44
|
+
[:volume, " Vol. %s"], [:issue, " (%s)"],
|
|
45
|
+
[:volume_edition, " %s"], [:volume_year, " (%s)"],
|
|
46
|
+
[:year, " (%s)"]
|
|
47
|
+
].freeze
|
|
48
|
+
|
|
18
49
|
def render(context: nil, **opts)
|
|
19
50
|
id = @id
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
rendered << " Annex #{id.annex}" if id.annex
|
|
26
|
-
rendered << " Suppl #{id.supplement}" if id.supplement
|
|
27
|
-
# The grammar's tail picks exactly one discriminator: a dotted
|
|
28
|
-
# version, a year, or a volume (with an optional issue). The
|
|
29
|
-
# issue prints parenthesized; "Issue N" is an input alias only.
|
|
30
|
-
rendered << " #{id.version}" if id.version
|
|
31
|
-
rendered << " #{id.year}" if id.year
|
|
32
|
-
rendered << " Vol. #{id.volume}" if id.volume
|
|
33
|
-
rendered << " (#{id.issue})" if id.issue
|
|
51
|
+
rendered = "#{id.publisher} #{id.class.type[:short]}-#{id.number}"
|
|
52
|
+
FACES.each do |component, face|
|
|
53
|
+
value = id.public_send(component)
|
|
54
|
+
rendered << (face % value) if value
|
|
55
|
+
end
|
|
34
56
|
rendered
|
|
35
57
|
end
|
|
36
58
|
end
|
|
@@ -4,16 +4,33 @@ module Pubid
|
|
|
4
4
|
module Iho
|
|
5
5
|
# Generates URN strings for IHO identifiers.
|
|
6
6
|
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
# "IHO P-1 Vol. 30 (2)").
|
|
7
|
+
# The URN is the documented positional segment grammar (see the
|
|
8
|
+
# grammar header in pubid-grammar's grammars/iho.parg and the flavor
|
|
9
|
+
# notes): containment-ordered, one semantic atom per segment, every
|
|
10
|
+
# non-number component a marker-prefixed segment whose marker is the
|
|
11
|
+
# lowercased human-face token (edition follows ISO's ed- precedent):
|
|
13
12
|
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
13
|
+
# urn:iho:<type>:<number>[:ed-<x.y.z>][:sup-<n>][:part-<X>]
|
|
14
|
+
# [:annex-<A>][:appendix-<B>][:<sub>][:vol-<n>][:issue-<n>]
|
|
15
|
+
# [:year-<yyyy>]
|
|
16
|
+
#
|
|
17
|
+
# Containment: part contains annexes, annex contains appendices, the
|
|
18
|
+
# subnumber is a child atom of its appendix, the issue a child of its
|
|
19
|
+
# volume. Letters are case-preserved. The volume carries every serial
|
|
20
|
+
# locator — the yearbook year IS a volume (P-5 Vol. 2024 gives
|
|
21
|
+
# vol-2024), so a periodical identifier has no year segment; the
|
|
22
|
+
# year- segment exists only for the standards with-year qualifier.
|
|
23
|
+
# Both are marker-tagged so neither can collide with a colon-number
|
|
24
|
+
# segment (the number is not escaped and may span bare segments:
|
|
25
|
+
# "IHO S-158:100" gives "urn:iho:s:158:100").
|
|
26
|
+
#
|
|
27
|
+
# Examples:
|
|
28
|
+
# - urn:iho:s:44:ed-5.0.0 IHO S-44 5.0.0
|
|
29
|
+
# - urn:iho:s:100:ed-5.0.0:part-2:year-2024 IHO S-100 5.0.0 Part 2 (2024)
|
|
30
|
+
# - urn:iho:s:122:ed-1.0.0:appendix-D:2 IHO S-122 1.0.0 Appendix D-2
|
|
31
|
+
# - urn:iho:m:2:sup-1 IHO M-2 Sup 1
|
|
32
|
+
# - urn:iho:p:5:vol-2024 IHO P-5 Vol. 2024
|
|
33
|
+
# - urn:iho:p:1:vol-30:issue-2 IHO P-1 Vol. 30 (2)
|
|
17
34
|
class UrnGenerator
|
|
18
35
|
attr_reader :identifier
|
|
19
36
|
|
|
@@ -21,22 +38,42 @@ module Pubid
|
|
|
21
38
|
@identifier = identifier
|
|
22
39
|
end
|
|
23
40
|
|
|
41
|
+
# [component, marker] pairs in the containment/canonical-face
|
|
42
|
+
# order. A component edition/year emits right after its component
|
|
43
|
+
# (the ed- segment attaches to the node it follows, recording the
|
|
44
|
+
# separately-published mode). nil markers emit their component as
|
|
45
|
+
# a BARE atom (the appendix subnumber directly after its
|
|
46
|
+
# appendix).
|
|
47
|
+
SEGMENTS = [
|
|
48
|
+
[:edition, "ed"], [:year, "year"],
|
|
49
|
+
[:supplement, "sup"], [:supplement_edition, "ed"],
|
|
50
|
+
[:supplement_year, "year"],
|
|
51
|
+
[:part, "part"], [:part_edition, "ed"], [:part_year, "year"],
|
|
52
|
+
[:annex, "annex"], [:annex_edition, "ed"], [:annex_year, "year"],
|
|
53
|
+
[:appendix, "appendix"], [:appendix_subnumber, nil],
|
|
54
|
+
[:appendix_edition, "ed"], [:appendix_year, "year"],
|
|
55
|
+
[:volume, "vol"], [:issue, "issue"], [:volume_edition, "ed"],
|
|
56
|
+
[:volume_year, "year"]
|
|
57
|
+
].freeze
|
|
58
|
+
|
|
24
59
|
def generate
|
|
25
60
|
parts = ["urn", "iho"]
|
|
26
61
|
parts << identifier.class.type[:short].to_s.downcase
|
|
27
62
|
parts << identifier.number.to_s
|
|
28
|
-
parts
|
|
29
|
-
parts << "part.#{identifier.part}" if identifier.part
|
|
30
|
-
parts << "annex.#{identifier.annex}" if identifier.annex
|
|
31
|
-
parts << "suppl.#{identifier.supplement}" if identifier.supplement
|
|
32
|
-
# The grammar's tail picks exactly one discriminator; a volume
|
|
33
|
-
# may carry an issue beside it.
|
|
34
|
-
parts << "vol.#{identifier.volume}" if identifier.volume
|
|
35
|
-
parts << "issue.#{identifier.issue}" if identifier.issue
|
|
36
|
-
parts << identifier.version.to_s if identifier.version
|
|
37
|
-
parts << "y.#{identifier.year}" if identifier.year
|
|
63
|
+
parts.concat(component_segments)
|
|
38
64
|
parts.join(":")
|
|
39
65
|
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
def component_segments
|
|
70
|
+
SEGMENTS.filter_map do |component, marker|
|
|
71
|
+
value = identifier.public_send(component)
|
|
72
|
+
next unless value
|
|
73
|
+
|
|
74
|
+
marker ? "#{marker}-#{value}" : value.to_s
|
|
75
|
+
end
|
|
76
|
+
end
|
|
40
77
|
end
|
|
41
78
|
end
|
|
42
79
|
end
|