pubid 2.0.0.pre.alpha.6 → 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.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 +62 -6
- data/lib/pubid/iala/identifier.rb +11 -7
- data/lib/pubid/iala/identifiers/advice.rb +15 -0
- data/lib/pubid/iala/identifiers/annex.rb +55 -0
- data/lib/pubid/iala/identifiers/general_assembly.rb +16 -0
- data/lib/pubid/iala/identifiers/letter.rb +16 -0
- data/lib/pubid/iala/identifiers.rb +12 -8
- data/lib/pubid/iala/parser.rb +39 -8
- data/lib/pubid/iala/renderer.rb +14 -0
- data/lib/pubid/iala/urn_generator.rb +17 -0
- data/lib/pubid/iala.rb +5 -0
- 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 +68 -0
- metadata +7 -1
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.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"
|
data/lib/pubid/etsi.rb
CHANGED
|
@@ -4,6 +4,13 @@ require "lutaml/model"
|
|
|
4
4
|
|
|
5
5
|
module Pubid
|
|
6
6
|
module Etsi
|
|
7
|
+
extend Pubid::PrefixesSupport
|
|
8
|
+
|
|
9
|
+
# Sole ETSI publisher token (see the parser's `etsi_prefix` rule). The
|
|
10
|
+
# document-type tokens (EN, TS, TR, ...) are sub-types that follow the ETSI
|
|
11
|
+
# prefix, not standalone routing tokens, so they are excluded.
|
|
12
|
+
PREFIXES = ["ETSI"].freeze
|
|
13
|
+
|
|
7
14
|
autoload :Builder, "#{__dir__}/etsi/builder"
|
|
8
15
|
autoload :Components, "#{__dir__}/etsi/components"
|
|
9
16
|
autoload :Identifier, "#{__dir__}/etsi/identifier"
|
data/lib/pubid/iala/builder.rb
CHANGED
|
@@ -6,10 +6,13 @@ module Pubid
|
|
|
6
6
|
class Builder
|
|
7
7
|
def build(parsed)
|
|
8
8
|
hash = parsed
|
|
9
|
+
|
|
10
|
+
return build_annex(hash) if hash[:annex_marker]
|
|
11
|
+
|
|
9
12
|
type_letter = stringify(hash[:type_letter])&.upcase
|
|
10
13
|
|
|
11
14
|
attrs = {
|
|
12
|
-
number: build_number(hash),
|
|
15
|
+
number: build_number(hash, type_letter),
|
|
13
16
|
edition: stringify(hash.dig(:edition, :edition_value)),
|
|
14
17
|
language: stringify(hash.dig(:language_group, :language))&.upcase,
|
|
15
18
|
}.compact
|
|
@@ -23,13 +26,66 @@ module Pubid
|
|
|
23
26
|
|
|
24
27
|
private
|
|
25
28
|
|
|
26
|
-
#
|
|
27
|
-
|
|
29
|
+
# Build the Annex wrapper from the parse tree. The base sub-tree
|
|
30
|
+
# (captured under :base) is recursively built via the same logic as
|
|
31
|
+
# a flat identifier, then wrapped.
|
|
32
|
+
def build_annex(hash)
|
|
33
|
+
base_hash = hash[:base]
|
|
34
|
+
base_type = stringify(base_hash[:type_letter])&.upcase
|
|
35
|
+
base_number = build_number(base_hash, base_type)
|
|
36
|
+
base = Iala.identifier_klass_for_type_letter(base_type).new(number: base_number)
|
|
37
|
+
|
|
38
|
+
attrs = {
|
|
39
|
+
base_identifier: base,
|
|
40
|
+
annex_form: stringify(hash[:annex_marker]),
|
|
41
|
+
letter: stringify(hash[:annex_letter]),
|
|
42
|
+
edition: stringify(hash.dig(:edition, :edition_value)),
|
|
43
|
+
language: stringify(hash.dig(:language_group, :language))&.upcase,
|
|
44
|
+
}.compact
|
|
45
|
+
|
|
46
|
+
Identifiers::Annex.new(**attrs)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Compose the full number string from the leading doc_number plus any
|
|
50
|
+
# dotted continuation (GA01.13 → "01.13", L2.1.11 → "2.1.11") plus
|
|
51
|
+
# the optional dash-separated subpart (C0103-1 → "0103-1", L2.7.1-2
|
|
52
|
+
# → "2.7.1-2"). Padding is type-aware per IALA's canonical form:
|
|
53
|
+
# typed S/R/G/M/C zero-pad to 4 digits; GeneralAssembly (GA) zero-
|
|
54
|
+
# pads each segment to 2; Advices (A) and Letters (L) preserve input
|
|
55
|
+
# (their canonical form is already what the IALA site prints).
|
|
56
|
+
def build_number(hash, type_letter = nil)
|
|
28
57
|
base = stringify(hash[:doc_number])
|
|
29
|
-
return base unless
|
|
58
|
+
return base unless base
|
|
59
|
+
|
|
60
|
+
base = canonical_base(base, type_letter)
|
|
61
|
+
|
|
62
|
+
dots = hash[:doc_number_dots]
|
|
63
|
+
dots_str = dots.is_a?(Array) ? "" : canonical_dots(stringify(dots).to_s, type_letter)
|
|
64
|
+
|
|
65
|
+
subpart_str = hash[:subpart] ? "-#{subpart_to_s(hash[:subpart])}" : ""
|
|
66
|
+
subpart_str = "" if subpart_str == "-"
|
|
67
|
+
"#{base}#{dots_str}#{subpart_str}"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Per-type canonical width for the leading numeric run.
|
|
71
|
+
def canonical_base(base, type_letter)
|
|
72
|
+
case type_letter
|
|
73
|
+
when "S", "R", "G", "M", "C", "X", "P" then base.rjust(4, "0")
|
|
74
|
+
when "GA" then base.rjust(2, "0")
|
|
75
|
+
else base
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Per-type canonical width for each dotted continuation segment.
|
|
80
|
+
# `str` looks like ".13" or ".1.11" (leading dot, then digits).
|
|
81
|
+
def canonical_dots(str, type_letter)
|
|
82
|
+
return "" if str.empty?
|
|
30
83
|
|
|
31
|
-
|
|
32
|
-
|
|
84
|
+
if type_letter == "GA"
|
|
85
|
+
str.split(".", -1).map { |seg| seg.empty? ? seg : seg.rjust(2, "0") }.join(".")
|
|
86
|
+
else
|
|
87
|
+
str
|
|
88
|
+
end
|
|
33
89
|
end
|
|
34
90
|
|
|
35
91
|
# The subpart capture may be an array of {subpart_number: "1"},
|
|
@@ -33,13 +33,17 @@ module Pubid
|
|
|
33
33
|
attribute :language, :string # "E", "F", "S", "C", "A", "R", nil
|
|
34
34
|
|
|
35
35
|
IALA_TYPE_MAP = {
|
|
36
|
-
"pubid:iala:standard"
|
|
37
|
-
"pubid:iala:recommendation"
|
|
38
|
-
"pubid:iala:guideline"
|
|
39
|
-
"pubid:iala:manual"
|
|
40
|
-
"pubid:iala:model-course"
|
|
41
|
-
"pubid:iala:
|
|
42
|
-
"pubid:iala:
|
|
36
|
+
"pubid:iala:standard" => "Pubid::Iala::Identifiers::Standard",
|
|
37
|
+
"pubid:iala:recommendation" => "Pubid::Iala::Identifiers::Recommendation",
|
|
38
|
+
"pubid:iala:guideline" => "Pubid::Iala::Identifiers::Guideline",
|
|
39
|
+
"pubid:iala:manual" => "Pubid::Iala::Identifiers::Manual",
|
|
40
|
+
"pubid:iala:model-course" => "Pubid::Iala::Identifiers::ModelCourse",
|
|
41
|
+
"pubid:iala:advice" => "Pubid::Iala::Identifiers::Advice",
|
|
42
|
+
"pubid:iala:general-assembly" => "Pubid::Iala::Identifiers::GeneralAssembly",
|
|
43
|
+
"pubid:iala:letter" => "Pubid::Iala::Identifiers::Letter",
|
|
44
|
+
"pubid:iala:annex" => "Pubid::Iala::Identifiers::Annex",
|
|
45
|
+
"pubid:iala:report" => "Pubid::Iala::Identifiers::Report",
|
|
46
|
+
"pubid:iala:resolution" => "Pubid::Iala::Identifiers::Resolution",
|
|
43
47
|
}.freeze
|
|
44
48
|
|
|
45
49
|
key_value do
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pubid
|
|
4
|
+
module Iala
|
|
5
|
+
module Identifiers
|
|
6
|
+
# IALA Advices (A prefix). Numbering is dashed: A12-01, A13-04.
|
|
7
|
+
# Carries no edition in the corpus.
|
|
8
|
+
class Advice < Base
|
|
9
|
+
def self.type
|
|
10
|
+
{ key: :advice, title: "Advice", short: "A" }
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pubid
|
|
4
|
+
module Iala
|
|
5
|
+
module Identifiers
|
|
6
|
+
# IALA Annex to a base publication. Two surface forms appear in the
|
|
7
|
+
# corpus, both wrapping a Guideline:
|
|
8
|
+
#
|
|
9
|
+
# "G1045 Annex Ed 1" — bare Annex, no letter
|
|
10
|
+
# "G1128 ANNEX A Ed 1.6" — uppercase + letter (A/B/C/D)
|
|
11
|
+
#
|
|
12
|
+
# The case of the marker is preserved (Annex vs ANNEX) for round-trip
|
|
13
|
+
# fidelity. Edition and language apply to the Annex itself, not the base.
|
|
14
|
+
class Annex < Base
|
|
15
|
+
# The publication this annex supplements. Always present; carries the
|
|
16
|
+
# type letter and number (e.g. G1045).
|
|
17
|
+
attribute :base_identifier, ::Pubid::Iala::Identifier, polymorphic: true
|
|
18
|
+
# "Annex" or "ANNEX" — preserved verbatim for exact round-trip.
|
|
19
|
+
attribute :annex_form, :string
|
|
20
|
+
# "A", "B", "C", "D", or nil for the bare form.
|
|
21
|
+
attribute :letter, :string
|
|
22
|
+
|
|
23
|
+
key_value do
|
|
24
|
+
map "base_identifier",
|
|
25
|
+
with: { to: :base_identifier_to_kv,
|
|
26
|
+
from: :base_identifier_from_kv }
|
|
27
|
+
map "annex_form", to: :annex_form
|
|
28
|
+
map "letter", to: :letter
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.type
|
|
32
|
+
{ key: :annex, title: "Annex", short: nil }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def base_identifier_to_kv(model, doc)
|
|
38
|
+
base = model.base_identifier
|
|
39
|
+
return unless base
|
|
40
|
+
|
|
41
|
+
doc.add_child(
|
|
42
|
+
Lutaml::KeyValue::DataModel::Element.new("base_identifier",
|
|
43
|
+
base.to_hash),
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def base_identifier_from_kv(model, value)
|
|
48
|
+
return unless value
|
|
49
|
+
|
|
50
|
+
model.base_identifier = ::Pubid::Iala::Identifier.from_hash(value)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pubid
|
|
4
|
+
module Iala
|
|
5
|
+
module Identifiers
|
|
6
|
+
# IALA General Assembly resolutions (GA prefix). Numbering is dotted
|
|
7
|
+
# with a series and an index: GA01.01, GA01.13. No edition.
|
|
8
|
+
class GeneralAssembly < Base
|
|
9
|
+
def self.type
|
|
10
|
+
{ key: :"general-assembly", title: "General Assembly Resolution",
|
|
11
|
+
short: "GA" }
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pubid
|
|
4
|
+
module Iala
|
|
5
|
+
module Identifiers
|
|
6
|
+
# IALA Letters (L prefix). Numbering is dotted with a series, sub-series,
|
|
7
|
+
# and item: L2.1.11, L2.7.1-2 (the trailing -2 is a sub-part on top of
|
|
8
|
+
# the dotted number).
|
|
9
|
+
class Letter < Base
|
|
10
|
+
def self.type
|
|
11
|
+
{ key: :letter, title: "Letter", short: "L" }
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -3,14 +3,18 @@
|
|
|
3
3
|
module Pubid
|
|
4
4
|
module Iala
|
|
5
5
|
module Identifiers
|
|
6
|
-
autoload :Base,
|
|
7
|
-
autoload :
|
|
8
|
-
autoload :
|
|
9
|
-
autoload :
|
|
10
|
-
autoload :
|
|
11
|
-
autoload :
|
|
12
|
-
autoload :
|
|
13
|
-
autoload :
|
|
6
|
+
autoload :Base, "#{__dir__}/identifiers/base"
|
|
7
|
+
autoload :Advice, "#{__dir__}/identifiers/advice"
|
|
8
|
+
autoload :Annex, "#{__dir__}/identifiers/annex"
|
|
9
|
+
autoload :Standard, "#{__dir__}/identifiers/standard"
|
|
10
|
+
autoload :Recommendation, "#{__dir__}/identifiers/recommendation"
|
|
11
|
+
autoload :Guideline, "#{__dir__}/identifiers/guideline"
|
|
12
|
+
autoload :Manual, "#{__dir__}/identifiers/manual"
|
|
13
|
+
autoload :ModelCourse, "#{__dir__}/identifiers/model_course"
|
|
14
|
+
autoload :GeneralAssembly, "#{__dir__}/identifiers/general_assembly"
|
|
15
|
+
autoload :Letter, "#{__dir__}/identifiers/letter"
|
|
16
|
+
autoload :Report, "#{__dir__}/identifiers/report"
|
|
17
|
+
autoload :Resolution, "#{__dir__}/identifiers/resolution"
|
|
14
18
|
end
|
|
15
19
|
end
|
|
16
20
|
end
|
data/lib/pubid/iala/parser.rb
CHANGED
|
@@ -28,16 +28,32 @@ module Pubid
|
|
|
28
28
|
rule(:lparen) { str("(") }
|
|
29
29
|
rule(:rparen) { str(")") }
|
|
30
30
|
|
|
31
|
-
# Type
|
|
32
|
-
# route to the right
|
|
31
|
+
# Type prefix — IALA document classes. GA must be tried before G so it
|
|
32
|
+
# isn't left with a dangling "A". Captured to route to the right
|
|
33
|
+
# Identifiers::* subclass.
|
|
33
34
|
rule(:type_letter) do
|
|
34
|
-
(str("S") | str("R") | str("G") | str("M") |
|
|
35
|
-
str("C") | str("X") | str("P")).as(:type_letter)
|
|
35
|
+
(str("GA") | str("S") | str("R") | str("G") | str("M") |
|
|
36
|
+
str("C") | str("A") | str("L") | str("X") | str("P")).as(:type_letter)
|
|
36
37
|
end
|
|
37
38
|
|
|
38
|
-
#
|
|
39
|
+
# Document number — one or more decimal digits. The dataset historically
|
|
40
|
+
# zero-pads to 4 (S1070, R0126, M0001) but the canonical form going
|
|
41
|
+
# forward is unpadded (M1, GA1.1). Accept any length so both shapes
|
|
42
|
+
# round-trip; the number attribute preserves whatever was parsed.
|
|
39
43
|
rule(:doc_number) do
|
|
40
|
-
match("[0-9]").repeat(
|
|
44
|
+
match("[0-9]").repeat(1).as(:doc_number)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Dotted continuation that some prefixes use as part of the number:
|
|
48
|
+
# GA01.01 (series.index), L2.1.11 (series.sub.item). Captured into the
|
|
49
|
+
# same :doc_number atom so the number attribute stays a single string
|
|
50
|
+
# (e.g. "01.01", "2.1.11") and the existing builder needs no changes.
|
|
51
|
+
rule(:dotted_number) do
|
|
52
|
+
dot >> match("[0-9]").repeat(1)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
rule(:number_with_dots) do
|
|
56
|
+
doc_number >> dotted_number.repeat.as(:doc_number_dots)
|
|
41
57
|
end
|
|
42
58
|
|
|
43
59
|
# Numeric sub-part suffix(es): "-1", "-9-10", "-11". The catalogue
|
|
@@ -48,7 +64,7 @@ module Pubid
|
|
|
48
64
|
end
|
|
49
65
|
|
|
50
66
|
rule(:code) do
|
|
51
|
-
type_letter >>
|
|
67
|
+
type_letter >> number_with_dots >> subpart.maybe
|
|
52
68
|
end
|
|
53
69
|
|
|
54
70
|
# Edition forms observed on covers and listing pages:
|
|
@@ -83,7 +99,22 @@ module Pubid
|
|
|
83
99
|
end
|
|
84
100
|
|
|
85
101
|
rule(:identifier) do
|
|
86
|
-
publisher >> code >> edition.maybe >> language.maybe
|
|
102
|
+
annex_identifier | (publisher >> code >> edition.maybe >> language.maybe)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Annex to a base publication. Wraps the base code and adds an
|
|
106
|
+
# "Annex" / "ANNEX" marker (case preserved verbatim), an optional
|
|
107
|
+
# single-letter suffix (A/B/C/D), then the usual edition + language.
|
|
108
|
+
# Tried before the flat `identifier` so the annex marker isn't left
|
|
109
|
+
# dangling after a successful base-code match.
|
|
110
|
+
#
|
|
111
|
+
# The letter suffix uses a negative lookahead for a following lowercase
|
|
112
|
+
# letter so the "E" in "Ed 1" isn't mis-captured as annex letter "E".
|
|
113
|
+
rule(:annex_identifier) do
|
|
114
|
+
(publisher >> type_letter >> number_with_dots >> subpart.maybe).as(:base) >>
|
|
115
|
+
space >> (str("Annex") | str("ANNEX")).as(:annex_marker) >>
|
|
116
|
+
(space >> match("[A-Z]").as(:annex_letter) >> match("[a-z]").absent?).maybe >>
|
|
117
|
+
edition.maybe >> language.maybe
|
|
87
118
|
end
|
|
88
119
|
|
|
89
120
|
# Parse a string and return the raw parslet tree.
|
data/lib/pubid/iala/renderer.rb
CHANGED
|
@@ -12,11 +12,25 @@ module Pubid
|
|
|
12
12
|
def render(context: nil, **_opts)
|
|
13
13
|
id = @id
|
|
14
14
|
|
|
15
|
+
return render_annex(id) if id.is_a?(Identifiers::Annex)
|
|
16
|
+
|
|
15
17
|
rendered = "#{id.publisher} #{id.type_letter}#{id.number}"
|
|
16
18
|
rendered << " Ed #{id.edition}" if id.edition
|
|
17
19
|
rendered << " (#{id.language})" if id.language
|
|
18
20
|
rendered
|
|
19
21
|
end
|
|
22
|
+
|
|
23
|
+
# Render an Annex wrapper. The base identifier is rendered first, then
|
|
24
|
+
# the marker form is preserved verbatim ("Annex" vs "ANNEX"), then the
|
|
25
|
+
# optional letter, edition, and language.
|
|
26
|
+
def render_annex(id)
|
|
27
|
+
base_str = id.base_identifier.to_s
|
|
28
|
+
rendered = "#{base_str} #{id.annex_form}"
|
|
29
|
+
rendered << " #{id.letter}" if id.letter
|
|
30
|
+
rendered << " Ed #{id.edition}" if id.edition
|
|
31
|
+
rendered << " (#{id.language})" if id.language
|
|
32
|
+
rendered
|
|
33
|
+
end
|
|
20
34
|
end
|
|
21
35
|
end
|
|
22
36
|
end
|
|
@@ -16,6 +16,8 @@ module Pubid
|
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def generate
|
|
19
|
+
return generate_annex if identifier.is_a?(Identifiers::Annex)
|
|
20
|
+
|
|
19
21
|
parts = ["urn:mrn:iala:pub", code_segment]
|
|
20
22
|
parts << "ed#{identifier.edition}" if identifier.edition
|
|
21
23
|
parts << identifier.language.downcase if identifier.language
|
|
@@ -24,6 +26,21 @@ module Pubid
|
|
|
24
26
|
|
|
25
27
|
private
|
|
26
28
|
|
|
29
|
+
# URN for an Annex: embeds the base's code, then an "annex" segment
|
|
30
|
+
# (with optional letter suffix), then the annex's own edition/lang.
|
|
31
|
+
# G1045 Annex Ed 1 → urn:mrn:iala:pub:g1045:annex:ed1
|
|
32
|
+
# G1128 ANNEX A Ed 1.6 (E) → urn:mrn:iala:pub:g1128:annex-a:ed1.6:e
|
|
33
|
+
def generate_annex
|
|
34
|
+
base = identifier.base_identifier
|
|
35
|
+
parts = ["urn:mrn:iala:pub",
|
|
36
|
+
"#{base.type_letter.downcase}#{base.number}"]
|
|
37
|
+
annex_seg = identifier.letter ? "annex-#{identifier.letter.downcase}" : "annex"
|
|
38
|
+
parts << annex_seg
|
|
39
|
+
parts << "ed#{identifier.edition}" if identifier.edition
|
|
40
|
+
parts << identifier.language.downcase if identifier.language
|
|
41
|
+
parts.join(":")
|
|
42
|
+
end
|
|
43
|
+
|
|
27
44
|
def code_segment
|
|
28
45
|
"#{identifier.type_letter.downcase}#{identifier.number}"
|
|
29
46
|
end
|
data/lib/pubid/iala.rb
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
module Pubid
|
|
4
4
|
module Iala
|
|
5
|
+
extend Pubid::PrefixesSupport
|
|
6
|
+
|
|
7
|
+
# Sole IALA publisher token (see the parser's `publisher` rule).
|
|
8
|
+
PREFIXES = ["IALA"].freeze
|
|
9
|
+
|
|
5
10
|
autoload :Builder, "#{__dir__}/iala/builder"
|
|
6
11
|
autoload :Identifier, "#{__dir__}/iala/identifier"
|
|
7
12
|
autoload :Identifiers, "#{__dir__}/iala/identifiers"
|
data/lib/pubid/idf.rb
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
module Pubid
|
|
4
4
|
module Idf
|
|
5
|
+
extend Pubid::PrefixesSupport
|
|
6
|
+
|
|
7
|
+
# Sole IDF publisher token (see the parser's `prefix_sole_publisher` rule).
|
|
8
|
+
PREFIXES = ["IDF"].freeze
|
|
9
|
+
|
|
5
10
|
autoload :Builder, "#{__dir__}/idf/builder"
|
|
6
11
|
autoload :Identifier, "#{__dir__}/idf/identifier"
|
|
7
12
|
autoload :Identifiers, "#{__dir__}/idf/identifiers"
|
data/lib/pubid/iec.rb
CHANGED
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
module Pubid
|
|
4
4
|
module Iec
|
|
5
5
|
autoload :Components, "#{__dir__}/iec/components"
|
|
6
|
+
|
|
7
|
+
extend Pubid::PrefixesSupport
|
|
8
|
+
|
|
9
|
+
# Sourced from the parser's publisher list (Components::Publisher::PUBLISHERS)
|
|
10
|
+
# minus the joint "ISO/IEC", which is contributed symmetrically via
|
|
11
|
+
# Pubid::JOINT_PREFIXES (along with IEC/ISO and ISO/IEC/IEEE).
|
|
12
|
+
PREFIXES = (Components::Publisher::PUBLISHERS.keys - ["ISO/IEC"]).freeze
|
|
13
|
+
|
|
6
14
|
autoload :Identifier, "#{__dir__}/iec/identifier"
|
|
7
15
|
autoload :Identifiers, "#{__dir__}/iec/identifiers"
|
|
8
16
|
autoload :SingleIdentifier, "#{__dir__}/iec/single_identifier"
|