Class: BibTeX::Bibliography

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Includes:
Comparable, Enumerable
Defined in:
lib/bibtex/bibliography.rb

Overview

The Bibliography class models a BibTeX bibliography; typically, it corresponds to a `.bib' file.

Defined Under Namespace

Classes: RDFConverter

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) {|_self| ... } ⇒ Bibliography

Creates a new bibliography.

Yields:

  • (_self)

Yield Parameters:


97
98
99
100
101
102
103
# File 'lib/bibtex/bibliography.rb', line 97

def initialize(options = {})
  @options = Bibliography.defaults.merge(options)
  @data, @strings, @errors = [], {}, []
  @entries = Hash.new { |h,k| h.fetch(k.to_s, nil) }

  yield self if block_given?
end

Class Attribute Details

.defaultsObject (readonly)

Returns the value of attribute defaults


35
36
37
# File 'lib/bibtex/bibliography.rb', line 35

def defaults
  @defaults
end

Instance Attribute Details

#dataObject (readonly)

Returns the value of attribute data


86
87
88
# File 'lib/bibtex/bibliography.rb', line 86

def data
  @data
end

#entriesObject (readonly)

Returns the value of attribute entries


86
87
88
# File 'lib/bibtex/bibliography.rb', line 86

def entries
  @entries
end

#errorsObject (readonly)

Returns the value of attribute errors


86
87
88
# File 'lib/bibtex/bibliography.rb', line 86

def errors
  @errors
end

#optionsObject (readonly)

Returns the value of attribute options


86
87
88
# File 'lib/bibtex/bibliography.rb', line 86

def options
  @options
end

#pathObject

Returns the value of attribute path


84
85
86
# File 'lib/bibtex/bibliography.rb', line 84

def path
  @path
end

#stringsObject (readonly)

Returns the value of attribute strings


86
87
88
# File 'lib/bibtex/bibliography.rb', line 86

def strings
  @strings
end

Class Method Details

.attr_by_type(*arguments) ⇒ Object

Defines a new accessor that selects elements by type.


76
77
78
79
80
81
# File 'lib/bibtex/bibliography.rb', line 76

def attr_by_type(*arguments)
  arguments.each do |type|
    method_id = "#{type}s"
    define_method(method_id) { find_by_type(type) } unless respond_to?(method_id)
  end
end

.open(path, options = {}) ⇒ Object

Opens and parses the `.bib' file at the given path. Returns a new Bibliography instance corresponding to the file, or, if a block is given, yields the instance to the block, ensuring that the file is saved after the block's execution (use the :out option if you want to specify a save path other than the path from where the file is loaded).

The options argument is passed on to BibTeX::Parser.new. Additional option parameters are:

-:parse_names: set to false to disable automatic name parsing -:parse_months: set to false to disable automatic month conversion -:filter: convert all entries using the sepcified filter (not set by default)


51
52
53
54
55
56
57
58
59
60
61
# File 'lib/bibtex/bibliography.rb', line 51

def open(path, options = {})
  b = parse(Kernel.open(path, 'r:UTF-8').read, options)
  b.path = path
  return b unless block_given?

  begin
    yield b
  ensure
    b.save_to(options[:out] || path)
  end
end

.parse(input, options = {}) ⇒ Object

Parses the given string and returns a corresponding Bibliography instance.


64
65
66
67
68
69
70
71
72
73
# File 'lib/bibtex/bibliography.rb', line 64

def parse(input, options = {})
  case input
  when Array, Hash, Element
    Bibliography.new(options).add(input)
  when ::String
    Parser.new(options).parse(input) || Bibliography.new(options)
  else
    raise ArgumentError, "failed to parse #{input.inspect}"
  end
end

Instance Method Details

#<=>(other) ⇒ Object


516
517
518
# File 'lib/bibtex/bibliography.rb', line 516

def <=>(other)
  other.respond_to?(:to_a) ? to_a <=> other.to_a : nil
end

#[](*arguments) ⇒ Object

call-seq: >> bib

> Returns the last element of the Bibliography or nil

>> bib

> Returns the second and third elements or nil

>> bib >> Same as above >> bib

> Returns the first entry with key 'key' or nil

>> bib

> Same as above

>> bib

> Returns all entries of type 'article' or []

>> bib

> Returns all preamble objects (this is the same as Bibliography#preambles) or []

>> bib

> Returns all objects that match 'ruby' anywhere or []

>> bib[‘@book']

> Returns all books whose keywords attribute equals 'ruby' or []

Returns an element or a list of elements according to the given index, range, or query. Contrary to the Bibliography#query this method does not yield to a block for additional refinement of the query.

Raises:


223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# File 'lib/bibtex/bibliography.rb', line 223

def [](*arguments)
  raise(ArgumentError, "wrong number of arguments (#{arguments.length} for 1..2)") unless arguments.length.between?(1,2)

  case
  when arguments[0].is_a?(Numeric) || arguments[0].is_a?(Range)
    data[*arguments]
  when arguments.length == 1
    case
    when arguments[0].nil?
      nil
    when arguments[0].respond_to?(:empty?) && arguments[0].empty?
      nil
    when arguments[0].is_a?(Symbol)
      entries[arguments[0]]
    when arguments[0].respond_to?(:start_with?) && !arguments[0].start_with?('@', '[email protected]')
      entries[arguments[0]]
    else
      query(*arguments)
    end
  else
    query(*arguments)
  end
end

#add(*arguments) ⇒ Object Also known as: <<, push

Adds a new element, or a list of new elements to the bibliography. Returns the Bibliography for chainability.


120
121
122
123
124
125
# File 'lib/bibtex/bibliography.rb', line 120

def add(*arguments)
  Element.parse(arguments.flatten, @options).each do |element|
    data << element.added_to_bibliography(self)
  end
  self
end

#convert(*filters) ⇒ Object

Converts all enties using the given filter(s). If an optional block is given the block is used as a condition (the block will be called with each entry). @see Entry#convert!


172
173
174
175
176
177
178
179
180
# File 'lib/bibtex/bibliography.rb', line 172

def convert(*filters)
  filters = filters.flatten.map { |f| Filters.resolve!(f) }

  entries.each_value do |entry|
    entry.convert!(*filters) if !block_given? || yield(entry)
  end

  self
end

#delete(*arguments, &block) ⇒ Object Also known as: remove, rm

Deletes an object, or a list of objects from the bibliography. If a list of objects is to be deleted, you can either supply the list of objects or use a query or block to define the list.

Returns the object (or the list of objects) that were deleted; nil if the object was not part of the bibliography.


189
190
191
192
193
# File 'lib/bibtex/bibliography.rb', line 189

def delete(*arguments, &block)
  objects = q(*arguments, &block).map { |o| o.removed_from_bibliography(self) }
  @data = @data - objects
  objects.length == 1 ? objects[0] : objects
end

#duplicates?Boolean

Returns:

  • (Boolean)

538
539
540
# File 'lib/bibtex/bibliography.rb', line 538

def duplicates?
  !select_duplicates_by.empty?
end

#eachObject


148
149
150
151
152
153
154
155
# File 'lib/bibtex/bibliography.rb', line 148

def each
  if block_given?
    data.each(&Proc.new)
    self
  else
    to_enum
  end
end

#each_entryObject


502
503
504
505
506
507
508
# File 'lib/bibtex/bibliography.rb', line 502

def each_entry
  if block_given?
    q('@entry').each(&Proc.new)
  else
    q('@entry').to_enum
  end
end

#errors?Boolean

Returns true if there are object which could not be parsed.

Returns:

  • (Boolean)

249
250
251
# File 'lib/bibtex/bibliography.rb', line 249

def errors?
  !errors.empty?
end

#extend_initials(*arguments) ⇒ Object

call-seq:

b.extend_initials(['Edgar Allen', 'Poe'], ['Nathaniel', 'Hawthorne'])
#=> Extends the initials in names like 'E.A. Poe' or 'Hawethorne, N.'
    in the bibliography.

Extends the initials for the given names. Returns the Bibliography.


302
303
304
305
306
307
308
309
310
# File 'lib/bibtex/bibliography.rb', line 302

def extend_initials(*arguments)
  arguments.each do |with_first, for_last|
    names.each do |name|
      name.extend_initials(with_first, for_last)
    end
  end

  self
end

#extend_initials!Object

This method combines all names in the bibliography that look identical when using initials as first names and then tries to extend the first names for all names in each group to the longest available form. Returns the bibliography.

If your bibliography contains the names 'Poe, Edgar A.', 'Poe, E.A.', and 'Poe, E. A.' calling this method would convert all three names to 'Poe, Edgar A.'.


320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
# File 'lib/bibtex/bibliography.rb', line 320

def extend_initials!
  groups = Hash.new do |h,k|
    h[k] = { :prototype => nil, :names => [] }
  end

  # group names together
  names.each do |name|
    group = groups[name.sort_order(:initials => true).downcase]
    group[:names] << name

    if group[:prototype].nil? || group[:prototype].first.to_s.length < name.first.to_s.length
      group[:prototype] = name
    end
  end

  # extend all names in group to prototype
  groups.each_value do |group|
    group[:names].each do |name|
      name.set(group[:prototype])
    end
  end

  self
end

#find_by_type(*types, &block) ⇒ Object Also known as: find_by_types


510
511
512
# File 'lib/bibtex/bibliography.rb', line 510

def find_by_type(*types, &block)
  q(types.flatten.compact.map { |t| "@#{t}" }.join(', '), &block)
end

#group_by(*arguments, &block) ⇒ Object


370
371
372
373
374
375
376
377
378
# File 'lib/bibtex/bibliography.rb', line 370

def group_by(*arguments, &block)
  groups = Hash.new { |h,k| h[k] = [] }

  entries.values.each do |e|
    groups[e.digest(arguments, &block)] << e
  end

  groups
end

#initialize_copy(other) ⇒ Object


105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/bibtex/bibliography.rb', line 105

def initialize_copy(other)
  @options = other.options.dup
  @errors = other.errors.dup
  @data, @strings = [], {}
  @entries = Hash.new { |h,k| h.fetch(k.to_s, nil) }

  other.each do |element|
    add element.dup
  end

  self
end

#inspectObject


399
400
401
# File 'lib/bibtex/bibliography.rb', line 399

def inspect
  "#<#{self.class} data=[#{length}]>"
end

#join(filter = '') ⇒ Object Also known as: join_strings


284
285
286
287
# File 'lib/bibtex/bibliography.rb', line 284

def join(filter = '')
  q(filter, &:join)
  self
end

#namesObject

Returns a list of the names of all authors, editors and translators in the Bibliography.


260
261
262
# File 'lib/bibtex/bibliography.rb', line 260

def names
  map(&:names).flatten
end

#parse_monthsObject


163
164
165
166
# File 'lib/bibtex/bibliography.rb', line 163

def parse_months
  entries.each_value { |e| e.parse_month }
  self
end

#parse_namesObject


158
159
160
161
# File 'lib/bibtex/bibliography.rb', line 158

def parse_names
  entries.each_value { |e| e.parse_names }
  self
end

#query(*arguments, &block) ⇒ Object Also known as: q

call-seq:

bib.query()          #=> returns all elements
bib.query('@book')   #=> returns all books
bib.query('@entry')  #=> returns all entries (books, articles etc.)
bib.query('@*')      #=> same as above
bib.query(:first, '@book, @article')
  #=> returns the first book or article or nil
bib.query('@book[year<=2011], @article)
  #=> returns all books published in 2011 or earlier and all articles
bib.query('@book, @article) { |o| o.year == '2011' }
  #=> returns all books and articles published in 2011
bib.query('@book[year=2011], @article[year=2011])
  #=> same as above without using a block

Returns objects in the Bibliography which match the given selector and, optionally, the conditions specified in the given block.

Queries offer syntactic sugar for common enumerator invocations:

>> bib.query(:all, '@book')
=> same as bib.select { |b| b.has_type?(:book) }
>> bib.query('@book')
=> same as above
>> bib.query(:first, '@book')
=> same as bib.detect { |b| b.has_type?(:book) }
>> bib.query(:none, '@book')
=> same as bib.reject { |b| b.has_type?(:book) }

482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
# File 'lib/bibtex/bibliography.rb', line 482

def query(*arguments, &block)
  case arguments.length
  when 0
    selector, q = :all, nil
  when 1
    selector, q = :all, arguments[0]
  when 2
    selector, q = arguments
  else
    raise ArgumentError, "wrong number of arguments (#{arguments.length} for 0..2)"
  end

  filter = block ? Proc.new { |e| e.match?(q) && block.call(e) } :
    Proc.new { |e| e.match?(q) }

  send(query_handler(selector), &filter)
end

#rename(*arguments, &block) ⇒ Object


291
292
293
294
# File 'lib/bibtex/bibliography.rb', line 291

def rename(*arguments, &block)
  q('@entry') { |e| e.rename(*arguments, &block) }
  self
end

#replace(filter = '') ⇒ Object Also known as: replace_strings

Replaces all string symbols which are defined in the bibliography.

By default symbols in @string, @preamble and entries are replaced; this behaviour can be changed using the optional query parameter.

Note that strings are replaced in the order in which they occur in the bibliography.

call-seq: bib.replace #=> replaces all symbols bib.replace('@string, @preamble') #=> replaces only symbols in @string and @preamble objects


277
278
279
280
# File 'lib/bibtex/bibliography.rb', line 277

def replace(filter = '')
  q(filter) { |e| e.replace(@strings.values) }
  self
end

#save(options = {}) ⇒ Object

Saves the bibliography to the current path.


132
133
134
# File 'lib/bibtex/bibliography.rb', line 132

def save(options = {})
  save_to(@path, options)
end

#save_to(path, options = {}) ⇒ Object

Saves the bibliography to a file at the given path. Returns the bibliography.


137
138
139
140
141
142
143
144
145
# File 'lib/bibtex/bibliography.rb', line 137

def save_to(path, options = {})
  options[:quotes] ||= %w({ })

  File.open(path, 'w:UTF-8') do |f|
    f.write(to_s(options))
  end

  self
end

#select_duplicates_by(*arguments) ⇒ Object Also known as: duplicates


520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
# File 'lib/bibtex/bibliography.rb', line 520

def select_duplicates_by(*arguments)
  arguments = [:year, :title] if arguments.empty?
  block = Proc.new if block_given?

  group_by(*arguments) { |digest, entry|

    # 1.8 compatibility
    # digest = digest[0] if digest.is_a?(Array)

    digest.gsub(/\s+/, '').downcase
    digest = block.call(digest, entry) unless block.nil?
    digest

  }.values.select { |d| d.length > 1 }
end

#sort(*arguments, &block) ⇒ Object


385
386
387
# File 'lib/bibtex/bibliography.rb', line 385

def sort(*arguments, &block)
  dup.sort!(*arguments, &block)
end

#sort!(*arguments, &block) ⇒ Object


380
381
382
383
# File 'lib/bibtex/bibliography.rb', line 380

def sort!(*arguments, &block)
  data.sort!(*arguments, &block)
  self
end

#sort_by!(*arguments, &block) ⇒ Object


389
390
391
392
# File 'lib/bibtex/bibliography.rb', line 389

def sort_by!(*arguments, &block)
  data.sort_by!(*arguments, &block)
  self
end

#to_a(options = {}) ⇒ Object


403
404
405
# File 'lib/bibtex/bibliography.rb', line 403

def to_a(options = {})
  map { |o| o.to_hash(options) }
end

#to_citeproc(options = {}) ⇒ Object

Returns a CiteProc JSON representation of the bibliography. Only BibTeX enrties are exported.


423
424
425
# File 'lib/bibtex/bibliography.rb', line 423

def to_citeproc(options = {})
  q('@entry').map { |o| o.to_citeproc(options) }
end

#to_hash(options = {}) ⇒ Object

Returns a Ruby hash representation of the bibliography.


408
409
410
# File 'lib/bibtex/bibliography.rb', line 408

def to_hash(options = {})
  { :bibliography => map { |o| o.to_hash(options) } }
end

#to_json(options = {}) ⇒ Object

Returns a JSON representation of the bibliography.


418
419
420
# File 'lib/bibtex/bibliography.rb', line 418

def to_json(options = {})
  ::JSON.dump(to_a(options))
end

#to_rdf(options = {}) ⇒ Object

Returns an RDF::Graph representation of the bibliography. The graph can be serialized using any of the RDF serializer plugins.


446
447
448
449
450
451
452
# File 'lib/bibtex/bibliography.rb', line 446

def to_rdf(options = {})
  if defined?(::RDF)
    RDFConverter.convert(self)
  else
    BibTeX.log.error 'Please `gem install rdf` for RDF support.'
  end
end

#to_s(options = {}) ⇒ Object

Returns a string representation of the bibliography.


395
396
397
# File 'lib/bibtex/bibliography.rb', line 395

def to_s(options = {})
  map { |o| o.to_s(options) }.join
end

#to_xml(options = {}) ⇒ Object

Returns a REXML::Document representation of the bibliography using the BibTeXML format.


429
430
431
432
433
434
435
436
437
438
439
440
441
442
# File 'lib/bibtex/bibliography.rb', line 429

def to_xml(options = {})
  require 'rexml/document'

  xml =  REXML::Document.new
  xml << REXML::XMLDecl.new('1.0','UTF-8')

  root = REXML::Element.new('bibtex:file')
  root.add_namespace('bibtex', 'http://bibtexml.sf.net/')

  each { |e| root.add_element(e.to_xml(options)) if e.is_a?(Entry) }

  xml.add_element(root)
  xml
end

#to_yaml(options = {}) ⇒ Object

Returns a YAML representation of the bibliography.


413
414
415
# File 'lib/bibtex/bibliography.rb', line 413

def to_yaml(options = {})
  to_a(options).to_yaml
end

#unify(field, pattern, value = nil) ⇒ Object

call-seq:

b.unify :publisher, /o'?reilly/i, "O'Reilly"
#=> Unifies the publisher name "O'Reilly" in the bibliography

Sets all fields matching the passed-in pattern to the supplied value. If a block is given, each matching entry will be passed to the block instead. Returns the bibliography.


352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
# File 'lib/bibtex/bibliography.rb', line 352

def unify(field, pattern, value = nil)
  pattern = Regexp.new(pattern) unless pattern.is_a?(Regexp)

  block = if block_given?
    Proc.new
  else
    Proc.new { |e| e[field] = value }
  end

  each_entry do |entry|
    if entry.field?(field) && entry[field].to_s =~ pattern
      block.call(entry)
    end
  end

  self
end

#uniq(*arguments, &block) ⇒ Object

Experimental! Returns a new Bibliography with all duplicates removed.


576
577
578
# File 'lib/bibtex/bibliography.rb', line 576

def uniq(*arguments, &block)
  dup.uniq!
end

#uniq!(*arguments, &block) ⇒ Object

call-seq:

bib.uniq!                                 -> bib
bib.uniq!(:title, :year)                  -> bib
bib.uniq! { |digest, entry| ... }         -> bib
bib.uniq!(:title) { |digest, entry| ... } -> bib

Removes duplicate entries from the Bibliography.

All arguments given will be used to calculate a digest for each entry. If a block is given, it will be be passed the computed digest as well as each entry; the block must then yield the final digest that will be used to compute duplicates.

This method will always retain the first entry and will discard subsequent duplicates on the basis of each entry's digest.

See Also:


563
564
565
566
567
568
569
570
571
572
# File 'lib/bibtex/bibliography.rb', line 563

def uniq!(*arguments, &block)
  select_duplicates_by(*arguments, &block).each do |dupes|
    dupes.shift
    dupes.each do |dupe|
      self.remove dupe
    end
  end

  self
end

#valid?Boolean

Returns true if the Bibliography contains no errors and only valid BibTeX objects (meta content is ignored).

Returns:

  • (Boolean)

255
256
257
# File 'lib/bibtex/bibliography.rb', line 255

def valid?
  !errors? && entries.values.all?(&:valid?)
end