relaton-render 3.0.0.pre.alpha.6 → 3.0.0.pre.alpha.8
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/relaton/render/general.rb +32 -6
- data/lib/relaton/render/iso690/element.rb +20 -3
- data/lib/relaton/render/iso690/elements/creator.rb +10 -12
- data/lib/relaton/render/iso690/elements/location.rb +24 -4
- data/lib/relaton/render/iso690/elements/production.rb +15 -6
- data/lib/relaton/render/iso690/elements/title.rb +2 -1
- data/lib/relaton/render/iso690/elements.rb +3 -2
- data/lib/relaton/render/iso690/fields.rb +5 -2
- data/lib/relaton/render/iso690/renderer.rb +17 -1
- data/lib/relaton/render/iso690/style/name_form.rb +4 -0
- data/lib/relaton/render/iso690/style/template_map.rb +2 -0
- data/lib/relaton/render/iso690/style/type_template.rb +8 -0
- data/lib/relaton/render/iso690/template.rb +31 -0
- data/lib/relaton/render/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 22dc4e39ee6744154270c700e9b80ff7e72e90929b19275e1705b4f0ab898d6b
|
|
4
|
+
data.tar.gz: 7f3482bab07cbcc5034ed2e282854caa0d87bcfae05a1185558ab1ec787cff78
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7349c542f2a79dfb2328d9a285ec36feaebfbba45307314dd7524b00e5c7a464c4fe21ff9f12204d9c3e5ca9e739dfd781a0d6fb4c3d43bde70e2c57b6e4c121
|
|
7
|
+
data.tar.gz: 0c6c6dca299f1870afa8c38b752b4206185441a6315cf7cab2d6ab141b71fc6a4cd4f1e740001556166cab2282db9f8da975c3163fe2f08c1e97c1998e388599
|
|
@@ -62,16 +62,42 @@ module Relaton
|
|
|
62
62
|
next
|
|
63
63
|
end
|
|
64
64
|
m[item.id] = {
|
|
65
|
-
id: item.id, ord: i,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
default: item.docidentifier.first&.content.to_s,
|
|
69
|
-
short: @renderer.citation(item),
|
|
70
|
-
},
|
|
65
|
+
id: item.id, ord: i,
|
|
66
|
+
formattedref: terminate_reference(ref),
|
|
67
|
+
citation: citation_renderings(item, ref),
|
|
71
68
|
}
|
|
72
69
|
end
|
|
73
70
|
end
|
|
74
71
|
|
|
72
|
+
# isodoc's styled citations consume these keys: the default is the
|
|
73
|
+
# authoritative identifier; the short cite is the style's citation,
|
|
74
|
+
# falling back to the reference rendering (the 1.x short-cite)
|
|
75
|
+
FIRST_DELIM = "<span class='fmt-first-biblio-delim'/>"
|
|
76
|
+
|
|
77
|
+
# render_all feeds the bibliography list: a reference not ending
|
|
78
|
+
# in the biblio terminator takes one (the 1.x render1 behaviour;
|
|
79
|
+
# single-item render stays verbatim)
|
|
80
|
+
def terminate_reference(ref)
|
|
81
|
+
return ref if ref.empty? || ref.rstrip.end_with?(".")
|
|
82
|
+
|
|
83
|
+
"#{ref}."
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def citation_renderings(item, ref)
|
|
87
|
+
short = @renderer.citation(item)
|
|
88
|
+
short = if short.empty?
|
|
89
|
+
@renderer.render_short(item, FIRST_DELIM)
|
|
90
|
+
else
|
|
91
|
+
short
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
{
|
|
95
|
+
full: ref,
|
|
96
|
+
default: item.docidentifier.first&.content.to_s,
|
|
97
|
+
short: short,
|
|
98
|
+
}
|
|
99
|
+
end
|
|
100
|
+
|
|
75
101
|
# isodoc's pref_ref_code_parse reads
|
|
76
102
|
# data[:authoritative_identifier] to build biblio-tag identifiers
|
|
77
103
|
# (presentation_function/docid.rb). Returns [data, self] as the
|
|
@@ -8,11 +8,12 @@ module Relaton
|
|
|
8
8
|
# predicate, #render the element's formatting method. Punctuation
|
|
9
9
|
# between elements belongs to the kind/style, not the element.
|
|
10
10
|
class Element
|
|
11
|
-
def initialize(model, style:, i18n:, kind: nil)
|
|
11
|
+
def initialize(model, style:, i18n:, kind: nil, short: false)
|
|
12
12
|
@model = model
|
|
13
13
|
@style = style
|
|
14
14
|
@i18n = i18n
|
|
15
15
|
@kind = kind
|
|
16
|
+
@short = short
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
def present?
|
|
@@ -48,8 +49,12 @@ module Relaton
|
|
|
48
49
|
end
|
|
49
50
|
|
|
50
51
|
def org_name(contributor)
|
|
52
|
+
shown = ->(n) do
|
|
53
|
+
text = localized(n)
|
|
54
|
+
@style.scheme.name_form.org_upcase ? text.upcase : text
|
|
55
|
+
end
|
|
51
56
|
name = Array(contributor.organization&.name)
|
|
52
|
-
.map
|
|
57
|
+
.map(&shown).join(", ")
|
|
53
58
|
abbrev = localized(contributor.organization&.abbreviation)
|
|
54
59
|
return name if name.empty? || abbrev.empty?
|
|
55
60
|
|
|
@@ -95,15 +100,27 @@ module Relaton
|
|
|
95
100
|
end
|
|
96
101
|
|
|
97
102
|
def join_names(names)
|
|
103
|
+
oxford = @i18n.label("oxford_comma")
|
|
104
|
+
if serial_list? && names.size > 1 && !oxford.empty?
|
|
105
|
+
return "#{names[0..-2].join(', ')}#{oxford} " \
|
|
106
|
+
"#{@i18n.label('and')} #{names.last}"
|
|
107
|
+
end
|
|
108
|
+
|
|
98
109
|
case names.size
|
|
99
110
|
when 0 then ""
|
|
100
111
|
when 1 then names.first
|
|
101
112
|
when 2 then names.join(" #{@i18n.label('and')} ")
|
|
102
113
|
else
|
|
103
|
-
"#{names[0..-2].join(', ')}#{
|
|
114
|
+
"#{names[0..-2].join(', ')}#{oxford} " \
|
|
104
115
|
"#{@i18n.label('and')} #{names.last}"
|
|
105
116
|
end
|
|
106
117
|
end
|
|
118
|
+
|
|
119
|
+
# Serial list style: the oxford comma appears at every gap of a
|
|
120
|
+
# name list, including between two names
|
|
121
|
+
def serial_list?
|
|
122
|
+
@style.scheme.name_form.list_style == "serial"
|
|
123
|
+
end
|
|
107
124
|
end
|
|
108
125
|
end
|
|
109
126
|
end
|
|
@@ -15,6 +15,8 @@ module Relaton
|
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def render
|
|
18
|
+
return plain_render if @short
|
|
19
|
+
|
|
18
20
|
joined = if (form = @style.templates.creators) && !form.empty?
|
|
19
21
|
::Relaton::Render::Iso690::Template.new(form).evaluate(
|
|
20
22
|
"names" => Template::Field[true, join(names)],
|
|
@@ -38,6 +40,14 @@ module Relaton
|
|
|
38
40
|
end
|
|
39
41
|
end
|
|
40
42
|
|
|
43
|
+
# The short cite splits after the creators: they render as a
|
|
44
|
+
# plain join, without the wrap and sentence period (1.x
|
|
45
|
+
# short-cite templates carried no wrap)
|
|
46
|
+
def plain_render
|
|
47
|
+
marker = editors? && creators.size > 1 ? " (#{role_word})" : ''
|
|
48
|
+
"#{join(names)}#{marker}"
|
|
49
|
+
end
|
|
50
|
+
|
|
41
51
|
def role_placement
|
|
42
52
|
@style.scheme.name_form.role_placement || "glue"
|
|
43
53
|
end
|
|
@@ -115,21 +125,9 @@ module Relaton
|
|
|
115
125
|
end
|
|
116
126
|
|
|
117
127
|
def join(names)
|
|
118
|
-
oxford = @i18n.label("oxford_comma")
|
|
119
|
-
if serial_list? && names.size > 1 && !oxford.empty?
|
|
120
|
-
return "#{names[0..-2].join(', ')}#{oxford} " \
|
|
121
|
-
"#{@i18n.label('and')} #{names.last}"
|
|
122
|
-
end
|
|
123
|
-
|
|
124
128
|
join_names(names)
|
|
125
129
|
end
|
|
126
130
|
|
|
127
|
-
# Serial list style: every gap takes the serial form, even
|
|
128
|
-
# between two names
|
|
129
|
-
def serial_list?
|
|
130
|
-
@style.scheme.name_form.list_style == "serial"
|
|
131
|
-
end
|
|
132
|
-
|
|
133
131
|
def editors?
|
|
134
132
|
!creators.empty? && creators.all? { |c| has_role?(c, "editor") }
|
|
135
133
|
end
|
|
@@ -12,7 +12,8 @@ module Relaton
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def render
|
|
15
|
-
|
|
15
|
+
form = uri_form
|
|
16
|
+
text = if form && !form.empty?
|
|
16
17
|
::Relaton::Render::Iso690::Template.new(form).evaluate(
|
|
17
18
|
"uri" => Template::Field[!uri.empty?, uri],
|
|
18
19
|
)
|
|
@@ -20,11 +21,25 @@ module Relaton
|
|
|
20
21
|
uri
|
|
21
22
|
end
|
|
22
23
|
|
|
23
|
-
"#{@i18n.label('available_from')}
|
|
24
|
+
label = @short ? "" : "#{@i18n.label('available_from')} "
|
|
25
|
+
"#{label}#{text}".strip
|
|
24
26
|
end
|
|
25
27
|
|
|
26
28
|
private
|
|
27
29
|
|
|
30
|
+
# The short cite renders the bare uri (1.x short templates),
|
|
31
|
+
# the reference the declared form
|
|
32
|
+
def uri_form
|
|
33
|
+
if @short
|
|
34
|
+
short_form = @style.templates.uri_short
|
|
35
|
+
return :bare if short_form.empty?
|
|
36
|
+
|
|
37
|
+
return short_form
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
@style.templates.uri
|
|
41
|
+
end
|
|
42
|
+
|
|
28
43
|
def uri
|
|
29
44
|
from_accesslocation =
|
|
30
45
|
Array(@model.accesslocation).map(&:to_s).reject(&:empty?)
|
|
@@ -32,8 +47,13 @@ module Relaton
|
|
|
32
47
|
return from_accesslocation.first.to_s
|
|
33
48
|
end
|
|
34
49
|
|
|
35
|
-
Array(@model.source).
|
|
36
|
-
.
|
|
50
|
+
uris = Array(@model.source).reject do |u|
|
|
51
|
+
u.content.to_s.strip.empty?
|
|
52
|
+
end
|
|
53
|
+
# the citation uri outranks the rest (1.x uri extraction)
|
|
54
|
+
preferred = uris.find { |u| u.type == "citation" } ||
|
|
55
|
+
uris.find { |u| u.type == "attachment" } || uris.first
|
|
56
|
+
preferred&.content.to_s
|
|
37
57
|
end
|
|
38
58
|
end
|
|
39
59
|
end
|
|
@@ -9,19 +9,28 @@ module Relaton
|
|
|
9
9
|
# organization contributors.
|
|
10
10
|
class Production < Element
|
|
11
11
|
def present?
|
|
12
|
-
!places.empty? || !publishers.empty?
|
|
12
|
+
!places.empty? || !publishers.empty? || no_place_declared?
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def render
|
|
16
|
-
|
|
16
|
+
return no_place_production if places.empty? && no_place_declared?
|
|
17
|
+
|
|
18
|
+
[places.join("; "), publishers.join("; ")]
|
|
17
19
|
.reject(&:empty?).join(@i18n.punct_fetch("production_sep", ": "))
|
|
18
20
|
end
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
# The no-place placeholder keeps the production separator even
|
|
23
|
+
# when no publisher follows ("n.p.: n.d.")
|
|
24
|
+
def no_place_production
|
|
23
25
|
label = @i18n.label("no_place")
|
|
24
|
-
|
|
26
|
+
return "" if label.empty? || label == "no_place"
|
|
27
|
+
|
|
28
|
+
pubs = publishers.join("; ")
|
|
29
|
+
pubs.empty? ? "#{label}:" : "#{label}: #{pubs}"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def no_place_declared?
|
|
33
|
+
@style.type_template_for(item_kind)&.no_place == true
|
|
25
34
|
end
|
|
26
35
|
|
|
27
36
|
private
|
|
@@ -14,7 +14,8 @@ module Relaton
|
|
|
14
14
|
def render
|
|
15
15
|
text = main_title
|
|
16
16
|
return text if analytic?
|
|
17
|
-
if
|
|
17
|
+
if !@short &&
|
|
18
|
+
(form = @style.title_form_for(Kinds.kind_for(@model.type)))
|
|
18
19
|
text = ::Relaton::Render::Iso690::Template.new(form).evaluate(
|
|
19
20
|
"title" => Template::Field[!text.empty?, text],
|
|
20
21
|
)
|
|
@@ -39,8 +39,9 @@ module Relaton
|
|
|
39
39
|
stddoc: Stddoc,
|
|
40
40
|
}.freeze
|
|
41
41
|
|
|
42
|
-
def self.build(name, model, style:, i18n:)
|
|
43
|
-
CLASSES.fetch(name).new(model, style: style, i18n: i18n
|
|
42
|
+
def self.build(name, model, style:, i18n:, short: false)
|
|
43
|
+
CLASSES.fetch(name).new(model, style: style, i18n: i18n,
|
|
44
|
+
short: short)
|
|
44
45
|
end
|
|
45
46
|
end
|
|
46
47
|
end
|
|
@@ -14,11 +14,13 @@ module Relaton
|
|
|
14
14
|
extent access stddoc
|
|
15
15
|
].freeze
|
|
16
16
|
|
|
17
|
-
def initialize(model, style:, i18n:, disambiguator: nil
|
|
17
|
+
def initialize(model, style:, i18n:, disambiguator: nil,
|
|
18
|
+
short: false)
|
|
18
19
|
@model = model
|
|
19
20
|
@style = style
|
|
20
21
|
@i18n = i18n
|
|
21
22
|
@disambiguator = disambiguator.to_s
|
|
23
|
+
@short = short
|
|
22
24
|
end
|
|
23
25
|
|
|
24
26
|
def to_h
|
|
@@ -33,7 +35,8 @@ module Relaton
|
|
|
33
35
|
private
|
|
34
36
|
|
|
35
37
|
def element_field(slot)
|
|
36
|
-
element = Elements.build(slot, @model, style: @style,
|
|
38
|
+
element = Elements.build(slot, @model, style: @style,
|
|
39
|
+
i18n: @i18n, short: @short)
|
|
37
40
|
text = element.render.to_s
|
|
38
41
|
if element.present? && !text.empty?
|
|
39
42
|
return Template::Field[true, text]
|
|
@@ -25,8 +25,11 @@ module Relaton
|
|
|
25
25
|
def initialize(style: DEFAULT_STYLE, lang: "en", script: "Latn",
|
|
26
26
|
labels: {})
|
|
27
27
|
@style = Style.load(style)
|
|
28
|
-
@i18n = I18n.load(lang)
|
|
28
|
+
@i18n = I18n.load(lang)
|
|
29
29
|
@i18n.overlay_hash!(labels) unless labels.empty?
|
|
30
|
+
# The style instance is the citation-style authority: its
|
|
31
|
+
# declared strings win over the caller's generic i18n hash
|
|
32
|
+
@i18n.overlay!(@style.scheme.locale)
|
|
30
33
|
@script = script
|
|
31
34
|
end
|
|
32
35
|
|
|
@@ -49,6 +52,19 @@ module Relaton
|
|
|
49
52
|
Array(model.docidentifier).any? { |d| types.include?(d.type) }
|
|
50
53
|
end
|
|
51
54
|
|
|
55
|
+
# The short cite: the reference with the first-biblio marker
|
|
56
|
+
def render_short(model, delim)
|
|
57
|
+
kind = Kinds.kind_for(model.type)
|
|
58
|
+
template = @style.type_template_for(kind)&.short ||
|
|
59
|
+
@style.template_for(kind, home: home_docid?(model))
|
|
60
|
+
Template.new(template).evaluate_short(
|
|
61
|
+
Fields.new(model, style: @style, i18n: @i18n, short: true).to_h,
|
|
62
|
+
delim,
|
|
63
|
+
)
|
|
64
|
+
rescue ::Relaton::Render::Unrenderable
|
|
65
|
+
""
|
|
66
|
+
end
|
|
67
|
+
|
|
52
68
|
# In-text form: SURNAME, year + disambiguator (name-and-date)
|
|
53
69
|
def citation(model, disambiguator: nil)
|
|
54
70
|
Template.new(@style.templates.citation).evaluate(
|
|
@@ -23,6 +23,9 @@ module Relaton
|
|
|
23
23
|
# "serial": the oxford comma appears at every gap of the
|
|
24
24
|
# creator list, including between two names
|
|
25
25
|
attribute :list_style, :string, default: ""
|
|
26
|
+
# Organization names take the inverted form's casing; small-cap
|
|
27
|
+
# styles keep the declared case
|
|
28
|
+
attribute :org_upcase, :boolean, default: -> { true }
|
|
26
29
|
# The inverted form's surname casing: the name-date convention
|
|
27
30
|
# upcases; small-cap styles keep the declared case
|
|
28
31
|
attribute :surname_upcase, :boolean, default: -> { true }
|
|
@@ -36,6 +39,7 @@ module Relaton
|
|
|
36
39
|
map "completenameUpcase", to: :completename_upcase
|
|
37
40
|
map "invertedAll", to: :inverted_all
|
|
38
41
|
map "listStyle", to: :list_style
|
|
42
|
+
map "orgUpcase", to: :org_upcase
|
|
39
43
|
map "surnameUpcase", to: :surname_upcase
|
|
40
44
|
map "initialsSeparator", to: :initials_separator
|
|
41
45
|
end
|
|
@@ -16,6 +16,7 @@ module Relaton
|
|
|
16
16
|
attribute :series, :string, default: ""
|
|
17
17
|
attribute :uri, :string, default: ""
|
|
18
18
|
attribute :creators, :string, default: ""
|
|
19
|
+
attribute :uri_short, :string, default: ""
|
|
19
20
|
|
|
20
21
|
key_value do
|
|
21
22
|
map "titleOpen", to: :title_open
|
|
@@ -26,6 +27,7 @@ module Relaton
|
|
|
26
27
|
map "series", to: :series
|
|
27
28
|
map "uri", to: :uri
|
|
28
29
|
map "creators", to: :creators
|
|
30
|
+
map "uriShort", to: :uri_short
|
|
29
31
|
end
|
|
30
32
|
end
|
|
31
33
|
end
|
|
@@ -13,6 +13,12 @@ module Relaton
|
|
|
13
13
|
attribute :title, :string
|
|
14
14
|
attribute :home, :boolean
|
|
15
15
|
attribute :fallbacks, :hash, default: -> { {} }
|
|
16
|
+
# The short-cite template for this kind; absent falls back to
|
|
17
|
+
# the reference template in short mode
|
|
18
|
+
attribute :short, :string
|
|
19
|
+
# Render the style's no_place label when the publisher has no
|
|
20
|
+
# place (the book-family templates of some flavors)
|
|
21
|
+
attribute :no_place, :boolean
|
|
16
22
|
|
|
17
23
|
key_value do
|
|
18
24
|
map "type", to: :type
|
|
@@ -21,6 +27,8 @@ module Relaton
|
|
|
21
27
|
map "title", to: :title
|
|
22
28
|
map "home", to: :home
|
|
23
29
|
map "fallbacks", to: :fallbacks
|
|
30
|
+
map "short", to: :short
|
|
31
|
+
map "noPlace", to: :no_place
|
|
24
32
|
end
|
|
25
33
|
end
|
|
26
34
|
end
|
|
@@ -22,6 +22,34 @@ module Relaton
|
|
|
22
22
|
scan(source.to_s)
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
+
# The short cite renders the reference with the first-biblio
|
|
26
|
+
# marker appended to its first component: isodoc's styled
|
|
27
|
+
# references (eref2linkshort) split there (the 1.x
|
|
28
|
+
# fmt-first-biblio-delim)
|
|
29
|
+
def evaluate_short(fields, delim)
|
|
30
|
+
rendered = @slots.filter_map do |(name, head, tail)|
|
|
31
|
+
field = fields[normalise(name)] or next
|
|
32
|
+
next unless field.present?
|
|
33
|
+
|
|
34
|
+
[head, field.text, tail]
|
|
35
|
+
end
|
|
36
|
+
return "" if rendered.empty?
|
|
37
|
+
|
|
38
|
+
parts = rendered.each_with_index.map do |(head, text, tail), i|
|
|
39
|
+
if i.zero?
|
|
40
|
+
# the split marker precedes the component's trailing
|
|
41
|
+
# punctuation (1.x ret[0] += delim, then join), with the
|
|
42
|
+
# join's separating space
|
|
43
|
+
"#{head}#{text}#{delim} #{tail}"
|
|
44
|
+
elsif i == rendered.size - 1
|
|
45
|
+
"#{head}#{text}"
|
|
46
|
+
else
|
|
47
|
+
"#{head}#{text}#{tail}"
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
terminate(parts.join).rstrip
|
|
51
|
+
end
|
|
52
|
+
|
|
25
53
|
def evaluate(fields)
|
|
26
54
|
rendered = @slots.filter_map do |(name, head, tail)|
|
|
27
55
|
field = fields[normalise(name)] or next
|
|
@@ -57,6 +85,9 @@ module Relaton
|
|
|
57
85
|
end
|
|
58
86
|
|
|
59
87
|
def terminate(body)
|
|
88
|
+
# An element ending in a colon carries its own separator: the
|
|
89
|
+
# following sentence period collapses (1.x punctuation cleanup)
|
|
90
|
+
body = body.gsub(/:\s*\.\s*/, ": ")
|
|
60
91
|
return body if @terminator.strip.empty?
|
|
61
92
|
return body if body.rstrip.end_with?(@terminator.strip)
|
|
62
93
|
|