citeproc 0.0.8 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,3 @@
1
- require 'forwardable'
2
-
3
1
  module CiteProc
4
2
 
5
3
  class Engine
@@ -9,10 +9,10 @@ module CiteProc
9
9
  end
10
10
  end
11
11
 
12
- class ParseError < Error; end
13
-
14
- class EngineError < Error; end
12
+ ParseError = Class.new(Error)
13
+
14
+ EngineError = Class.new(Error)
15
15
 
16
- class NotImplementedByEngine < EngineError; end
16
+ NotImplementedByEngine = Class.new(Error)
17
17
 
18
18
  end
@@ -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
- Hash[*map { |k,v| [
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 instances of CiteProc::Variable. When Items are constructed
7
- # from (or merged with) JSON objects or Hashes Variable instances are
8
- # automatically created using by passing the variables key as type to
9
- # Variable.create this will create the expected Variable type for all
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
- # CiteProc::Date object); unknown types will be converted to simple
12
- # CiteProc::Variable instances, which should be fine for numeric or string
13
- # values but may cause problems for more complex types.
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 -> #<CiteProc::Number "3">
20
- # i[:edition] -> #<CiteProc::Number "3">
21
- # i[:unknown_field] -> #<CiteProc::Variable "42">
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 (or
24
- # #to_json to get a JSON string).
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
- # Don't expose attributes. Items need to mimic Hash functionality in a controlled way.
71
- private :attributes
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.each(&Proc.new)
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
- # Returns a corresponding BibTeX::Entry if the bibtex-ruby gem is installed;
83
- # otherwise returns a BibTeX string.
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.to_s.to_sym
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
@@ -1,556 +1,655 @@
1
+ # -*- coding: utf-8 -*-
1
2
  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-]*$/
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
- # 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|
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
- wa, wm = "#{a}=", "#{m}="
84
+ wa, wm = "#{a}=", "#{m}="
92
85
  alias_method(wa, wm) if method_defined?(wm)
93
86
 
94
- pa, pm = "#{a}?", "#{m}?"
87
+ pa, pm = "#{a}?", "#{m}?"
95
88
  alias_method(pa, pm) if method_defined?(pm)
96
89
 
97
- pa, pm = "has_#{a}?", "has_#{m}?"
90
+ pa, pm = "has_#{a}?", "has_#{m}?"
98
91
  alias_method(pa, pm) if method_defined?(pm)
99
92
  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
-
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