relaton-render 3.0.0.pre.alpha.3 → 3.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/relaton/render/general.rb +5 -1
- data/lib/relaton/render/iso690/element.rb +52 -1
- data/lib/relaton/render/iso690/elements/access.rb +44 -0
- data/lib/relaton/render/iso690/elements/component_part.rb +59 -13
- data/lib/relaton/render/iso690/elements/creator.rb +40 -30
- data/lib/relaton/render/iso690/elements/edition.rb +18 -2
- data/lib/relaton/render/iso690/elements/extent.rb +51 -0
- data/lib/relaton/render/iso690/elements/location.rb +17 -2
- data/lib/relaton/render/iso690/elements/medium.rb +7 -2
- data/lib/relaton/render/iso690/elements/production.rb +10 -2
- data/lib/relaton/render/iso690/elements/pub_date.rb +2 -1
- data/lib/relaton/render/iso690/elements/series.rb +21 -0
- data/lib/relaton/render/iso690/elements/size.rb +37 -0
- data/lib/relaton/render/iso690/elements/stddoc.rb +40 -0
- data/lib/relaton/render/iso690/elements/title.rb +16 -1
- data/lib/relaton/render/iso690/elements.rb +8 -0
- data/lib/relaton/render/iso690/fields.rb +2 -1
- data/lib/relaton/render/iso690/i18n/en.yml +22 -0
- data/lib/relaton/render/iso690/kinds.rb +6 -2
- data/lib/relaton/render/iso690/renderer.rb +11 -4
- data/lib/relaton/render/iso690/style/locale.rb +6 -1
- data/lib/relaton/render/iso690/style/name_form.rb +10 -0
- data/lib/relaton/render/iso690/style/scheme.rb +2 -0
- data/lib/relaton/render/iso690/style/template_map.rb +4 -0
- data/lib/relaton/render/iso690/style/type_template.rb +6 -0
- data/lib/relaton/render/iso690/style.rb +14 -4
- data/lib/relaton/render/version.rb +1 -1
- data/lib/relaton-render.rb +4 -0
- metadata +5 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ee63b0602a2be7f4c00c3d0fee7795387cac5b695037ffec618271b380ce6098
|
|
4
|
+
data.tar.gz: 1bd28b75e32a979ba82cedc675851d61c26769010c62823a85180a5c8236a8a5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '070696e6d0f15855bb40365c0e5953f05a80ea0fc0b453b2c1b62da371d9bfa0459c9d7e593ba79add4227c4ecc97af247561cd78345d835a5262f9be30ee651'
|
|
7
|
+
data.tar.gz: 660e8b5815000dd129e2893f39584d64999eda4d863c704ba9a4ee1e8cda9d68bdefbdfb8675a2aea25309a649709d7fa24458e5682c27c5932344cbe5e8ceed
|
|
@@ -56,7 +56,11 @@ module Relaton
|
|
|
56
56
|
def render_all(bib, type: "author-date")
|
|
57
57
|
items = facade_bibitems(bib) or return nil
|
|
58
58
|
items.each_with_object({}).with_index do |(item, m), i|
|
|
59
|
-
ref =
|
|
59
|
+
ref = begin
|
|
60
|
+
@renderer.render(item)
|
|
61
|
+
rescue ::Relaton::Render::Unrenderable
|
|
62
|
+
next
|
|
63
|
+
end
|
|
60
64
|
m[item.id] = {
|
|
61
65
|
id: item.id, ord: i, formattedref: ref,
|
|
62
66
|
citation: {
|
|
@@ -8,10 +8,11 @@ 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:)
|
|
11
|
+
def initialize(model, style:, i18n:, kind: nil)
|
|
12
12
|
@model = model
|
|
13
13
|
@style = style
|
|
14
14
|
@i18n = i18n
|
|
15
|
+
@kind = kind
|
|
15
16
|
end
|
|
16
17
|
|
|
17
18
|
def present?
|
|
@@ -37,6 +38,56 @@ module Relaton
|
|
|
37
38
|
r.is_a?(String) ? r == role : r.type == role
|
|
38
39
|
end
|
|
39
40
|
end
|
|
41
|
+
|
|
42
|
+
def org_name(contributor)
|
|
43
|
+
name = Array(contributor.organization&.name)
|
|
44
|
+
.map { |n| localized(n).upcase }.join(", ")
|
|
45
|
+
abbrev = localized(contributor.organization&.abbreviation)
|
|
46
|
+
return name if name.empty? || abbrev.empty?
|
|
47
|
+
|
|
48
|
+
"#{name} (#{abbrev})"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def item_kind
|
|
52
|
+
@kind || Kinds.kind_for(@model.type)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def person_completename(person)
|
|
56
|
+
localized(person.name&.completename)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def person_surname(person)
|
|
60
|
+
localized(person.name&.surname)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def person_given(person)
|
|
64
|
+
parts = Array(person.name&.forename).map do |f|
|
|
65
|
+
content = localized(f)
|
|
66
|
+
next content unless content.empty?
|
|
67
|
+
next "#{f.initial}." if f.initial.to_s.length == 1
|
|
68
|
+
|
|
69
|
+
""
|
|
70
|
+
end.reject(&:empty?)
|
|
71
|
+
return localized(person.name&.formatted_initials) if parts.empty?
|
|
72
|
+
return parts.map { |p| "#{p[0]}." }.join(" ") if initials?
|
|
73
|
+
|
|
74
|
+
parts.join(" ")
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def initials?
|
|
78
|
+
@style.scheme.name_form.initials
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def join_names(names)
|
|
82
|
+
case names.size
|
|
83
|
+
when 0 then ""
|
|
84
|
+
when 1 then names.first
|
|
85
|
+
when 2 then names.join(" #{@i18n.label('and')} ")
|
|
86
|
+
else
|
|
87
|
+
"#{names[0..-2].join(', ')}#{@i18n.label('oxford_comma')} " \
|
|
88
|
+
"#{@i18n.label('and')} #{names.last}"
|
|
89
|
+
end
|
|
90
|
+
end
|
|
40
91
|
end
|
|
41
92
|
end
|
|
42
93
|
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "date"
|
|
4
|
+
|
|
5
|
+
module Relaton
|
|
6
|
+
module Render
|
|
7
|
+
module Iso690
|
|
8
|
+
module Elements
|
|
9
|
+
# Date of access (ISO 690 clause 7.12): "[viewed: September 3,
|
|
10
|
+
# 2019]" for online resources consulted.
|
|
11
|
+
class Access < Element
|
|
12
|
+
def present?
|
|
13
|
+
!date_text.empty?
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def render
|
|
17
|
+
"[#{@i18n.label('viewed')}: #{date_text}]"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def date_text
|
|
23
|
+
d = Array(@model.date).find { |x| x.type == "accessed" } or return ""
|
|
24
|
+
value = (d.at || d.from || d.to).to_s
|
|
25
|
+
parts = value.split("-")
|
|
26
|
+
case parts.size
|
|
27
|
+
when 3
|
|
28
|
+
date = ::Date.parse(value)
|
|
29
|
+
"#{month(date.month)} #{date.day}, #{date.year}"
|
|
30
|
+
when 2
|
|
31
|
+
"#{month(parts[1].to_i)} #{parts[0]}"
|
|
32
|
+
else
|
|
33
|
+
value
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def month(num)
|
|
38
|
+
@i18n.label("month_#{num}")
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -4,44 +4,90 @@ module Relaton
|
|
|
4
4
|
module Render
|
|
5
5
|
module Iso690
|
|
6
6
|
module Elements
|
|
7
|
-
# Component part within a host (ISO 690 clause 7.4):
|
|
8
|
-
#
|
|
9
|
-
# partOf
|
|
7
|
+
# Component part within a host (ISO 690 clause 7.4): host title
|
|
8
|
+
# (emphasised), host editors, host edition, host place and
|
|
9
|
+
# publisher. Host data comes from the typed partOf/includedIn
|
|
10
|
+
# relation.
|
|
10
11
|
class ComponentPart < Element
|
|
12
|
+
HOST_RELATION_TYPES = %w[partOf includedIn].freeze
|
|
13
|
+
|
|
11
14
|
def present?
|
|
12
|
-
!
|
|
15
|
+
!host_title_text.empty?
|
|
13
16
|
end
|
|
14
17
|
|
|
15
18
|
def render
|
|
16
|
-
"#{
|
|
17
|
-
"#{
|
|
18
|
-
"#{@style.templates.title_close}" \
|
|
19
|
-
"#{host_edition}"
|
|
19
|
+
"#{lead}#{host_title}#{host_editors}#{host_edition}" \
|
|
20
|
+
"#{host_production}"
|
|
20
21
|
end
|
|
21
22
|
|
|
22
23
|
private
|
|
23
24
|
|
|
25
|
+
def lead
|
|
26
|
+
label = @i18n.label("in")
|
|
27
|
+
label.empty? ? "" : "#{label} "
|
|
28
|
+
end
|
|
29
|
+
|
|
24
30
|
def relation
|
|
25
|
-
Array(@model.relation).find
|
|
31
|
+
Array(@model.relation).find do |r|
|
|
32
|
+
HOST_RELATION_TYPES.include?(r.type)
|
|
33
|
+
end
|
|
26
34
|
end
|
|
27
35
|
|
|
28
36
|
def host
|
|
29
37
|
relation&.bibitem
|
|
30
38
|
end
|
|
31
39
|
|
|
32
|
-
def
|
|
40
|
+
def host_title_text
|
|
33
41
|
return localized(relation&.description) if host.nil?
|
|
34
42
|
|
|
35
43
|
Array(host.title).map { |t| localized(t) }.join(" ").strip
|
|
36
44
|
end
|
|
37
45
|
|
|
46
|
+
def host_title
|
|
47
|
+
return "" if host_title_text.empty?
|
|
48
|
+
|
|
49
|
+
"#{@style.templates.title_open}#{host_title_text}" \
|
|
50
|
+
"#{@style.templates.title_close}"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def host_editors
|
|
54
|
+
eds = Array(host&.contributor).select { |c| has_role?(c, "editor") }
|
|
55
|
+
names = eds.each_with_index
|
|
56
|
+
.map { |c, i| host_person_name(c, first: i.zero?) }
|
|
57
|
+
.reject(&:empty?)
|
|
58
|
+
return "" if names.empty?
|
|
59
|
+
|
|
60
|
+
" (#{@i18n.label(eds.one? ? 'ed' : 'eds')} #{join_names(names)})"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# ISO 690 clause 7.2 host editors: the first name inverted
|
|
64
|
+
# (surname, initials), subsequent names in direct order, all in
|
|
65
|
+
# mixed case
|
|
66
|
+
def host_person_name(contributor, first:)
|
|
67
|
+
person = contributor.person or return ""
|
|
68
|
+
|
|
69
|
+
surname = person_surname(person)
|
|
70
|
+
given = person_given(person)
|
|
71
|
+
if first
|
|
72
|
+
[surname, given].reject(&:empty?).join(", ")
|
|
73
|
+
else
|
|
74
|
+
[given, surname].reject(&:empty?).join(" ")
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
38
78
|
def host_edition
|
|
39
79
|
return "" if host.nil?
|
|
40
80
|
|
|
41
|
-
edition = Edition.new(host, style: @style, i18n: @i18n
|
|
42
|
-
|
|
81
|
+
edition = Edition.new(host, style: @style, i18n: @i18n,
|
|
82
|
+
kind: item_kind)
|
|
83
|
+
edition.present? ? ". #{edition.render}" : ""
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def host_production
|
|
87
|
+
return "" if host.nil?
|
|
43
88
|
|
|
44
|
-
|
|
89
|
+
production = Production.new(host, style: @style, i18n: @i18n)
|
|
90
|
+
production.present? ? ". #{production.render}" : ""
|
|
45
91
|
end
|
|
46
92
|
end
|
|
47
93
|
end
|
|
@@ -15,7 +15,32 @@ module Relaton
|
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def render
|
|
18
|
-
|
|
18
|
+
case role_placement
|
|
19
|
+
when "afterPeriod"
|
|
20
|
+
# the in-slot trailing space separates the (eds.) marker
|
|
21
|
+
# from the next element, which follows directly in the
|
|
22
|
+
# template; a lone editor is unmarked
|
|
23
|
+
if editors? && creators.size > 1
|
|
24
|
+
"#{period(join(names))} (#{role_word}) "
|
|
25
|
+
else
|
|
26
|
+
"#{period(join(names))} "
|
|
27
|
+
end
|
|
28
|
+
else
|
|
29
|
+
marker = editors? && creators.size > 1 ? " (#{role_word})" : ''
|
|
30
|
+
"#{join(names)}#{marker}"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def role_placement
|
|
35
|
+
@style.scheme.name_form.role_placement || "glue"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def period(text)
|
|
39
|
+
text.end_with?(".") ? text : "#{text}."
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def role_word
|
|
43
|
+
@i18n.label(creators.one? ? "ed" : "eds")
|
|
19
44
|
end
|
|
20
45
|
|
|
21
46
|
# In-text (name-and-date) form: the principal creator only
|
|
@@ -55,11 +80,19 @@ module Relaton
|
|
|
55
80
|
|
|
56
81
|
def format(contributor, first:)
|
|
57
82
|
person = contributor.person or return org_name(contributor)
|
|
58
|
-
name =
|
|
83
|
+
name = completename_name(person, first: first) ||
|
|
59
84
|
personal_name(person, first: first)
|
|
60
85
|
name.empty? ? org_name(contributor) : name
|
|
61
86
|
end
|
|
62
87
|
|
|
88
|
+
def completename_name(person, first:)
|
|
89
|
+
complete = completename(person) or return nil
|
|
90
|
+
return complete unless name_form.completename_upcase
|
|
91
|
+
|
|
92
|
+
first ? @style.render_name(surname: complete, given: "") :
|
|
93
|
+
complete.upcase
|
|
94
|
+
end
|
|
95
|
+
|
|
63
96
|
def personal_name(person, first:)
|
|
64
97
|
surname = surname(person).to_s
|
|
65
98
|
given = given_names(person)
|
|
@@ -75,21 +108,7 @@ module Relaton
|
|
|
75
108
|
end
|
|
76
109
|
|
|
77
110
|
def join(names)
|
|
78
|
-
|
|
79
|
-
when 0 then ""
|
|
80
|
-
when 1 then names.first
|
|
81
|
-
when 2 then names.join(" #{@i18n.label('and')} ")
|
|
82
|
-
else
|
|
83
|
-
"#{names[0..-2].join(', ')}#{@i18n.label('oxford_comma')} " \
|
|
84
|
-
"#{@i18n.label('and')} #{names.last}"
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
def role_suffix
|
|
89
|
-
return "" unless editors?
|
|
90
|
-
|
|
91
|
-
suffix = creators.one? ? @i18n.label("ed") : @i18n.label("eds")
|
|
92
|
-
" (#{suffix})"
|
|
111
|
+
join_names(names)
|
|
93
112
|
end
|
|
94
113
|
|
|
95
114
|
def editors?
|
|
@@ -97,30 +116,21 @@ module Relaton
|
|
|
97
116
|
end
|
|
98
117
|
|
|
99
118
|
def completename(person)
|
|
100
|
-
value =
|
|
119
|
+
value = person_completename(person)
|
|
101
120
|
value.empty? ? nil : value
|
|
102
121
|
end
|
|
103
122
|
|
|
104
123
|
def surname(person)
|
|
105
|
-
value =
|
|
124
|
+
value = person_surname(person)
|
|
106
125
|
value.empty? ? nil : value
|
|
107
126
|
end
|
|
108
127
|
|
|
109
128
|
def given_names(person)
|
|
110
|
-
|
|
111
|
-
.reject(&:empty?)
|
|
112
|
-
return names.map { |n| "#{n[0]}." }.join(" ") if name_form.initials
|
|
113
|
-
|
|
114
|
-
names.join(" ")
|
|
129
|
+
person_given(person)
|
|
115
130
|
end
|
|
116
131
|
|
|
117
132
|
def org_name(contributor)
|
|
118
|
-
|
|
119
|
-
.map { |n| localized(n).upcase }.join(", ")
|
|
120
|
-
abbrev = localized(contributor.organization&.abbreviation)
|
|
121
|
-
return name if name.empty? || abbrev.empty?
|
|
122
|
-
|
|
123
|
-
"#{name} (#{abbrev})"
|
|
133
|
+
super
|
|
124
134
|
end
|
|
125
135
|
end
|
|
126
136
|
end
|
|
@@ -8,17 +8,33 @@ module Relaton
|
|
|
8
8
|
# edition through Edition#number (falling back to its content).
|
|
9
9
|
class Edition < Element
|
|
10
10
|
def present?
|
|
11
|
-
!
|
|
11
|
+
!raw.to_s.empty?
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def render
|
|
15
|
+
return "#{@i18n.label('version')} #{raw}" unless word_edition?
|
|
16
|
+
|
|
17
|
+
word = @i18n.label("edition_#{number}")
|
|
18
|
+
return word unless word == "edition_#{number}"
|
|
19
|
+
|
|
15
20
|
"#{ordinalize(number)} #{@i18n.label('edition')}"
|
|
16
21
|
end
|
|
17
22
|
|
|
18
23
|
private
|
|
19
24
|
|
|
25
|
+
# Worded editions are document editions; online resources
|
|
26
|
+
# carry numbered builds rendered as versions
|
|
27
|
+
def word_edition?
|
|
28
|
+
number.positive? &&
|
|
29
|
+
%w[monograph component_part serial_part]
|
|
30
|
+
.include?(item_kind)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def raw
|
|
34
|
+
(@model.edition&.number || @model.edition&.content).to_s
|
|
35
|
+
end
|
|
36
|
+
|
|
20
37
|
def number
|
|
21
|
-
raw = @model.edition&.number || @model.edition&.content
|
|
22
38
|
Integer(raw)
|
|
23
39
|
rescue ArgumentError, TypeError
|
|
24
40
|
0
|
|
@@ -0,0 +1,51 @@
|
|
|
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(", ")
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def numeration
|
|
21
|
+
%w[volume issue].filter_map do |type|
|
|
22
|
+
loc = localities.find { |l| l.type == type }
|
|
23
|
+
next if loc.nil? || loc.reference_from.to_s.empty?
|
|
24
|
+
|
|
25
|
+
"#{@i18n.label(type)} #{loc.reference_from}"
|
|
26
|
+
end.join(" ")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def page_range
|
|
30
|
+
loc = localities.find { |l| l.type == "page" } or return ""
|
|
31
|
+
from = loc.reference_from.to_s
|
|
32
|
+
to = loc.reference_to.to_s
|
|
33
|
+
if to.empty? || to == from
|
|
34
|
+
"#{@i18n.label('page')} #{from}"
|
|
35
|
+
else
|
|
36
|
+
"#{@i18n.label('pages')} " \
|
|
37
|
+
"#{from}#{@i18n.label('date_range')}#{to}"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def localities
|
|
42
|
+
Array(@model.extent).flat_map do |e|
|
|
43
|
+
Array(e.locality) +
|
|
44
|
+
Array(e.locality_stack).flat_map { |s| Array(s.locality) }
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -12,13 +12,28 @@ module Relaton
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def render
|
|
15
|
-
|
|
15
|
+
text = if (form = @style.templates.uri) && !form.empty?
|
|
16
|
+
::Relaton::Render::Iso690::Template.new(form).evaluate(
|
|
17
|
+
"uri" => Template::Field[!uri.empty?, uri],
|
|
18
|
+
)
|
|
19
|
+
else
|
|
20
|
+
uri
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
"#{@i18n.label('available_from')} #{text}".strip
|
|
16
24
|
end
|
|
17
25
|
|
|
18
26
|
private
|
|
19
27
|
|
|
20
28
|
def uri
|
|
21
|
-
|
|
29
|
+
from_accesslocation =
|
|
30
|
+
Array(@model.accesslocation).map(&:to_s).reject(&:empty?)
|
|
31
|
+
unless from_accesslocation.empty?
|
|
32
|
+
return from_accesslocation.first.to_s
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
Array(@model.source).map { |u| u.content.to_s }
|
|
36
|
+
.reject(&:empty?).first.to_s
|
|
22
37
|
end
|
|
23
38
|
end
|
|
24
39
|
end
|
|
@@ -12,13 +12,18 @@ module Relaton
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def render
|
|
15
|
-
|
|
15
|
+
medium
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
private
|
|
19
19
|
|
|
20
20
|
def medium
|
|
21
|
-
|
|
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
|
|
22
27
|
end
|
|
23
28
|
end
|
|
24
29
|
end
|
|
@@ -20,8 +20,16 @@ module Relaton
|
|
|
20
20
|
private
|
|
21
21
|
|
|
22
22
|
def places
|
|
23
|
-
Array(@model.place).map
|
|
24
|
-
|
|
23
|
+
Array(@model.place).map do |p|
|
|
24
|
+
structured = [p.city.to_s, p.region.map(&:content),
|
|
25
|
+
p.country.map(&:content)]
|
|
26
|
+
.flatten.reject { |c| c.strip.empty? }
|
|
27
|
+
if structured.empty? && !p.formatted_place.to_s.empty?
|
|
28
|
+
next p.formatted_place.to_s
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
structured.join(", ")
|
|
32
|
+
end.reject(&:empty?)
|
|
25
33
|
end
|
|
26
34
|
|
|
27
35
|
def publishers
|
|
@@ -21,7 +21,8 @@ module Relaton
|
|
|
21
21
|
|
|
22
22
|
def publication_date
|
|
23
23
|
Array(@model.date).find(&:published) ||
|
|
24
|
-
Array(@model.date).find { |d| d.type.nil? }
|
|
24
|
+
Array(@model.date).find { |d| d.type.nil? } ||
|
|
25
|
+
Array(@model.date).find { |d| d.type == "created" }
|
|
25
26
|
end
|
|
26
27
|
|
|
27
28
|
def publication_date_value
|
|
@@ -11,6 +11,15 @@ module Relaton
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
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
|
+
|
|
14
23
|
base = "#{@style.templates.title_open}#{series_title}" \
|
|
15
24
|
"#{@style.templates.title_close}"
|
|
16
25
|
if number.empty?
|
|
@@ -33,6 +42,18 @@ module Relaton
|
|
|
33
42
|
def number
|
|
34
43
|
series&.number.to_s
|
|
35
44
|
end
|
|
45
|
+
|
|
46
|
+
def run
|
|
47
|
+
series&.run.to_s
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# The style's series form for this item's resource kind, falling
|
|
51
|
+
# back to the style-wide form
|
|
52
|
+
def series_form
|
|
53
|
+
variant = @style.per_type
|
|
54
|
+
.find { |t| t.type == Kinds.kind_for(@model.type) }
|
|
55
|
+
variant&.series || @style.templates.series
|
|
56
|
+
end
|
|
36
57
|
end
|
|
37
58
|
end
|
|
38
59
|
end
|
|
@@ -0,0 +1,37 @@
|
|
|
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
|
+
unit.nil? || unit == "size_#{type}" ? content : "#{content} #{unit}"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
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
|
|
@@ -12,12 +12,27 @@ module Relaton
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def render
|
|
15
|
-
|
|
15
|
+
text = main_title
|
|
16
|
+
return text if analytic?
|
|
17
|
+
if (form = @style.title_form_for(Kinds.kind_for(@model.type)))
|
|
18
|
+
text = ::Relaton::Render::Iso690::Template.new(form).evaluate(
|
|
19
|
+
"title" => Template::Field[!text.empty?, text],
|
|
20
|
+
)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
"#{@style.templates.title_open}#{text}" \
|
|
16
24
|
"#{@style.templates.title_close}"
|
|
17
25
|
end
|
|
18
26
|
|
|
19
27
|
private
|
|
20
28
|
|
|
29
|
+
# ISO 690: analytic titles are not emphasised; the host or
|
|
30
|
+
# serial carries the emphasis
|
|
31
|
+
def analytic?
|
|
32
|
+
%w[component_part serial_part]
|
|
33
|
+
.include?(Kinds.kind_for(@model.type))
|
|
34
|
+
end
|
|
35
|
+
|
|
21
36
|
def main_title
|
|
22
37
|
t = Array(@model.title).find { |x| (x.type || "main") == "main" } ||
|
|
23
38
|
Array(@model.title).first
|
|
@@ -16,6 +16,10 @@ module Relaton
|
|
|
16
16
|
autoload :Location, "relaton/render/iso690/elements/location"
|
|
17
17
|
autoload :Numeration, "relaton/render/iso690/elements/numeration"
|
|
18
18
|
autoload :ComponentPart, "relaton/render/iso690/elements/component_part"
|
|
19
|
+
autoload :Size, "relaton/render/iso690/elements/size"
|
|
20
|
+
autoload :Extent, "relaton/render/iso690/elements/extent"
|
|
21
|
+
autoload :Access, "relaton/render/iso690/elements/access"
|
|
22
|
+
autoload :Stddoc, "relaton/render/iso690/elements/stddoc"
|
|
19
23
|
|
|
20
24
|
CLASSES = {
|
|
21
25
|
creator: Creator,
|
|
@@ -29,6 +33,10 @@ module Relaton
|
|
|
29
33
|
location: Location,
|
|
30
34
|
numeration: Numeration,
|
|
31
35
|
component_part: ComponentPart,
|
|
36
|
+
size: Size,
|
|
37
|
+
extent: Extent,
|
|
38
|
+
access: Access,
|
|
39
|
+
stddoc: Stddoc,
|
|
32
40
|
}.freeze
|
|
33
41
|
|
|
34
42
|
def self.build(name, model, style:, i18n:)
|
|
@@ -10,7 +10,8 @@ module Relaton
|
|
|
10
10
|
class Fields
|
|
11
11
|
ELEMENT_SLOTS = %i[
|
|
12
12
|
creator title edition medium series production
|
|
13
|
-
date numeration component_part identifier location
|
|
13
|
+
date numeration component_part identifier location size
|
|
14
|
+
extent access stddoc
|
|
14
15
|
].freeze
|
|
15
16
|
|
|
16
17
|
def initialize(model, style:, i18n:, disambiguator: nil)
|
|
@@ -1,6 +1,28 @@
|
|
|
1
1
|
lang: en
|
|
2
2
|
labels:
|
|
3
3
|
edition: "ed"
|
|
4
|
+
size_volume: "vol."
|
|
5
|
+
size_page: "pp."
|
|
6
|
+
page: "p."
|
|
7
|
+
pages: "pp."
|
|
8
|
+
volume: "vol."
|
|
9
|
+
issue: "no."
|
|
10
|
+
version: "Version"
|
|
11
|
+
medium_website: "[website]"
|
|
12
|
+
viewed: "viewed"
|
|
13
|
+
month_1: "January"
|
|
14
|
+
month_2: "February"
|
|
15
|
+
month_3: "March"
|
|
16
|
+
month_4: "April"
|
|
17
|
+
month_5: "May"
|
|
18
|
+
month_6: "June"
|
|
19
|
+
month_7: "July"
|
|
20
|
+
month_8: "August"
|
|
21
|
+
month_9: "September"
|
|
22
|
+
month_10: "October"
|
|
23
|
+
month_11: "November"
|
|
24
|
+
month_12: "December"
|
|
25
|
+
|
|
4
26
|
series_no: "no."
|
|
5
27
|
report_no: "Report no."
|
|
6
28
|
available_from: "Available from:"
|
|
@@ -26,8 +26,12 @@ module Relaton
|
|
|
26
26
|
"inbook" => "component_part",
|
|
27
27
|
"incollection" => "component_part",
|
|
28
28
|
"inproceedings" => "component_part",
|
|
29
|
-
"
|
|
30
|
-
"
|
|
29
|
+
"article" => "serial_part",
|
|
30
|
+
"software" => "online",
|
|
31
|
+
"unpublished" => "online",
|
|
32
|
+
"dataset" => "online",
|
|
33
|
+
"website" => "webdoc",
|
|
34
|
+
"webresource" => "webdoc",
|
|
31
35
|
"webpage" => "online",
|
|
32
36
|
"online" => "online",
|
|
33
37
|
}.freeze
|
|
@@ -8,8 +8,6 @@ module Relaton
|
|
|
8
8
|
# the template evaluator to the field resolver. No XML, no
|
|
9
9
|
# intermediate hashes.
|
|
10
10
|
class Renderer
|
|
11
|
-
class Unrenderable < StandardError; end
|
|
12
|
-
|
|
13
11
|
DEFAULT_STYLE = "author-date"
|
|
14
12
|
|
|
15
13
|
class << self
|
|
@@ -33,14 +31,23 @@ module Relaton
|
|
|
33
31
|
end
|
|
34
32
|
|
|
35
33
|
def render(model)
|
|
36
|
-
|
|
34
|
+
kind = Kinds.kind_for(model.type)
|
|
35
|
+
template = @style.template_for(kind, home: home_docid?(model))
|
|
36
|
+
out = Template.new(template).evaluate(
|
|
37
37
|
Fields.new(model, style: @style, i18n: @i18n).to_h,
|
|
38
38
|
)
|
|
39
|
-
raise Unrenderable,
|
|
39
|
+
raise ::Relaton::Render::Unrenderable,
|
|
40
|
+
"no renderable elements" if out.strip.empty?
|
|
40
41
|
|
|
41
42
|
out
|
|
42
43
|
end
|
|
43
44
|
|
|
45
|
+
def home_docid?(model)
|
|
46
|
+
type = @style.scheme.home_docid_type
|
|
47
|
+
!type.nil? && !type.empty? &&
|
|
48
|
+
Array(model.docidentifier).any? { |d| d.type == type }
|
|
49
|
+
end
|
|
50
|
+
|
|
44
51
|
# In-text form: SURNAME, year + disambiguator (name-and-date)
|
|
45
52
|
def citation(model, disambiguator: nil)
|
|
46
53
|
Template.new(@style.templates.citation).evaluate(
|
|
@@ -14,6 +14,9 @@ module Relaton
|
|
|
14
14
|
attribute :in_str, :string
|
|
15
15
|
attribute :at, :string
|
|
16
16
|
attribute :available_at, :string
|
|
17
|
+
# Arbitrary label overrides (edition words, join punctuation,
|
|
18
|
+
# size units): merged over the language pack verbatim.
|
|
19
|
+
attribute :labels, :hash, default: -> { {} }
|
|
17
20
|
|
|
18
21
|
key_value do
|
|
19
22
|
map "and", to: :conj
|
|
@@ -23,6 +26,7 @@ module Relaton
|
|
|
23
26
|
map "in", to: :in_str
|
|
24
27
|
map "at", to: :at
|
|
25
28
|
map "availableAt", to: :available_at
|
|
29
|
+
map "labels", to: :labels
|
|
26
30
|
end
|
|
27
31
|
|
|
28
32
|
LABEL_FOR = {
|
|
@@ -32,10 +36,11 @@ module Relaton
|
|
|
32
36
|
}.freeze
|
|
33
37
|
|
|
34
38
|
def label_map
|
|
35
|
-
LABEL_FOR.filter_map do |attr, label|
|
|
39
|
+
base = LABEL_FOR.filter_map do |attr, label|
|
|
36
40
|
value = public_send(attr)
|
|
37
41
|
[label, value] unless value.nil?
|
|
38
42
|
end.to_h
|
|
43
|
+
base.merge(labels || {})
|
|
39
44
|
end
|
|
40
45
|
end
|
|
41
46
|
end
|
|
@@ -9,10 +9,20 @@ module Relaton
|
|
|
9
9
|
class NameForm < Lutaml::Model::Serializable
|
|
10
10
|
attribute :initials, :boolean, default: -> { false }
|
|
11
11
|
attribute :given_name_first, :boolean, default: -> { false }
|
|
12
|
+
# Where an all-editor creator list's role marker sits relative
|
|
13
|
+
# to the name's terminating period: "glue" renders
|
|
14
|
+
# "NAME (eds.)." (the name-date default), "afterPeriod"
|
|
15
|
+
# renders "NAME. (eds.)" (ISO 690 clause-flavour).
|
|
16
|
+
attribute :role_placement, :string, default: "glue"
|
|
17
|
+
# A completename is rendered, not decomposed; styles that
|
|
18
|
+
# upcase inverted names may declare the completename treatment
|
|
19
|
+
attribute :completename_upcase, :boolean, default: -> { false }
|
|
12
20
|
|
|
13
21
|
key_value do
|
|
14
22
|
map "initials", to: :initials
|
|
15
23
|
map "givenNameFirst", to: :given_name_first
|
|
24
|
+
map "rolePlacement", to: :role_placement
|
|
25
|
+
map "completenameUpcase", to: :completename_upcase
|
|
16
26
|
end
|
|
17
27
|
end
|
|
18
28
|
end
|
|
@@ -10,11 +10,13 @@ module Relaton
|
|
|
10
10
|
attribute :system, :string, default: "name-date"
|
|
11
11
|
attribute :name_form, NameForm, default: -> { NameForm.new }
|
|
12
12
|
attribute :locale, Locale, default: -> { Locale.new }
|
|
13
|
+
attribute :home_docid_type, :string
|
|
13
14
|
|
|
14
15
|
key_value do
|
|
15
16
|
map "system", to: :system
|
|
16
17
|
map "nameForm", to: :name_form
|
|
17
18
|
map "locale", to: :locale
|
|
19
|
+
map "homeDocidType", to: :home_docid_type
|
|
18
20
|
end
|
|
19
21
|
end
|
|
20
22
|
end
|
|
@@ -13,6 +13,8 @@ module Relaton
|
|
|
13
13
|
attribute :citation, :string, default: "{{surname}}, {{date}}"
|
|
14
14
|
attribute :reference, :string
|
|
15
15
|
attribute :name, :string, default: "{{surname}}, {{givenNames}}"
|
|
16
|
+
attribute :series, :string, default: ""
|
|
17
|
+
attribute :uri, :string, default: ""
|
|
16
18
|
|
|
17
19
|
key_value do
|
|
18
20
|
map "titleOpen", to: :title_open
|
|
@@ -20,6 +22,8 @@ module Relaton
|
|
|
20
22
|
map "citation", to: :citation
|
|
21
23
|
map "reference", to: :reference
|
|
22
24
|
map "name", to: :name
|
|
25
|
+
map "series", to: :series
|
|
26
|
+
map "uri", to: :uri
|
|
23
27
|
end
|
|
24
28
|
end
|
|
25
29
|
end
|
|
@@ -9,10 +9,16 @@ module Relaton
|
|
|
9
9
|
class TypeTemplate < Lutaml::Model::Serializable
|
|
10
10
|
attribute :type, :string
|
|
11
11
|
attribute :template, :string
|
|
12
|
+
attribute :series, :string
|
|
13
|
+
attribute :title, :string
|
|
14
|
+
attribute :home, :boolean
|
|
12
15
|
|
|
13
16
|
key_value do
|
|
14
17
|
map "type", to: :type
|
|
15
18
|
map "template", to: :template
|
|
19
|
+
map "series", to: :series
|
|
20
|
+
map "title", to: :title
|
|
21
|
+
map "home", to: :home
|
|
16
22
|
end
|
|
17
23
|
end
|
|
18
24
|
end
|
|
@@ -41,10 +41,20 @@ module Relaton
|
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
# Per-type template selection is a data lookup; unmatched types
|
|
44
|
-
# fall back to the general reference template.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
# fall back to the general reference template. When a kind has
|
|
45
|
+
# home-flagged variants, the split is resolved by whether the item
|
|
46
|
+
# carries the scheme's home document identifier.
|
|
47
|
+
def template_for(type, home: nil)
|
|
48
|
+
candidates = per_type.select { |t| t.type == type.to_s }
|
|
49
|
+
if candidates.size > 1 && !home.nil?
|
|
50
|
+
candidates = candidates.select { |t| (t.home || false) == home }
|
|
51
|
+
end
|
|
52
|
+
candidates.first&.template || templates.reference
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def title_form_for(type)
|
|
56
|
+
per_type.lazy.select { |t| t.type == type.to_s }
|
|
57
|
+
.find { |t| t.title && !t.title.empty? }&.title
|
|
48
58
|
end
|
|
49
59
|
|
|
50
60
|
# First-creator name form: the style's declared name template with
|
data/lib/relaton-render.rb
CHANGED
|
@@ -5,6 +5,10 @@ require "relaton/render/iso690"
|
|
|
5
5
|
|
|
6
6
|
module Relaton
|
|
7
7
|
module Render
|
|
8
|
+
# A bibliographic item whose style template resolves to nothing: the
|
|
9
|
+
# single-item API raises, the batch API skips
|
|
10
|
+
class Unrenderable < StandardError; end
|
|
11
|
+
|
|
8
12
|
# Deprecated 1.x engine leaves, restored so flavor gems that still
|
|
9
13
|
# subclass them load against this line. Superseded by the Iso690
|
|
10
14
|
# engine; removed when the flavors have migrated to CitationStyle
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: relaton-render
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.0.0.pre.alpha.
|
|
4
|
+
version: 3.0.0.pre.alpha.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
@@ -120,9 +120,11 @@ files:
|
|
|
120
120
|
- lib/relaton/render/iso690.rb
|
|
121
121
|
- lib/relaton/render/iso690/element.rb
|
|
122
122
|
- lib/relaton/render/iso690/elements.rb
|
|
123
|
+
- lib/relaton/render/iso690/elements/access.rb
|
|
123
124
|
- lib/relaton/render/iso690/elements/component_part.rb
|
|
124
125
|
- lib/relaton/render/iso690/elements/creator.rb
|
|
125
126
|
- lib/relaton/render/iso690/elements/edition.rb
|
|
127
|
+
- lib/relaton/render/iso690/elements/extent.rb
|
|
126
128
|
- lib/relaton/render/iso690/elements/identifier.rb
|
|
127
129
|
- lib/relaton/render/iso690/elements/location.rb
|
|
128
130
|
- lib/relaton/render/iso690/elements/medium.rb
|
|
@@ -130,6 +132,8 @@ files:
|
|
|
130
132
|
- lib/relaton/render/iso690/elements/production.rb
|
|
131
133
|
- lib/relaton/render/iso690/elements/pub_date.rb
|
|
132
134
|
- lib/relaton/render/iso690/elements/series.rb
|
|
135
|
+
- lib/relaton/render/iso690/elements/size.rb
|
|
136
|
+
- lib/relaton/render/iso690/elements/stddoc.rb
|
|
133
137
|
- lib/relaton/render/iso690/elements/title.rb
|
|
134
138
|
- lib/relaton/render/iso690/fields.rb
|
|
135
139
|
- lib/relaton/render/iso690/i18n.rb
|