citeproc 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -43,7 +43,7 @@ module CiteProc
43
43
 
44
44
  # Returns the best available engine class or nil.
45
45
  def autodetect(options = {})
46
- klass = subclasses.detect { |e|
46
+ subclasses.detect { |e|
47
47
  !options.has_key?(:engine) || e.name == options[:engine] and
48
48
  !options.has_key?(:name) || e.name == options[:name]
49
49
  } || subclasses.first
@@ -52,7 +52,7 @@ module CiteProc
52
52
  # Loads the engine by requiring the engine name.
53
53
  def load(name)
54
54
  require name.gsub(/-/,'/')
55
- rescue LoadError => e
55
+ rescue LoadError
56
56
  warn "failed to load #{name} engine: try to gem install #{name}"
57
57
  end
58
58
 
@@ -70,14 +70,14 @@ module CiteProc
70
70
  end
71
71
  end
72
72
 
73
- attr_accessor :processor, :locales, :style
74
-
75
- def_delegators :@processor, :items, :options
76
-
73
+ attr_accessor :processor, :locales, :style, :items
77
74
 
78
75
  def initialize(attributes = {})
79
76
  @processor = attributes[:processor]
77
+
78
+ @items = attributes[:items] || {}
80
79
  @abbreviations = attributes[:abbreviations] || { :default => {} }
80
+
81
81
  yield self if block_given?
82
82
  end
83
83
 
@@ -113,11 +113,6 @@ module CiteProc
113
113
 
114
114
  alias append_citation_cluster append
115
115
 
116
- def preview
117
- raise NotImplementedByEngine
118
- end
119
-
120
- alias preview_citation_cluster preview
121
116
 
122
117
  def bibliography
123
118
  raise NotImplementedByEngine
@@ -3,15 +3,16 @@ module CiteProc
3
3
  class Error < StandardError
4
4
  attr_reader :original
5
5
 
6
- def initialize(message, original = nil)
7
- @original = original ? super([message, original.message].join(': ')) : super(message)
6
+ def initialize(message, original = $!)
7
+ @original = original
8
+ super(message)
8
9
  end
9
10
  end
10
11
 
12
+ class ParseError < Error; end
13
+
11
14
  class EngineError < Error; end
12
15
 
13
16
  class NotImplementedByEngine < EngineError; end
14
17
 
15
- class ArgumentError < Error; end
16
-
17
18
  end
@@ -2,11 +2,23 @@
2
2
  module CiteProc
3
3
  module Extensions
4
4
 
5
+ module Underscore
6
+ def underscore(word)
7
+ word = word.to_s.dup
8
+ word.gsub!(/::/, '/')
9
+ word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
10
+ word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
11
+ word.tr!('-', '_')
12
+ word.downcase!
13
+ word
14
+ end
15
+ end
16
+
5
17
  module DeepCopy
6
18
  def deep_copy
7
19
  Hash[*map { |k,v| [
8
- k.is_a?(Symbol) ? k : k.respond_to?(:deep_copy) ? k.deep_copy : k.clone,
9
- v.is_a?(Symbol) ? v : v.respond_to?(:deep_copy) ? v.deep_copy : v.clone
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
10
22
  ]}.flatten(1)]
11
23
  end
12
24
  end
@@ -35,8 +47,50 @@ module CiteProc
35
47
  def symbolize_keys!
36
48
  replace(symbolize_keys)
37
49
  end
50
+
38
51
  end
52
+
53
+ module StringifyKeys
54
+ def stringify_keys
55
+ inject({}) do |options, (key, value)|
56
+ options[(key.to_s rescue key) || key] = value
57
+ options
58
+ end
59
+ end
60
+
61
+ def stringify_keys!
62
+ replace(symbolize_keys)
63
+ end
64
+ end
39
65
 
66
+ module CompactJoin
67
+ def compact_join(delimiter = ' ')
68
+ reject { |t| t.nil? || (t.respond_to?(:empty?) && t.empty?) }.join(delimiter)
69
+ end
70
+ end
71
+
72
+ # based and compatible to the active support version
73
+ # module ToSentence
74
+ # def to_sentence(options = {})
75
+ # options = {
76
+ # :words_connector => ", ",
77
+ # :two_words_connector => " and ",
78
+ # :last_word_connector => ", and "
79
+ # }.merge!(options)
80
+ #
81
+ # case length
82
+ # when 0
83
+ # ""
84
+ # when 1
85
+ # self[0].to_s.dup
86
+ # when 2
87
+ # "#{self[0]}#{options[:two_words_connector]}#{self[1]}"
88
+ # else
89
+ # "#{self[0...-1].join(options[:words_connector])}#{options[:last_word_connector]}#{self[-1]}"
90
+ # end
91
+ # end
92
+ # end
93
+
40
94
  module AliasMethods
41
95
  private
42
96
  def alias_methods(*arguments)
@@ -49,9 +103,23 @@ module CiteProc
49
103
  end
50
104
 
51
105
  class Hash
106
+ warn "citeproc: re-defining Hash#deep_copy, this may cause conflicts with other libraries" if method_defined?(:deep_copy)
52
107
  include CiteProc::Extensions::DeepCopy
108
+
109
+ warn "citeproc: re-defining Hash#deep_copy, this may cause conflicts with other libraries" if method_defined?(:deep_fetch)
53
110
  include CiteProc::Extensions::DeepFetch
54
- include CiteProc::Extensions::SymbolizeKeys unless Hash.instance_methods.include?(:symbolize_keys)
111
+
112
+ include CiteProc::Extensions::SymbolizeKeys unless method_defined?(:symbolize_keys)
113
+ include CiteProc::Extensions::StringifyKeys unless method_defined?(:stringify_keys)
114
+ end
115
+
116
+ class Array
117
+ include CiteProc::Extensions::CompactJoin
118
+ # include CiteProc::Extensions::ToSentence unless method_defined?(:to_sentence)
119
+ end
120
+
121
+ class String
122
+ include CiteProc::Extensions::Underscore unless method_defined?(:underscore)
55
123
  end
56
124
 
57
125
  # module Kernel
@@ -0,0 +1,113 @@
1
+ module CiteProc
2
+
3
+ # Items are similar to a Ruby Hash but pose a number of constraints on their
4
+ # contents: keys are always (implicitly converted to) symbols and values
5
+ # are strictly instances of CiteProc::Variable. When Items are constructed
6
+ # from (or merged with) JSON objects or Hashes Variable instances are
7
+ # automatically created using by passing the variables key as type to
8
+ # Variable.create – this will create the expected Variable type for all
9
+ # fields defined in CSL (for example, the `issued' field will become a
10
+ # CiteProc::Date object); unknown types will be converted to simple
11
+ # CiteProc::Variable instances, which should be fine for numeric or string
12
+ # values but may cause problems for more complex types.
13
+ #
14
+ # Every Item provides accessor methods for all known field names; unknown
15
+ # fields can still be accessed using array accessor syntax.
16
+ #
17
+ # i = Item.new(:edition => 3, :unknown_field => 42)
18
+ # i.edition -> #<CiteProc::Number "3">
19
+ # i[:edition] -> #<CiteProc::Number "3">
20
+ # i[:unknown_field] -> #<CiteProc::Variable "42">
21
+ #
22
+ # Items can be converted to the CiteProc JSON format via #to_citeproc (or
23
+ # #to_json to get a JSON string).
24
+ class Item
25
+
26
+ @types = [
27
+ :article, :'article-journal', :'article-magazine', :'article-newspaper',
28
+ :bill, :book, :broadcast, :chapter, :entry, :'entry-dictionary',
29
+ :'entry-encyclopedia', :figure, :graphic, :interview, :legal_case,
30
+ :legislation, :manuscript, :map, :motion_picture, :musical_score,
31
+ :pamphlet, :'paper-conference', :patent, :personal_communication, :post,
32
+ :'post-weblog', :report, :review, :'review-book', :song, :speech,
33
+ :thesis, :treaty, :webpage].freeze
34
+
35
+ @bibtex_types = Hash.new { |h,k| :misc }.merge(Hash[*%w{
36
+ pamphlet booklet
37
+ paper-conference conference
38
+ chapter inbook
39
+ chapter incollection
40
+ paper-conference inproceedings
41
+ book manual
42
+ thesis phdthesis
43
+ paper-conference proceedings
44
+ report techreport
45
+ manuscript unpublished
46
+ article article
47
+ article-journal article
48
+ article-magazine article
49
+ }.map(&:intern)]).freeze
50
+
51
+ class << self
52
+ attr_reader :types, :bibtex_types
53
+ end
54
+
55
+ include Attributes
56
+ include Enumerable
57
+
58
+ attr_predicates :id, :'short-title', :'journal-abbreviation',
59
+ *Variable.fields[:all]
60
+
61
+ def initialize(attributes = nil)
62
+ merge(attributes)
63
+ end
64
+
65
+ def initialize_copy(other)
66
+ @attributes = other.attributes.deep_copy
67
+ end
68
+
69
+ # Don't expose attributes. Items need to mimic Hash functionality in a controlled way.
70
+ private :attributes
71
+
72
+ def each
73
+ if block_given?
74
+ attributes.each(&Proc.new)
75
+ self
76
+ else
77
+ to_enum
78
+ end
79
+ end
80
+
81
+ # Returns a corresponding BibTeX::Entry if the bibtex-ruby gem is installed;
82
+ # otherwise returns a BibTeX string.
83
+ def to_bibtex
84
+ # hash = to_hash
85
+ #
86
+ # hash[:type] = Item.bibtex_types[hash[:type]]
87
+ #
88
+ # if hash.has_key?(:issued)
89
+ # date = hash.delete(:issued)
90
+ # hash[:year] = date.year
91
+ # hash[:month] = date.month
92
+ # end
93
+ #
94
+ # Variable.fields[:date].each do |field|
95
+ # hash[field] = hash[field].to_s if hash.has_key?(field)
96
+ # end
97
+ #
98
+ # Variable.fields[:names].each do |field|
99
+ # hash[field] = hash[field].map(&:sort_order!).join(' and ')
100
+ # end
101
+
102
+ raise 'not implemented yet'
103
+ end
104
+
105
+ private
106
+
107
+ def filter_value(value, key)
108
+ Variable.create!(value, key)
109
+ end
110
+
111
+ end
112
+
113
+ end
@@ -0,0 +1,556 @@
1
+ module CiteProc
2
+
3
+ # Names consist of several dependent parts of strings. Simple personal names
4
+ # are composed of family and given elements, containing respectively the
5
+ # family and given name of the individual.
6
+ #
7
+ # Name.new(:family => 'Doe', :given => 'Jane')
8
+ #
9
+ # Institutional and other names that should always be presented literally
10
+ # (such as "The Artist Formerly Known as Prince", "Banksy", or "Ramses IV")
11
+ # should be delivered as a single :literal element:
12
+ #
13
+ # Name.new(:literal => 'Banksy')
14
+ #
15
+ # Name particles, such as the "von" in "Alexander von Humboldt", can be
16
+ # delivered separately from the family and given name, as :dropping-particle
17
+ # and :non-dropping-particle elements.
18
+ #
19
+ # Name suffixes such as the "Jr." in "Frank Bennett, Jr." and the "III" in
20
+ # "Horatio Ramses III" can be delivered as a suffix element.
21
+ #
22
+ # Name.new do |n|
23
+ # n.family, n.given, n.suffix = 'Ramses', 'Horatio', 'III'
24
+ # end
25
+ #
26
+ # Names not written in the Latin or Cyrillic scripts are always displayed
27
+ # with the family name first. Sometimes it might be desired to handle a
28
+ # Latin or Cyrillic transliteration as if it were a fixed (non-Byzantine)
29
+ # name. This behavior can be prompted by including activating
30
+ # static-ordering:
31
+ #
32
+ # Name.new(:family => 'Muramaki', :given => 'Haruki').to_s
33
+ # #=> "Haruki Muramaki"
34
+ # Name.new(:family => 'Muramaki', :given => 'Haruki').static_order!.to_s
35
+ # #=> "Muramaki Haruki"
36
+ #
37
+ class Name
38
+
39
+ extend Forwardable
40
+
41
+ include Attributes
42
+ include Comparable
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-]*$/
48
+
49
+ # Class instance variables
50
+
51
+ # Default formatting options
52
+ @defaults = {
53
+ :form => 'long',
54
+ :'name-as-sort-order' => false,
55
+ :'demote-non-dropping-particle' => 'never',
56
+ :'sort-separator' => ', ',
57
+ :'initialize-with' => nil
58
+ }.freeze
59
+
60
+ @parts = [:family, :given,:literal, :suffix, :'dropping-particle',
61
+ :'non-dropping-particle'].freeze
62
+
63
+ class << self
64
+
65
+ attr_reader :defaults, :parts
66
+
67
+ def parse(name_string)
68
+ parse!(name_string)
69
+ rescue ParseError
70
+ nil
71
+ end
72
+
73
+ def parse!(name_string)
74
+ fail 'not implemented yet'
75
+ end
76
+
77
+ end
78
+
79
+
80
+
81
+ # Method generators
82
+
83
+ attr_reader :options
84
+
85
+ attr_predicates :'comma-suffix', :'static-ordering', :multi, *@parts
86
+
87
+ # Aliases
88
+ [[:last, :family], [:first, :given], [:particle, :'non_dropping_particle']].each do |a, m|
89
+ alias_method(a, m) if method_defined?(m)
90
+
91
+ wa, wm = "#{a}=", "#{m}="
92
+ alias_method(wa, wm) if method_defined?(wm)
93
+
94
+ pa, pm = "#{a}?", "#{m}?"
95
+ alias_method(pa, pm) if method_defined?(pm)
96
+
97
+ pa, pm = "has_#{a}?", "has_#{m}?"
98
+ alias_method(pa, pm) if method_defined?(pm)
99
+ end
100
+
101
+ # Names quack sorta like a String
102
+ def_delegators :to_s, :=~, :===,
103
+ *String.instance_methods(false).reject { |m| m =~ /^\W|!$|to_s|replace|first|last/ }
104
+
105
+ # Delegate bang! methods to each field's value
106
+ String.instance_methods(false).each do |m|
107
+ if m.to_s.end_with?('!')
108
+ define_method(m) do |*arguments, &block|
109
+ Name.parts.each do |part|
110
+ p = attributes[part]
111
+ p.send(m, *arguments, &block) if p.respond_to?(m)
112
+ end
113
+ self
114
+ end
115
+ end
116
+ end
117
+
118
+
119
+ # Instance methods
120
+
121
+ def initialize(attributes = {}, options = {})
122
+ @options = Name.defaults.merge(options)
123
+ @sort_prefix = (/^(the|an?|der|die|das|eine?|l[ae])\s+|^l\W/i).freeze
124
+
125
+ merge(attributes)
126
+
127
+ yield self if block_given?
128
+ end
129
+
130
+ def initialize_copy(other)
131
+ @attributes = other.attributes.deep_copy
132
+ @options = other.options.dup
133
+ end
134
+
135
+
136
+ # Returns true if the Name looks like it belongs to a person.
137
+ def personal?
138
+ !!family && !literal?
139
+ end
140
+
141
+ # Returns true if the name contains only romanesque characters. This
142
+ # should be the case for the majority of names written in latin or
143
+ # greek based script. It will be false, for example, for names written
144
+ # in Chinese, Japanese, Arabic or Hebrew.
145
+ def romanesque?
146
+ !!([given, family].join.gsub(Variable.markup, '') =~ ROMANESQUE)
147
+ end
148
+
149
+ alias byzantine? romanesque?
150
+
151
+ def static_order?
152
+ static_ordering? || !romanesque?
153
+ end
154
+
155
+ def static_order!
156
+ self.static_ordering = true
157
+ self
158
+ end
159
+
160
+ alias static_order static_ordering
161
+ alias static_order= static_ordering=
162
+
163
+ # Returns true if this Name's sort-oder options currently set.
164
+ def sort_order?
165
+ !!(options[:'name-as-sort-order'].to_s =~ /^(y(es)?|always|t(rue)?)$/i)
166
+ end
167
+
168
+ def display_order?
169
+ !sort_order?
170
+ end
171
+
172
+ # Sets this name sort-order option to true. Returns the Name instance.
173
+ def sort_order!
174
+ options[:'name-as-sort-order'] = true
175
+ self
176
+ end
177
+
178
+ # The reverse of @sort_order!
179
+ def display_order!
180
+ options[:'name-as-sort-order'] = false
181
+ self
182
+ end
183
+
184
+ def sort_separator
185
+ options[:'sort-separator']
186
+ end
187
+
188
+ alias comma sort_separator
189
+
190
+ def short_form?
191
+ options[:form] == 'short'
192
+ end
193
+
194
+ def short_form!
195
+ options[:form] = 'short'
196
+ self
197
+ end
198
+
199
+ def long_form?
200
+ options[:form] == 'long'
201
+ end
202
+
203
+ def long_form!
204
+ options[:form] = 'long'
205
+ self
206
+ end
207
+
208
+ # TODO should be done via mixin to be reused for variables
209
+ # def transliterable?
210
+ # end
211
+ #
212
+ # def transliterate(locale)
213
+ # end
214
+
215
+ def initials?
216
+ !!options[:'initialize-with'] && personal? && romanesque?
217
+ end
218
+
219
+ def demote_non_dropping_particle?
220
+ always_demote_non_dropping_particle? ||
221
+ !!(sort_order? && options[:'demote-non-dropping-particle'] =~ /^sort(-only)?$/i)
222
+ end
223
+
224
+ alias demote_particle? demote_non_dropping_particle?
225
+
226
+ def never_demote_non_dropping_particle?
227
+ !!(options[:'demote-non-dropping-particle'] =~ /^never$/i)
228
+ end
229
+
230
+ def never_demote_non_dropping_particle!
231
+ options[:'demote-non-dropping-particle'] = 'never'
232
+ self
233
+ end
234
+
235
+ alias never_demote_particle? never_demote_non_dropping_particle?
236
+ alias never_demote_particle! never_demote_non_dropping_particle!
237
+
238
+ def always_demote_non_dropping_particle?
239
+ !!(options[:'demote-non-dropping-particle'] =~ /^(display-and-sort|always)$/i)
240
+ end
241
+
242
+ def always_demote_non_dropping_particle!
243
+ options[:'demote-non-dropping-particle'] = 'display-and-sort'
244
+ self
245
+ end
246
+
247
+ alias always_demote_particle? always_demote_non_dropping_particle?
248
+ alias always_demote_particle! always_demote_non_dropping_particle!
249
+
250
+ alias demote_particle! always_demote_non_dropping_particle!
251
+
252
+ # Compares two names. The comparison is based on #sort_order_downcase.
253
+ def <=>(other)
254
+ return nil unless other.respond_to?(:sort_order_downcase)
255
+ sort_order_downcase <=> other.sort_order_downcase
256
+ end
257
+
258
+ # Returns the Name as a String according to the Name's formatting options.
259
+ def to_s
260
+ case
261
+ when literal?
262
+ literal.to_s
263
+
264
+ when static_order?
265
+ [family, given].compact.join(' ')
266
+
267
+ when !short_form?
268
+ case
269
+ when !sort_order?
270
+ [[given, dropping_particle, particle, family].compact_join(' '),
271
+ suffix].compact_join(comma_suffix? ? comma : ' ')
272
+
273
+ when !demote_particle?
274
+ [[particle, family].compact_join(' '), [given,
275
+ dropping_particle].compact_join(' '), suffix].compact_join(comma)
276
+
277
+ else
278
+ [family, [given, dropping_particle, particle].compact_join(' '),
279
+ suffix].compact_join(comma)
280
+ end
281
+
282
+ else
283
+ [particle, family].compact_join(' ')
284
+ end
285
+ end
286
+
287
+ # Returns an ordered array of formatted name parts to be used for sorting.
288
+ def sort_order
289
+ case
290
+ when literal?
291
+ [literal.to_s.sub(sort_prefix, '')]
292
+ when never_demote_particle?
293
+ [[particle, family].compact_join(' '), dropping_particle, given, suffix].map(&:to_s)
294
+ else
295
+ [family, [particle, dropping_particle].compact_join(' '), given, suffix].map(&:to_s)
296
+ end
297
+ end
298
+
299
+ def strip_markup
300
+ gsub(Variable.markup, '')
301
+ end
302
+
303
+ def strip_markup!
304
+ gsub!(Variable.markup, '')
305
+ end
306
+
307
+ # Returns the sort order array stripped off markup and downcased.
308
+ def sort_order_downcase
309
+ sort_order.map { |s| s.downcase.gsub(Variable.markup, '') }
310
+ end
311
+
312
+ def inspect
313
+ "#<CiteProc::Name #{to_s.inspect}>"
314
+ end
315
+
316
+ private
317
+
318
+ attr_reader :sort_prefix
319
+
320
+ end
321
+
322
+
323
+
324
+
325
+ # Names are a CiteProc Variable containing an ordered list of Name objects.
326
+ #
327
+ # Names can be formatted using CSL formatting options. The available options
328
+ # and their default values are described below.
329
+ #
330
+ # * and: specifies the delimiter between the penultimate and the last name.
331
+ # Defaults to '&'.
332
+ #
333
+ # * delimiter: Soecifies the text string to seaparate the individual names.
334
+ # The default value is ', '.
335
+ #
336
+ # * delimiter-precedes-last: determines in which cases the delimiter used
337
+ # to delimit names is also used to separate the second to last and the
338
+ # last name in name lists. The possible values are: 'contextual' (default,
339
+ # the delimiter is only included for name lists with three or more names),
340
+ # 'always', and 'never'.
341
+ #
342
+ # * et-al-min and et-al-use-first: Together, these attributes control et-al
343
+ # abbreviation. When the number of names in a name variable matches or
344
+ # exceeds the number set on et-al-min, the rendered name list is truncated
345
+ # at the number of names set on et-al-use-first. If truncation occurs, the
346
+ # "et-al" term is appended to the names rendered (see also Et-al). With a
347
+ # single name (et-al-use-first="1"), the "et-al" term is preceded by a
348
+ # space (e.g. "Doe et al."). With multiple names, the "et-al" term is
349
+ # preceded by the name delimiter (e.g. "Doe, Smith, et al.").
350
+ #
351
+ # * et-al-subsequent-min / et-al-subsequent-use-first: The (optional)
352
+ # et-al-min and et-al-use-first attributes take effect for all cites and
353
+ # bibliographic entries. With the et-al-subsequent-min and
354
+ # et-al-subsequent-use-first attributes divergent et-al abbreviation rules
355
+ # can be specified for subsequent cites (cites referencing earlier cited
356
+ # items).
357
+ #
358
+ class Names < Variable
359
+
360
+ @defaults = {
361
+ :and => '&',
362
+ :delimiter => ', ',
363
+ :'delimiter-precedes-last' => :contextual,
364
+ :'et-al' => 'et al.',
365
+ :'et-al-min' => 5,
366
+ :'et-al-use-first' => 3,
367
+ :'et-al-subsequent-min' => 5,
368
+ :'et-al-subsequent-use-first' => 3
369
+ }.freeze
370
+
371
+ class << self
372
+
373
+ attr_reader :defaults
374
+
375
+ def parse(names_string)
376
+ parse!(names_string)
377
+ rescue ParseError
378
+ nil
379
+ end
380
+
381
+ def parse!(names_string)
382
+ fail 'not implemented yet'
383
+ end
384
+
385
+ end
386
+
387
+
388
+ include Enumerable
389
+
390
+ attr_reader :options
391
+
392
+ alias names value
393
+
394
+ # Don't expose value/names writer
395
+ undef_method :value=
396
+
397
+ # Delegate bang! methods to each name
398
+ Name.instance_methods(false).each do |m|
399
+ if m.to_s.end_with?('!')
400
+ define_method(m) do |*arguments, &block|
401
+ names.each do |name|
402
+ name.send(m, *arguments, &block)
403
+ end
404
+ self
405
+ end
406
+ end
407
+ end
408
+
409
+ # Names quack sorta like an Array
410
+ def_delegators :names, :length, :empty?, :[], :join
411
+
412
+ # Some delegators should return self
413
+ [:push, :<<, :unshift].each do |m|
414
+ define_method(m) do |*arguments, &block|
415
+ names.send(m, *arguments, &block)
416
+ self
417
+ end
418
+ end
419
+
420
+
421
+ def initialize(*arguments)
422
+ @options = Names.defaults.dup
423
+ super(arguments.flatten(1))
424
+ end
425
+
426
+ def initialize_copy(other)
427
+ @options, @value = other.options.dup, other.value.map(&:dup)
428
+ end
429
+
430
+ def replace(values)
431
+ @value = []
432
+
433
+ values.each do |value|
434
+ case
435
+ when value.is_a?(Name)
436
+ @value << value
437
+
438
+ when value.is_a?(Hash)
439
+ @value << Name.new(value)
440
+
441
+ when value.respond_to?(:to_s)
442
+ @value << Name.parse!(value.to_s)
443
+
444
+ else
445
+ raise TypeError, "failed to create names from #{value.inspect}"
446
+ end
447
+ end
448
+
449
+ self
450
+ end
451
+
452
+ # Returns true if the Names, if printed, will be abbreviated.
453
+ def abbreviate?
454
+ length >= options[:'et-al-min'].to_i
455
+ end
456
+
457
+ # Returns true if the Names, if printed on subsequent cites, will be abbreviated.
458
+ def abbreviate_subsequent?
459
+ length >= options[:'et-al-subsequent-min'].to_i
460
+ end
461
+
462
+ def delimiter
463
+ options[:delimiter]
464
+ end
465
+
466
+ def last_delimiter
467
+ delimiter_precedes_last? ? [delimiter, connector].compact.join : connector
468
+ end
469
+
470
+ # Returns whether or not the delimiter will be inserted between the penultimate and the last name.
471
+ def delimiter_precedes_last?
472
+ case
473
+ when delimiter_never_precedes_last?
474
+ false
475
+ when delimiter_always_precedes_last?
476
+ true
477
+ else
478
+ length > 2
479
+ end
480
+ end
481
+
482
+ def delimiter_always_precedes_last?
483
+ !!(options[:'delimiter-precedes-last'].to_s =~ /^always$/i)
484
+ end
485
+
486
+ def delimiter_always_precedes_last!
487
+ options[:'delimiter-precedes-last'].to_s = :always
488
+ end
489
+
490
+ alias delimiter_precedes_last! delimiter_always_precedes_last!
491
+
492
+ def delimiter_never_precedes_last?
493
+ !!(options[:'delimiter-precedes-last'].to_s =~ /^never$/i)
494
+ end
495
+
496
+ def delimiter_never_precedes_last!
497
+ options[:'delimiter-precedes-last'].to_s = :never
498
+ end
499
+
500
+ def delimiter_never_precedes_last?
501
+ !!(options[:'delimiter-precedes-last'].to_s =~ /^contextual/i)
502
+ end
503
+
504
+ def delimiter_contextually_precedes_last!
505
+ options[:'delimiter-precedes-last'].to_s = :contextual
506
+ end
507
+
508
+
509
+
510
+ # Returns the string used as connector between the penultimate and the last name.
511
+ def connector
512
+ options[:and]
513
+ end
514
+
515
+ # Names are not numeric
516
+ def numeric?
517
+ false
518
+ end
519
+
520
+ def each
521
+ if block_given?
522
+ names.each(&Proc.new)
523
+ self
524
+ else
525
+ to_enum
526
+ end
527
+ end
528
+
529
+ def <=>(other)
530
+ return nil unless other.respond_to?(:names)
531
+ names <=> other.names
532
+ end
533
+
534
+ def to_s
535
+ if length < 2
536
+ names.join(last_delimiter)
537
+ else
538
+ [names[0...-1].join(delimiter), names[-1]].join(last_delimiter)
539
+ end
540
+ end
541
+
542
+ def to_bibtex
543
+ map { |n| n.dup.sort_order! }.join(' and ')
544
+ end
545
+
546
+ def to_citeproc
547
+ map(&:to_citeproc)
548
+ end
549
+
550
+ def inspect
551
+ "#<CiteProc::Names #{to_s}>"
552
+ end
553
+
554
+ end
555
+
556
+ end