pubid 2.0.0.pre.alpha.5 → 2.0.0.pre.alpha.7
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/README.adoc +40 -0
- data/lib/pubid/amca.rb +6 -0
- data/lib/pubid/ansi.rb +7 -0
- data/lib/pubid/api.rb +5 -0
- data/lib/pubid/ashrae.rb +6 -0
- data/lib/pubid/asme.rb +5 -0
- data/lib/pubid/astm.rb +5 -0
- data/lib/pubid/bsi/builder.rb +41 -48
- data/lib/pubid/bsi/identifiers/amendment.rb +28 -0
- data/lib/pubid/bsi/identifiers/consolidated_identifier.rb +11 -1
- data/lib/pubid/bsi/identifiers/corrigendum.rb +24 -0
- data/lib/pubid/bsi/identifiers/expert_commentary.rb +5 -0
- data/lib/pubid/bsi/identifiers/flex.rb +4 -0
- data/lib/pubid/bsi/parser.rb +19 -5
- data/lib/pubid/bsi/renderer.rb +19 -17
- data/lib/pubid/bsi.rb +15 -0
- data/lib/pubid/ccsds.rb +5 -0
- data/lib/pubid/cen_cenelec.rb +7 -0
- data/lib/pubid/cie.rb +5 -0
- data/lib/pubid/csa.rb +5 -0
- data/lib/pubid/etsi.rb +7 -0
- data/lib/pubid/iala/builder.rb +107 -0
- data/lib/pubid/iala/identifier.rb +67 -0
- data/lib/pubid/iala/identifiers/advice.rb +15 -0
- data/lib/pubid/iala/identifiers/annex.rb +55 -0
- data/lib/pubid/iala/identifiers/base.rb +14 -0
- data/lib/pubid/iala/identifiers/general_assembly.rb +16 -0
- data/lib/pubid/iala/identifiers/guideline.rb +15 -0
- data/lib/pubid/iala/identifiers/letter.rb +16 -0
- data/lib/pubid/iala/identifiers/manual.rb +16 -0
- data/lib/pubid/iala/identifiers/model_course.rb +15 -0
- data/lib/pubid/iala/identifiers/recommendation.rb +15 -0
- data/lib/pubid/iala/identifiers/report.rb +16 -0
- data/lib/pubid/iala/identifiers/resolution.rb +16 -0
- data/lib/pubid/iala/identifiers/standard.rb +15 -0
- data/lib/pubid/iala/identifiers.rb +20 -0
- data/lib/pubid/iala/parser.rb +130 -0
- data/lib/pubid/iala/renderer.rb +36 -0
- data/lib/pubid/iala/urn_generator.rb +49 -0
- data/lib/pubid/iala/urn_parser.rb +50 -0
- data/lib/pubid/iala.rb +71 -0
- data/lib/pubid/identifier.rb +54 -1
- data/lib/pubid/idf.rb +5 -0
- data/lib/pubid/iec.rb +8 -0
- data/lib/pubid/ieee.rb +8 -0
- data/lib/pubid/iho.rb +5 -0
- data/lib/pubid/iso.rb +6 -0
- data/lib/pubid/itu.rb +5 -0
- data/lib/pubid/jcgm.rb +5 -0
- data/lib/pubid/jis.rb +6 -0
- data/lib/pubid/nist.rb +13 -0
- data/lib/pubid/oiml/builder.rb +33 -0
- data/lib/pubid/oiml/identifier.rb +1 -0
- data/lib/pubid/oiml/identifiers/bulletin.rb +101 -0
- data/lib/pubid/oiml/identifiers.rb +1 -0
- data/lib/pubid/oiml/parser.rb +43 -1
- data/lib/pubid/oiml/renderer.rb +51 -0
- data/lib/pubid/oiml/urn_generator.rb +22 -0
- data/lib/pubid/oiml.rb +5 -0
- data/lib/pubid/plateau.rb +5 -0
- data/lib/pubid/prefixes_support.rb +51 -0
- data/lib/pubid/sae.rb +6 -0
- data/lib/pubid/version.rb +1 -1
- data/lib/pubid.rb +69 -0
- metadata +24 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 28589e7efce00bb93f0cabd35bb4169210a69396e1da6e1869c51fec7dd80450
|
|
4
|
+
data.tar.gz: 181f33478261d666dc3bf93488326f0c88d999da0d958822bc49b5e9b113b5bd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 23924b813af8f851b79fcb39ff90133df69f9219559d4bbc15f6bd46540c63ef4bcd5ba5eccf1ba013a774b7c8e4b0bfdb11d9fb308db0ac1a2c1df8f005a0b6
|
|
7
|
+
data.tar.gz: 38d8b970836cfb889c4b1bef346daac97b340656e652ab4d1cd687e7ff7436c73c976404204be54ac2e18ab37cd6982d634e4b5d9a08255ae400d5418b2023e3
|
data/README.adoc
CHANGED
|
@@ -292,6 +292,46 @@ base.root.equal?(base) # => true
|
|
|
292
292
|
* ✅ **Works with supplements** - Traverses supplement chains correctly
|
|
293
293
|
|
|
294
294
|
|
|
295
|
+
== Flavor Prefix Routing
|
|
296
|
+
|
|
297
|
+
Every flavor exposes a static `prefixes` method returning the *leading
|
|
298
|
+
identifier prefix tokens* it recognizes — the tokens a printed identifier can
|
|
299
|
+
start with such that the string belongs to that SDO. This is the routing key a
|
|
300
|
+
consumer (such as relaton) uses to dispatch a reference string to the right
|
|
301
|
+
backend, without parsing.
|
|
302
|
+
|
|
303
|
+
[source,ruby]
|
|
304
|
+
----
|
|
305
|
+
require 'pubid'
|
|
306
|
+
|
|
307
|
+
Pubid::Iso.prefixes
|
|
308
|
+
# => ["ISO", "ISO/IEC", "IEC/ISO", "ISO/IEC/IEEE"]
|
|
309
|
+
|
|
310
|
+
Pubid::Bsi.prefixes
|
|
311
|
+
# => ["BS", "BSI", "DD", "PD", "PAS", "NA", "TS", "HB", ... , "BS EN ISO"]
|
|
312
|
+
|
|
313
|
+
# Look up a single flavor by name
|
|
314
|
+
Pubid.prefixes(:iec)
|
|
315
|
+
# => ["IEC", "CISPR", "IECEE", "IECEx", "IECQ", "ISO/IEC", "IEC/ISO", "ISO/IEC/IEEE"]
|
|
316
|
+
|
|
317
|
+
# Reverse index: prefix token => owning flavor(s)
|
|
318
|
+
Pubid.prefix_flavors["ISO/IEC"] # => [:iec, :iso]
|
|
319
|
+
Pubid.prefix_flavors["ISO/IEC/IEEE"] # => [:iec, :ieee, :iso]
|
|
320
|
+
Pubid.prefix_flavors["DD"] # => [:bsi]
|
|
321
|
+
----
|
|
322
|
+
|
|
323
|
+
**Semantics** (see `Pubid::PrefixesSupport` for the full contract):
|
|
324
|
+
|
|
325
|
+
* **Static** — no identifier string is parsed; callable at startup.
|
|
326
|
+
* **Uniform** — every registered flavor returns a plain `Array<String>`.
|
|
327
|
+
* **Symmetric joints** — a co-published prefix is listed by every co-publisher
|
|
328
|
+
(`"ISO/IEC"` is in both `Pubid::Iso.prefixes` and `Pubid::Iec.prefixes`).
|
|
329
|
+
* **Inclusion** — publisher prefixes (`ISO`, `NIST`, `BS`…) and leading
|
|
330
|
+
series/type tokens that can *begin* a reference (BSI `DD`/`PD`, NIST `FIPS`).
|
|
331
|
+
* **Exclusion** — pure sub-series that never lead a reference and
|
|
332
|
+
dangerously-ambiguous single letters (e.g. BSI aerospace `A`/`B`/`C`).
|
|
333
|
+
|
|
334
|
+
|
|
295
335
|
== Advanced Rendering Styles
|
|
296
336
|
|
|
297
337
|
ISO and IEC identifiers support multiple abbreviation forms for supplements to maintain round-trip fidelity with different official formats.
|
data/lib/pubid/amca.rb
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
module Pubid
|
|
4
4
|
module Amca
|
|
5
|
+
extend Pubid::PrefixesSupport
|
|
6
|
+
|
|
7
|
+
# AMCA publisher token; the ANSI/AMCA joint form comes from
|
|
8
|
+
# Pubid::JOINT_PREFIXES.
|
|
9
|
+
PREFIXES = ["AMCA"].freeze
|
|
10
|
+
|
|
5
11
|
autoload :Identifier, "#{__dir__}/amca/identifier"
|
|
6
12
|
autoload :Identifiers, "#{__dir__}/amca/identifiers"
|
|
7
13
|
autoload :Builder, "#{__dir__}/amca/builder"
|
data/lib/pubid/ansi.rb
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
module Pubid
|
|
4
4
|
module Ansi
|
|
5
|
+
extend Pubid::PrefixesSupport
|
|
6
|
+
|
|
7
|
+
# ANSI publisher token; ANSI/ASHRAE and ANSI/AMCA joint forms come from
|
|
8
|
+
# Pubid::JOINT_PREFIXES. Parser::ORGANIZATIONS are co-publishers, not
|
|
9
|
+
# leading ANSI tokens, so they are excluded.
|
|
10
|
+
PREFIXES = ["ANSI"].freeze
|
|
11
|
+
|
|
5
12
|
autoload :Builder, "#{__dir__}/ansi/builder"
|
|
6
13
|
autoload :Identifier, "#{__dir__}/ansi/identifier"
|
|
7
14
|
autoload :Identifiers, "#{__dir__}/ansi/identifiers"
|
data/lib/pubid/api.rb
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
module Pubid
|
|
4
4
|
module Api
|
|
5
|
+
extend Pubid::PrefixesSupport
|
|
6
|
+
|
|
7
|
+
# Sole API publisher token (see the parser's `publisher` rule).
|
|
8
|
+
PREFIXES = ["API"].freeze
|
|
9
|
+
|
|
5
10
|
autoload :Builder, "#{__dir__}/api/builder"
|
|
6
11
|
autoload :Identifier, "#{__dir__}/api/identifier"
|
|
7
12
|
autoload :Identifiers, "#{__dir__}/api/identifiers"
|
data/lib/pubid/ashrae.rb
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
module Pubid
|
|
4
4
|
module Ashrae
|
|
5
|
+
extend Pubid::PrefixesSupport
|
|
6
|
+
|
|
7
|
+
# ASHRAE publisher token; the ANSI/ASHRAE joint form comes from
|
|
8
|
+
# Pubid::JOINT_PREFIXES.
|
|
9
|
+
PREFIXES = ["ASHRAE"].freeze
|
|
10
|
+
|
|
5
11
|
autoload :Builder, "#{__dir__}/ashrae/builder"
|
|
6
12
|
autoload :Identifier, "#{__dir__}/ashrae/identifier"
|
|
7
13
|
autoload :Identifiers, "#{__dir__}/ashrae/identifiers"
|
data/lib/pubid/asme.rb
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
module Pubid
|
|
4
4
|
module Asme
|
|
5
|
+
extend Pubid::PrefixesSupport
|
|
6
|
+
|
|
7
|
+
# Sole ASME publisher token (see the parser's `asme_publisher` rule).
|
|
8
|
+
PREFIXES = ["ASME"].freeze
|
|
9
|
+
|
|
5
10
|
autoload :Builder, "#{__dir__}/asme/builder"
|
|
6
11
|
autoload :Components, "#{__dir__}/asme/components/code"
|
|
7
12
|
autoload :Identifier, "#{__dir__}/asme/identifier"
|
data/lib/pubid/astm.rb
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
module Pubid
|
|
4
4
|
module Astm
|
|
5
|
+
extend Pubid::PrefixesSupport
|
|
6
|
+
|
|
7
|
+
# Sole ASTM publisher token (see the parser's `publisher` rule).
|
|
8
|
+
PREFIXES = ["ASTM"].freeze
|
|
9
|
+
|
|
5
10
|
autoload :Components, "#{__dir__}/astm/components"
|
|
6
11
|
autoload :Identifier, "#{__dir__}/astm/identifier"
|
|
7
12
|
autoload :Identifiers, "#{__dir__}/astm/identifiers"
|
data/lib/pubid/bsi/builder.rb
CHANGED
|
@@ -114,19 +114,11 @@ module Pubid
|
|
|
114
114
|
if data[:na_prefix]
|
|
115
115
|
return build_national_annex(data, supplements_data)
|
|
116
116
|
elsif data[:adopted_string]
|
|
117
|
-
#
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
#
|
|
121
|
-
|
|
122
|
-
identifier = wrap_with_consolidated(identifier,
|
|
123
|
-
supplements_data)
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
# Wrap with ExpertCommentary if needed
|
|
127
|
-
identifier = wrap_with_expert_commentary(identifier) if data[:expert_commentary]
|
|
128
|
-
|
|
129
|
-
return identifier
|
|
117
|
+
# Adopted identifiers carry their supplements and Expert-commentary
|
|
118
|
+
# suffix *inside* the adopted string, so build_adopted_identifier does
|
|
119
|
+
# all the wrapping. Wrapping again here would double-wrap (a second
|
|
120
|
+
# ExpertCommentary around the first) and break #base_document.
|
|
121
|
+
return build_adopted_identifier(data)
|
|
130
122
|
end
|
|
131
123
|
|
|
132
124
|
# Determine identifier class using the module's lookup helpers
|
|
@@ -139,8 +131,11 @@ module Pubid
|
|
|
139
131
|
supplements_data)
|
|
140
132
|
end
|
|
141
133
|
|
|
142
|
-
# Wrap with ExpertCommentary if needed
|
|
143
|
-
|
|
134
|
+
# Wrap with ExpertCommentary if needed (full "Expert Commentary" form
|
|
135
|
+
# arrives as the standalone :expert_commentary_full token)
|
|
136
|
+
if data[:expert_commentary] || data[:expert_commentary_full]
|
|
137
|
+
identifier = wrap_with_expert_commentary(identifier)
|
|
138
|
+
end
|
|
144
139
|
|
|
145
140
|
identifier
|
|
146
141
|
end
|
|
@@ -922,6 +917,7 @@ module Pubid
|
|
|
922
917
|
number: (supp_data[:amd_number] || supp_data[:cor_number])&.to_s,
|
|
923
918
|
year: (supp_data[:amd_year] || supp_data[:cor_year])&.to_s,
|
|
924
919
|
separator: separator,
|
|
920
|
+
amd_suffix_form: !(supp_data[:amd_sep_plus] || supp_data[:amd_sep_slash]),
|
|
925
921
|
}
|
|
926
922
|
end
|
|
927
923
|
end
|
|
@@ -1157,8 +1153,10 @@ module Pubid
|
|
|
1157
1153
|
# Extract ExComm suffix before parsing (it should be preserved in data[:expert_commentary])
|
|
1158
1154
|
# Remove it from adopted_string so ISO/IEC parsers don't choke on it
|
|
1159
1155
|
# Handle all three formats: "Expert Commentary", "ExComm", "ExComm (Fire)"
|
|
1160
|
-
|
|
1161
|
-
|
|
1156
|
+
# Expert-commentary suffix matching is case-insensitive; output is always
|
|
1157
|
+
# canonicalised to "Expert Commentary" / "ExComm".
|
|
1158
|
+
if adopted_str.match?(/expert commentary$/i)
|
|
1159
|
+
adopted_str = adopted_str.sub(/expert commentary$/i, "")
|
|
1162
1160
|
data[:expert_commentary_full] = "Expert Commentary"
|
|
1163
1161
|
data[:expert_commentary] = true unless data.key?(:expert_commentary)
|
|
1164
1162
|
# Update @original_data so wrap_with_expert_commentary can access it
|
|
@@ -1167,8 +1165,8 @@ module Pubid
|
|
|
1167
1165
|
@original_data[:expert_commentary] =
|
|
1168
1166
|
true
|
|
1169
1167
|
end
|
|
1170
|
-
elsif adopted_str.
|
|
1171
|
-
adopted_str = adopted_str.sub(/
|
|
1168
|
+
elsif adopted_str.match?(/excomm \($/i)
|
|
1169
|
+
adopted_str = adopted_str.sub(/excomm \(.*\)$/i, "")
|
|
1172
1170
|
data[:expert_commentary_full] = "Expert Commentary"
|
|
1173
1171
|
data[:expert_commentary] = true unless data.key?(:expert_commentary)
|
|
1174
1172
|
@original_data[:expert_commentary_full] = "Expert Commentary"
|
|
@@ -1176,10 +1174,10 @@ module Pubid
|
|
|
1176
1174
|
@original_data[:expert_commentary] =
|
|
1177
1175
|
true
|
|
1178
1176
|
end
|
|
1179
|
-
elsif adopted_str.
|
|
1177
|
+
elsif adopted_str.match?(/excomm \(/i)
|
|
1180
1178
|
# Extract topic from "ExComm (Fire)"
|
|
1181
|
-
topic_match = adopted_str.match(/
|
|
1182
|
-
adopted_str = adopted_str.sub(/
|
|
1179
|
+
topic_match = adopted_str.match(/excomm\s*\(([^)]+)\)/i)
|
|
1180
|
+
adopted_str = adopted_str.sub(/excomm\s*\(.*\)$/i, "")
|
|
1183
1181
|
data[:expert_commentary_topic] = topic_match[1] if topic_match
|
|
1184
1182
|
data[:expert_commentary] = true unless data.key?(:expert_commentary)
|
|
1185
1183
|
if topic_match
|
|
@@ -1190,9 +1188,9 @@ module Pubid
|
|
|
1190
1188
|
@original_data[:expert_commentary] =
|
|
1191
1189
|
true
|
|
1192
1190
|
end
|
|
1193
|
-
elsif adopted_str.match?(/
|
|
1191
|
+
elsif adopted_str.match?(/excomm\s*$/i)
|
|
1194
1192
|
# Abbreviated form "ExComm" at the end
|
|
1195
|
-
adopted_str = adopted_str.sub(/
|
|
1193
|
+
adopted_str = adopted_str.sub(/excomm\s*$/i, "")
|
|
1196
1194
|
data[:expert_commentary] = true unless data.key?(:expert_commentary)
|
|
1197
1195
|
unless @original_data.key?(:expert_commentary)
|
|
1198
1196
|
@original_data[:expert_commentary] =
|
|
@@ -1258,9 +1256,10 @@ module Pubid
|
|
|
1258
1256
|
# BSI uses +A1:2020 format, but ISO/IEC parsers can't handle this
|
|
1259
1257
|
# Patterns: +A1:2020, +A11:2021, +AMD1:2001, +C1:2020
|
|
1260
1258
|
extracted_supplements = []
|
|
1261
|
-
# Match patterns like: +A1:2020, +A11:2021, +AMD1:2001, +C1:2020, +COR1:2020
|
|
1259
|
+
# Match patterns like: +A1:2020, +A11:2021, +AMD1:2001, +C1:2020, +COR1:2020,
|
|
1260
|
+
# and the year-less compact forms +A2 / +C1 (the ":YYYY" is optional).
|
|
1262
1261
|
# Must be at end of string or followed by space (not colon, which is part of date)
|
|
1263
|
-
adopted_str_clean = adopted_str_clean.gsub(/([+])(A(\d+)|AMD(\d+)|C(\d+)|COR(\d+))
|
|
1262
|
+
adopted_str_clean = adopted_str_clean.gsub(/([+])(A(\d+)|AMD(\d+)|C(\d+)|COR(\d+))(?::(\d{4}))?(?:\s|$)/) do |_match|
|
|
1264
1263
|
separator = $1 # "+" or "/"
|
|
1265
1264
|
type_code = $2 || $3 || $4 || $5 || $6
|
|
1266
1265
|
$2 || $3 || $4 || $5 || $6
|
|
@@ -1285,6 +1284,8 @@ module Pubid
|
|
|
1285
1284
|
number: supp_number,
|
|
1286
1285
|
year: year,
|
|
1287
1286
|
separator: separator,
|
|
1287
|
+
# compact "+A"/"/A" join form (not the trailing " AMD5" suffix form)
|
|
1288
|
+
amd_suffix_form: false,
|
|
1288
1289
|
}
|
|
1289
1290
|
|
|
1290
1291
|
"" # Remove from adopted_string
|
|
@@ -1300,6 +1301,7 @@ module Pubid
|
|
|
1300
1301
|
number: amd_number,
|
|
1301
1302
|
year: nil,
|
|
1302
1303
|
separator: nil, # No separator for AMD without year
|
|
1304
|
+
amd_suffix_form: true, # trailing " AMD5" suffix form
|
|
1303
1305
|
}
|
|
1304
1306
|
"" # Remove from adopted_string
|
|
1305
1307
|
end.strip
|
|
@@ -1365,6 +1367,9 @@ module Pubid
|
|
|
1365
1367
|
# Return appropriate wrapper based on adoption type
|
|
1366
1368
|
if adopted_id
|
|
1367
1369
|
# If adopted_id is a CEN identifier (in Cen module), use AdoptedEuropeanNorm
|
|
1370
|
+
# The Expert-commentary suffix is represented uniformly by an outer
|
|
1371
|
+
# ExpertCommentary wrapper (below), never a boolean on the inner norm,
|
|
1372
|
+
# so that #base_document peels cleanly to the bare standard.
|
|
1368
1373
|
identifier = if adopted_id.class.name.start_with?("Pubid::CenCenelec::")
|
|
1369
1374
|
Identifiers::AdoptedEuropeanNorm.new(
|
|
1370
1375
|
publisher: Components::Publisher.new(body: bsi_prefix),
|
|
@@ -1372,10 +1377,6 @@ module Pubid
|
|
|
1372
1377
|
edition: final_edition&.to_s,
|
|
1373
1378
|
translation_lang: data[:translation_lang]&.to_s,
|
|
1374
1379
|
translation_upper: data[:translation_upper]&.to_s,
|
|
1375
|
-
# Pass expert_commentary data so ConsolidatedIdentifier can render it
|
|
1376
|
-
expert_commentary: data[:expert_commentary],
|
|
1377
|
-
# Pass expert_commentary_topic if present
|
|
1378
|
-
expert_commentary_topic: data[:expert_commentary_topic],
|
|
1379
1380
|
# Pass translation_suffix_type for rendering
|
|
1380
1381
|
translation_suffix_type: data[:translation_suffix_type]&.to_s,
|
|
1381
1382
|
# Pass reaffirmation_year for rendering
|
|
@@ -1389,11 +1390,6 @@ module Pubid
|
|
|
1389
1390
|
edition: final_edition&.to_s,
|
|
1390
1391
|
translation_lang: data[:translation_lang]&.to_s,
|
|
1391
1392
|
translation_upper: data[:translation_upper]&.to_s,
|
|
1392
|
-
# Pass expert_commentary data ONLY if no supplements
|
|
1393
|
-
# When supplements are present, ConsolidatedIdentifier will add ExComm later
|
|
1394
|
-
expert_commentary: data[:expert_commentary] && extracted_supplements.none?,
|
|
1395
|
-
# Pass expert_commentary_topic if present and no supplements
|
|
1396
|
-
expert_commentary_topic: (data[:expert_commentary_topic] if extracted_supplements.none?),
|
|
1397
1393
|
# Pass translation_suffix_type for rendering
|
|
1398
1394
|
translation_suffix_type: data[:translation_suffix_type]&.to_s,
|
|
1399
1395
|
# Pass reaffirmation_year for rendering
|
|
@@ -1401,20 +1397,13 @@ module Pubid
|
|
|
1401
1397
|
)
|
|
1402
1398
|
end
|
|
1403
1399
|
|
|
1404
|
-
#
|
|
1405
|
-
#
|
|
1406
|
-
#
|
|
1400
|
+
# Wrap supplements first (they render before the ExComm suffix), then
|
|
1401
|
+
# wrap the whole thing in a single ExpertCommentary — same shape as the
|
|
1402
|
+
# pure-BSI path, keeping #base_document uniform.
|
|
1407
1403
|
if extracted_supplements.any?
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
identifier,
|
|
1412
|
-
extracted_supplements,
|
|
1413
|
-
expert_commentary: data[:expert_commentary],
|
|
1414
|
-
expert_commentary_topic: data[:expert_commentary_topic],
|
|
1415
|
-
)
|
|
1416
|
-
elsif data[:expert_commentary]
|
|
1417
|
-
# When no supplements but ExComm present, wrap with ExpertCommentary
|
|
1404
|
+
identifier = wrap_with_consolidated(identifier, extracted_supplements)
|
|
1405
|
+
end
|
|
1406
|
+
if data[:expert_commentary] || data[:expert_commentary_full]
|
|
1418
1407
|
identifier = wrap_with_expert_commentary(identifier)
|
|
1419
1408
|
end
|
|
1420
1409
|
|
|
@@ -1451,6 +1440,7 @@ expert_commentary: nil, expert_commentary_topic: nil)
|
|
|
1451
1440
|
amendment_number: supp[:number],
|
|
1452
1441
|
amendment_year: year_val&.to_i,
|
|
1453
1442
|
separator: supp[:separator] || "+",
|
|
1443
|
+
amd_suffix_form: supp[:amd_suffix_form] ? true : false,
|
|
1454
1444
|
)
|
|
1455
1445
|
else
|
|
1456
1446
|
Identifiers::Corrigendum.new(
|
|
@@ -1484,6 +1474,9 @@ expert_commentary: nil, expert_commentary_topic: nil)
|
|
|
1484
1474
|
number: (supp_data[:amd_number] || supp_data[:cor_number])&.to_s,
|
|
1485
1475
|
year: (supp_data[:amd_year] || supp_data[:cor_year])&.to_s,
|
|
1486
1476
|
separator: separator,
|
|
1477
|
+
# The compact "+A"/"/A" amendment rule captures an explicit separator;
|
|
1478
|
+
# the trailing " AMD5" suffix form does not.
|
|
1479
|
+
amd_suffix_form: !(supp_data[:amd_sep_plus] || supp_data[:amd_sep_slash]),
|
|
1487
1480
|
}
|
|
1488
1481
|
end
|
|
1489
1482
|
end
|
|
@@ -10,10 +10,38 @@ module Pubid
|
|
|
10
10
|
attribute :amendment_number, :string
|
|
11
11
|
attribute :amendment_year, :integer
|
|
12
12
|
attribute :separator, :string, default: -> { "+" }
|
|
13
|
+
# true for the trailing " AMD5" / " AMD AA" suffix form, false for the
|
|
14
|
+
# compact "+A5" / "/A5" join form. Distinguishes the two when no year is
|
|
15
|
+
# present (previously the presence of a year was a reliable proxy).
|
|
16
|
+
attribute :amd_suffix_form, :boolean, default: -> { false }
|
|
13
17
|
|
|
14
18
|
def publisher
|
|
15
19
|
base_identifier&.publisher
|
|
16
20
|
end
|
|
21
|
+
|
|
22
|
+
# Base document = the standard this amendment applies to, fully peeled.
|
|
23
|
+
def base_document
|
|
24
|
+
base_identifier&.base_document || self
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Dropping the supplement layer yields the base standard.
|
|
28
|
+
def drop_supplements
|
|
29
|
+
base_identifier || self
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Uniform supplement interface (shared with Corrigendum) so callers need
|
|
33
|
+
# not special-case the class.
|
|
34
|
+
def supplement_type
|
|
35
|
+
:amendment
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def supplement_number
|
|
39
|
+
amendment_number
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def supplement_year
|
|
43
|
+
amendment_year
|
|
44
|
+
end
|
|
17
45
|
end
|
|
18
46
|
end
|
|
19
47
|
end
|
|
@@ -6,7 +6,7 @@ module Pubid
|
|
|
6
6
|
# Consolidated Identifier - contains base document plus supplements
|
|
7
7
|
# Example: "BS 4592-0:2006+A1:2012" = [BS 4592-0:2006, Amendment 1:2012]
|
|
8
8
|
class ConsolidatedIdentifier < SingleIdentifier
|
|
9
|
-
attribute :identifiers, ::Pubid::Identifier, polymorphic: true,
|
|
9
|
+
attribute :identifiers, ::Pubid::Identifier, polymorphic: true,
|
|
10
10
|
collection: true
|
|
11
11
|
|
|
12
12
|
def to_urn
|
|
@@ -62,6 +62,16 @@ module Pubid
|
|
|
62
62
|
base = identifiers&.first
|
|
63
63
|
base.type if base&.class&.attributes&.key?(:type)
|
|
64
64
|
end
|
|
65
|
+
|
|
66
|
+
# Base document = the wrapped standard (first entry), fully peeled.
|
|
67
|
+
def base_document
|
|
68
|
+
identifiers&.first&.base_document || self
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Dropping the supplement layer yields the base standard alone.
|
|
72
|
+
def drop_supplements
|
|
73
|
+
identifiers&.first || self
|
|
74
|
+
end
|
|
65
75
|
end
|
|
66
76
|
end
|
|
67
77
|
end
|
|
@@ -14,6 +14,30 @@ module Pubid
|
|
|
14
14
|
def publisher
|
|
15
15
|
base_identifier&.publisher
|
|
16
16
|
end
|
|
17
|
+
|
|
18
|
+
# Base document = the standard this corrigendum applies to, fully peeled.
|
|
19
|
+
def base_document
|
|
20
|
+
base_identifier&.base_document || self
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Dropping the supplement layer yields the base standard.
|
|
24
|
+
def drop_supplements
|
|
25
|
+
base_identifier || self
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Uniform supplement interface (shared with Amendment) so callers need
|
|
29
|
+
# not special-case the class.
|
|
30
|
+
def supplement_type
|
|
31
|
+
:corrigendum
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def supplement_number
|
|
35
|
+
corrigendum_number
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def supplement_year
|
|
39
|
+
corrigendum_year
|
|
40
|
+
end
|
|
17
41
|
end
|
|
18
42
|
end
|
|
19
43
|
end
|
data/lib/pubid/bsi/parser.rb
CHANGED
|
@@ -5,6 +5,19 @@ require "parslet"
|
|
|
5
5
|
module Pubid
|
|
6
6
|
module Bsi
|
|
7
7
|
class Parser < Parslet::Parser
|
|
8
|
+
# Case-insensitive string match (Parslet 2.0 has no built-in `stri`).
|
|
9
|
+
# Each character becomes a two-case character class; other characters
|
|
10
|
+
# (spaces, punctuation) match verbatim.
|
|
11
|
+
def stri(string)
|
|
12
|
+
string.each_char.map do |char|
|
|
13
|
+
if char =~ /[a-z]/i
|
|
14
|
+
match["#{char.upcase}#{char.downcase}"]
|
|
15
|
+
else
|
|
16
|
+
str(char)
|
|
17
|
+
end
|
|
18
|
+
end.reduce(:>>)
|
|
19
|
+
end
|
|
20
|
+
|
|
8
21
|
rule(:digit) { match["0-9"] }
|
|
9
22
|
rule(:digits) { digit.repeat(1) }
|
|
10
23
|
rule(:space) { str(" ") }
|
|
@@ -296,14 +309,15 @@ module Pubid
|
|
|
296
309
|
# Single letter (a-z or A-Z) that comes immediately after number or part
|
|
297
310
|
rule(:letter_edition) { match["a-zA-Z"].as(:letter_edition) }
|
|
298
311
|
|
|
299
|
-
# Amendment (+A1:2008 or /A2:2019 or /Amd 1:2020 or +A1:15 for short year
|
|
312
|
+
# Amendment (+A1:2008 or /A2:2019 or /Amd 1:2020 or +A1:15 for short year,
|
|
313
|
+
# or +A2 / /A2 without a year — mirrors the optional-year corrigendum rule)
|
|
300
314
|
rule(:amendment) do
|
|
301
315
|
((plus.as(:amd_sep_plus) | slash.as(:amd_sep_slash)) >>
|
|
302
316
|
str("A") >> digits.as(:amd_number) >>
|
|
303
|
-
colon >> digit.repeat(2, 4).as(:amd_year)) |
|
|
317
|
+
(colon >> digit.repeat(2, 4).as(:amd_year)).maybe) |
|
|
304
318
|
((plus.as(:amd_sep_plus) | slash.as(:amd_sep_slash)) >>
|
|
305
319
|
str("Amd") >> space >> digits.as(:amd_number) >>
|
|
306
|
-
colon >> digit.repeat(4, 4).as(:amd_year))
|
|
320
|
+
(colon >> digit.repeat(4, 4).as(:amd_year)).maybe)
|
|
307
321
|
end
|
|
308
322
|
|
|
309
323
|
# Corrigendum (+AC:2009 or +AC1:2008 or /AC1:2005 or +C1:2018 or +C1 without year)
|
|
@@ -518,8 +532,8 @@ module Pubid
|
|
|
518
532
|
# 3. "ExComm (Fire)" (with optional topic suffix)
|
|
519
533
|
rule(:expert_commentary) do
|
|
520
534
|
space >> (
|
|
521
|
-
|
|
522
|
-
(
|
|
535
|
+
stri("Expert Commentary").as(:expert_commentary_full) |
|
|
536
|
+
(stri("ExComm") >> (space >> str("(") >> match["A-Za-z"].repeat(1).as(:expert_commentary_topic) >> str(")")).maybe).as(:expert_commentary)
|
|
523
537
|
)
|
|
524
538
|
end
|
|
525
539
|
|
data/lib/pubid/bsi/renderer.rb
CHANGED
|
@@ -289,20 +289,21 @@ module Pubid
|
|
|
289
289
|
end
|
|
290
290
|
|
|
291
291
|
def render_amendment(id)
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
"#{
|
|
302
|
-
elsif id.amendment_number&.match?(/^[A-Z]+$/)
|
|
303
|
-
"AMD #{id.amendment_number}"
|
|
292
|
+
base = id.base_identifier ? id.base_identifier.to_s : ""
|
|
293
|
+
|
|
294
|
+
if id.amd_suffix_form
|
|
295
|
+
# Trailing " AMD5" / " AMD AA" suffix form
|
|
296
|
+
suffix = if id.amendment_number&.match?(/^[A-Z]+$/)
|
|
297
|
+
" AMD #{id.amendment_number}"
|
|
298
|
+
else
|
|
299
|
+
" AMD#{id.amendment_number}"
|
|
300
|
+
end
|
|
301
|
+
base.empty? ? suffix.lstrip : "#{base}#{suffix}"
|
|
304
302
|
else
|
|
305
|
-
|
|
303
|
+
# Compact "+A5" / "/A5" join form, with an optional ":year"
|
|
304
|
+
compact = "#{id.separator}A#{id.amendment_number}"
|
|
305
|
+
compact += ":#{id.amendment_year}" if id.amendment_year
|
|
306
|
+
"#{base}#{compact}"
|
|
306
307
|
end
|
|
307
308
|
end
|
|
308
309
|
|
|
@@ -408,15 +409,16 @@ module Pubid
|
|
|
408
409
|
|
|
409
410
|
id.identifiers[1..].each do |idd|
|
|
410
411
|
if idd.is_a?(Identifiers::Amendment)
|
|
411
|
-
if idd.
|
|
412
|
-
sep = idd.separator || "+"
|
|
413
|
-
result += "#{sep}A#{idd.amendment_number}:#{idd.amendment_year}"
|
|
414
|
-
else
|
|
412
|
+
if idd.amd_suffix_form
|
|
415
413
|
result += if idd.amendment_number&.match?(/^[A-Z]+$/)
|
|
416
414
|
" AMD #{idd.amendment_number}"
|
|
417
415
|
else
|
|
418
416
|
" AMD#{idd.amendment_number}"
|
|
419
417
|
end
|
|
418
|
+
else
|
|
419
|
+
sep = idd.separator || "+"
|
|
420
|
+
result += "#{sep}A#{idd.amendment_number}"
|
|
421
|
+
result += ":#{idd.amendment_year}" if idd.amendment_year
|
|
420
422
|
end
|
|
421
423
|
elsif idd.is_a?(Identifiers::Corrigendum)
|
|
422
424
|
sep = idd.separator || "+"
|
data/lib/pubid/bsi.rb
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
module Pubid
|
|
4
4
|
module Bsi
|
|
5
|
+
extend Pubid::PrefixesSupport
|
|
6
|
+
|
|
7
|
+
# Leading tokens a printed BSI reference can start with, curated from the
|
|
8
|
+
# parser grammar (lib/pubid/bsi/parser.rb): the publisher (`bs` => BS/BSI),
|
|
9
|
+
# the native series/type rules that begin references (dd, pd, pas, na, ts,
|
|
10
|
+
# handbook => HB, pp, bip, ep_type), and the "BS <adopted-org>" combos the
|
|
11
|
+
# grammar builds from `bs` + `adopted_org_prefix`. Deliberately EXCLUDES the
|
|
12
|
+
# aerospace single/multi-letter prefixes (see
|
|
13
|
+
# identifiers/aerospace_standard.rb SINGLE_LETTER_PREFIXES / the parser's
|
|
14
|
+
# `single_letter_prefix` rule — A/B/C/S/X ...) because they are dangerously
|
|
15
|
+
# ambiguous and never lead a routable reference on their own; and the bare
|
|
16
|
+
# adopted forms (ISO/IEC/EN alone), which route to their own SDOs.
|
|
17
|
+
PREFIXES = (%w[BS BSI DD PD PAS NA TS HB PP BIP EP] +
|
|
18
|
+
["BS EN", "BS ISO", "BS IEC", "BS EN ISO", "BS EN IEC"]).freeze
|
|
19
|
+
|
|
5
20
|
module Components
|
|
6
21
|
autoload :Code, "#{__dir__}/bsi/components/code"
|
|
7
22
|
autoload :Date, "#{__dir__}/bsi/components/date"
|
data/lib/pubid/ccsds.rb
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
module Pubid
|
|
4
4
|
module Ccsds
|
|
5
|
+
extend Pubid::PrefixesSupport
|
|
6
|
+
|
|
7
|
+
# Sole CCSDS publisher token (see the parser's `ccsds_prefix` rule).
|
|
8
|
+
PREFIXES = ["CCSDS"].freeze
|
|
9
|
+
|
|
5
10
|
autoload :Builder, "#{__dir__}/ccsds/builder"
|
|
6
11
|
autoload :Identifier, "#{__dir__}/ccsds/identifier"
|
|
7
12
|
autoload :Identifiers, "#{__dir__}/ccsds/identifiers"
|
data/lib/pubid/cen_cenelec.rb
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
module Pubid
|
|
4
4
|
module CenCenelec
|
|
5
|
+
extend Pubid::PrefixesSupport
|
|
6
|
+
|
|
7
|
+
# CEN/CENELEC publisher tokens, mirroring the parser's publisher rules
|
|
8
|
+
# (lib/pubid/cen_cenelec/parser.rb). Adopted-org prefixes (ISO/IEC/CISPR)
|
|
9
|
+
# are excluded — they route to their own SDOs, not to CEN.
|
|
10
|
+
PREFIXES = %w[EN CEN CLC CWA HD ES CR ENV].freeze
|
|
11
|
+
|
|
5
12
|
autoload :Builder, "#{__dir__}/cen_cenelec/builder"
|
|
6
13
|
autoload :Identifier, "#{__dir__}/cen_cenelec/identifier"
|
|
7
14
|
autoload :Identifiers, "#{__dir__}/cen_cenelec/identifiers"
|
data/lib/pubid/cie.rb
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
module Pubid
|
|
4
4
|
module Cie
|
|
5
|
+
extend Pubid::PrefixesSupport
|
|
6
|
+
|
|
7
|
+
# Sole CIE publisher token (see the parser grammar).
|
|
8
|
+
PREFIXES = ["CIE"].freeze
|
|
9
|
+
|
|
5
10
|
module Components
|
|
6
11
|
autoload :Code, "#{__dir__}/cie/components/code"
|
|
7
12
|
autoload :Language, "#{__dir__}/cie/components/language"
|
data/lib/pubid/csa.rb
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
module Pubid
|
|
4
4
|
module Csa
|
|
5
|
+
extend Pubid::PrefixesSupport
|
|
6
|
+
|
|
7
|
+
# Sole CSA publisher token (see the parser's `publisher` rule).
|
|
8
|
+
PREFIXES = ["CSA"].freeze
|
|
9
|
+
|
|
5
10
|
autoload :Builder, "#{__dir__}/csa/builder"
|
|
6
11
|
autoload :Identifier, "#{__dir__}/csa/identifier"
|
|
7
12
|
autoload :Identifiers, "#{__dir__}/csa/identifiers"
|