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 624 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 346 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 358 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 371 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 433 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 179 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 573 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 592 def contained? has_field?(:container, :journal) || has_field?(:booktitle) && get(:booktitle) != get(:title) end
# File lib/bibtex/entry.rb, line 579 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 629 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 609 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 614 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 515 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 383 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 408 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 167 def each(&block) if block_given? fields.each(&block) self else to_enum end end
# File lib/bibtex/entry.rb, line 352 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 281 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 564 def has_children? !children.empty? end
# File lib/bibtex/entry.rb, line 224 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 540 def has_parent? !parent.nil? end
BibTeX::Element#has_type?
# File lib/bibtex/entry.rb, line 218 def has_type?(type) type.to_s.match(/^(?:entry|\*)$/i) || @type == type.to_sym || super end
# File lib/bibtex/entry.rb, line 420 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 293 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 232 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 483 def join fields.values.each(&:join) self end
# File lib/bibtex/entry.rb, line 202 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 189 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 303 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 488 def month=(month) fields[:month] = month ensure parse_month end
# File lib/bibtex/entry.rb, line 494 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 535 def names NAME_FIELDS.map { |k| has_field?(k) ? @fields[k].tokens : nil }.flatten.compact end
# File lib/bibtex/entry.rb, line 583 def pages_from fetch(:pages, '').split(/\D+/)[0] end
# File lib/bibtex/entry.rb, line 587 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 556 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 548 def parent_missing? has_field?(:crossref) && !has_parent? end
# File lib/bibtex/entry.rb, line 501 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 521 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 256 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 239 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 249 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 468 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 459 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 452 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 324 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 330 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 477 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 317 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 646 def to_citeproc(options = {}) CiteProcConverter.convert(self, options) end
# File lib/bibtex/entry.rb, line 639 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 655 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 634 def to_s(options = {}) options[:quotes] ||= %w[{ }] ["@#{type}{#{key},", content(options).gsub(/^/, ' '), "}\n"].join("\n") end
# File lib/bibtex/entry.rb, line 650 def to_xml(options = {}) BibTeXMLConverter.convert(self, options) end
Sets the type of the entry.
# File lib/bibtex/entry.rb, line 214 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 389 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 598 def values_at(*arguments) arguments.map do |key| get key end end
# File lib/bibtex/entry.rb, line 665 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 675 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