citeproc 0.0.8 → 0.0.9
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.
- data/.document +5 -0
- data/.gitignore +5 -0
- data/.travis.yml +13 -0
- data/.yardopts +2 -0
- data/AGPL +662 -0
- data/{LICENSE → BSDL} +2 -1
- data/Gemfile +13 -2
- data/README.md +32 -1
- data/Rakefile +39 -0
- data/citeproc.gemspec +18 -11
- data/lib/citeproc.rb +13 -11
- data/lib/citeproc/abbreviate.rb +1 -4
- data/lib/citeproc/assets.rb +11 -4
- data/lib/citeproc/attributes.rb +102 -95
- data/lib/citeproc/bibliography.rb +66 -42
- data/lib/citeproc/citation_data.rb +57 -46
- data/lib/citeproc/compatibility.rb +161 -4
- data/lib/citeproc/date.rb +517 -225
- data/lib/citeproc/engine.rb +0 -2
- data/lib/citeproc/errors.rb +4 -4
- data/lib/citeproc/extensions.rb +6 -5
- data/lib/citeproc/item.rb +85 -22
- data/lib/citeproc/names.rb +642 -543
- data/lib/citeproc/variable.rb +149 -70
- data/lib/citeproc/version.rb +1 -1
- data/spec/citeproc/assets_spec.rb +10 -4
- data/spec/citeproc/date_spec.rb +274 -8
- data/spec/citeproc/item_spec.rb +23 -4
- data/spec/citeproc/names_spec.rb +601 -486
- data/spec/citeproc/variable_spec.rb +4 -12
- data/spec/spec_helper.rb +13 -0
- metadata +64 -31
data/lib/citeproc/engine.rb
CHANGED
data/lib/citeproc/errors.rb
CHANGED
@@ -9,10 +9,10 @@ module CiteProc
|
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
ParseError = Class.new(Error)
|
13
|
+
|
14
|
+
EngineError = Class.new(Error)
|
15
15
|
|
16
|
-
|
16
|
+
NotImplementedByEngine = Class.new(Error)
|
17
17
|
|
18
18
|
end
|
data/lib/citeproc/extensions.rb
CHANGED
@@ -14,12 +14,10 @@ module CiteProc
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
-
module DeepCopy
|
17
|
+
module DeepCopy
|
18
|
+
# See Matz, Flanagan: 'The Ruby Programming Language', p.83
|
18
19
|
def deep_copy
|
19
|
-
|
20
|
-
begin k.respond_to?(:deep_copy) ? k.deep_copy : k.dup rescue k end,
|
21
|
-
begin v.respond_to?(:deep_copy) ? v.deep_copy : v.dup rescue v end
|
22
|
-
]}.flatten(1)]
|
20
|
+
Marshal.load(Marshal.dump(self))
|
23
21
|
end
|
24
22
|
end
|
25
23
|
|
@@ -116,6 +114,9 @@ end
|
|
116
114
|
class Array
|
117
115
|
include CiteProc::Extensions::CompactJoin
|
118
116
|
# include CiteProc::Extensions::ToSentence unless method_defined?(:to_sentence)
|
117
|
+
|
118
|
+
warn "citeproc: re-defining Array#deep_copy, this may cause conflicts with other libraries" if method_defined?(:deep_copy)
|
119
|
+
include CiteProc::Extensions::DeepCopy
|
119
120
|
end
|
120
121
|
|
121
122
|
class String
|
data/lib/citeproc/item.rb
CHANGED
@@ -1,27 +1,28 @@
|
|
1
1
|
module CiteProc
|
2
2
|
|
3
|
-
|
4
3
|
# Items are similar to a Ruby Hash but pose a number of constraints on their
|
5
4
|
# contents: keys are always (implicitly converted to) symbols and values
|
6
|
-
# are strictly
|
7
|
-
# from (or merged with) JSON objects or Hashes Variable instances are
|
8
|
-
# automatically created
|
9
|
-
# Variable.create
|
5
|
+
# are strictly {Variable Variables}. When Items are constructed
|
6
|
+
# from (or merged with) JSON objects or Hashes {Variable} instances are
|
7
|
+
# automatically created by passing the variable's key as type to
|
8
|
+
# {Variable.create}; this will create the expected {Variable} type for all
|
10
9
|
# fields defined in CSL (for example, the `issued' field will become a
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
10
|
+
# {Date} object; unknown types will be converted to simple {Variable}
|
11
|
+
# instances, which should be fine for numeric or string values but may
|
12
|
+
# cause problems for more complex types.
|
14
13
|
#
|
15
14
|
# Every Item provides accessor methods for all known field names; unknown
|
16
15
|
# fields can still be accessed using array accessor syntax.
|
17
16
|
#
|
18
17
|
# i = Item.new(:edition => 3, :unknown_field => 42)
|
19
|
-
# i.edition
|
20
|
-
#
|
21
|
-
#
|
18
|
+
# i.edition
|
19
|
+
# #-> #<CiteProc::Number "3">
|
20
|
+
#
|
21
|
+
# i[:unknown_field]
|
22
|
+
# #-> #<CiteProc::Variable "42">
|
22
23
|
#
|
23
|
-
# Items can be converted to the CiteProc JSON format via #to_citeproc
|
24
|
-
# #to_json
|
24
|
+
# Items can be converted to the CiteProc JSON format via {#to_citeproc}
|
25
|
+
# and {#to_json}.
|
25
26
|
class Item
|
26
27
|
|
27
28
|
@types = [
|
@@ -53,34 +54,88 @@ module CiteProc
|
|
53
54
|
attr_reader :types, :bibtex_types
|
54
55
|
end
|
55
56
|
|
57
|
+
extend Forwardable
|
58
|
+
|
56
59
|
include Attributes
|
57
60
|
include Enumerable
|
58
|
-
|
61
|
+
include Comparable
|
62
|
+
include Observable
|
63
|
+
|
64
|
+
|
59
65
|
attr_predicates :id, :'short-title', :'journal-abbreviation',
|
60
66
|
*Variable.fields[:all]
|
67
|
+
|
68
|
+
def_delegators :attributes, :values_at, :keys, :values
|
69
|
+
|
70
|
+
alias fields keys
|
71
|
+
|
72
|
+
# Hide attributes reader
|
73
|
+
protected :attributes
|
74
|
+
|
61
75
|
|
62
76
|
def initialize(attributes = nil)
|
63
77
|
merge(attributes)
|
78
|
+
yield self if block_given?
|
64
79
|
end
|
65
80
|
|
66
81
|
def initialize_copy(other)
|
67
82
|
@attributes = other.attributes.deep_copy
|
68
83
|
end
|
69
84
|
|
70
|
-
#
|
71
|
-
|
72
|
-
|
85
|
+
# Calls a block once for each field in the item, passing the field's
|
86
|
+
# name-value pair as parameters.
|
87
|
+
#
|
88
|
+
# If not block is given, an enumerator is returned instead.
|
89
|
+
#
|
90
|
+
# item.each { |name, value| block }
|
91
|
+
# #-> item
|
92
|
+
#
|
93
|
+
# item.each
|
94
|
+
# #-> an enumerator
|
95
|
+
#
|
96
|
+
# @yieldparam field [Symbol] the field name
|
97
|
+
# @yieldparam value [Variable] the value
|
98
|
+
# @return [self,Enumerator] the item or an enumerator if no block is given
|
73
99
|
def each
|
74
100
|
if block_given?
|
75
|
-
attributes.
|
101
|
+
attributes.each_pair(&Proc.new)
|
76
102
|
self
|
77
103
|
else
|
78
104
|
to_enum
|
79
105
|
end
|
80
106
|
end
|
81
107
|
|
82
|
-
|
83
|
-
|
108
|
+
alias each_pair each
|
109
|
+
|
110
|
+
# Calls a block once for each field in the item, passing the field's
|
111
|
+
# value as parameters.
|
112
|
+
#
|
113
|
+
# If not block is given, an enumerator is returned instead.
|
114
|
+
#
|
115
|
+
# item.each_value { |value| block }
|
116
|
+
# #-> item
|
117
|
+
#
|
118
|
+
# item.each_value
|
119
|
+
# #-> an enumerator
|
120
|
+
#
|
121
|
+
# @yieldparam value [Variable] the value
|
122
|
+
# @return [self,Enumerator] the item or an enumerator if no block is given
|
123
|
+
def each_value
|
124
|
+
if block_given?
|
125
|
+
attributes.each_value(&Proc.new)
|
126
|
+
self
|
127
|
+
else
|
128
|
+
enum_for :each_value
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def <=>(other)
|
133
|
+
return nil unless other.is_a?(Attributes)
|
134
|
+
eql?(other) ? 0 : length <=> other.length
|
135
|
+
end
|
136
|
+
|
137
|
+
# Returns a corresponding *BibTeX::Entry* if the bibtex-ruby gem is
|
138
|
+
# installed; otherwise returns a BibTeX string.
|
84
139
|
def to_bibtex
|
85
140
|
# hash = to_hash
|
86
141
|
#
|
@@ -103,16 +158,24 @@ module CiteProc
|
|
103
158
|
raise 'not implemented yet'
|
104
159
|
end
|
105
160
|
|
161
|
+
# @return [Symbol,nil] the item's id
|
106
162
|
def to_sym
|
107
|
-
id
|
163
|
+
if id?
|
164
|
+
id.to_s.intern
|
165
|
+
else
|
166
|
+
nil
|
167
|
+
end
|
108
168
|
end
|
109
169
|
|
170
|
+
# @return [String] a string containing a human-readable
|
171
|
+
# representation of the item
|
110
172
|
def inspect
|
111
|
-
"#<CiteProc::Item id=#{id.inspect} attributes={#{attributes.length}}>"
|
173
|
+
"#<CiteProc::Item id=#{id.to_s.inspect} attributes={#{attributes.length}}>"
|
112
174
|
end
|
113
175
|
|
114
176
|
private
|
115
177
|
|
178
|
+
# @private
|
116
179
|
def filter_value(value, key)
|
117
180
|
Variable.create!(value, key)
|
118
181
|
end
|
data/lib/citeproc/names.rb
CHANGED
@@ -1,556 +1,655 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
1
2
|
module CiteProc
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
# Based on the regular expression in Frank G. Bennett's citeproc-js
|
45
|
-
# https://bitbucket.org/fbennett/citeproc-js/overview
|
46
|
-
ROMANESQUE =
|
47
|
-
/^[a-zA-Z\u0080-\u017f\u0400-\u052f\u0386-\u03fb\u1f00-\u1ffe\.,\s\u0027\u02bc\u2019-]*$/
|
3
|
+
|
4
|
+
# Names consist of several dependent parts of strings. Simple personal names
|
5
|
+
# are composed of family and given elements, containing respectively the
|
6
|
+
# family and given name of the individual.
|
7
|
+
#
|
8
|
+
# Name.new(:family => 'Doe', :given => 'Jane')
|
9
|
+
#
|
10
|
+
# Institutional and other names that should always be presented literally
|
11
|
+
# (such as "The Artist Formerly Known as Prince", "Banksy", or "Ramses IV")
|
12
|
+
# should be delivered as a single :literal element:
|
13
|
+
#
|
14
|
+
# Name.new(:literal => 'Banksy')
|
15
|
+
#
|
16
|
+
# Name particles, such as the "von" in "Alexander von Humboldt", can be
|
17
|
+
# delivered separately from the family and given name, as :dropping-particle
|
18
|
+
# and :non-dropping-particle elements.
|
19
|
+
#
|
20
|
+
# Name suffixes such as the "Jr." in "Frank Bennett, Jr." and the "III" in
|
21
|
+
# "Horatio Ramses III" can be delivered as a suffix element.
|
22
|
+
#
|
23
|
+
# Name.new do |n|
|
24
|
+
# n.family, n.given, n.suffix = 'Ramses', 'Horatio', 'III'
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# Names not written in the Latin or Cyrillic scripts are always displayed
|
28
|
+
# with the family name first. Sometimes it might be desired to handle a
|
29
|
+
# Latin or Cyrillic transliteration as if it were a fixed (non-Byzantine)
|
30
|
+
# name (e.g., for Hungarian names). This behavior can be prompted by
|
31
|
+
# including activating static-ordering:
|
32
|
+
#
|
33
|
+
# Name.new(:family => 'Murakami', :given => 'Haruki').to_s
|
34
|
+
# #-> "Haruki Murakami"
|
35
|
+
#
|
36
|
+
# Name.new(:family => 'Murakami', :given => 'Haruki').static_order!.to_s
|
37
|
+
# #-> "Murakami Haruki"
|
38
|
+
class Name
|
39
|
+
|
40
|
+
extend Forwardable
|
41
|
+
|
42
|
+
include Attributes
|
43
|
+
include Comparable
|
48
44
|
|
49
45
|
# Class instance variables
|
50
46
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
# Aliases
|
88
|
-
[[:last, :family], [:first, :given], [:particle, :'non_dropping_particle']].each do |a, m|
|
47
|
+
@romanesque = CiteProc.oniguruma {
|
48
|
+
'^[\p{Latin}\p{Greek}\p{Cyrillic}\p{Hebrew}\p{Armenian}\p{Georgian}\p{Common}]*$'
|
49
|
+
} || CiteProc.ruby_18 {
|
50
|
+
# @todo improve fallback range
|
51
|
+
/^[a-zA-Zäöüéè\s[:punct:]]*$/u
|
52
|
+
}
|
53
|
+
|
54
|
+
# Default formatting options
|
55
|
+
@defaults = {
|
56
|
+
:form => 'long',
|
57
|
+
:'name-as-sort-order' => false,
|
58
|
+
:'demote-non-dropping-particle' => :never,
|
59
|
+
:'sort-separator' => ', ',
|
60
|
+
:'initialize-with' => nil
|
61
|
+
}.freeze
|
62
|
+
|
63
|
+
@parts = [:family, :given,:literal, :suffix, :'dropping-particle',
|
64
|
+
:'non-dropping-particle'].freeze
|
65
|
+
|
66
|
+
class << self
|
67
|
+
attr_reader :defaults, :parts, :romanesque
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
# Method generators
|
73
|
+
|
74
|
+
# @!attribute [r] options
|
75
|
+
# @return the name's formatting options
|
76
|
+
attr_reader :options
|
77
|
+
|
78
|
+
attr_predicates :'comma-suffix', :'static-ordering', :multi, *@parts
|
79
|
+
|
80
|
+
# Aliases
|
81
|
+
[[:particle, :'non_dropping_particle']].each do |a, m|
|
89
82
|
alias_method(a, m) if method_defined?(m)
|
90
83
|
|
91
|
-
|
84
|
+
wa, wm = "#{a}=", "#{m}="
|
92
85
|
alias_method(wa, wm) if method_defined?(wm)
|
93
86
|
|
94
|
-
|
87
|
+
pa, pm = "#{a}?", "#{m}?"
|
95
88
|
alias_method(pa, pm) if method_defined?(pm)
|
96
89
|
|
97
|
-
|
90
|
+
pa, pm = "has_#{a}?", "has_#{m}?"
|
98
91
|
alias_method(pa, pm) if method_defined?(pm)
|
99
92
|
end
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
93
|
+
|
94
|
+
# Names quack sorta like a String
|
95
|
+
def_delegators :to_s, :=~, :===,
|
96
|
+
*String.instance_methods(false).reject { |m| m =~ /^\W|!$|to_s|replace|first|last/ }
|
97
|
+
|
98
|
+
# Delegate bang! methods to each field's value
|
99
|
+
String.instance_methods(false).each do |m|
|
100
|
+
if m.to_s.end_with?('!')
|
101
|
+
define_method(m) do |*arguments, &block|
|
102
|
+
Name.parts.each do |part|
|
103
|
+
p = attributes[part]
|
104
|
+
p.send(m, *arguments, &block) if p.respond_to?(m)
|
105
|
+
end
|
106
|
+
self
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
# Instance methods
|
113
|
+
|
114
|
+
def initialize(attributes = {}, options = {})
|
115
|
+
@options = Name.defaults.merge(options)
|
116
|
+
@sort_prefix = (/^(the|an?|der|die|das|eine?|l[ae])\s+|^l\W/i).freeze
|
117
|
+
|
118
|
+
merge(attributes)
|
119
|
+
|
120
|
+
yield self if block_given?
|
121
|
+
end
|
122
|
+
|
123
|
+
def initialize_copy(other)
|
124
|
+
@attributes = other.attributes.deep_copy
|
125
|
+
@options = other.options.dup
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
# @return [Boolean] whether or not the Name looks like it belongs to a person
|
130
|
+
def personal?
|
131
|
+
!empty? && !literal?
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
# A name is `romanesque' if it contains only romanesque characters. This
|
136
|
+
# should be the case for the majority of names written in latin- or
|
137
|
+
# greek-based script. It will be false, for example, for names written
|
138
|
+
# in Chinese, Japanese, Arabic or Hebrew.
|
139
|
+
#
|
140
|
+
# @return [Boolean] whether or not the name is romanesque
|
141
|
+
def romanesque?
|
142
|
+
!!([given, family].join.gsub(Variable.markup, '') =~ Name.romanesque)
|
143
|
+
end
|
144
|
+
|
145
|
+
alias byzantine? romanesque?
|
146
|
+
|
147
|
+
# @return [Boolean] whether or not the name should be printed in static order
|
148
|
+
def static_order?
|
149
|
+
static_ordering? || !romanesque?
|
150
|
+
end
|
151
|
+
|
152
|
+
# Set the name to use static order for printing, i.e., print the family
|
153
|
+
# name before the given name as is customary, for example, in Hungarian
|
154
|
+
# and many Asian languages.
|
155
|
+
#
|
156
|
+
# @return [self]
|
157
|
+
def static_order!
|
158
|
+
self.static_ordering = true
|
159
|
+
self
|
160
|
+
end
|
161
|
+
|
162
|
+
alias static_order static_ordering
|
163
|
+
alias static_order= static_ordering=
|
164
|
+
|
165
|
+
# @return [Boolean] whether or not the name will be printed in sort-order
|
166
|
+
def sort_order?
|
167
|
+
!!(options[:'name-as-sort-order'].to_s =~ /^(y(es)?|always|t(rue)?)$/i)
|
168
|
+
end
|
169
|
+
|
170
|
+
def display_order?
|
171
|
+
!sort_order?
|
172
|
+
end
|
173
|
+
|
174
|
+
# Sets the name to use sort-order. The reverse of {#display_order!}.
|
175
|
+
# @return [self]
|
176
|
+
def sort_order!
|
177
|
+
options[:'name-as-sort-order'] = true
|
178
|
+
self
|
179
|
+
end
|
180
|
+
|
181
|
+
# Sets the name to use display-order. The reverse of {#sort_order!}.
|
182
|
+
# @return [self]
|
183
|
+
def display_order!
|
184
|
+
options[:'name-as-sort-order'] = false
|
185
|
+
self
|
186
|
+
end
|
187
|
+
|
188
|
+
# @return [String] the current sort separator
|
189
|
+
def sort_separator
|
190
|
+
options[:'sort-separator']
|
191
|
+
end
|
192
|
+
|
193
|
+
alias comma sort_separator
|
194
|
+
|
195
|
+
# @return [Boolean] whether or not the short form will be used for printing
|
196
|
+
def short_form?
|
197
|
+
options[:form].to_s =~ /short/i
|
198
|
+
end
|
199
|
+
|
200
|
+
# Use short form for printing the name
|
201
|
+
# @return [self]
|
202
|
+
def short_form!
|
203
|
+
options[:form] = :short
|
204
|
+
self
|
205
|
+
end
|
206
|
+
|
207
|
+
# @return [Boolean] whether or not the long form will be used for printing
|
208
|
+
def long_form?
|
209
|
+
!short_form?
|
210
|
+
end
|
211
|
+
|
212
|
+
# Use long form for printing the name
|
213
|
+
# @return [self]
|
214
|
+
def long_form!
|
215
|
+
options[:form] = :long
|
216
|
+
self
|
217
|
+
end
|
218
|
+
|
219
|
+
# @return [Boolean] whether or not initials will be used for printing
|
220
|
+
def initials?
|
221
|
+
!!options[:'initialize-with'] && personal? && romanesque?
|
222
|
+
end
|
223
|
+
|
224
|
+
def demote_non_dropping_particle?
|
225
|
+
always_demote_non_dropping_particle? ||
|
226
|
+
!!(sort_order? && options[:'demote-non-dropping-particle'] =~ /^sort(-only)?$/i)
|
227
|
+
end
|
228
|
+
|
229
|
+
alias demote_particle? demote_non_dropping_particle?
|
230
|
+
|
231
|
+
def never_demote_non_dropping_particle?
|
232
|
+
!!(options[:'demote-non-dropping-particle'] =~ /^never$/i)
|
233
|
+
end
|
234
|
+
|
235
|
+
def never_demote_non_dropping_particle!
|
236
|
+
options[:'demote-non-dropping-particle'] = 'never'
|
237
|
+
self
|
238
|
+
end
|
239
|
+
|
240
|
+
alias never_demote_particle? never_demote_non_dropping_particle?
|
241
|
+
alias never_demote_particle! never_demote_non_dropping_particle!
|
242
|
+
|
243
|
+
def always_demote_non_dropping_particle?
|
244
|
+
!!(options[:'demote-non-dropping-particle'] =~ /^(display-and-sort|always)$/i)
|
245
|
+
end
|
246
|
+
|
247
|
+
def always_demote_non_dropping_particle!
|
248
|
+
options[:'demote-non-dropping-particle'] = 'display-and-sort'
|
249
|
+
self
|
250
|
+
end
|
251
|
+
|
252
|
+
alias always_demote_particle? always_demote_non_dropping_particle?
|
253
|
+
alias always_demote_particle! always_demote_non_dropping_particle!
|
254
|
+
|
255
|
+
alias demote_particle! always_demote_non_dropping_particle!
|
256
|
+
|
257
|
+
# Compares two names. The comparison is based on #sort_order_downcase
|
258
|
+
#
|
259
|
+
# @see #sort_order_downcase
|
260
|
+
#
|
261
|
+
# @param other [#sort_order_downcase] the other name
|
262
|
+
# @return [Fixnum,nil] -1, 0, or 1 depending on the result of the
|
263
|
+
# comparison; nil if the name cannot be compared to the passed-in object
|
264
|
+
def <=>(other)
|
265
|
+
return nil unless other.respond_to?(:sort_order_downcase)
|
266
|
+
sort_order_downcase <=> other.sort_order_downcase
|
267
|
+
end
|
268
|
+
|
269
|
+
# @return [String] the name formatted according to the current options
|
270
|
+
def to_s
|
271
|
+
case
|
272
|
+
when literal?
|
273
|
+
literal.to_s
|
274
|
+
when static_order?
|
275
|
+
[family, given].compact.join(' ')
|
276
|
+
when !short_form?
|
277
|
+
case
|
278
|
+
when !sort_order?
|
279
|
+
[[given, dropping_particle, particle, family].compact_join(' '),
|
280
|
+
suffix].compact_join(comma_suffix? ? comma : ' ')
|
281
|
+
|
282
|
+
when !demote_particle?
|
283
|
+
[[particle, family].compact_join(' '), [given,
|
284
|
+
dropping_particle].compact_join(' '), suffix].compact_join(comma)
|
285
|
+
|
286
|
+
else
|
287
|
+
[family, [given, dropping_particle, particle].compact_join(' '),
|
288
|
+
suffix].compact_join(comma)
|
289
|
+
end
|
290
|
+
else
|
291
|
+
[particle, family].compact_join(' ')
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
# @return [Array<String>] an ordered array of formatted name parts to be used for sorting
|
296
|
+
def sort_order
|
297
|
+
case
|
298
|
+
when literal?
|
299
|
+
[literal.to_s.sub(sort_prefix, '')]
|
300
|
+
when never_demote_particle?
|
301
|
+
[[particle, family].compact_join(' '), dropping_particle, given, suffix].map(&:to_s)
|
302
|
+
else
|
303
|
+
[family, [particle, dropping_particle].compact_join(' '), given, suffix].map(&:to_s)
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
# @return [String] the name as a string stripped off all markup
|
308
|
+
def strip_markup
|
309
|
+
gsub(Variable.markup, '')
|
310
|
+
end
|
311
|
+
|
312
|
+
# @return [self] the name with all parts stripped off markup
|
313
|
+
def strip_markup!
|
314
|
+
gsub!(Variable.markup, '')
|
315
|
+
end
|
316
|
+
|
317
|
+
# @return [Array<String>] the sort order array stripped off markup and downcased
|
318
|
+
def sort_order_downcase
|
319
|
+
sort_order.map { |s| s.downcase.gsub(Variable.markup, '') }
|
320
|
+
end
|
321
|
+
|
322
|
+
# @return [String] a human-readable representation of the name object
|
323
|
+
def inspect
|
324
|
+
"#<CiteProc::Name #{to_s.inspect}>"
|
325
|
+
end
|
326
|
+
|
327
|
+
private
|
328
|
+
|
329
|
+
attr_reader :sort_prefix
|
330
|
+
|
331
|
+
end
|
332
|
+
|
333
|
+
|
334
|
+
|
335
|
+
|
336
|
+
# Represents a {Variable} containing an ordered list of {Name}
|
337
|
+
# objects. The names can be formatted using CSL formatting options (see
|
338
|
+
# {Names.defaults} for details).
|
339
|
+
class Names < Variable
|
340
|
+
|
341
|
+
@defaults = {
|
342
|
+
:and => ' & ',
|
343
|
+
:delimiter => ', ',
|
344
|
+
:'delimiter-precedes-last' => :contextual,
|
345
|
+
:'et-al' => 'et al.',
|
346
|
+
:'et-al-min' => 5,
|
347
|
+
:'et-al-use-first' => 3,
|
348
|
+
:'et-al-subsequent-min' => 5,
|
349
|
+
:'et-al-subsequent-use-first' => 3
|
350
|
+
}.freeze
|
351
|
+
|
352
|
+
class << self
|
353
|
+
|
354
|
+
# @!attribute [r] defaults
|
355
|
+
# @example
|
356
|
+
# {
|
357
|
+
# :and => '&',
|
358
|
+
# # The delimiter between the penultimate and last name
|
359
|
+
#
|
360
|
+
# :delimiter => ', ',
|
361
|
+
# # The delimiter between the other names
|
362
|
+
#
|
363
|
+
# :'delimiter-precedes-last' => :contextual,
|
364
|
+
# # Determines in which cases the delimiter used to delimit names
|
365
|
+
# # is also used to separate the second to last and the last name
|
366
|
+
# # in name lists. The possible values are: 'contextual' (default,
|
367
|
+
# # the delimiter is only included for name lists with three or
|
368
|
+
# # more names), 'always', and 'never'
|
369
|
+
#
|
370
|
+
# :'et-al' => 'et al.',
|
371
|
+
# # The string used for the phrase 'and others'
|
372
|
+
#
|
373
|
+
# :'et-al-min' => 5,
|
374
|
+
# :'et-al-use-first' => 3,
|
375
|
+
# # Together, these attributes control et-al abbreviation. When
|
376
|
+
# # the number of names in a name variable matches or exceeds
|
377
|
+
# # the number set on et-al-min, the rendered name list is truncated
|
378
|
+
# # at the number of names set on et-al-use-first. If truncation
|
379
|
+
# # occurs, the "et-al" term is appended to the names rendered.
|
380
|
+
# # With a single name (et-al-use-first="1"), the "et-al" term is
|
381
|
+
# # preceded by a space (e.g. "Doe et al."). With multiple names,
|
382
|
+
# # the "et-al" term is preceded by the name delimiter (e.g.
|
383
|
+
# # "Doe, Smith, et al.")
|
384
|
+
#
|
385
|
+
# :'et-al-subsequent-min' => 5,
|
386
|
+
# :'et-al-subsequent-use-first' => 3
|
387
|
+
# # See above. Abbreviation rules for subsequent cites (cites
|
388
|
+
# # referencing earlier cited items)
|
389
|
+
# }
|
390
|
+
#
|
391
|
+
# @return [Hash] the Names' default formatting options
|
392
|
+
attr_reader :defaults
|
393
|
+
|
394
|
+
# Parses the passed-in string and returns a Names object. Behaves like
|
395
|
+
# parse but returns nil for bad input without raising an error.
|
396
|
+
#
|
397
|
+
# @see .parse!
|
398
|
+
#
|
399
|
+
# @param names [String] the name or names to be parsed
|
400
|
+
# @return [Names,nil] the parsed names
|
401
|
+
def parse(names)
|
402
|
+
parse!(names)
|
403
|
+
rescue ParseError
|
404
|
+
nil
|
405
|
+
end
|
406
|
+
|
407
|
+
# Parses the passed-in string and returns a Names object.
|
408
|
+
#
|
409
|
+
# @param names [String] the name or names to be parsed
|
410
|
+
# @return [Names] the parsed names
|
411
|
+
#
|
412
|
+
# @raise [ParseError] if the string cannot be parsed.
|
413
|
+
def parse!(names)
|
414
|
+
new Namae.parse!(names)
|
415
|
+
rescue
|
416
|
+
raise ParseError, $!.message
|
417
|
+
end
|
418
|
+
|
419
|
+
end
|
420
|
+
|
421
|
+
include Enumerable
|
422
|
+
|
423
|
+
# @!attribute [r] options
|
424
|
+
# @return [Hash] the current formatting options
|
425
|
+
|
426
|
+
attr_reader :options
|
427
|
+
|
428
|
+
alias names value
|
429
|
+
|
430
|
+
# Don't expose value/names writer
|
431
|
+
undef_method :value=
|
432
|
+
|
433
|
+
# Delegate bang! methods to each name
|
434
|
+
Name.instance_methods(false).each do |m|
|
435
|
+
if m.to_s.end_with?('!')
|
436
|
+
define_method(m) do |*arguments, &block|
|
437
|
+
names.each do |name|
|
438
|
+
name.send(m, *arguments, &block)
|
439
|
+
end
|
440
|
+
self
|
441
|
+
end
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
# Names quack sorta like an Array
|
446
|
+
def_delegators :names, :length, :empty?, :[], :join
|
447
|
+
|
448
|
+
|
449
|
+
# Some delegators should return self
|
450
|
+
|
451
|
+
# @!method push(name)
|
452
|
+
# Appends the given name to the list of names.
|
453
|
+
# @param name [Name] a name
|
454
|
+
# @return [self]
|
455
|
+
# @!method unshift(name)
|
456
|
+
# Inserts the given name at the beginning of the list of names.
|
457
|
+
# @param name [Name] a name
|
458
|
+
# @return [self]
|
459
|
+
[:<<, :push, :unshift].each do |m|
|
460
|
+
define_method(m) do |*arguments, &block|
|
461
|
+
names.send(m, *arguments, &block)
|
462
|
+
self
|
463
|
+
end
|
464
|
+
end
|
465
|
+
|
466
|
+
def initialize(*arguments)
|
467
|
+
@options = Names.defaults.dup
|
468
|
+
super(arguments.flatten(1))
|
469
|
+
end
|
470
|
+
|
471
|
+
def initialize_copy(other)
|
472
|
+
@options, @value = other.options.dup, other.value.map(&:dup)
|
473
|
+
end
|
474
|
+
|
475
|
+
def replace(values)
|
476
|
+
@value = []
|
477
|
+
|
478
|
+
[*values].each do |value|
|
479
|
+
case
|
480
|
+
when value.is_a?(Name)
|
481
|
+
@value << value
|
482
|
+
when value.respond_to?(:each_pair), value.respond_to?(:to_hash)
|
483
|
+
@value << Name.new(value)
|
484
|
+
when value.respond_to?(:to_s)
|
485
|
+
begin
|
486
|
+
@value.concat Namae.parse!(value.to_s)
|
487
|
+
rescue
|
488
|
+
raise TypeError, $!.message
|
489
|
+
end
|
490
|
+
else
|
491
|
+
raise TypeError, "failed to create names from #{value.inspect}"
|
492
|
+
end
|
493
|
+
end
|
494
|
+
|
495
|
+
self
|
496
|
+
end
|
497
|
+
|
498
|
+
# @return [Fixnum] the maximum number of names that should be printed
|
499
|
+
def max_names
|
500
|
+
[length, options[:'et-al-use-first'].to_i.abs].min
|
501
|
+
end
|
502
|
+
|
503
|
+
# @return [Boolean] whether or not the Names should be truncate
|
504
|
+
def truncate?
|
505
|
+
length >= options[:'et-al-min'].to_i.abs
|
506
|
+
end
|
507
|
+
|
508
|
+
# @return [Boolean] whether ot not the Names, if printed on subsequent
|
509
|
+
# cites, should be truncated
|
510
|
+
def truncate_subsequent?
|
511
|
+
length >= options[:'et-al-subsequent-min'].to_i
|
512
|
+
end
|
513
|
+
|
514
|
+
# @return [String] the delimiter between names
|
515
|
+
def delimiter
|
516
|
+
options[:delimiter]
|
517
|
+
end
|
518
|
+
|
519
|
+
# @return [String] the delimiter between the penultimate and last name
|
520
|
+
# @see #connector
|
521
|
+
# @see #delimiter_precedes_last?
|
522
|
+
def last_delimiter
|
523
|
+
if delimiter_precedes_last?
|
524
|
+
[delimiter, connector].compact.join.squeeze(' ')
|
525
|
+
else
|
526
|
+
connector
|
527
|
+
end
|
528
|
+
end
|
529
|
+
|
530
|
+
# @return [String] the delimiter between the last name printed name and
|
531
|
+
# the 'and others' term
|
532
|
+
def truncated_delimiter
|
533
|
+
max_names > 1 ? delimiter : ' '
|
534
|
+
end
|
535
|
+
|
536
|
+
# @return [Boolean] whether or not the delimiter will be inserted between
|
537
|
+
# the penultimate and the last name
|
538
|
+
def delimiter_precedes_last?
|
539
|
+
case
|
540
|
+
when delimiter_never_precedes_last?
|
541
|
+
false
|
542
|
+
when delimiter_always_precedes_last?
|
543
|
+
true
|
544
|
+
else
|
545
|
+
length > 2
|
546
|
+
end
|
547
|
+
end
|
548
|
+
|
549
|
+
# @return [Boolean] whether or not the should always be inserted between
|
550
|
+
# the penultimate and the last name
|
551
|
+
def delimiter_always_precedes_last?
|
552
|
+
!!(options[:'delimiter-precedes-last'].to_s =~ /^always$/i)
|
553
|
+
end
|
554
|
+
|
555
|
+
# Set the :'delimiter-precedes-last' option to :always
|
556
|
+
# @return [self] self
|
557
|
+
def delimiter_always_precedes_last!
|
558
|
+
options[:'delimiter-precedes-last'] = :always
|
559
|
+
self
|
560
|
+
end
|
561
|
+
|
562
|
+
alias delimiter_precedes_last! delimiter_always_precedes_last!
|
563
|
+
|
564
|
+
|
565
|
+
# @return [Boolean] whether or not the should never be inserted between
|
566
|
+
# the penultimate and the last name
|
567
|
+
def delimiter_never_precedes_last?
|
568
|
+
!!(options[:'delimiter-precedes-last'].to_s =~ /^never$/i)
|
569
|
+
end
|
570
|
+
|
571
|
+
# Set the :'delimiter-precedes-last' option to :never
|
572
|
+
# @return [self] self
|
573
|
+
def delimiter_never_precedes_last!
|
574
|
+
options[:'delimiter-precedes-last'] = :never
|
575
|
+
self
|
576
|
+
end
|
577
|
+
|
578
|
+
# @return [Boolean] whether or not the should be inserted between the
|
579
|
+
# penultimate and the last name depending on the number of names
|
580
|
+
def delimiter_contextually_precedes_last?
|
581
|
+
!!(options[:'delimiter-precedes-last'].to_s =~ /^contextual/i)
|
582
|
+
end
|
583
|
+
|
584
|
+
# Set the :'delimiter-precedes-last' option to :contextual
|
585
|
+
# @return [self] self
|
586
|
+
def delimiter_contextually_precedes_last!
|
587
|
+
options[:'delimiter-precedes-last'] = :contextual
|
588
|
+
self
|
589
|
+
end
|
590
|
+
|
591
|
+
# @return [String] the connector between the penultimate and the last name
|
592
|
+
def connector
|
593
|
+
options[:and]
|
594
|
+
end
|
595
|
+
|
596
|
+
# @return [false] Names are non-numeric Variables
|
597
|
+
def numeric?
|
598
|
+
false
|
599
|
+
end
|
600
|
+
|
601
|
+
# Calls a block once for each name. If no block is given, an enumerator
|
602
|
+
# is returned instead.
|
603
|
+
#
|
604
|
+
# @yieldparam name [Name] a name in the list
|
605
|
+
# @return [self,Enumerator] self or an enumerator if no block is given
|
606
|
+
def each
|
607
|
+
if block_given?
|
608
|
+
names.each(&Proc.new)
|
609
|
+
self
|
610
|
+
else
|
611
|
+
to_enum
|
612
|
+
end
|
613
|
+
end
|
614
|
+
|
615
|
+
# Compares two lists of Names.
|
616
|
+
# @param other [(Name)] a list of names
|
617
|
+
# @return [Fixnum,nil] -1, 0, or 1 depending on the result of the
|
618
|
+
# comparison; or nil if the two objects cannot be compared
|
619
|
+
def <=>(other)
|
620
|
+
return nil unless other.respond_to?(:to_a)
|
621
|
+
to_a <=> other.to_a
|
622
|
+
end
|
623
|
+
|
624
|
+
# Converts the list of names into a formatted string depending on the
|
625
|
+
# current formatting options.
|
626
|
+
# @return [String] the formatted list of names
|
627
|
+
def to_s
|
628
|
+
case
|
629
|
+
when truncate?
|
630
|
+
[names[0...max_names].join(delimiter), options[:'et-al']].join(truncated_delimiter)
|
631
|
+
when length < 2
|
632
|
+
names.join(last_delimiter)
|
633
|
+
else
|
634
|
+
[names[0...-1].join(delimiter), names[-1]].join(last_delimiter)
|
635
|
+
end
|
636
|
+
end
|
637
|
+
|
638
|
+
# @return [String] the names in a BibTeX-compatible format
|
639
|
+
def to_bibtex
|
640
|
+
map { |n| n.dup.sort_order! }.join(' and ')
|
641
|
+
end
|
642
|
+
|
643
|
+
# @return [Array<Hash>] the list of names converted to hash objects
|
644
|
+
def to_citeproc
|
645
|
+
map(&:to_citeproc)
|
646
|
+
end
|
647
|
+
|
648
|
+
# @return [String] a human-readable representation of the Names object
|
649
|
+
def inspect
|
650
|
+
"#<CiteProc::Names #{to_s.inspect}>"
|
651
|
+
end
|
652
|
+
|
653
|
+
end
|
654
|
+
|
556
655
|
end
|