relaton-render 0.4.2 → 3.0.0.pre.alpha.26
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/.github/workflows/rake.yml +6 -1
- data/.github/workflows/release.yml +24 -0
- data/.gitignore +14 -0
- data/.rubocop.yml +2 -2
- data/Gemfile +17 -1
- data/README.adoc +13 -258
- data/docs/citation-style-pack.adoc +370 -0
- data/lib/relaton/render/general.rb +305 -0
- data/lib/relaton/render/iso690/element.rb +153 -0
- data/lib/relaton/render/iso690/elements/access.rb +44 -0
- data/lib/relaton/render/iso690/elements/authorizer.rb +30 -0
- data/lib/relaton/render/iso690/elements/citeid.rb +39 -0
- data/lib/relaton/render/iso690/elements/component_part.rb +99 -0
- data/lib/relaton/render/iso690/elements/creator.rb +258 -0
- data/lib/relaton/render/iso690/elements/edition.rb +92 -0
- data/lib/relaton/render/iso690/elements/extent.rb +71 -0
- data/lib/relaton/render/iso690/elements/identifier.rb +46 -0
- data/lib/relaton/render/iso690/elements/location.rb +73 -0
- data/lib/relaton/render/iso690/elements/medium.rb +32 -0
- data/lib/relaton/render/iso690/elements/numeration.rb +26 -0
- data/lib/relaton/render/iso690/elements/production.rb +92 -0
- data/lib/relaton/render/iso690/elements/pub_date.rb +56 -0
- data/lib/relaton/render/iso690/elements/series.rb +66 -0
- data/lib/relaton/render/iso690/elements/size.rb +41 -0
- data/lib/relaton/render/iso690/elements/status.rb +30 -0
- data/lib/relaton/render/iso690/elements/stddoc.rb +40 -0
- data/lib/relaton/render/iso690/elements/title.rb +56 -0
- data/lib/relaton/render/iso690/elements/updated.rb +38 -0
- data/lib/relaton/render/iso690/elements.rb +67 -0
- data/lib/relaton/render/iso690/fields.rb +142 -0
- data/lib/relaton/render/iso690/i18n/en.yml +47 -0
- data/lib/relaton/render/iso690/i18n/fr.yml +14 -0
- data/lib/relaton/render/iso690/i18n/ja.yml +38 -0
- data/lib/relaton/render/iso690/i18n.rb +83 -0
- data/lib/relaton/render/iso690/identifier_kinds.rb +29 -0
- data/lib/relaton/render/iso690/index_renderer.rb +57 -0
- data/lib/relaton/render/iso690/kinds.rb +53 -0
- data/lib/relaton/render/iso690/renderer.rb +148 -0
- data/lib/relaton/render/iso690/rules.rb +36 -0
- data/lib/relaton/render/iso690/style/locale.rb +53 -0
- data/lib/relaton/render/iso690/style/name_form.rb +87 -0
- data/lib/relaton/render/iso690/style/scheme.rb +33 -0
- data/lib/relaton/render/iso690/style/sort_rule.rb +25 -0
- data/lib/relaton/render/iso690/style/template_map.rb +40 -0
- data/lib/relaton/render/iso690/style/type_template.rb +37 -0
- data/lib/relaton/render/iso690/style.rb +222 -0
- data/lib/relaton/render/iso690/styles/apa.yml +45 -0
- data/lib/relaton/render/iso690/styles/author-date.yml +31 -0
- data/lib/relaton/render/iso690/styles/chicago.yml +48 -0
- data/lib/relaton/render/iso690/styles/ieee-sa.yml +45 -0
- data/lib/relaton/render/iso690/styles/lncs.yml +54 -0
- data/lib/relaton/render/iso690/template.rb +248 -0
- data/lib/relaton/render/iso690.rb +27 -0
- data/lib/relaton/render/version.rb +1 -1
- data/lib/relaton-render.rb +11 -5
- data/relaton-render.gemspec +6 -13
- metadata +61 -116
- data/lib/isodoc/i18n.rb +0 -33
- data/lib/isodoc-yaml/i18n-ar.yaml +0 -44
- data/lib/isodoc-yaml/i18n-de.yaml +0 -43
- data/lib/isodoc-yaml/i18n-en.yaml +0 -40
- data/lib/isodoc-yaml/i18n-es.yaml +0 -47
- data/lib/isodoc-yaml/i18n-fr.yaml +0 -48
- data/lib/isodoc-yaml/i18n-ru.yaml +0 -51
- data/lib/isodoc-yaml/i18n-zh-Hans.yaml +0 -40
- data/lib/relaton/render/fields/date.rb +0 -45
- data/lib/relaton/render/fields/fields.rb +0 -245
- data/lib/relaton/render/general/citations.rb +0 -96
- data/lib/relaton/render/general/config.yml +0 -79
- data/lib/relaton/render/general/render.rb +0 -185
- data/lib/relaton/render/general/render_classes.rb +0 -129
- data/lib/relaton/render/parse/parse.rb +0 -64
- data/lib/relaton/render/parse/parse_contributors.rb +0 -132
- data/lib/relaton/render/parse/parse_extract.rb +0 -180
- data/lib/relaton/render/template/liquid.rb +0 -15
- data/lib/relaton/render/template/template.rb +0 -188
- data/lib/relaton/render/utils/utils.rb +0 -27
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Render
|
|
5
|
+
module Iso690
|
|
6
|
+
module Elements
|
|
7
|
+
# Edition and version (ISO 690 clause 7.6). The model numbers the
|
|
8
|
+
# edition through Edition#number (falling back to its content).
|
|
9
|
+
class Edition < Element
|
|
10
|
+
def present?
|
|
11
|
+
!raw.to_s.empty?
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def render
|
|
15
|
+
return "#{@i18n.label('version')} #{raw}" unless word_edition?
|
|
16
|
+
|
|
17
|
+
# The worded forms first ("First edition", "第N版" via the
|
|
18
|
+
# ordinal template), the cardinal last
|
|
19
|
+
ordinal = locale_template("edition_ordinal", number)
|
|
20
|
+
return ordinal if ordinal
|
|
21
|
+
|
|
22
|
+
word = @i18n.label("edition_#{number}")
|
|
23
|
+
return word unless word == "edition_#{number}"
|
|
24
|
+
|
|
25
|
+
cardinal_form || "#{ordinalize(number)} #{@i18n.label('edition')}"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# The plain cardinal form ("edition 7")
|
|
29
|
+
def cardinal_form
|
|
30
|
+
locale_template("edition_cardinal", number) ||
|
|
31
|
+
locale_template("version_cardinal", number)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
# A locale template carrying {{ var1 }} (optionally through
|
|
37
|
+
# the ordinal_word filter) populated with the edition number;
|
|
38
|
+
# a bare label (no placeholder) is not one
|
|
39
|
+
def locale_template(key, value)
|
|
40
|
+
raw = @i18n.label(key)
|
|
41
|
+
return nil if raw == key || raw !~ /\{{/
|
|
42
|
+
|
|
43
|
+
if raw.include?("ordinal_word")
|
|
44
|
+
word = @i18n.label("ordinal_word_#{value}")
|
|
45
|
+
word = ordinalize(value) if word == "ordinal_word_#{value}"
|
|
46
|
+
raw.gsub(/\{\{\s*var1[^}]*\}\}/, word)
|
|
47
|
+
else
|
|
48
|
+
raw.gsub("{{ var1 }}", value.to_s)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
# Worded editions are document editions; online resources
|
|
55
|
+
# carry numbered builds rendered as versions
|
|
56
|
+
def word_edition?
|
|
57
|
+
number.positive? &&
|
|
58
|
+
%w[monograph component_part serial_part]
|
|
59
|
+
.include?(item_kind)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def raw
|
|
63
|
+
(@model.edition&.number || @model.edition&.content).to_s
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def number
|
|
67
|
+
Integer(raw)
|
|
68
|
+
rescue ArgumentError, TypeError
|
|
69
|
+
0
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def ordinalize(num)
|
|
73
|
+
"#{num}#{ordinal_suffix(num)}"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def ordinal_suffix(num)
|
|
77
|
+
case num % 100
|
|
78
|
+
when 11, 12, 13 then "th"
|
|
79
|
+
else
|
|
80
|
+
case num % 10
|
|
81
|
+
when 1 then "st"
|
|
82
|
+
when 2 then "nd"
|
|
83
|
+
when 3 then "rd"
|
|
84
|
+
else "th"
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Render
|
|
5
|
+
module Iso690
|
|
6
|
+
module Elements
|
|
7
|
+
# Extent of the item (ISO 690 clause 7.11): page localities.
|
|
8
|
+
class Extent < Element
|
|
9
|
+
def present?
|
|
10
|
+
!render.empty?
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def render
|
|
14
|
+
groups = [numeration, page_range].reject { |g| g.empty? }
|
|
15
|
+
groups.join(group_sep)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
# An extent label is a template over its value: "vol. %" in
|
|
21
|
+
# the Latin pack, "巻%" / "%頁" in the CJK packs
|
|
22
|
+
def unit(type, value)
|
|
23
|
+
label = @i18n.label(type)
|
|
24
|
+
return "#{label} #{value}" unless label.include?("%")
|
|
25
|
+
|
|
26
|
+
label.sub("%", value)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def cjk?(label)
|
|
30
|
+
label.match?(/\p{Han}|\p{Hiragana}|\p{Katakana}/)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def group_sep
|
|
34
|
+
sep = @i18n.label("extent_group_separator")
|
|
35
|
+
return sep unless sep == "extent_group_separator"
|
|
36
|
+
|
|
37
|
+
cjk?(@i18n.label("page")) ? "、 " : ", "
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def numeration
|
|
41
|
+
%w[volume issue].filter_map do |type|
|
|
42
|
+
loc = localities.find { |l| l.type == type }
|
|
43
|
+
next if loc.nil? || loc.reference_from.to_s.empty?
|
|
44
|
+
|
|
45
|
+
unit(type, loc.reference_from)
|
|
46
|
+
end.join(" ")
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def page_range
|
|
50
|
+
loc = localities.find { |l| l.type == "page" } or return ""
|
|
51
|
+
from = loc.reference_from.to_s
|
|
52
|
+
to = loc.reference_to.to_s
|
|
53
|
+
if to.empty? || to == from
|
|
54
|
+
unit("page", from)
|
|
55
|
+
else
|
|
56
|
+
range = "#{from}#{@i18n.label('date_range')}#{to}"
|
|
57
|
+
unit("pages", range)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def localities
|
|
62
|
+
Array(@model.extent).flat_map do |e|
|
|
63
|
+
Array(e.locality) +
|
|
64
|
+
Array(e.locality_stack).flat_map { |s| Array(s.locality) }
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Render
|
|
5
|
+
module Iso690
|
|
6
|
+
module Elements
|
|
7
|
+
# Standard and persistent identifiers, rendered by kind
|
|
8
|
+
class Identifier < Element
|
|
9
|
+
INTERNAL_TYPES = %w[metanorma metanorma-ordinal metanorma-objid]
|
|
10
|
+
.freeze
|
|
11
|
+
|
|
12
|
+
def present?
|
|
13
|
+
!identifiers.empty?
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def render
|
|
18
|
+
return "" if identifiers.empty?
|
|
19
|
+
|
|
20
|
+
ids = @style.templates.identifier_mode == "first" ?
|
|
21
|
+
[identifiers.first] : identifiers
|
|
22
|
+
ids.map { |d| render_id(d) }
|
|
23
|
+
.join(@i18n.punct_fetch("identifier_join", ". "))
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# A flavor's identifier kinds subclass this element and
|
|
27
|
+
# override here
|
|
28
|
+
def render_id(docidentifier)
|
|
29
|
+
IdentifierKinds.render(docidentifier)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
# Scoped identifiers (anchor, biblio-tag) are pipeline
|
|
35
|
+
# artefacts, not cited identifiers
|
|
36
|
+
def identifiers
|
|
37
|
+
Array(@model.docidentifier).reject do |d|
|
|
38
|
+
INTERNAL_TYPES.include?(d.type) ||
|
|
39
|
+
!d.scope.to_s.empty? || d.content.to_s.strip.empty?
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Render
|
|
5
|
+
module Iso690
|
|
6
|
+
module Elements
|
|
7
|
+
# Availability and location (ISO 690 clause 7.12): access location
|
|
8
|
+
# (URI) of the resource
|
|
9
|
+
class Location < Element
|
|
10
|
+
def present?
|
|
11
|
+
!uri.empty?
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def render
|
|
15
|
+
text = uri
|
|
16
|
+
form = uri_form
|
|
17
|
+
# an access location cites its own label ("At: Library"); a
|
|
18
|
+
# uri carries the availability label
|
|
19
|
+
if @from_accesslocation
|
|
20
|
+
return "#{@i18n.label('at_url')} #{text}".strip
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
text = if form && !form.empty?
|
|
24
|
+
::Relaton::Render::Iso690::Template.new(form).evaluate(
|
|
25
|
+
"uri" => Template::Field[!text.empty?, text],
|
|
26
|
+
)
|
|
27
|
+
else
|
|
28
|
+
text
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
label = @short ? "" : "#{@i18n.label('available_from')} "
|
|
32
|
+
"#{label}#{text}".strip
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
# The short cite renders the bare uri (1.x short templates),
|
|
38
|
+
# the reference the declared form
|
|
39
|
+
def uri_form
|
|
40
|
+
if @short
|
|
41
|
+
short_form = @style.templates.uri_short
|
|
42
|
+
return :bare if short_form.empty?
|
|
43
|
+
|
|
44
|
+
return short_form
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
@style.templates.uri
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def uri
|
|
51
|
+
from_accesslocation =
|
|
52
|
+
Array(@model.accesslocation).map(&:to_s).reject(&:empty?)
|
|
53
|
+
unless from_accesslocation.empty?
|
|
54
|
+
@from_accesslocation = true
|
|
55
|
+
return from_accesslocation.first.to_s
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
uris = Array(@model.source).reject do |u|
|
|
59
|
+
u.content.to_s.strip.empty?
|
|
60
|
+
end
|
|
61
|
+
# the citation uri outranks the rest (1.x uri extraction)
|
|
62
|
+
preferred = uris.find { |u| u.type == "citation" } ||
|
|
63
|
+
uris.find { |u| u.type == "attachment" } ||
|
|
64
|
+
uris.find { |u| u.type == "src" } || uris.first
|
|
65
|
+
# the uri lands inside a markup attribute: escape it
|
|
66
|
+
require "cgi"
|
|
67
|
+
CGI.escapeHTML(preferred&.content.to_s)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Render
|
|
5
|
+
module Iso690
|
|
6
|
+
module Elements
|
|
7
|
+
# Medium designation (ISO 690 clause 7.5), bracketed per the
|
|
8
|
+
# standard's worked examples, e.g. "[online]"
|
|
9
|
+
class Medium < Element
|
|
10
|
+
def present?
|
|
11
|
+
!medium.empty?
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def render
|
|
15
|
+
medium
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def medium
|
|
21
|
+
genre = @model.medium&.genre.to_s
|
|
22
|
+
return genre.sub(/^\w/) { |c| c.upcase } unless genre.empty?
|
|
23
|
+
|
|
24
|
+
key = "medium_#{@model.type}"
|
|
25
|
+
label = @i18n.label(key)
|
|
26
|
+
label == key ? "" : label
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Render
|
|
5
|
+
module Iso690
|
|
6
|
+
module Elements
|
|
7
|
+
# Numeration within a series (ISO 690 clause 7.9): report numbers
|
|
8
|
+
class Numeration < Element
|
|
9
|
+
def present?
|
|
10
|
+
!docnumber.to_s.empty?
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def render
|
|
14
|
+
"#{@i18n.label('report_no')} #{docnumber}"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def docnumber
|
|
20
|
+
@model.docnumber
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Render
|
|
5
|
+
module Iso690
|
|
6
|
+
module Elements
|
|
7
|
+
# Production information: place and publisher (ISO 690 clause 7.8).
|
|
8
|
+
# Place comes from the model's typed city; publishers from
|
|
9
|
+
# organization contributors.
|
|
10
|
+
class Production < Element
|
|
11
|
+
def present?
|
|
12
|
+
!places.empty? || !publishers.empty? || no_place_declared?
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def render
|
|
16
|
+
if publisher_first?
|
|
17
|
+
return no_place_production_publisher_first if places.empty? &&
|
|
18
|
+
no_place_declared?
|
|
19
|
+
|
|
20
|
+
return [publishers.join("; "), places.map { |p| cjk_place(p) }]
|
|
21
|
+
.reject(&:empty?).join(production_sep)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
return no_place_production if places.empty? && no_place_declared?
|
|
25
|
+
|
|
26
|
+
[places.map { |p| cjk_place(p) }, publishers.join("; ")]
|
|
27
|
+
.reject(&:empty?).join(production_sep)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# A CJK locale's place names carry the CJK-Latin separator
|
|
31
|
+
# ("Cambridge、UK" over "Cambridge, UK")
|
|
32
|
+
def cjk_place(place)
|
|
33
|
+
sep = @i18n.punct_fetch("cjk-latin-separator", "")
|
|
34
|
+
sep.empty? ? place : place.gsub(", ", sep)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def publisher_first?
|
|
38
|
+
@i18n.punct_fetch("production_order", "") == "publisher_first"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def production_sep
|
|
42
|
+
@i18n.punct_fetch("production_sep", ": ")
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def no_place_production_publisher_first
|
|
46
|
+
label = @i18n.label("no_place")
|
|
47
|
+
return "" if label.empty? || label == "no_place"
|
|
48
|
+
|
|
49
|
+
pubs = publishers.join("; ")
|
|
50
|
+
pubs.empty? ? label : "#{pubs}, #{label}"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# The no-place placeholder keeps the production separator even
|
|
54
|
+
# when no publisher follows ("n.p.: n.d.")
|
|
55
|
+
def no_place_production
|
|
56
|
+
label = @i18n.label("no_place")
|
|
57
|
+
return "" if label.empty? || label == "no_place"
|
|
58
|
+
|
|
59
|
+
pubs = publishers.join("; ")
|
|
60
|
+
pubs.empty? ? "#{label}#{production_sep}".rstrip :
|
|
61
|
+
"#{label}#{production_sep}#{pubs}"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def no_place_declared?
|
|
65
|
+
@style.type_template_for(item_kind)&.no_place == true
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
def places
|
|
71
|
+
Array(@model.place).map do |p|
|
|
72
|
+
structured = [p.city.to_s, p.region.map(&:content),
|
|
73
|
+
p.country.map(&:content)]
|
|
74
|
+
.flatten.reject { |c| c.strip.empty? }
|
|
75
|
+
if structured.empty? && !p.formatted_place.to_s.empty?
|
|
76
|
+
next p.formatted_place.to_s
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
structured.join(", ")
|
|
80
|
+
end.reject(&:empty?)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def publishers
|
|
84
|
+
contributors("publisher").map do |c|
|
|
85
|
+
Array(c.organization&.name).map { |n| localized(n) }.join(", ")
|
|
86
|
+
end.reject(&:empty?)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Render
|
|
5
|
+
module Iso690
|
|
6
|
+
module Elements
|
|
7
|
+
# Date of publication (ISO 690 clause 7.7). The relaton model
|
|
8
|
+
# provides typed date values (StringDate::Value#to_date); years
|
|
9
|
+
# come from the model, never from string manipulation. A from/to
|
|
10
|
+
# pair renders as a range (closed or open).
|
|
11
|
+
class PubDate < Element
|
|
12
|
+
def present?
|
|
13
|
+
!render.nil?
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def render
|
|
17
|
+
range || year&.to_s
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def publication_date
|
|
23
|
+
Array(@model.date).find(&:published) ||
|
|
24
|
+
Array(@model.date).find { |d| d.type.nil? } ||
|
|
25
|
+
Array(@model.date).find { |d| d.type == "created" } ||
|
|
26
|
+
Array(@model.date).find { |d| d.type == "issued" } ||
|
|
27
|
+
Array(@model.date).find { |d| d.type == "circulated" }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def publication_date_value
|
|
31
|
+
d = publication_date or return nil
|
|
32
|
+
d.from || d.at || d.to
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def range
|
|
36
|
+
d = publication_date or return nil
|
|
37
|
+
dash = @i18n.label("date_range")
|
|
38
|
+
if d.from && d.to
|
|
39
|
+
fy = d.from.to_date.year
|
|
40
|
+
ty = d.to.to_date.year
|
|
41
|
+
fy == ty ? fy.to_s : "#{fy}#{dash}#{ty}"
|
|
42
|
+
elsif d.from
|
|
43
|
+
"#{d.from.to_date.year}#{dash}"
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def year
|
|
48
|
+
publication_date_value.to_date.year
|
|
49
|
+
rescue ArgumentError, TypeError, NoMethodError
|
|
50
|
+
nil
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Render
|
|
5
|
+
module Iso690
|
|
6
|
+
module Elements
|
|
7
|
+
# Series title and number (ISO 690 clause 7.10)
|
|
8
|
+
class Series < Element
|
|
9
|
+
def present?
|
|
10
|
+
!series_title.empty?
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def render
|
|
14
|
+
if (form = series_form) && !form.empty?
|
|
15
|
+
return ::Relaton::Render::Iso690::Template.new(form).evaluate(
|
|
16
|
+
"seriestitle" => Template::Field[!series_title.empty?,
|
|
17
|
+
series_title],
|
|
18
|
+
"seriesnumber" => Template::Field[!number.empty?, number],
|
|
19
|
+
"seriesrun" => Template::Field[!run.empty?, run],
|
|
20
|
+
)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
base = "#{@style.templates.title_open}#{series_title}" \
|
|
24
|
+
"#{@style.templates.title_close}"
|
|
25
|
+
if number.empty?
|
|
26
|
+
base
|
|
27
|
+
else
|
|
28
|
+
# an empty series_no label joins on the space alone (the
|
|
29
|
+
# CJK form: "Series 472")
|
|
30
|
+
label = @i18n.label("series_no")
|
|
31
|
+
label.empty? ? "#{base} #{number}" :
|
|
32
|
+
"#{base}, #{label} #{number}"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def series
|
|
39
|
+
Array(@model.series).first
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def series_title
|
|
43
|
+
titles_in_lang(series&.title).map { |t| localized(t) }
|
|
44
|
+
.join(" ").strip
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def number
|
|
48
|
+
series&.number.to_s
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def run
|
|
52
|
+
series&.run.to_s
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# The style's series form for this item's resource kind, falling
|
|
56
|
+
# back to the style-wide form
|
|
57
|
+
def series_form
|
|
58
|
+
variant = @style.per_type
|
|
59
|
+
.find { |t| t.type == @style.kind_for(@model.type) }
|
|
60
|
+
variant&.series || @style.templates.series
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Render
|
|
5
|
+
module Iso690
|
|
6
|
+
module Elements
|
|
7
|
+
# Physical size (ISO 690 clause 7.5, area): the model's typed
|
|
8
|
+
# values render as "N unit" through the language pack; untyped
|
|
9
|
+
# values pass through as written.
|
|
10
|
+
class Size < Element
|
|
11
|
+
def present?
|
|
12
|
+
!values.empty?
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def render
|
|
16
|
+
values.map { |v| render_value(v) }.join(", ")
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def values
|
|
22
|
+
Array(@model.size&.value)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def render_value(value)
|
|
26
|
+
content = value.is_a?(String) ? value : value.content.to_s
|
|
27
|
+
return content if content.empty? || value.is_a?(String)
|
|
28
|
+
|
|
29
|
+
type = value.is_a?(Hash) ? value[:type] : value.type
|
|
30
|
+
unit = type && @i18n.label("size_#{type}")
|
|
31
|
+
return content if unit.nil? || unit == "size_#{type}"
|
|
32
|
+
|
|
33
|
+
# The value/unit glue is locale-declared (CJK cites tight,
|
|
34
|
+
# "巻1" over "vol. 1")
|
|
35
|
+
"#{unit}#{@i18n.punct_fetch('size-join', ' ')}#{content}"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Render
|
|
5
|
+
module Iso690
|
|
6
|
+
module Elements
|
|
7
|
+
# The item's status stage, capitalized, parenthesized beside
|
|
8
|
+
# the identifier ("OGC 05-020r27 (Draft)"); published statuses
|
|
9
|
+
# are not cited
|
|
10
|
+
class Status < Element
|
|
11
|
+
def present?
|
|
12
|
+
!stage.empty? &&
|
|
13
|
+
!%w[published retired].include?(stage.downcase)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def render
|
|
17
|
+
" (#{stage})"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def stage
|
|
23
|
+
raw = @model.status&.stage&.content.to_s
|
|
24
|
+
raw.empty? ? "" : raw.sub(/^\w/) { |c| c.upcase }
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Render
|
|
5
|
+
module Iso690
|
|
6
|
+
module Elements
|
|
7
|
+
# Issuing body or status of a standard (ISO 690 clause 8.9): the
|
|
8
|
+
# publisher or authorizer organization, falling back to the
|
|
9
|
+
# capitalized status stage.
|
|
10
|
+
class Stddoc < Element
|
|
11
|
+
def present?
|
|
12
|
+
!body.empty?
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def render
|
|
16
|
+
body
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def body
|
|
22
|
+
orgs = %w[publisher authorizer].flat_map { |role|
|
|
23
|
+
contributors(role).map { |c| org_plain(c) }
|
|
24
|
+
}.reject(&:empty?)
|
|
25
|
+
return orgs.join("; ") unless orgs.empty?
|
|
26
|
+
|
|
27
|
+
stage = @model.status&.stage&.content.to_s
|
|
28
|
+
stage.empty? ? "" : stage.sub(/^\w/) { |c| c.upcase }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Issuing bodies render in mixed case, unlike creator organs
|
|
32
|
+
def org_plain(contributor)
|
|
33
|
+
Array(contributor.organization&.name)
|
|
34
|
+
.map { |n| localized(n) }.join(", ")
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|