class BibTeX::Entry
Represents a regular BibTeX
entry.
Constants
- DATE_FIELDS
- FIELD_ALIASES
Defines the default fallbacks for values defined in cross-references
- MONTHS
- MONTHS_FILTER
- NAME_FIELDS
- REQUIRED_FIELDS
Defines the required fields of the standard entry types
Attributes
Public Class Methods
Creates a new instance. If a hash is given, the entry is populated accordingly.
# File lib/bibtex/entry.rb, line 72 def initialize(attributes = {}) @fields = {} @key = nil self.type = attributes.delete(:bibtex_type) if attributes.key?(:bibtex_type) self.key = attributes.delete(:bibtex_key) if attributes.key?(:bibtex_key) add(attributes) yield self if block_given? end
Public Instance Methods
# File lib/bibtex/entry.rb, line 587 def <=>(other) type != other.type ? type <=> other.type : key != other.key ? key <=> other.key : to_s <=> other.to_s end
Returns the value of the field with the given name. If the value is not defined and the entry has cross-reference, returns the cross-referenced value instead.
# File lib/bibtex/entry.rb, line 309 def [](name) fields[name.to_sym] || parent&.provide(name) end
Adds a new field (name-value pair) to the entry. Returns the new value.
# File lib/bibtex/entry.rb, line 321 def []=(name, value) add(name.to_sym, value) end
Adds a new field (name-value pair) or multiple fields to the entry. Returns the entry for chainability.
# File lib/bibtex/entry.rb, line 334 def add(*arguments) Hash[*arguments.flatten].each_pair do |name, value| fields[name.to_sym] = Value.create(value) end self end
Called when the element was added to a bibliography.
BibTeX::Element#added_to_bibliography
# File lib/bibtex/entry.rb, line 396 def added_to_bibliography(bibliography) super @key = register(key) %i[parse_names parse_months].each do |parser| send(parser) if bibliography.options[parser] end if bibliography.options.key?(:filter) [*bibliography.options[:filter]].each do |filter| convert!(filter) end end self end
Returns the Entry’s field name aliases.
# File lib/bibtex/entry.rb, line 142 def aliases @aliases ||= FIELD_ALIASES.dup end
Returns a list of all entries in the Bibliography
containing a cross-reference to this entry or [] if there are no references to this entry.
# File lib/bibtex/entry.rb, line 536 def children bibliography&.q("@entry[crossref=#{key}]") || [] end
Returns true if this entry is published inside a book, collection or journal
# File lib/bibtex/entry.rb, line 555 def contained? has_field?(:container, :journal) || has_field?(:booktitle) && get(:booktitle) != get(:title) end
# File lib/bibtex/entry.rb, line 542 def container_title get(:booktitle) || get(:journal) || get(:container) end
Returns a string of all the entry’s fields.
# File lib/bibtex/entry.rb, line 592 def content(options = {}) fields.map { |k, _v| "#{k} = #{fields[k].to_s(options)}" }.join(",\n") end
Returns a duplicate entry with all values converted using the filter(s). If an optional block is given, only those values will be converted where the block returns true (the block will be called with each key-value pair).
@see convert!
# File lib/bibtex/entry.rb, line 572 def convert(*filters, &block) block_given? ? dup.convert!(*filters, &block) : dup.convert!(*filters) end
In-place variant of @see convert
# File lib/bibtex/entry.rb, line 577 def convert!(*filters) filters = filters.flatten.map { |f| Filters.resolve!(f) } fields.each_pair do |k, v| !block_given? || yield(k, v) ? v.convert!(*filters) : v end self end
# File lib/bibtex/entry.rb, line 478 def date get(:date) || get(:year) end
Removes the field with a given name from the entry. Returns the value of the deleted field; nil if the field was not set.
# File lib/bibtex/entry.rb, line 346 def delete(name) fields.delete(name.to_sym) end
Creates the entry’s digest based on the passed-in filters.
The digest contains the type and all key-value pairs based on the passed in filter.
If a block is given, the computed digest will be passed to the block for post-processing (the entry itself will be passed as the second parameter).
@see field_names
@param [<Symbol>] the field names to use @return [String] the digest string
# File lib/bibtex/entry.rb, line 371 def digest(filter = []) names = field_names(filter) digest = type.to_s names.zip(values_at(*names)).each do |key, value| digest << "|#{key}:#{value}" end digest = yield(digest, self) if block_given? digest end
Calls block once for each key in entry, passing the key-value pair as parameters.
If no block is given, an enumerator is returned instead.
# File lib/bibtex/entry.rb, line 130 def each(&block) if block_given? fields.each(&block) self else to_enum end end
# File lib/bibtex/entry.rb, line 315 def fetch(name, default = nil) get(name) || (block_given? ? yield(name) : default) end
Returns a sorted list of the Entry’s field names. If a filter
is passed as argument, returns all field names that are also defined by the filter. If the filter
is empty, returns all field names.
If the second optional argument is true (default) and the Entry
contains a cross-reference, the list will include all inherited fields.
# File lib/bibtex/entry.rb, line 244 def field_names(filter = [], include_inherited = true) names = fields.keys names.concat(inherited_fields) if include_inherited && has_parent? names &= filter.map(&:to_sym) unless filter.empty? names.sort! names end
Returns true if the entry is cross-referenced by another entry in the Bibliography
.
# File lib/bibtex/entry.rb, line 527 def has_children? !children.empty? end
# File lib/bibtex/entry.rb, line 187 def has_field?(*names) names.flatten.any? do |name| name.respond_to?(:to_sym) ? fields.key?(name.to_sym) : false end end
Returns true if the Entry
has a valid cross-reference in the Bibliography
.
# File lib/bibtex/entry.rb, line 503 def has_parent? !parent.nil? end
BibTeX::Element#has_type?
# File lib/bibtex/entry.rb, line 181 def has_type?(type) type.to_s.match(/^(?:entry|\*)$/i) || @type.casecmp?(type.to_sym) || super end
# File lib/bibtex/entry.rb, line 383 def identifier if provides?(:doi) "info:doi/#{get(:doi)}" elsif provides?(:isbn) "urn:isbn:#{get(:isbn)}" elsif provides?(:issn) "urn:issn:#{get(:issn)}" else "urn:bibtex:#{key}" end end
Returns a sorted list of all field names referenced by this Entry’s cross-reference.
# File lib/bibtex/entry.rb, line 256 def inherited_fields return [] unless has_parent? names = parent.fields.keys - fields.keys names.concat(parent.aliases.select { |_k, v| parent.has_field?(v) }.keys) names.sort! names end
# File lib/bibtex/entry.rb, line 195 def inherits?(*names) names.flatten.any? do |name| !has_field(name) && has_parent? && parent.provides?(name) end end
# File lib/bibtex/entry.rb, line 84 def initialize_copy(other) @fields = {} self.type = other.type self.key = other.key add(other.fields) end
# File lib/bibtex/entry.rb, line 446 def join fields.values.each(&:join) self end
# File lib/bibtex/entry.rb, line 165 def key if @key.nil? || @key.empty? @key = default_key else @key end end
Sets the Entry’s key. If the Entry
is currently registered with a Bibliography
, re-registers the Entry
with the new key; note that this may change the key value if another Entry
is already regsitered with the same key.
Returns the new key.
# File lib/bibtex/entry.rb, line 152 def key=(key) key = key.to_s if registered? bibliography.entries.delete(@key) key = register(key) end @key = key rescue StandardError => e raise BibTeXError, "failed to set key to #{key.inspect}: #{e.message}" end
# File lib/bibtex/entry.rb, line 93 def merge(other, filter = field_names) dup.merge!(other, filter) end
# File lib/bibtex/entry.rb, line 97 def merge!(other, filter = field_names) raise InvalidArgument, "failed to merge entries: type mismatch: #{type} #{other.type}" unless type == other.type other.each do |name, value| if has_field?(name) get(name).merge!(value) if filter.include?(name) else add name, value.dup end end self end
# File lib/bibtex/entry.rb, line 266 def method_missing(name, *args, &block) if fields.key?(name) fields[name] elsif name.to_s =~ /^(.+)=$/ send(:add, Regexp.last_match(1).to_sym, args[0]) elsif name =~ /^(?:convert|from)_([a-z]+)(!)?$/ Regexp.last_match(2) ? convert!(Regexp.last_match(1), &block) : convert(Regexp.last_match(1), &block) elsif has_parent? && parent.provides?(name) parent.provide(name) else super end end
# File lib/bibtex/entry.rb, line 451 def month=(month) fields[:month] = month ensure parse_month end
# File lib/bibtex/entry.rb, line 457 def month_numeric return unless has_field?(:month) return unless (num = MONTHS.index fields[:month].to_sym) num.succ end
Returns a list of all names (authors, editors, translators).
# File lib/bibtex/entry.rb, line 498 def names NAME_FIELDS.map { |k| has_field?(k) ? @fields[k].tokens : nil }.flatten.compact end
# File lib/bibtex/entry.rb, line 546 def pages_from fetch(:pages, '').split(/\D+/)[0] end
# File lib/bibtex/entry.rb, line 550 def pages_to fetch(:pages, '').split(/\D+/)[-1] end
Returns the cross-referenced Entry
from the Bibliography
or nil if this Entry
does define a cross-reference.
# File lib/bibtex/entry.rb, line 519 def parent bibliography && bibliography[fields[:crossref]] end
Returns true if the Entry
cross-references an Entry
which is not registered in the current Bibliography
.
# File lib/bibtex/entry.rb, line 511 def parent_missing? has_field?(:crossref) && !has_parent? end
# File lib/bibtex/entry.rb, line 464 def parse_month fields.delete(:month_numeric) return unless has_field?(:month) fields[:month] = MONTHS_FILTER[fields[:month]] numeric = MONTHS.index(fields[:month].to_sym) fields[:month_numeric] = Value.new(numeric.succ) if numeric self end
Parses all name values of the entry. Tries to replace and join the value prior to parsing.
# File lib/bibtex/entry.rb, line 484 def parse_names strings = bibliography ? bibliography.strings.values : [] NAME_FIELDS.each do |key| if name = fields[key] name = name.dup.replace(strings).join.to_name fields[key] = name unless name.nil? end end self end
Returns the field value referenced by the passed-in name. For example, this will return the ‘title’ value for ‘booktitle’ if a corresponding alias is defined.
# File lib/bibtex/entry.rb, line 219 def provide(name) return nil unless name.respond_to?(:to_sym) name = name.to_sym fields[name] || fields[aliases[name]] end
Returns true if the Entry
has a field (or alias) for any of the passed-in names.
# File lib/bibtex/entry.rb, line 202 def provides?(*names) names.flatten.any? do |name| if name.respond_to?(:to_sym) has_field?(name) || has_field?(aliases[name.to_sym]) else false end end end
# File lib/bibtex/entry.rb, line 212 def provides_or_inherits?(*names) provides?(names) || inherits?(names) end
Registers this Entry
in the associated Bibliographies entries hash. This method may change the Entry’s key, if another entry is already registered with the current key.
Returns the key or nil if the Entry
is not associated with a Bibliography
.
# File lib/bibtex/entry.rb, line 431 def register(key) return nil if bibliography.nil? k = key.dup k.succ! while bibliography.key?(k) bibliography.entries[k] = self k end
Returns true if the Entry
is currently registered with the associated Bibliography
.
# File lib/bibtex/entry.rb, line 422 def registered? !!(bibliography && bibliography.entries[key].equal?(self)) end
Called when the element was removed from a bibliography.
BibTeX::Element#removed_from_bibliography
# File lib/bibtex/entry.rb, line 415 def removed_from_bibliography(bibliography) super bibliography.entries.delete(key) self end
Returns a copy of the Entry
with all the field names renamed.
# File lib/bibtex/entry.rb, line 287 def rename(*arguments) dup.rename!(*arguments) end
Renames the given field names unless a field with the new name already exists.
# File lib/bibtex/entry.rb, line 293 def rename!(*arguments) Hash[*arguments.flatten].each_pair do |from, to| if fields.key?(from) && !fields.key?(to) fields[to] = fields[from] fields.delete(from) end end self end
# File lib/bibtex/entry.rb, line 440 def replace(*arguments) arguments = bibliography.q('@string') if arguments.empty? fields.values.each { |v| v.replace(*arguments) } self end
# File lib/bibtex/entry.rb, line 280 def respond_to?(method, include_all = false) provides?(method.to_sym) || method.to_s.match(/=$/) || method =~ /^(?:convert|from)_([a-z]+)(!)?$/ || (has_parent? && parent.respond_to?(method, include_all)) || super end
# File lib/bibtex/entry.rb, line 609 def to_citeproc(options = {}) CiteProcConverter.convert(self, options) end
# File lib/bibtex/entry.rb, line 602 def to_hash(options = {}) options[:quotes] ||= %w[{ }] hash = { bibtex_key: key, bibtex_type: type } each_pair { |k, v| hash[k] = v.to_s(options) } hash end
Returns a RDF::Graph representation of the entry using the BIBO ontology.
# File lib/bibtex/entry.rb, line 618 def to_rdf(_options = {}) if defined?(::RDF) RDFConverter.convert(self) else BibTeX.log.error 'Please `gem install rdf` for RDF support.' end end
Returns a string representation of the entry.
# File lib/bibtex/entry.rb, line 597 def to_s(options = {}) options[:quotes] ||= %w[{ }] ["@#{type}{#{key},", content(options).gsub(/^/, ' '), "}\n"].join("\n") end
# File lib/bibtex/entry.rb, line 613 def to_xml(options = {}) BibTeXMLConverter.convert(self, options) end
Sets the type of the entry.
# File lib/bibtex/entry.rb, line 177 def type=(type) @type = type.to_sym end
# File lib/bibtex/entry.rb, line 112 def update(fields) fields.each do |name, value| add name, value end self end
Returns false if the entry is one of the standard entry types and does not have definitions of all the required fields for that type.
# File lib/bibtex/entry.rb, line 352 def valid? REQUIRED_FIELDS[type].all? do |f| f.is_a?(Array) ? !(f & fields.keys).empty? : !fields[f].nil? end end
Returns an array containing the values associated with the given keys.
# File lib/bibtex/entry.rb, line 561 def values_at(*arguments) arguments.map do |key| get key end end
# File lib/bibtex/entry.rb, line 628 def year return fields[:year] if has_field?(:year) return unless has_field?(:date) fields[:date].to_s[/\d{4}/] end
Private Instance Methods
Returns a default key for this entry.
# File lib/bibtex/entry.rb, line 676 def default_key k = names[0] k = k.respond_to?(:family) ? k.family : k.to_s k = BibTeX.transliterate(k).gsub(/["']/, '') k = k[/[A-Za-z-]+/] || 'unknown' k << (year.to_s[/\d+/] || '-') k << 'a' k.downcase! k end