citeproc 0.0.2 → 0.0.3

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.
@@ -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