pubid 2.0.0.pre.alpha.3 → 2.0.0.pre.alpha.4
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 +90 -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 +21 -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 +59 -12
- data/lib/pubid/oiml/renderer.rb +27 -17
- data/lib/pubid/oiml/single_identifier.rb +58 -0
- data/lib/pubid/oiml/supplement_identifier.rb +30 -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,21 @@ 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
|
+
|
|
82
|
+
# Determine supplement type. The trailing word ("Amendment"/"Errata")
|
|
83
|
+
# selects the class; the concrete class then carries the word via
|
|
84
|
+
# #supplement_type, so only the `trailing` flag needs storing.
|
|
81
85
|
supplement_class = if parsed_hash[:annex_letter] || parsed_hash[:annex_marker]
|
|
82
86
|
Identifiers::Annex
|
|
87
|
+
elsif marker == "Errata"
|
|
88
|
+
Identifiers::Errata
|
|
83
89
|
else
|
|
84
90
|
Identifiers::Amendment
|
|
85
91
|
end
|
|
86
92
|
|
|
87
93
|
supplement = supplement_class.new
|
|
94
|
+
supplement.trailing = true if marker
|
|
88
95
|
|
|
89
96
|
# Recursively parse base identifier
|
|
90
97
|
if parsed_hash[:base_identifier]
|
|
@@ -103,6 +110,13 @@ module Pubid
|
|
|
103
110
|
supplement.language = extract_language(parsed_hash[:language]) if parsed_hash[:language]
|
|
104
111
|
supplement.letter = parsed_hash[:annex_letter].to_s if parsed_hash[:annex_letter]
|
|
105
112
|
|
|
113
|
+
# Annex with no year of its own but a dated base ("R 60:2017 Annexes"):
|
|
114
|
+
# the year belongs to the base and must render glued to it.
|
|
115
|
+
if supplement.is_a?(Identifiers::Annex) && !year_value &&
|
|
116
|
+
supplement.base_identifier&.date
|
|
117
|
+
supplement.year_on_base = true
|
|
118
|
+
end
|
|
119
|
+
|
|
106
120
|
# Track if supplement itself was parsed with Edition format
|
|
107
121
|
if parsed_hash[:edition_format]
|
|
108
122
|
supplement.parsed_format = "long"
|
|
@@ -119,8 +133,8 @@ module Pubid
|
|
|
119
133
|
|
|
120
134
|
identifier = identifier_class.new
|
|
121
135
|
|
|
122
|
-
# Handle code (number-part-subpart) specially
|
|
123
|
-
if parsed_hash[:number] || parsed_hash[:part] || parsed_hash[:subpart]
|
|
136
|
+
# Handle code (number-part-subpart-suffix) specially
|
|
137
|
+
if parsed_hash[:number] || parsed_hash[:part] || parsed_hash[:subpart] || parsed_hash[:code_suffix]
|
|
124
138
|
code_attrs = {}
|
|
125
139
|
if parsed_hash[:number]
|
|
126
140
|
code_attrs[:number] =
|
|
@@ -131,6 +145,10 @@ module Pubid
|
|
|
131
145
|
code_attrs[:subpart] =
|
|
132
146
|
parsed_hash[:subpart].to_s
|
|
133
147
|
end
|
|
148
|
+
if parsed_hash[:code_suffix]
|
|
149
|
+
code_attrs[:suffix] = parsed_hash[:code_suffix].to_s
|
|
150
|
+
code_attrs[:space_suffix] = true if parsed_hash.key?(:space_suffix)
|
|
151
|
+
end
|
|
134
152
|
identifier.code = Components::Code.new(**code_attrs)
|
|
135
153
|
end
|
|
136
154
|
|
|
@@ -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,8 @@ 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 | trailing_supplement_identifier | base_identifier
|
|
22
23
|
end
|
|
23
24
|
|
|
24
25
|
# Publisher - always "OIML"
|
|
@@ -30,14 +31,33 @@ module Pubid
|
|
|
30
31
|
# Number with optional part and subpart
|
|
31
32
|
rule(:number_only) { digits.as(:number) }
|
|
32
33
|
|
|
33
|
-
|
|
34
|
+
# Part number, optionally a slashed multi-part continuation captured
|
|
35
|
+
# verbatim, e.g. "1/-2" in "OIML R 46-1/-2:2012".
|
|
36
|
+
rule(:part_number) do
|
|
37
|
+
dash >> (digits >> (slash >> dash >> digits).repeat).as(:part)
|
|
38
|
+
end
|
|
34
39
|
|
|
35
40
|
rule(:subpart_number) { dash >> digits.as(:subpart) }
|
|
36
41
|
|
|
42
|
+
# Free-form named/lettered code suffix glued after the numeric part:
|
|
43
|
+
# -GUM 1, -special, -sup, -erratum, -A, -ISO3930, -Amend, -Amended_2012
|
|
44
|
+
# plus the space-separated "Brochure" label. "GUM <n>" is tried first so
|
|
45
|
+
# its trailing number isn't lost to the bare-word alternative. The
|
|
46
|
+
# trailing `(_?digits)*` captures joint codes ("ISO3930") and the
|
|
47
|
+
# "Amended_2012" amendment label (the hand-off treats these as part
|
|
48
|
+
# suffixes, preserved verbatim for round-trip).
|
|
49
|
+
rule(:named_suffix) do
|
|
50
|
+
(dash >> (
|
|
51
|
+
(str("GUM") >> space >> digits) |
|
|
52
|
+
(match("[A-Za-z]").repeat(1) >> (str("_").maybe >> digits).repeat)
|
|
53
|
+
).as(:code_suffix)) |
|
|
54
|
+
(space >> str("Brochure").as(:code_suffix) >> str("").as(:space_suffix))
|
|
55
|
+
end
|
|
56
|
+
|
|
37
57
|
rule(:full_number) do
|
|
38
|
-
(number_only >> part_number >> subpart_number) |
|
|
39
|
-
(number_only >> part_number) |
|
|
40
|
-
number_only
|
|
58
|
+
(number_only >> part_number >> subpart_number >> named_suffix.maybe) |
|
|
59
|
+
(number_only >> part_number >> named_suffix.maybe) |
|
|
60
|
+
(number_only >> named_suffix.maybe)
|
|
41
61
|
end
|
|
42
62
|
|
|
43
63
|
# Edition number - ordinal numbers
|
|
@@ -82,8 +102,18 @@ module Pubid
|
|
|
82
102
|
end
|
|
83
103
|
|
|
84
104
|
# Language codes
|
|
105
|
+
#
|
|
106
|
+
# relaton-data-oiml encodes the publication language with its own code
|
|
107
|
+
# set (lib/oiml_fetcher.rb DOCID_LANG_CODE): single uppercase letters
|
|
108
|
+
# E F D R S C A U X, plus two-letter PO/PT/PE/SR. The two-letter OIML
|
|
109
|
+
# codes must be tried before the single-letter rule so e.g. "PE" isn't
|
|
110
|
+
# left with a dangling "E".
|
|
85
111
|
rule(:lang_single) do
|
|
86
|
-
match("[
|
|
112
|
+
match("[EFRXDSCAU]")
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
rule(:lang_multi_oiml) do
|
|
116
|
+
str("PO") | str("PT") | str("PE") | str("SR")
|
|
87
117
|
end
|
|
88
118
|
|
|
89
119
|
rule(:lang_multi) do
|
|
@@ -93,8 +123,9 @@ module Pubid
|
|
|
93
123
|
rule(:language_code) do
|
|
94
124
|
(
|
|
95
125
|
(lang_single >> slash >> lang_single) | # E/F
|
|
96
|
-
|
|
97
|
-
|
|
126
|
+
lang_multi_oiml | # PO, PT, PE, SR
|
|
127
|
+
lang_single | # E, F, D, R, S, C, A, U, X
|
|
128
|
+
lang_multi # en, fr
|
|
98
129
|
).as(:language)
|
|
99
130
|
end
|
|
100
131
|
|
|
@@ -131,21 +162,37 @@ module Pubid
|
|
|
131
162
|
language_portion.maybe.as(:language)
|
|
132
163
|
end
|
|
133
164
|
|
|
134
|
-
#
|
|
165
|
+
# Trailing supplement word - "BASE Amendment" / "BASE Errata" where the
|
|
166
|
+
# publication year stays on the base (e.g. "OIML R 138:2009 Amendment").
|
|
167
|
+
# amendment_short is tried first; it only matches when a year follows the
|
|
168
|
+
# word, so the no-year trailing form falls through to here.
|
|
169
|
+
rule(:trailing_supplement_identifier) do
|
|
170
|
+
base_without_language.as(:base_identifier) >>
|
|
171
|
+
space >> (str("Amendment") | str("Errata")).as(:trailing_marker) >>
|
|
172
|
+
language_portion.maybe.as(:language)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Annex identifier - "BASE Annexes Edition YYYY" / "BASE Annexes:YYYY" /
|
|
176
|
+
# "BASE:YYYY Annexes" (year on the base, no annex year).
|
|
135
177
|
rule(:annex_identifier) do
|
|
136
178
|
base_without_language.as(:base_identifier) >>
|
|
137
179
|
space >> str("Annexes").as(:annex_marker) >>
|
|
138
180
|
(
|
|
139
181
|
(space >> edition_text >> space >> year_digits.as(:year)).as(:edition_format) |
|
|
140
182
|
(colon >> year_digits.as(:year))
|
|
141
|
-
) >>
|
|
183
|
+
).maybe >>
|
|
142
184
|
language_portion.maybe.as(:language)
|
|
143
185
|
end
|
|
144
186
|
|
|
145
|
-
# Annex
|
|
187
|
+
# Annex letter or letter range, e.g. "A" or "B-C".
|
|
188
|
+
rule(:annex_letter_value) do
|
|
189
|
+
(match("[A-Z]") >> (dash >> match("[A-Z]")).maybe).as(:annex_letter)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Annex with letter - "BASE Annex A Edition YYYY" / "BASE:YYYY Annex B-C"
|
|
146
193
|
rule(:annex_letter_identifier) do
|
|
147
194
|
base_without_language.as(:base_identifier) >>
|
|
148
|
-
space >> str("Annex") >> space >>
|
|
195
|
+
space >> str("Annex") >> space >> annex_letter_value >>
|
|
149
196
|
((space >> edition_text >> space >> year_digits.as(:year)) | (colon >> year_digits.as(:year))).maybe >>
|
|
150
197
|
language_portion.maybe.as(:language)
|
|
151
198
|
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,16 @@ module Pubid
|
|
|
80
84
|
def render_supplement(id)
|
|
81
85
|
format = effective_format(id)
|
|
82
86
|
|
|
87
|
+
# Trailing-word shorthand: "BASE Amendment" / "BASE Errata" with the
|
|
88
|
+
# publication year kept on the base identifier. The word comes from the
|
|
89
|
+
# concrete supplement class.
|
|
90
|
+
if id.trailing
|
|
91
|
+
base_str = strip_language(id.base_identifier.to_s)
|
|
92
|
+
result = "#{base_str} #{id.supplement_type}"
|
|
93
|
+
result += " (#{id.language})" if id.language
|
|
94
|
+
return result
|
|
95
|
+
end
|
|
96
|
+
|
|
83
97
|
base_format = if format && format != :short
|
|
84
98
|
format
|
|
85
99
|
elsif id.base_identifier.class.attributes.key?(:parsed_format) && id.base_identifier.parsed_format == "long"
|
|
@@ -93,7 +107,7 @@ module Pubid
|
|
|
93
107
|
else
|
|
94
108
|
id.base_identifier.to_s
|
|
95
109
|
end
|
|
96
|
-
base_str = base_str
|
|
110
|
+
base_str = strip_language(base_str)
|
|
97
111
|
|
|
98
112
|
result = "#{id.supplement_type} (#{id.year}) to #{base_str}"
|
|
99
113
|
result += " (#{id.language})" if id.language
|
|
@@ -104,6 +118,16 @@ module Pubid
|
|
|
104
118
|
def render_annex(id)
|
|
105
119
|
format = effective_format(id)
|
|
106
120
|
|
|
121
|
+
# "BASE:YYYY Annex(es)" — the year is glued to the base, the marker
|
|
122
|
+
# carries none. Keep the base date instead of stripping it.
|
|
123
|
+
if id.year_on_base
|
|
124
|
+
base_str = strip_language(id.base_identifier.to_s)
|
|
125
|
+
marker = id.letter ? "Annex #{id.letter}" : "Annexes"
|
|
126
|
+
result = "#{base_str} #{marker}"
|
|
127
|
+
result += " (#{id.language})" if id.language
|
|
128
|
+
return result
|
|
129
|
+
end
|
|
130
|
+
|
|
107
131
|
base_format = if id.base_identifier.class.attributes.key?(:parsed_format) && id.base_identifier.parsed_format == "long"
|
|
108
132
|
:long
|
|
109
133
|
else
|
|
@@ -142,20 +166,6 @@ module Pubid
|
|
|
142
166
|
result += " (#{id.language})" if id.language
|
|
143
167
|
result
|
|
144
168
|
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
169
|
end
|
|
160
170
|
end
|
|
161
171
|
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,42 @@ 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
|
|
11
16
|
attribute :parsed_format, :string, default: -> {
|
|
12
17
|
"short"
|
|
13
18
|
} # Track supplement's parsed format
|
|
14
19
|
|
|
20
|
+
# Serialization delta on top of Oiml::Identifier's shared block. The
|
|
21
|
+
# nested base_identifier is (de)serialized recursively through the
|
|
22
|
+
# polymorphic router so its own `_type` selects the right subclass.
|
|
23
|
+
key_value do
|
|
24
|
+
map "base_identifier",
|
|
25
|
+
with: { to: :base_identifier_to_kv, from: :base_identifier_from_kv }
|
|
26
|
+
map "year", to: :year
|
|
27
|
+
map "trailing", to: :trailing
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def base_identifier_to_kv(model, doc)
|
|
31
|
+
base = model.base_identifier
|
|
32
|
+
return unless base
|
|
33
|
+
|
|
34
|
+
doc.add_child(
|
|
35
|
+
Lutaml::KeyValue::DataModel::Element.new("base_identifier",
|
|
36
|
+
base.to_hash),
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def base_identifier_from_kv(model, value)
|
|
41
|
+
model.base_identifier = ::Pubid::Oiml::Identifier.from_hash(value) if value
|
|
42
|
+
end
|
|
43
|
+
|
|
15
44
|
attr_reader :requested_format
|
|
16
45
|
|
|
17
46
|
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"
|
|
@@ -2,50 +2,59 @@
|
|
|
2
2
|
|
|
3
3
|
module Pubid
|
|
4
4
|
module Plateau
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def publisher
|
|
15
|
-
"PLATEAU"
|
|
16
|
-
end
|
|
5
|
+
# Base class for all PLATEAU identifiers. Canonical name
|
|
6
|
+
# Pubid::Plateau::Identifier (Identifiers::Base is a back-compat alias).
|
|
7
|
+
class Identifier < ::Pubid::Identifier
|
|
8
|
+
# Delegate to the flavor module so callers can use
|
|
9
|
+
# `Pubid::Plateau::Identifier.parse` consistently with other flavors.
|
|
10
|
+
def self.parse(identifier)
|
|
11
|
+
Pubid::Plateau.parse(identifier)
|
|
12
|
+
end
|
|
17
13
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
raise NotImplementedError, "Subclasses must implement type_string"
|
|
21
|
-
end
|
|
14
|
+
attribute :number, :integer
|
|
15
|
+
attribute :annex, :integer, default: -> {}
|
|
22
16
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
17
|
+
# Stored as a plain string (always "PLATEAU") so it round-trips through
|
|
18
|
+
# to_hash/from_hash. Was a `def publisher` method, which made lutaml
|
|
19
|
+
# serialize a String against the Components::Publisher attribute.
|
|
20
|
+
attribute :publisher, :string, default: -> { "PLATEAU" }
|
|
26
21
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
# Subclasses must implement type_string
|
|
23
|
+
def type_string
|
|
24
|
+
raise NotImplementedError, "Subclasses must implement type_string"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def formatted_number
|
|
28
|
+
"#%02d" % number
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def formatted_annex
|
|
32
|
+
annex ? "-#{annex}" : ""
|
|
33
|
+
end
|
|
30
34
|
|
|
31
|
-
|
|
32
|
-
|
|
35
|
+
def ==(other)
|
|
36
|
+
return false unless other.class == self.class
|
|
33
37
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
38
|
+
number == other.number &&
|
|
39
|
+
annex == other.annex
|
|
40
|
+
end
|
|
37
41
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
end
|
|
45
|
-
hash[:annex] = annex if annex
|
|
46
|
-
hash
|
|
42
|
+
# Include type_string and annex in serialization for round-trip compatibility
|
|
43
|
+
def base_hash
|
|
44
|
+
hash = super
|
|
45
|
+
if self.class.attributes.key?(:type_string) && type_string
|
|
46
|
+
hash[:type] =
|
|
47
|
+
type_string
|
|
47
48
|
end
|
|
49
|
+
hash[:annex] = annex if annex
|
|
50
|
+
hash
|
|
48
51
|
end
|
|
49
52
|
end
|
|
53
|
+
|
|
54
|
+
module Identifiers
|
|
55
|
+
# Backward-compatible alias: PLATEAU's base class used to be
|
|
56
|
+
# Pubid::Plateau::Identifiers::Base. It is now Pubid::Plateau::Identifier.
|
|
57
|
+
Base = Pubid::Plateau::Identifier
|
|
58
|
+
end
|
|
50
59
|
end
|
|
51
60
|
end
|