bibtex-ruby 2.1.2 → 2.2.0

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,17 +1,17 @@
1
1
  #--
2
2
  # BibTeX-Ruby
3
3
  # Copyright (C) 2010-2012 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
7
7
  # the Free Software Foundation, either version 3 of the License, or
8
8
  # (at your option) any later version.
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
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
16
  # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
17
  #++
@@ -21,7 +21,7 @@ module BibTeX
21
21
  # Represents a regular BibTeX entry.
22
22
  #
23
23
  class Entry < Element
24
- extend Forwardable
24
+ extend Forwardable
25
25
  include Enumerable
26
26
 
27
27
  # Defines the required fields of the standard entry types
@@ -47,11 +47,11 @@ module BibTeX
47
47
  :booktitle => :title,
48
48
  # :editor => :author
49
49
  }.freeze
50
-
51
-
50
+
51
+
52
52
  NAME_FIELDS = [:author,:editor,:translator].freeze
53
53
  DATE_FIELDS = [:year,:month].freeze
54
-
54
+
55
55
  MONTHS = [:jan,:feb,:mar,:apr,:may,:jun,:jul,:aug,:sep,:oct,:nov,:dec].freeze
56
56
 
57
57
  MONTHS_FILTER = Hash.new do |h,k|
@@ -64,7 +64,7 @@ module BibTeX
64
64
  h[k] = Value.new(k)
65
65
  end
66
66
  end
67
-
67
+
68
68
  CSL_FILTER = Hash.new {|h,k|k}.merge(Hash[*%w{
69
69
  date issued
70
70
  isbn ISBN
@@ -82,7 +82,7 @@ module BibTeX
82
82
  CSL_FIELDS = %w{ abstract annote archive archive_location archive-place
83
83
  authority call-number chapter-number citation-label citation-number
84
84
  collection-title container-title DOI edition event event-place
85
- first-reference-note-number genre ISBN issue jurisdiction keyword locator
85
+ first-reference-note-number genre ISBN issue jurisdiction keyword locator
86
86
  medium note number number-of-pages number-of-volumes original-publisher
87
87
  original-publisher-place original-title page page-first publisher
88
88
  publisher-place references section status title URL version volume
@@ -90,7 +90,7 @@ module BibTeX
90
90
  author editor translator recipient interviewer publisher composer
91
91
  original-publisher original-author container-author collection-editor
92
92
  }.map(&:intern).freeze
93
-
93
+
94
94
  CSL_TYPES = Hash.new {|h,k|k}.merge(Hash[*%w{
95
95
  booklet pamphlet
96
96
  conference paper-conference
@@ -136,33 +136,51 @@ module BibTeX
136
136
  article Article
137
137
  }.map(&:intern)]).freeze
138
138
 
139
-
139
+
140
140
  attr_reader :fields, :type
141
-
141
+
142
142
  def_delegators :@fields, :empty?
143
143
 
144
144
  # Creates a new instance. If a hash is given, the entry is populated accordingly.
145
145
  def initialize(attributes = {})
146
146
  @fields = {}
147
-
147
+
148
148
  self.type = attributes.delete(:type) if attributes.has_key?(:type)
149
149
  self.key = attributes.delete(:key) if attributes.has_key?(:key)
150
-
150
+
151
151
  add(attributes)
152
-
152
+
153
153
  yield self if block_given?
154
154
  end
155
155
 
156
- def initialize_copy (other)
156
+ def initialize_copy(other)
157
157
  @fields = {}
158
-
158
+
159
159
  self.type = other.type
160
160
  self.key = other.key
161
-
161
+
162
162
  add(other.fields)
163
163
  end
164
164
 
165
+ def merge(other, filter = field_names)
166
+ dup.merge!(other, filter)
167
+ end
168
+
169
+ def merge!(other, filter = field_names)
170
+ raise InvalidArgument, "failed to merge entries: type mismatch: #{type} #{other.type}" unless
171
+ type == other.type
165
172
 
173
+ other.each do |name, value|
174
+ if has_field?(name)
175
+ get(name).merge!(value) if filter.include?(name)
176
+ else
177
+ add name, value.dup
178
+ end
179
+ end
180
+
181
+ self
182
+ end
183
+
166
184
  # Generate Accessors for required fields (#52)
167
185
 
168
186
  REQUIRED_FIELDS.values.flatten.uniq.each do |name|
@@ -177,12 +195,12 @@ module BibTeX
177
195
  nil
178
196
  end
179
197
  end
180
-
198
+
181
199
  define_method("#{name}=") do |value|
182
200
  add name, value
183
201
  end
184
202
  end
185
-
203
+
186
204
  # call-seq:
187
205
  # entry.each { |key, value| block } -> entry
188
206
  # entry.each_pair { |key, value| block } -> entry
@@ -201,15 +219,15 @@ module BibTeX
201
219
  to_enum
202
220
  end
203
221
  end
204
-
222
+
205
223
  alias each_pair each
206
-
207
-
224
+
225
+
208
226
  # Returns the Entry's field name aliases.
209
227
  def aliases
210
228
  @aliases ||= FIELD_ALIASES.dup
211
229
  end
212
-
230
+
213
231
  # Sets the Entry's key. If the Entry is currently registered with a
214
232
  # Bibliography, re-registers the Entry with the new key; note that this
215
233
  # may change the key value if another Entry is already regsitered with
@@ -218,7 +236,7 @@ module BibTeX
218
236
  # Returns the new key.
219
237
  def key=(key)
220
238
  key = key.to_s
221
-
239
+
222
240
  if registered?
223
241
  bibliography.entries.delete(@key)
224
242
  key = register(key)
@@ -228,7 +246,7 @@ module BibTeX
228
246
  rescue => e
229
247
  raise BibTeXError, "failed to set key to #{key.inspect}: #{e.message}"
230
248
  end
231
-
249
+
232
250
  def key
233
251
  @key ||= default_key
234
252
  end
@@ -240,14 +258,14 @@ module BibTeX
240
258
  def type=(type)
241
259
  @type = type.to_sym
242
260
  end
243
-
261
+
244
262
  def has_type?(type)
245
263
  type.to_s.match(/^(?:entry|\*)$/i) || @type == type.to_sym || super
246
264
  end
247
-
265
+
248
266
  alias type? has_type?
249
-
250
-
267
+
268
+
251
269
  def has_field?(*names)
252
270
  names.flatten.any? do |name|
253
271
  name.respond_to?(:to_sym) ? fields.has_key?(name.to_sym) : false
@@ -261,7 +279,7 @@ module BibTeX
261
279
  !has_field(name) && has_parent? && parent.provides?(name)
262
280
  end
263
281
  end
264
-
282
+
265
283
  # Returns true if the Entry has a field (or alias) for any of the passed-in names.
266
284
  def provides?(*names)
267
285
  names.flatten.any? do |name|
@@ -272,20 +290,20 @@ module BibTeX
272
290
  end
273
291
  end
274
292
  end
275
-
293
+
276
294
  def provides_or_inherits?(*names)
277
295
  provides?(names) || inherits?(names)
278
296
  end
279
-
297
+
280
298
  # Returns the field value referenced by the passed-in name.
281
299
  # For example, this will return the 'title' value for 'booktitle' if a
282
300
  # corresponding alias is defined.
283
301
  def provide(name)
284
302
  return nil unless name.respond_to?(:to_sym)
285
- name = name.to_sym
303
+ name = name.to_sym
286
304
  fields[name] || fields[aliases[name]]
287
305
  end
288
-
306
+
289
307
  # If the Entry has a cross-reference, copies all referenced all inherited
290
308
  # values from the parent.
291
309
  #
@@ -294,10 +312,10 @@ module BibTeX
294
312
  inherited_fields.each do |name|
295
313
  fields[name] = parent.provide(name)
296
314
  end
297
-
315
+
298
316
  self
299
317
  end
300
-
318
+
301
319
  # Returns a sorted list of the Entry's field names. If a +filter+ is passed
302
320
  # as argument, returns all field names that are also defined by the filter.
303
321
  # If the +filter+ is empty, returns all field names.
@@ -306,15 +324,15 @@ module BibTeX
306
324
  # a cross-reference, the list will include all inherited fields.
307
325
  def field_names(filter = [], include_inherited = true)
308
326
  names = fields.keys
309
-
327
+
310
328
  if include_inherited && has_parent?
311
329
  names.concat(inherited_fields)
312
330
  end
313
-
331
+
314
332
  unless filter.empty?
315
333
  names = names & filter.map(&:to_sym)
316
334
  end
317
-
335
+
318
336
  names.sort!
319
337
  names
320
338
  end
@@ -322,21 +340,21 @@ module BibTeX
322
340
  # Returns a sorted list of all field names referenced by this Entry's cross-reference.
323
341
  def inherited_fields
324
342
  return [] unless has_parent?
325
-
343
+
326
344
  names = parent.fields.keys - fields.keys
327
345
  names.concat(parent.aliases.reject { |k,v| !parent.has_field?(v) }.keys)
328
346
  names.sort!
329
-
347
+
330
348
  names
331
349
  end
332
-
333
-
350
+
351
+
334
352
  def method_missing(name, *args, &block)
335
353
  case
336
354
  when fields.has_key?(name)
337
355
  fields[name]
338
356
  when name.to_s =~ /^(.+)=$/
339
- send(:add, $1.to_sym, args[0])
357
+ send(:add, $1.to_sym, args[0])
340
358
  when name =~ /^(?:convert|from)_([a-z]+)(!)?$/
341
359
  $2 ? convert!($1, &block) : convert($1, &block)
342
360
  when has_parent? && parent.provides?(name)
@@ -350,12 +368,12 @@ module BibTeX
350
368
  provides?(method.to_sym) || method.to_s.match(/=$/) ||
351
369
  method =~ /^(?:convert|from)_([a-z]+)(!)?$/ || (has_parent? && parent.respond_to?(method)) || super
352
370
  end
353
-
371
+
354
372
  # Returns a copy of the Entry with all the field names renamed.
355
373
  def rename(*arguments)
356
374
  dup.rename!(*arguments)
357
375
  end
358
-
376
+
359
377
  # Renames the given field names unless a field with the new name already
360
378
  # exists.
361
379
  def rename!(*arguments)
@@ -370,16 +388,16 @@ module BibTeX
370
388
 
371
389
  alias rename_fields rename
372
390
  alias rename_fields! rename!
373
-
391
+
374
392
  # Returns the value of the field with the given name. If the value is not
375
393
  # defined and the entry has cross-reference, returns the cross-referenced
376
394
  # value instead.
377
395
  def [](name)
378
396
  fields[name.to_sym] || parent && parent.provide(name)
379
397
  end
380
-
398
+
381
399
  alias get []
382
-
400
+
383
401
  def fetch(name, default = nil)
384
402
  get(name) || block_given? ? yield(name) : default
385
403
  end
@@ -395,11 +413,11 @@ module BibTeX
395
413
  define_method(contributor) do
396
414
  get(contributor)
397
415
  end
398
-
416
+
399
417
  alias_method "#{contributor}s", contributor
400
418
  end
401
-
402
-
419
+
420
+
403
421
  # call-seq:
404
422
  # add(:author, "Edgar A. Poe")
405
423
  # add(:author, "Edgar A. Poe", :title, "The Raven")
@@ -413,10 +431,10 @@ module BibTeX
413
431
  Hash[*arguments.flatten].each_pair do |name, value|
414
432
  fields[name.to_sym] = Value.create(value)
415
433
  end
416
-
434
+
417
435
  self
418
436
  end
419
-
437
+
420
438
  alias << add
421
439
 
422
440
  # Removes the field with a given name from the entry.
@@ -433,10 +451,31 @@ module BibTeX
433
451
  end
434
452
  end
435
453
 
436
- def generate_hash(filter = [])
437
- Digest::MD5.hexdigest(field_names(filter).map { |k| [k, fields[k]] }.flatten.join)
454
+ # Creates the entry's digest based on the passed-in filters.
455
+ #
456
+ # The digest contains the type and all key-value pairs based
457
+ # on the passed in filter.
458
+ #
459
+ # If a block is given, the computed digest will be passed to
460
+ # the block for post-processing (the entry itself will be passed
461
+ # as the second parameter).
462
+ #
463
+ # @see #field_names
464
+ #
465
+ # @param [<Symbol>] the field names to use
466
+ # @return [String] the digest string
467
+ def digest(filter = [])
468
+ names = field_names(filter)
469
+ digest = type.to_s
470
+
471
+ names.zip(values_at(*names)).each do |key, value|
472
+ digest << "|#{key}:#{value}"
473
+ end
474
+
475
+ digest = yield(digest, self) if block_given?
476
+ digest
438
477
  end
439
-
478
+
440
479
  def identifier
441
480
  case
442
481
  when provides?(:doi)
@@ -449,23 +488,23 @@ module BibTeX
449
488
  "urn:bibtex:#{key}"
450
489
  end
451
490
  end
452
-
491
+
453
492
  # Called when the element was added to a bibliography.
454
493
  def added_to_bibliography(bibliography)
455
494
  super
456
495
 
457
496
  @key = register(key)
458
-
497
+
459
498
  [:parse_names, :parse_months].each do |parser|
460
499
  send(parser) if bibliography.options[parser]
461
500
  end
462
-
501
+
463
502
  if bibliography.options.has_key?(:filter)
464
503
  [*bibliography.options[:filter]].each do |filter|
465
504
  convert!(filter)
466
505
  end
467
506
  end
468
-
507
+
469
508
  self
470
509
  end
471
510
 
@@ -480,7 +519,7 @@ module BibTeX
480
519
  def registered?
481
520
  !!(bibliography && bibliography.entries[key].equal?(self))
482
521
  end
483
-
522
+
484
523
  # Registers this Entry in the associated Bibliographies entries hash.
485
524
  # This method may change the Entry's key, if another entry is already
486
525
  # registered with the current key.
@@ -488,13 +527,13 @@ module BibTeX
488
527
  # Returns the key or nil if the Entry is not associated with a Bibliography.
489
528
  def register(key)
490
529
  return nil if bibliography.nil?
491
-
530
+
492
531
  k = key.dup
493
532
  k.succ! while bibliography.has_key?(k)
494
533
  bibliography.entries[k] = self
495
534
  k
496
535
  end
497
-
536
+
498
537
  def replace(*arguments)
499
538
  arguments = bibliography.q('@string') if arguments.empty?
500
539
  fields.values.each { |v| v.replace(*arguments) }
@@ -509,18 +548,18 @@ module BibTeX
509
548
  def month=(month)
510
549
  fields[:month] = MONTHS_FILTER[month]
511
550
  end
512
-
551
+
513
552
  def parse_month
514
553
  fields[:month] = MONTHS_FILTER[fields[:month]] if has_field?(:month)
515
554
  self
516
555
  end
517
-
556
+
518
557
  alias parse_months parse_month
519
-
558
+
520
559
  def date
521
560
  get(:date) || get(:year)
522
561
  end
523
-
562
+
524
563
  # Parses all name values of the entry. Tries to replace and join the
525
564
  # value prior to parsing.
526
565
  def parse_names
@@ -535,34 +574,34 @@ module BibTeX
535
574
 
536
575
  self
537
576
  end
538
-
577
+
539
578
  # Returns a list of all names (authors, editors, translators).
540
579
  def names
541
580
  NAME_FIELDS.map { |k| has_field?(k) ? @fields[k].tokens : nil }.flatten.compact
542
581
  end
543
-
544
-
582
+
583
+
545
584
  # Returns true if the Entry has a valid cross-reference in the Bibliography.
546
585
  def has_parent?
547
586
  !parent.nil?
548
587
  end
549
588
 
550
- alias has_cross_reference? has_parent?
589
+ alias has_cross_reference? has_parent?
551
590
 
552
591
  # Returns true if the Entry cross-references an Entry which is not
553
592
  # registered in the current Bibliography.
554
593
  def parent_missing?
555
594
  has_field?(:crossref) && !has_parent?
556
595
  end
557
-
596
+
558
597
  alias cross_reference_missing? parent_missing?
559
-
598
+
560
599
  # Returns the cross-referenced Entry from the Bibliography or nil if this
561
600
  # Entry does define a cross-reference.
562
601
  def parent
563
602
  bibliography && bibliography[fields[:crossref]]
564
603
  end
565
-
604
+
566
605
  alias cross_reference parent
567
606
 
568
607
 
@@ -571,9 +610,9 @@ module BibTeX
571
610
  def has_children?
572
611
  !children.empty?
573
612
  end
574
-
613
+
575
614
  alias cross_referenced? has_children?
576
-
615
+
577
616
  # Returns a list of all entries in the Bibliography containing a
578
617
  # cross-reference to this entry or [] if there are no references to this
579
618
  # entry.
@@ -582,11 +621,11 @@ module BibTeX
582
621
  end
583
622
 
584
623
  alias cross_referenced_by children
585
-
624
+
586
625
  def container_title
587
626
  get(:booktitle) || get(:journal) || get(:container)
588
627
  end
589
-
628
+
590
629
  def pages_from
591
630
  fetch(:pages, '').split(/\D+/)[0]
592
631
  end
@@ -594,13 +633,19 @@ module BibTeX
594
633
  def pages_to
595
634
  fetch(:pages, '').split(/\D+/)[-1]
596
635
  end
597
-
636
+
598
637
  # Returns true if this entry is published inside a book, collection or journal
599
638
  def contained?
600
639
  has_field?(:booktitle, :container, :journal)
601
640
  end
602
-
603
-
641
+
642
+ # Returns an array containing the values associated with the given keys.
643
+ def values_at(*arguments)
644
+ arguments.map do |key|
645
+ get key
646
+ end
647
+ end
648
+
604
649
  # Returns a duplicate entry with all values converted using the filter.
605
650
  # If an optional block is given, only those values will be converted where
606
651
  # the block returns true (the block will be called with each key-value pair).
@@ -609,18 +654,18 @@ module BibTeX
609
654
  def convert(filter)
610
655
  block_given? ? dup.convert!(filter, &Proc.new) : dup.convert!(filter)
611
656
  end
612
-
657
+
613
658
  # In-place variant of @see #convert
614
659
  def convert!(filter)
615
660
  fields.each_pair { |k,v| !block_given? || yield(k,v) ? v.convert!(filter) : v }
616
661
  self
617
662
  end
618
-
663
+
619
664
  def <=>(other)
620
665
  type != other.type ? type <=> other.type : key != other.key ? key <=> other.key : to_s <=> other.to_s
621
666
  end
622
-
623
-
667
+
668
+
624
669
  # Returns a string of all the entry's fields.
625
670
  def content(options = {})
626
671
  fields.map { |k,v| "#{k} = #{ fields[k].to_s(options) }" }.join(",\n")
@@ -631,7 +676,7 @@ module BibTeX
631
676
  options[:quotes] ||= %w({ })
632
677
  ["@#{type}{#{key},", content(options).gsub(/^/,' '), "}\n"].join("\n")
633
678
  end
634
-
679
+
635
680
  def to_hash(options = {})
636
681
  options[:quotes] ||= %w({ })
637
682
  hash = { :key => key, :type => type }
@@ -641,32 +686,32 @@ module BibTeX
641
686
 
642
687
  def to_citeproc(options = {})
643
688
  options[:quotes] ||= []
644
-
689
+
645
690
  parse_names
646
691
  parse_month
647
-
692
+
648
693
  hash = { 'id' => key.to_s, 'type' => CSL_TYPES[type].to_s }
649
-
694
+
650
695
  each_pair do |k,v|
651
696
  hash[CSL_FILTER[k].to_s] = v.to_citeproc(options) unless DATE_FIELDS.include?(k)
652
697
  end
653
-
698
+
654
699
  hash['issued'] = citeproc_date
655
700
  hash
656
701
  end
657
-
702
+
658
703
  def issued
659
704
  m = MONTHS.find_index(fields[:month].to_s.intern) unless !has_field?(:month)
660
705
  m = m + 1 unless m.nil?
661
-
706
+
662
707
  Hash['date-parts', [[fields[:year],m].compact.map(&:to_i)]]
663
708
  end
664
-
709
+
665
710
  alias citeproc_date issued
666
-
711
+
667
712
  def to_xml(options = {})
668
713
  require 'rexml/document'
669
-
714
+
670
715
  xml = REXML::Element.new('bibtex:entry')
671
716
  xml.attributes['id'] = key
672
717
 
@@ -674,54 +719,54 @@ module BibTeX
674
719
 
675
720
  fields.each do |key, value|
676
721
  field = REXML::Element.new("bibtex:#{key}")
677
-
722
+
678
723
  if options[:extended] && value.name?
679
724
  value.each { |n| entry.add_element(n.to_xml) }
680
725
  else
681
726
  field.text = value.to_s(options)
682
727
  end
683
-
728
+
684
729
  entry.add_element(field)
685
730
  end
686
731
 
687
732
  xml.add_element(entry)
688
733
  xml
689
734
  end
690
-
735
+
691
736
  # Returns a RDF::Graph representation of the entry using the BIBO ontology.
692
737
  # TODO: improve level of detail captured by export
693
738
  def to_rdf(options = {})
694
739
  require 'rdf'
695
-
740
+
696
741
  bibo = RDF::Vocabulary.new('http://purl.org/ontology/bibo/')
697
-
742
+
698
743
  graph = RDF::Graph.new
699
744
  entry = RDF::URI.new(identifier)
700
745
 
701
746
  graph << [entry, RDF.type, bibo[BIBO_TYPES[type]]]
702
-
703
- [:title, :language].each do |key|
747
+
748
+ [:title, :language].each do |key|
704
749
  graph << [entry, RDF::DC[key], get(key).to_s] if field?(key)
705
750
  end
706
751
 
707
752
  graph << [entry, RDF::DC.date, get(:year).to_s] if field?(:year)
708
-
753
+
709
754
  if field?(:publisher)
710
755
  address = RDF::Vocabulary.new('http://schemas.talis.com/2005/address/schema#')
711
756
  pub = RDF::Node.new
712
757
 
713
758
  graph << [pub, RDF.type, RDF::FOAF[:Organization]]
714
759
  graph << [pub, RDF::FOAF.name, get(:publisher)]
715
-
760
+
716
761
  graph << [pub, address[:localityName], get(:address)] if field?(:address)
717
-
762
+
718
763
  graph << [entry, RDF::DC.published, pub]
719
764
  end
720
765
 
721
- [:doi, :edition, :volume].each do |key|
766
+ [:doi, :edition, :volume].each do |key|
722
767
  graph << [entry, bibo[key], get(key).to_s] if field?(key)
723
768
  end
724
-
769
+
725
770
  if has_field?(:pages)
726
771
  if get(:pages).to_s =~ /^\s*(\d+)\s*-+\s*(\d+)\s*$/
727
772
  graph << [entry, bibo[:pageStart], $1]
@@ -736,13 +781,13 @@ module BibTeX
736
781
 
737
782
  graph << [seq, RDF.type, RDF[:Seq]]
738
783
  graph << [entry, bibo[:authorList], seq]
739
-
784
+
740
785
  authors.each do |author|
741
786
  a = RDF::Node.new
742
-
787
+
743
788
  graph << [a, RDF.type, RDF::FOAF[:Person]]
744
-
745
- if author.is_a?(Name)
789
+
790
+ if author.is_a?(Name)
746
791
  [:given, :family, :prefix, :suffix].each do |part|
747
792
  graph << [a, bibo["#{part}Name"], author.send(part).to_s]
748
793
  end
@@ -778,30 +823,30 @@ module BibTeX
778
823
  graph << [seq, RDF.li, e]
779
824
  end
780
825
  end
781
-
826
+
782
827
  graph
783
828
  rescue LoadError
784
829
  BibTeX.log.error "Please gem install rdf for RDF support."
785
830
  end
786
-
831
+
787
832
  alias to_bibo to_rdf
788
-
789
-
833
+
834
+
790
835
 
791
836
 
792
837
  private
793
-
838
+
794
839
  # Returns a default key for this entry.
795
840
  def default_key
796
841
  k = names[0]
797
842
  k = k.respond_to?(:family) ? k.family : k.to_s
798
843
  k = k[/[A-Za-z]+/] || 'unknown'
799
844
  k << (has_field?(:year) ? year : '-')
800
- k << 'a'
845
+ k << 'a'
801
846
  k.downcase!
802
847
  k
803
848
  end
804
-
805
-
849
+
850
+
806
851
  end
807
852
  end