relaton-render 3.0.0.pre.alpha.11 → 3.0.0.pre.alpha.12
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 +4 -4
- data/lib/relaton/render/general.rb +4 -2
- data/lib/relaton/render/iso690/element.rb +1 -1
- data/lib/relaton/render/iso690/elements/pub_date.rb +0 -2
- data/lib/relaton/render/iso690/fields.rb +1 -1
- data/lib/relaton/render/iso690/kinds.rb +6 -0
- data/lib/relaton/render/iso690/renderer.rb +2 -2
- data/lib/relaton/render/iso690/style/scheme.rb +2 -0
- data/lib/relaton/render/iso690/style/template_map.rb +0 -2
- data/lib/relaton/render/iso690/style.rb +10 -0
- data/lib/relaton/render/iso690/template.rb +103 -51
- data/lib/relaton/render/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6940d659b738ee1c15dc46b7ee7be2bfb3f3bf322fd6cb8ae5fe70e19a2ab7ce
|
|
4
|
+
data.tar.gz: 060007b9cd392e9bf4b84cac3961aa8f0881849e74195c4ca6eb3f5f6dcf64f6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4a989f5e702e6e27afbad1fab8dc59c7bd092355eab5dbfb1cedf0b99c2373e71c99c3ed418e033f6638f00addcf68d983006213206a4200b7ef6e45c9907682
|
|
7
|
+
data.tar.gz: 56ac7f18bd13de8324a8ac118d68c7c9d98b6b3eb49b455a32cbca194802097b2e5123e725427ff84d16fd8fe1bd4fe952de7e90c610bf27e39cd94d105a76c2
|
|
@@ -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
|
-
|
|
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}."
|
|
@@ -35,7 +35,7 @@ module Relaton
|
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
def render(model)
|
|
38
|
-
kind =
|
|
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 =
|
|
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
|
-
@
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
next unless field.present?
|
|
30
|
+
present = present_indices(fields)
|
|
31
|
+
return "" if present.empty?
|
|
33
32
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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(
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
next unless field.present?
|
|
63
|
+
present = present_indices(fields)
|
|
64
|
+
return "" if present.empty?
|
|
57
65
|
|
|
58
|
-
|
|
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
|
-
|
|
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
|
-
|
|
73
|
-
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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:
|
|
89
|
-
#
|
|
90
|
-
#
|
|
91
|
-
#
|
|
92
|
-
#
|
|
93
|
-
#
|
|
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(
|
|
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
|
-
|
|
100
|
-
return body if
|
|
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
|
-
|
|
154
|
+
terminator.sub(/\A\)+/, "")
|
|
103
155
|
end
|
|
104
156
|
|
|
105
157
|
def normalise(slot)
|