pubid 2.0.0.pre.alpha.3 → 2.0.0.pre.alpha.5
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/lib/pubid/amca/identifier.rb +4 -23
- data/lib/pubid/amca/identifiers/base.rb +67 -30
- data/lib/pubid/amca/identifiers/interpretation.rb +0 -4
- data/lib/pubid/amca/identifiers/publication.rb +0 -4
- data/lib/pubid/amca/identifiers/standard.rb +0 -4
- data/lib/pubid/amca/renderer.rb +1 -1
- data/lib/pubid/amca/urn_generator.rb +2 -1
- data/lib/pubid/api/builder.rb +4 -0
- data/lib/pubid/api/identifier.rb +5 -1
- data/lib/pubid/api/identifiers/base.rb +2 -12
- data/lib/pubid/api/identifiers/mpms.rb +0 -4
- data/lib/pubid/api/identifiers/typeless_standard.rb +0 -3
- data/lib/pubid/api/single_identifier.rb +5 -8
- data/lib/pubid/ashrae/identifier.rb +7 -22
- data/lib/pubid/ashrae/identifiers/base.rb +28 -17
- data/lib/pubid/ashrae/parser.rb +4 -0
- data/lib/pubid/ashrae.rb +4 -0
- data/lib/pubid/asme/single_identifier.rb +3 -1
- data/lib/pubid/astm/single_identifier.rb +3 -1
- data/lib/pubid/bsi/identifier.rb +5 -31
- data/lib/pubid/bsi/identifiers/addendum_document.rb +0 -4
- data/lib/pubid/bsi/identifiers/adopted_european_norm.rb +0 -4
- data/lib/pubid/bsi/identifiers/adopted_international_standard.rb +0 -4
- data/lib/pubid/bsi/identifiers/aerospace_standard.rb +0 -3
- data/lib/pubid/bsi/identifiers/amendment.rb +0 -4
- data/lib/pubid/bsi/identifiers/british_industrial_practice.rb +0 -3
- data/lib/pubid/bsi/identifiers/bundled_identifier.rb +2 -5
- data/lib/pubid/bsi/identifiers/committee_document.rb +0 -3
- data/lib/pubid/bsi/identifiers/consolidated_identifier.rb +2 -5
- data/lib/pubid/bsi/identifiers/corrigendum.rb +0 -4
- data/lib/pubid/bsi/identifiers/detailed_specification.rb +0 -3
- data/lib/pubid/bsi/identifiers/disc.rb +0 -3
- data/lib/pubid/bsi/identifiers/draft_document.rb +0 -3
- data/lib/pubid/bsi/identifiers/electronic_book.rb +0 -3
- data/lib/pubid/bsi/identifiers/expert_commentary.rb +0 -4
- data/lib/pubid/bsi/identifiers/explanatory_supplement.rb +0 -3
- data/lib/pubid/bsi/identifiers/flex.rb +0 -3
- data/lib/pubid/bsi/identifiers/handbook.rb +0 -3
- data/lib/pubid/bsi/identifiers/index.rb +0 -3
- data/lib/pubid/bsi/identifiers/method.rb +0 -3
- data/lib/pubid/bsi/identifiers/national_annex.rb +0 -3
- data/lib/pubid/bsi/identifiers/practice_guide.rb +0 -3
- data/lib/pubid/bsi/identifiers/publicly_available_specification.rb +0 -3
- data/lib/pubid/bsi/identifiers/published_document.rb +0 -3
- data/lib/pubid/bsi/identifiers/section.rb +0 -3
- data/lib/pubid/bsi/identifiers/set.rb +2 -5
- data/lib/pubid/bsi/identifiers/standalone_amendment.rb +0 -3
- data/lib/pubid/bsi/identifiers/supplement_document.rb +0 -4
- data/lib/pubid/bsi/identifiers/supplementary_index.rb +0 -3
- data/lib/pubid/bsi/identifiers/technical_specification.rb +0 -3
- data/lib/pubid/bsi/identifiers/test_method.rb +0 -3
- data/lib/pubid/bsi/identifiers/value_added_publication.rb +0 -4
- data/lib/pubid/bsi/single_identifier.rb +32 -13
- data/lib/pubid/bsi.rb +8 -1
- data/lib/pubid/ccsds/identifier.rb +2 -14
- data/lib/pubid/cen_cenelec/identifier.rb +10 -8
- data/lib/pubid/cen_cenelec/identifiers/adopted_european_norm.rb +0 -4
- data/lib/pubid/cen_cenelec/identifiers/amendment.rb +0 -4
- data/lib/pubid/cen_cenelec/identifiers/base.rb +2 -9
- data/lib/pubid/cen_cenelec/identifiers/consolidated_identifier.rb +0 -4
- data/lib/pubid/cen_cenelec/identifiers/corrigendum.rb +0 -4
- data/lib/pubid/cen_cenelec/identifiers/european_prestandard.rb +0 -3
- data/lib/pubid/cen_cenelec/identifiers/fragment.rb +0 -4
- data/lib/pubid/cen_cenelec/single_identifier.rb +4 -7
- data/lib/pubid/cen_cenelec.rb +4 -0
- data/lib/pubid/cie/identifier.rb +4 -0
- data/lib/pubid/cie/single_identifier.rb +4 -4
- data/lib/pubid/cie.rb +4 -0
- data/lib/pubid/csa/identifier.rb +10 -1
- data/lib/pubid/csa/identifiers/base.rb +0 -3
- data/lib/pubid/csa/identifiers/cec.rb +0 -4
- data/lib/pubid/csa/identifiers/series.rb +0 -4
- data/lib/pubid/csa/single_identifier.rb +1 -1
- data/lib/pubid/csa.rb +4 -0
- data/lib/pubid/etsi/components/code.rb +1 -7
- data/lib/pubid/etsi/components/version.rb +0 -5
- data/lib/pubid/etsi/identifier.rb +4 -12
- data/lib/pubid/etsi/identifiers/base.rb +31 -24
- data/lib/pubid/etsi/identifiers/supplement_identifier.rb +0 -3
- data/lib/pubid/etsi.rb +3 -1
- data/lib/pubid/identifier.rb +168 -1
- data/lib/pubid/idf/identifier.rb +4 -0
- data/lib/pubid/idf.rb +4 -0
- data/lib/pubid/iec/identifier.rb +14 -14
- data/lib/pubid/iec/identifiers/base.rb +0 -4
- data/lib/pubid/iec/identifiers/consolidated_identifier.rb +0 -4
- data/lib/pubid/iec/identifiers/fragment_identifier.rb +1 -5
- data/lib/pubid/iec/identifiers/sheet_identifier.rb +1 -5
- data/lib/pubid/iec/identifiers/test_report_form.rb +0 -3
- data/lib/pubid/iec/identifiers/vap_identifier.rb +1 -5
- data/lib/pubid/iec/identifiers/working_document.rb +0 -4
- data/lib/pubid/iec.rb +4 -0
- data/lib/pubid/ieee/identifier.rb +5 -17
- data/lib/pubid/ieee/identifiers/adopted_standard.rb +0 -4
- data/lib/pubid/ieee/identifiers/base.rb +223 -201
- data/lib/pubid/ieee/identifiers/conformance_identifier.rb +0 -4
- data/lib/pubid/ieee/identifiers/corrigendum.rb +0 -4
- data/lib/pubid/ieee/identifiers/csa_dual_published.rb +0 -4
- data/lib/pubid/ieee/identifiers/dual_identifier.rb +0 -4
- data/lib/pubid/ieee/identifiers/dual_published.rb +0 -4
- data/lib/pubid/ieee/identifiers/iec_ieee_copublished.rb +0 -4
- data/lib/pubid/ieee/identifiers/interpretation_identifier.rb +0 -4
- data/lib/pubid/ieee/identifiers/multi_numbered_identifier.rb +0 -4
- data/lib/pubid/ieee/identifiers/parenthetical_identifier.rb +0 -4
- data/lib/pubid/ieee/identifiers/redlined_standard.rb +0 -4
- data/lib/pubid/ieee/identifiers/si_standard.rb +0 -4
- data/lib/pubid/ieee.rb +4 -0
- data/lib/pubid/iho/identifier.rb +7 -25
- data/lib/pubid/iho/identifiers/base.rb +62 -68
- data/lib/pubid/iho.rb +3 -1
- data/lib/pubid/iso/identifier.rb +15 -14
- data/lib/pubid/iso/single_identifier.rb +0 -4
- data/lib/pubid/iso.rb +4 -0
- data/lib/pubid/itu/components/code.rb +1 -7
- data/lib/pubid/itu/components/sector.rb +15 -4
- data/lib/pubid/itu/components/series.rb +0 -4
- data/lib/pubid/itu/identifier.rb +4 -18
- data/lib/pubid/itu/identifiers/base.rb +124 -122
- data/lib/pubid/itu.rb +3 -1
- data/lib/pubid/jcgm/identifiers/amendment.rb +0 -4
- data/lib/pubid/jcgm/identifiers/gum_guide.rb +0 -4
- data/lib/pubid/jcgm/single_identifier.rb +0 -4
- data/lib/pubid/jis/identifier.rb +2 -14
- data/lib/pubid/nist/identifier.rb +8 -23
- data/lib/pubid/nist/identifiers/base.rb +553 -544
- data/lib/pubid/nist/preprocessor.rb +12 -0
- data/lib/pubid/nist.rb +4 -0
- data/lib/pubid/oiml/builder.rb +23 -3
- data/lib/pubid/oiml/components/code.rb +7 -0
- data/lib/pubid/oiml/identifier.rb +44 -0
- data/lib/pubid/oiml/identifiers/annex.rb +10 -1
- data/lib/pubid/oiml/identifiers/errata.rb +15 -0
- data/lib/pubid/oiml/identifiers.rb +1 -1
- data/lib/pubid/oiml/parser.rb +72 -12
- data/lib/pubid/oiml/renderer.rb +37 -17
- data/lib/pubid/oiml/single_identifier.rb +58 -0
- data/lib/pubid/oiml/supplement_identifier.rb +34 -1
- data/lib/pubid/plateau/identifier.rb +4 -21
- data/lib/pubid/plateau/identifiers/base.rb +45 -36
- data/lib/pubid/plateau/identifiers/handbook.rb +0 -4
- data/lib/pubid/plateau/identifiers/technical_report.rb +0 -4
- data/lib/pubid/plateau/supplement_identifier.rb +0 -4
- data/lib/pubid/sae/identifier.rb +4 -12
- data/lib/pubid/sae/identifiers/base.rb +26 -17
- data/lib/pubid/version.rb +1 -1
- data/lib/pubid.rb +18 -2
- data/lib/tasks/export.rake +6 -5
- metadata +3 -4
- data/lib/pubid/identifier_facade.rb +0 -114
- data/lib/pubid/oiml/identifiers/base.rb +0 -21
|
@@ -53,6 +53,7 @@ module Pubid
|
|
|
53
53
|
# Extracted so rubocop can scope length/ABC metrics narrowly.
|
|
54
54
|
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
|
55
55
|
def run_stages
|
|
56
|
+
normalize_spurious_u_suffix!
|
|
56
57
|
normalize_publisher_and_series!
|
|
57
58
|
normalize_lcirc_supplement_contexts!
|
|
58
59
|
normalize_revision_spacing!
|
|
@@ -84,6 +85,17 @@ module Pubid
|
|
|
84
85
|
|
|
85
86
|
private
|
|
86
87
|
|
|
88
|
+
# Strip the spurious "U" a bad v2 data migration prefixed onto real
|
|
89
|
+
# letter/revision suffixes (e.g. "800-38a" → "800-38Ua",
|
|
90
|
+
# "73-197r" → "73-197Ur"). NIST's authoritative records
|
|
91
|
+
# (allrecords.xml, DOIs) carry no such "U"; it exists only in the
|
|
92
|
+
# migrated corpus. Removing it here lets the remainder parse through
|
|
93
|
+
# the normal letter-suffix / revision path — "U<letter>" is never a
|
|
94
|
+
# real NIST suffix, so this only ever undoes the corruption.
|
|
95
|
+
def normalize_spurious_u_suffix!
|
|
96
|
+
@cleaned = @cleaned.gsub(/(\d)U([a-z])/, '\1\2')
|
|
97
|
+
end
|
|
98
|
+
|
|
87
99
|
# Lowercase publishers, publisher+series concatenations, lowercase
|
|
88
100
|
# series codes, and the lone "LC" → "LCIRC" expansion.
|
|
89
101
|
def normalize_publisher_and_series!
|
data/lib/pubid/nist.rb
CHANGED
|
@@ -53,6 +53,10 @@ module Pubid
|
|
|
53
53
|
# @param identifier [String] the identifier string to parse
|
|
54
54
|
# @return [Identifiers::Base] the parsed identifier
|
|
55
55
|
def self.parse(identifier)
|
|
56
|
+
if identifier.length > Pubid::MAX_INPUT_LENGTH
|
|
57
|
+
raise ArgumentError, Pubid::INPUT_TOO_LONG_MESSAGE
|
|
58
|
+
end
|
|
59
|
+
|
|
56
60
|
# Use the Parser class's preprocessing method
|
|
57
61
|
# Note: We call the class method directly to ensure preprocessing is applied
|
|
58
62
|
parsed = Parser.class_parse_with_preprocessing(identifier)
|
data/lib/pubid/oiml/builder.rb
CHANGED
|
@@ -77,14 +77,23 @@ module Pubid
|
|
|
77
77
|
end
|
|
78
78
|
|
|
79
79
|
def build_supplement(parsed_hash)
|
|
80
|
-
|
|
80
|
+
marker = parsed_hash[:trailing_marker].to_s if parsed_hash[:trailing_marker]
|
|
81
|
+
plus_marker = parsed_hash[:plus_marker].to_s if parsed_hash[:plus_marker]
|
|
82
|
+
|
|
83
|
+
# Determine supplement type. The trailing word ("Amendment"/"Errata")
|
|
84
|
+
# selects the class; the concrete class then carries the word via
|
|
85
|
+
# #supplement_type, so only the `trailing` flag needs storing.
|
|
81
86
|
supplement_class = if parsed_hash[:annex_letter] || parsed_hash[:annex_marker]
|
|
82
87
|
Identifiers::Annex
|
|
88
|
+
elsif marker == "Errata" || plus_marker == "Errata"
|
|
89
|
+
Identifiers::Errata
|
|
83
90
|
else
|
|
84
91
|
Identifiers::Amendment
|
|
85
92
|
end
|
|
86
93
|
|
|
87
94
|
supplement = supplement_class.new
|
|
95
|
+
supplement.trailing = true if marker
|
|
96
|
+
supplement.joined = true if plus_marker
|
|
88
97
|
|
|
89
98
|
# Recursively parse base identifier
|
|
90
99
|
if parsed_hash[:base_identifier]
|
|
@@ -103,6 +112,13 @@ module Pubid
|
|
|
103
112
|
supplement.language = extract_language(parsed_hash[:language]) if parsed_hash[:language]
|
|
104
113
|
supplement.letter = parsed_hash[:annex_letter].to_s if parsed_hash[:annex_letter]
|
|
105
114
|
|
|
115
|
+
# Annex with no year of its own but a dated base ("R 60:2017 Annexes"):
|
|
116
|
+
# the year belongs to the base and must render glued to it.
|
|
117
|
+
if supplement.is_a?(Identifiers::Annex) && !year_value &&
|
|
118
|
+
supplement.base_identifier&.date
|
|
119
|
+
supplement.year_on_base = true
|
|
120
|
+
end
|
|
121
|
+
|
|
106
122
|
# Track if supplement itself was parsed with Edition format
|
|
107
123
|
if parsed_hash[:edition_format]
|
|
108
124
|
supplement.parsed_format = "long"
|
|
@@ -119,8 +135,8 @@ module Pubid
|
|
|
119
135
|
|
|
120
136
|
identifier = identifier_class.new
|
|
121
137
|
|
|
122
|
-
# Handle code (number-part-subpart) specially
|
|
123
|
-
if parsed_hash[:number] || parsed_hash[:part] || parsed_hash[:subpart]
|
|
138
|
+
# Handle code (number-part-subpart-suffix) specially
|
|
139
|
+
if parsed_hash[:number] || parsed_hash[:part] || parsed_hash[:subpart] || parsed_hash[:code_suffix]
|
|
124
140
|
code_attrs = {}
|
|
125
141
|
if parsed_hash[:number]
|
|
126
142
|
code_attrs[:number] =
|
|
@@ -131,6 +147,10 @@ module Pubid
|
|
|
131
147
|
code_attrs[:subpart] =
|
|
132
148
|
parsed_hash[:subpart].to_s
|
|
133
149
|
end
|
|
150
|
+
if parsed_hash[:code_suffix]
|
|
151
|
+
code_attrs[:suffix] = parsed_hash[:code_suffix].to_s
|
|
152
|
+
code_attrs[:space_suffix] = true if parsed_hash.key?(:space_suffix)
|
|
153
|
+
end
|
|
134
154
|
identifier.code = Components::Code.new(**code_attrs)
|
|
135
155
|
end
|
|
136
156
|
|
|
@@ -13,11 +13,18 @@ module Pubid
|
|
|
13
13
|
attribute :number, :string
|
|
14
14
|
attribute :part, :string
|
|
15
15
|
attribute :subpart, :string
|
|
16
|
+
# Free-form trailing suffix glued to the code, e.g. "sup", "A",
|
|
17
|
+
# "erratum", "GUM 1", "ISO3930". Preserved verbatim for round-trip.
|
|
18
|
+
attribute :suffix, :string
|
|
19
|
+
# When true the suffix is space-separated ("D 1 Brochure") rather than
|
|
20
|
+
# the default dash ("R 60-sup").
|
|
21
|
+
attribute :space_suffix, :boolean, default: false
|
|
16
22
|
|
|
17
23
|
def to_s
|
|
18
24
|
result = number.to_s
|
|
19
25
|
result += "-#{part}" if part
|
|
20
26
|
result += "-#{subpart}" if subpart
|
|
27
|
+
result += "#{space_suffix ? ' ' : '-'}#{suffix}" if suffix
|
|
21
28
|
result
|
|
22
29
|
end
|
|
23
30
|
|
|
@@ -3,6 +3,50 @@
|
|
|
3
3
|
module Pubid
|
|
4
4
|
module Oiml
|
|
5
5
|
class Identifier < Pubid::Identifier
|
|
6
|
+
# Maps each concrete identifier's polymorphic_name => class name, so
|
|
7
|
+
# key_value (de)serialization can re-instantiate the correct subclass
|
|
8
|
+
# from the `_type` discriminator. Kept in sync with the Identifiers
|
|
9
|
+
# namespace by a spec assertion (see spec/pubid/oiml/to_hash_spec.rb).
|
|
10
|
+
OIML_TYPE_MAP = {
|
|
11
|
+
"pubid:oiml:recommendation" => "Pubid::Oiml::Identifiers::Recommendation",
|
|
12
|
+
"pubid:oiml:basic-publication" => "Pubid::Oiml::Identifiers::BasicPublication",
|
|
13
|
+
"pubid:oiml:document" => "Pubid::Oiml::Identifiers::Document",
|
|
14
|
+
"pubid:oiml:guide" => "Pubid::Oiml::Identifiers::Guide",
|
|
15
|
+
"pubid:oiml:vocabulary" => "Pubid::Oiml::Identifiers::Vocabulary",
|
|
16
|
+
"pubid:oiml:expert-report" => "Pubid::Oiml::Identifiers::ExpertReport",
|
|
17
|
+
"pubid:oiml:seminar-report" => "Pubid::Oiml::Identifiers::SeminarReport",
|
|
18
|
+
"pubid:oiml:amendment" => "Pubid::Oiml::Identifiers::Amendment",
|
|
19
|
+
"pubid:oiml:errata" => "Pubid::Oiml::Identifiers::Errata",
|
|
20
|
+
"pubid:oiml:annex" => "Pubid::Oiml::Identifiers::Annex",
|
|
21
|
+
}.freeze
|
|
22
|
+
|
|
23
|
+
# The base Pubid::Identifier no longer auto-maps attributes, so OIML must
|
|
24
|
+
# declare its own key_value mapping. SingleIdentifier and
|
|
25
|
+
# SupplementIdentifier (siblings) add their disjoint attribute deltas on
|
|
26
|
+
# top of this shared block. `type` is deliberately NOT mapped: OIML's
|
|
27
|
+
# `type` is a String-returning method (e.g. "R"), incompatible with the
|
|
28
|
+
# inherited Components::Type attribute — and the `_type` discriminator
|
|
29
|
+
# already pins the concrete subclass (and thus the type letter).
|
|
30
|
+
key_value do
|
|
31
|
+
map "_type", to: :_type, polymorphic_map: OIML_TYPE_MAP
|
|
32
|
+
map "language", to: :language
|
|
33
|
+
map "parsed_format", to: :parsed_format
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# lutaml's polymorphic_map only validates `_type` on deserialization; it
|
|
37
|
+
# does not re-instantiate the concrete subclass. Route by `_type` so
|
|
38
|
+
# `Pubid::Oiml::Identifier.from_hash(h)` returns the right class (and its
|
|
39
|
+
# nested base_identifier), mirroring Pubid::Iso::Identifier.from_hash.
|
|
40
|
+
def self.from_hash(data, options = {})
|
|
41
|
+
type = data["_type"] || data[:_type]
|
|
42
|
+
klass_name = OIML_TYPE_MAP[type]
|
|
43
|
+
if klass_name
|
|
44
|
+
klass = Object.const_get(klass_name)
|
|
45
|
+
return klass.from_hash(data, options) unless klass == self
|
|
46
|
+
end
|
|
47
|
+
super
|
|
48
|
+
end
|
|
49
|
+
|
|
6
50
|
def to_urn
|
|
7
51
|
UrnGenerator.new(self).generate
|
|
8
52
|
end
|
|
@@ -4,7 +4,16 @@ module Pubid
|
|
|
4
4
|
module Oiml
|
|
5
5
|
module Identifiers
|
|
6
6
|
class Annex < SupplementIdentifier
|
|
7
|
-
attribute :letter, :string # For "Annex A", "Annex B",
|
|
7
|
+
attribute :letter, :string # For "Annex A", "Annex B", "Annex B-C"
|
|
8
|
+
# True when the publication year is glued to the base ("OIML R 60:2017
|
|
9
|
+
# Annexes") rather than the marker ("OIML R 60 Annexes:2017"); drives
|
|
10
|
+
# exact round-trip in the renderer.
|
|
11
|
+
attribute :year_on_base, :boolean, default: false
|
|
12
|
+
|
|
13
|
+
key_value do
|
|
14
|
+
map "letter", to: :letter
|
|
15
|
+
map "year_on_base", to: :year_on_base
|
|
16
|
+
end
|
|
8
17
|
|
|
9
18
|
def supplement_type
|
|
10
19
|
letter ? "Annex #{letter}" : "Annexes"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pubid
|
|
4
|
+
module Oiml
|
|
5
|
+
module Identifiers
|
|
6
|
+
# Errata supplement, used by the trailing-word shorthand
|
|
7
|
+
# "OIML R 126:2015 Errata" (the publication year lives on the base).
|
|
8
|
+
class Errata < SupplementIdentifier
|
|
9
|
+
def supplement_type
|
|
10
|
+
"Errata"
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -5,9 +5,9 @@ module Pubid
|
|
|
5
5
|
module Identifiers
|
|
6
6
|
autoload :Amendment, "#{__dir__}/identifiers/amendment"
|
|
7
7
|
autoload :Annex, "#{__dir__}/identifiers/annex"
|
|
8
|
-
autoload :Base, "#{__dir__}/identifiers/base"
|
|
9
8
|
autoload :BasicPublication, "#{__dir__}/identifiers/basic_publication"
|
|
10
9
|
autoload :Document, "#{__dir__}/identifiers/document"
|
|
10
|
+
autoload :Errata, "#{__dir__}/identifiers/errata"
|
|
11
11
|
autoload :ExpertReport, "#{__dir__}/identifiers/expert_report"
|
|
12
12
|
autoload :Guide, "#{__dir__}/identifiers/guide"
|
|
13
13
|
autoload :Recommendation, "#{__dir__}/identifiers/recommendation"
|
data/lib/pubid/oiml/parser.rb
CHANGED
|
@@ -18,7 +18,9 @@ module Pubid
|
|
|
18
18
|
|
|
19
19
|
# Main identifier pattern - check supplements first
|
|
20
20
|
rule(:identifier) do
|
|
21
|
-
amendment_identifier | amendment_short | annex_letter_identifier |
|
|
21
|
+
amendment_identifier | amendment_short | annex_letter_identifier |
|
|
22
|
+
annex_identifier | plus_supplement_identifier |
|
|
23
|
+
trailing_supplement_identifier | base_identifier
|
|
22
24
|
end
|
|
23
25
|
|
|
24
26
|
# Publisher - always "OIML"
|
|
@@ -30,14 +32,33 @@ module Pubid
|
|
|
30
32
|
# Number with optional part and subpart
|
|
31
33
|
rule(:number_only) { digits.as(:number) }
|
|
32
34
|
|
|
33
|
-
|
|
35
|
+
# Part number, optionally a slashed multi-part continuation captured
|
|
36
|
+
# verbatim, e.g. "1/-2" in "OIML R 46-1/-2:2012".
|
|
37
|
+
rule(:part_number) do
|
|
38
|
+
dash >> (digits >> (slash >> dash >> digits).repeat).as(:part)
|
|
39
|
+
end
|
|
34
40
|
|
|
35
41
|
rule(:subpart_number) { dash >> digits.as(:subpart) }
|
|
36
42
|
|
|
43
|
+
# Free-form named/lettered code suffix glued after the numeric part:
|
|
44
|
+
# -GUM 1, -special, -sup, -erratum, -A, -ISO3930, -Amend, -Amended_2012
|
|
45
|
+
# plus the space-separated "Brochure" label. "GUM <n>" is tried first so
|
|
46
|
+
# its trailing number isn't lost to the bare-word alternative. The
|
|
47
|
+
# trailing `(_?digits)*` captures joint codes ("ISO3930") and the
|
|
48
|
+
# "Amended_2012" amendment label (the hand-off treats these as part
|
|
49
|
+
# suffixes, preserved verbatim for round-trip).
|
|
50
|
+
rule(:named_suffix) do
|
|
51
|
+
(dash >> (
|
|
52
|
+
(str("GUM") >> space >> digits) |
|
|
53
|
+
(match("[A-Za-z]").repeat(1) >> (str("_").maybe >> digits).repeat)
|
|
54
|
+
).as(:code_suffix)) |
|
|
55
|
+
(space >> str("Brochure").as(:code_suffix) >> str("").as(:space_suffix))
|
|
56
|
+
end
|
|
57
|
+
|
|
37
58
|
rule(:full_number) do
|
|
38
|
-
(number_only >> part_number >> subpart_number) |
|
|
39
|
-
(number_only >> part_number) |
|
|
40
|
-
number_only
|
|
59
|
+
(number_only >> part_number >> subpart_number >> named_suffix.maybe) |
|
|
60
|
+
(number_only >> part_number >> named_suffix.maybe) |
|
|
61
|
+
(number_only >> named_suffix.maybe)
|
|
41
62
|
end
|
|
42
63
|
|
|
43
64
|
# Edition number - ordinal numbers
|
|
@@ -82,8 +103,18 @@ module Pubid
|
|
|
82
103
|
end
|
|
83
104
|
|
|
84
105
|
# Language codes
|
|
106
|
+
#
|
|
107
|
+
# relaton-data-oiml encodes the publication language with its own code
|
|
108
|
+
# set (lib/oiml_fetcher.rb DOCID_LANG_CODE): single uppercase letters
|
|
109
|
+
# E F D R S C A U X, plus two-letter PO/PT/PE/SR. The two-letter OIML
|
|
110
|
+
# codes must be tried before the single-letter rule so e.g. "PE" isn't
|
|
111
|
+
# left with a dangling "E".
|
|
85
112
|
rule(:lang_single) do
|
|
86
|
-
match("[
|
|
113
|
+
match("[EFRXDSCAU]")
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
rule(:lang_multi_oiml) do
|
|
117
|
+
str("PO") | str("PT") | str("PE") | str("SR")
|
|
87
118
|
end
|
|
88
119
|
|
|
89
120
|
rule(:lang_multi) do
|
|
@@ -93,8 +124,9 @@ module Pubid
|
|
|
93
124
|
rule(:language_code) do
|
|
94
125
|
(
|
|
95
126
|
(lang_single >> slash >> lang_single) | # E/F
|
|
96
|
-
|
|
97
|
-
|
|
127
|
+
lang_multi_oiml | # PO, PT, PE, SR
|
|
128
|
+
lang_single | # E, F, D, R, S, C, A, U, X
|
|
129
|
+
lang_multi # en, fr
|
|
98
130
|
).as(:language)
|
|
99
131
|
end
|
|
100
132
|
|
|
@@ -131,21 +163,49 @@ module Pubid
|
|
|
131
163
|
language_portion.maybe.as(:language)
|
|
132
164
|
end
|
|
133
165
|
|
|
134
|
-
#
|
|
166
|
+
# Trailing supplement word - "BASE Amendment" / "BASE Errata" where the
|
|
167
|
+
# publication year stays on the base (e.g. "OIML R 138:2009 Amendment").
|
|
168
|
+
# amendment_short is tried first; it only matches when a year follows the
|
|
169
|
+
# word, so the no-year trailing form falls through to here.
|
|
170
|
+
rule(:trailing_supplement_identifier) do
|
|
171
|
+
base_without_language.as(:base_identifier) >>
|
|
172
|
+
space >> (str("Amendment") | str("Errata")).as(:trailing_marker) >>
|
|
173
|
+
language_portion.maybe.as(:language)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Plus-joined supplement - "BASE:YEAR+Supplement:YEAR" form where both
|
|
177
|
+
# the base and the supplement carry their own year. Used for amendments
|
|
178
|
+
# and errata to dated bases (e.g. "OIML B 10:2011+Amendment:2012").
|
|
179
|
+
# Annexes already encode the year-on-base intent via their own model.
|
|
180
|
+
rule(:plus_supplement_identifier) do
|
|
181
|
+
base_without_language.as(:base_identifier) >>
|
|
182
|
+
str("+") >>
|
|
183
|
+
(str("Amendment") | str("Errata")).as(:plus_marker) >>
|
|
184
|
+
(colon >> year_digits.as(:year)).maybe >>
|
|
185
|
+
language_portion.maybe.as(:language)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Annex identifier - "BASE Annexes Edition YYYY" / "BASE Annexes:YYYY" /
|
|
189
|
+
# "BASE:YYYY Annexes" (year on the base, no annex year).
|
|
135
190
|
rule(:annex_identifier) do
|
|
136
191
|
base_without_language.as(:base_identifier) >>
|
|
137
192
|
space >> str("Annexes").as(:annex_marker) >>
|
|
138
193
|
(
|
|
139
194
|
(space >> edition_text >> space >> year_digits.as(:year)).as(:edition_format) |
|
|
140
195
|
(colon >> year_digits.as(:year))
|
|
141
|
-
) >>
|
|
196
|
+
).maybe >>
|
|
142
197
|
language_portion.maybe.as(:language)
|
|
143
198
|
end
|
|
144
199
|
|
|
145
|
-
# Annex
|
|
200
|
+
# Annex letter or letter range, e.g. "A" or "B-C".
|
|
201
|
+
rule(:annex_letter_value) do
|
|
202
|
+
(match("[A-Z]") >> (dash >> match("[A-Z]")).maybe).as(:annex_letter)
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# Annex with letter - "BASE Annex A Edition YYYY" / "BASE:YYYY Annex B-C"
|
|
146
206
|
rule(:annex_letter_identifier) do
|
|
147
207
|
base_without_language.as(:base_identifier) >>
|
|
148
|
-
space >> str("Annex") >> space >>
|
|
208
|
+
space >> str("Annex") >> space >> annex_letter_value >>
|
|
149
209
|
((space >> edition_text >> space >> year_digits.as(:year)) | (colon >> year_digits.as(:year))).maybe >>
|
|
150
210
|
language_portion.maybe.as(:language)
|
|
151
211
|
end
|
data/lib/pubid/oiml/renderer.rb
CHANGED
|
@@ -24,8 +24,6 @@ module Pubid
|
|
|
24
24
|
render_supplement(id)
|
|
25
25
|
when SingleIdentifier
|
|
26
26
|
render_single(id)
|
|
27
|
-
when Identifiers::Base
|
|
28
|
-
render_base_identifier(id)
|
|
29
27
|
else
|
|
30
28
|
id.to_s
|
|
31
29
|
end
|
|
@@ -38,6 +36,12 @@ module Pubid
|
|
|
38
36
|
(id.parsed_format == "long" ? :long : :short)
|
|
39
37
|
end
|
|
40
38
|
|
|
39
|
+
# Strip a trailing language group "(...)" from a rendered base string so
|
|
40
|
+
# the supplement can re-attach its own language at the end.
|
|
41
|
+
def strip_language(str)
|
|
42
|
+
str.sub(/\s*\([^)]+\)\s*$/, "").strip
|
|
43
|
+
end
|
|
44
|
+
|
|
41
45
|
def render_single(id)
|
|
42
46
|
format = effective_format(id)
|
|
43
47
|
|
|
@@ -80,6 +84,26 @@ module Pubid
|
|
|
80
84
|
def render_supplement(id)
|
|
81
85
|
format = effective_format(id)
|
|
82
86
|
|
|
87
|
+
# Plus-joined: "BASE+Amendment:YEAR" / "BASE+Errata:YEAR" with both
|
|
88
|
+
# the base and the supplement carrying their own year.
|
|
89
|
+
if id.joined
|
|
90
|
+
base_str = strip_language(id.base_identifier.to_s)
|
|
91
|
+
result = "#{base_str}+#{id.supplement_type}"
|
|
92
|
+
result += ":#{id.year}" if id.year
|
|
93
|
+
result += " (#{id.language})" if id.language
|
|
94
|
+
return result
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Trailing-word shorthand: "BASE Amendment" / "BASE Errata" with the
|
|
98
|
+
# publication year kept on the base identifier. The word comes from the
|
|
99
|
+
# concrete supplement class.
|
|
100
|
+
if id.trailing
|
|
101
|
+
base_str = strip_language(id.base_identifier.to_s)
|
|
102
|
+
result = "#{base_str} #{id.supplement_type}"
|
|
103
|
+
result += " (#{id.language})" if id.language
|
|
104
|
+
return result
|
|
105
|
+
end
|
|
106
|
+
|
|
83
107
|
base_format = if format && format != :short
|
|
84
108
|
format
|
|
85
109
|
elsif id.base_identifier.class.attributes.key?(:parsed_format) && id.base_identifier.parsed_format == "long"
|
|
@@ -93,7 +117,7 @@ module Pubid
|
|
|
93
117
|
else
|
|
94
118
|
id.base_identifier.to_s
|
|
95
119
|
end
|
|
96
|
-
base_str = base_str
|
|
120
|
+
base_str = strip_language(base_str)
|
|
97
121
|
|
|
98
122
|
result = "#{id.supplement_type} (#{id.year}) to #{base_str}"
|
|
99
123
|
result += " (#{id.language})" if id.language
|
|
@@ -104,6 +128,16 @@ module Pubid
|
|
|
104
128
|
def render_annex(id)
|
|
105
129
|
format = effective_format(id)
|
|
106
130
|
|
|
131
|
+
# "BASE:YYYY Annex(es)" — the year is glued to the base, the marker
|
|
132
|
+
# carries none. Keep the base date instead of stripping it.
|
|
133
|
+
if id.year_on_base
|
|
134
|
+
base_str = strip_language(id.base_identifier.to_s)
|
|
135
|
+
marker = id.letter ? "Annex #{id.letter}" : "Annexes"
|
|
136
|
+
result = "#{base_str} #{marker}"
|
|
137
|
+
result += " (#{id.language})" if id.language
|
|
138
|
+
return result
|
|
139
|
+
end
|
|
140
|
+
|
|
107
141
|
base_format = if id.base_identifier.class.attributes.key?(:parsed_format) && id.base_identifier.parsed_format == "long"
|
|
108
142
|
:long
|
|
109
143
|
else
|
|
@@ -142,20 +176,6 @@ module Pubid
|
|
|
142
176
|
result += " (#{id.language})" if id.language
|
|
143
177
|
result
|
|
144
178
|
end
|
|
145
|
-
|
|
146
|
-
def render_base_identifier(id)
|
|
147
|
-
result = "#{id.publisher} #{id.type} #{id.code}"
|
|
148
|
-
result += ":#{id.date.render(context: @context)}" if id.date
|
|
149
|
-
|
|
150
|
-
if id.stage || id.iteration
|
|
151
|
-
result += " "
|
|
152
|
-
result += id.iteration.to_s if id.iteration
|
|
153
|
-
result += id.stage.to_s if id.stage
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
result += "(#{id.language})" if id.language
|
|
157
|
-
result
|
|
158
|
-
end
|
|
159
179
|
end
|
|
160
180
|
end
|
|
161
181
|
end
|
|
@@ -15,6 +15,64 @@ module Pubid
|
|
|
15
15
|
"short"
|
|
16
16
|
} # Track parsed format
|
|
17
17
|
|
|
18
|
+
# Serialization delta on top of Oiml::Identifier's shared block. The
|
|
19
|
+
# `code` (number/part/subpart/suffix) and `date` (year) components are
|
|
20
|
+
# flattened to top-level keys rather than nested hashes, mirroring ISO
|
|
21
|
+
# (lib/pubid/iso/identifier.rb). `type` is intentionally omitted
|
|
22
|
+
# (recomputed from the class on load).
|
|
23
|
+
key_value do
|
|
24
|
+
map "publisher", to: :publisher
|
|
25
|
+
map "number", with: { to: :number_to_kv, from: :number_from_kv }
|
|
26
|
+
map "part", with: { to: :part_to_kv, from: :part_from_kv }
|
|
27
|
+
map "subpart", with: { to: :subpart_to_kv, from: :subpart_from_kv }
|
|
28
|
+
map "suffix", with: { to: :suffix_to_kv, from: :suffix_from_kv }
|
|
29
|
+
map "space_suffix",
|
|
30
|
+
with: { to: :space_suffix_to_kv, from: :space_suffix_from_kv }
|
|
31
|
+
map "year", with: { to: :year_to_kv, from: :year_from_kv }
|
|
32
|
+
map "edition", to: :edition
|
|
33
|
+
map "stage", to: :stage
|
|
34
|
+
map "iteration", to: :iteration
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# --- code components flattened to top-level keys ---
|
|
38
|
+
def number_to_kv(model, doc) = emit_kv(doc, "number", model.code&.number)
|
|
39
|
+
def number_from_kv(model, value) = code_for(model).number = value.to_s
|
|
40
|
+
def part_to_kv(model, doc) = emit_kv(doc, "part", model.code&.part)
|
|
41
|
+
def part_from_kv(model, value) = code_for(model).part = value.to_s
|
|
42
|
+
def subpart_to_kv(model, doc) = emit_kv(doc, "subpart", model.code&.subpart)
|
|
43
|
+
def subpart_from_kv(model, value) = code_for(model).subpart = value.to_s
|
|
44
|
+
def suffix_to_kv(model, doc) = emit_kv(doc, "suffix", model.code&.suffix)
|
|
45
|
+
def suffix_from_kv(model, value) = code_for(model).suffix = value.to_s
|
|
46
|
+
|
|
47
|
+
def space_suffix_to_kv(model, doc)
|
|
48
|
+
return unless model.code&.space_suffix
|
|
49
|
+
|
|
50
|
+
doc.add_child(
|
|
51
|
+
Lutaml::KeyValue::DataModel::Element.new("space_suffix", true),
|
|
52
|
+
)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def space_suffix_from_kv(model, value)
|
|
56
|
+
code_for(model).space_suffix = value
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# --- date flattened to a top-level year ---
|
|
60
|
+
def year_to_kv(model, doc) = emit_kv(doc, "year", model.date&.year)
|
|
61
|
+
|
|
62
|
+
def year_from_kv(model, value)
|
|
63
|
+
(model.date ||= Pubid::Components::Date.new).year = value.to_s
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def emit_kv(doc, key, value)
|
|
67
|
+
return if value.nil? || value.to_s.empty?
|
|
68
|
+
|
|
69
|
+
doc.add_child(Lutaml::KeyValue::DataModel::Element.new(key, value.to_s))
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def code_for(model)
|
|
73
|
+
model.code ||= Components::Code.new
|
|
74
|
+
end
|
|
75
|
+
|
|
18
76
|
attr_reader :requested_format
|
|
19
77
|
|
|
20
78
|
# Type is determined by the subclass
|
|
@@ -5,13 +5,46 @@ module Pubid
|
|
|
5
5
|
class SupplementIdentifier < Identifier
|
|
6
6
|
# Base class for OIML supplements (amendments, annexes)
|
|
7
7
|
# These wrap a base identifier like ISO amendments
|
|
8
|
-
attribute :base_identifier, Oiml::Identifier
|
|
8
|
+
attribute :base_identifier, Oiml::Identifier, polymorphic: true
|
|
9
9
|
attribute :year, :string
|
|
10
10
|
attribute :language, :string
|
|
11
|
+
# True for the trailing-word shorthand ("OIML R 138:2009 Amendment"),
|
|
12
|
+
# where the supplement word is appended after a dated base instead of the
|
|
13
|
+
# "Amendment (YYYY) to BASE" prose form. The word itself comes from the
|
|
14
|
+
# concrete class (#supplement_type), so only this flag is stored.
|
|
15
|
+
attribute :trailing, :boolean, default: false
|
|
16
|
+
# True for the plus-joined form ("OIML B 10:2011+Amendment:2012") where
|
|
17
|
+
# both the base and the supplement carry their own year, joined by "+".
|
|
18
|
+
attribute :joined, :boolean, default: false
|
|
11
19
|
attribute :parsed_format, :string, default: -> {
|
|
12
20
|
"short"
|
|
13
21
|
} # Track supplement's parsed format
|
|
14
22
|
|
|
23
|
+
# Serialization delta on top of Oiml::Identifier's shared block. The
|
|
24
|
+
# nested base_identifier is (de)serialized recursively through the
|
|
25
|
+
# polymorphic router so its own `_type` selects the right subclass.
|
|
26
|
+
key_value do
|
|
27
|
+
map "base_identifier",
|
|
28
|
+
with: { to: :base_identifier_to_kv, from: :base_identifier_from_kv }
|
|
29
|
+
map "year", to: :year
|
|
30
|
+
map "trailing", to: :trailing
|
|
31
|
+
map "joined", to: :joined
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def base_identifier_to_kv(model, doc)
|
|
35
|
+
base = model.base_identifier
|
|
36
|
+
return unless base
|
|
37
|
+
|
|
38
|
+
doc.add_child(
|
|
39
|
+
Lutaml::KeyValue::DataModel::Element.new("base_identifier",
|
|
40
|
+
base.to_hash),
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def base_identifier_from_kv(model, value)
|
|
45
|
+
model.base_identifier = ::Pubid::Oiml::Identifier.from_hash(value) if value
|
|
46
|
+
end
|
|
47
|
+
|
|
15
48
|
attr_reader :requested_format
|
|
16
49
|
|
|
17
50
|
def to_s(format: nil, **opts)
|
|
@@ -1,23 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
# consistency with the other pubid flavors.
|
|
8
|
-
#
|
|
9
|
-
# NOTE: intentionally does NOT `extend Pubid::IdentifierFacade`. PLATEAU's
|
|
10
|
-
# `to_hash` currently raises (publisher stored as a String, not a Publisher
|
|
11
|
-
# component), so enabling the facade's identity check would route PLATEAU
|
|
12
|
-
# ids through a consumer's `to_hash` (e.g. relaton-index#save) and crash.
|
|
13
|
-
# Re-enable once PLATEAU serialization round-trips cleanly. See
|
|
14
|
-
# identifier_facade.rb.
|
|
15
|
-
module Identifier
|
|
16
|
-
# Delegate to the flavor module so callers can use
|
|
17
|
-
# `Pubid::Plateau::Identifier.parse` consistently with other flavors.
|
|
18
|
-
def self.parse(identifier)
|
|
19
|
-
Pubid::Plateau.parse(identifier)
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
3
|
+
# Pubid::Plateau::Identifier is the PLATEAU base class (a real Pubid::Identifier
|
|
4
|
+
# subclass); its body, `.parse`, and the Identifiers::Base back-compat alias
|
|
5
|
+
# live in identifiers/base.rb. This file just ensures it is loaded.
|
|
6
|
+
require_relative "identifiers/base"
|