citeproc 0.0.8 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- 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
|