relaton-render 3.0.0.pre.alpha.11 → 3.0.0.pre.alpha.13

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: c0b28a1996c4db280d4b0f1f976c91c2dc99926b60839df65e76407df0a491f2
4
- data.tar.gz: 675de3d7b9821c9838a63730bff1d49f39e6595213d2b9476c6875ed5b76f024
3
+ metadata.gz: 3e80ae8de229852d85ba4eabcd093152e140c9252f07c301fb6091c03694eac8
4
+ data.tar.gz: c378a86333710cabdcd00fd085a43e74a924c47e0b5df3d37eb830b56697ecf8
5
5
  SHA512:
6
- metadata.gz: 8a6d75a746549935b322e7437692938d3a126692de6e074053f1768b97b7a4da3c6ce33e8fb5b182f89f92b388c5bcb54bda1d18a38e429c533e72f0cc03b92b
7
- data.tar.gz: 0cbb4c77dc482860c76834d3dff1f418ab8726dfd84477e5787d06c9b11df4dff139e22019cf2d6b525d980ae6d907207075d315143afc8f472d2828df634116
6
+ metadata.gz: 51b3ee0d7f8486e221ad0ea3fc0a69b8ae43a8f18056aa7357e77a362ea9c414c71ae724f241c8c12cc76ee29d34a906788a0b1abf82e1bf5a740d1c8dd5cada
7
+ data.tar.gz: 5d99388b322ae77613713554f9d6a0a7effb284a31267dda00427fbedf122a1c8d3abc990eceef6de27350b3394b6c710d433311bb025614c7c9db11adfb7e5d
@@ -63,7 +63,7 @@ module Relaton
63
63
  end
64
64
  m[item.id] = {
65
65
  id: item.id, ord: i,
66
- formattedref: terminate_reference(ref),
66
+ formattedref: terminate_reference(ref, item),
67
67
  citation: citation_renderings(item, ref),
68
68
  }
69
69
  end
@@ -82,7 +82,9 @@ module Relaton
82
82
  # render_all feeds the bibliography list: a reference not ending
83
83
  # in the biblio terminator takes one (the 1.x render1 behaviour;
84
84
  # single-item render stays verbatim)
85
- def terminate_reference(ref)
85
+ # The bibliography terminator; flavors override to suppress it
86
+ # (1.x use_terminator?), receiving the item it terminates
87
+ def terminate_reference(ref, _item = nil)
86
88
  return ref if ref.empty? || ref.rstrip.end_with?(".")
87
89
 
88
90
  "#{ref}."
@@ -62,7 +62,7 @@ module Relaton
62
62
  end
63
63
 
64
64
  def item_kind
65
- @kind || Kinds.kind_for(@model.type)
65
+ @kind || @style.kind_for(@model.type)
66
66
  end
67
67
 
68
68
  def person_completename(person)
@@ -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
@@ -13,8 +13,6 @@ module Relaton
13
13
  !render.nil?
14
14
  end
15
15
 
16
- # The bare date; the style's dateForm wraps it in the
17
- # dategroup slot (Fields#dategroup_field)
18
16
  def render
19
17
  range || year&.to_s
20
18
  end
@@ -21,6 +21,7 @@ module Relaton
21
21
  autoload :Access, "relaton/render/iso690/elements/access"
22
22
  autoload :Stddoc, "relaton/render/iso690/elements/stddoc"
23
23
  autoload :Status, "relaton/render/iso690/elements/status"
24
+ autoload :Authorizer, "relaton/render/iso690/elements/authorizer"
24
25
  autoload :Citeid, "relaton/render/iso690/elements/citeid"
25
26
  autoload :Updated, "relaton/render/iso690/elements/updated"
26
27
 
@@ -41,6 +42,7 @@ module Relaton
41
42
  access: Access,
42
43
  stddoc: Stddoc,
43
44
  status: Status,
45
+ authorizer: Authorizer,
44
46
  citeid: Citeid,
45
47
  updated: Updated,
46
48
  }.freeze
@@ -11,7 +11,7 @@ module Relaton
11
11
  ELEMENT_SLOTS = %i[
12
12
  creator title edition medium series production
13
13
  date numeration component_part identifier location size
14
- extent access stddoc status citeid updated
14
+ extent access stddoc status citeid authorizer updated
15
15
  ].freeze
16
16
 
17
17
  def initialize(model, style:, i18n:, disambiguator: nil,
@@ -98,7 +98,7 @@ module Relaton
98
98
  end
99
99
 
100
100
  def item_kind
101
- Kinds.kind_for(@model.type)
101
+ @style.kind_for(@model.type)
102
102
  end
103
103
 
104
104
  def name_fields
@@ -38,6 +38,12 @@ module Relaton
38
38
 
39
39
  DEFAULT_KIND = "report"
40
40
 
41
+ class << self
42
+ def mapped?(type)
43
+ TO_KIND.key?(type.to_s)
44
+ end
45
+ end
46
+
41
47
  def self.kind_for(type)
42
48
  TO_KIND.fetch(type.to_s, DEFAULT_KIND)
43
49
  end
@@ -35,7 +35,7 @@ module Relaton
35
35
  end
36
36
 
37
37
  def render(model)
38
- kind = Kinds.kind_for(model.type)
38
+ kind = @style.kind_for(model.type)
39
39
  template = @style.template_for(kind, home: home_docid?(model))
40
40
  out = Template.new(template).evaluate(
41
41
  Fields.new(model, style: @style, i18n: @i18n,
@@ -62,7 +62,7 @@ module Relaton
62
62
 
63
63
  # The short cite: the reference with the first-biblio marker
64
64
  def render_short(model, delim)
65
- kind = Kinds.kind_for(model.type)
65
+ kind = @style.kind_for(model.type)
66
66
  template = @style.type_template_for(kind)&.short ||
67
67
  @style.template_for(kind, home: home_docid?(model))
68
68
  Template.new(template).evaluate_short(
@@ -11,12 +11,14 @@ module Relaton
11
11
  attribute :name_form, NameForm, default: -> { NameForm.new }
12
12
  attribute :locale, Locale, default: -> { Locale.new }
13
13
  attribute :home_docid_type, :string, collection: true
14
+ attribute :default_kind, :string, default: ""
14
15
 
15
16
  key_value do
16
17
  map "system", to: :system
17
18
  map "nameForm", to: :name_form
18
19
  map "locale", to: :locale
19
20
  map "homeDocidType", to: :home_docid_type
21
+ map "defaultKind", to: :default_kind
20
22
  end
21
23
  end
22
24
  end
@@ -17,7 +17,6 @@ module Relaton
17
17
  attribute :uri, :string, default: ""
18
18
  attribute :creators, :string, default: ""
19
19
  attribute :uri_short, :string, default: ""
20
- attribute :date_form, :string, default: ""
21
20
  # "first": cite only the first identifier (1.x
22
21
  # authoritative_identifier|first styles)
23
22
  attribute :identifier_mode, :string, default: "all"
@@ -32,7 +31,6 @@ module Relaton
32
31
  map "uri", to: :uri
33
32
  map "creators", to: :creators
34
33
  map "uriShort", to: :uri_short
35
- map "dateForm", to: :date_form
36
34
  map "identifierMode", to: :identifier_mode
37
35
  end
38
36
  end
@@ -40,6 +40,16 @@ module Relaton
40
40
  [SortRule.new(attribute: "creator"), SortRule.new(attribute: "date")]
41
41
  end
42
42
 
43
+ # The ISO 690 kind of an item: the vocabulary's mapping, or
44
+ # the style's declared kind for types the vocabulary does not
45
+ # map (a flavor whose untyped items are not reports)
46
+ def kind_for(type)
47
+ return Kinds.kind_for(type) if Kinds.mapped?(type)
48
+
49
+ declared = scheme.default_kind.to_s
50
+ declared.empty? ? Kinds.kind_for(type) : declared
51
+ end
52
+
43
53
  # Per-type template selection is a data lookup; unmatched types
44
54
  # fall back to the general reference template. When a kind has
45
55
  # home-flagged variants, the split is resolved by whether the item
@@ -17,7 +17,7 @@ module Relaton
17
17
  Field = Struct.new(:present?, :text)
18
18
 
19
19
  def initialize(source)
20
- @slots = []
20
+ @chunks = []
21
21
  @terminator = ""
22
22
  scan(source.to_s)
23
23
  end
@@ -27,41 +27,56 @@ module Relaton
27
27
  # references (eref2linkshort) split there (the 1.x
28
28
  # fmt-first-biblio-delim)
29
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?
30
+ present = present_indices(fields)
31
+ return "" if present.empty?
33
32
 
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
33
+ out = +""
34
+ first_seen = false
35
+ @chunks.each_with_index do |(kind, value), i|
36
+ text =
37
+ case kind
38
+ when :slot
39
+ field = fields[normalise(value)]
40
+ next unless field&.present?
41
+
42
+ first_seen ? field.text : "#{field.text}#{delim} "
43
+ 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
+ next unless bridged?(i, present)
48
+
49
+ value
50
+ end
51
+ first_seen = true if @chunks[i].first == :slot
52
+ out << text unless text.nil?
49
53
  end
50
- terminate(parts.join).rstrip
54
+ terminate(out).rstrip
51
55
  end
52
56
 
57
+ # 1.x segment-join semantics: present elements joined with the
58
+ # template's separators — a literal renders only when it
59
+ # separates (a run of absent elements between) two present
60
+ # elements; leading and trailing literals drop with the
61
+ # elements they would have attached to
53
62
  def evaluate(fields)
54
- rendered = @slots.filter_map do |(name, head, tail)|
55
- field = fields[normalise(name)] or next
56
- next unless field.present?
63
+ present = present_indices(fields)
64
+ return "" if present.empty?
57
65
 
58
- [head, field.text, tail]
66
+ out = +"".dup
67
+ @chunks.each_with_index do |(kind, value), i|
68
+ if kind == :slot
69
+ field = fields[normalise(value)]
70
+ out << field.text if field&.present?
71
+ elsif bridged?(i, present) ||
72
+ (value.include?("<") && present.any? { |j| j < i })
73
+ # markup attaches to the element it closes
74
+ out << value
75
+ elsif close_pending_paren?(out, value)
76
+ out << value
77
+ end
59
78
  end
60
- return "" if rendered.empty?
61
-
62
- terminate(rendered.each_with_index.map do |(head, text, tail), i|
63
- i == rendered.size - 1 ? "#{head}#{text}" : "#{head}#{text}#{tail}"
64
- end.join).rstrip
79
+ terminate(out).rstrip
65
80
  end
66
81
 
67
82
  private
@@ -69,37 +84,74 @@ module Relaton
69
84
  def scan(source)
70
85
  rest = source
71
86
  while (slot = SLOT.match(rest))
72
- head = @slots.empty? ? rest[0...slot.begin(0)] : ""
73
- @slots << [slot[1], head, ""]
87
+ @chunks << [:text, rest[0...slot.begin(0)]] if
88
+ slot.begin(0).positive?
89
+ @chunks << [:slot, slot[1]]
74
90
  rest = rest[slot.end(0)..]
75
- following = SLOT.match(rest)
76
- boundary = following ? following.begin(0) : rest.length
77
- literal = rest[0...boundary]
78
- if following
79
- @slots.last[2] = literal
80
- else
81
- @terminator = literal
82
- end
83
- rest = rest[boundary..]
91
+ end
92
+ @terminator = rest
93
+ end
94
+
95
+ def present_indices(fields)
96
+ @chunks.each_index.select do |i|
97
+ kind, value = @chunks[i]
98
+ next false unless kind == :slot
99
+
100
+ field = fields[normalise(value)]
101
+ !field.nil? && field.present?
84
102
  end
85
103
  end
86
104
 
105
+ # A literal bridges two present elements when at least one
106
+ # present element stands on each side of it; a leading literal
107
+ # (no slot precedes it) attaches to the first slot, emitting
108
+ # only when that slot is present
109
+ def bridged?(index, present)
110
+ first_slot = @chunks.index { |(kind, _)| kind == :slot }
111
+ return present.include?(first_slot) if index < first_slot
112
+
113
+ present.any? { |i| i < index } && present.any? { |i| i > index }
114
+ end
115
+
116
+ # A template's paired literal ("({{production}})") closes even
117
+ # when nothing follows the present element it wraps
118
+ def close_pending_paren?(out, value)
119
+ value.start_with?(")") && out.count("(") > out.count(")")
120
+ end
121
+
87
122
  def terminate(body)
88
- # 1.x punctuation cleanup: an element ending in a colon
89
- # carries its own separator (the following sentence period
90
- # collapses); a sentence period followed by a spaced comma
91
- # or another period collapses into one (", 2013" over
92
- # ". , 2013"; "n.d.." into "n.d.") — an initials period
93
- # before a comma ("P.,") stands; spaces collapse before a
94
- # comma
123
+ # 1.x punctuation cleanup: a sentence period before a spaced
124
+ # comma yields to the comma (", 2013" over ". , 2013"; an
125
+ # initials period before a comma ("P.,") stands, being
126
+ # unspaced), and stacked separators around absent elements
127
+ # collapse into one (". : ." into ". "); an empty pair left
128
+ # by an absent element drops
95
129
  body = body.gsub(/:\s*\.\s*/, ": ")
96
130
  body = body.gsub(/\.\s+,/, ",")
97
- body = body.gsub(/\.\s*\./, ".")
131
+ body = body.gsub(/(?:[.,:;]\s+)+[.,:;]/, ". ")
132
+ body = body.gsub(/\.\s*\./, ". ")
133
+ body = body.gsub(/:\s*\.\s*/, ": ")
134
+ body = body.gsub(/\(\s*\)/, "")
98
135
  body = body.gsub(/ +/, " ").gsub(" ,", ",")
99
- return body if @terminator.strip.empty?
100
- return body if body.rstrip.end_with?(@terminator.strip)
136
+ terminator = unbalanced_close(body, @terminator)
137
+ return body if terminator.strip.empty?
138
+ if terminator !~ /[<\w]/ && body.rstrip.end_with?(terminator.strip)
139
+ return body
140
+ end
141
+
142
+ "#{body}#{terminator}"
143
+ end
144
+
145
+ # The terminator's closing parens stand only when the body
146
+ # opened them (their "(" died with an absent element)
147
+ def unbalanced_close(body, terminator)
148
+ return terminator unless terminator.start_with?(")")
149
+
150
+ open = body.count("(") - body.count(")")
151
+ return terminator.sub(/\A\)+/) { |closes| closes[0, open] } if
152
+ open.positive?
101
153
 
102
- "#{body}#{@terminator}"
154
+ terminator.sub(/\A\)+/, "")
103
155
  end
104
156
 
105
157
  def normalise(slot)
@@ -1,5 +1,5 @@
1
1
  module Relaton
2
2
  module Render
3
- VERSION = "3.0.0.pre.alpha.11".freeze
3
+ VERSION = "3.0.0.pre.alpha.13".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.11
4
+ version: 3.0.0.pre.alpha.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
@@ -121,6 +121,7 @@ files:
121
121
  - lib/relaton/render/iso690/element.rb
122
122
  - lib/relaton/render/iso690/elements.rb
123
123
  - lib/relaton/render/iso690/elements/access.rb
124
+ - lib/relaton/render/iso690/elements/authorizer.rb
124
125
  - lib/relaton/render/iso690/elements/citeid.rb
125
126
  - lib/relaton/render/iso690/elements/component_part.rb
126
127
  - lib/relaton/render/iso690/elements/creator.rb