relaton 3.0.7 → 3.1.0

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 (240) hide show
  1. checksums.yaml +4 -4
  2. data/bin/relaton +20 -0
  3. data/lib/relaton/3gpp/processor.rb +1 -1
  4. data/lib/relaton/3gpp.rb +3 -3
  5. data/lib/relaton/adobe/processor.rb +1 -1
  6. data/lib/relaton/adobe.rb +3 -3
  7. data/lib/relaton/bib/item_data.rb +2 -2
  8. data/lib/relaton/bib/model/docidentifier.rb +17 -2
  9. data/lib/relaton/bib.rb +2 -2
  10. data/lib/relaton/bibcollection.rb +146 -0
  11. data/lib/relaton/bibdata.rb +92 -0
  12. data/lib/relaton/bipm/data_outcomes_parser.rb +6 -1
  13. data/lib/relaton/bipm/processor.rb +1 -1
  14. data/lib/relaton/bipm.rb +3 -3
  15. data/lib/relaton/bsi/processor.rb +1 -1
  16. data/lib/relaton/bsi.rb +1 -1
  17. data/lib/relaton/calconnect/data_fetcher.rb +2 -2
  18. data/lib/relaton/calconnect/model/item.rb +1 -1
  19. data/lib/relaton/calconnect/processor.rb +1 -1
  20. data/lib/relaton/calconnect.rb +2 -2
  21. data/lib/relaton/ccsds/data/fetcher.rb +1 -1
  22. data/lib/relaton/ccsds/model/item.rb +1 -1
  23. data/lib/relaton/ccsds/processor.rb +1 -1
  24. data/lib/relaton/ccsds/util.rb +1 -1
  25. data/lib/relaton/ccsds.rb +1 -1
  26. data/lib/relaton/cen/processor.rb +1 -1
  27. data/lib/relaton/cen.rb +2 -2
  28. data/lib/relaton/cie/data_fetcher.rb +3 -3
  29. data/lib/relaton/cie/processor.rb +1 -1
  30. data/lib/relaton/cie.rb +2 -2
  31. data/lib/relaton/cli/base_convertor.rb +134 -0
  32. data/lib/relaton/cli/command.rb +396 -0
  33. data/lib/relaton/cli/data_fetcher.rb +20 -0
  34. data/lib/relaton/cli/frontend/dist/404.html +18 -0
  35. data/lib/relaton/cli/frontend/dist/app.iife.js +1 -0
  36. data/lib/relaton/cli/frontend/dist/style.css +3 -0
  37. data/lib/relaton/cli/frontend/index.html +14 -0
  38. data/lib/relaton/cli/frontend/package-lock.json +2945 -0
  39. data/lib/relaton/cli/frontend/package.json +29 -0
  40. data/lib/relaton/cli/frontend/public/404.html +18 -0
  41. data/lib/relaton/cli/frontend/src/App.test.ts +501 -0
  42. data/lib/relaton/cli/frontend/src/App.vue +506 -0
  43. data/lib/relaton/cli/frontend/src/app.ts +90 -0
  44. data/lib/relaton/cli/frontend/src/components/DocumentDetail.test.ts +142 -0
  45. data/lib/relaton/cli/frontend/src/components/DocumentDetail.vue +228 -0
  46. data/lib/relaton/cli/frontend/src/components/DocumentRow.vue +88 -0
  47. data/lib/relaton/cli/frontend/src/components/Icon.test.ts +44 -0
  48. data/lib/relaton/cli/frontend/src/components/Icon.vue +55 -0
  49. data/lib/relaton/cli/frontend/src/components/Pagination.vue +26 -0
  50. data/lib/relaton/cli/frontend/src/dev/main.ts +100 -0
  51. data/lib/relaton/cli/frontend/src/dev/sample-data.json +71 -0
  52. data/lib/relaton/cli/frontend/src/lib/filter.test.ts +88 -0
  53. data/lib/relaton/cli/frontend/src/lib/filter.ts +95 -0
  54. data/lib/relaton/cli/frontend/src/lib/hydrate.test.ts +102 -0
  55. data/lib/relaton/cli/frontend/src/lib/hydrate.ts +55 -0
  56. data/lib/relaton/cli/frontend/src/lib/shards.test.ts +319 -0
  57. data/lib/relaton/cli/frontend/src/lib/shards.ts +183 -0
  58. data/lib/relaton/cli/frontend/src/lib/types.ts +148 -0
  59. data/lib/relaton/cli/frontend/src/lib/url.test.ts +151 -0
  60. data/lib/relaton/cli/frontend/src/lib/url.ts +111 -0
  61. data/lib/relaton/cli/frontend/src/shims-vue.d.ts +5 -0
  62. data/lib/relaton/cli/frontend/src/styles/index.css +27 -0
  63. data/lib/relaton/cli/frontend/src/test/fetch.ts +58 -0
  64. data/lib/relaton/cli/frontend/tsconfig.json +20 -0
  65. data/lib/relaton/cli/frontend/vite.config.ts +42 -0
  66. data/lib/relaton/cli/frontend/vitest.config.ts +11 -0
  67. data/lib/relaton/cli/frontend_assets.rb +58 -0
  68. data/lib/relaton/cli/full_text_search.rb +98 -0
  69. data/lib/relaton/cli/i18n.yaml +1 -0
  70. data/lib/relaton/cli/index_item_normalizer.rb +380 -0
  71. data/lib/relaton/cli/index_site_generator.rb +1043 -0
  72. data/lib/relaton/cli/relaton_file.rb +290 -0
  73. data/lib/relaton/cli/subcommand_collection.rb +263 -0
  74. data/lib/relaton/cli/subcommand_db.rb +81 -0
  75. data/lib/relaton/cli/templates/_document.liquid +111 -0
  76. data/lib/relaton/cli/templates/_index.liquid +61 -0
  77. data/lib/relaton/cli/templates/index/page.liquid +20 -0
  78. data/lib/relaton/cli/templates/index-style.css +1017 -0
  79. data/lib/relaton/cli/util.rb +8 -0
  80. data/lib/relaton/cli/xml_convertor.rb +47 -0
  81. data/lib/relaton/cli/xml_to_html_renderer.rb +117 -0
  82. data/lib/relaton/cli/yaml_convertor.rb +102 -0
  83. data/lib/relaton/cli.rb +103 -0
  84. data/lib/relaton/core/processor.rb +1 -1
  85. data/lib/relaton/core.rb +1 -1
  86. data/lib/relaton/db/registry.rb +1 -1
  87. data/lib/relaton/db.rb +1 -1
  88. data/lib/relaton/doi/CLAUDE.md +1 -1
  89. data/lib/relaton/doi/parser.rb +2 -1
  90. data/lib/relaton/doi/processor.rb +1 -1
  91. data/lib/relaton/doi.rb +4 -4
  92. data/lib/relaton/easc/processor.rb +1 -1
  93. data/lib/relaton/easc.rb +3 -3
  94. data/lib/relaton/ecma/data_fetcher.rb +1 -1
  95. data/lib/relaton/ecma/processor.rb +1 -1
  96. data/lib/relaton/ecma.rb +2 -2
  97. data/lib/relaton/element_finder.rb +29 -0
  98. data/lib/relaton/etsi/data_fetcher.rb +2 -2
  99. data/lib/relaton/etsi/item.rb +1 -1
  100. data/lib/relaton/etsi/processor.rb +2 -2
  101. data/lib/relaton/etsi.rb +1 -1
  102. data/lib/relaton/evs/processor.rb +1 -1
  103. data/lib/relaton/evs.rb +2 -2
  104. data/lib/relaton/gb/CLAUDE.md +1 -1
  105. data/lib/relaton/gb/bibliography.rb +1 -1
  106. data/lib/relaton/gb/processor.rb +1 -1
  107. data/lib/relaton/gb.rb +2 -2
  108. data/lib/relaton/gost/processor.rb +1 -1
  109. data/lib/relaton/gost.rb +3 -3
  110. data/lib/relaton/iala/processor.rb +1 -1
  111. data/lib/relaton/iala.rb +3 -3
  112. data/lib/relaton/iana/data_fetcher.rb +1 -1
  113. data/lib/relaton/iana/processor.rb +1 -1
  114. data/lib/relaton/iana.rb +2 -2
  115. data/lib/relaton/iec/data_fetcher.rb +1 -1
  116. data/lib/relaton/iec/processor.rb +1 -1
  117. data/lib/relaton/iec.rb +2 -2
  118. data/lib/relaton/ieee/processor.rb +1 -1
  119. data/lib/relaton/ieee.rb +2 -2
  120. data/lib/relaton/ietf/CLAUDE.md +1 -1
  121. data/lib/relaton/ietf/data_fetcher.rb +1 -1
  122. data/lib/relaton/ietf/processor.rb +1 -1
  123. data/lib/relaton/ietf.rb +2 -2
  124. data/lib/relaton/iho/CLAUDE.md +3 -3
  125. data/lib/relaton/iho/bibliography.rb +41 -27
  126. data/lib/relaton/iho/hash_parser_v1.rb +1 -1
  127. data/lib/relaton/iho/processor.rb +4 -3
  128. data/lib/relaton/iho.rb +2 -2
  129. data/lib/relaton/index.rb +1 -1
  130. data/lib/relaton/isbn/processor.rb +1 -1
  131. data/lib/relaton/isbn.rb +1 -1
  132. data/lib/relaton/iso/hash_parser_v1.rb +1 -1
  133. data/lib/relaton/iso/processor.rb +1 -1
  134. data/lib/relaton/iso.rb +3 -3
  135. data/lib/relaton/itu/processor.rb +1 -1
  136. data/lib/relaton/itu.rb +3 -3
  137. data/lib/relaton/jcgm/processor.rb +1 -1
  138. data/lib/relaton/jcgm.rb +2 -2
  139. data/lib/relaton/jis/processor.rb +1 -1
  140. data/lib/relaton/jis.rb +2 -2
  141. data/lib/relaton/nist/CLAUDE.md +11 -1
  142. data/lib/relaton/nist/README.adoc +7 -1
  143. data/lib/relaton/nist/hit_collection.rb +25 -1
  144. data/lib/relaton/nist/mods_parser.rb +32 -8
  145. data/lib/relaton/nist/processor.rb +1 -1
  146. data/lib/relaton/nist/pubs_export.rb +69 -11
  147. data/lib/relaton/nist.rb +2 -2
  148. data/lib/relaton/oasis/processor.rb +1 -1
  149. data/lib/relaton/oasis.rb +2 -2
  150. data/lib/relaton/ogc/CLAUDE.md +1 -1
  151. data/lib/relaton/ogc/processor.rb +1 -1
  152. data/lib/relaton/ogc.rb +2 -2
  153. data/lib/relaton/oiml/processor.rb +1 -1
  154. data/lib/relaton/oiml.rb +2 -2
  155. data/lib/relaton/omg/CLAUDE.md +1 -1
  156. data/lib/relaton/omg/processor.rb +1 -1
  157. data/lib/relaton/omg.rb +1 -1
  158. data/lib/relaton/plateau/CLAUDE.md +1 -1
  159. data/lib/relaton/plateau/data_fetcher.rb +1 -1
  160. data/lib/relaton/plateau/processor.rb +1 -1
  161. data/lib/relaton/plateau.rb +2 -2
  162. data/lib/relaton/render/general.rb +319 -0
  163. data/lib/relaton/render/iso690/csl_import.rb +129 -0
  164. data/lib/relaton/render/iso690/element.rb +153 -0
  165. data/lib/relaton/render/iso690/elements/access.rb +44 -0
  166. data/lib/relaton/render/iso690/elements/authorizer.rb +45 -0
  167. data/lib/relaton/render/iso690/elements/citeid.rb +39 -0
  168. data/lib/relaton/render/iso690/elements/component_part.rb +97 -0
  169. data/lib/relaton/render/iso690/elements/creator.rb +267 -0
  170. data/lib/relaton/render/iso690/elements/edition.rb +97 -0
  171. data/lib/relaton/render/iso690/elements/extent.rb +71 -0
  172. data/lib/relaton/render/iso690/elements/identifier.rb +46 -0
  173. data/lib/relaton/render/iso690/elements/location.rb +73 -0
  174. data/lib/relaton/render/iso690/elements/medium.rb +32 -0
  175. data/lib/relaton/render/iso690/elements/numeration.rb +26 -0
  176. data/lib/relaton/render/iso690/elements/production.rb +111 -0
  177. data/lib/relaton/render/iso690/elements/pub_date.rb +63 -0
  178. data/lib/relaton/render/iso690/elements/series.rb +66 -0
  179. data/lib/relaton/render/iso690/elements/size.rb +45 -0
  180. data/lib/relaton/render/iso690/elements/status.rb +30 -0
  181. data/lib/relaton/render/iso690/elements/stddoc.rb +40 -0
  182. data/lib/relaton/render/iso690/elements/title.rb +56 -0
  183. data/lib/relaton/render/iso690/elements/updated.rb +38 -0
  184. data/lib/relaton/render/iso690/elements.rb +67 -0
  185. data/lib/relaton/render/iso690/fields.rb +142 -0
  186. data/lib/relaton/render/iso690/i18n/en.yml +48 -0
  187. data/lib/relaton/render/iso690/i18n/fr.yml +14 -0
  188. data/lib/relaton/render/iso690/i18n/ja.yml +38 -0
  189. data/lib/relaton/render/iso690/i18n.rb +83 -0
  190. data/lib/relaton/render/iso690/identifier_kinds.rb +29 -0
  191. data/lib/relaton/render/iso690/index_renderer.rb +57 -0
  192. data/lib/relaton/render/iso690/kinds.rb +53 -0
  193. data/lib/relaton/render/iso690/renderer.rb +150 -0
  194. data/lib/relaton/render/iso690/rules/bipm.rb +186 -0
  195. data/lib/relaton/render/iso690/rules/jis.rb +103 -0
  196. data/lib/relaton/render/iso690/rules/nist.rb +384 -0
  197. data/lib/relaton/render/iso690/rules.rb +227 -0
  198. data/lib/relaton/render/iso690/style/locale.rb +59 -0
  199. data/lib/relaton/render/iso690/style/name_form.rb +87 -0
  200. data/lib/relaton/render/iso690/style/scheme.rb +33 -0
  201. data/lib/relaton/render/iso690/style/sort_rule.rb +25 -0
  202. data/lib/relaton/render/iso690/style/template_map.rb +40 -0
  203. data/lib/relaton/render/iso690/style/type_template.rb +45 -0
  204. data/lib/relaton/render/iso690/style.rb +227 -0
  205. data/lib/relaton/render/iso690/styles/apa.yml +45 -0
  206. data/lib/relaton/render/iso690/styles/author-date.yml +31 -0
  207. data/lib/relaton/render/iso690/styles/chicago.yml +49 -0
  208. data/lib/relaton/render/iso690/styles/ieee-sa.yml +45 -0
  209. data/lib/relaton/render/iso690/styles/jis-en.yml +68 -0
  210. data/lib/relaton/render/iso690/styles/jis-ja.yml +67 -0
  211. data/lib/relaton/render/iso690/styles/lncs.yml +54 -0
  212. data/lib/relaton/render/iso690/styles/mla.yml +56 -0
  213. data/lib/relaton/render/iso690/template.rb +248 -0
  214. data/lib/relaton/render/iso690.rb +28 -0
  215. data/lib/relaton/sae/CLAUDE.md +89 -0
  216. data/lib/relaton/sae/bibdata.rb +8 -0
  217. data/lib/relaton/sae/bibitem.rb +8 -0
  218. data/lib/relaton/sae/bibliography.rb +128 -0
  219. data/lib/relaton/sae/data_fetcher.rb +219 -0
  220. data/lib/relaton/sae/doi_code.rb +83 -0
  221. data/lib/relaton/sae/item.rb +10 -0
  222. data/lib/relaton/sae/item_data.rb +6 -0
  223. data/lib/relaton/sae/processor.rb +62 -0
  224. data/lib/relaton/sae/util.rb +9 -0
  225. data/lib/relaton/sae.rb +37 -0
  226. data/lib/relaton/un/CLAUDE.md +1 -1
  227. data/lib/relaton/un/processor.rb +1 -1
  228. data/lib/relaton/un.rb +1 -1
  229. data/lib/relaton/version.rb +1 -1
  230. data/lib/relaton/w3c/bibliography.rb +1 -1
  231. data/lib/relaton/w3c/data_fetcher.rb +1 -1
  232. data/lib/relaton/w3c/processor.rb +1 -1
  233. data/lib/relaton/w3c.rb +2 -2
  234. data/lib/relaton/xsf/data_fetcher.rb +1 -1
  235. data/lib/relaton/xsf/processor.rb +1 -1
  236. data/lib/relaton/xsf.rb +2 -2
  237. data/lib/relaton-cli.rb +17 -0
  238. data/lib/relaton-render.rb +12 -0
  239. data/lib/relaton.rb +3 -2
  240. metadata +203 -23
@@ -0,0 +1,267 @@
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}#{etal_suffix}" : 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)}#{etal_suffix}"
108
+ end
109
+
110
+ # The et-al suffix is the locale's label (roman by default; a
111
+ # pack's `others` label may carry its own presentation), joined
112
+ # per the style's list style — the serial comma before it is the
113
+ # MLA form ("Fielding, Roy, et al.")
114
+ def etal_suffix
115
+ sep = name_form.list_style == "serial" ? "," : ""
116
+ "#{sep} #{@i18n.label('others')}"
117
+ end
118
+
119
+ # The in-text cite names by surname alone (the 1.x
120
+ # authorcitetemplate), never by initials
121
+ def in_text_name(contributor)
122
+ person = contributor.person or return org_name(contributor)
123
+ complete = completename(person)
124
+ return complete unless complete.nil?
125
+
126
+ raw = surname(person).to_s
127
+ name_form.surname_upcase ? raw.upcase : raw
128
+ end
129
+
130
+ def in_text_etal?
131
+ count = name_form.in_text_etal_count
132
+ count.positive? && creators.size >= count
133
+ end
134
+
135
+ def etal?
136
+ count = name_form.etal_count
137
+ count.positive? && creators.size >= count
138
+ end
139
+
140
+ # The principal creator's given names, as the name form declares
141
+ def principal_given
142
+ principal = creators.first or return ""
143
+ person = principal.person or return ""
144
+ complete = completename(person)
145
+ return "" if complete
146
+
147
+ given_names(person)
148
+ end
149
+
150
+ private
151
+
152
+ def creators
153
+ @creators ||= begin
154
+ found = contributors("author")
155
+ found = contributors("editor") if found.empty?
156
+ found
157
+ end
158
+ end
159
+
160
+ # The declared creator fallback for items with no personal
161
+ # creators: the publishers' abbreviation or full name — all
162
+ # publishers cite, joined per the style's list style ("ISO and
163
+ # IEC")
164
+ def fallback_name
165
+ orgs = contributors("publisher")
166
+ name = case name_form.creator_fallback
167
+ when "publisher_abbrev"
168
+ orgs.map do |c|
169
+ localized(c.organization&.abbreviation)
170
+ end
171
+ when "publisher_name"
172
+ orgs.map do |c|
173
+ localized(Array(c.organization&.name).first)
174
+ end
175
+ else
176
+ []
177
+ end
178
+ join_names(name.reject(&:empty?))
179
+ end
180
+
181
+ def names
182
+ creators.each_with_index
183
+ .map do |contributor, index|
184
+ format(contributor,
185
+ first: index.zero?)
186
+ end
187
+ end
188
+
189
+ # The style's et-al truncation ("Aluffi, P., D. Anderson,
190
+ # M. Hering <em>et al.</em>"): at etalCount creators, the
191
+ # first etalDisplay names cite, comma-joined without the
192
+ # terminal and-join
193
+ def truncated_creators
194
+ count = name_form.etal_count
195
+ display = name_form.etal_display
196
+ return nil if count <= 0 || display <= 0 ||
197
+ creators.size < count
198
+
199
+ creators.first(display).each_with_index
200
+ .map do |contributor, index|
201
+ format(contributor, first: index.zero?)
202
+ end.join(", ") + etal_suffix
203
+ end
204
+
205
+ def format(contributor, first:)
206
+ person = contributor.person or return org_name(contributor)
207
+ name = completename_name(person, first: first) ||
208
+ personal_name(person, first: first)
209
+ name.empty? ? org_name(contributor) : name
210
+ end
211
+
212
+ def completename_name(person, first:)
213
+ complete = completename(person) or return nil
214
+ return complete unless name_form.completename_upcase
215
+
216
+ first ? @style.render_name(surname: complete, given: "") :
217
+ complete.upcase
218
+ end
219
+
220
+ def personal_name(person, first:)
221
+ surname = surname(person).to_s
222
+ given = given_names(person)
223
+ if (first || name_form.inverted_all) && !name_form.given_name_first
224
+ @style.render_name(surname: surname, given: given)
225
+ else
226
+ # the direct form initializes per the style's initials
227
+ # knob ("S. Payne" over "Sam Payne")
228
+ shown = name_form.subsequent_surname_upcase ? surname.upcase :
229
+ surname
230
+ [person_given(person), shown].reject(&:empty?).join(" ")
231
+ end
232
+ end
233
+
234
+ def name_form
235
+ @style.scheme.name_form
236
+ end
237
+
238
+ def join(names)
239
+ join_names(names)
240
+ end
241
+
242
+ def editors?
243
+ !creators.empty? && creators.all? { |c| has_role?(c, "editor") }
244
+ end
245
+
246
+ def completename(person)
247
+ value = person_completename(person)
248
+ value.empty? ? nil : value
249
+ end
250
+
251
+ def surname(person)
252
+ value = person_surname(person)
253
+ value.empty? ? nil : value
254
+ end
255
+
256
+ def given_names(person)
257
+ person_given(person)
258
+ end
259
+
260
+ def org_name(contributor)
261
+ super
262
+ end
263
+ end
264
+ end
265
+ end
266
+ end
267
+ end
@@ -0,0 +1,97 @@
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 exact worded forms first ("first edition", "第1版"),
18
+ # then the generic ordinal template, the cardinal last: a
19
+ # specific label outranks a template that would apply to
20
+ # any number
21
+ word = @i18n.label("edition_#{number}")
22
+ return word unless word == "edition_#{number}"
23
+
24
+ ordinal = locale_template("edition_ordinal", number)
25
+ return ordinal if ordinal
26
+
27
+ cardinal_form || "#{ordinalize(number)} #{@i18n.label('edition')}"
28
+ end
29
+
30
+ # The plain cardinal form ("edition 7")
31
+ def cardinal_form
32
+ locale_template("edition_cardinal", number) ||
33
+ locale_template("version_cardinal", number)
34
+ end
35
+
36
+ private
37
+
38
+ # A locale template carrying {{ var1 }} (optionally through
39
+ # the ordinal_word filter) populated with the edition number;
40
+ # a bare label (no placeholder) is not one
41
+ def locale_template(key, value)
42
+ raw = @i18n.label(key)
43
+ return nil if raw == key || raw !~ /\{{/
44
+
45
+ if raw.include?("ordinal_word")
46
+ word = @i18n.label("ordinal_word_#{value}")
47
+ word = ordinalize(value) if word == "ordinal_word_#{value}"
48
+ raw.gsub(/\{\{\s*var1[^}]*\}\}/, word)
49
+ else
50
+ raw.gsub("{{ var1 }}", value.to_s)
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ # Worded editions are document editions; online resources
57
+ # carry numbered builds rendered as versions. A standard is a
58
+ # document edition too (its report kind carries the clause 8.11
59
+ # fallback templates, but its numbered editions read as
60
+ # "Nth edition" — the TS contract renders them so).
61
+ def word_edition?
62
+ number.positive? &&
63
+ %w[monograph component_part serial_part report]
64
+ .include?(item_kind)
65
+ end
66
+
67
+ def raw
68
+ (@model.edition&.number || @model.edition&.content).to_s
69
+ end
70
+
71
+ def number
72
+ Integer(raw)
73
+ rescue ArgumentError, TypeError
74
+ 0
75
+ end
76
+
77
+ def ordinalize(num)
78
+ "#{num}#{ordinal_suffix(num)}"
79
+ end
80
+
81
+ def ordinal_suffix(num)
82
+ case num % 100
83
+ when 11, 12, 13 then "th"
84
+ else
85
+ case num % 10
86
+ when 1 then "st"
87
+ when 2 then "nd"
88
+ when 3 then "rd"
89
+ else "th"
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ 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.gsub("%") { 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