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 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
new(options = {}) { |self| ... } click to toggle source

Creates a new bibliography.

# 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
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 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 = {}) click to toggle source

Parses the given string and returns a corresponding Bibliography instance.

# 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

Public Instance Methods

<<(*arguments)
Alias for: add
<=>(other) click to toggle source
# File lib/bibtex/bibliography.rb, line 516
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 #query this method does not yield to a block for additional refinement of the query.

# 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?('@', '!@')
      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 120
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 BibTeX::Entry#convert!

# 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) 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 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
Also aliased as: remove, rm
duplicates(*arguments)
duplicates?() click to toggle source
# File lib/bibtex/bibliography.rb, line 538
def duplicates?
  !select_duplicates_by.empty?
end
each() click to toggle source
# File lib/bibtex/bibliography.rb, line 148
def each
  if block_given?
    data.each(&Proc.new)
    self
  else
    to_enum
  end
end
each_entry() click to toggle source
# 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?() click to toggle source

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

# File lib/bibtex/bibliography.rb, line 249
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 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!() 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 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) click to toggle source
# File lib/bibtex/bibliography.rb, line 510
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 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) 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 399
def inspect
  "#<#{self.class} data=[#{length}]>"
end
join(filter = '') click to toggle source
# File lib/bibtex/bibliography.rb, line 284
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 260
def names
  map(&:names).flatten
end
parse_months() click to toggle source
# File lib/bibtex/bibliography.rb, line 163
def parse_months
  entries.each_value { |e| e.parse_month }
  self
end
parse_names() click to toggle source
# File lib/bibtex/bibliography.rb, line 158
def parse_names
  entries.each_value { |e| e.parse_names }
  self
end
push(*arguments)
Alias for: add
q(*arguments, &block)
Alias for: query
query() #→ returns all elements click to toggle source
query('@book') #→ returns all books
query('@entry') #→ returns all entries (books, articles etc.)
query('@*') #→ same as above
query(:first, '@book, @article')
#→ returns the first book or article or nil
query('@book[year<=2011], @article)
#→ returns all books published in 2011 or earlier and all articles
query('@book, @article) { |o| o.year == '2011' }
#→ returns all books and articles published in 2011
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) }
# 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
Also aliased as: q
remove(*arguments, &block)
Alias for: delete
rename(*arguments, &block) click to toggle source
# File lib/bibtex/bibliography.rb, line 291
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 277
def replace(filter = '')
  q(filter) { |e| e.replace(@strings.values) }
  self
end
Also aliased as: replace_strings
replace_strings(filter = '')
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) click to toggle source
# 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
Also aliased as: duplicates
sort(*arguments, &block) click to toggle source
# File lib/bibtex/bibliography.rb, line 385
def sort(*arguments, &block)
  dup.sort!(*arguments, &block)
end
sort!(*arguments, &block) click to toggle source
# File lib/bibtex/bibliography.rb, line 380
def sort!(*arguments, &block)
  data.sort!(*arguments, &block)
  self
end
sort_by!(*arguments, &block) click to toggle source
# File lib/bibtex/bibliography.rb, line 389
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 403
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 423
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 408
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 418
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 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 = {}) click to toggle source

Returns a string representation of the bibliography.

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

Returns a YAML representation of the bibliography.

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

Experimental! Returns a new Bibliography with all duplicates removed.

# File lib/bibtex/bibliography.rb, line 576
def uniq(*arguments, &block)
  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 BibTeX::Entry#digest @see @duplicates_by

# 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?() 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 255
def valid?
  !errors? && entries.values.all?(&:valid?)
end

Private Instance Methods

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