bibtex-ruby 2.0.1 → 2.0.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of bibtex-ruby might be problematic. Click here for more details.

data/lib/bibtex/entry.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  #--
2
2
  # BibTeX-Ruby
3
- # Copyright (C) 2010-2011 Sylvester Keil <sylvester.keil.or.at>
3
+ # Copyright (C) 2010-2011 Sylvester Keil <sylvester.keil.or.at>
4
4
  #
5
5
  # This program is free software: you can redistribute it and/or modify
6
6
  # it under the terms of the GNU General Public License as published by
@@ -9,48 +9,46 @@
9
9
  #
10
10
  # This program is distributed in the hope that it will be useful,
11
11
  # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
13
  # GNU General Public License for more details.
14
14
  #
15
15
  # You should have received a copy of the GNU General Public License
16
- # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
17
  #++
18
18
 
19
- require 'forwardable'
20
-
21
19
  module BibTeX
22
- #
23
- # Represents a regular BibTeX entry.
24
- #
25
- class Entry < Element
26
- extend Forwardable
27
- include Enumerable
28
-
29
- # Defines the required fields of the standard entry types
30
- REQUIRED_FIELDS = Hash.new([]).merge({
31
- :article => [:author,:title,:journal,:year],
32
- :book => [[:author,:editor],:title,:publisher,:year],
33
- :booklet => [:title],
34
- :conference => [:author,:title,:booktitle,:year],
35
- :inbook => [[:author,:editor],:title,[:chapter,:pages],:publisher,:year],
36
- :incollection => [:author,:title,:booktitle,:publisher,:year],
37
- :inproceedings => [:author,:title,:booktitle,:year],
38
- :manual => [:title],
39
- :mastersthesis => [:author,:title,:school,:year],
40
- :misc => [],
41
- :phdthesis => [:author,:title,:school,:year],
42
- :proceedings => [:title,:year],
43
- :techreport => [:author,:title,:institution,:year],
44
- :unpublished => [:author,:title,:note]
45
- }).freeze
46
-
47
- # Defines the default fallbacks for values defined in cross-references
48
- FIELD_ALIASES = {
49
- :booktitle => :title,
50
- # :editor => :author
51
- }.freeze
52
-
53
-
20
+ #
21
+ # Represents a regular BibTeX entry.
22
+ #
23
+ class Entry < Element
24
+ extend Forwardable
25
+ include Enumerable
26
+
27
+ # Defines the required fields of the standard entry types
28
+ REQUIRED_FIELDS = Hash.new([]).merge({
29
+ :article => [:author,:title,:journal,:year],
30
+ :book => [[:author,:editor],:title,:publisher,:year],
31
+ :booklet => [:title],
32
+ :conference => [:author,:title,:booktitle,:year],
33
+ :inbook => [[:author,:editor],:title,[:chapter,:pages],:publisher,:year],
34
+ :incollection => [:author,:title,:booktitle,:publisher,:year],
35
+ :inproceedings => [:author,:title,:booktitle,:year],
36
+ :manual => [:title],
37
+ :mastersthesis => [:author,:title,:school,:year],
38
+ :misc => [],
39
+ :phdthesis => [:author,:title,:school,:year],
40
+ :proceedings => [:title,:year],
41
+ :techreport => [:author,:title,:institution,:year],
42
+ :unpublished => [:author,:title,:note]
43
+ }).freeze
44
+
45
+ # Defines the default fallbacks for values defined in cross-references
46
+ FIELD_ALIASES = {
47
+ :booktitle => :title,
48
+ # :editor => :author
49
+ }.freeze
50
+
51
+
54
52
  NAME_FIELDS = [:author,:editor,:translator].freeze
55
53
  DATE_FIELDS = [:year,:month].freeze
56
54
 
@@ -109,22 +107,22 @@ module BibTeX
109
107
  article article-journal
110
108
  }.map(&:intern)]).freeze
111
109
 
112
-
113
- attr_reader :fields, :type
114
-
115
- def_delegators :@fields, :empty?, :each, :each_pair
116
-
117
- # Creates a new instance. If a hash is given, the entry is populated accordingly.
118
- def initialize(attributes = {})
119
- @fields = {}
120
-
121
- self.type = attributes.delete(:type) if attributes.has_key?(:type)
122
- self.key = attributes.delete(:key) if attributes.has_key?(:key)
123
-
124
- add(attributes)
125
-
126
- yield self if block_given?
127
- end
110
+
111
+ attr_reader :fields, :type
112
+
113
+ def_delegators :@fields, :empty?
114
+
115
+ # Creates a new instance. If a hash is given, the entry is populated accordingly.
116
+ def initialize(attributes = {})
117
+ @fields = {}
118
+
119
+ self.type = attributes.delete(:type) if attributes.has_key?(:type)
120
+ self.key = attributes.delete(:key) if attributes.has_key?(:key)
121
+
122
+ add(attributes)
123
+
124
+ yield self if block_given?
125
+ end
128
126
 
129
127
  def initialize_copy (other)
130
128
  @fields = {}
@@ -135,262 +133,287 @@ module BibTeX
135
133
  add(other.fields)
136
134
  end
137
135
 
138
- # Returns the Entry's field name aliases.
139
- def aliases
140
- @aliases ||= FIELD_ALIASES.dup
141
- end
142
-
143
- # Sets the Entry's key. If the Entry is currently registered with a
144
- # Bibliography, re-registers the Entry with the new key; note that this
145
- # may change the key value if another Entry is already regsitered with
146
- # the same key.
147
- #
148
- # Returns the new key.
149
- def key=(key)
150
- key = key.to_s
151
-
136
+ # call-seq:
137
+ # entry.each { |key, value| block } -> entry
138
+ # entry.each_pair { |key, value| block } -> entry
139
+ # entry.each -> an_enumerator
140
+ # entry.each_pair -> an_enumerator
141
+ #
142
+ # Calls block once for each key in entry, passing the key-value
143
+ # pair as parameters.
144
+ #
145
+ # If no block is given, an enumerator is returned instead.
146
+ def each
147
+ if block_given?
148
+ fields.each(&Proc.new)
149
+ self
150
+ else
151
+ to_enum
152
+ end
153
+ end
154
+
155
+ alias each_pair each
156
+
157
+
158
+ # Returns the Entry's field name aliases.
159
+ def aliases
160
+ @aliases ||= FIELD_ALIASES.dup
161
+ end
162
+
163
+ # Sets the Entry's key. If the Entry is currently registered with a
164
+ # Bibliography, re-registers the Entry with the new key; note that this
165
+ # may change the key value if another Entry is already regsitered with
166
+ # the same key.
167
+ #
168
+ # Returns the new key.
169
+ def key=(key)
170
+ key = key.to_s
171
+
152
172
  if registered?
153
- bibliography.entries.delete(@key)
154
- key = register(key)
173
+ bibliography.entries.delete(@key)
174
+ key = register(key)
155
175
  end
156
176
 
157
- @key = key
158
- rescue => e
159
- raise BibTeXError, "failed to set key to #{key.inspect}: #{e.message}"
160
- end
161
-
162
- def key
163
- @key ||= default_key
164
- end
165
-
166
- alias id key
167
- alias id= key=
168
-
169
- # TODO we should be more lenient: allow strings as key or don't check at all
170
- # Sets the type of the entry.
171
- def type=(type)
172
- # raise(ArgumentError, "types must be convertible to Symbol; was: #{type.class.name}.") unless type.respond_to?(:to_sym)
173
- @type = type.to_sym
174
- end
175
-
176
- def has_type?(type)
177
- type.to_s.match(/^entry$/i) || @type == type.to_sym || super
178
- end
177
+ @key = key
178
+ rescue => e
179
+ raise BibTeXError, "failed to set key to #{key.inspect}: #{e.message}"
180
+ end
181
+
182
+ def key
183
+ @key ||= default_key
184
+ end
185
+
186
+ alias id key
187
+ alias id= key=
188
+
189
+ # Sets the type of the entry.
190
+ def type=(type)
191
+ @type = type.to_sym
192
+ end
193
+
194
+ def has_type?(type)
195
+ type.to_s.match(/^entry$/i) || @type == type.to_sym || super
196
+ end
197
+
198
+ alias type? has_type?
199
+
179
200
 
180
201
  def has_field?(name)
181
- name.respond_to?(:to_sym) ? fields.has_key?(name.to_sym) : false
182
- end
183
-
184
- def inherits?(name)
185
- !has_field(name) && has_parent? && parent.provides?(name)
186
- end
187
-
188
- # Returns true if the Entry has a field (or alias) for the passed-in name.
189
- def provides?(name)
190
- return nil unless name.respond_to?(:to_sym)
191
- has_field?(name) || has_field?(aliases[name.to_sym])
192
- end
193
-
194
- # Returns the field value referenced by the passed-in name.
195
- # For example, this will return the 'title' value for 'booktitle' if a
196
- # corresponding alias is defined.
197
- def provide(name)
198
- return nil unless name.respond_to?(:to_sym)
199
- name = name.to_sym
200
- fields[name] || fields[aliases[name]]
201
- end
202
-
203
- # If the Entry has a cross-reference, copies all referenced all inherited
204
- # values from the parent.
205
- #
206
- # Returns the Entry.
207
- def save_inherited_fields
208
- inherited_fields.each do |name|
209
- fields[name] = parent.provide(name)
210
- end
211
-
212
- self
213
- end
214
-
215
- # Returns a sorted list of the Entry's field names. If a +filter+ is passed
216
- # as argument, returns all field names that are also defined by the filter.
217
- # If the +filter+ is empty, returns all field names.
218
- #
219
- # If the second optional argument is true (default) and the Entry contains
220
- # a cross-reference, the list will include all inherited fields.
221
- def field_names(filter = [], include_inherited = true)
222
- names = fields.keys
223
-
224
- if include_inherited && has_parent?
225
- names.concat(inherited_fields)
226
- end
227
-
228
- unless filter.empty?
229
- names = names & filter.map(&:to_sym)
230
- end
231
-
232
- names.sort!
233
- names
234
- end
235
-
236
- # Returns a sorted list of all field names referenced by this Entry's cross-reference.
237
- def inherited_fields
238
- return [] unless has_parent?
239
-
240
- names = parent.fields.keys - fields.keys
241
- names.concat(parent.aliases.reject { |k,v| !parent.has_field?(v) }.keys)
242
- names.sort!
243
-
244
- names
245
- end
246
-
247
-
248
- def method_missing(name, *args, &block)
249
- case
250
- when fields.has_key?(name)
251
- fields[name]
252
- when name.to_s =~ /^(.+)=$/
253
- send(:add, $1.to_sym, args[0])
254
- when name =~ /^(?:convert|from)_([a-z]+)(!)?$/
255
- $2 ? convert!($1, &block) : convert($1, &block)
256
- when has_parent? && parent.provides?(name)
257
- parent.provide(name)
258
- else
259
- super
260
- end
261
- end
262
-
263
- def respond_to?(method)
264
- provides?(method.to_sym) || method.to_s.match(/=$/) ||
265
- method =~ /^(?:convert|from)_([a-z]+)(!)?$/ || (has_parent? && parent.respond_to(method)) || super
266
- end
267
-
268
- # Returns a copy of the Entry with all the field names renamed.
269
- def rename(*arguments)
270
- dup.rename!(*arguments)
271
- end
272
-
273
- # Renames the given field names unless a field with the new name already
274
- # exists.
275
- def rename!(*arguments)
276
- Hash[*arguments.flatten].each_pair do |from,to|
277
- if fields.has_key?(from) && !fields.has_key?(to)
278
- fields[to] = fields[from]
279
- fields.delete(from)
280
- end
281
- end
282
- self
283
- end
284
-
285
- alias rename_fields rename
286
- alias rename_fields! rename!
287
-
288
- # Returns the value of the field with the given name. If the value is not
289
- # defined and the entry has cross-reference, returns the cross-referenced
290
- # value instead.
291
- def [](name)
292
- fields[name.to_sym] || parent && parent.provide(name)
293
- end
294
-
295
- alias get []
296
-
297
- def fetch(name, default = nil)
298
- get(name) || block_given? ? yield(name) : default
299
- end
300
-
301
- # Adds a new field (name-value pair) to the entry.
302
- # Returns the new value.
303
- def []=(name, value)
304
- add(name.to_sym, value)
305
- end
306
-
307
- # Adds a new field (name-value pair) or multiple fields to the entry.
308
- # Returns the entry for chainability.
309
- #
310
- # call-seq:
311
- # add(:author, "Edgar A. Poe")
312
- # add(:author, "Edgar A. Poe", :title, "The Raven")
313
- # add([:author, "Edgar A. Poe", :title, "The Raven"])
314
- # add(:author => "Edgar A. Poe", :title => "The Raven")
315
- def add(*arguments)
316
- Hash[*arguments.flatten].each_pair do |name, value|
317
- fields[name.to_sym] = Value.new(value)
318
- end
319
-
320
- self
321
- end
322
-
323
- alias << add
324
-
325
- # Removes the field with a given name from the entry.
326
- # Returns the value of the deleted field; nil if the field was not set.
327
- def delete(name)
328
- fields.delete(name.to_sym)
329
- end
330
-
331
- # Returns false if the entry is one of the standard entry types and does not have
332
- # definitions of all the required fields for that type.
333
- def valid?
334
- REQUIRED_FIELDS[@type].all? do |f|
335
- f.is_a?(Array) ? !(f & fields.keys).empty? : !fields[f].nil?
336
- end
337
- end
338
-
339
- def generate_hash(filter = [])
340
- Digest::MD5.hexdigest(field_names(filter).map { |k| [k, fields[k]] }.flatten.join)
341
- end
342
-
343
- # Called when the element was added to a bibliography.
344
- def added_to_bibliography(bibliography)
345
- super
346
-
347
- @key = register(key)
348
-
349
- [:parse_names, :parse_months].each do |parser|
350
- send(parser) if bibliography.options[parser]
351
- end
352
-
353
- if bibliography.options.has_key?(:filter)
354
- convert!(bibliography.options[:filter])
355
- end
356
-
357
- self
358
- end
359
-
360
- # Called when the element was removed from a bibliography.
361
- def removed_from_bibliography(bibliography)
362
- super
363
- bibliography.entries.delete(key)
364
- self
365
- end
366
-
367
- # Returns true if the Entry is currently registered with the associated Bibliography.
368
- def registered?
369
- !!(bibliography && bibliography.entries[key].equal?(self))
370
- end
371
-
372
- # Registers this Entry in the associated Bibliographies entries hash.
373
- # This method may change the Entry's key, if another entry is already
374
- # registered with the current key.
375
- #
376
- # Returns the key or nil if the Entry is not associated with a Bibliography.
377
- def register(key)
378
- return nil if bibliography.nil?
379
-
380
- k = key.dup
381
- k.succ! while bibliography.has_key?(k)
382
- bibliography.entries[k] = self
383
- k
384
- end
385
-
386
- def replace(*arguments)
387
- arguments = bibliography.q('@string') if arguments.empty?
388
- @fields.values.each { |v| v.replace(*arguments) }
389
- self
390
- end
202
+ name.respond_to?(:to_sym) ? fields.has_key?(name.to_sym) : false
203
+ end
204
+
205
+ alias field? has_field?
206
+
207
+ def inherits?(name)
208
+ !has_field(name) && has_parent? && parent.provides?(name)
209
+ end
210
+
211
+ # Returns true if the Entry has a field (or alias) for the passed-in name.
212
+ def provides?(name)
213
+ return nil unless name.respond_to?(:to_sym)
214
+ has_field?(name) || has_field?(aliases[name.to_sym])
215
+ end
216
+
217
+ # Returns the field value referenced by the passed-in name.
218
+ # For example, this will return the 'title' value for 'booktitle' if a
219
+ # corresponding alias is defined.
220
+ def provide(name)
221
+ return nil unless name.respond_to?(:to_sym)
222
+ name = name.to_sym
223
+ fields[name] || fields[aliases[name]]
224
+ end
225
+
226
+ # If the Entry has a cross-reference, copies all referenced all inherited
227
+ # values from the parent.
228
+ #
229
+ # Returns the Entry.
230
+ def save_inherited_fields
231
+ inherited_fields.each do |name|
232
+ fields[name] = parent.provide(name)
233
+ end
234
+
235
+ self
236
+ end
237
+
238
+ # Returns a sorted list of the Entry's field names. If a +filter+ is passed
239
+ # as argument, returns all field names that are also defined by the filter.
240
+ # If the +filter+ is empty, returns all field names.
241
+ #
242
+ # If the second optional argument is true (default) and the Entry contains
243
+ # a cross-reference, the list will include all inherited fields.
244
+ def field_names(filter = [], include_inherited = true)
245
+ names = fields.keys
246
+
247
+ if include_inherited && has_parent?
248
+ names.concat(inherited_fields)
249
+ end
250
+
251
+ unless filter.empty?
252
+ names = names & filter.map(&:to_sym)
253
+ end
254
+
255
+ names.sort!
256
+ names
257
+ end
258
+
259
+ # Returns a sorted list of all field names referenced by this Entry's cross-reference.
260
+ def inherited_fields
261
+ return [] unless has_parent?
262
+
263
+ names = parent.fields.keys - fields.keys
264
+ names.concat(parent.aliases.reject { |k,v| !parent.has_field?(v) }.keys)
265
+ names.sort!
266
+
267
+ names
268
+ end
269
+
270
+
271
+ def method_missing(name, *args, &block)
272
+ case
273
+ when fields.has_key?(name)
274
+ fields[name]
275
+ when name.to_s =~ /^(.+)=$/
276
+ send(:add, $1.to_sym, args[0])
277
+ when name =~ /^(?:convert|from)_([a-z]+)(!)?$/
278
+ $2 ? convert!($1, &block) : convert($1, &block)
279
+ when has_parent? && parent.provides?(name)
280
+ parent.provide(name)
281
+ else
282
+ super
283
+ end
284
+ end
285
+
286
+ def respond_to?(method)
287
+ provides?(method.to_sym) || method.to_s.match(/=$/) ||
288
+ method =~ /^(?:convert|from)_([a-z]+)(!)?$/ || (has_parent? && parent.respond_to?(method)) || super
289
+ end
290
+
291
+ # Returns a copy of the Entry with all the field names renamed.
292
+ def rename(*arguments)
293
+ dup.rename!(*arguments)
294
+ end
295
+
296
+ # Renames the given field names unless a field with the new name already
297
+ # exists.
298
+ def rename!(*arguments)
299
+ Hash[*arguments.flatten].each_pair do |from,to|
300
+ if fields.has_key?(from) && !fields.has_key?(to)
301
+ fields[to] = fields[from]
302
+ fields.delete(from)
303
+ end
304
+ end
305
+ self
306
+ end
307
+
308
+ alias rename_fields rename
309
+ alias rename_fields! rename!
310
+
311
+ # Returns the value of the field with the given name. If the value is not
312
+ # defined and the entry has cross-reference, returns the cross-referenced
313
+ # value instead.
314
+ def [](name)
315
+ fields[name.to_sym] || parent && parent.provide(name)
316
+ end
317
+
318
+ alias get []
319
+
320
+ def fetch(name, default = nil)
321
+ get(name) || block_given? ? yield(name) : default
322
+ end
323
+
324
+ # Adds a new field (name-value pair) to the entry.
325
+ # Returns the new value.
326
+ def []=(name, value)
327
+ add(name.to_sym, value)
328
+ end
329
+
330
+ # Adds a new field (name-value pair) or multiple fields to the entry.
331
+ # Returns the entry for chainability.
332
+ #
333
+ # call-seq:
334
+ # add(:author, "Edgar A. Poe")
335
+ # add(:author, "Edgar A. Poe", :title, "The Raven")
336
+ # add([:author, "Edgar A. Poe", :title, "The Raven"])
337
+ # add(:author => "Edgar A. Poe", :title => "The Raven")
338
+ def add(*arguments)
339
+ Hash[*arguments.flatten].each_pair do |name, value|
340
+ fields[name.to_sym] = Value.new(value)
341
+ end
342
+
343
+ self
344
+ end
345
+
346
+ alias << add
347
+
348
+ # Removes the field with a given name from the entry.
349
+ # Returns the value of the deleted field; nil if the field was not set.
350
+ def delete(name)
351
+ fields.delete(name.to_sym)
352
+ end
353
+
354
+ # Returns false if the entry is one of the standard entry types and does not have
355
+ # definitions of all the required fields for that type.
356
+ def valid?
357
+ REQUIRED_FIELDS[@type].all? do |f|
358
+ f.is_a?(Array) ? !(f & fields.keys).empty? : !fields[f].nil?
359
+ end
360
+ end
361
+
362
+ def generate_hash(filter = [])
363
+ Digest::MD5.hexdigest(field_names(filter).map { |k| [k, fields[k]] }.flatten.join)
364
+ end
365
+
366
+ # Called when the element was added to a bibliography.
367
+ def added_to_bibliography(bibliography)
368
+ super
369
+
370
+ @key = register(key)
371
+
372
+ [:parse_names, :parse_months].each do |parser|
373
+ send(parser) if bibliography.options[parser]
374
+ end
375
+
376
+ if bibliography.options.has_key?(:filter)
377
+ convert!(bibliography.options[:filter])
378
+ end
379
+
380
+ self
381
+ end
382
+
383
+ # Called when the element was removed from a bibliography.
384
+ def removed_from_bibliography(bibliography)
385
+ super
386
+ bibliography.entries.delete(key)
387
+ self
388
+ end
389
+
390
+ # Returns true if the Entry is currently registered with the associated Bibliography.
391
+ def registered?
392
+ !!(bibliography && bibliography.entries[key].equal?(self))
393
+ end
394
+
395
+ # Registers this Entry in the associated Bibliographies entries hash.
396
+ # This method may change the Entry's key, if another entry is already
397
+ # registered with the current key.
398
+ #
399
+ # Returns the key or nil if the Entry is not associated with a Bibliography.
400
+ def register(key)
401
+ return nil if bibliography.nil?
402
+
403
+ k = key.dup
404
+ k.succ! while bibliography.has_key?(k)
405
+ bibliography.entries[k] = self
406
+ k
407
+ end
408
+
409
+ def replace(*arguments)
410
+ arguments = bibliography.q('@string') if arguments.empty?
411
+ fields.values.each { |v| v.replace(*arguments) }
412
+ self
413
+ end
391
414
 
392
415
  def join
393
- @fields.values.each(&:join)
416
+ fields.values.each(&:join)
394
417
  self
395
418
  end
396
419
 
@@ -399,12 +422,13 @@ module BibTeX
399
422
  end
400
423
 
401
424
  def parse_month
402
- fields[:month] = MONTHS_FILTER[fields[:month]] if fields.has_key?(:month)
425
+ fields[:month] = MONTHS_FILTER[fields[:month]] if has_field?(:month)
403
426
  self
404
427
  end
405
428
 
406
429
  alias parse_months parse_month
407
430
 
431
+
408
432
  # Parses all name values of the entry. Tries to replace and join the
409
433
  # value prior to parsing.
410
434
  def parse_names
@@ -417,154 +441,155 @@ module BibTeX
417
441
  end
418
442
  end
419
443
 
420
- self
421
- end
422
-
423
- # Returns a list of all names (authors, editors, translators).
424
- def names
425
- NAME_FIELDS.map { |k| has_field?(k) ? @fields[k].tokens : nil }.flatten.compact
426
- end
427
-
428
-
429
- # Returns true if the Entry has a valid cross-reference in the Bibliography.
430
- def has_parent?
431
- !parent.nil?
432
- end
433
-
434
- alias has_cross_reference? has_parent?
435
-
436
- # Returns true if the Entry cross-references an Entry which is not
437
- # registered in the current Bibliography.
438
- def parent_missing?
439
- has_field?(:crossref) && !has_parent?
440
- end
441
-
442
- alias cross_reference_missing? parent_missing?
443
-
444
- # Returns the cross-referenced Entry from the Bibliography or nil if this
445
- # Entry does define a cross-reference.
446
- def parent
447
- bibliography && bibliography[fields[:crossref]]
448
- end
449
-
450
- alias cross_reference parent
451
-
452
-
453
- # Returns true if the entry is cross-referenced by another entry in the
454
- # Bibliography.
455
- def has_children?
456
- !children.empty?
457
- end
458
-
459
- alias cross_referenced? has_children?
460
-
461
- # Returns a list of all entries in the Bibliography containing a
462
- # cross-reference to this entry or [] if there are no references to this
463
- # entry.
464
- def children
465
- (bibliography && bibliography.q("@entry[crossref=#{key}]")) || []
466
- end
467
-
468
- alias cross_referenced_by children
469
-
470
-
471
- # Returns a string of all the entry's fields.
472
- def content(options = {})
473
- fields.map { |k,v| "#{k} = #{ fields[k].to_s(options) }" }.join(",\n")
474
- end
475
-
476
- # Returns a string representation of the entry.
477
- def to_s(options = {})
478
- options[:quotes] ||= %w({ })
479
- ["@#{type}{#{key},", content(options).gsub(/^/,' '), "}\n"].join("\n")
480
- end
481
-
482
- def to_hash(options = {})
483
- options[:quotes] ||= %w({ })
484
- hash = { :key => key, :type => type }
485
- each_pair { |k,v| hash[k] = v.to_s(options) }
486
- hash
487
- end
488
-
489
- def to_citeproc(options = {})
490
- options[:quotes] ||= []
491
-
492
- parse_names
493
- parse_month
494
-
495
- hash = { 'id' => key.to_s, 'type' => CSL_TYPES[type].to_s }
496
-
444
+ self
445
+ end
446
+
447
+ # Returns a list of all names (authors, editors, translators).
448
+ def names
449
+ NAME_FIELDS.map { |k| has_field?(k) ? @fields[k].tokens : nil }.flatten.compact
450
+ end
451
+
452
+
453
+ # Returns true if the Entry has a valid cross-reference in the Bibliography.
454
+ def has_parent?
455
+ !parent.nil?
456
+ end
457
+
458
+ alias has_cross_reference? has_parent?
459
+
460
+ # Returns true if the Entry cross-references an Entry which is not
461
+ # registered in the current Bibliography.
462
+ def parent_missing?
463
+ has_field?(:crossref) && !has_parent?
464
+ end
465
+
466
+ alias cross_reference_missing? parent_missing?
467
+
468
+ # Returns the cross-referenced Entry from the Bibliography or nil if this
469
+ # Entry does define a cross-reference.
470
+ def parent
471
+ bibliography && bibliography[fields[:crossref]]
472
+ end
473
+
474
+ alias cross_reference parent
475
+
476
+
477
+ # Returns true if the entry is cross-referenced by another entry in the
478
+ # Bibliography.
479
+ def has_children?
480
+ !children.empty?
481
+ end
482
+
483
+ alias cross_referenced? has_children?
484
+
485
+ # Returns a list of all entries in the Bibliography containing a
486
+ # cross-reference to this entry or [] if there are no references to this
487
+ # entry.
488
+ def children
489
+ bibliography && bibliography.q("@entry[crossref=#{key}]") or []
490
+ end
491
+
492
+ alias cross_referenced_by children
493
+
494
+
495
+ # Returns a string of all the entry's fields.
496
+ def content(options = {})
497
+ fields.map { |k,v| "#{k} = #{ fields[k].to_s(options) }" }.join(",\n")
498
+ end
499
+
500
+ # Returns a string representation of the entry.
501
+ def to_s(options = {})
502
+ options[:quotes] ||= %w({ })
503
+ ["@#{type}{#{key},", content(options).gsub(/^/,' '), "}\n"].join("\n")
504
+ end
505
+
506
+ def to_hash(options = {})
507
+ options[:quotes] ||= %w({ })
508
+ hash = { :key => key, :type => type }
509
+ each_pair { |k,v| hash[k] = v.to_s(options) }
510
+ hash
511
+ end
512
+
513
+ def to_citeproc(options = {})
514
+ options[:quotes] ||= []
515
+
516
+ parse_names
517
+ parse_month
518
+
519
+ hash = { 'id' => key.to_s, 'type' => CSL_TYPES[type].to_s }
520
+
497
521
  each_pair do |k,v|
498
- hash[CSL_FILTER[k].to_s] = v.to_citeproc(options) unless DATE_FIELDS.include?(k)
499
- end
500
-
501
- hash['issued'] = citeproc_date
502
- hash
503
- end
504
-
505
- def issued
506
- m = MONTHS.find_index(@fields[:month] && @fields[:month].v)
507
- m = m + 1 unless m.nil?
508
- Hash['date-parts', [[@fields[:year],m].compact.map(&:to_i)]]
509
- end
510
-
511
- alias citeproc_date issued
512
-
513
- def to_xml(options = {})
514
- require 'rexml/document'
515
-
516
- xml = REXML::Element.new('bibtex:entry')
517
- xml.attributes['id'] = key
518
-
519
- entry = REXML::Element.new("bibtex:#{type}")
522
+ hash[CSL_FILTER[k].to_s] = v.to_citeproc(options) unless DATE_FIELDS.include?(k)
523
+ end
524
+
525
+ hash['issued'] = citeproc_date
526
+ hash
527
+ end
528
+
529
+ def issued
530
+ m = MONTHS.find_index(fields[:month].to_s.intern)
531
+ m = m + 1 unless m.nil?
532
+
533
+ Hash['date-parts', [[fields[:year],m].compact.map(&:to_i)]]
534
+ end
535
+
536
+ alias citeproc_date issued
537
+
538
+ def to_xml(options = {})
539
+ require 'rexml/document'
540
+
541
+ xml = REXML::Element.new('bibtex:entry')
542
+ xml.attributes['id'] = key
543
+
544
+ entry = REXML::Element.new("bibtex:#{type}")
520
545
 
521
546
  fields.each do |key, value|
522
547
  field = REXML::Element.new("bibtex:#{key}")
523
-
524
- if options[:extended] && value.name?
525
- value.each { |n| entry.add_element(n.to_xml) }
526
- else
527
- field.text = value.to_s(options)
528
- end
529
-
548
+
549
+ if options[:extended] && value.name?
550
+ value.each { |n| entry.add_element(n.to_xml) }
551
+ else
552
+ field.text = value.to_s(options)
553
+ end
554
+
530
555
  entry.add_element(field)
531
556
  end
532
557
 
533
558
  xml.add_element(entry)
534
- xml
535
- end
536
-
537
- # Returns a duplicate entry with all values converted using the filter.
538
- # If an optional block is given, only those values will be converted where
539
- # the block returns true (the block will be called with each key-value pair).
540
- #
541
- # @see #convert!
542
- def convert(filter)
543
- block_given? ? dup.convert!(filter, &Proc.new) : dup.convert!(filter)
544
- end
545
-
546
- # In-place variant of @see #convert
547
- def convert!(filter)
548
- fields.each_pair { |k,v| !block_given? || yield(k,v) ? v.convert!(filter) : v }
549
- self
550
- end
551
-
552
- def <=>(other)
553
- type != other.type ? type <=> other.type : key != other.key ? key <=> other.key : to_s <=> other.to_s
554
- end
555
-
556
- private
557
-
558
- # Returns a default key for this entry.
559
- def default_key
560
- k = names[0]
561
- k = k.respond_to?(:family) ? k.family : k.to_s
562
- k = k[/[A-Za-z]+/] || 'unknown'
563
- k << (has_field?(:year) ? year : '-')
564
- k << 'a'
565
- k.downcase!
566
- k
567
- end
568
-
569
- end
559
+ xml
560
+ end
561
+
562
+ # Returns a duplicate entry with all values converted using the filter.
563
+ # If an optional block is given, only those values will be converted where
564
+ # the block returns true (the block will be called with each key-value pair).
565
+ #
566
+ # @see #convert!
567
+ def convert(filter)
568
+ block_given? ? dup.convert!(filter, &Proc.new) : dup.convert!(filter)
569
+ end
570
+
571
+ # In-place variant of @see #convert
572
+ def convert!(filter)
573
+ fields.each_pair { |k,v| !block_given? || yield(k,v) ? v.convert!(filter) : v }
574
+ self
575
+ end
576
+
577
+ def <=>(other)
578
+ type != other.type ? type <=> other.type : key != other.key ? key <=> other.key : to_s <=> other.to_s
579
+ end
580
+
581
+ private
582
+
583
+ # Returns a default key for this entry.
584
+ def default_key
585
+ k = names[0]
586
+ k = k.respond_to?(:family) ? k.family : k.to_s
587
+ k = k[/[A-Za-z]+/] || 'unknown'
588
+ k << (has_field?(:year) ? year : '-')
589
+ k << 'a'
590
+ k.downcase!
591
+ k
592
+ end
593
+
594
+ end
570
595
  end