pubid 2.0.0.pre.alpha.1 → 2.0.0.pre.alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/data/nist/update_codes.yaml +2 -0
- data/lib/pubid/amca/identifier.rb +39 -0
- data/lib/pubid/ansi/identifier.rb +42 -0
- data/lib/pubid/api/identifier.rb +47 -0
- data/lib/pubid/ashrae/identifier.rb +39 -0
- data/lib/pubid/asme/identifier.rb +46 -0
- data/lib/pubid/astm/identifier.rb +77 -0
- data/lib/pubid/bsi/identifier.rb +60 -0
- data/lib/pubid/ccsds/identifier.rb +68 -0
- data/lib/pubid/ccsds/identifiers/base.rb +11 -0
- data/lib/pubid/ccsds/single_identifier.rb +4 -1
- data/lib/pubid/cen_cenelec/identifier.rb +37 -0
- data/lib/pubid/cie/identifier.rb +53 -0
- data/lib/pubid/components/factory.rb +50 -0
- data/lib/pubid/components/typed_stage.rb +4 -0
- data/lib/pubid/components.rb +1 -0
- data/lib/pubid/csa/identifier.rb +56 -0
- data/lib/pubid/etsi/identifier.rb +43 -0
- data/lib/pubid/identifier.rb +8 -1
- data/lib/pubid/idf/identifier.rb +60 -0
- data/lib/pubid/iec/builder.rb +2 -1
- data/lib/pubid/iec/components/code.rb +2 -1
- data/lib/pubid/iec/components/publisher.rb +2 -1
- data/lib/pubid/iec/identifier.rb +235 -0
- data/lib/pubid/iec/identifiers/base.rb +4 -0
- data/lib/pubid/iec/identifiers/consolidated_identifier.rb +0 -4
- data/lib/pubid/iec/identifiers/fragment_identifier.rb +0 -4
- data/lib/pubid/iec/identifiers/sheet_identifier.rb +0 -4
- data/lib/pubid/iec/identifiers/vap_identifier.rb +0 -4
- data/lib/pubid/iec/parser.rb +7 -2
- data/lib/pubid/iec/urn_generator.rb +57 -171
- data/lib/pubid/iec/urn_parser.rb +53 -252
- data/lib/pubid/ieee/identifier.rb +41 -0
- data/lib/pubid/iho/identifier.rb +42 -0
- data/lib/pubid/iho/identifiers/base.rb +1 -1
- data/lib/pubid/iho/identifiers/bibliographic.rb +0 -4
- data/lib/pubid/iho/identifiers/circular_letter.rb +0 -4
- data/lib/pubid/iho/identifiers/miscellaneous.rb +0 -4
- data/lib/pubid/iho/identifiers/publication.rb +0 -4
- data/lib/pubid/iho/identifiers/standard.rb +0 -4
- data/lib/pubid/iho/urn_generator.rb +1 -1
- data/lib/pubid/iso/builder.rb +5 -1
- data/lib/pubid/iso/identifier.rb +261 -0
- data/lib/pubid/iso/parser.rb +4 -2
- data/lib/pubid/iso/scheme.rb +6 -0
- data/lib/pubid/iso/single_identifier.rb +6 -3
- data/lib/pubid/iso/urn_generator.rb +17 -3
- data/lib/pubid/iso/urn_parser.rb +16 -2
- data/lib/pubid/itu/identifier.rb +87 -22
- data/lib/pubid/jcgm/identifier.rb +43 -0
- data/lib/pubid/jis/identifier.rb +43 -0
- data/lib/pubid/nist/builder.rb +174 -5
- data/lib/pubid/nist/components/edition.rb +16 -0
- data/lib/pubid/nist/components/supplement.rb +88 -21
- data/lib/pubid/nist/identifier.rb +62 -0
- data/lib/pubid/nist/identifiers/base.rb +103 -24
- data/lib/pubid/nist/identifiers/circular_supplement.rb +1 -1
- data/lib/pubid/nist/identifiers/crpl_report.rb +1 -4
- data/lib/pubid/nist/identifiers/federal_information_processing_standards.rb +10 -0
- data/lib/pubid/nist/identifiers/report.rb +1 -2
- data/lib/pubid/nist/parser.rb +36 -3
- data/lib/pubid/nist/supplement_identifier.rb +8 -24
- data/lib/pubid/nist/urn_generator.rb +14 -8
- data/lib/pubid/nist.rb +1 -0
- data/lib/pubid/oiml/identifier.rb +50 -0
- data/lib/pubid/plateau/identifier.rb +57 -0
- data/lib/pubid/plateau.rb +1 -0
- data/lib/pubid/renderers/base.rb +34 -0
- data/lib/pubid/renderers/directives_renderer.rb +13 -14
- data/lib/pubid/renderers/guide_renderer.rb +5 -1
- data/lib/pubid/renderers/human_readable.rb +20 -8
- data/lib/pubid/renderers/iwa_renderer.rb +5 -1
- data/lib/pubid/renderers/supplement_renderer.rb +4 -1
- data/lib/pubid/rendering/context.rb +5 -2
- data/lib/pubid/sae/identifier.rb +23 -0
- data/lib/pubid/scheme.rb +12 -0
- data/lib/pubid/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 16698d2e32777261efbf6842754351b03a2a0d272992ca1525ebd3fbedaf648d
|
|
4
|
+
data.tar.gz: 46d2aa4fb40d9f9ab3fcf7f42bdc3c9b2662637653a9e5c98f26baebecaea0cd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 638b16ccaed8a371fecee3ec8372a9790ae4649d689d0aeafa306b4b1681e0df42365d5ede2da46da743f8b42e7a70ddcc882774a6c6c333a1a2fec8d30255e4
|
|
7
|
+
data.tar.gz: 2d55ed5b29d6121665c808d697832307bcea629345b7782308c5c0fe76ffc9baae849f752a8f75b5d77c7b3aaed782be9e15dcc9e1dc85e7c229ae77cf4431b8
|
data/data/nist/update_codes.yaml
CHANGED
|
@@ -26,7 +26,9 @@ NIST.CSWP.01162020pt: NIST.CSWP.01162020(por)
|
|
|
26
26
|
FIPS PUB: FIPS
|
|
27
27
|
NBS CRPL c4-4: NBS CRPL 4-4
|
|
28
28
|
NIST AMS 300-8r1 (February 2021 update): NIST AMS 300-8r1/Upd1-202102
|
|
29
|
+
NIST AMS 300-8r1/upd: NIST AMS 300-8r1/Upd1-202102
|
|
29
30
|
NIST IR 8115r1-upd: NIST IR 8115r1/Upd1-202103
|
|
31
|
+
NIST IR 8170-upd: NIST IR 8170/Upd1-202003
|
|
30
32
|
NIST TN 2150-upd: NIST TN 2150/Upd1-202102
|
|
31
33
|
NBS IR 73-197r: NBS IR 73-197r1
|
|
32
34
|
NBS.HB.105-1r1990: NIST.HB.105-1r1990
|
|
@@ -13,6 +13,45 @@ module Pubid
|
|
|
13
13
|
rescue Parslet::ParseFailed => e
|
|
14
14
|
raise "Failed to parse ACMA identifier '#{identifier}': #{e.message}"
|
|
15
15
|
end
|
|
16
|
+
|
|
17
|
+
# Factory that builds an AMCA identifier from a hash of primitives.
|
|
18
|
+
#
|
|
19
|
+
# Dispatch on `:type`:
|
|
20
|
+
# * `:standard` (default) → Identifiers::Standard
|
|
21
|
+
# * `:publication` → Identifiers::Publication
|
|
22
|
+
# * `:interpretation` → Identifiers::Interpretation
|
|
23
|
+
#
|
|
24
|
+
# AMCA stores the "AMCA" prefix in the `copublisher` string
|
|
25
|
+
# attribute (matching parsed output); `.create` defaults it to
|
|
26
|
+
# "AMCA" unless the caller supplies one.
|
|
27
|
+
def self.create(type: nil, **opts)
|
|
28
|
+
klass = resolve_create_class(type)
|
|
29
|
+
klass.new(**coerce_create_attrs(opts))
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.resolve_create_class(type)
|
|
33
|
+
case type&.to_sym
|
|
34
|
+
when nil, :standard then Identifiers::Standard
|
|
35
|
+
when :publication then Identifiers::Publication
|
|
36
|
+
when :interpretation then Identifiers::Interpretation
|
|
37
|
+
else
|
|
38
|
+
raise ArgumentError, "Unknown AMCA type: #{type.inspect}"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.coerce_create_attrs(opts)
|
|
43
|
+
attrs = { copublisher: (opts[:copublisher] || "AMCA").to_s }
|
|
44
|
+
if (v = opts[:code] || opts[:number])
|
|
45
|
+
attrs[:code] = Pubid::Components::Code.new(value: v.to_s)
|
|
46
|
+
end
|
|
47
|
+
if (v = opts[:year])
|
|
48
|
+
attrs[:year] = Pubid::Components::Date.new(year: v.to_s)
|
|
49
|
+
end
|
|
50
|
+
attrs[:suffix] = opts[:suffix].to_s if opts[:suffix]
|
|
51
|
+
attrs[:reaffirmed] = opts[:reaffirmed].to_s if opts[:reaffirmed]
|
|
52
|
+
attrs
|
|
53
|
+
end
|
|
54
|
+
private_class_method :resolve_create_class, :coerce_create_attrs
|
|
16
55
|
end
|
|
17
56
|
end
|
|
18
57
|
end
|
|
@@ -8,6 +8,48 @@ module Pubid
|
|
|
8
8
|
parsed = Pubid::Ansi::Parser.new.parse(string)
|
|
9
9
|
Pubid::Ansi::Builder.new(Pubid::Ansi::Scheme).build(parsed)
|
|
10
10
|
end
|
|
11
|
+
|
|
12
|
+
# Factory that builds an ANSI identifier from a hash of primitives.
|
|
13
|
+
#
|
|
14
|
+
# Defaults to {Identifiers::Standard} (ANSI has Standard and
|
|
15
|
+
# AmericanNationalStandard, both sharing the same type key `:ans`).
|
|
16
|
+
#
|
|
17
|
+
# ANSI quirk: the parser stores the publication year in the `part`
|
|
18
|
+
# attribute, not `date`. `.create` mirrors that — `:year` is coerced
|
|
19
|
+
# into `part` so the rendered output matches a parsed identifier.
|
|
20
|
+
def self.create(type: nil, **opts)
|
|
21
|
+
klass = resolve_create_class(type)
|
|
22
|
+
klass.new(**coerce_create_attrs(opts))
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.resolve_create_class(type)
|
|
26
|
+
case type&.to_sym
|
|
27
|
+
when nil, :ans, :standard
|
|
28
|
+
Identifiers::Standard
|
|
29
|
+
when :american_national_standard
|
|
30
|
+
Identifiers::AmericanNationalStandard
|
|
31
|
+
else
|
|
32
|
+
raise ArgumentError, "Unknown ANSI type: #{type.inspect}"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.coerce_create_attrs(opts)
|
|
37
|
+
attrs = {
|
|
38
|
+
publisher: Pubid::Components::Publisher.new(
|
|
39
|
+
body: (opts[:publisher] || "ANSI").to_s,
|
|
40
|
+
),
|
|
41
|
+
}
|
|
42
|
+
if (v = opts[:number])
|
|
43
|
+
attrs[:number] = Pubid::Components::Code.new(value: v.to_s)
|
|
44
|
+
end
|
|
45
|
+
# ANSI parser stores year in part; mirror that here.
|
|
46
|
+
year_or_part = opts[:year] || opts[:part]
|
|
47
|
+
if year_or_part
|
|
48
|
+
attrs[:part] = Pubid::Components::Code.new(value: year_or_part.to_s)
|
|
49
|
+
end
|
|
50
|
+
attrs
|
|
51
|
+
end
|
|
52
|
+
private_class_method :resolve_create_class, :coerce_create_attrs
|
|
11
53
|
end
|
|
12
54
|
end
|
|
13
55
|
end
|
data/lib/pubid/api/identifier.rb
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require_relative "parser"
|
|
4
4
|
require_relative "builder"
|
|
5
5
|
require_relative "single_identifier"
|
|
6
|
+
require_relative "scheme"
|
|
6
7
|
|
|
7
8
|
module Pubid
|
|
8
9
|
module Api
|
|
@@ -16,6 +17,52 @@ module Pubid
|
|
|
16
17
|
rescue Parslet::ParseFailed => e
|
|
17
18
|
raise e
|
|
18
19
|
end
|
|
20
|
+
|
|
21
|
+
# Factory that builds an API identifier from a hash of primitives.
|
|
22
|
+
# Dispatch on `:type` to the matching subclass; default is
|
|
23
|
+
# {Identifiers::Standard}.
|
|
24
|
+
#
|
|
25
|
+
# API identifiers have a hardcoded "API" publisher (via
|
|
26
|
+
# SingleIdentifier#publisher method) so the `:publisher` kwarg is
|
|
27
|
+
# silently dropped.
|
|
28
|
+
TYPE_KEY_TO_KLASS = {
|
|
29
|
+
std: "Standard",
|
|
30
|
+
rp: "RecommendedPractice",
|
|
31
|
+
spec: "Specification",
|
|
32
|
+
tr: "TechnicalReport",
|
|
33
|
+
bull: "Bulletin",
|
|
34
|
+
mpms: "Mpms",
|
|
35
|
+
cos: "ContinuousOperationsStandard",
|
|
36
|
+
publ: "Publication",
|
|
37
|
+
typeless: "TypelessStandard",
|
|
38
|
+
}.freeze
|
|
39
|
+
|
|
40
|
+
def self.create(type: nil, **opts)
|
|
41
|
+
klass = resolve_create_class(type)
|
|
42
|
+
klass.new(**coerce_create_attrs(opts))
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def self.resolve_create_class(type)
|
|
46
|
+
return Identifiers::Standard if type.nil?
|
|
47
|
+
|
|
48
|
+
klass_name = TYPE_KEY_TO_KLASS[type.to_sym]
|
|
49
|
+
raise ArgumentError, "Unknown API type: #{type.inspect}" unless klass_name
|
|
50
|
+
|
|
51
|
+
Identifiers.const_get(klass_name)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def self.coerce_create_attrs(opts)
|
|
55
|
+
attrs = {}
|
|
56
|
+
if (v = opts[:code] || opts[:number])
|
|
57
|
+
attrs[:code] = Pubid::Api::Components::Code.new(value: v.to_s)
|
|
58
|
+
end
|
|
59
|
+
%i[part year reaffirmation].each do |k|
|
|
60
|
+
attrs[k] = opts[k].to_s unless opts[k].nil?
|
|
61
|
+
end
|
|
62
|
+
# TODO(create-shim): :publisher silently dropped (API hardcoded).
|
|
63
|
+
attrs
|
|
64
|
+
end
|
|
65
|
+
private_class_method :resolve_create_class, :coerce_create_attrs
|
|
19
66
|
end
|
|
20
67
|
end
|
|
21
68
|
end
|
|
@@ -13,6 +13,45 @@ module Pubid
|
|
|
13
13
|
rescue Parslet::ParseFailed => e
|
|
14
14
|
raise "Failed to parse ASHRAE identifier '#{identifier}': #{e.message}"
|
|
15
15
|
end
|
|
16
|
+
|
|
17
|
+
# Factory that builds an ASHRAE identifier from a hash of primitives.
|
|
18
|
+
# Dispatch on `:type` to the matching subclass; default is
|
|
19
|
+
# {Identifiers::Standard}.
|
|
20
|
+
TYPE_KEY_TO_KLASS = {
|
|
21
|
+
standard: "Standard",
|
|
22
|
+
guideline: "Guideline",
|
|
23
|
+
addendum: "Addendum",
|
|
24
|
+
addenda_package: "AddendaPackage",
|
|
25
|
+
combined_addenda: "CombinedAddenda",
|
|
26
|
+
errata: "Errata",
|
|
27
|
+
interpretation: "Interpretation",
|
|
28
|
+
}.freeze
|
|
29
|
+
|
|
30
|
+
def self.create(type: nil, **opts)
|
|
31
|
+
klass = resolve_create_class(type)
|
|
32
|
+
klass.new(**coerce_create_attrs(opts))
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.resolve_create_class(type)
|
|
36
|
+
return Identifiers::Standard if type.nil?
|
|
37
|
+
|
|
38
|
+
klass_name = TYPE_KEY_TO_KLASS[type.to_sym]
|
|
39
|
+
raise ArgumentError, "Unknown ASHRAE type: #{type.inspect}" unless klass_name
|
|
40
|
+
|
|
41
|
+
Identifiers.const_get(klass_name)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def self.coerce_create_attrs(opts)
|
|
45
|
+
attrs = { publisher: (opts[:publisher] || "ASHRAE").to_s }
|
|
46
|
+
if (v = opts[:code] || opts[:number])
|
|
47
|
+
attrs[:code] = v.to_s
|
|
48
|
+
end
|
|
49
|
+
%i[year suffix amendment reaffirmed copublisher].each do |k|
|
|
50
|
+
attrs[k] = opts[k].to_s unless opts[k].nil?
|
|
51
|
+
end
|
|
52
|
+
attrs
|
|
53
|
+
end
|
|
54
|
+
private_class_method :resolve_create_class, :coerce_create_attrs
|
|
16
55
|
end
|
|
17
56
|
end
|
|
18
57
|
end
|
|
@@ -10,6 +10,52 @@ module Pubid
|
|
|
10
10
|
parsed = parser.parse(str)
|
|
11
11
|
builder.build(parsed)
|
|
12
12
|
end
|
|
13
|
+
|
|
14
|
+
# Factory that builds an ASME identifier from a hash of primitives.
|
|
15
|
+
# Default is {Identifiers::Standard}. ASME's Code Component has
|
|
16
|
+
# `:designator` (leading letters, e.g. "B") and `:number` (rest,
|
|
17
|
+
# e.g. "16.34"); `.create` accepts either an already-split pair or
|
|
18
|
+
# a combined `:code` string which it splits at the first digit.
|
|
19
|
+
def self.create(**opts)
|
|
20
|
+
Identifiers::Standard.new(**coerce_create_attrs(opts))
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.coerce_create_attrs(opts)
|
|
24
|
+
attrs = { publisher: (opts[:publisher] || "ASME").to_s }
|
|
25
|
+
|
|
26
|
+
if opts[:designator] || opts[:number]
|
|
27
|
+
attrs[:code] = Pubid::Asme::Components::Code.new(
|
|
28
|
+
designator: opts[:designator]&.to_s,
|
|
29
|
+
number: opts[:number]&.to_s,
|
|
30
|
+
)
|
|
31
|
+
elsif (combined = opts[:code])
|
|
32
|
+
designator, number = split_asme_code(combined.to_s)
|
|
33
|
+
attrs[:code] = Pubid::Asme::Components::Code.new(
|
|
34
|
+
designator: designator,
|
|
35
|
+
number: number,
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
%i[year reaffirmation language csa_number draft_year
|
|
40
|
+
revision_note parenthetical_revision ptc_suffix
|
|
41
|
+
joint_publisher first_publisher first_code].each do |k|
|
|
42
|
+
attrs[k] = opts[k].to_s unless opts[k].nil?
|
|
43
|
+
end
|
|
44
|
+
attrs[:handbook] = opts[:handbook] if opts.key?(:handbook)
|
|
45
|
+
attrs
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# ASME codes pair a letter designator with a numeric/dotted number.
|
|
49
|
+
# Split at the first digit. "B16.34" → ["B", "16.34"]; "Y14.5" →
|
|
50
|
+
# ["Y", "14.5"]; "BPVC.III.1.NB" → ["BPVC.III.1.NB", nil] (no
|
|
51
|
+
# leading all-letters before the first digit — pass through).
|
|
52
|
+
def self.split_asme_code(str)
|
|
53
|
+
match = str.match(/\A([A-Za-z]+)(\d.*)\z/)
|
|
54
|
+
return match.captures if match
|
|
55
|
+
|
|
56
|
+
[str, nil]
|
|
57
|
+
end
|
|
58
|
+
private_class_method :coerce_create_attrs, :split_asme_code
|
|
13
59
|
end
|
|
14
60
|
end
|
|
15
61
|
end
|
|
@@ -10,6 +10,83 @@ module Pubid
|
|
|
10
10
|
parsed = parser.parse(str)
|
|
11
11
|
builder.build(parsed)
|
|
12
12
|
end
|
|
13
|
+
|
|
14
|
+
# Factory that builds an ASTM identifier from a hash of primitives.
|
|
15
|
+
# Default is {Identifiers::Standard}.
|
|
16
|
+
#
|
|
17
|
+
# ASTM's Code Component has :letter (single letter A–Z) + :number
|
|
18
|
+
# (rest); `.create` accepts either an already-split pair or a
|
|
19
|
+
# combined `:code` string which it splits at the first digit.
|
|
20
|
+
TYPE_KEY_TO_KLASS = {
|
|
21
|
+
standard: "Standard",
|
|
22
|
+
adjunct: "Adjunct",
|
|
23
|
+
manual: "Manual",
|
|
24
|
+
monograph: "Monograph",
|
|
25
|
+
research_report: "ResearchReport",
|
|
26
|
+
technical_report: "TechnicalReport",
|
|
27
|
+
data_series: "DataSeries",
|
|
28
|
+
iso_dual_published: "IsoDualPublished",
|
|
29
|
+
work_in_progress: "WorkInProgress",
|
|
30
|
+
}.freeze
|
|
31
|
+
|
|
32
|
+
def self.create(type: nil, **opts)
|
|
33
|
+
klass = resolve_create_class(type)
|
|
34
|
+
klass.new(**coerce_create_attrs(opts, klass: klass))
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.resolve_create_class(type)
|
|
38
|
+
return Identifiers::Standard if type.nil?
|
|
39
|
+
|
|
40
|
+
klass_name = TYPE_KEY_TO_KLASS[type.to_sym]
|
|
41
|
+
raise ArgumentError, "Unknown ASTM type: #{type.inspect}" unless klass_name
|
|
42
|
+
|
|
43
|
+
Identifiers.const_get(klass_name)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def self.coerce_create_attrs(opts, klass:)
|
|
47
|
+
attrs = { publisher: (opts[:publisher] || "ASTM").to_s }
|
|
48
|
+
|
|
49
|
+
if opts[:letter] || opts[:number]
|
|
50
|
+
attrs[:code] = Pubid::Astm::Components::Code.new(
|
|
51
|
+
letter: opts[:letter]&.to_s,
|
|
52
|
+
number: opts[:number]&.to_s,
|
|
53
|
+
suffix: opts[:suffix]&.to_s,
|
|
54
|
+
subseries: opts[:subseries]&.to_s,
|
|
55
|
+
dual_m: opts[:dual_m],
|
|
56
|
+
)
|
|
57
|
+
elsif (combined = opts[:code])
|
|
58
|
+
letter, number = split_astm_code(combined.to_s)
|
|
59
|
+
attrs[:code] = Pubid::Astm::Components::Code.new(
|
|
60
|
+
letter: letter, number: number,
|
|
61
|
+
)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
attrs[:year] = opts[:year].to_s if opts[:year]
|
|
65
|
+
attrs[:format_suffix] = opts[:format_suffix].to_s if opts[:format_suffix]
|
|
66
|
+
|
|
67
|
+
# Pass through subclass-specific kwargs (designation, edition,
|
|
68
|
+
# committee, …) when the class declares them.
|
|
69
|
+
opts.each do |k, v|
|
|
70
|
+
next if attrs.key?(k)
|
|
71
|
+
next if %i[publisher code letter number suffix subseries
|
|
72
|
+
dual_m year format_suffix].include?(k)
|
|
73
|
+
attrs[k] = v if klass.attributes.key?(k)
|
|
74
|
+
end
|
|
75
|
+
attrs
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# ASTM codes use a single letter prefix (A–Z) followed by digits.
|
|
79
|
+
# "A36" → ["A", "36"]; "E1444" → ["E", "1444"]. Codes without a
|
|
80
|
+
# leading letter (data series numbers, etc.) pass through with
|
|
81
|
+
# letter: nil.
|
|
82
|
+
def self.split_astm_code(str)
|
|
83
|
+
match = str.match(/\A([A-Za-z])(\d.*)\z/)
|
|
84
|
+
return match.captures if match
|
|
85
|
+
|
|
86
|
+
[nil, str]
|
|
87
|
+
end
|
|
88
|
+
private_class_method :resolve_create_class, :coerce_create_attrs,
|
|
89
|
+
:split_astm_code
|
|
13
90
|
end
|
|
14
91
|
end
|
|
15
92
|
end
|
data/lib/pubid/bsi/identifier.rb
CHANGED
|
@@ -22,6 +22,66 @@ module Pubid
|
|
|
22
22
|
rescue Parslet::ParseFailed => e
|
|
23
23
|
raise StandardError, "Failed to parse '#{string}': #{e.message}"
|
|
24
24
|
end
|
|
25
|
+
|
|
26
|
+
# Factory mirroring pubid 1.x's `Pubid::Bsi::Identifier.create` API.
|
|
27
|
+
# Dispatches via {Pubid::Bsi::Scheme}'s IDENTIFIER_CLASS_MAP and
|
|
28
|
+
# TYPED_STAGES_REGISTRY; default subclass is BritishStandard.
|
|
29
|
+
#
|
|
30
|
+
# BSI's renderer requires BSI-namespaced Component subclasses
|
|
31
|
+
# (`Bsi::Components::*`), so coercion is inlined rather than reusing
|
|
32
|
+
# {Pubid::Components::Factory}.
|
|
33
|
+
def self.create(type: nil, stage: nil, **opts)
|
|
34
|
+
klass = resolve_create_class(type: type, stage: stage)
|
|
35
|
+
attrs = coerce_create_attrs(opts)
|
|
36
|
+
ts = resolve_create_typed_stage(klass, stage)
|
|
37
|
+
attrs[:typed_stage] = ts if ts
|
|
38
|
+
klass.new(**attrs)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.resolve_create_class(type:, stage:)
|
|
42
|
+
scheme = Scheme.new
|
|
43
|
+
klass = nil
|
|
44
|
+
if type
|
|
45
|
+
klass = scheme.locate_identifier_klass_by_type_code(type)
|
|
46
|
+
elsif stage
|
|
47
|
+
ts = scheme.locate_typed_stage_by_abbr(stage.to_s)
|
|
48
|
+
klass = scheme.locate_identifier_klass_by_type_code(ts.type_code) if ts
|
|
49
|
+
end
|
|
50
|
+
klass || Identifiers::BritishStandard
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.resolve_create_typed_stage(klass, stage)
|
|
54
|
+
if stage
|
|
55
|
+
Scheme.new.locate_typed_stage_by_abbr(stage.to_s)
|
|
56
|
+
elsif klass.const_defined?(:TYPED_STAGES)
|
|
57
|
+
klass.const_get(:TYPED_STAGES).find do |ts|
|
|
58
|
+
ts.stage_code.to_sym == :published
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def self.coerce_create_attrs(opts)
|
|
64
|
+
attrs = {}
|
|
65
|
+
if (v = opts[:publisher])
|
|
66
|
+
attrs[:publisher] = Components::Publisher.new(body: v.to_s)
|
|
67
|
+
end
|
|
68
|
+
%i[number part subpart].each do |k|
|
|
69
|
+
v = opts[k]
|
|
70
|
+
attrs[k] = Components::Code.new(value: v.to_s) unless v.nil?
|
|
71
|
+
end
|
|
72
|
+
if (v = opts[:year])
|
|
73
|
+
attrs[:date] = Components::Date.new(year: v.to_s)
|
|
74
|
+
end
|
|
75
|
+
# BSI :edition is a plain string attribute (not a Component).
|
|
76
|
+
attrs[:edition] = opts[:edition].to_s if opts[:edition]
|
|
77
|
+
# TODO(create-shim): BSI also has prefix, flex_prefix, iteration,
|
|
78
|
+
# second_number, month, translation_lang/upper attributes that
|
|
79
|
+
# aren't yet wired through. Add as call sites require them.
|
|
80
|
+
attrs
|
|
81
|
+
end
|
|
82
|
+
private_class_method :resolve_create_class,
|
|
83
|
+
:resolve_create_typed_stage,
|
|
84
|
+
:coerce_create_attrs
|
|
25
85
|
end
|
|
26
86
|
end
|
|
27
87
|
end
|
|
@@ -11,6 +11,74 @@ module Pubid
|
|
|
11
11
|
rescue Parslet::ParseFailed => e
|
|
12
12
|
raise "Failed to parse CCSDS identifier '#{identifier}': #{e.message}"
|
|
13
13
|
end
|
|
14
|
+
|
|
15
|
+
# Factory that builds a CCSDS identifier from a hash of primitives.
|
|
16
|
+
#
|
|
17
|
+
# Accepts the field shape used by relaton-data-ccsds index entries:
|
|
18
|
+
# `:publisher`, `:number`, `:series`, `:part`, `:book_color`,
|
|
19
|
+
# `:retired`, `:edition`, plus the parsed-shape fields `:type`,
|
|
20
|
+
# `:suffix`, `:language`. CCSDS 2.x maps these as:
|
|
21
|
+
#
|
|
22
|
+
# * `:series` + `:number` → combined number ("A" + "20" → "A20")
|
|
23
|
+
# * `:book_color` → `:type` (B/G/M/R/Y/O)
|
|
24
|
+
# * `:publisher` → ignored (hardcoded "CCSDS")
|
|
25
|
+
# * `:retired` → ignored (no 2.x field)
|
|
26
|
+
#
|
|
27
|
+
# Supplement subclasses (currently just Corrigendum, via `type: :cor`)
|
|
28
|
+
# raise ArgumentError until `base:` kwarg is wired through.
|
|
29
|
+
def self.create(type: nil, **opts)
|
|
30
|
+
if type
|
|
31
|
+
type_sym = type.to_sym
|
|
32
|
+
base_type_key = Identifiers::Base.type[:key].to_sym
|
|
33
|
+
|
|
34
|
+
if type_sym != base_type_key
|
|
35
|
+
supplement = supplement_klass_for(type_sym)
|
|
36
|
+
if supplement
|
|
37
|
+
# TODO(create-shim): supplement subclasses (Corrigendum)
|
|
38
|
+
# require a base_identifier. Wire `base:` kwarg through once
|
|
39
|
+
# a caller needs it.
|
|
40
|
+
raise ArgumentError, "#{supplement} requires a " \
|
|
41
|
+
"base_identifier; Identifier.create " \
|
|
42
|
+
"cannot build supplements yet"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Not a class-dispatch key — treat as book_color data
|
|
46
|
+
# (e.g. `type: "G"` for Green Book) and merge back into opts.
|
|
47
|
+
opts = opts.merge(type: type)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
Identifiers::Base.new(**coerce_create_attrs(opts))
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def self.supplement_klass_for(type_sym)
|
|
55
|
+
Scheme.supplement_identifiers.find do |k|
|
|
56
|
+
k.type[:key].to_sym == type_sym
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def self.coerce_create_attrs(opts)
|
|
61
|
+
attrs = {}
|
|
62
|
+
|
|
63
|
+
if opts[:number]
|
|
64
|
+
num = opts[:number].to_s
|
|
65
|
+
attrs[:number] =
|
|
66
|
+
opts[:series] ? "#{opts[:series]}#{num}" : num
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
attrs[:type] = opts[:book_color].to_s if opts[:book_color]
|
|
70
|
+
# Allow caller to pass :type as data when no :book_color given
|
|
71
|
+
# (parsed identifiers expose it via Base#type attribute).
|
|
72
|
+
attrs[:type] = opts[:type].to_s if opts[:type] && !attrs[:type]
|
|
73
|
+
|
|
74
|
+
%i[part edition suffix language].each do |k|
|
|
75
|
+
v = opts[k]
|
|
76
|
+
attrs[k] = v.to_s unless v.nil?
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
attrs
|
|
80
|
+
end
|
|
81
|
+
private_class_method :supplement_klass_for, :coerce_create_attrs
|
|
14
82
|
end
|
|
15
83
|
end
|
|
16
84
|
end
|
|
@@ -24,6 +24,17 @@ module Pubid
|
|
|
24
24
|
{ key: :base, title: "Base Standard", short: nil }
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
# Return a copy of this identifier with the named attributes
|
|
28
|
+
# nil'd. Mirrors the {Pubid::Identifier#exclude} convenience.
|
|
29
|
+
# CCSDS Base extends Lutaml::Model::Serializable directly (not
|
|
30
|
+
# Pubid::Identifier), so the method is defined here too.
|
|
31
|
+
def exclude(*args)
|
|
32
|
+
attrs = self.class.attributes.each_with_object({}) do |(name, _), h|
|
|
33
|
+
h[name] = args.include?(name) ? nil : send(name)
|
|
34
|
+
end
|
|
35
|
+
self.class.new(attrs)
|
|
36
|
+
end
|
|
37
|
+
|
|
27
38
|
# Generate URN for this identifier
|
|
28
39
|
#
|
|
29
40
|
# @return [String] URN representation
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
module Pubid
|
|
4
4
|
module Ccsds
|
|
5
|
-
|
|
5
|
+
# Qualified as ::Pubid::Identifier to avoid resolving to the local
|
|
6
|
+
# Pubid::Ccsds::Identifier module, which is a parser entry point
|
|
7
|
+
# (`self.parse`) — not the base class.
|
|
8
|
+
class SingleIdentifier < ::Pubid::Identifier
|
|
6
9
|
attribute :publisher, Components::Publisher, default: -> {
|
|
7
10
|
Components::Publisher.new(body: "CCSDS")
|
|
8
11
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "../components/factory"
|
|
4
|
+
|
|
3
5
|
module Pubid
|
|
4
6
|
module CenCenelec
|
|
5
7
|
module Identifier
|
|
@@ -10,6 +12,41 @@ module Pubid
|
|
|
10
12
|
rescue Parslet::ParseFailed => e
|
|
11
13
|
raise "Failed to parse CEN identifier '#{identifier}': #{e.message}"
|
|
12
14
|
end
|
|
15
|
+
|
|
16
|
+
# Factory mirroring pubid 1.x's `Pubid::Cen::Identifier.create` API.
|
|
17
|
+
# Dispatches via {Pubid::CenCenelec::Scheme}'s IDENTIFIER_CLASS_MAP
|
|
18
|
+
# and TYPED_STAGES_REGISTRY. Default is EuropeanNorm.
|
|
19
|
+
def self.create(type: nil, stage: nil, **opts)
|
|
20
|
+
klass = resolve_create_class(type: type, stage: stage)
|
|
21
|
+
attrs = ::Pubid::Components::Factory.from_hash(opts)
|
|
22
|
+
ts = resolve_create_typed_stage(klass, stage)
|
|
23
|
+
attrs[:typed_stage] = ts if ts
|
|
24
|
+
klass.new(**attrs)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.resolve_create_class(type:, stage:)
|
|
28
|
+
scheme = Scheme.new
|
|
29
|
+
klass = nil
|
|
30
|
+
if type
|
|
31
|
+
klass = scheme.locate_identifier_klass_by_type_code(type)
|
|
32
|
+
elsif stage
|
|
33
|
+
ts = scheme.locate_typed_stage_by_abbr(stage.to_s)
|
|
34
|
+
klass = scheme.locate_identifier_klass_by_type_code(ts.type_code) if ts
|
|
35
|
+
end
|
|
36
|
+
klass || Identifiers::EuropeanNorm
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def self.resolve_create_typed_stage(klass, stage)
|
|
40
|
+
if stage
|
|
41
|
+
Scheme.new.locate_typed_stage_by_abbr(stage.to_s)
|
|
42
|
+
elsif klass.const_defined?(:TYPED_STAGES)
|
|
43
|
+
klass.const_get(:TYPED_STAGES).find do |ts|
|
|
44
|
+
ts.stage_code.to_sym == :published
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
private_class_method :resolve_create_class,
|
|
49
|
+
:resolve_create_typed_stage
|
|
13
50
|
end
|
|
14
51
|
end
|
|
15
52
|
end
|
data/lib/pubid/cie/identifier.rb
CHANGED
|
@@ -13,6 +13,59 @@ module Pubid
|
|
|
13
13
|
builder = Builder.new
|
|
14
14
|
builder.build(parsed, original_string: input)
|
|
15
15
|
end
|
|
16
|
+
|
|
17
|
+
# Factory that builds a CIE identifier from a hash of primitives.
|
|
18
|
+
# Dispatch on `:type` to a SingleIdentifier subclass; default is
|
|
19
|
+
# {Identifiers::Standard}.
|
|
20
|
+
TYPE_KEY_TO_KLASS = {
|
|
21
|
+
standard: "Standard",
|
|
22
|
+
conference: "Conference",
|
|
23
|
+
bundle: "Bundle",
|
|
24
|
+
joint_published: "JointPublished",
|
|
25
|
+
dual_published: "DualPublished",
|
|
26
|
+
identical: "Identical",
|
|
27
|
+
tutorial_bundle: "TutorialBundle",
|
|
28
|
+
}.freeze
|
|
29
|
+
|
|
30
|
+
def self.create(type: nil, **opts)
|
|
31
|
+
klass = resolve_create_class(type)
|
|
32
|
+
klass.new(**coerce_create_attrs(opts, klass: klass))
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.resolve_create_class(type)
|
|
36
|
+
return Identifiers::Standard if type.nil?
|
|
37
|
+
|
|
38
|
+
klass_name = TYPE_KEY_TO_KLASS[type.to_sym]
|
|
39
|
+
raise ArgumentError, "Unknown CIE type: #{type.inspect}" unless klass_name
|
|
40
|
+
|
|
41
|
+
Identifiers.const_get(klass_name)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def self.coerce_create_attrs(opts, klass:)
|
|
45
|
+
attrs = {}
|
|
46
|
+
attrs[:year] = opts[:year].to_s if opts[:year]
|
|
47
|
+
attrs[:date_separator] = opts[:date_separator].to_s if opts[:date_separator]
|
|
48
|
+
|
|
49
|
+
if (v = opts[:code] || opts[:number])
|
|
50
|
+
attrs[:code] = Pubid::Cie::Components::Code.new(
|
|
51
|
+
number: v.to_s,
|
|
52
|
+
part: opts[:part]&.to_s,
|
|
53
|
+
iteration: opts[:iteration]&.to_s,
|
|
54
|
+
style: opts[:style]&.to_s,
|
|
55
|
+
part_separator: opts[:part_separator]&.to_s,
|
|
56
|
+
)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Standard-only attributes
|
|
60
|
+
if klass.attributes.key?(:s_prefix) && opts.key?(:s_prefix)
|
|
61
|
+
attrs[:s_prefix] = opts[:s_prefix]
|
|
62
|
+
end
|
|
63
|
+
if klass.attributes.key?(:stage) && opts[:stage]
|
|
64
|
+
attrs[:stage] = opts[:stage].to_s
|
|
65
|
+
end
|
|
66
|
+
attrs
|
|
67
|
+
end
|
|
68
|
+
private_class_method :resolve_create_class, :coerce_create_attrs
|
|
16
69
|
end
|
|
17
70
|
end
|
|
18
71
|
end
|