bibtex-ruby 2.0.1 → 2.0.2

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.

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