relaton-render 3.0.0.pre.alpha.22 → 3.0.0.pre.alpha.23

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d485246fb8d04c771c257c63295000ade2b54462caaa884aa0a1aecdb22a6498
4
- data.tar.gz: 8cbb2bed01af3e82d57ebc064853ead1dc0fd7215aab812b782ce5faaf59d4c3
3
+ metadata.gz: 84f33badbec6cb9d4d80c763d0538b9a3bde40dea703623568cb52a4758134bf
4
+ data.tar.gz: b6d9d4b6317b917472350aa45c7c7056b4eb8f68337daea43a97a3603cbf8517
5
5
  SHA512:
6
- metadata.gz: 467f05b5d476d4eb759d2248b863eca01e5319b18ab98c95c7283921a27ba5cdaf5ac9fcf93f8b10e24c2dee08028bdeeea25a975473d6311820f46229670826
7
- data.tar.gz: cd5caf02198e2d34538eab81c0cd0732b1b276bc62195352e9aee68cd61221b180a585b4a3ff9d218664b3ec6deb7dc1d7c937fe85cd02bf887b8a84162071b9
6
+ metadata.gz: dd0961f370beaf6e0bc3b5233d71aee7d47773b4443a956de18278228356f164f90c6e62a9da2bb19019f1cd4076675fdc85fa9e56b1f47395d0d134912540dc
7
+ data.tar.gz: 4331fc6e0974c1975b8340e374f23522f7d252aa6dfb3b65d37afa8da919128c78f5b7589115c06c25cf74dfac0649546177b4f8494dd969cb6291e486f8401c
@@ -79,13 +79,15 @@ module Relaton
79
79
  next
80
80
  end
81
81
  m[item.id] = {
82
- id: item.id, ord: i,
83
82
  author: renderer.in_text_author(item),
84
- formattedref: terminate_reference(ref, item),
83
+ date: renderer.disambiguated_date(item,
84
+ disambiguator:
85
+ disambiguators[item.id]),
85
86
  citation: citation_renderings(item, ref, renderer,
86
87
  type: type,
87
88
  disambiguator:
88
89
  disambiguators[item.id]),
90
+ formattedref: terminate_reference(ref, item),
89
91
  }
90
92
  end
91
93
  end
@@ -93,7 +95,7 @@ module Relaton
93
95
  # isodoc's styled citations consume these keys: the default is the
94
96
  # authoritative identifier; the short cite is the style's citation,
95
97
  # falling back to the reference rendering (the 1.x short-cite)
96
- FIRST_DELIM = "<span class='fmt-first-biblio-delim'/>"
98
+ FIRST_DELIM = '<span class="fmt-first-biblio-delim"/>'
97
99
 
98
100
  # A creator list alone (flavors' document-history name forms)
99
101
  def creator_names(item)
@@ -141,11 +143,14 @@ module Relaton
141
143
  # names the key)
142
144
  def citation_renderings(item, ref, renderer, type: "author-date",
143
145
  disambiguator: nil)
144
- short = renderer.citation(item)
145
- short = if short.empty?
146
+ # A style declaring shortFromReference takes the reference form
147
+ # with the first-biblio marker (the 1.x citetemplate short)
148
+ short = if renderer.short_from_reference?
146
149
  renderer.render_short(item, FIRST_DELIM)
147
150
  else
148
- short
151
+ short = renderer.citation(item)
152
+ short.empty? ? renderer.render_short(item, FIRST_DELIM) :
153
+ short
149
154
  end
150
155
 
151
156
  title = renderer.title_citation(item)
@@ -154,15 +159,15 @@ module Relaton
154
159
  full: ref,
155
160
  default: item.docidentifier.first&.content.to_s,
156
161
  short: short,
157
- author_date: renderer.author_date_citation(
158
- item, disambiguator: disambiguator,
159
- ),
162
+ author_date: renderer.citation(item,
163
+ disambiguator: disambiguator),
160
164
  author_date_br: renderer.bracketed_date_citation(
161
165
  item, disambiguator: disambiguator,
162
166
  ),
163
167
  author: renderer.in_text_author(item),
164
- date: renderer.disambiguated_date(item, disambiguator: disambiguator),
165
- reference_tag: "",
168
+ date: renderer.disambiguated_date(item),
169
+ # reference_tag: no rendering — isodoc falls back to the
170
+ # biblio-tag anchor (the 1.x nil)
166
171
  title: title,
167
172
  title_reference_tag: title,
168
173
  }
@@ -240,7 +245,9 @@ module Relaton
240
245
  end
241
246
  out.empty? or break
242
247
  end
243
- out.map { |x| x.text.strip }
248
+ # esc-wrapped: isodoc's l10n skips the tags, leaving a standard
249
+ # identifier's punctuation verbatim (the 1.x Parse behaviour)
250
+ out.map { |x| "<esc>#{x.text.strip}</esc>" }
244
251
  end
245
252
 
246
253
  def facade_id_type(id)
@@ -48,8 +48,7 @@ module Relaton
48
48
  # plain join, without the wrap and sentence period (1.x
49
49
  # short-cite templates carried no wrap)
50
50
  def plain_render
51
- marker = editors? && creators.size > 1 ?
52
- "#{role_space}#{role_parenthesis}" : ''
51
+ marker = editors? && marked? ? "#{role_space}#{role_parenthesis}" : ''
53
52
  "#{join(names)}#{marker}"
54
53
  end
55
54
 
@@ -12,17 +12,34 @@ module Relaton
12
12
 
13
13
  def render
14
14
  groups = [numeration, page_range].reject { |g| g.empty? }
15
- groups.join(", ")
15
+ groups.join(group_sep)
16
16
  end
17
17
 
18
18
  private
19
19
 
20
+ # An extent label is a template over its value: "vol. %" in
21
+ # the Latin pack, "巻%" / "%頁" in the CJK packs
22
+ def unit(type, value)
23
+ label = @i18n.label(type)
24
+ return "#{label} #{value}" unless label.include?("%")
25
+
26
+ label.sub("%", value)
27
+ end
28
+
29
+ def cjk?(label)
30
+ label.match?(/\p{Han}|\p{Hiragana}|\p{Katakana}/)
31
+ end
32
+
33
+ def group_sep
34
+ cjk?(@i18n.label("page")) ? "、 " : ", "
35
+ end
36
+
20
37
  def numeration
21
38
  %w[volume issue].filter_map do |type|
22
39
  loc = localities.find { |l| l.type == type }
23
40
  next if loc.nil? || loc.reference_from.to_s.empty?
24
41
 
25
- "#{@i18n.label(type)} #{loc.reference_from}"
42
+ unit(type, loc.reference_from)
26
43
  end.join(" ")
27
44
  end
28
45
 
@@ -31,10 +48,10 @@ module Relaton
31
48
  from = loc.reference_from.to_s
32
49
  to = loc.reference_to.to_s
33
50
  if to.empty? || to == from
34
- "#{@i18n.label('page')} #{from}"
51
+ unit("page", from)
35
52
  else
36
- "#{@i18n.label('pages')} " \
37
- "#{from}#{@i18n.label('date_range')}#{to}"
53
+ range = "#{from}#{@i18n.label('date_range')}#{to}"
54
+ unit("pages", range)
38
55
  end
39
56
  end
40
57
 
@@ -57,7 +57,8 @@ module Relaton
57
57
  return "" if label.empty? || label == "no_place"
58
58
 
59
59
  pubs = publishers.join("; ")
60
- pubs.empty? ? "#{label}:" : "#{label}: #{pubs}"
60
+ pubs.empty? ? "#{label}#{production_sep}".rstrip :
61
+ "#{label}#{production_sep}#{pubs}"
61
62
  end
62
63
 
63
64
  def no_place_declared?
@@ -25,7 +25,11 @@ module Relaton
25
25
  if number.empty?
26
26
  base
27
27
  else
28
- "#{base}, #{@i18n.label('series_no')} #{number}"
28
+ # an empty series_no label joins on the space alone (the
29
+ # CJK form: "Series 472")
30
+ label = @i18n.label("series_no")
31
+ label.empty? ? "#{base} #{number}" :
32
+ "#{base}, #{label} #{number}"
29
33
  end
30
34
  end
31
35
 
@@ -13,10 +13,11 @@ labels:
13
13
  ordinal_word_10: "Tenth"
14
14
  size_volume: "vol."
15
15
  size_page: "pp."
16
- page: "p."
17
- pages: "pp."
18
- volume: "vol."
19
- issue: "no."
16
+ page: "p. %"
17
+ pages: "pp. %"
18
+ volume: "vol. %"
19
+ issue: "no. %"
20
+ no_place: "n.p."
20
21
  version: "Version"
21
22
  medium_website: "[website]"
22
23
  viewed: "viewed"
@@ -3,10 +3,10 @@ labels:
3
3
  edition: "版"
4
4
  size_volume: "巻"
5
5
  size_page: "頁"
6
- page: "頁"
7
- pages: "頁"
8
- volume: "巻"
9
- issue: "号"
6
+ page: "%頁"
7
+ pages: "%頁"
8
+ volume: "巻%"
9
+ issue: "%号"
10
10
  version: "版"
11
11
  viewed: "閲覧"
12
12
  series_no: "番号"
@@ -25,7 +25,7 @@ labels:
25
25
  list_separator: "、 "
26
26
  and: ""
27
27
  oxford_comma: ""
28
- date_range: "–"
28
+ date_range: "〜"
29
29
  punct:
30
30
  size-join: ""
31
31
  marker-space: ""
@@ -64,7 +64,8 @@ module Relaton
64
64
  # The short cite: the reference with the first-biblio marker
65
65
  def render_short(model, delim)
66
66
  kind = @style.kind_for(model.type)
67
- template = @style.type_template_for(kind)&.short ||
67
+ template = @style.short_template_for(kind,
68
+ home: home_docid?(model)) ||
68
69
  @style.template_for(kind, home: home_docid?(model))
69
70
  Template.new(template).evaluate_short(
70
71
  Fields.new(model, style: @style, i18n: @i18n, short: true,
@@ -93,12 +94,12 @@ module Relaton
93
94
  end
94
95
 
95
96
  # The bracketed author-date in-text capsule ("Aluffi (2022)"):
96
- # the author and the date in the locale's parentheses
97
- def bracketed_date_citation(model, disambiguator: nil)
97
+ # the author and the PLAIN date (un-disambiguated, the 1.x
98
+ # citetemplate's {{ date }}) in the locale's parentheses
99
+ def bracketed_date_citation(model, **)
98
100
  f = Fields.new(model, style: @style, i18n: @i18n,
99
- disambiguator: disambiguator,
100
101
  elements: @elements).to_h
101
- date = f["disambiguateddate"].text
102
+ date = f["date"].text
102
103
  return "" if date.empty?
103
104
 
104
105
  open = @i18n.punct_fetch("open-paren", "(")
@@ -114,6 +115,10 @@ module Relaton
114
115
  elements: @elements).to_h["disambiguateddate"].text
115
116
  end
116
117
 
118
+ def short_from_reference?
119
+ @style.scheme.short_from_reference
120
+ end
121
+
117
122
  # The title citation ("{{ title }}" in the 1.x citetemplate):
118
123
  # the title element's plain form, no style markers
119
124
  def title_citation(model)
@@ -13,12 +13,18 @@ module Relaton
13
13
  attribute :home_docid_type, :string, collection: true
14
14
  attribute :default_kind, :string, default: ""
15
15
 
16
- key_value do
16
+ # The renderings' short cite takes the reference form with the
17
+ # first-biblio marker (the 1.x citetemplate short), not the
18
+ # citation template
19
+ attribute :short_from_reference, :boolean, default: -> { false }
20
+
21
+ key_value do
17
22
  map "system", to: :system
18
23
  map "nameForm", to: :name_form
19
24
  map "locale", to: :locale
20
25
  map "homeDocidType", to: :home_docid_type
21
26
  map "defaultKind", to: :default_kind
27
+ map "shortFromReference", to: :short_from_reference
22
28
  end
23
29
  end
24
30
  end
@@ -66,6 +66,17 @@ module Relaton
66
66
  per_type.find { |t| t.type == type.to_s }
67
67
  end
68
68
 
69
+ # The home split applies to the short cite as it does to the
70
+ # reference template (a home standard's short cite is not an
71
+ # external standard's)
72
+ def short_template_for(type, home: nil)
73
+ candidates = per_type.select { |t| t.type == type.to_s }
74
+ if candidates.size > 1 && !home.nil?
75
+ candidates = candidates.select { |t| (t.home || false) == home }
76
+ end
77
+ candidates.filter_map(&:short).first
78
+ end
79
+
69
80
  # The absent-slot replacement text declared for a type, when any
70
81
  def fallback_for(type, slot)
71
82
  type_template_for(type)&.fallbacks&.[](slot.to_s)
@@ -30,8 +30,18 @@ module Relaton
30
30
  present = present_indices(fields)
31
31
  return "" if present.empty?
32
32
 
33
+ # The split marker follows the leading run of present slots
34
+ # (the 1.x first SEGMENT: "{{creator}}{{role}}" splits after
35
+ # the role), before the first literal that follows it
36
+ lead_slots = []
37
+ @chunks.each_with_index do |(kind, _value), i|
38
+ break if kind != :slot || !present.include?(i)
39
+
40
+ lead_slots << i
41
+ end
42
+ split_at = lead_slots.last ? lead_slots.last + 1 : nil
43
+
33
44
  out = +""
34
- first_seen = false
35
45
  @chunks.each_with_index do |(kind, value), i|
36
46
  text =
37
47
  case kind
@@ -39,18 +49,20 @@ module Relaton
39
49
  field = fields[normalise(value)]
40
50
  next unless field&.present?
41
51
 
42
- first_seen ? field.text : "#{field.text}#{delim} "
52
+ field.text
43
53
  else
44
- # the split marker precedes the component's trailing
45
- # punctuation (1.x ret[0] += delim, then join), with
46
- # the join's separating space
47
54
  next unless bridged?(i, present)
48
55
 
49
56
  value
50
57
  end
51
- first_seen = true if @chunks[i].first == :slot
58
+ if split_at == i
59
+ out << fieldless(lead_slots, fields) if false
60
+ out << delim.to_s
61
+ split_at = nil
62
+ end
52
63
  out << text unless text.nil?
53
64
  end
65
+ out << delim.to_s if split_at == @chunks.length
54
66
  terminate(out).rstrip
55
67
  end
56
68
 
@@ -64,10 +76,44 @@ module Relaton
64
76
  return "" if present.empty?
65
77
 
66
78
  out = +"".dup
79
+ # Parenthetical groups balance on the template's literal
80
+ # parens alone: data text (a series run "(N.S.)", a host
81
+ # creator's closing paren) carries parens of its own that
82
+ # are no part of the template's group structure
83
+ paren_balance = 0
67
84
  @chunks.each_with_index do |(kind, value), i|
68
85
  if kind == :slot
69
86
  field = fields[normalise(value)]
70
87
  out << field.text if field&.present?
88
+ elsif value.to_s.match?(/\A[))]/)
89
+ # Symmetric close: with no pending open the close-paren
90
+ # drops, and any trailing punctuation bridges normally
91
+ closes = value.to_s[/\A[))]+/]
92
+ emitted = [paren_balance, closes.size].min
93
+ if emitted.positive?
94
+ out << closes[0, emitted] +
95
+ value.to_s[closes.size..].to_s
96
+ paren_balance -= emitted
97
+ else
98
+ rest = value.to_s.sub(/\A[))]+/, "")
99
+ # the trailing separator of a dropped group bridges as
100
+ # though the group were never there
101
+ if !rest.empty? && present.any? { |j| j > i } &&
102
+ group_gap?(i, present)
103
+ out << rest
104
+ end
105
+ end
106
+ elsif value.to_s.match?(/[((]\z/)
107
+ # A literal opening a parenthetical group ("({{series}})",
108
+ # "。({{series}})") attaches its paren to the slot it
109
+ # opens — the pair drops with an absent element, while
110
+ # any leading punctuation bridges normally
111
+ lead, paren = value.to_s.match(/\A(.*)([((])\z/).captures
112
+ out << lead if bridged_text?(i, lead, present)
113
+ if present.include?(i + 1)
114
+ out << paren
115
+ paren_balance += 1
116
+ end
71
117
  elsif bridged?(i, present) ||
72
118
  (value.include?("<") && present.any? { |j| j < i })
73
119
  # markup attaches to the element it closes
@@ -102,6 +148,30 @@ module Relaton
102
148
  end
103
149
  end
104
150
 
151
+ # The texts between this close and the last present slot are
152
+ # all parenthetical-group parts (each dropped or partial)
153
+ def group_gap?(index, present)
154
+ left = present.select { |i| i < index }.max
155
+ @chunks[(left + 1)...index].all? do |(_, text)|
156
+ text.to_s.match?(/\A[))]|[((]\z/)
157
+ end
158
+ end
159
+
160
+ # The leading punctuation of a paren-opening literal bridges
161
+ # per the segment-join rule (a bare paren has no lead)
162
+ def bridged_text?(index, lead, present)
163
+ return true if lead.empty?
164
+
165
+ first_slot = @chunks.index { |(kind, _)| kind == :slot }
166
+ return present.include?(first_slot) if index < first_slot
167
+
168
+ left = present.select { |i| i < index }.max
169
+ left && present.any? { |i| i > index } &&
170
+ !@chunks[(left + 1)...index].any? do |(kind, _)|
171
+ kind == :text
172
+ end
173
+ end
174
+
105
175
  # A literal is the trailing separator of the element that
106
176
  # precedes it: it renders when that element renders and a
107
177
  # later element renders, and it is the FIRST literal of its
@@ -1,5 +1,5 @@
1
1
  module Relaton
2
2
  module Render
3
- VERSION = "3.0.0.pre.alpha.22".freeze
3
+ VERSION = "3.0.0.pre.alpha.23".freeze
4
4
  end
5
5
  end
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.22
4
+ version: 3.0.0.pre.alpha.23
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.