relaton-render 3.0.0.pre.alpha.6 → 3.0.0.pre.alpha.8

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