Nothing
#' @title Add or retrieve related items (DataCite/Dublin Core)
#'
#' @description Manage related resources for a dataset using a unified accessor.
#' - For **DataCite 4.x**, this maps to `relatedIdentifier` (+ type & relation).
#' - For **Dublin Core**, this maps to `dct:relation` (string).
#'
#' @details To remain compatible with [utils::bibentry()], the bibentry stores
#' only the
#' **string identifier** (e.g., DOI/URL). The full structured object created by
#' [related_create()] is preserved in the `"relation"` attribute.
#'
#' A `"related"` object is a small S3 list with the following elements:
#' - `relatedIdentifier`: the related resource identifier (DOI, URL, etc.)
#' - `relationType`: the DataCite relation type (e.g., `"IsPartOf"`, `"References"`)
#' - `relatedIdentifierType`: the type of identifier (`"DOI"`, `"URL"`, etc.)
#' - `resourceTypeGeneral`: optional, the general type of the related resource (e.g., `"Text"`, `"Dataset"`)
#'
#' @param x A dataset object created with [dataset_df()] or [as_dataset_df()].
#' @param value A `related` object from [related_create()] or a character.
#' Vectors of characters are also supported and will be converted to a list of
#' `"related"` objects.
#' @param relatedIdentifier A string with the identifier of the related
#' resource.
#' @param relationType A string naming the relation type (per DataCite
#' vocabulary).
#' @param relatedIdentifierType A string naming the identifier type (`"DOI"`,
#' `"URL"`, etc.).
#' @param resourceTypeGeneral Optional: a string naming the general type of the
#' related resource.
#'
#' @return
#' * `relation(x)` returns:
#' - a single structured `"related"` object (from [related_create()]) if only
#' one relation is present,
#' - a list of `"related"` objects if multiple relations are present,
#' - otherwise it falls back to the bibentry field (`relatedidentifier` for
#' DataCite or `relation` for Dublin Core).
#' * `relation(x) <- value` sets the `"relation"` attribute (structured object
#' or list of objects) and the bibentry string fields (`relatedidentifier` and
#' `relation`), and returns the dataset invisibly.
#' * `related_create()` constructs a structured `"related"` object.
#' * `is.related(x)` returns `TRUE` if `x` inherits from class `"related"`.
#'
#' @examples
#' df <- dataset_df(data.frame(x = 1))
#' relation(df) <- related_create(
#' relatedIdentifier = "10.1234/example",
#' relationType = "IsPartOf",
#' relatedIdentifierType = "DOI"
#' )
#' relation(df) # structured object
#' get_bibentry(df)$relation # "10.1234/example"
#' get_bibentry(df)$relatedidentifier # "10.1234/example"
#'
#' # Character input is normalized to a DOI/URL with default types
#' relation(df) <- "https://doi.org/10.5678/xyz"
#' relation(df) # structured object (relationType/Type filled with defaults)
#'
#' # Create related object directly
#' rel <- related_create("https://doi.org/10.5678/xyz", "References", "DOI")
#' is.related(rel) # TRUE
#'
#' @family bibliographic helper functions
#' @export
relation <- function(x) {
assertthat::assert_that(
is.dataset_df(x),
msg = "relation(x): x must be a dataset_df object."
)
rel_attr <- attr(x, "relation", exact = TRUE)
if (!is.null(rel_attr)) {
if (is.related(rel_attr)) {
return(rel_attr)
}
if (is.list(rel_attr) && all(vapply(rel_attr, is.related, logical(1)))) {
return(if (length(rel_attr) == 1) rel_attr[[1]] else rel_attr)
}
return(rel_attr)
}
be <- get_bibentry(x)
if (!is.null(be$relatedidentifier)) {
return(be$relatedidentifier)
}
if (!is.null(be$relation)) {
return(be$relation)
}
message("No related item is recorded.")
NULL
}
#' @rdname relation
#' @export
`relation<-` <- function(x, value) {
assertthat::assert_that(
is.dataset_df(x),
msg = "relation<-(x, value): x must be a dataset_df object created with dataset_df() or as_dataset_df()."
)
be <- get_bibentry(x)
# --- normalize input ---
if (is.null(value)) {
value <- list(related_create(":unas", "IsPartOf", "URL"))
} else if (is.character(value)) {
# allow vector of strings too
value <- lapply(value, function(v) {
related_create(
relatedIdentifier = v,
relationType = "IsPartOf",
relatedIdentifierType = if (grepl("^https?://", v)) "URL" else "DOI"
)
})
} else if (is.related(value)) {
value <- list(value) # wrap single related in a list
} else if (is.list(value) && all(vapply(value, is.related, logical(1)))) {
# already a list of related objects → nothing to do
} else {
stop("relation(x, value)<- : value must be created with `related_create()` or be a character string (or list thereof).")
}
# --- store in dataset_bibentry ---
# keep only the identifiers (vector of strings)
be$relatedidentifier <- vapply(value, function(v) v$relatedIdentifier, character(1))
be$relation <- be$relatedidentifier
attr(be, "relation") <- value # structured list of related items
attr(x, "dataset_bibentry") <- be
attr(x, "relation") <- value
invisible(x)
}
#' @rdname relation
#' @export
related_create <- function(relatedIdentifier,
relationType,
relatedIdentifierType,
resourceTypeGeneral = NULL) {
rel <- list(
relatedIdentifier = as.character(relatedIdentifier),
relationType = as.character(relationType),
relatedIdentifierType = as.character(relatedIdentifierType),
resourceTypeGeneral = if (is.null(resourceTypeGeneral)) NULL else as.character(resourceTypeGeneral)
)
class(rel) <- c("related", "list")
rel
}
#' @rdname relation
#' @export
is.related <- function(x) inherits(x, "related")
#' @rdname relation
#' @export
related_item <- relation
#' @rdname relation
#' @export
`related_item<-` <- `relation<-`
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.