lib/cff/reference.rb
# frozen_string_literal: true
# Copyright (c) 2018-2022 The Ruby Citation File Format Developers.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require_relative 'licensable'
require_relative 'model_part'
require_relative 'schema'
require_relative 'util'
##
module CFF
# Reference provides a reference pertaining to the software version or the
# software itself, e.g., a software paper describing the abstract concepts of
# the software, a paper describing an algorithm that has been implemented in
# the software version, etc.
#
# Reference implements all of the fields listed in the
# [CFF standard](https://citation-file-format.github.io/). Complex
# fields - `authors`, `contact`, `editors`, `editors_series`, `identifiers`,
# `keywords`, `languages`, `patent_states`, `recipients`, `senders` and
# `translators` - are documented below. All other fields are simple strings
# and can be set as such. A field which has not been set will return the
# empty string. The simple fields are (with defaults in parentheses):
#
# * `abbreviation`
# * `abstract`
# * `collection_doi`
# * `collection_title`
# * `collection_type`
# * `commit`
# * `conference`
# * `copyright`
# * `data-type`
# * `database`
# * `database_provider`
# * `date_accessed` - *Note:* returns a `Date` object
# * `date_downloaded` - *Note:* returns a `Date` object
# * `date_published` - *Note:* returns a `Date` object
# * `date_released` - *Note:* returns a `Date` object
# * `department`
# * `doi`
# * `edition`
# * `end`
# * `entry`
# * `filename`
# * `format`
# * `institution`
# * `isbn`
# * `issn`
# * `issue`
# * `issue_date` - *Note:* returns a `Date` object
# * `issue_title`
# * `journal`
# * `license` - *Note:* see documentation for `license =` below
# * `license_url`
# * `loc_end`
# * `loc_start`
# * `location`
# * `medium`
# * `month`
# * `nihmsid`
# * `notes`
# * `number`
# * `number_volumes`
# * `pages`
# * `pmcid`
# * `publisher`
# * `repository`
# * `repository_artifact`
# * `repository_code`
# * `scope`
# * `section`
# * `start`
# * `status` - *Note:* see documentation for `status =` below
# * `term`
# * `thesis_type`
# * `title`
# * `type` - *Note:* see documentation for `type =` below
# * `url`
# * `version`
# * `volume`
# * `volume_title`
# * `year`
# * `year_original`
class Reference < ModelPart
include Licensable
# This list does not include `format` for reasons explained below, where
# the `format` method is defined!
ALLOWED_FIELDS = (
SCHEMA_FILE['definitions']['reference']['properties'].keys - %w[format languages]
).freeze # :nodoc:
# The [defined set of reference types](https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md#definitionsreferencetype).
REFERENCE_TYPES =
SCHEMA_FILE['definitions']['reference']['properties']['type']['enum'].dup.freeze
# The [defined set of reference status types](https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md#definitionsreferencestatus).
REFERENCE_STATUS_TYPES =
SCHEMA_FILE['definitions']['reference']['properties']['status']['enum'].dup.freeze
attr_date :date_accessed, :date_downloaded, :date_published,
:date_released, :issue_date
# :call-seq:
# new(title) -> Reference
# new(title) { |ref| block } -> Reference
# new(title, type) -> Reference
# new(title, type) { |ref| block } -> Reference
#
# Create a new Reference with the supplied title and, optionally, type.
# If type is not given, or is not one of the
# [defined set of reference types](https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md#definitionsreferencetype),
# 'generic' will be used by default.
def initialize(param, *more) # rubocop:disable Metrics
super()
if param.is_a?(Hash)
@fields = build_model(param)
else
@fields = {}
type = more[0] &&= more[0].downcase
@fields['type'] = REFERENCE_TYPES.include?(type) ? type : 'generic'
@fields['title'] = param
end
%w[
authors contact editors editors-series identifiers
keywords patent-states recipients senders translators
].each do |field|
@fields[field] = [] if @fields[field].nil? || @fields[field].empty?
end
yield self if block_given?
end
# :call-seq:
# from_cff(File, type: 'software') -> Reference
# from_cff(Index, type: 'software') -> Reference
#
# Create a Reference from another CFF File or Index. This is useful for
# easily adding a reference to something with its own CITATION.cff file
# already.
#
# This method assumes that the type of the Reference should be `software`,
# but this can be overridden with the `type` parameter.
def self.from_cff(model, type: 'software')
new(model.title, type) do |ref|
%w[
abstract authors contact commit date_released doi
identifiers keywords license license_url repository
repository_artifact repository_code url version
].each do |field|
value = model.send(field)
ref.send("#{field}=", value.dup) unless value == ''
end
end
end
# :call-seq:
# add_language language
#
# Add a language to this Reference. Input is converted to the ISO 639-3
# three letter language code, so `GER` becomes `deu`, `french` becomes
# `fra` and `en` becomes `eng`.
def add_language(lang)
require 'language_list'
@fields['languages'] = [] if @fields['languages'].nil? || @fields['languages'].empty?
lang = LanguageList::LanguageInfo.find(lang)
return if lang.nil?
lang = lang.iso_639_3
@fields['languages'] << lang unless @fields['languages'].include?(lang)
end
# :call-seq:
# reset_languages
#
# Reset the list of languages for this Reference to be empty.
def reset_languages
@fields.delete('languages')
end
# :call-seq:
# languages -> Array
#
# Return the list of languages associated with this Reference.
def languages
@fields['languages'].nil? || @fields['languages'].empty? ? [] : @fields['languages'].dup
end
# Returns the format of this Reference.
#
# This method is explicitly defined to override the private format method
# that all objects seem to have.
def format # :nodoc:
@fields['format'].nil? ? '' : @fields['format']
end
# Sets the format of this Reference.
#
# This method is explicitly defined to override the private format method
# that all objects seem to have.
def format=(fmt) # :nodoc:
@fields['format'] = fmt
end
# :call-seq:
# status = status
#
# Sets the status of this Reference. The status is restricted to a
# [defined set of status types](https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md#definitionsreferencestatus).
def status=(status)
status = status.downcase
@fields['status'] = status if REFERENCE_STATUS_TYPES.include?(status)
end
# :call-seq:
# type = type
#
# Sets the type of this Reference. The type is restricted to a
# [defined set of reference types](https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md#definitionsreferencetype).
def type=(type)
type = type.downcase
@fields['type'] = type if REFERENCE_TYPES.include?(type)
end
# Override superclass #fields as References contain model parts too.
def fields # :nodoc:
%w[
authors contact editors editors-series identifiers
recipients senders translators
].each do |field|
Util.normalize_modelpart_array!(@fields[field])
end
Util.fields_to_hash(@fields)
end
private
def build_model(fields) # :nodoc:
%w[
authors contact editors editors-series recipients senders translators
].each do |field|
Util.build_actor_collection!(fields[field]) if fields.include?(field)
end
%w[
conference database-provider institution location publisher
].each do |field|
fields[field] &&= Entity.new(fields[field])
end
(fields['identifiers'] || []).map! do |i|
Identifier.new(i)
end
fields
end
public
# Some documentation of "hidden" methods is provided here, out of the
# way of the main class code.
##
# :method: authors
# :call-seq:
# authors -> Array
#
# Return the list of authors for this Reference. To add an author to the
# list, use:
#
# ```
# reference.authors << author
# ```
#
# Authors can be a Person or Entity.
##
# :method: authors=
# :call-seq:
# authors = array_of_authors -> Array
#
# Replace the list of authors for this reference.
#
# Authors can be a Person or Entity.
##
# :method: contact
# :call-seq:
# contact -> Array
#
# Return the list of contacts for this Reference. To add a contact to the
# list, use:
#
# ```
# reference.contact << contact
# ```
#
# Contacts can be a Person or Entity.
##
# :method: contact=
# :call-seq:
# contact = array_of_contacts -> Array
#
# Replace the list of contacts for this reference.
#
# Contacts can be a Person or Entity.
##
# :method: editors
# :call-seq:
# editors -> Array
#
# Return the list of editors for this Reference. To add an editor to the
# list, use:
#
# ```
# reference.editors << editor
# ```
#
# An editor can be a Person or Entity.
##
# :method: editors=
# :call-seq:
# editors = array_of_editors -> Array
#
# Replace the list of editors for this reference.
#
# Editors can be a Person or Entity.
##
# :method: editors_series
# :call-seq:
# editors_series -> Array
#
# Return the list of series editors for this Reference. To add a series
# editor to the list, use:
#
# ```
# reference.editors_series << editor
# ```
#
# An editor can be a Person or Entity.
##
# :method: editors_series=
# :call-seq:
# editors_series = array_of_series_editors -> Array
#
# Replace the list of series editors for this reference.
#
# Series editors can be a Person or Entity.
##
# :method: identifiers
# :call-seq:
# identifiers -> Array
#
# Return the list of identifiers for this citation. To add a identifier to
# the list, use:
#
# ```
# reference.identifiers << identifier
# ```
##
# :method: identifiers=
# :call-seq:
# identifiers = array_of_identifiers -> Array
#
# Replace the list of identifiers for this citation.
##
# :method: keywords
# :call-seq:
# keywords -> Array
#
# Return the list of keywords for this reference. To add a keyword to the
# list, use:
#
# ```
# reference.keywords << keyword
# ```
#
# Keywords will be converted to Strings on output.
##
# :method: keywords=
# :call-seq:
# keywords = array_of_keywords -> Array
#
# Replace the list of keywords for this reference.
#
# Keywords will be converted to Strings on output.
##
# :method: patent_states
# :call-seq:
# patent_states -> Array
#
# Return the list of patent states for this reference. To add a patent
# state to the list, use:
#
# ```
# reference.patent_states << patent_state
# ```
#
# Patent states will be converted to Strings on output.
##
# :method: patent_states=
# :call-seq:
# patent_states = array_of_states -> Array
#
# Replace the list of patent states for this reference.
#
# Patent states will be converted to Strings on output.
##
# :method: recipients
# :call-seq:
# recipients -> Array
#
# Return the list of recipients for this Reference. To add a recipient
# to the list, use:
#
# ```
# reference.recipients << recipient
# ```
#
# Recipients can be a Person or Entity.
##
# :method: recipients=
# :call-seq:
# recipients = array_of_recipients -> Array
#
# Replace the list of recipients for this reference.
#
# Recipients can be a Person or Entity.
##
# :method: senders
# :call-seq:
# senders -> Array
#
# Return the list of senders for this Reference. To add a sender to the
# list, use:
#
# ```
# reference.senders << sender
# ```
#
# Senders can be a Person or Entity.
##
# :method: senders=
# :call-seq:
# senders = array_of_senders -> Array
#
# Replace the list of senders for this reference.
#
# Senders can be a Person or Entity.
##
# :method: translators
# :call-seq:
# translators -> Array
#
# Return the list of translators for this Reference. To add a translator
# to the list, use:
#
# ```
# reference.translators << translator
# ```
#
# Translators can be a Person or Entity.
##
# :method: translators=
# :call-seq:
# translators = array_of_translators -> Array
#
# Replace the list of translators for this reference.
#
# Translators can be a Person or Entity.
end
end