class BibTeX::Bibliography

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

Attributes

defaults[R]
data[R]
entries[R]
errors[R]
options[R]
path[RW]
strings[R]

Public Class Methods

attr_by_type(*arguments) click to toggle source

Defines a new accessor that selects elements by type.

# File lib/bibtex/bibliography.rb, line 74
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
new(options = {}) { |self| ... } click to toggle source

Creates a new bibliography.

# File lib/bibtex/bibliography.rb, line 95
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
open(path, options = {}) { |b| ... } click to toggle source

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)

# File lib/bibtex/bibliography.rb, line 49
def open(path, options = {})
  b = parse(File.read(path, encoding: 'utf-8'), options)
  b.path = path
  return b unless block_given?

  begin
    yield b
  ensure
    b.save_to(options[:out] || path)
  end
end
parse(input, options = {}) click to toggle source

Parses the given string and returns a corresponding Bibliography instance.

# File lib/bibtex/bibliography.rb, line 62
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

Public Instance Methods

<<(*arguments)
Alias for: add
<=>(other) click to toggle source
# File lib/bibtex/bibliography.rb, line 501
def <=>(other)
  other.respond_to?(:to_a) ? to_a <=> other.to_a : nil
end
>> bib[-1] click to toggle source
→ Returns the last element of the Bibliography or nil
>> bib[1,2]
→ Returns the second and third elements or nil
>> bib[1..2]
>> Same as above
>> bib[:key]
→ Returns the first entry with key 'key' or nil
>> bib['key']
→ Same as above
>> bib['@article']
→ Returns all entries of type 'article' or []
>> bib['@preamble']
→ Returns all preamble objects (this is the same as Bibliography#preambles) or []
>> bib[/ruby/]
→ Returns all objects that match 'ruby' anywhere or []
>> bib['@book[keywords=ruby]']
→ 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.

# File lib/bibtex/bibliography.rb, line 218
def [](*arguments)
  raise(ArgumentError, "wrong number of arguments (#{arguments.length} for 1..2)") unless arguments.length.between?(1, 2)

  if arguments[0].is_a?(Numeric) || arguments[0].is_a?(Range)
    data[*arguments]
  elsif arguments.length == 1
    if arguments[0].nil?
      nil
    elsif arguments[0].respond_to?(:empty?) && arguments[0].empty?
      nil
    elsif arguments[0].is_a?(Symbol)
      entries[arguments[0]]
    elsif arguments[0].respond_to?(:start_with?) && !arguments[0].start_with?('@', '!@')
      entries[arguments[0]]
    else
      query(*arguments)
    end
  else
    query(*arguments)
  end
end
add(*arguments) click to toggle source

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

# File lib/bibtex/bibliography.rb, line 121
def add(*arguments)
  Element.parse(arguments.flatten, @options).each do |element|
    data << element.added_to_bibliography(self)
  end
  self
end
Also aliased as: <<, push
convert(*filters) { |entry| ... } click to toggle source

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!

# File lib/bibtex/bibliography.rb, line 169
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) click to toggle source

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.

# File lib/bibtex/bibliography.rb, line 185
def delete(*arguments, &block)
  objects = q(*arguments, &block).map { |o| o.removed_from_bibliography(self) }
  @data -= objects
  objects.length == 1 ? objects[0] : objects
end
Also aliased as: remove, rm
duplicates(*arguments, &block)
duplicates?() click to toggle source
# File lib/bibtex/bibliography.rb, line 520
def duplicates?
  !select_duplicates_by.empty?
end
each(&block) click to toggle source
# File lib/bibtex/bibliography.rb, line 147
def each(&block)
  if block_given?
    data.each(&block)
    self
  else
    to_enum
  end
end
each_entry(&block) click to toggle source
# File lib/bibtex/bibliography.rb, line 487
def each_entry(&block)
  if block_given?
    q('@entry').each(&block)
  else
    q('@entry').to_enum
  end
end
errors?() click to toggle source

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

# File lib/bibtex/bibliography.rb, line 241
def errors?
  !errors.empty?
end
extend_initials(['Edgar Allen', 'Poe'], ['Nathaniel', 'Hawthorne']) click to toggle source
#→ 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.

# File lib/bibtex/bibliography.rb, line 294
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!() click to toggle source

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.'.

# File lib/bibtex/bibliography.rb, line 312
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

    group[:prototype] = name if group[:prototype].nil? || group[:prototype].first.to_s.length < name.first.to_s.length
  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) click to toggle source
# File lib/bibtex/bibliography.rb, line 495
def find_by_type(*types, &block)
  q(types.flatten.compact.map { |t| "@#{t}" }.join(', '), &block)
end
Also aliased as: find_by_types
find_by_types(*types, &block)
Alias for: find_by_type
group_by(*arguments, &block) click to toggle source
# File lib/bibtex/bibliography.rb, line 353
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) click to toggle source
# 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
inspect() click to toggle source
# File lib/bibtex/bibliography.rb, line 382
def inspect
  "#<#{self.class} data=[#{length}]>"
end
join(filter = '') click to toggle source
# File lib/bibtex/bibliography.rb, line 276
def join(filter = '')
  q(filter, &:join)
  self
end
Also aliased as: join_strings
join_strings(filter = '')
Alias for: join
names() click to toggle source

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

# File lib/bibtex/bibliography.rb, line 252
def names
  map(&:names).flatten
end
parse_months() click to toggle source
# File lib/bibtex/bibliography.rb, line 161
def parse_months
  entries.each_value(&:parse_month)
  self
end
parse_names() click to toggle source
# File lib/bibtex/bibliography.rb, line 156
def parse_names
  entries.each_value(&:parse_names)
  self
end
push(*arguments)
Alias for: add
#→ returns the first book or article or nil
#→ returns all books published in 2011 or earlier and all articles
#→ returns all books and articles published in 2011
#→ same as above without using a block
Alias for: query
#→ returns the first book or article or nil click to toggle source
#→ returns all books published in 2011 or earlier and all articles
#→ returns all books and articles published in 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) }
# File lib/bibtex/bibliography.rb, line 465
def query(*arguments, &block)
  case arguments.length
  when 0
    selector = :all
    q = nil
  when 1
    selector = :all
    q = arguments[0]
  when 2
    selector, q = arguments
  else
    raise ArgumentError, "wrong number of arguments (#{arguments.length} for 0..2)"
  end

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

  send(query_handler(selector), &filter)
end
Also aliased as: q
remove(*arguments, &block)
Alias for: delete
rename(*arguments, &block) click to toggle source
# File lib/bibtex/bibliography.rb, line 283
def rename(*arguments, &block)
  q('@entry') { |e| e.rename(*arguments, &block) }
  self
end
replace #→ replaces all symbols click to toggle source
replace('@string, @preamble')
#→ replaces only symbols in @string and @preamble objects

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.

# File lib/bibtex/bibliography.rb, line 269
def replace(filter = '')
  q(filter) { |e| e.replace(@strings.values) }
  self
end
Also aliased as: replace_strings
#→ replaces only symbols in @string and @preamble objects
Alias for: replace
rm(*arguments, &block)
Alias for: delete
save(options = {}) click to toggle source

Saves the bibliography to the current path.

# File lib/bibtex/bibliography.rb, line 132
def save(options = {})
  save_to(@path, options)
end
save_to(path, options = {}) click to toggle source

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

# 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, &block) click to toggle source
# File lib/bibtex/bibliography.rb, line 505
def select_duplicates_by(*arguments, &block)
  arguments = %i[year title] if arguments.empty?

  group_by(*arguments) do |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
  end.values.select { |d| d.length > 1 }
end
Also aliased as: duplicates
sort(*arguments, &block) click to toggle source
# File lib/bibtex/bibliography.rb, line 368
def sort(*arguments, &block)
  dup.sort!(*arguments, &block)
end
sort!(*arguments, &block) click to toggle source
# File lib/bibtex/bibliography.rb, line 363
def sort!(*arguments, &block)
  data.sort!(*arguments, &block)
  self
end
sort_by!(*arguments, &block) click to toggle source
# File lib/bibtex/bibliography.rb, line 372
def sort_by!(*arguments, &block)
  data.sort_by!(*arguments, &block)
  self
end
to_a(options = {}) click to toggle source
# File lib/bibtex/bibliography.rb, line 386
def to_a(options = {})
  map { |o| o.to_hash(options) }
end
to_citeproc(options = {}) click to toggle source

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

# File lib/bibtex/bibliography.rb, line 406
def to_citeproc(options = {})
  q('@entry').map { |o| o.to_citeproc(options) }
end
to_hash(options = {}) click to toggle source

Returns a Ruby hash representation of the bibliography.

# File lib/bibtex/bibliography.rb, line 391
def to_hash(options = {})
  { bibliography: map { |o| o.to_hash(options) } }
end
to_json(options = {}) click to toggle source

Returns a JSON representation of the bibliography.

# File lib/bibtex/bibliography.rb, line 401
def to_json(options = {})
  ::JSON.dump(to_a(options))
end
to_rdf(_options = {}) click to toggle source

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

# File lib/bibtex/bibliography.rb, line 429
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 = {}) click to toggle source

Returns a string representation of the bibliography.

# File lib/bibtex/bibliography.rb, line 378
def to_s(options = {})
  map { |o| o.to_s(options) }.join
end
to_xml(options = {}) click to toggle source

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

# File lib/bibtex/bibliography.rb, line 412
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 = {}) click to toggle source

Returns a YAML representation of the bibliography.

# File lib/bibtex/bibliography.rb, line 396
def to_yaml(options = {})
  to_a(options).to_yaml
end
unify :publisher, /o'?reilly/i, "O'Reilly" click to toggle source
#→ 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.

# File lib/bibtex/bibliography.rb, line 342
def unify(field, pattern, value = nil, &block)
  pattern = Regexp.new(pattern) unless pattern.is_a?(Regexp)
  block = proc { |e| e[field] = value } unless block_given?

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

  self
end
uniq(*_arguments) click to toggle source

Experimental! Returns a new Bibliography with all duplicates removed.

# File lib/bibtex/bibliography.rb, line 558
def uniq(*_arguments)
  dup.uniq!
end
uniq! → bib click to toggle source
uniq!(:title, :year) → bib
uniq! { |digest, entry| ... } → 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 Entry#digest @see @duplicates_by

# File lib/bibtex/bibliography.rb, line 545
def uniq!(*arguments, &block)
  select_duplicates_by(*arguments, &block).each do |dupes|
    dupes.shift
    dupes.each do |dupe|
      remove dupe
    end
  end

  self
end
valid?() click to toggle source

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

# File lib/bibtex/bibliography.rb, line 247
def valid?
  !errors? && entries.values.all?(&:valid?)
end

Private Instance Methods

query_handler(selector) click to toggle source
# File lib/bibtex/bibliography.rb, line 564
def query_handler(selector)
  case selector.to_s
  when /first|distinct|detect/i
    :detect
  when /none|reject|not/i
    :reject
  else
    :select
  end
end