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.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rake.yml +6 -1
  3. data/.github/workflows/release.yml +24 -0
  4. data/.gitignore +14 -0
  5. data/.rubocop.yml +2 -2
  6. data/Gemfile +17 -1
  7. data/README.adoc +13 -258
  8. data/docs/citation-style-pack.adoc +370 -0
  9. data/lib/relaton/render/general.rb +305 -0
  10. data/lib/relaton/render/iso690/element.rb +153 -0
  11. data/lib/relaton/render/iso690/elements/access.rb +44 -0
  12. data/lib/relaton/render/iso690/elements/authorizer.rb +30 -0
  13. data/lib/relaton/render/iso690/elements/citeid.rb +39 -0
  14. data/lib/relaton/render/iso690/elements/component_part.rb +99 -0
  15. data/lib/relaton/render/iso690/elements/creator.rb +258 -0
  16. data/lib/relaton/render/iso690/elements/edition.rb +92 -0
  17. data/lib/relaton/render/iso690/elements/extent.rb +71 -0
  18. data/lib/relaton/render/iso690/elements/identifier.rb +46 -0
  19. data/lib/relaton/render/iso690/elements/location.rb +73 -0
  20. data/lib/relaton/render/iso690/elements/medium.rb +32 -0
  21. data/lib/relaton/render/iso690/elements/numeration.rb +26 -0
  22. data/lib/relaton/render/iso690/elements/production.rb +92 -0
  23. data/lib/relaton/render/iso690/elements/pub_date.rb +56 -0
  24. data/lib/relaton/render/iso690/elements/series.rb +66 -0
  25. data/lib/relaton/render/iso690/elements/size.rb +41 -0
  26. data/lib/relaton/render/iso690/elements/status.rb +30 -0
  27. data/lib/relaton/render/iso690/elements/stddoc.rb +40 -0
  28. data/lib/relaton/render/iso690/elements/title.rb +56 -0
  29. data/lib/relaton/render/iso690/elements/updated.rb +38 -0
  30. data/lib/relaton/render/iso690/elements.rb +67 -0
  31. data/lib/relaton/render/iso690/fields.rb +142 -0
  32. data/lib/relaton/render/iso690/i18n/en.yml +47 -0
  33. data/lib/relaton/render/iso690/i18n/fr.yml +14 -0
  34. data/lib/relaton/render/iso690/i18n/ja.yml +38 -0
  35. data/lib/relaton/render/iso690/i18n.rb +83 -0
  36. data/lib/relaton/render/iso690/identifier_kinds.rb +29 -0
  37. data/lib/relaton/render/iso690/index_renderer.rb +57 -0
  38. data/lib/relaton/render/iso690/kinds.rb +53 -0
  39. data/lib/relaton/render/iso690/renderer.rb +148 -0
  40. data/lib/relaton/render/iso690/rules.rb +36 -0
  41. data/lib/relaton/render/iso690/style/locale.rb +53 -0
  42. data/lib/relaton/render/iso690/style/name_form.rb +87 -0
  43. data/lib/relaton/render/iso690/style/scheme.rb +33 -0
  44. data/lib/relaton/render/iso690/style/sort_rule.rb +25 -0
  45. data/lib/relaton/render/iso690/style/template_map.rb +40 -0
  46. data/lib/relaton/render/iso690/style/type_template.rb +37 -0
  47. data/lib/relaton/render/iso690/style.rb +222 -0
  48. data/lib/relaton/render/iso690/styles/apa.yml +45 -0
  49. data/lib/relaton/render/iso690/styles/author-date.yml +31 -0
  50. data/lib/relaton/render/iso690/styles/chicago.yml +48 -0
  51. data/lib/relaton/render/iso690/styles/ieee-sa.yml +45 -0
  52. data/lib/relaton/render/iso690/styles/lncs.yml +54 -0
  53. data/lib/relaton/render/iso690/template.rb +248 -0
  54. data/lib/relaton/render/iso690.rb +27 -0
  55. data/lib/relaton/render/version.rb +1 -1
  56. data/lib/relaton-render.rb +11 -5
  57. data/relaton-render.gemspec +6 -13
  58. metadata +61 -116
  59. data/lib/isodoc/i18n.rb +0 -33
  60. data/lib/isodoc-yaml/i18n-ar.yaml +0 -44
  61. data/lib/isodoc-yaml/i18n-de.yaml +0 -43
  62. data/lib/isodoc-yaml/i18n-en.yaml +0 -40
  63. data/lib/isodoc-yaml/i18n-es.yaml +0 -47
  64. data/lib/isodoc-yaml/i18n-fr.yaml +0 -48
  65. data/lib/isodoc-yaml/i18n-ru.yaml +0 -51
  66. data/lib/isodoc-yaml/i18n-zh-Hans.yaml +0 -40
  67. data/lib/relaton/render/fields/date.rb +0 -45
  68. data/lib/relaton/render/fields/fields.rb +0 -245
  69. data/lib/relaton/render/general/citations.rb +0 -96
  70. data/lib/relaton/render/general/config.yml +0 -79
  71. data/lib/relaton/render/general/render.rb +0 -185
  72. data/lib/relaton/render/general/render_classes.rb +0 -129
  73. data/lib/relaton/render/parse/parse.rb +0 -64
  74. data/lib/relaton/render/parse/parse_contributors.rb +0 -132
  75. data/lib/relaton/render/parse/parse_extract.rb +0 -180
  76. data/lib/relaton/render/template/liquid.rb +0 -15
  77. data/lib/relaton/render/template/template.rb +0 -188
  78. data/lib/relaton/render/utils/utils.rb +0 -27
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Relaton
4
+ module Render
5
+ module Iso690
6
+ # Base data element (ISO 690 clause 7). An element wraps typed data
7
+ # taken from the relaton model instance; #present? is the omission
8
+ # predicate, #render the element's formatting method. Punctuation
9
+ # between elements belongs to the kind/style, not the element.
10
+ class Element
11
+ def initialize(model, style:, i18n:, kind: nil, short: false)
12
+ @model = model
13
+ @style = style
14
+ @i18n = i18n
15
+ @kind = kind
16
+ @short = short
17
+ end
18
+
19
+ def present?
20
+ false
21
+ end
22
+
23
+ def render
24
+ nil
25
+ end
26
+
27
+ private
28
+
29
+ def localized(value)
30
+ value&.content.to_s
31
+ end
32
+
33
+ # Localizable values scoped to the render language: the variants
34
+ # declared in that language, or all of them when none matches
35
+ def titles_in_lang(values)
36
+ titles = Array(values)
37
+ in_lang = titles.select { |t| Array(t.language).include?(@i18n.lang) }
38
+ in_lang.empty? ? titles : in_lang
39
+ end
40
+
41
+ def contributors(role)
42
+ Array(@model.contributor).select { |c| has_role?(c, role) }
43
+ end
44
+
45
+ def has_role?(contributor, role)
46
+ Array(contributor.role).any? do |r|
47
+ r.is_a?(String) ? r == role : r.type == role
48
+ end
49
+ end
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
56
+ name = Array(contributor.organization&.name)
57
+ .map(&shown).join(", ")
58
+ abbrev = localized(contributor.organization&.abbreviation)
59
+ return name if name.empty? || abbrev.empty?
60
+
61
+ "#{name} (#{abbrev})"
62
+ end
63
+
64
+ def item_kind
65
+ @kind || @style.kind_for(@model.type)
66
+ end
67
+
68
+ def person_completename(person)
69
+ localized(person.name&.completename)
70
+ end
71
+
72
+ def person_surname(person)
73
+ localized(person.name&.surname)
74
+ end
75
+
76
+ def person_given(person)
77
+ parts = Array(person.name&.forename).map do |f|
78
+ content = localized(f)
79
+ next content unless content.empty?
80
+ next initial(f.initial) if f.initial.to_s.length == 1
81
+
82
+ ""
83
+ end.reject(&:empty?)
84
+ sep = @style.scheme.name_form.initials_separator.to_s
85
+ return reinitials(localized(person.name&.formatted_initials)) if
86
+ parts.empty?
87
+ return given_with_first_full(parts, sep) if initials?
88
+
89
+ parts.join(" ")
90
+ end
91
+
92
+ # The document-history name form: the first forename cites in
93
+ # full, the rest as initials ("Milena S.")
94
+ def given_with_first_full(parts, sep)
95
+ initials = parts.map { |p| initial(p) }
96
+ return initials.join(sep) unless
97
+ @style.scheme.name_form.subsequent_initials
98
+
99
+ rest = parts.drop(1).map { |p| initial(p) }.join(sep)
100
+ rest.empty? ? parts.first : "#{parts.first}#{sep}#{rest}"
101
+ end
102
+
103
+ # Declared initials are a list in string form; rejoin them with
104
+ # the style's separator
105
+ def reinitials(raw)
106
+ sep = @style.scheme.name_form.initials_separator.to_s
107
+ dots = @style.scheme.name_form.initials_period
108
+ raw.split(/\s+/).reject(&:empty?).map { |i| dots ? i : i.delete(".") }
109
+ .join(sep)
110
+ end
111
+
112
+ def initial(name)
113
+ @style.scheme.name_form.initials_period ? "#{name[0]}." : name[0]
114
+ end
115
+
116
+ def initials?
117
+ @style.scheme.name_form.initials
118
+ end
119
+
120
+ def join_names(names)
121
+ # A style with no join word renders a plain serial list,
122
+ # separated per the locale (the comma, or the CJK 、)
123
+ if @i18n.label("and").empty? && names.size > 1
124
+ sep = @i18n.label("list_separator")
125
+ sep = ", " if sep == "list_separator"
126
+ return names.join(sep)
127
+ end
128
+
129
+ oxford = @i18n.label("oxford_comma")
130
+ if serial_list? && names.size > 1 && !oxford.empty?
131
+ return "#{names[0..-2].join(', ')}#{oxford} " \
132
+ "#{@i18n.label('and')} #{names.last}"
133
+ end
134
+
135
+ case names.size
136
+ when 0 then ""
137
+ when 1 then names.first
138
+ when 2 then names.join(" #{@i18n.label('and')} ")
139
+ else
140
+ "#{names[0..-2].join(', ')}#{oxford} " \
141
+ "#{@i18n.label('and')} #{names.last}"
142
+ end
143
+ end
144
+
145
+ # Serial list style: the oxford comma appears at every gap of a
146
+ # name list, including between two names
147
+ def serial_list?
148
+ @style.scheme.name_form.list_style == "serial"
149
+ end
150
+ end
151
+ end
152
+ end
153
+ 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
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Relaton
4
+ module Render
5
+ module Iso690
6
+ module Elements
7
+ # The authorizing body of a standard (the IETF RFC Series, the
8
+ # W3C Recommendation process): the organization carrying the
9
+ # authorizer role, cited after the status
10
+ class Authorizer < Element
11
+ def present?
12
+ !authorizer_name.empty?
13
+ end
14
+
15
+ def render
16
+ authorizer_name
17
+ end
18
+
19
+ private
20
+
21
+ def authorizer_name
22
+ contributors("authorizer").map do |c|
23
+ Array(c.organization&.name).map { |n| localized(n) }.join(", ")
24
+ end.reject(&:empty?).first.to_s
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Relaton
4
+ module Render
5
+ module Iso690
6
+ module Elements
7
+ # The cited identifier with the item's status beside it and the
8
+ # list comma ("OGC 05-020r27 (Draft)," / "ISO 712,"): the comma
9
+ # stays with the identifier whichever side of the status it
10
+ # follows
11
+ class Citeid < Element
12
+ def present?
13
+ identifier_element.present?
14
+ end
15
+
16
+ def render
17
+ text = identifier_element.render
18
+ stage = status_element.present? ? status_element.render : ""
19
+ stage.empty? ? "#{text}," : "#{text}#{stage},"
20
+ end
21
+
22
+ private
23
+
24
+ def identifier_element
25
+ @identifier_element ||=
26
+ Identifier.new(@model, style: @style, i18n: @i18n, kind: @kind,
27
+ short: @short)
28
+ end
29
+
30
+ def status_element
31
+ @status_element ||=
32
+ Status.new(@model, style: @style, i18n: @i18n, kind: @kind,
33
+ short: @short)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Relaton
4
+ module Render
5
+ module Iso690
6
+ module Elements
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.
11
+ class ComponentPart < Element
12
+ HOST_RELATION_TYPES = %w[partOf includedIn].freeze
13
+
14
+ def present?
15
+ !host_title_text.empty?
16
+ end
17
+
18
+ def render
19
+ "#{lead}#{host_title}#{host_editors}#{host_edition}" \
20
+ "#{host_production}"
21
+ end
22
+
23
+ private
24
+
25
+ def lead
26
+ label = @i18n.label("in")
27
+ label.empty? ? "" : "#{label} "
28
+ end
29
+
30
+ def relation
31
+ Array(@model.relation).find do |r|
32
+ HOST_RELATION_TYPES.include?(r.type)
33
+ end
34
+ end
35
+
36
+ def host
37
+ relation&.bibitem
38
+ end
39
+
40
+ def host_title_text
41
+ return localized(relation&.description) if host.nil?
42
+
43
+ titles_in_lang(host.title).map { |t| localized(t) }
44
+ .join(" ").strip
45
+ end
46
+
47
+ def host_title
48
+ return "" if host_title_text.empty?
49
+
50
+ "#{@style.templates.title_open}#{host_title_text}" \
51
+ "#{@style.templates.title_close}"
52
+ end
53
+
54
+ def host_editors
55
+ eds = Array(host&.contributor).select { |c| has_role?(c, "editor") }
56
+ names = eds.each_with_index
57
+ .map { |c, i| host_person_name(c, first: i.zero?) }
58
+ .reject(&:empty?)
59
+ return "" if names.empty?
60
+
61
+ " (#{@i18n.label(eds.one? ? 'ed' : 'eds')} #{join_names(names)})"
62
+ end
63
+
64
+ # ISO 690 clause 7.2 host editors: the first name inverted
65
+ # (surname, initials), subsequent names in direct order, all in
66
+ # mixed case
67
+ def host_person_name(contributor, first:)
68
+ person = contributor.person or return ""
69
+
70
+ surname = person_surname(person)
71
+ given = person_given(person)
72
+ if @style.scheme.name_form.inverted_all
73
+ @style.render_name(surname: surname, given: given)
74
+ elsif first
75
+ [surname, given].reject(&:empty?).join(", ")
76
+ else
77
+ [given, surname].reject(&:empty?).join(" ")
78
+ end
79
+ end
80
+
81
+ def host_edition
82
+ return "" if host.nil?
83
+
84
+ edition = Edition.new(host, style: @style, i18n: @i18n,
85
+ kind: item_kind)
86
+ edition.present? ? ". #{edition.render}" : ""
87
+ end
88
+
89
+ def host_production
90
+ return "" if host.nil?
91
+
92
+ production = Production.new(host, style: @style, i18n: @i18n)
93
+ production.present? ? ". #{production.render}" : ""
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,258 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Relaton
4
+ module Render
5
+ module Iso690
6
+ module Elements
7
+ # Name(s) of creator(s) (ISO 690 clause 7.2). The first creator is
8
+ # given inverted per the style's name form (SURNAME, forenames),
9
+ # subsequent creators in direct order (forenames SURNAME); name
10
+ # parts come from the typed model, never decomposed from a
11
+ # rendered string.
12
+ class Creator < Element
13
+ def present?
14
+ !creators.empty? || !fallback_name.empty?
15
+ end
16
+
17
+ def render
18
+ return plain_render if @short
19
+
20
+ joined = if (truncated = truncated_creators)
21
+ truncated
22
+ elsif creators.empty?
23
+ fallback_name
24
+ elsif (form = @style.templates.creators) && !form.empty?
25
+ ::Relaton::Render::Iso690::Template.new(form).evaluate(
26
+ "names" => Template::Field[true, join(names)],
27
+ )
28
+ else
29
+ join(names)
30
+ end
31
+ case role_placement
32
+ when "afterPeriod"
33
+ # the in-slot trailing space separates the (eds.) marker
34
+ # from the next element, which follows directly in the
35
+ # template; a lone editor is unmarked unless declared
36
+ if editors? && marked?
37
+ "#{period(joined)}#{role_space}#{role_parenthesis} "
38
+ else
39
+ "#{period(joined)} "
40
+ end
41
+ else
42
+ marker = editors? && marked? ? "#{role_space}#{role_parenthesis}" : ''
43
+ "#{joined}#{marker}"
44
+ end
45
+ end
46
+
47
+ # The short cite splits after the creators: they render as a
48
+ # plain join, without the wrap and sentence period (1.x
49
+ # short-cite templates carried no wrap)
50
+ def plain_render
51
+ marker = editors? && marked? ? "#{role_space}#{role_parenthesis}" : ''
52
+ "#{join(names)}#{marker}"
53
+ end
54
+
55
+ # The editor role marker's parentheses follow the locale
56
+ # (ASCII for Latin scripts, fullwidth for CJK)
57
+ def role_parenthesis
58
+ open = @i18n.punct_fetch("open-paren", "(")
59
+ close = @i18n.punct_fetch("close-paren", ")")
60
+ "#{open}#{role_word}#{close}"
61
+ end
62
+
63
+ # CJK cites the role marker tight against the name list
64
+ def role_space
65
+ @i18n.punct_fetch("marker-space", " ")
66
+ end
67
+
68
+ # The template-level role slot ("{{role}}"): the marker with
69
+ # its locale spacing, when the creators are all editors
70
+ def role_marker
71
+ editors? ? "#{role_space}#{role_parenthesis}" : ""
72
+ end
73
+
74
+ def role_placement
75
+ @style.scheme.name_form.role_placement || "glue"
76
+ end
77
+
78
+ def period(text)
79
+ text.end_with?(".") ? text : "#{text}."
80
+ end
81
+
82
+ def marked?
83
+ name_form.editors_marked &&
84
+ (creators.size > 1 || name_form.lone_editor_marked)
85
+ end
86
+
87
+ def role_word
88
+ @i18n.label(creators.one? ? "ed" : "eds")
89
+ end
90
+
91
+ # In-text (name-and-date) form: the principal creator only;
92
+ # at the style's et-al threshold the cite truncates
93
+ # ("Aluffi <em>et al.</em>")
94
+ def in_text
95
+ return in_text_etal if in_text_etal?
96
+
97
+ name = creators.first ? in_text_name(creators.first) : ""
98
+ etal? ? "#{name} <em>et al.</em>" : name
99
+ end
100
+
101
+ # The in-text cite truncates at its own threshold: at
102
+ # inTextEtalCount creators it shows the first
103
+ # inTextEtalDisplay surnames ("Aluffi <em>et al.</em>")
104
+ def in_text_etal
105
+ names = creators.first(name_form.in_text_etal_display)
106
+ .map { |c| in_text_name(c) }
107
+ "#{join(names)} <em>et al.</em>"
108
+ end
109
+
110
+ # The in-text cite names by surname alone (the 1.x
111
+ # authorcitetemplate), never by initials
112
+ def in_text_name(contributor)
113
+ person = contributor.person or return org_name(contributor)
114
+ complete = completename(person)
115
+ return complete unless complete.nil?
116
+
117
+ raw = surname(person).to_s
118
+ name_form.surname_upcase ? raw.upcase : raw
119
+ end
120
+
121
+ def in_text_etal?
122
+ count = name_form.in_text_etal_count
123
+ count.positive? && creators.size >= count
124
+ end
125
+
126
+ def etal?
127
+ count = name_form.etal_count
128
+ count.positive? && creators.size >= count
129
+ end
130
+
131
+ # The principal creator's given names, as the name form declares
132
+ def principal_given
133
+ principal = creators.first or return ""
134
+ person = principal.person or return ""
135
+ complete = completename(person)
136
+ return "" if complete
137
+
138
+ given_names(person)
139
+ end
140
+
141
+ private
142
+
143
+ def creators
144
+ @creators ||= begin
145
+ found = contributors("author")
146
+ found = contributors("editor") if found.empty?
147
+ found
148
+ end
149
+ end
150
+
151
+ # The declared creator fallback for items with no personal
152
+ # creators: the publishers' abbreviation or full name — all
153
+ # publishers cite, joined per the style's list style ("ISO and
154
+ # IEC")
155
+ def fallback_name
156
+ orgs = contributors("publisher")
157
+ name = case name_form.creator_fallback
158
+ when "publisher_abbrev"
159
+ orgs.map do |c|
160
+ localized(c.organization&.abbreviation)
161
+ end
162
+ when "publisher_name"
163
+ orgs.map do |c|
164
+ localized(Array(c.organization&.name).first)
165
+ end
166
+ else
167
+ []
168
+ end
169
+ join_names(name.reject(&:empty?))
170
+ end
171
+
172
+ def names
173
+ creators.each_with_index
174
+ .map do |contributor, index|
175
+ format(contributor,
176
+ first: index.zero?)
177
+ end
178
+ end
179
+
180
+ # The style's et-al truncation ("Aluffi, P., D. Anderson,
181
+ # M. Hering <em>et al.</em>"): at etalCount creators, the
182
+ # first etalDisplay names cite, comma-joined without the
183
+ # terminal and-join
184
+ def truncated_creators
185
+ count = name_form.etal_count
186
+ display = name_form.etal_display
187
+ return nil if count <= 0 || display <= 0 ||
188
+ creators.size < count
189
+
190
+ creators.first(display).each_with_index
191
+ .map do |contributor, index|
192
+ format(contributor, first: index.zero?)
193
+ end.join(", ") + " <em>et al.</em>"
194
+ end
195
+
196
+ def format(contributor, first:)
197
+ person = contributor.person or return org_name(contributor)
198
+ name = completename_name(person, first: first) ||
199
+ personal_name(person, first: first)
200
+ name.empty? ? org_name(contributor) : name
201
+ end
202
+
203
+ def completename_name(person, first:)
204
+ complete = completename(person) or return nil
205
+ return complete unless name_form.completename_upcase
206
+
207
+ first ? @style.render_name(surname: complete, given: "") :
208
+ complete.upcase
209
+ end
210
+
211
+ def personal_name(person, first:)
212
+ surname = surname(person).to_s
213
+ given = given_names(person)
214
+ if (first || name_form.inverted_all) && !name_form.given_name_first
215
+ @style.render_name(surname: surname, given: given)
216
+ else
217
+ # the direct form initializes per the style's initials
218
+ # knob ("S. Payne" over "Sam Payne")
219
+ shown = name_form.subsequent_surname_upcase ? surname.upcase :
220
+ surname
221
+ [person_given(person), shown].reject(&:empty?).join(" ")
222
+ end
223
+ end
224
+
225
+ def name_form
226
+ @style.scheme.name_form
227
+ end
228
+
229
+ def join(names)
230
+ join_names(names)
231
+ end
232
+
233
+ def editors?
234
+ !creators.empty? && creators.all? { |c| has_role?(c, "editor") }
235
+ end
236
+
237
+ def completename(person)
238
+ value = person_completename(person)
239
+ value.empty? ? nil : value
240
+ end
241
+
242
+ def surname(person)
243
+ value = person_surname(person)
244
+ value.empty? ? nil : value
245
+ end
246
+
247
+ def given_names(person)
248
+ person_given(person)
249
+ end
250
+
251
+ def org_name(contributor)
252
+ super
253
+ end
254
+ end
255
+ end
256
+ end
257
+ end
258
+ end