From b021277e1572760552e9db6c7befa160b85fdec3 Mon Sep 17 00:00:00 2001 From: dieghernan Date: Thu, 15 Feb 2024 14:44:29 +0000 Subject: [PATCH 01/32] Add new as.data.frame method --- .lintr | 7 +- NAMESPACE | 1 + NEWS.md | 2 + R/assertions.R | 11 +- R/cff-methods.R | 46 + R/cff_read.R | 22 - R/cff_to_bibentry.R | 4 - R/deprecated.R | 21 +- R/{cff-class.R => docs.R} | 8 +- R/utils-methods.R | 142 +++ man/cff-class.Rd | 130 ++- man/cff_to_bibentry.Rd | 1 - man/chunks/cffclass.Rmd | 70 ++ ...us_cff_to_bib.Rd => renamed_cff_to_bib.Rd} | 6 +- tests/testthat/_snaps/cff-class.md | 839 ++++++++++++++++++ tests/testthat/_snaps/deprecated.md | 11 +- tests/testthat/test-assertions.R | 2 + tests/testthat/test-cff-class.R | 134 +++ tests/testthat/test-deprecated.R | 9 + tests/testthat/test_ci/test-full_cff.R | 1 + 20 files changed, 1421 insertions(+), 46 deletions(-) create mode 100644 R/cff-methods.R rename R/{cff-class.R => docs.R} (64%) create mode 100644 R/utils-methods.R create mode 100644 man/chunks/cffclass.Rmd rename man/{previous_cff_to_bib.Rd => renamed_cff_to_bib.Rd} (88%) create mode 100644 tests/testthat/_snaps/cff-class.md create mode 100644 tests/testthat/test-cff-class.R diff --git a/.lintr b/.lintr index d4708be9..add4d27e 100644 --- a/.lintr +++ b/.lintr @@ -1,3 +1,8 @@ linters: linters_with_defaults() # see vignette("lintr") encoding: "UTF-8" -exclusions: list("data-raw") +exclusions: list( + "data-raw", + "tests/testthat/test_ci/test-full_cff.R", + "vignettes/cffr.Rmd", + "vignettes/bibtex_cff.Rmd" + ) diff --git a/NAMESPACE b/NAMESPACE index 16d8f1c6..2d684bc2 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,5 +1,6 @@ # Generated by roxygen2: do not edit by hand +S3method(as.data.frame,cff) S3method(c,cff) S3method(print,cff) export(as.cff) diff --git a/NEWS.md b/NEWS.md index fe67541a..5043f7bd 100644 --- a/NEWS.md +++ b/NEWS.md @@ -5,6 +5,8 @@ - The conversion from `cff` to `bibentry` is performed now by a new function `cff_to_bibentry()`. Previous names of this function were `cff_to_bibtex()` and `cff_extract_to_bibtex()` that are now superseded. +- New methods: + - `as.data.frame.cff()` ## Changes on bibtex crosswalk diff --git a/R/assertions.R b/R/assertions.R index d703d6f5..2ec03beb 100644 --- a/R/assertions.R +++ b/R/assertions.R @@ -6,10 +6,11 @@ is_email <- function(email) { return(FALSE) } - # See https://www.nicebread.de/validating-email-adresses-in-r/ - x <- grepl("\\<[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}\\>", - as.character(email), - ignore.case = TRUE + email <- trimws(as.character(email)) + + # See CFF validation schema + x <- grepl("^[\\S]+@[\\S]+\\.[\\S]{2,}$",email , + ignore.case = TRUE, perl = TRUE ) x } @@ -22,7 +23,7 @@ is_url <- function(url) { return(FALSE) } - x <- grepl("^http://|^https://|^ftp://|sftp://", url) + x <- grepl("^(https|http|ftp|sftp)://.+", url) x } diff --git a/R/cff-methods.R b/R/cff-methods.R new file mode 100644 index 00000000..2aa0e012 --- /dev/null +++ b/R/cff-methods.R @@ -0,0 +1,46 @@ +#' Print Values +#' +#' @noRd +#' @export +print.cff <- function(x, ...) { + cat(yaml::as.yaml(x)) +} + +#' Combine Values into a Vector or List +#' +#' @source +#' Based on `?c.person` \CRANpkg{utils}. +#' +#' +#' +#' @noRd +#' @export +c.cff <- + function(..., recursive = FALSE) { + args <- list(...) + args <- lapply(args, unclass) + rval <- do.call("c", args) + class(rval) <- "cff" + rval + } + + +#' Coerce to a Data Frame +#' +#' @noRd +#' @export +as.data.frame.cff <- function(x, row.names = NULL, optional = FALSE, ...) { + # If the cff is unnamed is a list of persons/references + if (is.null(names(x))) { + the_df <- cff_list_to_df(x) + } else { + the_df <- cff_to_df(x) + } + + the_df <- as.data.frame(the_df, + row.names = row.names, optional = optional, + ... + ) + + return(the_df) +} diff --git a/R/cff_read.R b/R/cff_read.R index d2c8433f..bddc362d 100644 --- a/R/cff_read.R +++ b/R/cff_read.R @@ -159,28 +159,6 @@ as.cff <- function(x) { x } - -# Print method - -#' @export -print.cff <- function(x, ...) { - cat(yaml::as.yaml(x)) -} - -# c method -# Based on c.person (utils package) -# https://github.com/wch/r-source/blob/trunk/src/library/utils/R/citation.R - -#' @export -c.cff <- - function(..., recursive = FALSE) { - args <- list(...) - args <- lapply(args, unclass) - rval <- do.call("c", args) - class(rval) <- "cff" - rval - } - # Helper---- #' Recursively clean lists and assign cff classes diff --git a/R/cff_to_bibentry.R b/R/cff_to_bibentry.R index c44fe704..f7e4c0a3 100644 --- a/R/cff_to_bibentry.R +++ b/R/cff_to_bibentry.R @@ -127,10 +127,6 @@ cff_to_bibentry <- function(x, return(pref) } -#' @export -#' @rdname cff_to_bibentry -#' @usage NULL -cff_to_bibtex <- cff_to_bibentry cff_bibtex_parser <- function(x) { if (is.null(x)) { diff --git a/R/deprecated.R b/R/deprecated.R index 07b26ba9..31a80b52 100644 --- a/R/deprecated.R +++ b/R/deprecated.R @@ -1,10 +1,9 @@ #' Previous API: Create BibTeX entries from several sources #' #' @description -#' `r lifecycle::badge('superseded')` -#' Please use [cff_to_bibentry()] instead. +#' `r lifecycle::badge('superseded')` Please use [cff_to_bibentry()] instead. #' -#' @rdname previous_cff_to_bib +#' @rdname renamed_cff_to_bib #' @inheritParams cff_to_bibentry #' @export #' @keywords internal @@ -25,7 +24,21 @@ cff_extract_to_bibtex <- function(x, if (requireNamespace("lifecycle", quietly = TRUE)) { lifecycle::deprecate_soft( "0.5.0", "cff_extract_to_bibtex()", - "cff_to_bibentry()" + details = "Function renamed, use `cff_to_bibentry()` instead." + ) + } + cff_to_bibentry(x, what) +} + +#' @rdname renamed_cff_to_bib +#' @export +#' @keywords internal +cff_to_bibtex <- function(x, + what = c("preferred", "references", "all")) { + if (requireNamespace("lifecycle", quietly = TRUE)) { + lifecycle::deprecate_soft( + "0.5.0", "cff_extract_to_bibtex()", + details = "Function renamed, use `cff_to_bibentry()` instead." ) } cff_to_bibentry(x, what) diff --git a/R/cff-class.R b/R/docs.R similarity index 64% rename from R/cff-class.R rename to R/docs.R index 87b0d599..164afee7 100644 --- a/R/cff-class.R +++ b/R/docs.R @@ -2,11 +2,11 @@ #' #' @name cff-class #' -#' @description -#' TODO -#' #' @keywords internal #' +#' @description #' -#' +#' ```{r child = "man/chunks/cffclass.Rmd"} +#' ``` NULL + diff --git a/R/utils-methods.R b/R/utils-methods.R new file mode 100644 index 00000000..b783d89c --- /dev/null +++ b/R/utils-methods.R @@ -0,0 +1,142 @@ +# Utils for df ---- +unnamed_to_df <- function(key, nm) { + key_l <- as.integer(lengths(key)) + m <- matrix(unlist(key), nrow = 1) + df <- as.data.frame(m) + names(df) <- paste0(nm, ".", sprintf("%02d", seq_len(key_l) - 1)) + return(df) +} + +named_to_df <- function(key, nm) { + key_un <- unlist(key) + + + m <- matrix(as.character(key_un), nrow = 1) + df <- as.data.frame(m) + names(df) <- names(key_un) + return(df) +} + +nested_named_to_df <- function(key, nm) { + key_unlist <- key[[1]] + key_len <- seq_len(length(key_unlist)) + + df_l_type3 <- lapply(key_len, function(z) { + df <- cff_to_df(key_unlist[[z]]) + + # Prepend names + names(df) <- paste0(nm, ".", sprintf("%02d", z - 1), ".", names(df)) + return(df) + }) + + df_list_to_df(df_l_type3) +} + +prefcit_to_df <- function(key, nm = "preferred_citation.") { + key_df <- cff_to_df(key[[1]]) + names(key_df) <- paste0(nm, names(key_df)) + return(key_df) +} + +reflist_to_df <- function(key, nm) { + key_unlist <- key[[1]] + key_len <- seq_len(length(key_unlist)) + + prefix_key <- paste0(nm, ".", sprintf("%02d", key_len - 1), ".") + + df_l <- lapply(key_len, function(y) { + key_l <- key_unlist[y] + nm_pref <- prefix_key[y] + + dff <- prefcit_to_df(key_l, nm_pref) + + dff + }) + + final_df <- df_list_to_df(df_l) + + final_df +} + +df_list_to_df <- function(x) { + # Clean NULL + df_l_clean <- x[!vapply(x, is.null, logical(1))] + + final_df <- do.call(cbind, df_l_clean) + return(final_df) +} +cff_to_df <- function(x) { + # CFF has different models + # type 1: unnamed arrays + unnamed_array <- c("keywords", "languages", "patent-states") + + + # type 2: named arrays + named_array <- c( + "conference", "database-provider", "institution", + "location", "publisher" + ) + + + # type 3: nested named arrays + nested_named_array <- c( + "authors", "contact", "editors", "editors-series", + "recipients", "senders", "translators", "identifiers" + ) + + nms <- names(x) + x_len <- seq_len(length(x)) + + + df_l <- lapply(x_len, function(y) { + nm <- nms[y] + + if (nm %in% unnamed_array) { + return(unnamed_to_df(x[y], nm)) + } + if (nm %in% named_array) { + return(named_to_df(x[y], nm)) + } + if (nm %in% nested_named_array) { + return(nested_named_to_df(x[y], nm)) + } + if (nm == "preferred-citation") { + return(prefcit_to_df(x[y])) + } + + if (nm == "references") { + return(reflist_to_df(x[y], nm)) + } + + the_df <- as.data.frame(x[[y]]) + names(the_df) <- gsub("-", "_", nm) + return(the_df) + }) + + final_df <- df_list_to_df(df_l) + + return(final_df) +} + +cff_list_to_df <- function(x) { + # Applicable to lists of persons or references + # Guess type + if (!"type" %in% names(x[[1]])) { + guess <- "person" + } else { + guess <- "reference" + } + + + x_len <- seq_len(length(x)) + df_l <- lapply(x_len, function(y) { + df <- as.data.frame(x[y]) + newnames <- paste0(guess, ".", sprintf("%02d", y - 1), ".", names(df)) + names(df) <- newnames + + df + }) + + df_end <- df_list_to_df(df_l) + df_end +} diff --git a/man/cff-class.Rd b/man/cff-class.Rd index 79127b37..bf7eb71a 100644 --- a/man/cff-class.Rd +++ b/man/cff-class.Rd @@ -1,9 +1,135 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/cff-class.R +% Please edit documentation in R/docs.R \name{cff-class} \alias{cff-class} \title{The \code{cff} class} \description{ -TODO +\subsection{The \code{cff} class}{ + +\strong{cffr} implements a S3 object with \code{\link[=class]{class()}} \code{cff}, that it is used to +represent the information of a \verb{*.cff} file in \strong{R}. + +Under the hood, a \code{cff} object is simply a named \code{\link[=list]{list()}} to which we added +additional methods, most notably \code{\link[=print]{print()}}. + +\if{html}{\out{
}}\preformatted{a_named_list <- list( + first = "I", second = "am", third = "a", fourth = "list", + fifth = "with", sixth = "names", "none" = NULL +) + + +dput(a_named_list) +#> list(first = "I", second = "am", third = "a", fourth = "list", +#> fifth = "with", sixth = "names", none = NULL) + +# Default print +a_named_list +#> $first +#> [1] "I" +#> +#> $second +#> [1] "am" +#> +#> $third +#> [1] "a" +#> +#> $fourth +#> [1] "list" +#> +#> $fifth +#> [1] "with" +#> +#> $sixth +#> [1] "names" +#> +#> $none +#> NULL + +# But +a_cff_object <- as.cff(a_named_list) + +class(a_cff_object) +#> [1] "cff" + +a_cff_object +#> first: I +#> second: am +#> third: a +#> fourth: list +#> fifth: with +#> sixth: names + +dput(a_cff_object) +#> structure(list(first = "I", second = "am", third = "a", fourth = "list", +#> fifth = "with", sixth = "names"), class = "cff") +}\if{html}{\out{
}} + +\code{\link[=as.cff]{as.cff()}} not only converts a \code{\link[=list]{list()}} to \code{cff} but also removes items (known +as \code{keys} in CFF terminology) that are \code{NULL} or \code{NA}. +} + +\subsection{Valid \code{cff} objects}{ + +Although \code{a_cff_object} is a \code{cff} object, it is not valid, since it does not +comply with the validation rules of the CFF specification. + +\if{html}{\out{
}}\preformatted{cff_validate(a_cff_object) +#> == Validating cff ============================================================== +#> x Oops! This has the following errors: +#> * cff.authors: is required +#> * cff["cff-version"]: is required +#> * cff.message: is required +#> * cff.title: is required +}\if{html}{\out{
}} + +\code{\link[=cff_validate]{cff_validate()}} gives minimal messages of what's wrong with our \code{cff} and +(invisibly) returns the result of the validation (\code{TRUE/FALSE}). +} + +\subsection{Other methods}{ +\subsection{\code{\link[=as.data.frame]{as.data.frame()}}}{ + +\if{html}{\out{
}}\preformatted{minimal_cff <- cff() + +minimal_cff +#> authors: +#> - family-names: Doe +#> given-names: John +#> cff-version: 1.2.0 +#> message: If you use this software, please cite it using these metadata. +#> title: My Research Software + +as_df <- as.data.frame(minimal_cff) + +class(as_df) +#> [1] "data.frame" + +t(as_df) +#> [,1] +#> authors.00.family_names "Doe" +#> authors.00.given_names "John" +#> cff_version "1.2.0" +#> message "If you use this software, please cite it using these metadata." +#> title "My Research Software" +}\if{html}{\out{
}} +} + +} + +\subsection{\code{\link[=c]{c()}}}{ + +\if{html}{\out{
}}\preformatted{new_keys <- c("date-released" = "2020-01-31", abstract = "Minimal example") + +c(minimal_cff, new_keys) +#> authors: +#> - family-names: Doe +#> given-names: John +#> cff-version: 1.2.0 +#> message: If you use this software, please cite it using these metadata. +#> title: My Research Software +#> date-released: '2020-01-31' +#> abstract: Minimal example +}\if{html}{\out{
}} +} } \keyword{internal} diff --git a/man/cff_to_bibentry.Rd b/man/cff_to_bibentry.Rd index 338a14e4..c1c64b0d 100644 --- a/man/cff_to_bibentry.Rd +++ b/man/cff_to_bibentry.Rd @@ -2,7 +2,6 @@ % Please edit documentation in R/cff_to_bibentry.R \name{cff_to_bibentry} \alias{cff_to_bibentry} -\alias{cff_to_bibtex} \title{Create BibTeX entries from several sources} \usage{ cff_to_bibentry(x, what = c("preferred", "references", "all")) diff --git a/man/chunks/cffclass.Rmd b/man/chunks/cffclass.Rmd new file mode 100644 index 00000000..0ffd1561 --- /dev/null +++ b/man/chunks/cffclass.Rmd @@ -0,0 +1,70 @@ +## The `cff` class + +**cffr** implements a S3 object with [class()] `cff`, that it is used to +represent the information of a `*.cff` file in **R**. + +Under the hood, a `cff` object is simply a named [list()] to which we added +additional methods, most notably [print()]. + +```{r} +a_named_list <- list( + first = "I", second = "am", third = "a", fourth = "list", + fifth = "with", sixth = "names", "none" = NULL +) + + +dput(a_named_list) + +# Default print +a_named_list + +# But +a_cff_object <- as.cff(a_named_list) + +class(a_cff_object) + +a_cff_object + +dput(a_cff_object) +``` + +[as.cff()] not only converts a [list()] to `cff` but also removes items (known +as `keys` in CFF terminology) that are `NULL` or `NA`. + +## Valid `cff` objects + +Although `a_cff_object` is a `cff` object, it is not valid, since it does not +comply with the validation rules of the CFF specification. + +```{r} +cff_validate(a_cff_object) +``` + +[cff_validate()] gives minimal messages of what's wrong with our `cff` and +(invisibly) returns the result of the validation (`TRUE/FALSE`). + +## Other methods + +### [as.data.frame()] + +```{r} +minimal_cff <- cff() + +minimal_cff + +as_df <- as.data.frame(minimal_cff) + +class(as_df) + +t(as_df) +``` + +## [c()] + + +```{r} +new_keys <- c("date-released" = "2020-01-31", abstract = "Minimal example") + +c(minimal_cff, new_keys) +``` + diff --git a/man/previous_cff_to_bib.Rd b/man/renamed_cff_to_bib.Rd similarity index 88% rename from man/previous_cff_to_bib.Rd rename to man/renamed_cff_to_bib.Rd index 94761d70..11f1def6 100644 --- a/man/previous_cff_to_bib.Rd +++ b/man/renamed_cff_to_bib.Rd @@ -2,9 +2,12 @@ % Please edit documentation in R/deprecated.R \name{cff_extract_to_bibtex} \alias{cff_extract_to_bibtex} +\alias{cff_to_bibtex} \title{Previous API: Create BibTeX entries from several sources} \usage{ cff_extract_to_bibtex(x, what = c("preferred", "references", "all")) + +cff_to_bibtex(x, what = c("preferred", "references", "all")) } \arguments{ \item{x}{The source that would be used for generating @@ -31,8 +34,7 @@ both the preferred citation info and the references. See \code{\link[=cff_to_bibentry]{cff_to_bibentry()}} } \description{ -\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#superseded}{\figure{lifecycle-superseded.svg}{options: alt='[Superseded]'}}}{\strong{[Superseded]}} -Please use \code{\link[=cff_to_bibentry]{cff_to_bibentry()}} instead. +\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#superseded}{\figure{lifecycle-superseded.svg}{options: alt='[Superseded]'}}}{\strong{[Superseded]}} Please use \code{\link[=cff_to_bibentry]{cff_to_bibentry()}} instead. } \examples{ \donttest{ diff --git a/tests/testthat/_snaps/cff-class.md b/tests/testthat/_snaps/cff-class.md new file mode 100644 index 00000000..85c94c3b --- /dev/null +++ b/tests/testthat/_snaps/cff-class.md @@ -0,0 +1,839 @@ +# as data frame complete + + Code + names(the_df) + Output + [1] "cff_version" + [2] "message" + [3] "abstract" + [4] "authors.00.family_names" + [5] "authors.00.given_names" + [6] "authors.00.name_particle" + [7] "authors.00.name_suffix" + [8] "authors.00.alias" + [9] "authors.00.affiliation" + [10] "authors.00.address" + [11] "authors.00.city" + [12] "authors.00.region" + [13] "authors.00.post_code" + [14] "authors.00.country" + [15] "authors.00.orcid" + [16] "authors.00.email" + [17] "authors.00.tel" + [18] "authors.00.fax" + [19] "authors.00.website" + [20] "authors.01.name" + [21] "authors.01.address" + [22] "authors.01.city" + [23] "authors.01.region" + [24] "authors.01.post_code" + [25] "authors.01.country" + [26] "authors.01.orcid" + [27] "authors.01.email" + [28] "authors.01.tel" + [29] "authors.01.fax" + [30] "authors.01.website" + [31] "authors.01.date_start" + [32] "authors.01.date_end" + [33] "authors.01.location" + [34] "commit" + [35] "contact.00.family_names" + [36] "contact.00.given_names" + [37] "contact.00.name_particle" + [38] "contact.00.name_suffix" + [39] "contact.00.alias" + [40] "contact.00.affiliation" + [41] "contact.00.address" + [42] "contact.00.city" + [43] "contact.00.region" + [44] "contact.00.post_code" + [45] "contact.00.country" + [46] "contact.00.orcid" + [47] "contact.00.email" + [48] "contact.00.tel" + [49] "contact.00.fax" + [50] "contact.00.website" + [51] "contact.01.name" + [52] "contact.01.address" + [53] "contact.01.city" + [54] "contact.01.region" + [55] "contact.01.post_code" + [56] "contact.01.country" + [57] "contact.01.orcid" + [58] "contact.01.email" + [59] "contact.01.tel" + [60] "contact.01.fax" + [61] "contact.01.website" + [62] "contact.01.date_start" + [63] "contact.01.date_end" + [64] "contact.01.location" + [65] "date_released" + [66] "doi" + [67] "identifiers.00.type" + [68] "identifiers.00.value" + [69] "identifiers.01.type" + [70] "identifiers.01.value" + [71] "identifiers.02.type" + [72] "identifiers.02.value" + [73] "identifiers.03.type" + [74] "identifiers.03.value" + [75] "keywords.00" + [76] "keywords.01" + [77] "keywords.02" + [78] "keywords.03" + [79] "license" + [80] "license_url" + [81] "repository" + [82] "repository_code" + [83] "repository_artifact" + [84] "title" + [85] "type" + [86] "url" + [87] "version" + [88] "preferred_citation.type" + [89] "preferred_citation.title" + [90] "preferred_citation.abbreviation" + [91] "preferred_citation.abstract" + [92] "preferred_citation.collection_doi" + [93] "preferred_citation.collection_title" + [94] "preferred_citation.collection_type" + [95] "preferred_citation.commit" + [96] "preferred_citation.copyright" + [97] "preferred_citation.data_type" + [98] "preferred_citation.database" + [99] "preferred_citation.date_accessed" + [100] "preferred_citation.date_downloaded" + [101] "preferred_citation.date_released" + [102] "preferred_citation.date_published" + [103] "preferred_citation.department" + [104] "preferred_citation.doi" + [105] "preferred_citation.edition" + [106] "preferred_citation.end" + [107] "preferred_citation.entry" + [108] "preferred_citation.filename" + [109] "preferred_citation.format" + [110] "preferred_citation.identifiers.00.type" + [111] "preferred_citation.identifiers.00.value" + [112] "preferred_citation.identifiers.01.type" + [113] "preferred_citation.identifiers.01.value" + [114] "preferred_citation.identifiers.02.type" + [115] "preferred_citation.identifiers.02.value" + [116] "preferred_citation.identifiers.03.type" + [117] "preferred_citation.identifiers.03.value" + [118] "preferred_citation.isbn" + [119] "preferred_citation.issn" + [120] "preferred_citation.issue" + [121] "preferred_citation.issue_date" + [122] "preferred_citation.issue_title" + [123] "preferred_citation.journal" + [124] "preferred_citation.keywords.00" + [125] "preferred_citation.keywords.01" + [126] "preferred_citation.languages.00" + [127] "preferred_citation.languages.01" + [128] "preferred_citation.license" + [129] "preferred_citation.license_url" + [130] "preferred_citation.loc_start" + [131] "preferred_citation.loc_end" + [132] "preferred_citation.medium" + [133] "preferred_citation.month" + [134] "preferred_citation.nihmsid" + [135] "preferred_citation.notes" + [136] "preferred_citation.number" + [137] "preferred_citation.number_volumes" + [138] "preferred_citation.pages" + [139] "preferred_citation.patent-states.00" + [140] "preferred_citation.patent-states.01" + [141] "preferred_citation.patent-states.02" + [142] "preferred_citation.patent-states.03" + [143] "preferred_citation.patent-states.04" + [144] "preferred_citation.pmcid" + [145] "preferred_citation.repository" + [146] "preferred_citation.repository_code" + [147] "preferred_citation.repository_artifact" + [148] "preferred_citation.scope" + [149] "preferred_citation.section" + [150] "preferred_citation.status" + [151] "preferred_citation.start" + [152] "preferred_citation.thesis_type" + [153] "preferred_citation.url" + [154] "preferred_citation.version" + [155] "preferred_citation.volume" + [156] "preferred_citation.volume_title" + [157] "preferred_citation.year" + [158] "preferred_citation.year_original" + [159] "preferred_citation.conference.name" + [160] "preferred_citation.conference.address" + [161] "preferred_citation.conference.city" + [162] "preferred_citation.conference.region" + [163] "preferred_citation.conference.post-code" + [164] "preferred_citation.conference.country" + [165] "preferred_citation.conference.orcid" + [166] "preferred_citation.conference.email" + [167] "preferred_citation.conference.tel" + [168] "preferred_citation.conference.fax" + [169] "preferred_citation.conference.website" + [170] "preferred_citation.conference.date-start" + [171] "preferred_citation.conference.date-end" + [172] "preferred_citation.conference.location" + [173] "preferred_citation.authors.00.family_names" + [174] "preferred_citation.authors.00.given_names" + [175] "preferred_citation.authors.00.name_particle" + [176] "preferred_citation.authors.00.name_suffix" + [177] "preferred_citation.authors.00.alias" + [178] "preferred_citation.authors.00.affiliation" + [179] "preferred_citation.authors.00.address" + [180] "preferred_citation.authors.00.city" + [181] "preferred_citation.authors.00.region" + [182] "preferred_citation.authors.00.post_code" + [183] "preferred_citation.authors.00.country" + [184] "preferred_citation.authors.00.orcid" + [185] "preferred_citation.authors.00.email" + [186] "preferred_citation.authors.00.tel" + [187] "preferred_citation.authors.00.fax" + [188] "preferred_citation.authors.00.website" + [189] "preferred_citation.authors.01.name" + [190] "preferred_citation.authors.01.address" + [191] "preferred_citation.authors.01.city" + [192] "preferred_citation.authors.01.region" + [193] "preferred_citation.authors.01.post_code" + [194] "preferred_citation.authors.01.country" + [195] "preferred_citation.authors.01.orcid" + [196] "preferred_citation.authors.01.email" + [197] "preferred_citation.authors.01.tel" + [198] "preferred_citation.authors.01.fax" + [199] "preferred_citation.authors.01.website" + [200] "preferred_citation.authors.01.date_start" + [201] "preferred_citation.authors.01.date_end" + [202] "preferred_citation.authors.01.location" + [203] "preferred_citation.contact.00.family_names" + [204] "preferred_citation.contact.00.given_names" + [205] "preferred_citation.contact.00.name_particle" + [206] "preferred_citation.contact.00.name_suffix" + [207] "preferred_citation.contact.00.alias" + [208] "preferred_citation.contact.00.affiliation" + [209] "preferred_citation.contact.00.address" + [210] "preferred_citation.contact.00.city" + [211] "preferred_citation.contact.00.region" + [212] "preferred_citation.contact.00.post_code" + [213] "preferred_citation.contact.00.country" + [214] "preferred_citation.contact.00.orcid" + [215] "preferred_citation.contact.00.email" + [216] "preferred_citation.contact.00.tel" + [217] "preferred_citation.contact.00.fax" + [218] "preferred_citation.contact.00.website" + [219] "preferred_citation.contact.01.name" + [220] "preferred_citation.contact.01.address" + [221] "preferred_citation.contact.01.city" + [222] "preferred_citation.contact.01.region" + [223] "preferred_citation.contact.01.post_code" + [224] "preferred_citation.contact.01.country" + [225] "preferred_citation.contact.01.orcid" + [226] "preferred_citation.contact.01.email" + [227] "preferred_citation.contact.01.tel" + [228] "preferred_citation.contact.01.fax" + [229] "preferred_citation.contact.01.website" + [230] "preferred_citation.contact.01.date_start" + [231] "preferred_citation.contact.01.date_end" + [232] "preferred_citation.contact.01.location" + [233] "preferred_citation.database-provider.name" + [234] "preferred_citation.database-provider.address" + [235] "preferred_citation.database-provider.city" + [236] "preferred_citation.database-provider.region" + [237] "preferred_citation.database-provider.post-code" + [238] "preferred_citation.database-provider.country" + [239] "preferred_citation.database-provider.orcid" + [240] "preferred_citation.database-provider.email" + [241] "preferred_citation.database-provider.tel" + [242] "preferred_citation.database-provider.fax" + [243] "preferred_citation.database-provider.website" + [244] "preferred_citation.database-provider.date-start" + [245] "preferred_citation.database-provider.date-end" + [246] "preferred_citation.database-provider.location" + [247] "preferred_citation.editors.00.family_names" + [248] "preferred_citation.editors.00.given_names" + [249] "preferred_citation.editors.00.name_particle" + [250] "preferred_citation.editors.00.name_suffix" + [251] "preferred_citation.editors.00.alias" + [252] "preferred_citation.editors.00.affiliation" + [253] "preferred_citation.editors.00.address" + [254] "preferred_citation.editors.00.city" + [255] "preferred_citation.editors.00.region" + [256] "preferred_citation.editors.00.post_code" + [257] "preferred_citation.editors.00.country" + [258] "preferred_citation.editors.00.orcid" + [259] "preferred_citation.editors.00.email" + [260] "preferred_citation.editors.00.tel" + [261] "preferred_citation.editors.00.fax" + [262] "preferred_citation.editors.00.website" + [263] "preferred_citation.editors.01.name" + [264] "preferred_citation.editors.01.address" + [265] "preferred_citation.editors.01.city" + [266] "preferred_citation.editors.01.region" + [267] "preferred_citation.editors.01.post_code" + [268] "preferred_citation.editors.01.country" + [269] "preferred_citation.editors.01.orcid" + [270] "preferred_citation.editors.01.email" + [271] "preferred_citation.editors.01.tel" + [272] "preferred_citation.editors.01.fax" + [273] "preferred_citation.editors.01.website" + [274] "preferred_citation.editors.01.date_start" + [275] "preferred_citation.editors.01.date_end" + [276] "preferred_citation.editors.01.location" + [277] "preferred_citation.editors-series.00.family_names" + [278] "preferred_citation.editors-series.00.given_names" + [279] "preferred_citation.editors-series.00.name_particle" + [280] "preferred_citation.editors-series.00.name_suffix" + [281] "preferred_citation.editors-series.00.alias" + [282] "preferred_citation.editors-series.00.affiliation" + [283] "preferred_citation.editors-series.00.address" + [284] "preferred_citation.editors-series.00.city" + [285] "preferred_citation.editors-series.00.region" + [286] "preferred_citation.editors-series.00.post_code" + [287] "preferred_citation.editors-series.00.country" + [288] "preferred_citation.editors-series.00.orcid" + [289] "preferred_citation.editors-series.00.email" + [290] "preferred_citation.editors-series.00.tel" + [291] "preferred_citation.editors-series.00.fax" + [292] "preferred_citation.editors-series.00.website" + [293] "preferred_citation.editors-series.01.name" + [294] "preferred_citation.editors-series.01.address" + [295] "preferred_citation.editors-series.01.city" + [296] "preferred_citation.editors-series.01.region" + [297] "preferred_citation.editors-series.01.post_code" + [298] "preferred_citation.editors-series.01.country" + [299] "preferred_citation.editors-series.01.orcid" + [300] "preferred_citation.editors-series.01.email" + [301] "preferred_citation.editors-series.01.tel" + [302] "preferred_citation.editors-series.01.fax" + [303] "preferred_citation.editors-series.01.website" + [304] "preferred_citation.editors-series.01.date_start" + [305] "preferred_citation.editors-series.01.date_end" + [306] "preferred_citation.editors-series.01.location" + [307] "preferred_citation.institution.name" + [308] "preferred_citation.institution.address" + [309] "preferred_citation.institution.city" + [310] "preferred_citation.institution.region" + [311] "preferred_citation.institution.post-code" + [312] "preferred_citation.institution.country" + [313] "preferred_citation.institution.orcid" + [314] "preferred_citation.institution.email" + [315] "preferred_citation.institution.tel" + [316] "preferred_citation.institution.fax" + [317] "preferred_citation.institution.website" + [318] "preferred_citation.institution.date-start" + [319] "preferred_citation.institution.date-end" + [320] "preferred_citation.institution.location" + [321] "preferred_citation.location.name" + [322] "preferred_citation.location.address" + [323] "preferred_citation.location.city" + [324] "preferred_citation.location.region" + [325] "preferred_citation.location.post-code" + [326] "preferred_citation.location.country" + [327] "preferred_citation.location.orcid" + [328] "preferred_citation.location.email" + [329] "preferred_citation.location.tel" + [330] "preferred_citation.location.fax" + [331] "preferred_citation.location.website" + [332] "preferred_citation.location.date-start" + [333] "preferred_citation.location.date-end" + [334] "preferred_citation.location.location" + [335] "preferred_citation.publisher.name" + [336] "preferred_citation.publisher.address" + [337] "preferred_citation.publisher.city" + [338] "preferred_citation.publisher.region" + [339] "preferred_citation.publisher.post-code" + [340] "preferred_citation.publisher.country" + [341] "preferred_citation.publisher.orcid" + [342] "preferred_citation.publisher.email" + [343] "preferred_citation.publisher.tel" + [344] "preferred_citation.publisher.fax" + [345] "preferred_citation.publisher.website" + [346] "preferred_citation.publisher.date-start" + [347] "preferred_citation.publisher.date-end" + [348] "preferred_citation.publisher.location" + [349] "preferred_citation.recipients.00.family_names" + [350] "preferred_citation.recipients.00.given_names" + [351] "preferred_citation.recipients.00.name_particle" + [352] "preferred_citation.recipients.00.name_suffix" + [353] "preferred_citation.recipients.00.alias" + [354] "preferred_citation.recipients.00.affiliation" + [355] "preferred_citation.recipients.00.address" + [356] "preferred_citation.recipients.00.city" + [357] "preferred_citation.recipients.00.region" + [358] "preferred_citation.recipients.00.post_code" + [359] "preferred_citation.recipients.00.country" + [360] "preferred_citation.recipients.00.orcid" + [361] "preferred_citation.recipients.00.email" + [362] "preferred_citation.recipients.00.tel" + [363] "preferred_citation.recipients.00.fax" + [364] "preferred_citation.recipients.00.website" + [365] "preferred_citation.recipients.01.name" + [366] "preferred_citation.recipients.01.address" + [367] "preferred_citation.recipients.01.city" + [368] "preferred_citation.recipients.01.region" + [369] "preferred_citation.recipients.01.post_code" + [370] "preferred_citation.recipients.01.country" + [371] "preferred_citation.recipients.01.orcid" + [372] "preferred_citation.recipients.01.email" + [373] "preferred_citation.recipients.01.tel" + [374] "preferred_citation.recipients.01.fax" + [375] "preferred_citation.recipients.01.website" + [376] "preferred_citation.recipients.01.date_start" + [377] "preferred_citation.recipients.01.date_end" + [378] "preferred_citation.recipients.01.location" + [379] "preferred_citation.senders.00.family_names" + [380] "preferred_citation.senders.00.given_names" + [381] "preferred_citation.senders.00.name_particle" + [382] "preferred_citation.senders.00.name_suffix" + [383] "preferred_citation.senders.00.alias" + [384] "preferred_citation.senders.00.affiliation" + [385] "preferred_citation.senders.00.address" + [386] "preferred_citation.senders.00.city" + [387] "preferred_citation.senders.00.region" + [388] "preferred_citation.senders.00.post_code" + [389] "preferred_citation.senders.00.country" + [390] "preferred_citation.senders.00.orcid" + [391] "preferred_citation.senders.00.email" + [392] "preferred_citation.senders.00.tel" + [393] "preferred_citation.senders.00.fax" + [394] "preferred_citation.senders.00.website" + [395] "preferred_citation.senders.01.name" + [396] "preferred_citation.senders.01.address" + [397] "preferred_citation.senders.01.city" + [398] "preferred_citation.senders.01.region" + [399] "preferred_citation.senders.01.post_code" + [400] "preferred_citation.senders.01.country" + [401] "preferred_citation.senders.01.orcid" + [402] "preferred_citation.senders.01.email" + [403] "preferred_citation.senders.01.tel" + [404] "preferred_citation.senders.01.fax" + [405] "preferred_citation.senders.01.website" + [406] "preferred_citation.senders.01.date_start" + [407] "preferred_citation.senders.01.date_end" + [408] "preferred_citation.senders.01.location" + [409] "preferred_citation.translators.00.family_names" + [410] "preferred_citation.translators.00.given_names" + [411] "preferred_citation.translators.00.name_particle" + [412] "preferred_citation.translators.00.name_suffix" + [413] "preferred_citation.translators.00.alias" + [414] "preferred_citation.translators.00.affiliation" + [415] "preferred_citation.translators.00.address" + [416] "preferred_citation.translators.00.city" + [417] "preferred_citation.translators.00.region" + [418] "preferred_citation.translators.00.post_code" + [419] "preferred_citation.translators.00.country" + [420] "preferred_citation.translators.00.orcid" + [421] "preferred_citation.translators.00.email" + [422] "preferred_citation.translators.00.tel" + [423] "preferred_citation.translators.00.fax" + [424] "preferred_citation.translators.00.website" + [425] "preferred_citation.translators.01.name" + [426] "preferred_citation.translators.01.address" + [427] "preferred_citation.translators.01.city" + [428] "preferred_citation.translators.01.region" + [429] "preferred_citation.translators.01.post_code" + [430] "preferred_citation.translators.01.country" + [431] "preferred_citation.translators.01.orcid" + [432] "preferred_citation.translators.01.email" + [433] "preferred_citation.translators.01.tel" + [434] "preferred_citation.translators.01.fax" + [435] "preferred_citation.translators.01.website" + [436] "preferred_citation.translators.01.date_start" + [437] "preferred_citation.translators.01.date_end" + [438] "preferred_citation.translators.01.location" + [439] "references.00.type" + [440] "references.00.title" + [441] "references.00.abbreviation" + [442] "references.00.abstract" + [443] "references.00.collection_doi" + [444] "references.00.collection_title" + [445] "references.00.collection_type" + [446] "references.00.commit" + [447] "references.00.copyright" + [448] "references.00.data_type" + [449] "references.00.database" + [450] "references.00.date_accessed" + [451] "references.00.date_downloaded" + [452] "references.00.date_released" + [453] "references.00.date_published" + [454] "references.00.department" + [455] "references.00.doi" + [456] "references.00.edition" + [457] "references.00.end" + [458] "references.00.entry" + [459] "references.00.filename" + [460] "references.00.format" + [461] "references.00.identifiers.00.type" + [462] "references.00.identifiers.00.value" + [463] "references.00.identifiers.01.type" + [464] "references.00.identifiers.01.value" + [465] "references.00.identifiers.02.type" + [466] "references.00.identifiers.02.value" + [467] "references.00.identifiers.03.type" + [468] "references.00.identifiers.03.value" + [469] "references.00.isbn" + [470] "references.00.issn" + [471] "references.00.issue" + [472] "references.00.issue_date" + [473] "references.00.issue_title" + [474] "references.00.journal" + [475] "references.00.keywords.00" + [476] "references.00.keywords.01" + [477] "references.00.languages.00" + [478] "references.00.languages.01" + [479] "references.00.license" + [480] "references.00.license_url" + [481] "references.00.loc_start" + [482] "references.00.loc_end" + [483] "references.00.medium" + [484] "references.00.month" + [485] "references.00.nihmsid" + [486] "references.00.notes" + [487] "references.00.number" + [488] "references.00.number_volumes" + [489] "references.00.pages" + [490] "references.00.patent-states.00" + [491] "references.00.patent-states.01" + [492] "references.00.patent-states.02" + [493] "references.00.patent-states.03" + [494] "references.00.patent-states.04" + [495] "references.00.pmcid" + [496] "references.00.repository" + [497] "references.00.repository_code" + [498] "references.00.repository_artifact" + [499] "references.00.scope" + [500] "references.00.section" + [501] "references.00.status" + [502] "references.00.start" + [503] "references.00.thesis_type" + [504] "references.00.url" + [505] "references.00.version" + [506] "references.00.volume" + [507] "references.00.volume_title" + [508] "references.00.year" + [509] "references.00.year_original" + [510] "references.00.conference.name" + [511] "references.00.conference.address" + [512] "references.00.conference.city" + [513] "references.00.conference.region" + [514] "references.00.conference.post-code" + [515] "references.00.conference.country" + [516] "references.00.conference.orcid" + [517] "references.00.conference.email" + [518] "references.00.conference.tel" + [519] "references.00.conference.fax" + [520] "references.00.conference.website" + [521] "references.00.conference.date-start" + [522] "references.00.conference.date-end" + [523] "references.00.conference.location" + [524] "references.00.authors.00.family_names" + [525] "references.00.authors.00.given_names" + [526] "references.00.authors.00.name_particle" + [527] "references.00.authors.00.name_suffix" + [528] "references.00.authors.00.alias" + [529] "references.00.authors.00.affiliation" + [530] "references.00.authors.00.address" + [531] "references.00.authors.00.city" + [532] "references.00.authors.00.region" + [533] "references.00.authors.00.post_code" + [534] "references.00.authors.00.country" + [535] "references.00.authors.00.orcid" + [536] "references.00.authors.00.email" + [537] "references.00.authors.00.tel" + [538] "references.00.authors.00.fax" + [539] "references.00.authors.00.website" + [540] "references.00.authors.01.name" + [541] "references.00.authors.01.address" + [542] "references.00.authors.01.city" + [543] "references.00.authors.01.region" + [544] "references.00.authors.01.post_code" + [545] "references.00.authors.01.country" + [546] "references.00.authors.01.orcid" + [547] "references.00.authors.01.email" + [548] "references.00.authors.01.tel" + [549] "references.00.authors.01.fax" + [550] "references.00.authors.01.website" + [551] "references.00.authors.01.date_start" + [552] "references.00.authors.01.date_end" + [553] "references.00.authors.01.location" + [554] "references.00.contact.00.family_names" + [555] "references.00.contact.00.given_names" + [556] "references.00.contact.00.name_particle" + [557] "references.00.contact.00.name_suffix" + [558] "references.00.contact.00.alias" + [559] "references.00.contact.00.affiliation" + [560] "references.00.contact.00.address" + [561] "references.00.contact.00.city" + [562] "references.00.contact.00.region" + [563] "references.00.contact.00.post_code" + [564] "references.00.contact.00.country" + [565] "references.00.contact.00.orcid" + [566] "references.00.contact.00.email" + [567] "references.00.contact.00.tel" + [568] "references.00.contact.00.fax" + [569] "references.00.contact.00.website" + [570] "references.00.contact.01.name" + [571] "references.00.contact.01.address" + [572] "references.00.contact.01.city" + [573] "references.00.contact.01.region" + [574] "references.00.contact.01.post_code" + [575] "references.00.contact.01.country" + [576] "references.00.contact.01.orcid" + [577] "references.00.contact.01.email" + [578] "references.00.contact.01.tel" + [579] "references.00.contact.01.fax" + [580] "references.00.contact.01.website" + [581] "references.00.contact.01.date_start" + [582] "references.00.contact.01.date_end" + [583] "references.00.contact.01.location" + [584] "references.00.database-provider.name" + [585] "references.00.database-provider.address" + [586] "references.00.database-provider.city" + [587] "references.00.database-provider.region" + [588] "references.00.database-provider.post-code" + [589] "references.00.database-provider.country" + [590] "references.00.database-provider.orcid" + [591] "references.00.database-provider.email" + [592] "references.00.database-provider.tel" + [593] "references.00.database-provider.fax" + [594] "references.00.database-provider.website" + [595] "references.00.database-provider.date-start" + [596] "references.00.database-provider.date-end" + [597] "references.00.database-provider.location" + [598] "references.00.editors.00.family_names" + [599] "references.00.editors.00.given_names" + [600] "references.00.editors.00.name_particle" + [601] "references.00.editors.00.name_suffix" + [602] "references.00.editors.00.alias" + [603] "references.00.editors.00.affiliation" + [604] "references.00.editors.00.address" + [605] "references.00.editors.00.city" + [606] "references.00.editors.00.region" + [607] "references.00.editors.00.post_code" + [608] "references.00.editors.00.country" + [609] "references.00.editors.00.orcid" + [610] "references.00.editors.00.email" + [611] "references.00.editors.00.tel" + [612] "references.00.editors.00.fax" + [613] "references.00.editors.00.website" + [614] "references.00.editors.01.name" + [615] "references.00.editors.01.address" + [616] "references.00.editors.01.city" + [617] "references.00.editors.01.region" + [618] "references.00.editors.01.post_code" + [619] "references.00.editors.01.country" + [620] "references.00.editors.01.orcid" + [621] "references.00.editors.01.email" + [622] "references.00.editors.01.tel" + [623] "references.00.editors.01.fax" + [624] "references.00.editors.01.website" + [625] "references.00.editors.01.date_start" + [626] "references.00.editors.01.date_end" + [627] "references.00.editors.01.location" + [628] "references.00.editors-series.00.family_names" + [629] "references.00.editors-series.00.given_names" + [630] "references.00.editors-series.00.name_particle" + [631] "references.00.editors-series.00.name_suffix" + [632] "references.00.editors-series.00.alias" + [633] "references.00.editors-series.00.affiliation" + [634] "references.00.editors-series.00.address" + [635] "references.00.editors-series.00.city" + [636] "references.00.editors-series.00.region" + [637] "references.00.editors-series.00.post_code" + [638] "references.00.editors-series.00.country" + [639] "references.00.editors-series.00.orcid" + [640] "references.00.editors-series.00.email" + [641] "references.00.editors-series.00.tel" + [642] "references.00.editors-series.00.fax" + [643] "references.00.editors-series.00.website" + [644] "references.00.editors-series.01.name" + [645] "references.00.editors-series.01.address" + [646] "references.00.editors-series.01.city" + [647] "references.00.editors-series.01.region" + [648] "references.00.editors-series.01.post_code" + [649] "references.00.editors-series.01.country" + [650] "references.00.editors-series.01.orcid" + [651] "references.00.editors-series.01.email" + [652] "references.00.editors-series.01.tel" + [653] "references.00.editors-series.01.fax" + [654] "references.00.editors-series.01.website" + [655] "references.00.editors-series.01.date_start" + [656] "references.00.editors-series.01.date_end" + [657] "references.00.editors-series.01.location" + [658] "references.00.institution.name" + [659] "references.00.institution.address" + [660] "references.00.institution.city" + [661] "references.00.institution.region" + [662] "references.00.institution.post-code" + [663] "references.00.institution.country" + [664] "references.00.institution.orcid" + [665] "references.00.institution.email" + [666] "references.00.institution.tel" + [667] "references.00.institution.fax" + [668] "references.00.institution.website" + [669] "references.00.institution.date-start" + [670] "references.00.institution.date-end" + [671] "references.00.institution.location" + [672] "references.00.location.name" + [673] "references.00.location.address" + [674] "references.00.location.city" + [675] "references.00.location.region" + [676] "references.00.location.post-code" + [677] "references.00.location.country" + [678] "references.00.location.orcid" + [679] "references.00.location.email" + [680] "references.00.location.tel" + [681] "references.00.location.fax" + [682] "references.00.location.website" + [683] "references.00.location.date-start" + [684] "references.00.location.date-end" + [685] "references.00.location.location" + [686] "references.00.publisher.name" + [687] "references.00.publisher.address" + [688] "references.00.publisher.city" + [689] "references.00.publisher.region" + [690] "references.00.publisher.post-code" + [691] "references.00.publisher.country" + [692] "references.00.publisher.orcid" + [693] "references.00.publisher.email" + [694] "references.00.publisher.tel" + [695] "references.00.publisher.fax" + [696] "references.00.publisher.website" + [697] "references.00.publisher.date-start" + [698] "references.00.publisher.date-end" + [699] "references.00.publisher.location" + [700] "references.00.recipients.00.family_names" + [701] "references.00.recipients.00.given_names" + [702] "references.00.recipients.00.name_particle" + [703] "references.00.recipients.00.name_suffix" + [704] "references.00.recipients.00.alias" + [705] "references.00.recipients.00.affiliation" + [706] "references.00.recipients.00.address" + [707] "references.00.recipients.00.city" + [708] "references.00.recipients.00.region" + [709] "references.00.recipients.00.post_code" + [710] "references.00.recipients.00.country" + [711] "references.00.recipients.00.orcid" + [712] "references.00.recipients.00.email" + [713] "references.00.recipients.00.tel" + [714] "references.00.recipients.00.fax" + [715] "references.00.recipients.00.website" + [716] "references.00.recipients.01.name" + [717] "references.00.recipients.01.address" + [718] "references.00.recipients.01.city" + [719] "references.00.recipients.01.region" + [720] "references.00.recipients.01.post_code" + [721] "references.00.recipients.01.country" + [722] "references.00.recipients.01.orcid" + [723] "references.00.recipients.01.email" + [724] "references.00.recipients.01.tel" + [725] "references.00.recipients.01.fax" + [726] "references.00.recipients.01.website" + [727] "references.00.recipients.01.date_start" + [728] "references.00.recipients.01.date_end" + [729] "references.00.recipients.01.location" + [730] "references.00.senders.00.family_names" + [731] "references.00.senders.00.given_names" + [732] "references.00.senders.00.name_particle" + [733] "references.00.senders.00.name_suffix" + [734] "references.00.senders.00.alias" + [735] "references.00.senders.00.affiliation" + [736] "references.00.senders.00.address" + [737] "references.00.senders.00.city" + [738] "references.00.senders.00.region" + [739] "references.00.senders.00.post_code" + [740] "references.00.senders.00.country" + [741] "references.00.senders.00.orcid" + [742] "references.00.senders.00.email" + [743] "references.00.senders.00.tel" + [744] "references.00.senders.00.fax" + [745] "references.00.senders.00.website" + [746] "references.00.senders.01.name" + [747] "references.00.senders.01.address" + [748] "references.00.senders.01.city" + [749] "references.00.senders.01.region" + [750] "references.00.senders.01.post_code" + [751] "references.00.senders.01.country" + [752] "references.00.senders.01.orcid" + [753] "references.00.senders.01.email" + [754] "references.00.senders.01.tel" + [755] "references.00.senders.01.fax" + [756] "references.00.senders.01.website" + [757] "references.00.senders.01.date_start" + [758] "references.00.senders.01.date_end" + [759] "references.00.senders.01.location" + [760] "references.00.translators.00.family_names" + [761] "references.00.translators.00.given_names" + [762] "references.00.translators.00.name_particle" + [763] "references.00.translators.00.name_suffix" + [764] "references.00.translators.00.alias" + [765] "references.00.translators.00.affiliation" + [766] "references.00.translators.00.address" + [767] "references.00.translators.00.city" + [768] "references.00.translators.00.region" + [769] "references.00.translators.00.post_code" + [770] "references.00.translators.00.country" + [771] "references.00.translators.00.orcid" + [772] "references.00.translators.00.email" + [773] "references.00.translators.00.tel" + [774] "references.00.translators.00.fax" + [775] "references.00.translators.00.website" + [776] "references.00.translators.01.name" + [777] "references.00.translators.01.address" + [778] "references.00.translators.01.city" + [779] "references.00.translators.01.region" + [780] "references.00.translators.01.post_code" + [781] "references.00.translators.01.country" + [782] "references.00.translators.01.orcid" + [783] "references.00.translators.01.email" + [784] "references.00.translators.01.tel" + [785] "references.00.translators.01.fax" + [786] "references.00.translators.01.website" + [787] "references.00.translators.01.date_start" + [788] "references.00.translators.01.date_end" + [789] "references.00.translators.01.location" + +# Convert a citation only + + Code + names(the_df) + Output + [1] "type" "title" + [3] "authors.00.family_names" "authors.00.given_names" + [5] "authors.01.family_names" "authors.01.given_names" + [7] "authors.02.family_names" "authors.02.given_names" + [9] "authors.03.name" "url" + [11] "keywords.00" "keywords.01" + [13] "keywords.02" "keywords.03" + [15] "keywords.04" "keywords.05" + [17] "keywords.06" "keywords.07" + [19] "abstract" "version" + +# Convert authors only + + Code + names(the_df) + Output + [1] "family_names" "given_names" + +# Convert list of authors + + Code + names(the_df) + Output + [1] "person.00.family_names" "person.00.given_names" + [3] "person.00.name_particle" "person.00.name_suffix" + [5] "person.00.alias" "person.00.affiliation" + [7] "person.00.address" "person.00.city" + [9] "person.00.region" "person.00.post_code" + [11] "person.00.country" "person.00.orcid" + [13] "person.00.email" "person.00.tel" + [15] "person.00.fax" "person.00.website" + [17] "person.01.name" "person.01.address" + [19] "person.01.city" "person.01.region" + [21] "person.01.post_code" "person.01.country" + [23] "person.01.orcid" "person.01.email" + [25] "person.01.tel" "person.01.fax" + [27] "person.01.website" "person.01.date_start" + [29] "person.01.date_end" "person.01.location" + diff --git a/tests/testthat/_snaps/deprecated.md b/tests/testthat/_snaps/deprecated.md index ac22bc72..59613433 100644 --- a/tests/testthat/_snaps/deprecated.md +++ b/tests/testthat/_snaps/deprecated.md @@ -5,5 +5,14 @@ Condition Warning: `cff_extract_to_bibtex()` was deprecated in cffr 0.5.0. - i Please use `cff_to_bibentry()` instead. + i Function renamed, use `cff_to_bibentry()` instead. + +# cff_to_bibtex + + Code + old1 <- cff_to_bibtex(a_cff) + Condition + Warning: + `cff_extract_to_bibtex()` was deprecated in cffr 0.5.0. + i Function renamed, use `cff_to_bibentry()` instead. diff --git a/tests/testthat/test-assertions.R b/tests/testthat/test-assertions.R index aaebfdb9..cd96eabe 100644 --- a/tests/testthat/test-assertions.R +++ b/tests/testthat/test-assertions.R @@ -19,6 +19,8 @@ test_that("Check is_email", { is_email("felix@nicebread@de"), is_email("felixnicebread.de"), is_email("@felixnicebread"), + is_email("a @felixnicebread.co"), + is_email("a@fe lixnicebread.de"), is_email(NULL), is_email(NA) ) diff --git a/tests/testthat/test-cff-class.R b/tests/testthat/test-cff-class.R new file mode 100644 index 00000000..200259b1 --- /dev/null +++ b/tests/testthat/test-cff-class.R @@ -0,0 +1,134 @@ +test_that("as data frame complete", { + path <- system.file("examples/CITATION_complete.cff", package = "cffr") + + the_cff <- cff_read(path) + + # To df + the_df <- as.data.frame(the_cff) + + expect_s3_class(the_df, "data.frame") + expect_equal(nrow(the_df), 1) + + # lengths + allf <- unlist(the_cff, recursive = TRUE, use.names = FALSE) + expect_identical(length(allf), ncol(the_df)) + + + expect_identical(ncol(the_df), length(unique(names(the_df)))) + + # As vector + df_as_v <- as.character(as.vector(the_df[1, ])) + + + expect_identical(allf, df_as_v) + expect_snapshot(names(the_df)) +}) + +test_that("as data frame partial", { + path <- system.file("examples/CITATION_complete.cff", package = "cffr") + + the_cff <- cff_read(path) + + # To df authors only + subcff <- the_cff$authors[[1]] + the_df <- as.data.frame(subcff) + expect_s3_class(the_df, "data.frame") + expect_equal(nrow(the_df), 1) + # lengths + allf <- unlist(subcff, recursive = TRUE, use.names = FALSE) + expect_identical(length(allf), ncol(the_df)) + expect_identical(ncol(the_df), length(unique(names(the_df)))) + + + + # To df references only + subcff <- the_cff$references[[1]] + the_df <- as.data.frame(subcff) + expect_s3_class(the_df, "data.frame") + expect_equal(nrow(the_df), 1) + # lengths + allf <- unlist(subcff, recursive = TRUE, use.names = FALSE) + expect_identical(length(allf), ncol(the_df)) + expect_identical(ncol(the_df), length(unique(names(the_df)))) +}) + + +test_that("Convert a citation only", { + path <- system.file("examples/DESCRIPTION_many_persons", package = "cffr") + a_cit <- cff_to_bibentry(cff_create(path)) + + the_cff <- cff_parse_citation(a_cit) + + # To df + the_df <- as.data.frame(the_cff) + + expect_s3_class(the_df, "data.frame") + expect_equal(nrow(the_df), 1) + + # lengths + allf <- unlist(the_cff, recursive = TRUE, use.names = FALSE) + expect_identical(length(allf), ncol(the_df)) + + + expect_identical(ncol(the_df), length(unique(names(the_df)))) + + # As vector + df_as_v <- as.character(as.vector(the_df[1, ])) + + + expect_identical(allf, df_as_v) + expect_snapshot(names(the_df)) +}) + + +test_that("Convert authors only", { + a_pers_list <- cff_parse_person_bibtex( + "A person and {A Entity inc.} and {One person} more" + ) + + the_cff <- a_pers_list[[1]] + + # To df + the_df <- as.data.frame(the_cff) + + expect_s3_class(the_df, "data.frame") + expect_equal(nrow(the_df), 1) + + # lengths + allf <- unlist(the_cff, recursive = TRUE, use.names = FALSE) + expect_identical(length(allf), ncol(the_df)) + + expect_identical(ncol(the_df), length(unique(names(the_df)))) + + # As vector + df_as_v <- as.character(as.vector(the_df[1, ])) + + + expect_identical(allf, df_as_v) + expect_snapshot(names(the_df)) +}) + +test_that("Convert list of authors", { + path <- system.file("examples/CITATION_complete.cff", package = "cffr") + + the_cff <- cff_read(path)$authors + + # To df + the_df <- as.data.frame(the_cff) + + expect_s3_class(the_df, "data.frame") + expect_equal(nrow(the_df), 1) + + # lengths + allf <- unlist(the_cff, recursive = TRUE, use.names = FALSE) + expect_identical(length(allf), ncol(the_df)) + + expect_identical(ncol(the_df), length(unique(names(the_df)))) + + # As vector + df_as_v <- as.character(as.vector(the_df[1, ])) + + + expect_identical(allf, df_as_v) + expect_snapshot(names(the_df)) +}) diff --git a/tests/testthat/test-deprecated.R b/tests/testthat/test-deprecated.R index 7097e013..26bc33eb 100644 --- a/tests/testthat/test-deprecated.R +++ b/tests/testthat/test-deprecated.R @@ -6,3 +6,12 @@ test_that("cff_extract_to_bibtex", { expect_identical(current, old1) }) + +test_that("cff_to_bibtex", { + a_cff <- cff_create("cffr") + + current <- cff_to_bibentry(a_cff) + expect_snapshot(old1 <- cff_to_bibtex(a_cff)) + + expect_identical(current, old1) +}) diff --git a/tests/testthat/test_ci/test-full_cff.R b/tests/testthat/test_ci/test-full_cff.R index 453fe002..2da3ad32 100644 --- a/tests/testthat/test_ci/test-full_cff.R +++ b/tests/testthat/test_ci/test-full_cff.R @@ -2,6 +2,7 @@ # DEPRECATED: SEE ./tests/testthat/test_ci/test-new.R file instead # ---------------------------------------------------------------- +# skip lint # test_that("Test ALL installed packages", { # expect_snapshot_output(print_snapshot("Sessioninfo", sessionInfo())) From 7567cd6d1591818ba9d856eab9ccbda2f29413b3 Mon Sep 17 00:00:00 2001 From: dieghernan Date: Thu, 15 Feb 2024 15:18:26 +0000 Subject: [PATCH 02/32] Review docs --- R/assertions.R | 4 +- R/cff_create.R | 258 +++++------------------------------------ R/cff_description.R | 46 -------- R/cff_from_bibtex.R | 14 ++- R/cff_gha_update.R | 12 +- R/cff_git_hook.R | 23 ++-- R/deprecated.R | 4 +- R/docs.R | 1 - R/utils-create.R | 234 +++++++++++++++++++++++++++++++++++++ man/cff_create.Rd | 34 +++--- man/cff_from_bibtex.Rd | 13 ++- man/cff_gha_update.Rd | 7 +- man/cff_git_hook.Rd | 18 +-- man/cff_write.Rd | 2 +- 14 files changed, 328 insertions(+), 342 deletions(-) delete mode 100644 R/cff_description.R create mode 100644 R/utils-create.R diff --git a/R/assertions.R b/R/assertions.R index 2ec03beb..ddf705f7 100644 --- a/R/assertions.R +++ b/R/assertions.R @@ -9,8 +9,8 @@ is_email <- function(email) { email <- trimws(as.character(email)) # See CFF validation schema - x <- grepl("^[\\S]+@[\\S]+\\.[\\S]{2,}$",email , - ignore.case = TRUE, perl = TRUE + x <- grepl("^[\\S]+@[\\S]+\\.[\\S]{2,}$", email, + ignore.case = TRUE, perl = TRUE ) x } diff --git a/R/cff_create.R b/R/cff_create.R index ddb05320..c61a457d 100644 --- a/R/cff_create.R +++ b/R/cff_create.R @@ -1,32 +1,36 @@ -#' Create `cff` object +#' Create a [`cff`][cff-class] object #' #' @description -#' Create a [`cff`] object from a given source for further manipulation. -#' Similar to [cff_write()], but returns a object rather than writing -#' directly to a file. See **Examples**. #' -#' @return A [`cff`] list object. +#' Create a [`cff`][cff-class] object from a given source for further +#' manipulation. This object can be written to a `*.cff ` file with +#' [cff_write()], see **Examples**. +#' +#' Most of the heavy lifting of \CRANpkg{cffr} is done via this function. +#' +#' @return A [`cff`][cff-class] list object. #' #' @family Core functions #' #' @export #' #' @param x The source that would be used for generating -#' the [`cff`] object. It could be: -#' * A missing value. That would retrieve the DESCRIPTION -#' file on your in-development package. -#' * An existing [`cff`] object, +#' the [`cff`][cff-class] object. It could be: +#' * A missing value. That would retrieve the `DESCRIPTION` file on your +#' in-development **R** package. +#' * An existing [`cff`][cff-class] object, #' * The name of an installed package (`"jsonlite"`), or -#' * Path to a DESCRIPTION file (`"*/DESCRIPTION*"`). +#' * Path to a `DESCRIPTION` file (`"./DESCRIPTION"`). #' -#' @param keys List of additional keys to add to the [`cff`] object. See +#' @param keys +#' List of additional keys to add to the [`cff`][cff-class] object. See #' **Details**. #' @param cff_version The Citation File Format schema version that the #' `CITATION.cff` file adheres to for providing the citation metadata. #' @param gh_keywords Logical `TRUE/FALSE`. If the package is hosted on #' GitHub, would you like to add the repo topics as keywords? #' @param dependencies Logical `TRUE/FALSE`. Would you like to add the -#' of your package to the `reference` key? +#' of your package to the `references` CFF key? #' @param authors_roles Roles to be considered as authors of the package when #' generating the `CITATION.cff` file. See **Details**. #' @@ -41,6 +45,7 @@ #' ``` #' #' `vignette("cffr", "cffr")` +#' #' @details #' #' It is possible to add additional keys not detected by [cff_create()] using @@ -59,11 +64,11 @@ #' ``` #' for additional details. #' -#' If `x` is a path to a DESCRIPTION file or `inst/CITATION`, is not present on -#' your package, \CRANpkg{cffr} would auto-generate a `preferred-citation` key -#' using the information provided on that file. +#' If `x` is a path to a `DESCRIPTION` file or `inst/CITATION`, is not present +#' on your package, \CRANpkg{cffr} would auto-generate a `preferred-citation` +#' key using the information provided on that file. #' -#' By default, only persons whose role in the DESCRIPTION file of the package +#' By default, only persons whose role in the `DESCRIPTION` file of the package #' is author (`"aut"`) or maintainer (`"cre"`) are considered to be authors #' of the package. The default setting can be controlled via the `authors_roles` #' parameter. See **Details** on [utils::person()] to get additional insights @@ -108,11 +113,8 @@ #' #' cff_create(demo_file, keys = list("contact" = new_contact)) #' } -cff_create <- function(x, - keys = list(), - cff_version = "1.2.0", - gh_keywords = TRUE, - dependencies = TRUE, +cff_create <- function(x, keys = list(), cff_version = "1.2.0", + gh_keywords = TRUE, dependencies = TRUE, authors_roles = c("aut", "cre")) { # On missing use package root if (missing(x)) x <- getwd() @@ -148,10 +150,7 @@ cff_create <- function(x, # If it doesn't exists look on the root # this is for call to installed packages with system.file() if (!file.exists(cit_path)) { - cit_path <- gsub( - "DESCRIPTION$", - "CITATION", x - ) + cit_path <- gsub("DESCRIPTION$", "CITATION", x) } if (file.exists(cit_path)) { citobj <- parse_r_citation(desc_path, cit_path) @@ -173,7 +172,8 @@ cff_create <- function(x, } cffobj <- cff_description(desc_path, cff_version, - gh_keywords = gh_keywords, authors_roles = authors_roles + gh_keywords = gh_keywords, + authors_roles = authors_roles ) } @@ -187,10 +187,7 @@ cff_create <- function(x, if (dependencies) { deps <- parse_dependencies(desc_path, instpack) - cffobjend$references <- unique(c( - cffobjend$references, - deps - )) + cffobjend$references <- unique(c(cffobjend$references, deps)) } # Additional keys @@ -206,210 +203,9 @@ cff_create <- function(x, cffobjend <- cffobjend[cff_schema_keys()] # Enhance authors info - if (!is.null(cffobjend$`preferred-citation`)) { cffobjend$`preferred-citation`$authors <- enhance_pref_authors(cffobjend) } cffobjend <- as.cff(cffobjend) cffobjend } - - -#' Merge the information of a parsed description with a parsed citation -#' @noRd -merge_desc_cit <- function(cffobj, citobj) { - # If no citobj then return null - - if (is.null(citobj)) { - return(cffobj) - } - - # Add doi from citation if missing - if (is.null(cffobj$doi)) { - cffobj$doi <- clean_str(citobj[[1]]$doi) - } - cffobjend <- c(cffobj, - "preferred-citation" = citobj[1], - references = list(citobj[-1]) - ) - - - - # Merge identifiers - oldids <- cffobjend$identifiers - cffobjend$identifiers <- c( - citobj[[1]]$identifiers, - oldids - ) - - # Reorder - cffobjfinal <- c( - cffobjend[!names(cffobjend) %in% c("identifiers", "references")], - cffobjend["identifiers"], - cffobjend["references"] - ) - - cffobjfinal <- drop_null(cffobjfinal) - - return(cffobjfinal) -} - -#' Enhance authors info from preferred-citation using metadata from DESCRIPTION -#' @noRd -enhance_pref_authors <- function(cffobjend) { - # Create index of authors extracted from DESCRIPTION (First cff level) - auth_desc <- cffobjend$authors - key_aut_desc <- lapply(auth_desc, function(x) { - l <- list(x["family-names"], x["given-names"], x["name"]) - l <- unlist(drop_null(l)) - tolower(paste0(l, collapse = "")) - }) - names(auth_desc) <- unlist(key_aut_desc) - - # Create index of authors from preferred-citation - auth_pref <- cffobjend$`preferred-citation`$authors - key_aut_cit <- lapply(auth_pref, function(x) { - l <- list(x["family-names"], x["given-names"], x["name"]) - l <- unlist(drop_null(l)) - tolower(paste0(l, collapse = "")) - }) - names(auth_pref) <- unlist(key_aut_cit) - - # Add missing keys to authors - enhancedauth <- lapply(names(auth_pref), function(x) { - newdata <- auth_desc[x] - olddata <- auth_pref[x] - - # New fields only - oldkeys <- names(olddata[[1]]) - newkeys <- names(newdata[[1]]) - fieldstoadd <- newdata[[1]][!newkeys %in% oldkeys] - - newinfo <- c(olddata[[1]], fieldstoadd) - - newinfo - }) - - enhancedauth -} - - -parse_dependencies <- function(desc_path, - instpack = as.character( - installed.packages()[, "Package"] - )) { - # nocov start - if (!is.character(desc_path)) { - return(NULL) - } - if (!file.exists(desc_path)) { - return(NULL) - } - # nocov end - - getdeps <- desc::desc(desc_path) - - deps <- getdeps$get_deps() - - # Adapt version - - deps$version_clean <- gsub("*", "", deps$version, fixed = TRUE) - - # Save copy for later - origdeps <- deps - - # Dedupe rows - deps <- unique(deps[, c("package", "version_clean")]) - - - # Get dependency type and add to scope - scope <- vapply(deps$package, - FUN.VALUE = character(1), - function(x) { - y <- origdeps[origdeps$package == x, "type"] - - y[1] - } - ) - deps$scope <- scope - - av_deps <- deps[deps$package %in% c("R", instpack), ] - - # Get references from DESCRIPTION of dependencies - cff_deps <- lapply(seq_len(nrow(av_deps)), function(y) { - n <- av_deps[y, ] - - if (n$package == "R") { - mod <- cff_parse_citation(citation()[1]) - mod$year <- format(Sys.Date(), "%Y") - } else { - mod <- try(cff_parse_citation(citation(n$package, auto = TRUE)[1]), - silent = TRUE - ) - - if (inherits(mod, "try-error")) { - return(NULL) - } - - # Simplified version of the cff obj - # Avoid cluttering the output - mod$abstract <- mod$title - mod$title <- n$package - } - - mod$type <- "software" - mod$version <- ifelse(is.na(n$version_clean), - NULL, - paste(n$version_clean) - ) - # Get url and repo from package DESCRIPTION - # urls from citation() vary due to auto = TRUE - dfile <- system.file("DESCRIPTION", package = n$package) - - if (file.exists(dfile)) { - pkg <- desc::desc(dfile) - mod$url <- parse_desc_urls(pkg)$url - mod$repository <- parse_desc_repository(pkg) - } - - mod <- drop_null(mod) - - # Get year - date_rel <- mod[["date-released"]] - - if (is.null(date_rel)) { - year <- format(Sys.Date(), "%Y") - } else { - year <- format(as.Date(date_rel), "%Y") - } - - mod$year <- year - mod$notes <- clean_str(n$scope) - - # Re-arrange - mod <- c( - mod[c( - "type", - "title", "abstract", - "notes", - "url", "repository" - )], - mod[!names(mod) %in% c( - "type", - "title", "abstract", - "notes", - "url", "repository" - )] - ) - - mod <- as.cff(mod) - }) - - cff_deps <- drop_null(cff_deps) - - cff_deps <- unique(cff_deps) - - class(cff_deps) <- "cff" - - return(cff_deps) -} diff --git a/R/cff_description.R b/R/cff_description.R deleted file mode 100644 index b2676718..00000000 --- a/R/cff_description.R +++ /dev/null @@ -1,46 +0,0 @@ -#' @noRd -cff_description <- function(desc_path = "DESCRIPTION", - cff_version = "1.2.0", - gh_keywords = TRUE, - authors_roles = c("aut", "cre")) { - pkg <- desc::desc(desc_path) - pkg$coerce_authors_at_r() - - msg <- paste0( - 'To cite package "', - pkg$get("Package"), - '" in publications use:' - ) - - - list_fields <- list( - "cff-version" = cff_version, - message = msg, - type = "software", - title = parse_desc_title(pkg), - version = parse_desc_version(pkg), - authors = parse_desc_authors(pkg, authors_roles = authors_roles), - abstract = parse_desc_abstract(pkg), - repository = parse_desc_repository(pkg), - "repository-code" = parse_desc_urls(pkg)$repo, - url = parse_desc_urls(pkg)$url, - identifiers = parse_desc_urls(pkg)$identifiers, - "date-released" = parse_desc_date_released(pkg), - contact = parse_desc_contacts(pkg), - keywords = parse_desc_keywords(pkg), - license = unlist(parse_desc_license(pkg)) - ) - - if (gh_keywords) { - ghtopics <- parse_ghtopics(list_fields) - list_fields$keywords <- unique( - c( - list_fields$keywords, - ghtopics - ) - ) - } - - list_fields <- as.cff(list_fields) - list_fields -} diff --git a/R/cff_from_bibtex.R b/R/cff_from_bibtex.R index 302617aa..6c26ad80 100644 --- a/R/cff_from_bibtex.R +++ b/R/cff_from_bibtex.R @@ -1,12 +1,12 @@ -#' Create a cff object from BibTeX entries +#' Create a [`cff`][cff-class] object from BibTeX entries #' #' @description #' #' Extract the information of a BibTeX file or BibTeX entry and creates the -#' corresponding [`cff`] object with [cff_parse_citation()]. +#' corresponding [`cff`][cff-class] object with [cff_parse_citation()]. #' -#' @param x The source that would be used for generating the `cff` object. A -#' `character` object indicating either: +#' @param x The source that would be used for generating the +#' [`cff`][cff-class] object. Must be `character` object indicating either: #' - The path to a BibTeX file. #' - A vector of characters with the full BibTeX string. See **Examples** #' @param encoding Encoding to be assumed for `x`. See [readLines()]. @@ -14,7 +14,9 @@ #' #' @family BibTeX helpers #' -#' @return A [`cff`] object ready to be used on [cff_create()]. +#' @return +#' A [`cff`][cff-class] object ready to be used with other functions (i.e. +#' [cff_create()]. #' #' @export #' @@ -26,7 +28,7 @@ #' @seealso #' #' `vignette("bibtex_cff", package = "cffr")` to learn about the mapping of -#' information between BibTeX and CITATION.cff. +#' information between BibTeX and `CITATION.cff`. #' #' @examples #' if (requireNamespace("bibtex", quietly = TRUE)) { diff --git a/R/cff_gha_update.R b/R/cff_gha_update.R index 5a066074..17c365b3 100644 --- a/R/cff_gha_update.R +++ b/R/cff_gha_update.R @@ -1,15 +1,16 @@ -#' Install a cffr GitHub Action +#' Install a \CRANpkg{cffr} GitHub Action #' #' @description #' #' This function would install a GitHub Action on your repo. The action #' will update your `CITATION.cff` when any of these events occur: #' - You publish a new release of the package. -#' - Your DESCRIPTION or inst/CITATION are modified. +#' - Your `DESCRIPTION` or `inst/CITATION` are modified. #' - The action can be run also manually. #' #' @param path Project directory -#' @param overwrite If already present, do you want to overwrite your action? +#' @param overwrite Logical. If already present, do you want to overwrite your +#' action? #' #' @return Invisible, this function is called by its side effects. #' @@ -39,9 +40,8 @@ cff_gha_update <- function(path = ".", newfile <- file.path(destdir, "update-citation-cff.yaml") if (!file.exists(newfile) || isTRUE(overwrite)) { - cli::cli_alert_success( - "Installing {.file {newfile}}" - ) + cli::cli_alert_success("Installing {.file {newfile}}") + file.copy(system.file("yaml/update-citation-cff.yaml", package = "cffr"), newfile, overwrite = TRUE diff --git a/R/cff_git_hook.R b/R/cff_git_hook.R index 0c54797b..d04e63c5 100644 --- a/R/cff_git_hook.R +++ b/R/cff_git_hook.R @@ -2,8 +2,7 @@ #' #' @description #' -#' Install a -#' [pre-commit +#' Install a [pre-commit #' hook](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks#_committing_workflow_hooks) #' that remembers you to update your `CITATION.cff` file. #' @@ -25,9 +24,9 @@ #' A pre-commit hook is a script that identifies simple issues before #' submission to code review. This pre-commit hook would warn you if any of the #' following conditions are met: -#' - You included in a commit your DESCRIPTION or inst/CITATION file, you +#' - You included in a commit your `DESCRIPTION` or `inst/CITATION` file, you #' are not including your `CITATION.cff` and the `CITATION.cff` file is -#' "older" than any of your DESCRIPTION or inst/CITATION file, or +#' "older" than any of your `DESCRIPTION` or `inst/CITATION` file, or #' - You have updated your `CITATION.cff` but you are not including it on #' your commit. #' @@ -37,14 +36,15 @@ #' `CITATION.cff`. However, the mechanism of detection is not perfect and would #' be triggered also even if you have tried to update your `CITATION.cff` file. #' -#' This is typically the case when you have updated your DESCRIPTION or -#' inst/CITATION files but those changes doesn't make a change on your +#' This is typically the case when you have updated your `DESCRIPTION` or +#' `inst/CITATION` files but those changes doesn't make a change on your #' `CITATION.cff` file (i.e. you are including new dependencies). #' #' In those cases, you can override the check running `git commit --no-verify` -#' on the Terminal tab. If you are using -#' RStudio you can run also this command from a R script by selecting that -#' line and sending it to the Terminal using: +#' on the terminal. +#' +#' If you are using **RStudio** you can run also this command from a **R** +#' script by selecting that line and sending it to the terminal using: #' #' - `Ctrl+Alt+Enter` (Windows & Linux), or #' - `Cmd+Option+Return` (Mac). @@ -63,10 +63,7 @@ cff_git_hook_install <- function() { bash_file <- system.file("bash/citation-cff-pre-commit.sh", package = "cffr") if (requireNamespace("usethis", quietly = TRUE)) { - usethis::use_git_hook( - "pre-commit", - readLines(con = bash_file) - ) + usethis::use_git_hook("pre-commit", readLines(con = bash_file)) } else { cli::cli_alert_danger( paste0( diff --git a/R/deprecated.R b/R/deprecated.R index 31a80b52..f717e218 100644 --- a/R/deprecated.R +++ b/R/deprecated.R @@ -33,8 +33,8 @@ cff_extract_to_bibtex <- function(x, #' @rdname renamed_cff_to_bib #' @export #' @keywords internal -cff_to_bibtex <- function(x, - what = c("preferred", "references", "all")) { +cff_to_bibtex <- function(x, + what = c("preferred", "references", "all")) { if (requireNamespace("lifecycle", quietly = TRUE)) { lifecycle::deprecate_soft( "0.5.0", "cff_extract_to_bibtex()", diff --git a/R/docs.R b/R/docs.R index 164afee7..98e41ea8 100644 --- a/R/docs.R +++ b/R/docs.R @@ -9,4 +9,3 @@ #' ```{r child = "man/chunks/cffclass.Rmd"} #' ``` NULL - diff --git a/R/utils-create.R b/R/utils-create.R new file mode 100644 index 00000000..4a553d0c --- /dev/null +++ b/R/utils-create.R @@ -0,0 +1,234 @@ +#' @noRd +cff_description <- function(desc_path = "DESCRIPTION", + cff_version = "1.2.0", + gh_keywords = TRUE, + authors_roles = c("aut", "cre")) { + pkg <- desc::desc(desc_path) + pkg$coerce_authors_at_r() + + msg <- paste0( + 'To cite package "', + pkg$get("Package"), + '" in publications use:' + ) + + + list_fields <- list( + "cff-version" = cff_version, + message = msg, + type = "software", + title = parse_desc_title(pkg), + version = parse_desc_version(pkg), + authors = parse_desc_authors(pkg, authors_roles = authors_roles), + abstract = parse_desc_abstract(pkg), + repository = parse_desc_repository(pkg), + "repository-code" = parse_desc_urls(pkg)$repo, + url = parse_desc_urls(pkg)$url, + identifiers = parse_desc_urls(pkg)$identifiers, + "date-released" = parse_desc_date_released(pkg), + contact = parse_desc_contacts(pkg), + keywords = parse_desc_keywords(pkg), + license = unlist(parse_desc_license(pkg)) + ) + + if (gh_keywords) { + ghtopics <- parse_ghtopics(list_fields) + list_fields$keywords <- unique( + c( + list_fields$keywords, + ghtopics + ) + ) + } + + list_fields <- as.cff(list_fields) + list_fields +} + + + +#' Merge the information of a parsed description with a parsed citation +#' @noRd +merge_desc_cit <- function(cffobj, citobj) { + # If no citobj then return null + + if (is.null(citobj)) { + return(cffobj) + } + + # Add doi from citation if missing + if (is.null(cffobj$doi)) { + cffobj$doi <- clean_str(citobj[[1]]$doi) + } + cffobjend <- c(cffobj, + "preferred-citation" = citobj[1], + references = list(citobj[-1]) + ) + + + + # Merge identifiers + oldids <- cffobjend$identifiers + cffobjend$identifiers <- c(citobj[[1]]$identifiers, oldids) + + # Reorder + cffobjfinal <- c( + cffobjend[!names(cffobjend) %in% c("identifiers", "references")], + cffobjend["identifiers"], + cffobjend["references"] + ) + + cffobjfinal <- drop_null(cffobjfinal) + + return(cffobjfinal) +} + +#' Enhance authors info from preferred-citation using metadata from DESCRIPTION +#' @noRd +enhance_pref_authors <- function(cffobjend) { + # Create index of authors extracted from DESCRIPTION (First cff level) + auth_desc <- cffobjend$authors + key_aut_desc <- lapply(auth_desc, function(x) { + l <- list(x["family-names"], x["given-names"], x["name"]) + l <- unlist(drop_null(l)) + tolower(paste0(l, collapse = "")) + }) + names(auth_desc) <- unlist(key_aut_desc) + + # Create index of authors from preferred-citation + auth_pref <- cffobjend$`preferred-citation`$authors + key_aut_cit <- lapply(auth_pref, function(x) { + l <- list(x["family-names"], x["given-names"], x["name"]) + l <- unlist(drop_null(l)) + tolower(paste0(l, collapse = "")) + }) + names(auth_pref) <- unlist(key_aut_cit) + + # Add missing keys to authors + enhancedauth <- lapply(names(auth_pref), function(x) { + newdata <- auth_desc[x] + olddata <- auth_pref[x] + + # New fields only + oldkeys <- names(olddata[[1]]) + newkeys <- names(newdata[[1]]) + fieldstoadd <- newdata[[1]][!newkeys %in% oldkeys] + + newinfo <- c(olddata[[1]], fieldstoadd) + + newinfo + }) + + enhancedauth +} + + +parse_dependencies <- function(desc_path, + instpack = as.character( + installed.packages()[, "Package"] + )) { + # nocov start + if (!is.character(desc_path)) { + return(NULL) + } + if (!file.exists(desc_path)) { + return(NULL) + } + # nocov end + + getdeps <- desc::desc(desc_path) + + deps <- getdeps$get_deps() + + # Adapt version + + deps$version_clean <- gsub("*", "", deps$version, fixed = TRUE) + + # Save copy for later + origdeps <- deps + + # Dedupe rows + deps <- unique(deps[, c("package", "version_clean")]) + + + # Get dependency type and add to scope + scope <- vapply(deps$package, function(x) { + y <- origdeps[origdeps$package == x, "type"] + + y[1] + }, FUN.VALUE = character(1)) + deps$scope <- scope + + av_deps <- deps[deps$package %in% c("R", instpack), ] + + # Get references from DESCRIPTION of dependencies + cff_deps <- lapply(seq_len(nrow(av_deps)), function(y) { + n <- av_deps[y, ] + + if (n$package == "R") { + mod <- cff_parse_citation(citation()[1]) + mod$year <- format(Sys.Date(), "%Y") + } else { + mod <- try(cff_parse_citation(citation(n$package, auto = TRUE)[1]), + silent = TRUE + ) + + if (inherits(mod, "try-error")) { + return(NULL) + } + + # Simplified version of the cff obj + # Avoid cluttering the output + mod$abstract <- mod$title + mod$title <- n$package + } + + mod$type <- "software" + mod$version <- ifelse(is.na(n$version_clean), + NULL, + paste(n$version_clean) + ) + # Get url and repo from package DESCRIPTION + # urls from citation() vary due to auto = TRUE + dfile <- system.file("DESCRIPTION", package = n$package) + + if (file.exists(dfile)) { + pkg <- desc::desc(dfile) + mod$url <- parse_desc_urls(pkg)$url + mod$repository <- parse_desc_repository(pkg) + } + + mod <- drop_null(mod) + + # Get year + date_rel <- mod[["date-released"]] + + if (is.null(date_rel)) { + year <- format(Sys.Date(), "%Y") + } else { + year <- format(as.Date(date_rel), "%Y") + } + + mod$year <- year + mod$notes <- clean_str(n$scope) + + # Re-arrange + mod <- c( + mod[c("type", "title", "abstract", "notes", "url", "repository")], + mod[!names(mod) %in% c( + "type", "title", "abstract", "notes", + "url", "repository" + )] + ) + + mod <- as.cff(mod) + }) + + cff_deps <- drop_null(cff_deps) + + cff_deps <- unique(cff_deps) + + class(cff_deps) <- "cff" + + return(cff_deps) +} diff --git a/man/cff_create.Rd b/man/cff_create.Rd index 98562aef..33e8da16 100644 --- a/man/cff_create.Rd +++ b/man/cff_create.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/cff_create.R \name{cff_create} \alias{cff_create} -\title{Create \code{cff} object} +\title{Create a \code{\link[=cff-class]{cff}} object} \usage{ cff_create( x, @@ -15,16 +15,16 @@ cff_create( } \arguments{ \item{x}{The source that would be used for generating -the \code{\link{cff}} object. It could be: +the \code{\link[=cff-class]{cff}} object. It could be: \itemize{ -\item A missing value. That would retrieve the DESCRIPTION -file on your in-development package. -\item An existing \code{\link{cff}} object, +\item A missing value. That would retrieve the \code{DESCRIPTION} file on your +in-development \strong{R} package. +\item An existing \code{\link[=cff-class]{cff}} object, \item The name of an installed package (\code{"jsonlite"}), or -\item Path to a DESCRIPTION file (\code{"*/DESCRIPTION*"}). +\item Path to a \code{DESCRIPTION} file (\code{"./DESCRIPTION"}). }} -\item{keys}{List of additional keys to add to the \code{\link{cff}} object. See +\item{keys}{List of additional keys to add to the \code{\link[=cff-class]{cff}} object. See \strong{Details}.} \item{cff_version}{The Citation File Format schema version that the @@ -34,18 +34,20 @@ file on your in-development package. GitHub, would you like to add the repo topics as keywords?} \item{dependencies}{Logical \code{TRUE/FALSE}. Would you like to add the -of your package to the \code{reference} key?} +of your package to the \code{references} CFF key?} \item{authors_roles}{Roles to be considered as authors of the package when generating the \code{CITATION.cff} file. See \strong{Details}.} } \value{ -A \code{\link{cff}} list object. +A \code{\link[=cff-class]{cff}} list object. } \description{ -Create a \code{\link{cff}} object from a given source for further manipulation. -Similar to \code{\link[=cff_write]{cff_write()}}, but returns a object rather than writing -directly to a file. See \strong{Examples}. +Create a \code{\link[=cff-class]{cff}} object from a given source for further +manipulation. This object can be written to a \verb{*.cff } file with +\code{\link[=cff_write]{cff_write()}}, see \strong{Examples}. + +Most of the heavy lifting of \CRANpkg{cffr} is done via this function. } \details{ It is possible to add additional keys not detected by \code{\link[=cff_create]{cff_create()}} using @@ -56,11 +58,11 @@ Please refer to \href{https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md}{Guide to Citation File Format schema version 1.2.0}. for additional details. -If \code{x} is a path to a DESCRIPTION file or \code{inst/CITATION}, is not present on -your package, \CRANpkg{cffr} would auto-generate a \code{preferred-citation} key -using the information provided on that file. +If \code{x} is a path to a \code{DESCRIPTION} file or \code{inst/CITATION}, is not present +on your package, \CRANpkg{cffr} would auto-generate a \code{preferred-citation} +key using the information provided on that file. -By default, only persons whose role in the DESCRIPTION file of the package +By default, only persons whose role in the \code{DESCRIPTION} file of the package is author (\code{"aut"}) or maintainer (\code{"cre"}) are considered to be authors of the package. The default setting can be controlled via the \code{authors_roles} parameter. See \strong{Details} on \code{\link[utils:person]{utils::person()}} to get additional insights diff --git a/man/cff_from_bibtex.Rd b/man/cff_from_bibtex.Rd index d87fc3b4..d0c1d327 100644 --- a/man/cff_from_bibtex.Rd +++ b/man/cff_from_bibtex.Rd @@ -2,13 +2,13 @@ % Please edit documentation in R/cff_from_bibtex.R \name{cff_from_bibtex} \alias{cff_from_bibtex} -\title{Create a cff object from BibTeX entries} +\title{Create a \code{\link[=cff-class]{cff}} object from BibTeX entries} \usage{ cff_from_bibtex(x, encoding = "UTF-8", ...) } \arguments{ -\item{x}{The source that would be used for generating the \code{cff} object. A -\code{character} object indicating either: +\item{x}{The source that would be used for generating the +\code{\link[=cff-class]{cff}} object. Must be \code{character} object indicating either: \itemize{ \item The path to a BibTeX file. \item A vector of characters with the full BibTeX string. See \strong{Examples} @@ -19,11 +19,12 @@ cff_from_bibtex(x, encoding = "UTF-8", ...) \item{...}{Other arguments passed to \code{\link[bibtex:read.bib]{bibtex::read.bib()}}.} } \value{ -A \code{\link{cff}} object ready to be used on \code{\link[=cff_create]{cff_create()}}. +A \code{\link[=cff-class]{cff}} object ready to be used with other functions (i.e. +\code{\link[=cff_create]{cff_create()}}. } \description{ Extract the information of a BibTeX file or BibTeX entry and creates the -corresponding \code{\link{cff}} object with \code{\link[=cff_parse_citation]{cff_parse_citation()}}. +corresponding \code{\link[=cff-class]{cff}} object with \code{\link[=cff_parse_citation]{cff_parse_citation()}}. } \details{ This function requires the package \CRANpkg{bibtex} (>= 0.5.0), that is @@ -60,7 +61,7 @@ if (requireNamespace("bibtex", quietly = TRUE)) { } \seealso{ \code{vignette("bibtex_cff", package = "cffr")} to learn about the mapping of -information between BibTeX and CITATION.cff. +information between BibTeX and \code{CITATION.cff}. Other BibTeX helpers: \code{\link{cff_to_bibentry}()}, diff --git a/man/cff_gha_update.Rd b/man/cff_gha_update.Rd index e522d757..9242f2f4 100644 --- a/man/cff_gha_update.Rd +++ b/man/cff_gha_update.Rd @@ -2,14 +2,15 @@ % Please edit documentation in R/cff_gha_update.R \name{cff_gha_update} \alias{cff_gha_update} -\title{Install a cffr GitHub Action} +\title{Install a \CRANpkg{cffr} GitHub Action} \usage{ cff_gha_update(path = ".", overwrite = FALSE) } \arguments{ \item{path}{Project directory} -\item{overwrite}{If already present, do you want to overwrite your action?} +\item{overwrite}{Logical. If already present, do you want to overwrite your +action?} } \value{ Invisible, this function is called by its side effects. @@ -19,7 +20,7 @@ This function would install a GitHub Action on your repo. The action will update your \code{CITATION.cff} when any of these events occur: \itemize{ \item You publish a new release of the package. -\item Your DESCRIPTION or inst/CITATION are modified. +\item Your \code{DESCRIPTION} or \code{inst/CITATION} are modified. \item The action can be run also manually. } } diff --git a/man/cff_git_hook.Rd b/man/cff_git_hook.Rd index f7926930..c93875b1 100644 --- a/man/cff_git_hook.Rd +++ b/man/cff_git_hook.Rd @@ -14,8 +14,7 @@ cff_git_hook_remove() Invisible. This function is called for its side effects. } \description{ -Install a -\href{https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks#_committing_workflow_hooks}{pre-commit hook} +Install a \href{https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks#_committing_workflow_hooks}{pre-commit hook} that remembers you to update your \code{CITATION.cff} file. } \details{ @@ -26,9 +25,9 @@ A pre-commit hook is a script that identifies simple issues before submission to code review. This pre-commit hook would warn you if any of the following conditions are met: \itemize{ -\item You included in a commit your DESCRIPTION or inst/CITATION file, you +\item You included in a commit your \code{DESCRIPTION} or \code{inst/CITATION} file, you are not including your \code{CITATION.cff} and the \code{CITATION.cff} file is -"older" than any of your DESCRIPTION or inst/CITATION file, or +"older" than any of your \code{DESCRIPTION} or \code{inst/CITATION} file, or \item You have updated your \code{CITATION.cff} but you are not including it on your commit. } @@ -38,14 +37,15 @@ The pre-commit hook may prevent you to commit if you are not updating your \code{CITATION.cff}. However, the mechanism of detection is not perfect and would be triggered also even if you have tried to update your \code{CITATION.cff} file. -This is typically the case when you have updated your DESCRIPTION or -inst/CITATION files but those changes doesn't make a change on your +This is typically the case when you have updated your \code{DESCRIPTION} or +\code{inst/CITATION} files but those changes doesn't make a change on your \code{CITATION.cff} file (i.e. you are including new dependencies). In those cases, you can override the check running \verb{git commit --no-verify} -on the Terminal tab. If you are using -RStudio you can run also this command from a R script by selecting that -line and sending it to the Terminal using: +on the terminal. + +If you are using \strong{RStudio} you can run also this command from a \strong{R} +script by selecting that line and sending it to the terminal using: \itemize{ \item \code{Ctrl+Alt+Enter} (Windows & Linux), or \item \code{Cmd+Option+Return} (Mac). diff --git a/man/cff_write.Rd b/man/cff_write.Rd index cc4e592e..25dad11c 100644 --- a/man/cff_write.Rd +++ b/man/cff_write.Rd @@ -39,7 +39,7 @@ file on your in-development package. GitHub, would you like to add the repo topics as keywords?} \item{dependencies}{Logical \code{TRUE/FALSE}. Would you like to add the -of your package to the \code{reference} key?} +of your package to the \code{references} CFF key?} \item{validate}{Logical \code{TRUE/FALSE}. Should the new file be validated using \code{\link[=cff_validate]{cff_validate()}}?} From 784c8dcdb69c1f6351315023ccff7b972cb90628 Mon Sep 17 00:00:00 2001 From: Diego H Date: Fri, 16 Feb 2024 18:29:40 +0100 Subject: [PATCH 03/32] Add as.person method --- NAMESPACE | 1 + NEWS.md | 8 +- R/cff-methods.R | 80 +- R/cff_create.R | 2 +- R/cff_from_bibtex.R | 5 +- R/deprecated.R | 2 +- README.md | 2 +- codemeta.json | 2 +- man/cff-class.Rd | 46 + man/cff_create.Rd | 2 +- man/chunks/cffclass.Rmd | 14 +- man/chunks/value.Rmd | 2 + tests/testthat/_snaps/cff-methods.md | 907 ++++++++++++++++++ tests/testthat/_snaps/deprecated.md | 2 +- .../{test-cff-class.R => test-cff-methods.R} | 54 ++ 15 files changed, 1111 insertions(+), 18 deletions(-) create mode 100644 man/chunks/value.Rmd create mode 100644 tests/testthat/_snaps/cff-methods.md rename tests/testthat/{test-cff-class.R => test-cff-methods.R} (70%) diff --git a/NAMESPACE b/NAMESPACE index 2d684bc2..d766eb4c 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,6 +1,7 @@ # Generated by roxygen2: do not edit by hand S3method(as.data.frame,cff) +S3method(as.person,cff) S3method(c,cff) S3method(print,cff) export(as.cff) diff --git a/NEWS.md b/NEWS.md index 5043f7bd..20d53857 100644 --- a/NEWS.md +++ b/NEWS.md @@ -6,7 +6,13 @@ `cff_to_bibentry()`. Previous names of this function were `cff_to_bibtex()` and `cff_extract_to_bibtex()` that are now superseded. - New methods: - - `as.data.frame.cff()` + - `as.data.frame.cff().` + - `as.person.cff()`, that provides results **only** for CFF keys defined + as + [person](https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md#definitionsperson) + or + [entity](https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md#definitionsentity) + (e.g. authors, contacts, editors, publisher). ## Changes on bibtex crosswalk diff --git a/R/cff-methods.R b/R/cff-methods.R index 2aa0e012..d56c4c19 100644 --- a/R/cff-methods.R +++ b/R/cff-methods.R @@ -15,14 +15,13 @@ print.cff <- function(x, ...) { #' #' @noRd #' @export -c.cff <- - function(..., recursive = FALSE) { - args <- list(...) - args <- lapply(args, unclass) - rval <- do.call("c", args) - class(rval) <- "cff" - rval - } +c.cff <- function(..., recursive = FALSE) { + args <- list(...) + args <- lapply(args, unclass) + rval <- do.call("c", args) + class(rval) <- "cff" + rval +} #' Coerce to a Data Frame @@ -44,3 +43,68 @@ as.data.frame.cff <- function(x, row.names = NULL, optional = FALSE, ...) { return(the_df) } + +#' Persons +#' +#' @noRd +#' @export +as.person.cff <- function(x) { + # If single enclose on a list + is_single <- any(grepl("^name$|^given-names|^family-names", names(x))) + + if (is_single) x <- list(x) + + + pers <- lapply(x, make_r_person) + + # If not all extracted, malformed, return null + if (!all(lengths(pers) > 0)) { + return(person()) + } + do.call(c, pers) +} +make_r_person <- function(x) { + if (is.null(names(x))) { + return(person()) + } + checknames <- grepl("^name$|given-names|family-names", names(x)) + if (!isTRUE(any(checknames))) { + return(person()) + } + # Prepare list + # Family is special key + fam1 <- clean_str(x$name) + fam2 <- clean_str( + paste( + clean_str(x$`name-particle`), clean_str(x$`family-names`), + clean_str(x$`name-suffix`) + ) + ) + + given <- clean_str(x$`given-names`) + family <- clean_str(c(fam1, fam2)) + + # Make comments + x_comments <- x[!names(x) %in% c( + "family-names", "given-names", + "name-particle", "name-suffix", "email" + )] + + x_comments <- lapply(x_comments, clean_str) + x_comments <- unlist(x_comments, use.names = TRUE) + + # Prepare ORCID + x_comments <- gsub("^https://orcid.org/", "", x_comments) + nm <- gsub("orcid", "ORCID", names(x_comments), fixed = TRUE) + names(x_comments) <- nm + + pers_list <- list( + given = given, + family = family, + email = clean_str(x$email), + comment = x_comments + ) + + + do.call(person, pers_list) +} diff --git a/R/cff_create.R b/R/cff_create.R index c61a457d..4cc1965f 100644 --- a/R/cff_create.R +++ b/R/cff_create.R @@ -8,7 +8,7 @@ #' #' Most of the heavy lifting of \CRANpkg{cffr} is done via this function. #' -#' @return A [`cff`][cff-class] list object. +#' @return A [`cff`][cff-class] object. #' #' @family Core functions #' diff --git a/R/cff_from_bibtex.R b/R/cff_from_bibtex.R index 6c26ad80..3036af33 100644 --- a/R/cff_from_bibtex.R +++ b/R/cff_from_bibtex.R @@ -15,8 +15,9 @@ #' @family BibTeX helpers #' #' @return -#' A [`cff`][cff-class] object ready to be used with other functions (i.e. -#' [cff_create()]. +#' +#' ```{r child = "man/chunks/value.Rmd"} +#' ``` #' #' @export #' diff --git a/R/deprecated.R b/R/deprecated.R index f717e218..a5079304 100644 --- a/R/deprecated.R +++ b/R/deprecated.R @@ -37,7 +37,7 @@ cff_to_bibtex <- function(x, what = c("preferred", "references", "all")) { if (requireNamespace("lifecycle", quietly = TRUE)) { lifecycle::deprecate_soft( - "0.5.0", "cff_extract_to_bibtex()", + "0.6.0", "cff_extract_to_bibtex()", details = "Function renamed, use `cff_to_bibentry()` instead." ) } diff --git a/README.md b/README.md index b87bcf35..11d7750f 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ file and the `CITATION` file (if present) of your package. Note that **cffr** works best if your package pass `R CMD check/devtools::check()`. -As per 2024-02-13 there are at least 235 repos on GitHub using **cffr**. +As per 2024-02-16 there are at least 240 repos on GitHub using **cffr**. [Check them out here](https://github.com/search?q=cffr%20path%3A**%2FCITATION.cff&type=code). diff --git a/codemeta.json b/codemeta.json index 1551c39b..e3f6821c 100644 --- a/codemeta.json +++ b/codemeta.json @@ -200,7 +200,7 @@ }, "isPartOf": "https://ropensci.org", "keywords": ["attribution", "citation", "credit", "citation-files", "cff", "metadata", "r", "r-package", "citation-file-format", "rstats", "ropensci", "cran"], - "fileSize": "911.371KB", + "fileSize": "1048.177KB", "citation": [ { "@type": "ScholarlyArticle", diff --git a/man/cff-class.Rd b/man/cff-class.Rd index bf7eb71a..8f856d3e 100644 --- a/man/cff-class.Rd +++ b/man/cff-class.Rd @@ -131,5 +131,51 @@ c(minimal_cff, new_keys) #> abstract: Minimal example }\if{html}{\out{}} } + +\subsection{\code{\link[=as.person]{as.person()}}}{ + +Special case for those CFF keys that are person-like. + +\if{html}{\out{
}}\preformatted{ + path <- system.file("examples/CITATION_complete.cff", package = "cffr") + the_cff <- cff_read(path) + + the_cff$authors +#> - family-names: Real Person +#> given-names: One Truly +#> name-particle: van der +#> name-suffix: IV +#> alias: Citey +#> affiliation: Excellent University, Niceplace, Arcadia +#> address: 22 Acacia Avenue +#> city: Citationburgh +#> region: Renfrewshire +#> post-code: C13 7X7 +#> country: GB +#> orcid: https://orcid.org/0000-0001-2345-6789 +#> email: project@entity.com +#> tel: +44(0)141-323 4567 +#> fax: +44(0)141-323 45678 +#> website: https://www.entity-project-team.io +#> - name: Entity Project Team Conference entity +#> address: 22 Acacia Avenue +#> city: Citationburgh +#> region: Renfrewshire +#> post-code: C13 7X7 +#> country: GB +#> orcid: https://orcid.org/0000-0001-2345-6789 +#> email: project@entity.com +#> tel: +44(0)141-323 4567 +#> fax: +44(0)141-323 45678 +#> website: https://www.entity-project-team.io +#> date-start: '2017-01-01' +#> date-end: '2017-01-31' +#> location: The team garage + + as.person(the_cff$authors) +#> [1] "One Truly van der Real Person IV (Citey, Excellent University, Niceplace, Arcadia, 22 Acacia Avenue, Citationburgh, Renfrewshire, C13 7X7, GB, , +44(0)141-323 4567, +44(0)141-323 45678, https://www.entity-project-team.io)" +#> [2] "Entity Project Team Conference entity (Entity Project Team Conference entity, 22 Acacia Avenue, Citationburgh, Renfrewshire, C13 7X7, GB, , +44(0)141-323 4567, +44(0)141-323 45678, https://www.entity-project-team.io, 2017-01-01, 2017-01-31, The team garage)" +}\if{html}{\out{
}} +} } \keyword{internal} diff --git a/man/cff_create.Rd b/man/cff_create.Rd index 33e8da16..643693bd 100644 --- a/man/cff_create.Rd +++ b/man/cff_create.Rd @@ -40,7 +40,7 @@ of your package to the \code{references} CFF key?} generating the \code{CITATION.cff} file. See \strong{Details}.} } \value{ -A \code{\link[=cff-class]{cff}} list object. +A \code{\link[=cff-class]{cff}} object. } \description{ Create a \code{\link[=cff-class]{cff}} object from a given source for further diff --git a/man/chunks/cffclass.Rmd b/man/chunks/cffclass.Rmd index 0ffd1561..c2ca6834 100644 --- a/man/chunks/cffclass.Rmd +++ b/man/chunks/cffclass.Rmd @@ -61,10 +61,22 @@ t(as_df) ## [c()] - ```{r} new_keys <- c("date-released" = "2020-01-31", abstract = "Minimal example") c(minimal_cff, new_keys) ``` +## [as.person()] + +Special case for those CFF keys that are person-like. + +```{r} + + path <- system.file("examples/CITATION_complete.cff", package = "cffr") + the_cff <- cff_read(path) + + the_cff$authors + + as.person(the_cff$authors) +``` diff --git a/man/chunks/value.Rmd b/man/chunks/value.Rmd new file mode 100644 index 00000000..5d8ae303 --- /dev/null +++ b/man/chunks/value.Rmd @@ -0,0 +1,2 @@ +A [`cff`][cff-class] object ready to be used with other functions (i.e. +[cff_create()]. diff --git a/tests/testthat/_snaps/cff-methods.md b/tests/testthat/_snaps/cff-methods.md new file mode 100644 index 00000000..a6535fa1 --- /dev/null +++ b/tests/testthat/_snaps/cff-methods.md @@ -0,0 +1,907 @@ +# as data frame complete + + Code + names(the_df) + Output + [1] "cff_version" + [2] "message" + [3] "abstract" + [4] "authors.00.family_names" + [5] "authors.00.given_names" + [6] "authors.00.name_particle" + [7] "authors.00.name_suffix" + [8] "authors.00.alias" + [9] "authors.00.affiliation" + [10] "authors.00.address" + [11] "authors.00.city" + [12] "authors.00.region" + [13] "authors.00.post_code" + [14] "authors.00.country" + [15] "authors.00.orcid" + [16] "authors.00.email" + [17] "authors.00.tel" + [18] "authors.00.fax" + [19] "authors.00.website" + [20] "authors.01.name" + [21] "authors.01.address" + [22] "authors.01.city" + [23] "authors.01.region" + [24] "authors.01.post_code" + [25] "authors.01.country" + [26] "authors.01.orcid" + [27] "authors.01.email" + [28] "authors.01.tel" + [29] "authors.01.fax" + [30] "authors.01.website" + [31] "authors.01.date_start" + [32] "authors.01.date_end" + [33] "authors.01.location" + [34] "commit" + [35] "contact.00.family_names" + [36] "contact.00.given_names" + [37] "contact.00.name_particle" + [38] "contact.00.name_suffix" + [39] "contact.00.alias" + [40] "contact.00.affiliation" + [41] "contact.00.address" + [42] "contact.00.city" + [43] "contact.00.region" + [44] "contact.00.post_code" + [45] "contact.00.country" + [46] "contact.00.orcid" + [47] "contact.00.email" + [48] "contact.00.tel" + [49] "contact.00.fax" + [50] "contact.00.website" + [51] "contact.01.name" + [52] "contact.01.address" + [53] "contact.01.city" + [54] "contact.01.region" + [55] "contact.01.post_code" + [56] "contact.01.country" + [57] "contact.01.orcid" + [58] "contact.01.email" + [59] "contact.01.tel" + [60] "contact.01.fax" + [61] "contact.01.website" + [62] "contact.01.date_start" + [63] "contact.01.date_end" + [64] "contact.01.location" + [65] "date_released" + [66] "doi" + [67] "identifiers.00.type" + [68] "identifiers.00.value" + [69] "identifiers.01.type" + [70] "identifiers.01.value" + [71] "identifiers.02.type" + [72] "identifiers.02.value" + [73] "identifiers.03.type" + [74] "identifiers.03.value" + [75] "keywords.00" + [76] "keywords.01" + [77] "keywords.02" + [78] "keywords.03" + [79] "license" + [80] "license_url" + [81] "repository" + [82] "repository_code" + [83] "repository_artifact" + [84] "title" + [85] "type" + [86] "url" + [87] "version" + [88] "preferred_citation.type" + [89] "preferred_citation.title" + [90] "preferred_citation.abbreviation" + [91] "preferred_citation.abstract" + [92] "preferred_citation.collection_doi" + [93] "preferred_citation.collection_title" + [94] "preferred_citation.collection_type" + [95] "preferred_citation.commit" + [96] "preferred_citation.copyright" + [97] "preferred_citation.data_type" + [98] "preferred_citation.database" + [99] "preferred_citation.date_accessed" + [100] "preferred_citation.date_downloaded" + [101] "preferred_citation.date_released" + [102] "preferred_citation.date_published" + [103] "preferred_citation.department" + [104] "preferred_citation.doi" + [105] "preferred_citation.edition" + [106] "preferred_citation.end" + [107] "preferred_citation.entry" + [108] "preferred_citation.filename" + [109] "preferred_citation.format" + [110] "preferred_citation.identifiers.00.type" + [111] "preferred_citation.identifiers.00.value" + [112] "preferred_citation.identifiers.01.type" + [113] "preferred_citation.identifiers.01.value" + [114] "preferred_citation.identifiers.02.type" + [115] "preferred_citation.identifiers.02.value" + [116] "preferred_citation.identifiers.03.type" + [117] "preferred_citation.identifiers.03.value" + [118] "preferred_citation.isbn" + [119] "preferred_citation.issn" + [120] "preferred_citation.issue" + [121] "preferred_citation.issue_date" + [122] "preferred_citation.issue_title" + [123] "preferred_citation.journal" + [124] "preferred_citation.keywords.00" + [125] "preferred_citation.keywords.01" + [126] "preferred_citation.languages.00" + [127] "preferred_citation.languages.01" + [128] "preferred_citation.license" + [129] "preferred_citation.license_url" + [130] "preferred_citation.loc_start" + [131] "preferred_citation.loc_end" + [132] "preferred_citation.medium" + [133] "preferred_citation.month" + [134] "preferred_citation.nihmsid" + [135] "preferred_citation.notes" + [136] "preferred_citation.number" + [137] "preferred_citation.number_volumes" + [138] "preferred_citation.pages" + [139] "preferred_citation.patent-states.00" + [140] "preferred_citation.patent-states.01" + [141] "preferred_citation.patent-states.02" + [142] "preferred_citation.patent-states.03" + [143] "preferred_citation.patent-states.04" + [144] "preferred_citation.pmcid" + [145] "preferred_citation.repository" + [146] "preferred_citation.repository_code" + [147] "preferred_citation.repository_artifact" + [148] "preferred_citation.scope" + [149] "preferred_citation.section" + [150] "preferred_citation.status" + [151] "preferred_citation.start" + [152] "preferred_citation.thesis_type" + [153] "preferred_citation.url" + [154] "preferred_citation.version" + [155] "preferred_citation.volume" + [156] "preferred_citation.volume_title" + [157] "preferred_citation.year" + [158] "preferred_citation.year_original" + [159] "preferred_citation.conference.name" + [160] "preferred_citation.conference.address" + [161] "preferred_citation.conference.city" + [162] "preferred_citation.conference.region" + [163] "preferred_citation.conference.post-code" + [164] "preferred_citation.conference.country" + [165] "preferred_citation.conference.orcid" + [166] "preferred_citation.conference.email" + [167] "preferred_citation.conference.tel" + [168] "preferred_citation.conference.fax" + [169] "preferred_citation.conference.website" + [170] "preferred_citation.conference.date-start" + [171] "preferred_citation.conference.date-end" + [172] "preferred_citation.conference.location" + [173] "preferred_citation.authors.00.family_names" + [174] "preferred_citation.authors.00.given_names" + [175] "preferred_citation.authors.00.name_particle" + [176] "preferred_citation.authors.00.name_suffix" + [177] "preferred_citation.authors.00.alias" + [178] "preferred_citation.authors.00.affiliation" + [179] "preferred_citation.authors.00.address" + [180] "preferred_citation.authors.00.city" + [181] "preferred_citation.authors.00.region" + [182] "preferred_citation.authors.00.post_code" + [183] "preferred_citation.authors.00.country" + [184] "preferred_citation.authors.00.orcid" + [185] "preferred_citation.authors.00.email" + [186] "preferred_citation.authors.00.tel" + [187] "preferred_citation.authors.00.fax" + [188] "preferred_citation.authors.00.website" + [189] "preferred_citation.authors.01.name" + [190] "preferred_citation.authors.01.address" + [191] "preferred_citation.authors.01.city" + [192] "preferred_citation.authors.01.region" + [193] "preferred_citation.authors.01.post_code" + [194] "preferred_citation.authors.01.country" + [195] "preferred_citation.authors.01.orcid" + [196] "preferred_citation.authors.01.email" + [197] "preferred_citation.authors.01.tel" + [198] "preferred_citation.authors.01.fax" + [199] "preferred_citation.authors.01.website" + [200] "preferred_citation.authors.01.date_start" + [201] "preferred_citation.authors.01.date_end" + [202] "preferred_citation.authors.01.location" + [203] "preferred_citation.contact.00.family_names" + [204] "preferred_citation.contact.00.given_names" + [205] "preferred_citation.contact.00.name_particle" + [206] "preferred_citation.contact.00.name_suffix" + [207] "preferred_citation.contact.00.alias" + [208] "preferred_citation.contact.00.affiliation" + [209] "preferred_citation.contact.00.address" + [210] "preferred_citation.contact.00.city" + [211] "preferred_citation.contact.00.region" + [212] "preferred_citation.contact.00.post_code" + [213] "preferred_citation.contact.00.country" + [214] "preferred_citation.contact.00.orcid" + [215] "preferred_citation.contact.00.email" + [216] "preferred_citation.contact.00.tel" + [217] "preferred_citation.contact.00.fax" + [218] "preferred_citation.contact.00.website" + [219] "preferred_citation.contact.01.name" + [220] "preferred_citation.contact.01.address" + [221] "preferred_citation.contact.01.city" + [222] "preferred_citation.contact.01.region" + [223] "preferred_citation.contact.01.post_code" + [224] "preferred_citation.contact.01.country" + [225] "preferred_citation.contact.01.orcid" + [226] "preferred_citation.contact.01.email" + [227] "preferred_citation.contact.01.tel" + [228] "preferred_citation.contact.01.fax" + [229] "preferred_citation.contact.01.website" + [230] "preferred_citation.contact.01.date_start" + [231] "preferred_citation.contact.01.date_end" + [232] "preferred_citation.contact.01.location" + [233] "preferred_citation.database-provider.name" + [234] "preferred_citation.database-provider.address" + [235] "preferred_citation.database-provider.city" + [236] "preferred_citation.database-provider.region" + [237] "preferred_citation.database-provider.post-code" + [238] "preferred_citation.database-provider.country" + [239] "preferred_citation.database-provider.orcid" + [240] "preferred_citation.database-provider.email" + [241] "preferred_citation.database-provider.tel" + [242] "preferred_citation.database-provider.fax" + [243] "preferred_citation.database-provider.website" + [244] "preferred_citation.database-provider.date-start" + [245] "preferred_citation.database-provider.date-end" + [246] "preferred_citation.database-provider.location" + [247] "preferred_citation.editors.00.family_names" + [248] "preferred_citation.editors.00.given_names" + [249] "preferred_citation.editors.00.name_particle" + [250] "preferred_citation.editors.00.name_suffix" + [251] "preferred_citation.editors.00.alias" + [252] "preferred_citation.editors.00.affiliation" + [253] "preferred_citation.editors.00.address" + [254] "preferred_citation.editors.00.city" + [255] "preferred_citation.editors.00.region" + [256] "preferred_citation.editors.00.post_code" + [257] "preferred_citation.editors.00.country" + [258] "preferred_citation.editors.00.orcid" + [259] "preferred_citation.editors.00.email" + [260] "preferred_citation.editors.00.tel" + [261] "preferred_citation.editors.00.fax" + [262] "preferred_citation.editors.00.website" + [263] "preferred_citation.editors.01.name" + [264] "preferred_citation.editors.01.address" + [265] "preferred_citation.editors.01.city" + [266] "preferred_citation.editors.01.region" + [267] "preferred_citation.editors.01.post_code" + [268] "preferred_citation.editors.01.country" + [269] "preferred_citation.editors.01.orcid" + [270] "preferred_citation.editors.01.email" + [271] "preferred_citation.editors.01.tel" + [272] "preferred_citation.editors.01.fax" + [273] "preferred_citation.editors.01.website" + [274] "preferred_citation.editors.01.date_start" + [275] "preferred_citation.editors.01.date_end" + [276] "preferred_citation.editors.01.location" + [277] "preferred_citation.editors-series.00.family_names" + [278] "preferred_citation.editors-series.00.given_names" + [279] "preferred_citation.editors-series.00.name_particle" + [280] "preferred_citation.editors-series.00.name_suffix" + [281] "preferred_citation.editors-series.00.alias" + [282] "preferred_citation.editors-series.00.affiliation" + [283] "preferred_citation.editors-series.00.address" + [284] "preferred_citation.editors-series.00.city" + [285] "preferred_citation.editors-series.00.region" + [286] "preferred_citation.editors-series.00.post_code" + [287] "preferred_citation.editors-series.00.country" + [288] "preferred_citation.editors-series.00.orcid" + [289] "preferred_citation.editors-series.00.email" + [290] "preferred_citation.editors-series.00.tel" + [291] "preferred_citation.editors-series.00.fax" + [292] "preferred_citation.editors-series.00.website" + [293] "preferred_citation.editors-series.01.name" + [294] "preferred_citation.editors-series.01.address" + [295] "preferred_citation.editors-series.01.city" + [296] "preferred_citation.editors-series.01.region" + [297] "preferred_citation.editors-series.01.post_code" + [298] "preferred_citation.editors-series.01.country" + [299] "preferred_citation.editors-series.01.orcid" + [300] "preferred_citation.editors-series.01.email" + [301] "preferred_citation.editors-series.01.tel" + [302] "preferred_citation.editors-series.01.fax" + [303] "preferred_citation.editors-series.01.website" + [304] "preferred_citation.editors-series.01.date_start" + [305] "preferred_citation.editors-series.01.date_end" + [306] "preferred_citation.editors-series.01.location" + [307] "preferred_citation.institution.name" + [308] "preferred_citation.institution.address" + [309] "preferred_citation.institution.city" + [310] "preferred_citation.institution.region" + [311] "preferred_citation.institution.post-code" + [312] "preferred_citation.institution.country" + [313] "preferred_citation.institution.orcid" + [314] "preferred_citation.institution.email" + [315] "preferred_citation.institution.tel" + [316] "preferred_citation.institution.fax" + [317] "preferred_citation.institution.website" + [318] "preferred_citation.institution.date-start" + [319] "preferred_citation.institution.date-end" + [320] "preferred_citation.institution.location" + [321] "preferred_citation.location.name" + [322] "preferred_citation.location.address" + [323] "preferred_citation.location.city" + [324] "preferred_citation.location.region" + [325] "preferred_citation.location.post-code" + [326] "preferred_citation.location.country" + [327] "preferred_citation.location.orcid" + [328] "preferred_citation.location.email" + [329] "preferred_citation.location.tel" + [330] "preferred_citation.location.fax" + [331] "preferred_citation.location.website" + [332] "preferred_citation.location.date-start" + [333] "preferred_citation.location.date-end" + [334] "preferred_citation.location.location" + [335] "preferred_citation.publisher.name" + [336] "preferred_citation.publisher.address" + [337] "preferred_citation.publisher.city" + [338] "preferred_citation.publisher.region" + [339] "preferred_citation.publisher.post-code" + [340] "preferred_citation.publisher.country" + [341] "preferred_citation.publisher.orcid" + [342] "preferred_citation.publisher.email" + [343] "preferred_citation.publisher.tel" + [344] "preferred_citation.publisher.fax" + [345] "preferred_citation.publisher.website" + [346] "preferred_citation.publisher.date-start" + [347] "preferred_citation.publisher.date-end" + [348] "preferred_citation.publisher.location" + [349] "preferred_citation.recipients.00.family_names" + [350] "preferred_citation.recipients.00.given_names" + [351] "preferred_citation.recipients.00.name_particle" + [352] "preferred_citation.recipients.00.name_suffix" + [353] "preferred_citation.recipients.00.alias" + [354] "preferred_citation.recipients.00.affiliation" + [355] "preferred_citation.recipients.00.address" + [356] "preferred_citation.recipients.00.city" + [357] "preferred_citation.recipients.00.region" + [358] "preferred_citation.recipients.00.post_code" + [359] "preferred_citation.recipients.00.country" + [360] "preferred_citation.recipients.00.orcid" + [361] "preferred_citation.recipients.00.email" + [362] "preferred_citation.recipients.00.tel" + [363] "preferred_citation.recipients.00.fax" + [364] "preferred_citation.recipients.00.website" + [365] "preferred_citation.recipients.01.name" + [366] "preferred_citation.recipients.01.address" + [367] "preferred_citation.recipients.01.city" + [368] "preferred_citation.recipients.01.region" + [369] "preferred_citation.recipients.01.post_code" + [370] "preferred_citation.recipients.01.country" + [371] "preferred_citation.recipients.01.orcid" + [372] "preferred_citation.recipients.01.email" + [373] "preferred_citation.recipients.01.tel" + [374] "preferred_citation.recipients.01.fax" + [375] "preferred_citation.recipients.01.website" + [376] "preferred_citation.recipients.01.date_start" + [377] "preferred_citation.recipients.01.date_end" + [378] "preferred_citation.recipients.01.location" + [379] "preferred_citation.senders.00.family_names" + [380] "preferred_citation.senders.00.given_names" + [381] "preferred_citation.senders.00.name_particle" + [382] "preferred_citation.senders.00.name_suffix" + [383] "preferred_citation.senders.00.alias" + [384] "preferred_citation.senders.00.affiliation" + [385] "preferred_citation.senders.00.address" + [386] "preferred_citation.senders.00.city" + [387] "preferred_citation.senders.00.region" + [388] "preferred_citation.senders.00.post_code" + [389] "preferred_citation.senders.00.country" + [390] "preferred_citation.senders.00.orcid" + [391] "preferred_citation.senders.00.email" + [392] "preferred_citation.senders.00.tel" + [393] "preferred_citation.senders.00.fax" + [394] "preferred_citation.senders.00.website" + [395] "preferred_citation.senders.01.name" + [396] "preferred_citation.senders.01.address" + [397] "preferred_citation.senders.01.city" + [398] "preferred_citation.senders.01.region" + [399] "preferred_citation.senders.01.post_code" + [400] "preferred_citation.senders.01.country" + [401] "preferred_citation.senders.01.orcid" + [402] "preferred_citation.senders.01.email" + [403] "preferred_citation.senders.01.tel" + [404] "preferred_citation.senders.01.fax" + [405] "preferred_citation.senders.01.website" + [406] "preferred_citation.senders.01.date_start" + [407] "preferred_citation.senders.01.date_end" + [408] "preferred_citation.senders.01.location" + [409] "preferred_citation.translators.00.family_names" + [410] "preferred_citation.translators.00.given_names" + [411] "preferred_citation.translators.00.name_particle" + [412] "preferred_citation.translators.00.name_suffix" + [413] "preferred_citation.translators.00.alias" + [414] "preferred_citation.translators.00.affiliation" + [415] "preferred_citation.translators.00.address" + [416] "preferred_citation.translators.00.city" + [417] "preferred_citation.translators.00.region" + [418] "preferred_citation.translators.00.post_code" + [419] "preferred_citation.translators.00.country" + [420] "preferred_citation.translators.00.orcid" + [421] "preferred_citation.translators.00.email" + [422] "preferred_citation.translators.00.tel" + [423] "preferred_citation.translators.00.fax" + [424] "preferred_citation.translators.00.website" + [425] "preferred_citation.translators.01.name" + [426] "preferred_citation.translators.01.address" + [427] "preferred_citation.translators.01.city" + [428] "preferred_citation.translators.01.region" + [429] "preferred_citation.translators.01.post_code" + [430] "preferred_citation.translators.01.country" + [431] "preferred_citation.translators.01.orcid" + [432] "preferred_citation.translators.01.email" + [433] "preferred_citation.translators.01.tel" + [434] "preferred_citation.translators.01.fax" + [435] "preferred_citation.translators.01.website" + [436] "preferred_citation.translators.01.date_start" + [437] "preferred_citation.translators.01.date_end" + [438] "preferred_citation.translators.01.location" + [439] "references.00.type" + [440] "references.00.title" + [441] "references.00.abbreviation" + [442] "references.00.abstract" + [443] "references.00.collection_doi" + [444] "references.00.collection_title" + [445] "references.00.collection_type" + [446] "references.00.commit" + [447] "references.00.copyright" + [448] "references.00.data_type" + [449] "references.00.database" + [450] "references.00.date_accessed" + [451] "references.00.date_downloaded" + [452] "references.00.date_released" + [453] "references.00.date_published" + [454] "references.00.department" + [455] "references.00.doi" + [456] "references.00.edition" + [457] "references.00.end" + [458] "references.00.entry" + [459] "references.00.filename" + [460] "references.00.format" + [461] "references.00.identifiers.00.type" + [462] "references.00.identifiers.00.value" + [463] "references.00.identifiers.01.type" + [464] "references.00.identifiers.01.value" + [465] "references.00.identifiers.02.type" + [466] "references.00.identifiers.02.value" + [467] "references.00.identifiers.03.type" + [468] "references.00.identifiers.03.value" + [469] "references.00.isbn" + [470] "references.00.issn" + [471] "references.00.issue" + [472] "references.00.issue_date" + [473] "references.00.issue_title" + [474] "references.00.journal" + [475] "references.00.keywords.00" + [476] "references.00.keywords.01" + [477] "references.00.languages.00" + [478] "references.00.languages.01" + [479] "references.00.license" + [480] "references.00.license_url" + [481] "references.00.loc_start" + [482] "references.00.loc_end" + [483] "references.00.medium" + [484] "references.00.month" + [485] "references.00.nihmsid" + [486] "references.00.notes" + [487] "references.00.number" + [488] "references.00.number_volumes" + [489] "references.00.pages" + [490] "references.00.patent-states.00" + [491] "references.00.patent-states.01" + [492] "references.00.patent-states.02" + [493] "references.00.patent-states.03" + [494] "references.00.patent-states.04" + [495] "references.00.pmcid" + [496] "references.00.repository" + [497] "references.00.repository_code" + [498] "references.00.repository_artifact" + [499] "references.00.scope" + [500] "references.00.section" + [501] "references.00.status" + [502] "references.00.start" + [503] "references.00.thesis_type" + [504] "references.00.url" + [505] "references.00.version" + [506] "references.00.volume" + [507] "references.00.volume_title" + [508] "references.00.year" + [509] "references.00.year_original" + [510] "references.00.conference.name" + [511] "references.00.conference.address" + [512] "references.00.conference.city" + [513] "references.00.conference.region" + [514] "references.00.conference.post-code" + [515] "references.00.conference.country" + [516] "references.00.conference.orcid" + [517] "references.00.conference.email" + [518] "references.00.conference.tel" + [519] "references.00.conference.fax" + [520] "references.00.conference.website" + [521] "references.00.conference.date-start" + [522] "references.00.conference.date-end" + [523] "references.00.conference.location" + [524] "references.00.authors.00.family_names" + [525] "references.00.authors.00.given_names" + [526] "references.00.authors.00.name_particle" + [527] "references.00.authors.00.name_suffix" + [528] "references.00.authors.00.alias" + [529] "references.00.authors.00.affiliation" + [530] "references.00.authors.00.address" + [531] "references.00.authors.00.city" + [532] "references.00.authors.00.region" + [533] "references.00.authors.00.post_code" + [534] "references.00.authors.00.country" + [535] "references.00.authors.00.orcid" + [536] "references.00.authors.00.email" + [537] "references.00.authors.00.tel" + [538] "references.00.authors.00.fax" + [539] "references.00.authors.00.website" + [540] "references.00.authors.01.name" + [541] "references.00.authors.01.address" + [542] "references.00.authors.01.city" + [543] "references.00.authors.01.region" + [544] "references.00.authors.01.post_code" + [545] "references.00.authors.01.country" + [546] "references.00.authors.01.orcid" + [547] "references.00.authors.01.email" + [548] "references.00.authors.01.tel" + [549] "references.00.authors.01.fax" + [550] "references.00.authors.01.website" + [551] "references.00.authors.01.date_start" + [552] "references.00.authors.01.date_end" + [553] "references.00.authors.01.location" + [554] "references.00.contact.00.family_names" + [555] "references.00.contact.00.given_names" + [556] "references.00.contact.00.name_particle" + [557] "references.00.contact.00.name_suffix" + [558] "references.00.contact.00.alias" + [559] "references.00.contact.00.affiliation" + [560] "references.00.contact.00.address" + [561] "references.00.contact.00.city" + [562] "references.00.contact.00.region" + [563] "references.00.contact.00.post_code" + [564] "references.00.contact.00.country" + [565] "references.00.contact.00.orcid" + [566] "references.00.contact.00.email" + [567] "references.00.contact.00.tel" + [568] "references.00.contact.00.fax" + [569] "references.00.contact.00.website" + [570] "references.00.contact.01.name" + [571] "references.00.contact.01.address" + [572] "references.00.contact.01.city" + [573] "references.00.contact.01.region" + [574] "references.00.contact.01.post_code" + [575] "references.00.contact.01.country" + [576] "references.00.contact.01.orcid" + [577] "references.00.contact.01.email" + [578] "references.00.contact.01.tel" + [579] "references.00.contact.01.fax" + [580] "references.00.contact.01.website" + [581] "references.00.contact.01.date_start" + [582] "references.00.contact.01.date_end" + [583] "references.00.contact.01.location" + [584] "references.00.database-provider.name" + [585] "references.00.database-provider.address" + [586] "references.00.database-provider.city" + [587] "references.00.database-provider.region" + [588] "references.00.database-provider.post-code" + [589] "references.00.database-provider.country" + [590] "references.00.database-provider.orcid" + [591] "references.00.database-provider.email" + [592] "references.00.database-provider.tel" + [593] "references.00.database-provider.fax" + [594] "references.00.database-provider.website" + [595] "references.00.database-provider.date-start" + [596] "references.00.database-provider.date-end" + [597] "references.00.database-provider.location" + [598] "references.00.editors.00.family_names" + [599] "references.00.editors.00.given_names" + [600] "references.00.editors.00.name_particle" + [601] "references.00.editors.00.name_suffix" + [602] "references.00.editors.00.alias" + [603] "references.00.editors.00.affiliation" + [604] "references.00.editors.00.address" + [605] "references.00.editors.00.city" + [606] "references.00.editors.00.region" + [607] "references.00.editors.00.post_code" + [608] "references.00.editors.00.country" + [609] "references.00.editors.00.orcid" + [610] "references.00.editors.00.email" + [611] "references.00.editors.00.tel" + [612] "references.00.editors.00.fax" + [613] "references.00.editors.00.website" + [614] "references.00.editors.01.name" + [615] "references.00.editors.01.address" + [616] "references.00.editors.01.city" + [617] "references.00.editors.01.region" + [618] "references.00.editors.01.post_code" + [619] "references.00.editors.01.country" + [620] "references.00.editors.01.orcid" + [621] "references.00.editors.01.email" + [622] "references.00.editors.01.tel" + [623] "references.00.editors.01.fax" + [624] "references.00.editors.01.website" + [625] "references.00.editors.01.date_start" + [626] "references.00.editors.01.date_end" + [627] "references.00.editors.01.location" + [628] "references.00.editors-series.00.family_names" + [629] "references.00.editors-series.00.given_names" + [630] "references.00.editors-series.00.name_particle" + [631] "references.00.editors-series.00.name_suffix" + [632] "references.00.editors-series.00.alias" + [633] "references.00.editors-series.00.affiliation" + [634] "references.00.editors-series.00.address" + [635] "references.00.editors-series.00.city" + [636] "references.00.editors-series.00.region" + [637] "references.00.editors-series.00.post_code" + [638] "references.00.editors-series.00.country" + [639] "references.00.editors-series.00.orcid" + [640] "references.00.editors-series.00.email" + [641] "references.00.editors-series.00.tel" + [642] "references.00.editors-series.00.fax" + [643] "references.00.editors-series.00.website" + [644] "references.00.editors-series.01.name" + [645] "references.00.editors-series.01.address" + [646] "references.00.editors-series.01.city" + [647] "references.00.editors-series.01.region" + [648] "references.00.editors-series.01.post_code" + [649] "references.00.editors-series.01.country" + [650] "references.00.editors-series.01.orcid" + [651] "references.00.editors-series.01.email" + [652] "references.00.editors-series.01.tel" + [653] "references.00.editors-series.01.fax" + [654] "references.00.editors-series.01.website" + [655] "references.00.editors-series.01.date_start" + [656] "references.00.editors-series.01.date_end" + [657] "references.00.editors-series.01.location" + [658] "references.00.institution.name" + [659] "references.00.institution.address" + [660] "references.00.institution.city" + [661] "references.00.institution.region" + [662] "references.00.institution.post-code" + [663] "references.00.institution.country" + [664] "references.00.institution.orcid" + [665] "references.00.institution.email" + [666] "references.00.institution.tel" + [667] "references.00.institution.fax" + [668] "references.00.institution.website" + [669] "references.00.institution.date-start" + [670] "references.00.institution.date-end" + [671] "references.00.institution.location" + [672] "references.00.location.name" + [673] "references.00.location.address" + [674] "references.00.location.city" + [675] "references.00.location.region" + [676] "references.00.location.post-code" + [677] "references.00.location.country" + [678] "references.00.location.orcid" + [679] "references.00.location.email" + [680] "references.00.location.tel" + [681] "references.00.location.fax" + [682] "references.00.location.website" + [683] "references.00.location.date-start" + [684] "references.00.location.date-end" + [685] "references.00.location.location" + [686] "references.00.publisher.name" + [687] "references.00.publisher.address" + [688] "references.00.publisher.city" + [689] "references.00.publisher.region" + [690] "references.00.publisher.post-code" + [691] "references.00.publisher.country" + [692] "references.00.publisher.orcid" + [693] "references.00.publisher.email" + [694] "references.00.publisher.tel" + [695] "references.00.publisher.fax" + [696] "references.00.publisher.website" + [697] "references.00.publisher.date-start" + [698] "references.00.publisher.date-end" + [699] "references.00.publisher.location" + [700] "references.00.recipients.00.family_names" + [701] "references.00.recipients.00.given_names" + [702] "references.00.recipients.00.name_particle" + [703] "references.00.recipients.00.name_suffix" + [704] "references.00.recipients.00.alias" + [705] "references.00.recipients.00.affiliation" + [706] "references.00.recipients.00.address" + [707] "references.00.recipients.00.city" + [708] "references.00.recipients.00.region" + [709] "references.00.recipients.00.post_code" + [710] "references.00.recipients.00.country" + [711] "references.00.recipients.00.orcid" + [712] "references.00.recipients.00.email" + [713] "references.00.recipients.00.tel" + [714] "references.00.recipients.00.fax" + [715] "references.00.recipients.00.website" + [716] "references.00.recipients.01.name" + [717] "references.00.recipients.01.address" + [718] "references.00.recipients.01.city" + [719] "references.00.recipients.01.region" + [720] "references.00.recipients.01.post_code" + [721] "references.00.recipients.01.country" + [722] "references.00.recipients.01.orcid" + [723] "references.00.recipients.01.email" + [724] "references.00.recipients.01.tel" + [725] "references.00.recipients.01.fax" + [726] "references.00.recipients.01.website" + [727] "references.00.recipients.01.date_start" + [728] "references.00.recipients.01.date_end" + [729] "references.00.recipients.01.location" + [730] "references.00.senders.00.family_names" + [731] "references.00.senders.00.given_names" + [732] "references.00.senders.00.name_particle" + [733] "references.00.senders.00.name_suffix" + [734] "references.00.senders.00.alias" + [735] "references.00.senders.00.affiliation" + [736] "references.00.senders.00.address" + [737] "references.00.senders.00.city" + [738] "references.00.senders.00.region" + [739] "references.00.senders.00.post_code" + [740] "references.00.senders.00.country" + [741] "references.00.senders.00.orcid" + [742] "references.00.senders.00.email" + [743] "references.00.senders.00.tel" + [744] "references.00.senders.00.fax" + [745] "references.00.senders.00.website" + [746] "references.00.senders.01.name" + [747] "references.00.senders.01.address" + [748] "references.00.senders.01.city" + [749] "references.00.senders.01.region" + [750] "references.00.senders.01.post_code" + [751] "references.00.senders.01.country" + [752] "references.00.senders.01.orcid" + [753] "references.00.senders.01.email" + [754] "references.00.senders.01.tel" + [755] "references.00.senders.01.fax" + [756] "references.00.senders.01.website" + [757] "references.00.senders.01.date_start" + [758] "references.00.senders.01.date_end" + [759] "references.00.senders.01.location" + [760] "references.00.translators.00.family_names" + [761] "references.00.translators.00.given_names" + [762] "references.00.translators.00.name_particle" + [763] "references.00.translators.00.name_suffix" + [764] "references.00.translators.00.alias" + [765] "references.00.translators.00.affiliation" + [766] "references.00.translators.00.address" + [767] "references.00.translators.00.city" + [768] "references.00.translators.00.region" + [769] "references.00.translators.00.post_code" + [770] "references.00.translators.00.country" + [771] "references.00.translators.00.orcid" + [772] "references.00.translators.00.email" + [773] "references.00.translators.00.tel" + [774] "references.00.translators.00.fax" + [775] "references.00.translators.00.website" + [776] "references.00.translators.01.name" + [777] "references.00.translators.01.address" + [778] "references.00.translators.01.city" + [779] "references.00.translators.01.region" + [780] "references.00.translators.01.post_code" + [781] "references.00.translators.01.country" + [782] "references.00.translators.01.orcid" + [783] "references.00.translators.01.email" + [784] "references.00.translators.01.tel" + [785] "references.00.translators.01.fax" + [786] "references.00.translators.01.website" + [787] "references.00.translators.01.date_start" + [788] "references.00.translators.01.date_end" + [789] "references.00.translators.01.location" + +# Convert a citation only + + Code + names(the_df) + Output + [1] "type" "title" + [3] "authors.00.family_names" "authors.00.given_names" + [5] "authors.01.family_names" "authors.01.given_names" + [7] "authors.02.family_names" "authors.02.given_names" + [9] "authors.03.name" "url" + [11] "keywords.00" "keywords.01" + [13] "keywords.02" "keywords.03" + [15] "keywords.04" "keywords.05" + [17] "keywords.06" "keywords.07" + [19] "abstract" "version" + +# Convert authors only + + Code + names(the_df) + Output + [1] "family_names" "given_names" + +# Convert list of authors + + Code + names(the_df) + Output + [1] "person.00.family_names" "person.00.given_names" + [3] "person.00.name_particle" "person.00.name_suffix" + [5] "person.00.alias" "person.00.affiliation" + [7] "person.00.address" "person.00.city" + [9] "person.00.region" "person.00.post_code" + [11] "person.00.country" "person.00.orcid" + [13] "person.00.email" "person.00.tel" + [15] "person.00.fax" "person.00.website" + [17] "person.01.name" "person.01.address" + [19] "person.01.city" "person.01.region" + [21] "person.01.post_code" "person.01.country" + [23] "person.01.orcid" "person.01.email" + [25] "person.01.tel" "person.01.fax" + [27] "person.01.website" "person.01.date_start" + [29] "person.01.date_end" "person.01.location" + +# as.person method + + Code + dput(pub) + Output + structure(list(list(given = NULL, family = "Entity Project Team Conference entity", + role = NULL, email = "project@entity.com", comment = c(name = "Entity Project Team Conference entity", + address = "22 Acacia Avenue", city = "Citationburgh", region = "Renfrewshire", + `post-code` = "C13 7X7", country = "GB", ORCID = "0000-0001-2345-6789", + tel = "+44(0)141-323 4567", fax = "+44(0)141-323 45678", + website = "https://www.entity-project-team.io", `date-start` = "2017-01-01", + `date-end` = "2017-01-31", location = "The team garage"))), class = "person") + +--- + + Code + format(pub, include = c("given", "family", "email", "role", "comment")) + Output + [1] "Entity Project Team Conference entity (Entity Project Team Conference entity, 22 Acacia Avenue, Citationburgh, Renfrewshire, C13 7X7, GB, , +44(0)141-323 4567, +44(0)141-323 45678, https://www.entity-project-team.io, 2017-01-01, 2017-01-31, The team garage)" + +--- + + Code + dput(aut) + Output + structure(list(list(given = "One Truly", family = "van der Real Person IV", + role = NULL, email = "project@entity.com", comment = c(alias = "Citey", + affiliation = "Excellent University, Niceplace, Arcadia", + address = "22 Acacia Avenue", city = "Citationburgh", region = "Renfrewshire", + `post-code` = "C13 7X7", country = "GB", ORCID = "0000-0001-2345-6789", + tel = "+44(0)141-323 4567", fax = "+44(0)141-323 45678", + website = "https://www.entity-project-team.io"))), class = "person") + +--- + + Code + format(aut, include = c("given", "family", "email", "role", "comment")) + Output + [1] "One Truly van der Real Person IV (Citey, Excellent University, Niceplace, Arcadia, 22 Acacia Avenue, Citationburgh, Renfrewshire, C13 7X7, GB, , +44(0)141-323 4567, +44(0)141-323 45678, https://www.entity-project-team.io)" + +--- + + Code + dput(aut2) + Output + structure(list(list(given = "One Truly", family = "van der Real Person IV", + role = NULL, email = "project@entity.com", comment = c(alias = "Citey", + affiliation = "Excellent University, Niceplace, Arcadia", + address = "22 Acacia Avenue", city = "Citationburgh", region = "Renfrewshire", + `post-code` = "C13 7X7", country = "GB", ORCID = "0000-0001-2345-6789", + tel = "+44(0)141-323 4567", fax = "+44(0)141-323 45678", + website = "https://www.entity-project-team.io")), list(given = NULL, + family = "Entity Project Team Conference entity", role = NULL, + email = "project@entity.com", comment = c(name = "Entity Project Team Conference entity", + address = "22 Acacia Avenue", city = "Citationburgh", region = "Renfrewshire", + `post-code` = "C13 7X7", country = "GB", ORCID = "0000-0001-2345-6789", + tel = "+44(0)141-323 4567", fax = "+44(0)141-323 45678", + website = "https://www.entity-project-team.io", `date-start` = "2017-01-01", + `date-end` = "2017-01-31", location = "The team garage"))), class = "person") + +--- + + Code + format(aut2, include = c("given", "family", "email", "role", "comment")) + Output + [1] "One Truly van der Real Person IV (Citey, Excellent University, Niceplace, Arcadia, 22 Acacia Avenue, Citationburgh, Renfrewshire, C13 7X7, GB, , +44(0)141-323 4567, +44(0)141-323 45678, https://www.entity-project-team.io)" + [2] "Entity Project Team Conference entity (Entity Project Team Conference entity, 22 Acacia Avenue, Citationburgh, Renfrewshire, C13 7X7, GB, , +44(0)141-323 4567, +44(0)141-323 45678, https://www.entity-project-team.io, 2017-01-01, 2017-01-31, The team garage)" + diff --git a/tests/testthat/_snaps/deprecated.md b/tests/testthat/_snaps/deprecated.md index 59613433..df31b200 100644 --- a/tests/testthat/_snaps/deprecated.md +++ b/tests/testthat/_snaps/deprecated.md @@ -13,6 +13,6 @@ old1 <- cff_to_bibtex(a_cff) Condition Warning: - `cff_extract_to_bibtex()` was deprecated in cffr 0.5.0. + `cff_extract_to_bibtex()` was deprecated in cffr 0.6.0. i Function renamed, use `cff_to_bibentry()` instead. diff --git a/tests/testthat/test-cff-class.R b/tests/testthat/test-cff-methods.R similarity index 70% rename from tests/testthat/test-cff-class.R rename to tests/testthat/test-cff-methods.R index 200259b1..53f7f50b 100644 --- a/tests/testthat/test-cff-class.R +++ b/tests/testthat/test-cff-methods.R @@ -132,3 +132,57 @@ test_that("Convert list of authors", { expect_identical(allf, df_as_v) expect_snapshot(names(the_df)) }) + + +test_that("as.person method", { + path <- system.file("examples/CITATION_complete.cff", package = "cffr") + + the_cff <- cff_read(path) + + getref <- the_cff$references[[1]] + + # Single entity + pub <- as.person(getref$publisher) + expect_s3_class(pub, "person") + + expect_snapshot(dput(pub)) + expect_snapshot( + format(pub, include = c("given", "family", "email", "role", "comment")) + ) + + # Single person + aut <- as.person(getref$authors[[1]]) + expect_s3_class(aut, "person") + expect_snapshot(dput(aut)) + expect_snapshot( + format(aut, include = c("given", "family", "email", "role", "comment")) + ) + + # List of authors + aut2 <- as.person(getref$authors) + + expect_s3_class(aut2, "person") + expect_s3_class(aut2[1], "person") + expect_s3_class(aut2[2], "person") + expect_snapshot(dput(aut2)) + expect_snapshot( + format(aut2, include = c("given", "family", "email", "role", "comment")) + ) +}) + +test_that("as person with another cff", { + path <- system.file("examples/CITATION_complete.cff", package = "cffr") + the_cff <- cff_read(path) + expect_s3_class(the_cff, "cff") + expect_identical(as.person(the_cff), person()) + + # identifiers + key <- the_cff$identifiers + expect_s3_class(key, "cff") + expect_identical(as.person(key), person()) + + # preferred + key <- the_cff$`preferred-citation` + expect_s3_class(key, "cff") + expect_identical(as.person(key), person()) +}) From 6d8ece239d483ead78e1df54e896ef6718ab0cc3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 16 Feb 2024 17:58:18 +0000 Subject: [PATCH 04/32] Update docs with pkgdev --- CITATION.cff | 3 +++ README.md | 25 ++++++++++++++++++++----- codemeta.json | 6 +++--- data/cran_to_spdx.rda | Bin 916 -> 916 bytes inst/schemaorg.json | 2 +- man/cff-class.Rd | 11 +++++------ man/chunks/cffclass.Rmd | 9 ++++----- 7 files changed, 36 insertions(+), 20 deletions(-) diff --git a/CITATION.cff b/CITATION.cff index ae2c7213..59675863 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -138,6 +138,9 @@ references: email: jeroen@berkeley.edu orcid: https://orcid.org/0000-0002-4035-0289 year: '2024' + identifiers: + - type: url + value: https://arxiv.org/abs/1403.2805 version: '>= 1.7.2' - type: software title: jsonvalidate diff --git a/README.md b/README.md index 11d7750f..b20d2624 100644 --- a/README.md +++ b/README.md @@ -385,6 +385,9 @@ test <- cff_create("rmarkdown") email: jeroen@berkeley.edu orcid: https://orcid.org/0000-0002-4035-0289 year: '2024' + identifiers: + - type: url + value: https://arxiv.org/abs/1403.2805 - type: software title: knitr abstract: 'knitr: A General-Purpose Package for Dynamic Report Generation in R' @@ -750,6 +753,18 @@ test <- cff_create("rmarkdown") given-names: Davis email: davis@posit.co year: '2024' + - type: software + title: cleanrmd + abstract: 'cleanrmd: Clean Class-Less ''R Markdown'' HTML Documents' + notes: Suggests + url: https://pkg.garrickadenbuie.com/cleanrmd/ + repository: https://CRAN.R-project.org/package=cleanrmd + authors: + - family-names: Aden-Buie + given-names: Garrick + email: garrick@adenbuie.com + orcid: https://orcid.org/0000-0002-7111-0077 + year: '2024' - type: software title: withr abstract: 'withr: Run Code ''With'' Temporarily Modified Global State' @@ -888,9 +903,9 @@ for more info.
-Boettiger, Carl, and Maëlle Salmon. 2021a. -*codemeta: A Smaller -codemetar Package*. +Boettiger, Carl, and Maëlle Salmon. 2021a. *codemeta: A Smaller codemetar Package*. .
@@ -911,8 +926,8 @@ Among Citation Formats*.
-Dietrich, Jan Philipp, and Waldir Leoncio. 2022. -*citation: Software Citation Tools*. +Dietrich, Jan Philipp, and Waldir Leoncio. 2022. *citation: Software Citation Tools*.
diff --git a/codemeta.json b/codemeta.json index e3f6821c..6db1f419 100644 --- a/codemeta.json +++ b/codemeta.json @@ -14,7 +14,7 @@ "name": "R", "url": "https://r-project.org" }, - "runtimePlatform": "R version 4.3.2 (2023-10-31 ucrt)", + "runtimePlatform": "R version 4.3.2 (2023-10-31)", "provider": { "@id": "https://cran.r-project.org", "@type": "Organization", @@ -200,7 +200,7 @@ }, "isPartOf": "https://ropensci.org", "keywords": ["attribution", "citation", "credit", "citation-files", "cff", "metadata", "r", "r-package", "citation-file-format", "rstats", "ropensci", "cran"], - "fileSize": "1048.177KB", + "fileSize": "1030.282KB", "citation": [ { "@type": "ScholarlyArticle", @@ -286,7 +286,7 @@ "sameAs": "https://doi.org/10.5281/zenodo.5171937" } ], - "releaseNotes": "https://github.com/ropensci/cffr/blob/main/NEWS.md", + "releaseNotes": "https://github.com/ropensci/cffr/blob/master/NEWS.md", "readme": "https://github.com/ropensci/cffr/blob/main/README.md", "contIntegration": ["https://github.com/ropensci/cffr/actions/workflows/check-full.yaml", "https://app.codecov.io/gh/ropensci/cffr", "https://github.com/ropensci/cffr/actions/workflows/cff-validator.yml"], "developmentStatus": ["https://www.repostatus.org/#active", "https://lifecycle.r-lib.org/articles/stages.html#experimental"], diff --git a/data/cran_to_spdx.rda b/data/cran_to_spdx.rda index 0bd1435cae708e762b962cb7a97ec9ae0c12fef9..54267cd1b0840c06bdfc1e554391353a60cd0a13 100644 GIT binary patch delta 19 XcmbQjK82l2zMF#q445}^buj|~Baj0= delta 19 XcmbQjK82l2zMF#q4A?eubuj|~BcB67 diff --git a/inst/schemaorg.json b/inst/schemaorg.json index dec7a9c6..8056af78 100644 --- a/inst/schemaorg.json +++ b/inst/schemaorg.json @@ -26,6 +26,6 @@ "name": "Comprehensive R Archive Network (CRAN)", "url": "https://cran.r-project.org" }, - "runtimePlatform": "R version 4.3.2 (2023-10-31 ucrt)", + "runtimePlatform": "R version 4.3.2 (2023-10-31)", "version": "0.5.0.9000" } diff --git a/man/cff-class.Rd b/man/cff-class.Rd index 8f856d3e..b6205b88 100644 --- a/man/cff-class.Rd +++ b/man/cff-class.Rd @@ -136,11 +136,10 @@ c(minimal_cff, new_keys) Special case for those CFF keys that are person-like. -\if{html}{\out{
}}\preformatted{ - path <- system.file("examples/CITATION_complete.cff", package = "cffr") - the_cff <- cff_read(path) +\if{html}{\out{
}}\preformatted{path <- system.file("examples/CITATION_complete.cff", package = "cffr") +the_cff <- cff_read(path) - the_cff$authors +the_cff$authors #> - family-names: Real Person #> given-names: One Truly #> name-particle: van der @@ -171,8 +170,8 @@ Special case for those CFF keys that are person-like. #> date-start: '2017-01-01' #> date-end: '2017-01-31' #> location: The team garage - - as.person(the_cff$authors) + +as.person(the_cff$authors) #> [1] "One Truly van der Real Person IV (Citey, Excellent University, Niceplace, Arcadia, 22 Acacia Avenue, Citationburgh, Renfrewshire, C13 7X7, GB, , +44(0)141-323 4567, +44(0)141-323 45678, https://www.entity-project-team.io)" #> [2] "Entity Project Team Conference entity (Entity Project Team Conference entity, 22 Acacia Avenue, Citationburgh, Renfrewshire, C13 7X7, GB, , +44(0)141-323 4567, +44(0)141-323 45678, https://www.entity-project-team.io, 2017-01-01, 2017-01-31, The team garage)" }\if{html}{\out{
}} diff --git a/man/chunks/cffclass.Rmd b/man/chunks/cffclass.Rmd index c2ca6834..cd673784 100644 --- a/man/chunks/cffclass.Rmd +++ b/man/chunks/cffclass.Rmd @@ -72,11 +72,10 @@ c(minimal_cff, new_keys) Special case for those CFF keys that are person-like. ```{r} +path <- system.file("examples/CITATION_complete.cff", package = "cffr") +the_cff <- cff_read(path) - path <- system.file("examples/CITATION_complete.cff", package = "cffr") - the_cff <- cff_read(path) +the_cff$authors - the_cff$authors - - as.person(the_cff$authors) +as.person(the_cff$authors) ``` From d4642b654a3ecf91a7a255acf9fce6c13dcf6d68 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 16 Feb 2024 18:07:31 +0000 Subject: [PATCH 05/32] revdepcheck --- revdep/README.md | 50 +++++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/revdep/README.md b/revdep/README.md index 78b0eb13..4fdd6143 100644 --- a/revdep/README.md +++ b/revdep/README.md @@ -1,34 +1,32 @@ # Platform -|field |value | -|:--------|:--------------------------------------| -|version |R version 4.3.0 (2023-04-21 ucrt) | -|os |Windows 11 x64 (build 22621) | -|system |x86_64, mingw32 | -|ui |RStudio | -|language |(EN) | -|collate |Spanish_Spain.utf8 | -|ctype |Spanish_Spain.utf8 | -|tz |Europe/Madrid | -|date |2023-05-05 | -|rstudio |2023.03.0+386 Cherry Blossom (desktop) | -|pandoc |2.19.2 @ C:\PROGRA~1\Pandoc\pandoc.exe | +|field |value | +|:--------|:------------------------------| +|version |R version 4.3.2 (2023-10-31) | +|os |macOS Monterey 12.7.3 | +|system |x86_64, darwin20 | +|ui |X11 | +|language |(EN) | +|collate |en_US.UTF-8 | +|ctype |en_US.UTF-8 | +|tz |UTC | +|date |2024-02-16 | +|pandoc |2.19.2 @ /usr/local/bin/pandoc | # Dependencies -|package |old |new |Δ | -|:------------|:------|:------|:--| -|cffr |0.4.1 |0.5.0 |* | -|cli |3.6.1 |3.6.1 | | -|curl |5.0.0 |5.0.0 | | -|desc |1.4.2 |1.4.2 | | -|jsonlite |1.8.4 |1.8.4 | | -|jsonvalidate |1.3.2 |1.3.2 | | -|R6 |2.5.1 |2.5.1 | | -|Rcpp |1.0.10 |1.0.10 | | -|rprojroot |2.0.3 |2.0.3 | | -|V8 |4.3.0 |4.3.0 | | -|yaml |2.3.7 |2.3.7 | | +|package |old |new |Δ | +|:------------|:------|:----------|:--| +|cffr |0.5.0 |0.5.0.9000 |* | +|cli |3.6.2 |3.6.2 | | +|curl |5.2.0 |5.2.0 | | +|desc |1.4.3 |1.4.3 | | +|jsonlite |1.8.8 |1.8.8 | | +|jsonvalidate |1.3.2 |1.3.2 | | +|R6 |2.5.1 |2.5.1 | | +|Rcpp |1.0.12 |1.0.12 | | +|V8 |4.4.2 |4.4.2 | | +|yaml |2.3.8 |2.3.8 | | # Revdeps From c0d7d6308d2098ef9f53e5734e56243a53e9ffab Mon Sep 17 00:00:00 2001 From: dieghernan Date: Mon, 19 Feb 2024 15:55:54 +0000 Subject: [PATCH 06/32] Add head, tail and start working in reading files --- NAMESPACE | 7 + NEWS.md | 18 +- R/cff-methods.R | 17 + R/cff.R | 206 ++ R/cff_create.R | 2 +- R/cff_read.R | 279 ++- R/cff_to_bibentry.R | 2 +- R/cffr-package.R | 2 +- R/utils-create.R | 49 - ...description.R => utils-read-description.R} | 0 man/cff.Rd | 112 ++ man/cff_create.Rd | 2 +- man/cff_read.Rd | 141 +- man/cff_validate.Rd | 2 +- man/cff_write.Rd | 2 +- man/chunks/cffclass.Rmd | 5 + tests/testthat/_snaps/cff-class.md | 839 -------- tests/testthat/_snaps/cff-methods.md | 30 + tests/testthat/_snaps/cff.md | 1709 ++++++++++++++++ tests/testthat/_snaps/cff_from_bibtex.md | 22 - tests/testthat/_snaps/cff_read.md | 1731 +---------------- tests/testthat/test-cff-methods.R | 10 + tests/testthat/test-cff.R | 114 ++ tests/testthat/test-cff_from_bibtex.R | 24 - tests/testthat/test-cff_read.R | 146 +- tests/testthat/test-cff_to_bibentry.R | 9 +- tests/testthat/test-merge_desc_cit.R | 2 +- tests/testthat/test-utils-persons.R | 4 +- 28 files changed, 2505 insertions(+), 2981 deletions(-) create mode 100644 R/cff.R rename R/{parse_description.R => utils-read-description.R} (100%) create mode 100644 man/cff.Rd delete mode 100644 tests/testthat/_snaps/cff-class.md create mode 100644 tests/testthat/_snaps/cff.md create mode 100644 tests/testthat/test-cff.R diff --git a/NAMESPACE b/NAMESPACE index d766eb4c..41017f89 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -3,7 +3,9 @@ S3method(as.data.frame,cff) S3method(as.person,cff) S3method(c,cff) +S3method(head,cff) S3method(print,cff) +S3method(tail,cff) export(as.cff) export(cff) export(cff_create) @@ -16,6 +18,9 @@ export(cff_parse_citation) export(cff_parse_person) export(cff_parse_person_bibtex) export(cff_read) +export(cff_read_bib) +export(cff_read_cff_citation) +export(cff_read_description) export(cff_schema_definitions_entity) export(cff_schema_definitions_person) export(cff_schema_definitions_refs) @@ -35,9 +40,11 @@ importFrom(utils,bibentry) importFrom(utils,capture.output) importFrom(utils,citation) importFrom(utils,download.file) +importFrom(utils,head) importFrom(utils,installed.packages) importFrom(utils,modifyList) importFrom(utils,packageDescription) importFrom(utils,person) importFrom(utils,read.csv) +importFrom(utils,tail) importFrom(utils,toBibtex) diff --git a/NEWS.md b/NEWS.md index 20d53857..94412bb8 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,11 +1,24 @@ # cffr (development version) -## Major changes in the API +## Major changes + +- Now `class()` of `cff` objects are `c("cff", "list")` instead of single + value (`"cff"`). + + ### API - The conversion from `cff` to `bibentry` is performed now by a new function `cff_to_bibentry()`. Previous names of this function were `cff_to_bibtex()` and `cff_extract_to_bibtex()` that are now superseded. -- New methods: + +- Now reading from external files is performed exclusively by `cff_read()` and + additionally by the more-specific new functions `cff_read_cff_citation()`, + `cff_read_description()`, `cff_read_citation()` and `cff_read_bib()`. + + ### Methods + +- New methods added: + - `as.data.frame.cff().` - `as.person.cff()`, that provides results **only** for CFF keys defined as @@ -13,6 +26,7 @@ or [entity](https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md#definitionsentity) (e.g. authors, contacts, editors, publisher). + - `head.cff()`, `tail.cff()`. ## Changes on bibtex crosswalk diff --git a/R/cff-methods.R b/R/cff-methods.R index d56c4c19..766de4d6 100644 --- a/R/cff-methods.R +++ b/R/cff-methods.R @@ -63,6 +63,23 @@ as.person.cff <- function(x) { } do.call(c, pers) } + +#' Head +#' +#' @noRd +#' @export +head.cff <- function(x, n = 6L, ...) { + as.cff(NextMethod()) +} + +#' Tail +#' +#' @noRd +#' @export +tail.cff <- function(x, n = 6L, ...) { + as.cff(NextMethod()) +} + make_r_person <- function(x) { if (is.null(names(x))) { return(person()) diff --git a/R/cff.R b/R/cff.R new file mode 100644 index 00000000..6afd6055 --- /dev/null +++ b/R/cff.R @@ -0,0 +1,206 @@ +#' Read and manipulate `cff` objects +#' +#' A class and utility methods for reading, creating and holding CFF +#' information. +#' +#' @rdname cff +#' @name cff +#' @return +#' A `cff` object. Under the hood, a `cff` object is a regular [`list`] object +#' with a special [print()] method. +#' +#' @family Core functions +#' +#' @param path The path to a `CITATION.cff` file. +#' @param ... Named arguments to be used for creating a [`cff`] object. See +#' **Details**. +#' +#' @details +#' +#' This object can be manipulated using [cff_create()]. +#' +#' **Note that** this function reads `CITATION.cff` files. If you want to +#' create `cff` objects from DESCRIPTION files use [cff_create()]. +#' +#' If no additional `...` parameters are supplied (the default behavior), +#' a minimal valid `cff` object is created. Valid parameters are those +#' specified on [cff_schema_keys()]: +#' +#' +#' +#' ```{r, echo=FALSE} +#' +#' +#' valid_keys <- cff_schema_keys() +#' +#' knitr::kable(valid_keys, col.names = "**valid cff keys**") +#' +#' +#' ``` +#' @export +#' @examples +#' +#' # Blank cff +#' cff() +#' +#' # From file +#' cff_read(system.file("examples/CITATION_basic.cff", +#' package = "cffr" +#' )) +#' +#' # Use custom params +#' test <- cff( +#' title = "Manipulating files", +#' keywords = c("A", "new", "list", "of", "keywords"), +#' authors = list(cff_parse_person("New author")) +#' ) +#' test +#' \donttest{ +#' # Would fail +#' cff_validate(test) +#' +#' +#' # Modify with cff_create +#' new <- cff_create(test, keys = list( +#' "cff-version" = "1.2.0", +#' message = "A blank file" +#' )) +#' new +#' +#' # Would pass +#' cff_validate(new) +#' } +#' @export +cff <- function(path, ...) { + if (!missing(path) && is_cff(path)) { + return(path) + } + + # Capture args + cffobj <- list(...) + + if (!missing(path)) { + stopifnotexists(path) + stopifnotcff(path) + cffobj <- yaml::read_yaml(path) + } else if (length(cffobj) != 0) { + cffobj <- fuzzy_keys(cffobj) + + cffobj <- cffobj + } else { + # If nothing is provided use a minimal cff + path <- system.file("examples/CITATION_skeleton.cff", + package = "cffr" + ) + cffobj <- yaml::read_yaml(path) + } + cffobj <- drop_null(cffobj) + + cffobj <- as.cff(cffobj) + + return(cffobj) +} + + + + +#' @rdname cff +#' +#' @param x a character string for the [`as.cff`] default method +#' +#' @export +#' +#' @examples +#' +#' +#' # Convert a list to "cff" object +#' cffobj <- as.cff(list( +#' "cff-version" = "1.2.0", +#' title = "Manipulating files" +#' )) +#' +#' class(cffobj) +#' +#' # Nice display thanks to yaml package +#' cffobj +as.cff <- function(x) { + if (is_cff(x)) { + return(x) + } + + # Clean all strings recursively + + x <- rapply(x, function(x) { + if (is.list(x) || length(x) > 1) { + return(x) + } + return(clean_str(x)) + }, + how = "list" + ) + + # Remove NULLs + x <- drop_null(x) + + # Remove duplicated names + x <- x[!duplicated(names(x))] + + # Now apply cff class to nested lists + x <- lapply(x, rapply_cff) + + class(x) <- "cff" + x +} + +# Helper---- + +#' Recursively clean lists and assign cff classes +#' to all nested lists +#' +#' +#' @noRd +rapply_cff <- function(x) { + if (inherits(x, "cff")) { + return(x) + } + + if (is.list(x) && length(x) > 0) { + x <- drop_null(x) + x <- lapply(x, rapply_cff) + return(structure(x, class = c("cff", "list"))) + } else { + return(x) + } +} + +# https://adv-r.hadley.nz/s3.html#s3-constructor +# Constructor +new_cff <- function(x) { + if (is_cff(x)) { + class(x) <- c("cff", "list") + return(x) + } + + # Clean all strings recursively + + x <- rapply(x, function(x) { + if (is.list(x) || length(x) > 1) { + return(x) + } + return(clean_str(x)) + }, + how = "list" + ) + + # Remove NULLs + x <- drop_null(x) + + # Remove duplicated names + x <- x[!duplicated(names(x))] + + # Now apply cff class to nested lists + x <- lapply(x, rapply_cff) + + class(x) <- c("cff", "list") + x +} diff --git a/R/cff_create.R b/R/cff_create.R index 4cc1965f..d8965aad 100644 --- a/R/cff_create.R +++ b/R/cff_create.R @@ -171,7 +171,7 @@ cff_create <- function(x, keys = list(), cff_version = "1.2.0", cli::cli_abort("No {.file DESCRIPTION} file found with {.arg x}") } - cffobj <- cff_description(desc_path, cff_version, + cffobj <- cff_read_description(desc_path, cff_version, gh_keywords = gh_keywords, authors_roles = authors_roles ) diff --git a/R/cff_read.R b/R/cff_read.R index bddc362d..b0c3af75 100644 --- a/R/cff_read.R +++ b/R/cff_read.R @@ -1,181 +1,158 @@ -#' Read and manipulate `cff` objects +#' Read an external file as a [`cff`][cff-class] object +#' +#' @description +#' Read files and convert them to [`cff`][cff-class] objects. Files supported +#' are: +#' - `CITATION.cff` files +#' - `DESCRIPTION` files +#' - **R** citation files (usually located in `inst/CITATION`) +#' - BibTeX files (with extension `*.bib`). +#' +#' [cff_read()] would try to guess the type of file provided in `path`. However +#' we provide a series of alias for each specific type of file: +#' - [cff_read_cff_citation()], that uses [yaml::read_yaml()]. +#' - [cff_read_description()], using [desc::desc()]. +#' - [cff_read_citation()] uses [utils::readCitationFile()]. +#' - [cff_read_bib()] requires \CRANpkg{bibtex} (>= 0.5.0) and uses +#' [bibtex::read.bib()]. #' -#' A class and utility methods for reading, creating and holding CFF -#' information. -#' -#' @name cff_read -#' @aliases cff -#' @return -#' A `cff` object. Under the hood, a `cff` object is a regular [`list`] object -#' with a special [print()] method. -#' -#' @family Core functions -#' -#' @param path The path to a `CITATION.cff` file. -#' @param ... Named arguments to be used for creating a [`cff`] object. See -#' **Details**. -#' -#' @details -#' -#' This object can be manipulated using [cff_create()]. -#' -#' **Note that** this function reads `CITATION.cff` files. If you want to -#' create `cff` objects from DESCRIPTION files use [cff_create()]. -#' -#' If no additional `...` parameters are supplied (the default behavior), -#' a minimal valid `cff` object is created. Valid parameters are those -#' specified on [cff_schema_keys()]: -#' -#' -#' -#' ```{r, echo=FALSE} -#' -#' -#' valid_keys <- cff_schema_keys() -#' -#' knitr::kable(valid_keys, col.names = "**valid cff keys**") -#' -#' -#' ``` #' @export -#' @examples -#' -#' # Blank cff -#' cff() -#' -#' # From file -#' cff_read(system.file("examples/CITATION_basic.cff", -#' package = "cffr" -#' )) +#' @rdname cff_read #' -#' # Use custom params -#' test <- cff( -#' title = "Manipulating files", -#' keywords = c("A", "new", "list", "of", "keywords"), -#' authors = list(cff_parse_person("New author")) -#' ) -#' test -#' \donttest{ -#' # Would fail -#' cff_validate(test) +#' @param path Path to a file +#' @param cff_version The Citation File Format schema version that the +#' `CITATION.cff` file adheres to for providing the citation metadata. +#' @param gh_keywords Logical `TRUE/FALSE`. If the package is hosted on +#' GitHub, would you like to add the repo topics as keywords? +#' @param authors_roles Roles to be considered as authors of the package when +#' generating the [`cff`][cff-class] object. +#' @param encoding Encoding to be assumed for `path`. See [readLines()]. +#' @param ... Arguments to be passed to other functions. #' +#' @return A [`cff`][cff-class] object. #' -#' # Modify with cff_create -#' new <- cff_create(test, keys = list( -#' "cff-version" = "1.2.0", -#' message = "A blank file" -#' )) -#' new #' -#' # Would pass -#' cff_validate(new) -#' } -cff_read <- function(path) { - cffobj <- cff(path = path) - - return(cffobj) -} - -#' @rdname cff_read -#' @export -cff <- function(path, ...) { - if (!missing(path) && is_cff(path)) { - return(path) +cff_read <- function(path, ...) { + if (length(path) > 1) { + cli::cli_abort( + "Use a single value, {.arg path} has length {.val {length(path)}}" + ) } - # Capture args - cffobj <- list(...) - - if (!missing(path)) { - stopifnotexists(path) - stopifnotcff(path) - cffobj <- yaml::read_yaml(path) - } else if (length(cffobj) != 0) { - cffobj <- fuzzy_keys(cffobj) - - cffobj <- cffobj - } else { - # If nothing is provided use a minimal cff - path <- system.file("examples/CITATION_skeleton.cff", - package = "cffr" + if (!file.exists(path)) { + cli::cli_abort( + paste( + "{.file {path}} does not exist. ", + "Check the {.file {dirname(path)}} directory" + ) ) - cffobj <- yaml::read_yaml(path) } - cffobj <- drop_null(cffobj) + filetype <- guess_type_file(path) - cffobj <- as.cff(cffobj) + endobj <- switch(filetype, + "cff_citation" = cff_read_cff_citation(path, ...), + "description" = cff_read_description(path, ...), + "bib" = cff_read_bib(path, ...), + NULL + ) - return(cffobj) + endobj } +#' @export +#' @rdname cff_read +cff_read_cff_citation <- function(path, ...) { + cffobj <- yaml::read_yaml(path) + new_cff(cffobj) +} +#' @export +#' @rdname cff_read +cff_read_description <- function(path, cff_version = "1.2.0", + gh_keywords = TRUE, + authors_roles = c("aut", "cre"), ...) { + pkg <- desc::desc(path) + pkg$coerce_authors_at_r() + + msg <- paste0( + 'To cite package "', pkg$get("Package"), + '" in publications use:' + ) -#' @rdname cff_read -#' -#' @param x a character string for the [`as.cff`] default method -#' -#' @export -#' -#' @examples -#' -#' -#' # Convert a list to "cff" object -#' cffobj <- as.cff(list( -#' "cff-version" = "1.2.0", -#' title = "Manipulating files" -#' )) -#' -#' class(cffobj) -#' -#' # Nice display thanks to yaml package -#' cffobj -as.cff <- function(x) { - if (is_cff(x)) { - return(x) + list_fields <- list( + "cff-version" = cff_version, + message = msg, + type = "software", + title = parse_desc_title(pkg), + version = parse_desc_version(pkg), + authors = parse_desc_authors(pkg, authors_roles = authors_roles), + abstract = parse_desc_abstract(pkg), + repository = parse_desc_repository(pkg), + "repository-code" = parse_desc_urls(pkg)$repo, + url = parse_desc_urls(pkg)$url, + identifiers = parse_desc_urls(pkg)$identifiers, + "date-released" = parse_desc_date_released(pkg), + contact = parse_desc_contacts(pkg), + keywords = parse_desc_keywords(pkg), + license = unlist(parse_desc_license(pkg)) + ) + + if (gh_keywords) { + ghtopics <- parse_ghtopics(list_fields) + list_fields$keywords <- unique(c(list_fields$keywords, ghtopics)) } - # Clean all strings recursively + new_cff(list_fields) +} - x <- rapply(x, function(x) { - if (is.list(x) || length(x) > 1) { - return(x) - } - return(clean_str(x)) - }, - how = "list" - ) +#' @export +#' @rdname cff_read +cff_read_cff_citation <- function(path, ...) { + cffobj <- yaml::read_yaml(path) + new_cff(cffobj) +} - # Remove NULLs - x <- drop_null(x) +#' @export +#' @rdname cff_read +cff_read_bib <- function(path, encoding = "UTF-8", ...) { + # nocov start + if (!requireNamespace("bibtex", quietly = TRUE)) { + msg <- paste0( + "{.pkg bibtex} package required for using this function: ", + '{.run install.packages("bibtex")}' + ) + cli::cli_abort(msg) + } + # nocov end - # Remove duplicated names - x <- x[!duplicated(names(x))] + # Read from tempfile + read_bib <- bibtex::read.bib(file = path, encoding = encoding, ...) - # Now apply cff class to nested lists - x <- lapply(x, rapply_cff) - class(x) <- "cff" - x + tocff <- cff_parse_citation(read_bib) + new_cff(tocff) } -# Helper---- -#' Recursively clean lists and assign cff classes -#' to all nested lists -#' -#' -#' @noRd -rapply_cff <- function(x) { - if (inherits(x, "cff")) { - return(x) +guess_type_file <- function(path) { + if (grepl("\\.cff$", path, ignore.case = TRUE)) { + return("cff_citation") } - - if (is.list(x) && length(x) > 0) { - x <- drop_null(x) - x <- lapply(x, rapply_cff) - return(structure(x, class = "cff")) - } else { - return(x) + if (grepl("\\.bib$", path, ignore.case = TRUE)) { + return("bib") + } + if (grepl("citat", path, ignore.case = TRUE)) { + return("citation") + } + if (grepl("desc", path, ignore.case = TRUE)) { + return("description") } + + cli::cli_abort( + paste0( + "Don't recognize the file type of {.file {path}}.", + " Use a specific function (e.g. {.fn cffr:cff_read_description}" + ) + ) } diff --git a/R/cff_to_bibentry.R b/R/cff_to_bibentry.R index f7e4c0a3..0f8e344e 100644 --- a/R/cff_to_bibentry.R +++ b/R/cff_to_bibentry.R @@ -95,7 +95,7 @@ cff_to_bibentry <- function(x, } if (is_cff_file(x)) { - x <- cff_read(x) + x <- cff_read_cff_citation(x) } if (is_cff(x)) { diff --git a/R/cffr-package.R b/R/cffr-package.R index 9a858619..3a4d0134 100644 --- a/R/cffr-package.R +++ b/R/cffr-package.R @@ -8,5 +8,5 @@ NULL # import stuffs #' @importFrom utils download.file as.person read.csv person capture.output #' installed.packages packageDescription citation available.packages -#' modifyList toBibtex bibentry +#' modifyList toBibtex bibentry head tail NULL diff --git a/R/utils-create.R b/R/utils-create.R index 4a553d0c..45678633 100644 --- a/R/utils-create.R +++ b/R/utils-create.R @@ -1,52 +1,3 @@ -#' @noRd -cff_description <- function(desc_path = "DESCRIPTION", - cff_version = "1.2.0", - gh_keywords = TRUE, - authors_roles = c("aut", "cre")) { - pkg <- desc::desc(desc_path) - pkg$coerce_authors_at_r() - - msg <- paste0( - 'To cite package "', - pkg$get("Package"), - '" in publications use:' - ) - - - list_fields <- list( - "cff-version" = cff_version, - message = msg, - type = "software", - title = parse_desc_title(pkg), - version = parse_desc_version(pkg), - authors = parse_desc_authors(pkg, authors_roles = authors_roles), - abstract = parse_desc_abstract(pkg), - repository = parse_desc_repository(pkg), - "repository-code" = parse_desc_urls(pkg)$repo, - url = parse_desc_urls(pkg)$url, - identifiers = parse_desc_urls(pkg)$identifiers, - "date-released" = parse_desc_date_released(pkg), - contact = parse_desc_contacts(pkg), - keywords = parse_desc_keywords(pkg), - license = unlist(parse_desc_license(pkg)) - ) - - if (gh_keywords) { - ghtopics <- parse_ghtopics(list_fields) - list_fields$keywords <- unique( - c( - list_fields$keywords, - ghtopics - ) - ) - } - - list_fields <- as.cff(list_fields) - list_fields -} - - - #' Merge the information of a parsed description with a parsed citation #' @noRd merge_desc_cit <- function(cffobj, citobj) { diff --git a/R/parse_description.R b/R/utils-read-description.R similarity index 100% rename from R/parse_description.R rename to R/utils-read-description.R diff --git a/man/cff.Rd b/man/cff.Rd new file mode 100644 index 00000000..6731e872 --- /dev/null +++ b/man/cff.Rd @@ -0,0 +1,112 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/cff.R +\name{cff} +\alias{cff} +\alias{as.cff} +\title{Read and manipulate \code{cff} objects} +\usage{ +cff(path, ...) + +as.cff(x) +} +\arguments{ +\item{path}{The path to a \code{CITATION.cff} file.} + +\item{...}{Named arguments to be used for creating a \code{\link{cff}} object. See +\strong{Details}.} + +\item{x}{a character string for the \code{\link{as.cff}} default method} +} +\value{ +A \code{cff} object. Under the hood, a \code{cff} object is a regular \code{\link{list}} object +with a special \code{\link[=print]{print()}} method. +} +\description{ +A class and utility methods for reading, creating and holding CFF +information. +} +\details{ +This object can be manipulated using \code{\link[=cff_create]{cff_create()}}. + +\strong{Note that} this function reads \code{CITATION.cff} files. If you want to +create \code{cff} objects from DESCRIPTION files use \code{\link[=cff_create]{cff_create()}}. + +If no additional \code{...} parameters are supplied (the default behavior), +a minimal valid \code{cff} object is created. Valid parameters are those +specified on \code{\link[=cff_schema_keys]{cff_schema_keys()}}:\tabular{l}{ + \strong{valid cff keys} \cr + cff-version \cr + message \cr + type \cr + license \cr + title \cr + version \cr + doi \cr + abstract \cr + authors \cr + preferred-citation \cr + repository \cr + repository-artifact \cr + repository-code \cr + url \cr + date-released \cr + contact \cr + keywords \cr + references \cr + commit \cr + identifiers \cr + license-url \cr +} +} +\examples{ + +# Blank cff +cff() + +# From file +cff_read(system.file("examples/CITATION_basic.cff", + package = "cffr" +)) + +# Use custom params +test <- cff( + title = "Manipulating files", + keywords = c("A", "new", "list", "of", "keywords"), + authors = list(cff_parse_person("New author")) +) +test +\donttest{ +# Would fail +cff_validate(test) + + +# Modify with cff_create +new <- cff_create(test, keys = list( + "cff-version" = "1.2.0", + message = "A blank file" +)) +new + +# Would pass +cff_validate(new) +} + + +# Convert a list to "cff" object +cffobj <- as.cff(list( + "cff-version" = "1.2.0", + title = "Manipulating files" +)) + +class(cffobj) + +# Nice display thanks to yaml package +cffobj +} +\seealso{ +Other Core functions: +\code{\link{cff_create}()}, +\code{\link{cff_validate}()}, +\code{\link{cff_write}()} +} +\concept{Core functions} diff --git a/man/cff_create.Rd b/man/cff_create.Rd index 643693bd..39db6269 100644 --- a/man/cff_create.Rd +++ b/man/cff_create.Rd @@ -113,7 +113,7 @@ cff_create(demo_file, keys = list("contact" = new_contact)) \code{vignette("cffr", "cffr")} Other Core functions: -\code{\link{cff_read}()}, +\code{\link{cff}()}, \code{\link{cff_validate}()}, \code{\link{cff_write}()} } diff --git a/man/cff_read.Rd b/man/cff_read.Rd index 3dfdb77b..52dd0300 100644 --- a/man/cff_read.Rd +++ b/man/cff_read.Rd @@ -2,114 +2,63 @@ % Please edit documentation in R/cff_read.R \name{cff_read} \alias{cff_read} -\alias{cff} -\alias{as.cff} -\title{Read and manipulate \code{cff} objects} +\alias{cff_read_cff_citation} +\alias{cff_read_description} +\alias{cff_read_bib} +\title{Read an external file as a \code{\link[=cff-class]{cff}} object} \usage{ -cff_read(path) +cff_read(path, ...) -cff(path, ...) +cff_read_cff_citation(path, ...) -as.cff(x) -} -\arguments{ -\item{path}{The path to a \code{CITATION.cff} file.} - -\item{...}{Named arguments to be used for creating a \code{\link{cff}} object. See -\strong{Details}.} - -\item{x}{a character string for the \code{\link{as.cff}} default method} -} -\value{ -A \code{cff} object. Under the hood, a \code{cff} object is a regular \code{\link{list}} object -with a special \code{\link[=print]{print()}} method. -} -\description{ -A class and utility methods for reading, creating and holding CFF -information. -} -\details{ -This object can be manipulated using \code{\link[=cff_create]{cff_create()}}. +cff_read_description( + path, + cff_version = "1.2.0", + gh_keywords = TRUE, + authors_roles = c("aut", "cre"), + ... +) -\strong{Note that} this function reads \code{CITATION.cff} files. If you want to -create \code{cff} objects from DESCRIPTION files use \code{\link[=cff_create]{cff_create()}}. +cff_read_cff_citation(path, ...) -If no additional \code{...} parameters are supplied (the default behavior), -a minimal valid \code{cff} object is created. Valid parameters are those -specified on \code{\link[=cff_schema_keys]{cff_schema_keys()}}:\tabular{l}{ - \strong{valid cff keys} \cr - cff-version \cr - message \cr - type \cr - license \cr - title \cr - version \cr - doi \cr - abstract \cr - authors \cr - preferred-citation \cr - repository \cr - repository-artifact \cr - repository-code \cr - url \cr - date-released \cr - contact \cr - keywords \cr - references \cr - commit \cr - identifiers \cr - license-url \cr -} +cff_read_bib(path, encoding = "UTF-8", ...) } -\examples{ - -# Blank cff -cff() +\arguments{ +\item{path}{Path to a file} -# From file -cff_read(system.file("examples/CITATION_basic.cff", - package = "cffr" -)) +\item{...}{Arguments to be passed to other functions.} -# Use custom params -test <- cff( - title = "Manipulating files", - keywords = c("A", "new", "list", "of", "keywords"), - authors = list(cff_parse_person("New author")) -) -test -\donttest{ -# Would fail -cff_validate(test) +\item{cff_version}{The Citation File Format schema version that the +\code{CITATION.cff} file adheres to for providing the citation metadata.} +\item{gh_keywords}{Logical \code{TRUE/FALSE}. If the package is hosted on +GitHub, would you like to add the repo topics as keywords?} -# Modify with cff_create -new <- cff_create(test, keys = list( - "cff-version" = "1.2.0", - message = "A blank file" -)) -new +\item{authors_roles}{Roles to be considered as authors of the package when +generating the \code{\link[=cff-class]{cff}} object.} -# Would pass -cff_validate(new) +\item{encoding}{Encoding to be assumed for \code{path}. See \code{\link[=readLines]{readLines()}}.} +} +\value{ +A \code{\link[=cff-class]{cff}} object. +} +\description{ +Read files and convert them to \code{\link[=cff-class]{cff}} objects. Files supported +are: +\itemize{ +\item \code{CITATION.cff} files +\item \code{DESCRIPTION} files +\item \strong{R} citation files (usually located in \code{inst/CITATION}) +\item BibTeX files (with extension \verb{*.bib}). } - -# Convert a list to "cff" object -cffobj <- as.cff(list( - "cff-version" = "1.2.0", - title = "Manipulating files" -)) - -class(cffobj) - -# Nice display thanks to yaml package -cffobj +\code{\link[=cff_read]{cff_read()}} would try to guess the type of file provided in \code{path}. However +we provide a series of alias for each specific type of file: +\itemize{ +\item \code{\link[=cff_read_cff_citation]{cff_read_cff_citation()}}, that uses \code{\link[yaml:read_yaml]{yaml::read_yaml()}}. +\item \code{\link[=cff_read_description]{cff_read_description()}}, using \code{\link[desc:desc]{desc::desc()}}. +\item \code{\link[=cff_read_citation]{cff_read_citation()}} uses \code{\link[utils:citation]{utils::readCitationFile()}}. +\item \code{\link[=cff_read_bib]{cff_read_bib()}} requires \CRANpkg{bibtex} (>= 0.5.0) and uses +\code{\link[bibtex:read.bib]{bibtex::read.bib()}}. } -\seealso{ -Other Core functions: -\code{\link{cff_create}()}, -\code{\link{cff_validate}()}, -\code{\link{cff_write}()} } -\concept{Core functions} diff --git a/man/cff_validate.Rd b/man/cff_validate.Rd index 4478e99b..f44cfe85 100644 --- a/man/cff_validate.Rd +++ b/man/cff_validate.Rd @@ -51,8 +51,8 @@ try(cff_validate(system.file("CITATION", package = "cffr"))) \href{https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md}{Guide to Citation File Format schema version 1.2.0}. Other Core functions: +\code{\link{cff}()}, \code{\link{cff_create}()}, -\code{\link{cff_read}()}, \code{\link{cff_write}()} } \concept{Core functions} diff --git a/man/cff_write.Rd b/man/cff_write.Rd index 25dad11c..5678352e 100644 --- a/man/cff_write.Rd +++ b/man/cff_write.Rd @@ -80,8 +80,8 @@ file.remove(tmpfile) \href{https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md}{Guide to Citation File Format schema version 1.2.0}. Other Core functions: +\code{\link{cff}()}, \code{\link{cff_create}()}, -\code{\link{cff_read}()}, \code{\link{cff_validate}()} } \concept{Core functions} diff --git a/man/chunks/cffclass.Rmd b/man/chunks/cffclass.Rmd index cd673784..e1dda711 100644 --- a/man/chunks/cffclass.Rmd +++ b/man/chunks/cffclass.Rmd @@ -6,6 +6,11 @@ represent the information of a `*.cff` file in **R**. Under the hood, a `cff` object is simply a named [list()] to which we added additional methods, most notably [print()]. +```{r include=FALSE} +library(cffr) +``` + + ```{r} a_named_list <- list( first = "I", second = "am", third = "a", fourth = "list", diff --git a/tests/testthat/_snaps/cff-class.md b/tests/testthat/_snaps/cff-class.md deleted file mode 100644 index 85c94c3b..00000000 --- a/tests/testthat/_snaps/cff-class.md +++ /dev/null @@ -1,839 +0,0 @@ -# as data frame complete - - Code - names(the_df) - Output - [1] "cff_version" - [2] "message" - [3] "abstract" - [4] "authors.00.family_names" - [5] "authors.00.given_names" - [6] "authors.00.name_particle" - [7] "authors.00.name_suffix" - [8] "authors.00.alias" - [9] "authors.00.affiliation" - [10] "authors.00.address" - [11] "authors.00.city" - [12] "authors.00.region" - [13] "authors.00.post_code" - [14] "authors.00.country" - [15] "authors.00.orcid" - [16] "authors.00.email" - [17] "authors.00.tel" - [18] "authors.00.fax" - [19] "authors.00.website" - [20] "authors.01.name" - [21] "authors.01.address" - [22] "authors.01.city" - [23] "authors.01.region" - [24] "authors.01.post_code" - [25] "authors.01.country" - [26] "authors.01.orcid" - [27] "authors.01.email" - [28] "authors.01.tel" - [29] "authors.01.fax" - [30] "authors.01.website" - [31] "authors.01.date_start" - [32] "authors.01.date_end" - [33] "authors.01.location" - [34] "commit" - [35] "contact.00.family_names" - [36] "contact.00.given_names" - [37] "contact.00.name_particle" - [38] "contact.00.name_suffix" - [39] "contact.00.alias" - [40] "contact.00.affiliation" - [41] "contact.00.address" - [42] "contact.00.city" - [43] "contact.00.region" - [44] "contact.00.post_code" - [45] "contact.00.country" - [46] "contact.00.orcid" - [47] "contact.00.email" - [48] "contact.00.tel" - [49] "contact.00.fax" - [50] "contact.00.website" - [51] "contact.01.name" - [52] "contact.01.address" - [53] "contact.01.city" - [54] "contact.01.region" - [55] "contact.01.post_code" - [56] "contact.01.country" - [57] "contact.01.orcid" - [58] "contact.01.email" - [59] "contact.01.tel" - [60] "contact.01.fax" - [61] "contact.01.website" - [62] "contact.01.date_start" - [63] "contact.01.date_end" - [64] "contact.01.location" - [65] "date_released" - [66] "doi" - [67] "identifiers.00.type" - [68] "identifiers.00.value" - [69] "identifiers.01.type" - [70] "identifiers.01.value" - [71] "identifiers.02.type" - [72] "identifiers.02.value" - [73] "identifiers.03.type" - [74] "identifiers.03.value" - [75] "keywords.00" - [76] "keywords.01" - [77] "keywords.02" - [78] "keywords.03" - [79] "license" - [80] "license_url" - [81] "repository" - [82] "repository_code" - [83] "repository_artifact" - [84] "title" - [85] "type" - [86] "url" - [87] "version" - [88] "preferred_citation.type" - [89] "preferred_citation.title" - [90] "preferred_citation.abbreviation" - [91] "preferred_citation.abstract" - [92] "preferred_citation.collection_doi" - [93] "preferred_citation.collection_title" - [94] "preferred_citation.collection_type" - [95] "preferred_citation.commit" - [96] "preferred_citation.copyright" - [97] "preferred_citation.data_type" - [98] "preferred_citation.database" - [99] "preferred_citation.date_accessed" - [100] "preferred_citation.date_downloaded" - [101] "preferred_citation.date_released" - [102] "preferred_citation.date_published" - [103] "preferred_citation.department" - [104] "preferred_citation.doi" - [105] "preferred_citation.edition" - [106] "preferred_citation.end" - [107] "preferred_citation.entry" - [108] "preferred_citation.filename" - [109] "preferred_citation.format" - [110] "preferred_citation.identifiers.00.type" - [111] "preferred_citation.identifiers.00.value" - [112] "preferred_citation.identifiers.01.type" - [113] "preferred_citation.identifiers.01.value" - [114] "preferred_citation.identifiers.02.type" - [115] "preferred_citation.identifiers.02.value" - [116] "preferred_citation.identifiers.03.type" - [117] "preferred_citation.identifiers.03.value" - [118] "preferred_citation.isbn" - [119] "preferred_citation.issn" - [120] "preferred_citation.issue" - [121] "preferred_citation.issue_date" - [122] "preferred_citation.issue_title" - [123] "preferred_citation.journal" - [124] "preferred_citation.keywords.00" - [125] "preferred_citation.keywords.01" - [126] "preferred_citation.languages.00" - [127] "preferred_citation.languages.01" - [128] "preferred_citation.license" - [129] "preferred_citation.license_url" - [130] "preferred_citation.loc_start" - [131] "preferred_citation.loc_end" - [132] "preferred_citation.medium" - [133] "preferred_citation.month" - [134] "preferred_citation.nihmsid" - [135] "preferred_citation.notes" - [136] "preferred_citation.number" - [137] "preferred_citation.number_volumes" - [138] "preferred_citation.pages" - [139] "preferred_citation.patent-states.00" - [140] "preferred_citation.patent-states.01" - [141] "preferred_citation.patent-states.02" - [142] "preferred_citation.patent-states.03" - [143] "preferred_citation.patent-states.04" - [144] "preferred_citation.pmcid" - [145] "preferred_citation.repository" - [146] "preferred_citation.repository_code" - [147] "preferred_citation.repository_artifact" - [148] "preferred_citation.scope" - [149] "preferred_citation.section" - [150] "preferred_citation.status" - [151] "preferred_citation.start" - [152] "preferred_citation.thesis_type" - [153] "preferred_citation.url" - [154] "preferred_citation.version" - [155] "preferred_citation.volume" - [156] "preferred_citation.volume_title" - [157] "preferred_citation.year" - [158] "preferred_citation.year_original" - [159] "preferred_citation.conference.name" - [160] "preferred_citation.conference.address" - [161] "preferred_citation.conference.city" - [162] "preferred_citation.conference.region" - [163] "preferred_citation.conference.post-code" - [164] "preferred_citation.conference.country" - [165] "preferred_citation.conference.orcid" - [166] "preferred_citation.conference.email" - [167] "preferred_citation.conference.tel" - [168] "preferred_citation.conference.fax" - [169] "preferred_citation.conference.website" - [170] "preferred_citation.conference.date-start" - [171] "preferred_citation.conference.date-end" - [172] "preferred_citation.conference.location" - [173] "preferred_citation.authors.00.family_names" - [174] "preferred_citation.authors.00.given_names" - [175] "preferred_citation.authors.00.name_particle" - [176] "preferred_citation.authors.00.name_suffix" - [177] "preferred_citation.authors.00.alias" - [178] "preferred_citation.authors.00.affiliation" - [179] "preferred_citation.authors.00.address" - [180] "preferred_citation.authors.00.city" - [181] "preferred_citation.authors.00.region" - [182] "preferred_citation.authors.00.post_code" - [183] "preferred_citation.authors.00.country" - [184] "preferred_citation.authors.00.orcid" - [185] "preferred_citation.authors.00.email" - [186] "preferred_citation.authors.00.tel" - [187] "preferred_citation.authors.00.fax" - [188] "preferred_citation.authors.00.website" - [189] "preferred_citation.authors.01.name" - [190] "preferred_citation.authors.01.address" - [191] "preferred_citation.authors.01.city" - [192] "preferred_citation.authors.01.region" - [193] "preferred_citation.authors.01.post_code" - [194] "preferred_citation.authors.01.country" - [195] "preferred_citation.authors.01.orcid" - [196] "preferred_citation.authors.01.email" - [197] "preferred_citation.authors.01.tel" - [198] "preferred_citation.authors.01.fax" - [199] "preferred_citation.authors.01.website" - [200] "preferred_citation.authors.01.date_start" - [201] "preferred_citation.authors.01.date_end" - [202] "preferred_citation.authors.01.location" - [203] "preferred_citation.contact.00.family_names" - [204] "preferred_citation.contact.00.given_names" - [205] "preferred_citation.contact.00.name_particle" - [206] "preferred_citation.contact.00.name_suffix" - [207] "preferred_citation.contact.00.alias" - [208] "preferred_citation.contact.00.affiliation" - [209] "preferred_citation.contact.00.address" - [210] "preferred_citation.contact.00.city" - [211] "preferred_citation.contact.00.region" - [212] "preferred_citation.contact.00.post_code" - [213] "preferred_citation.contact.00.country" - [214] "preferred_citation.contact.00.orcid" - [215] "preferred_citation.contact.00.email" - [216] "preferred_citation.contact.00.tel" - [217] "preferred_citation.contact.00.fax" - [218] "preferred_citation.contact.00.website" - [219] "preferred_citation.contact.01.name" - [220] "preferred_citation.contact.01.address" - [221] "preferred_citation.contact.01.city" - [222] "preferred_citation.contact.01.region" - [223] "preferred_citation.contact.01.post_code" - [224] "preferred_citation.contact.01.country" - [225] "preferred_citation.contact.01.orcid" - [226] "preferred_citation.contact.01.email" - [227] "preferred_citation.contact.01.tel" - [228] "preferred_citation.contact.01.fax" - [229] "preferred_citation.contact.01.website" - [230] "preferred_citation.contact.01.date_start" - [231] "preferred_citation.contact.01.date_end" - [232] "preferred_citation.contact.01.location" - [233] "preferred_citation.database-provider.name" - [234] "preferred_citation.database-provider.address" - [235] "preferred_citation.database-provider.city" - [236] "preferred_citation.database-provider.region" - [237] "preferred_citation.database-provider.post-code" - [238] "preferred_citation.database-provider.country" - [239] "preferred_citation.database-provider.orcid" - [240] "preferred_citation.database-provider.email" - [241] "preferred_citation.database-provider.tel" - [242] "preferred_citation.database-provider.fax" - [243] "preferred_citation.database-provider.website" - [244] "preferred_citation.database-provider.date-start" - [245] "preferred_citation.database-provider.date-end" - [246] "preferred_citation.database-provider.location" - [247] "preferred_citation.editors.00.family_names" - [248] "preferred_citation.editors.00.given_names" - [249] "preferred_citation.editors.00.name_particle" - [250] "preferred_citation.editors.00.name_suffix" - [251] "preferred_citation.editors.00.alias" - [252] "preferred_citation.editors.00.affiliation" - [253] "preferred_citation.editors.00.address" - [254] "preferred_citation.editors.00.city" - [255] "preferred_citation.editors.00.region" - [256] "preferred_citation.editors.00.post_code" - [257] "preferred_citation.editors.00.country" - [258] "preferred_citation.editors.00.orcid" - [259] "preferred_citation.editors.00.email" - [260] "preferred_citation.editors.00.tel" - [261] "preferred_citation.editors.00.fax" - [262] "preferred_citation.editors.00.website" - [263] "preferred_citation.editors.01.name" - [264] "preferred_citation.editors.01.address" - [265] "preferred_citation.editors.01.city" - [266] "preferred_citation.editors.01.region" - [267] "preferred_citation.editors.01.post_code" - [268] "preferred_citation.editors.01.country" - [269] "preferred_citation.editors.01.orcid" - [270] "preferred_citation.editors.01.email" - [271] "preferred_citation.editors.01.tel" - [272] "preferred_citation.editors.01.fax" - [273] "preferred_citation.editors.01.website" - [274] "preferred_citation.editors.01.date_start" - [275] "preferred_citation.editors.01.date_end" - [276] "preferred_citation.editors.01.location" - [277] "preferred_citation.editors-series.00.family_names" - [278] "preferred_citation.editors-series.00.given_names" - [279] "preferred_citation.editors-series.00.name_particle" - [280] "preferred_citation.editors-series.00.name_suffix" - [281] "preferred_citation.editors-series.00.alias" - [282] "preferred_citation.editors-series.00.affiliation" - [283] "preferred_citation.editors-series.00.address" - [284] "preferred_citation.editors-series.00.city" - [285] "preferred_citation.editors-series.00.region" - [286] "preferred_citation.editors-series.00.post_code" - [287] "preferred_citation.editors-series.00.country" - [288] "preferred_citation.editors-series.00.orcid" - [289] "preferred_citation.editors-series.00.email" - [290] "preferred_citation.editors-series.00.tel" - [291] "preferred_citation.editors-series.00.fax" - [292] "preferred_citation.editors-series.00.website" - [293] "preferred_citation.editors-series.01.name" - [294] "preferred_citation.editors-series.01.address" - [295] "preferred_citation.editors-series.01.city" - [296] "preferred_citation.editors-series.01.region" - [297] "preferred_citation.editors-series.01.post_code" - [298] "preferred_citation.editors-series.01.country" - [299] "preferred_citation.editors-series.01.orcid" - [300] "preferred_citation.editors-series.01.email" - [301] "preferred_citation.editors-series.01.tel" - [302] "preferred_citation.editors-series.01.fax" - [303] "preferred_citation.editors-series.01.website" - [304] "preferred_citation.editors-series.01.date_start" - [305] "preferred_citation.editors-series.01.date_end" - [306] "preferred_citation.editors-series.01.location" - [307] "preferred_citation.institution.name" - [308] "preferred_citation.institution.address" - [309] "preferred_citation.institution.city" - [310] "preferred_citation.institution.region" - [311] "preferred_citation.institution.post-code" - [312] "preferred_citation.institution.country" - [313] "preferred_citation.institution.orcid" - [314] "preferred_citation.institution.email" - [315] "preferred_citation.institution.tel" - [316] "preferred_citation.institution.fax" - [317] "preferred_citation.institution.website" - [318] "preferred_citation.institution.date-start" - [319] "preferred_citation.institution.date-end" - [320] "preferred_citation.institution.location" - [321] "preferred_citation.location.name" - [322] "preferred_citation.location.address" - [323] "preferred_citation.location.city" - [324] "preferred_citation.location.region" - [325] "preferred_citation.location.post-code" - [326] "preferred_citation.location.country" - [327] "preferred_citation.location.orcid" - [328] "preferred_citation.location.email" - [329] "preferred_citation.location.tel" - [330] "preferred_citation.location.fax" - [331] "preferred_citation.location.website" - [332] "preferred_citation.location.date-start" - [333] "preferred_citation.location.date-end" - [334] "preferred_citation.location.location" - [335] "preferred_citation.publisher.name" - [336] "preferred_citation.publisher.address" - [337] "preferred_citation.publisher.city" - [338] "preferred_citation.publisher.region" - [339] "preferred_citation.publisher.post-code" - [340] "preferred_citation.publisher.country" - [341] "preferred_citation.publisher.orcid" - [342] "preferred_citation.publisher.email" - [343] "preferred_citation.publisher.tel" - [344] "preferred_citation.publisher.fax" - [345] "preferred_citation.publisher.website" - [346] "preferred_citation.publisher.date-start" - [347] "preferred_citation.publisher.date-end" - [348] "preferred_citation.publisher.location" - [349] "preferred_citation.recipients.00.family_names" - [350] "preferred_citation.recipients.00.given_names" - [351] "preferred_citation.recipients.00.name_particle" - [352] "preferred_citation.recipients.00.name_suffix" - [353] "preferred_citation.recipients.00.alias" - [354] "preferred_citation.recipients.00.affiliation" - [355] "preferred_citation.recipients.00.address" - [356] "preferred_citation.recipients.00.city" - [357] "preferred_citation.recipients.00.region" - [358] "preferred_citation.recipients.00.post_code" - [359] "preferred_citation.recipients.00.country" - [360] "preferred_citation.recipients.00.orcid" - [361] "preferred_citation.recipients.00.email" - [362] "preferred_citation.recipients.00.tel" - [363] "preferred_citation.recipients.00.fax" - [364] "preferred_citation.recipients.00.website" - [365] "preferred_citation.recipients.01.name" - [366] "preferred_citation.recipients.01.address" - [367] "preferred_citation.recipients.01.city" - [368] "preferred_citation.recipients.01.region" - [369] "preferred_citation.recipients.01.post_code" - [370] "preferred_citation.recipients.01.country" - [371] "preferred_citation.recipients.01.orcid" - [372] "preferred_citation.recipients.01.email" - [373] "preferred_citation.recipients.01.tel" - [374] "preferred_citation.recipients.01.fax" - [375] "preferred_citation.recipients.01.website" - [376] "preferred_citation.recipients.01.date_start" - [377] "preferred_citation.recipients.01.date_end" - [378] "preferred_citation.recipients.01.location" - [379] "preferred_citation.senders.00.family_names" - [380] "preferred_citation.senders.00.given_names" - [381] "preferred_citation.senders.00.name_particle" - [382] "preferred_citation.senders.00.name_suffix" - [383] "preferred_citation.senders.00.alias" - [384] "preferred_citation.senders.00.affiliation" - [385] "preferred_citation.senders.00.address" - [386] "preferred_citation.senders.00.city" - [387] "preferred_citation.senders.00.region" - [388] "preferred_citation.senders.00.post_code" - [389] "preferred_citation.senders.00.country" - [390] "preferred_citation.senders.00.orcid" - [391] "preferred_citation.senders.00.email" - [392] "preferred_citation.senders.00.tel" - [393] "preferred_citation.senders.00.fax" - [394] "preferred_citation.senders.00.website" - [395] "preferred_citation.senders.01.name" - [396] "preferred_citation.senders.01.address" - [397] "preferred_citation.senders.01.city" - [398] "preferred_citation.senders.01.region" - [399] "preferred_citation.senders.01.post_code" - [400] "preferred_citation.senders.01.country" - [401] "preferred_citation.senders.01.orcid" - [402] "preferred_citation.senders.01.email" - [403] "preferred_citation.senders.01.tel" - [404] "preferred_citation.senders.01.fax" - [405] "preferred_citation.senders.01.website" - [406] "preferred_citation.senders.01.date_start" - [407] "preferred_citation.senders.01.date_end" - [408] "preferred_citation.senders.01.location" - [409] "preferred_citation.translators.00.family_names" - [410] "preferred_citation.translators.00.given_names" - [411] "preferred_citation.translators.00.name_particle" - [412] "preferred_citation.translators.00.name_suffix" - [413] "preferred_citation.translators.00.alias" - [414] "preferred_citation.translators.00.affiliation" - [415] "preferred_citation.translators.00.address" - [416] "preferred_citation.translators.00.city" - [417] "preferred_citation.translators.00.region" - [418] "preferred_citation.translators.00.post_code" - [419] "preferred_citation.translators.00.country" - [420] "preferred_citation.translators.00.orcid" - [421] "preferred_citation.translators.00.email" - [422] "preferred_citation.translators.00.tel" - [423] "preferred_citation.translators.00.fax" - [424] "preferred_citation.translators.00.website" - [425] "preferred_citation.translators.01.name" - [426] "preferred_citation.translators.01.address" - [427] "preferred_citation.translators.01.city" - [428] "preferred_citation.translators.01.region" - [429] "preferred_citation.translators.01.post_code" - [430] "preferred_citation.translators.01.country" - [431] "preferred_citation.translators.01.orcid" - [432] "preferred_citation.translators.01.email" - [433] "preferred_citation.translators.01.tel" - [434] "preferred_citation.translators.01.fax" - [435] "preferred_citation.translators.01.website" - [436] "preferred_citation.translators.01.date_start" - [437] "preferred_citation.translators.01.date_end" - [438] "preferred_citation.translators.01.location" - [439] "references.00.type" - [440] "references.00.title" - [441] "references.00.abbreviation" - [442] "references.00.abstract" - [443] "references.00.collection_doi" - [444] "references.00.collection_title" - [445] "references.00.collection_type" - [446] "references.00.commit" - [447] "references.00.copyright" - [448] "references.00.data_type" - [449] "references.00.database" - [450] "references.00.date_accessed" - [451] "references.00.date_downloaded" - [452] "references.00.date_released" - [453] "references.00.date_published" - [454] "references.00.department" - [455] "references.00.doi" - [456] "references.00.edition" - [457] "references.00.end" - [458] "references.00.entry" - [459] "references.00.filename" - [460] "references.00.format" - [461] "references.00.identifiers.00.type" - [462] "references.00.identifiers.00.value" - [463] "references.00.identifiers.01.type" - [464] "references.00.identifiers.01.value" - [465] "references.00.identifiers.02.type" - [466] "references.00.identifiers.02.value" - [467] "references.00.identifiers.03.type" - [468] "references.00.identifiers.03.value" - [469] "references.00.isbn" - [470] "references.00.issn" - [471] "references.00.issue" - [472] "references.00.issue_date" - [473] "references.00.issue_title" - [474] "references.00.journal" - [475] "references.00.keywords.00" - [476] "references.00.keywords.01" - [477] "references.00.languages.00" - [478] "references.00.languages.01" - [479] "references.00.license" - [480] "references.00.license_url" - [481] "references.00.loc_start" - [482] "references.00.loc_end" - [483] "references.00.medium" - [484] "references.00.month" - [485] "references.00.nihmsid" - [486] "references.00.notes" - [487] "references.00.number" - [488] "references.00.number_volumes" - [489] "references.00.pages" - [490] "references.00.patent-states.00" - [491] "references.00.patent-states.01" - [492] "references.00.patent-states.02" - [493] "references.00.patent-states.03" - [494] "references.00.patent-states.04" - [495] "references.00.pmcid" - [496] "references.00.repository" - [497] "references.00.repository_code" - [498] "references.00.repository_artifact" - [499] "references.00.scope" - [500] "references.00.section" - [501] "references.00.status" - [502] "references.00.start" - [503] "references.00.thesis_type" - [504] "references.00.url" - [505] "references.00.version" - [506] "references.00.volume" - [507] "references.00.volume_title" - [508] "references.00.year" - [509] "references.00.year_original" - [510] "references.00.conference.name" - [511] "references.00.conference.address" - [512] "references.00.conference.city" - [513] "references.00.conference.region" - [514] "references.00.conference.post-code" - [515] "references.00.conference.country" - [516] "references.00.conference.orcid" - [517] "references.00.conference.email" - [518] "references.00.conference.tel" - [519] "references.00.conference.fax" - [520] "references.00.conference.website" - [521] "references.00.conference.date-start" - [522] "references.00.conference.date-end" - [523] "references.00.conference.location" - [524] "references.00.authors.00.family_names" - [525] "references.00.authors.00.given_names" - [526] "references.00.authors.00.name_particle" - [527] "references.00.authors.00.name_suffix" - [528] "references.00.authors.00.alias" - [529] "references.00.authors.00.affiliation" - [530] "references.00.authors.00.address" - [531] "references.00.authors.00.city" - [532] "references.00.authors.00.region" - [533] "references.00.authors.00.post_code" - [534] "references.00.authors.00.country" - [535] "references.00.authors.00.orcid" - [536] "references.00.authors.00.email" - [537] "references.00.authors.00.tel" - [538] "references.00.authors.00.fax" - [539] "references.00.authors.00.website" - [540] "references.00.authors.01.name" - [541] "references.00.authors.01.address" - [542] "references.00.authors.01.city" - [543] "references.00.authors.01.region" - [544] "references.00.authors.01.post_code" - [545] "references.00.authors.01.country" - [546] "references.00.authors.01.orcid" - [547] "references.00.authors.01.email" - [548] "references.00.authors.01.tel" - [549] "references.00.authors.01.fax" - [550] "references.00.authors.01.website" - [551] "references.00.authors.01.date_start" - [552] "references.00.authors.01.date_end" - [553] "references.00.authors.01.location" - [554] "references.00.contact.00.family_names" - [555] "references.00.contact.00.given_names" - [556] "references.00.contact.00.name_particle" - [557] "references.00.contact.00.name_suffix" - [558] "references.00.contact.00.alias" - [559] "references.00.contact.00.affiliation" - [560] "references.00.contact.00.address" - [561] "references.00.contact.00.city" - [562] "references.00.contact.00.region" - [563] "references.00.contact.00.post_code" - [564] "references.00.contact.00.country" - [565] "references.00.contact.00.orcid" - [566] "references.00.contact.00.email" - [567] "references.00.contact.00.tel" - [568] "references.00.contact.00.fax" - [569] "references.00.contact.00.website" - [570] "references.00.contact.01.name" - [571] "references.00.contact.01.address" - [572] "references.00.contact.01.city" - [573] "references.00.contact.01.region" - [574] "references.00.contact.01.post_code" - [575] "references.00.contact.01.country" - [576] "references.00.contact.01.orcid" - [577] "references.00.contact.01.email" - [578] "references.00.contact.01.tel" - [579] "references.00.contact.01.fax" - [580] "references.00.contact.01.website" - [581] "references.00.contact.01.date_start" - [582] "references.00.contact.01.date_end" - [583] "references.00.contact.01.location" - [584] "references.00.database-provider.name" - [585] "references.00.database-provider.address" - [586] "references.00.database-provider.city" - [587] "references.00.database-provider.region" - [588] "references.00.database-provider.post-code" - [589] "references.00.database-provider.country" - [590] "references.00.database-provider.orcid" - [591] "references.00.database-provider.email" - [592] "references.00.database-provider.tel" - [593] "references.00.database-provider.fax" - [594] "references.00.database-provider.website" - [595] "references.00.database-provider.date-start" - [596] "references.00.database-provider.date-end" - [597] "references.00.database-provider.location" - [598] "references.00.editors.00.family_names" - [599] "references.00.editors.00.given_names" - [600] "references.00.editors.00.name_particle" - [601] "references.00.editors.00.name_suffix" - [602] "references.00.editors.00.alias" - [603] "references.00.editors.00.affiliation" - [604] "references.00.editors.00.address" - [605] "references.00.editors.00.city" - [606] "references.00.editors.00.region" - [607] "references.00.editors.00.post_code" - [608] "references.00.editors.00.country" - [609] "references.00.editors.00.orcid" - [610] "references.00.editors.00.email" - [611] "references.00.editors.00.tel" - [612] "references.00.editors.00.fax" - [613] "references.00.editors.00.website" - [614] "references.00.editors.01.name" - [615] "references.00.editors.01.address" - [616] "references.00.editors.01.city" - [617] "references.00.editors.01.region" - [618] "references.00.editors.01.post_code" - [619] "references.00.editors.01.country" - [620] "references.00.editors.01.orcid" - [621] "references.00.editors.01.email" - [622] "references.00.editors.01.tel" - [623] "references.00.editors.01.fax" - [624] "references.00.editors.01.website" - [625] "references.00.editors.01.date_start" - [626] "references.00.editors.01.date_end" - [627] "references.00.editors.01.location" - [628] "references.00.editors-series.00.family_names" - [629] "references.00.editors-series.00.given_names" - [630] "references.00.editors-series.00.name_particle" - [631] "references.00.editors-series.00.name_suffix" - [632] "references.00.editors-series.00.alias" - [633] "references.00.editors-series.00.affiliation" - [634] "references.00.editors-series.00.address" - [635] "references.00.editors-series.00.city" - [636] "references.00.editors-series.00.region" - [637] "references.00.editors-series.00.post_code" - [638] "references.00.editors-series.00.country" - [639] "references.00.editors-series.00.orcid" - [640] "references.00.editors-series.00.email" - [641] "references.00.editors-series.00.tel" - [642] "references.00.editors-series.00.fax" - [643] "references.00.editors-series.00.website" - [644] "references.00.editors-series.01.name" - [645] "references.00.editors-series.01.address" - [646] "references.00.editors-series.01.city" - [647] "references.00.editors-series.01.region" - [648] "references.00.editors-series.01.post_code" - [649] "references.00.editors-series.01.country" - [650] "references.00.editors-series.01.orcid" - [651] "references.00.editors-series.01.email" - [652] "references.00.editors-series.01.tel" - [653] "references.00.editors-series.01.fax" - [654] "references.00.editors-series.01.website" - [655] "references.00.editors-series.01.date_start" - [656] "references.00.editors-series.01.date_end" - [657] "references.00.editors-series.01.location" - [658] "references.00.institution.name" - [659] "references.00.institution.address" - [660] "references.00.institution.city" - [661] "references.00.institution.region" - [662] "references.00.institution.post-code" - [663] "references.00.institution.country" - [664] "references.00.institution.orcid" - [665] "references.00.institution.email" - [666] "references.00.institution.tel" - [667] "references.00.institution.fax" - [668] "references.00.institution.website" - [669] "references.00.institution.date-start" - [670] "references.00.institution.date-end" - [671] "references.00.institution.location" - [672] "references.00.location.name" - [673] "references.00.location.address" - [674] "references.00.location.city" - [675] "references.00.location.region" - [676] "references.00.location.post-code" - [677] "references.00.location.country" - [678] "references.00.location.orcid" - [679] "references.00.location.email" - [680] "references.00.location.tel" - [681] "references.00.location.fax" - [682] "references.00.location.website" - [683] "references.00.location.date-start" - [684] "references.00.location.date-end" - [685] "references.00.location.location" - [686] "references.00.publisher.name" - [687] "references.00.publisher.address" - [688] "references.00.publisher.city" - [689] "references.00.publisher.region" - [690] "references.00.publisher.post-code" - [691] "references.00.publisher.country" - [692] "references.00.publisher.orcid" - [693] "references.00.publisher.email" - [694] "references.00.publisher.tel" - [695] "references.00.publisher.fax" - [696] "references.00.publisher.website" - [697] "references.00.publisher.date-start" - [698] "references.00.publisher.date-end" - [699] "references.00.publisher.location" - [700] "references.00.recipients.00.family_names" - [701] "references.00.recipients.00.given_names" - [702] "references.00.recipients.00.name_particle" - [703] "references.00.recipients.00.name_suffix" - [704] "references.00.recipients.00.alias" - [705] "references.00.recipients.00.affiliation" - [706] "references.00.recipients.00.address" - [707] "references.00.recipients.00.city" - [708] "references.00.recipients.00.region" - [709] "references.00.recipients.00.post_code" - [710] "references.00.recipients.00.country" - [711] "references.00.recipients.00.orcid" - [712] "references.00.recipients.00.email" - [713] "references.00.recipients.00.tel" - [714] "references.00.recipients.00.fax" - [715] "references.00.recipients.00.website" - [716] "references.00.recipients.01.name" - [717] "references.00.recipients.01.address" - [718] "references.00.recipients.01.city" - [719] "references.00.recipients.01.region" - [720] "references.00.recipients.01.post_code" - [721] "references.00.recipients.01.country" - [722] "references.00.recipients.01.orcid" - [723] "references.00.recipients.01.email" - [724] "references.00.recipients.01.tel" - [725] "references.00.recipients.01.fax" - [726] "references.00.recipients.01.website" - [727] "references.00.recipients.01.date_start" - [728] "references.00.recipients.01.date_end" - [729] "references.00.recipients.01.location" - [730] "references.00.senders.00.family_names" - [731] "references.00.senders.00.given_names" - [732] "references.00.senders.00.name_particle" - [733] "references.00.senders.00.name_suffix" - [734] "references.00.senders.00.alias" - [735] "references.00.senders.00.affiliation" - [736] "references.00.senders.00.address" - [737] "references.00.senders.00.city" - [738] "references.00.senders.00.region" - [739] "references.00.senders.00.post_code" - [740] "references.00.senders.00.country" - [741] "references.00.senders.00.orcid" - [742] "references.00.senders.00.email" - [743] "references.00.senders.00.tel" - [744] "references.00.senders.00.fax" - [745] "references.00.senders.00.website" - [746] "references.00.senders.01.name" - [747] "references.00.senders.01.address" - [748] "references.00.senders.01.city" - [749] "references.00.senders.01.region" - [750] "references.00.senders.01.post_code" - [751] "references.00.senders.01.country" - [752] "references.00.senders.01.orcid" - [753] "references.00.senders.01.email" - [754] "references.00.senders.01.tel" - [755] "references.00.senders.01.fax" - [756] "references.00.senders.01.website" - [757] "references.00.senders.01.date_start" - [758] "references.00.senders.01.date_end" - [759] "references.00.senders.01.location" - [760] "references.00.translators.00.family_names" - [761] "references.00.translators.00.given_names" - [762] "references.00.translators.00.name_particle" - [763] "references.00.translators.00.name_suffix" - [764] "references.00.translators.00.alias" - [765] "references.00.translators.00.affiliation" - [766] "references.00.translators.00.address" - [767] "references.00.translators.00.city" - [768] "references.00.translators.00.region" - [769] "references.00.translators.00.post_code" - [770] "references.00.translators.00.country" - [771] "references.00.translators.00.orcid" - [772] "references.00.translators.00.email" - [773] "references.00.translators.00.tel" - [774] "references.00.translators.00.fax" - [775] "references.00.translators.00.website" - [776] "references.00.translators.01.name" - [777] "references.00.translators.01.address" - [778] "references.00.translators.01.city" - [779] "references.00.translators.01.region" - [780] "references.00.translators.01.post_code" - [781] "references.00.translators.01.country" - [782] "references.00.translators.01.orcid" - [783] "references.00.translators.01.email" - [784] "references.00.translators.01.tel" - [785] "references.00.translators.01.fax" - [786] "references.00.translators.01.website" - [787] "references.00.translators.01.date_start" - [788] "references.00.translators.01.date_end" - [789] "references.00.translators.01.location" - -# Convert a citation only - - Code - names(the_df) - Output - [1] "type" "title" - [3] "authors.00.family_names" "authors.00.given_names" - [5] "authors.01.family_names" "authors.01.given_names" - [7] "authors.02.family_names" "authors.02.given_names" - [9] "authors.03.name" "url" - [11] "keywords.00" "keywords.01" - [13] "keywords.02" "keywords.03" - [15] "keywords.04" "keywords.05" - [17] "keywords.06" "keywords.07" - [19] "abstract" "version" - -# Convert authors only - - Code - names(the_df) - Output - [1] "family_names" "given_names" - -# Convert list of authors - - Code - names(the_df) - Output - [1] "person.00.family_names" "person.00.given_names" - [3] "person.00.name_particle" "person.00.name_suffix" - [5] "person.00.alias" "person.00.affiliation" - [7] "person.00.address" "person.00.city" - [9] "person.00.region" "person.00.post_code" - [11] "person.00.country" "person.00.orcid" - [13] "person.00.email" "person.00.tel" - [15] "person.00.fax" "person.00.website" - [17] "person.01.name" "person.01.address" - [19] "person.01.city" "person.01.region" - [21] "person.01.post_code" "person.01.country" - [23] "person.01.orcid" "person.01.email" - [25] "person.01.tel" "person.01.fax" - [27] "person.01.website" "person.01.date_start" - [29] "person.01.date_end" "person.01.location" - diff --git a/tests/testthat/_snaps/cff-methods.md b/tests/testthat/_snaps/cff-methods.md index a6535fa1..498fef21 100644 --- a/tests/testthat/_snaps/cff-methods.md +++ b/tests/testthat/_snaps/cff-methods.md @@ -905,3 +905,33 @@ [1] "One Truly van der Real Person IV (Citey, Excellent University, Niceplace, Arcadia, 22 Acacia Avenue, Citationburgh, Renfrewshire, C13 7X7, GB, , +44(0)141-323 4567, +44(0)141-323 45678, https://www.entity-project-team.io)" [2] "Entity Project Team Conference entity (Entity Project Team Conference entity, 22 Acacia Avenue, Citationburgh, Renfrewshire, C13 7X7, GB, , +44(0)141-323 4567, +44(0)141-323 45678, https://www.entity-project-team.io, 2017-01-01, 2017-01-31, The team garage)" +# head and tail + + Code + a_cff + Output + authors: + - family-names: Doe + given-names: John + cff-version: 1.2.0 + message: If you use this software, please cite it using these metadata. + title: My Research Software + +--- + + Code + head(a_cff, 2) + Output + authors: + - family-names: Doe + given-names: John + cff-version: 1.2.0 + +--- + + Code + tail(a_cff, 2) + Output + message: If you use this software, please cite it using these metadata. + title: My Research Software + diff --git a/tests/testthat/_snaps/cff.md b/tests/testthat/_snaps/cff.md new file mode 100644 index 00000000..435bfce9 --- /dev/null +++ b/tests/testthat/_snaps/cff.md @@ -0,0 +1,1709 @@ +# Walk trough full lifecycle + + Code + print_snapshot("Read object", read) + Output + + + ## Read object + + cff-version: 1.2.0 + message: If you use this software, please cite it as below. + abstract: This is an awesome piece of research software! + authors: + - family-names: Real Person + given-names: One Truly + name-particle: van der + name-suffix: IV + alias: Citey + affiliation: Excellent University, Niceplace, Arcadia + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + - name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + commit: 156a04c74a8a79d40c5d705cddf9d36735feab4d + contact: + - family-names: Real Person + given-names: One Truly + name-particle: van der + name-suffix: IV + alias: Citey + affiliation: Excellent University, Niceplace, Arcadia + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + - name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + date-released: '2017-12-11' + doi: 10.5281/zenodo.1003150 + identifiers: + - type: doi + value: 10.5281/zenodo.1003150 + - type: swh + value: swh:1:rel:99f6850374dc6597af01bd0ee1d3fc0699301b9f + - type: url + value: https://example.com + - type: other + value: other-schema://abcd.1234.efgh.5678 + keywords: + - One + - Two + - Three + - '4' + license: CC-BY-SA-4.0 + license-url: https://spdx.org/licenses/CC-BY-SA-4.0.html#licenseText + repository: https://www.example.com/foo/?bar=baz&inga=42&quux + repository-code: http://foo.com/blah_(wikipedia)_blah#cite-1 + repository-artifact: https://files.pythonhosted.org/packages/0a/84/10507b69a07768bc16981184b4d147a0fc84b71fbf35c03bafc8dcced8e1/cffconvert-1.3.3.tar.gz + title: Citation File Format 1.0.0 + type: software + url: http://userid:password@example.com:8080/ + version: 1.0.0 + preferred-citation: + type: book + title: Book Title + abbreviation: Abbr + abstract: Description of the book. + collection-doi: 10.5281/zenodo.1003150 + collection-title: Collection Title + collection-type: Collection Type + commit: 156a04c74a8a79d40c5d705cddf9d36735feab4d + copyright: 2017 Stephan Druskat + data-type: Data Type + database: Database + date-accessed: '2017-10-31' + date-downloaded: '2017-10-31' + date-released: '2017-10-31' + date-published: '2017-10-31' + department: Department + doi: 10.5281/zenodo.1003150 + edition: 2nd edition + end: '456' + entry: Chapter 9 + filename: book.zip + format: Printed book + identifiers: + - type: doi + value: 10.5281/zenodo.1003150 + - type: swh + value: swh:1:rel:99f6850374dc6597af01bd0ee1d3fc0699301b9f + - type: url + value: https://example.com + - type: other + value: other-schema://abcd.1234.efgh.5678 + isbn: 978-1-89183-044-0 + issn: 1234-543X + issue: '123' + issue-date: December + issue-title: Special Issue on Software Citation + journal: PeerJ + keywords: + - Software + - Citation + languages: + - aaa + - zu + license: Apache-2.0 + license-url: https://spdx.org/licenses/Apache-2.0.html#licenseText + loc-start: '14' + loc-end: '54' + medium: hardcover book + month: '3' + nihmsid: Don't know what format a NIHMSID is in + notes: A field for general notes about the reference, usable in other formats such + as BibTeX. + number: A general-purpose field for accession numbers, cf. the specifications for + examples. + number-volumes: '7' + pages: '765' + patent-states: + - Germany + - ROI + - 'but also for example US states, such as:' + - IL + - RI + pmcid: PMC1234567 + repository: http://code.google.com/events/#&product=browser + repository-code: http://142.42.1.1:8080/ + repository-artifact: https://files.pythonhosted.org/packages/0a/84/10507b69a07768bc16981184b4d147a0fc84b71fbf35c03bafc8dcced8e1/cffconvert-1.3.3.tar.gz + scope: Cite this book if you want to reference the general concepts implemented + in Citation File Format 1.0.0. + section: Chapter 2 - "Reference keys" + status: advance-online + start: '123' + thesis-type: Doctoral dissertation + url: http://j.mp + version: 0.0.1423-BETA + volume: '2' + volume-title: Advances in Software Citation + year: '2017' + year-original: '2012' + conference: + name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + authors: + - family-names: Real Person + given-names: One Truly + name-particle: van der + name-suffix: IV + alias: Citey + affiliation: Excellent University, Niceplace, Arcadia + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + - name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + contact: + - family-names: Real Person + given-names: One Truly + name-particle: van der + name-suffix: IV + alias: Citey + affiliation: Excellent University, Niceplace, Arcadia + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + - name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + database-provider: + name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + editors: + - family-names: Real Person + given-names: One Truly + name-particle: van der + name-suffix: IV + alias: Citey + affiliation: Excellent University, Niceplace, Arcadia + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + - name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + editors-series: + - family-names: Real Person + given-names: One Truly + name-particle: van der + name-suffix: IV + alias: Citey + affiliation: Excellent University, Niceplace, Arcadia + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + - name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + institution: + name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + location: + name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + publisher: + name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + recipients: + - family-names: Real Person + given-names: One Truly + name-particle: van der + name-suffix: IV + alias: Citey + affiliation: Excellent University, Niceplace, Arcadia + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + - name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + senders: + - family-names: Real Person + given-names: One Truly + name-particle: van der + name-suffix: IV + alias: Citey + affiliation: Excellent University, Niceplace, Arcadia + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + - name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + translators: + - family-names: Real Person + given-names: One Truly + name-particle: van der + name-suffix: IV + alias: Citey + affiliation: Excellent University, Niceplace, Arcadia + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + - name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + references: + - type: book + title: Book Title + abbreviation: Abbr + abstract: Description of the book. + collection-doi: 10.5281/zenodo.1003150 + collection-title: Collection Title + collection-type: Collection Type + commit: 156a04c74a8a79d40c5d705cddf9d36735feab4d + copyright: 2017 Stephan Druskat + data-type: Data Type + database: Database + date-accessed: '2017-10-31' + date-downloaded: '2017-10-31' + date-released: '2017-10-31' + date-published: '2017-10-31' + department: Department + doi: 10.5281/zenodo.1003150 + edition: 2nd edition + end: '123' + entry: Chapter 9 + filename: book.zip + format: Printed book + identifiers: + - type: doi + value: 10.5281/zenodo.1003150 + - type: swh + value: swh:1:rel:99f6850374dc6597af01bd0ee1d3fc0699301b9f + - type: url + value: https://example.com + - type: other + value: other-schema://abcd.1234.efgh.5678 + isbn: 978-1-89183-044-0 + issn: 1234-543X + issue: '123' + issue-date: December + issue-title: Special Issue on Software Citation + journal: PeerJ + keywords: + - Software + - Citation + languages: + - aaa + - zu + license: Apache-2.0 + license-url: https://spdx.org/licenses/Apache-2.0.html#licenseText + loc-start: '14' + loc-end: '54' + medium: hardcover book + month: '3' + nihmsid: Don't know what format a NIHMSID is in + notes: A field for general notes about the reference, usable in other formats such + as BibTeX. + number: A general-purpose field for accession numbers, cf. the specifications for + examples. + number-volumes: '7' + pages: '765' + patent-states: + - Germany + - ROI + - 'but also for example US states, such as:' + - IL + - RI + pmcid: PMC1234567 + repository: http://code.google.com/events/#&product=browser + repository-code: http://142.42.1.1:8080/ + repository-artifact: https://files.pythonhosted.org/packages/0a/84/10507b69a07768bc16981184b4d147a0fc84b71fbf35c03bafc8dcced8e1/cffconvert-1.3.3.tar.gz + scope: Cite this book if you want to reference the general concepts implemented + in Citation File Format 1.0.0. + section: Chapter 2 - "Reference keys" + status: advance-online + start: '123' + thesis-type: Doctoral dissertation + url: http://j.mp + version: 0.0.1423-BETA + volume: '2' + volume-title: Advances in Software Citation + year: '2017' + year-original: '2012' + conference: + name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + authors: + - family-names: Real Person + given-names: One Truly + name-particle: van der + name-suffix: IV + alias: Citey + affiliation: Excellent University, Niceplace, Arcadia + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + - name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + contact: + - family-names: Real Person + given-names: One Truly + name-particle: van der + name-suffix: IV + alias: Citey + affiliation: Excellent University, Niceplace, Arcadia + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + - name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + database-provider: + name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + editors: + - family-names: Real Person + given-names: One Truly + name-particle: van der + name-suffix: IV + alias: Citey + affiliation: Excellent University, Niceplace, Arcadia + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + - name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + editors-series: + - family-names: Real Person + given-names: One Truly + name-particle: van der + name-suffix: IV + alias: Citey + affiliation: Excellent University, Niceplace, Arcadia + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + - name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + institution: + name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + location: + name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + publisher: + name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + recipients: + - family-names: Real Person + given-names: One Truly + name-particle: van der + name-suffix: IV + alias: Citey + affiliation: Excellent University, Niceplace, Arcadia + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + - name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + senders: + - family-names: Real Person + given-names: One Truly + name-particle: van der + name-suffix: IV + alias: Citey + affiliation: Excellent University, Niceplace, Arcadia + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + - name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + translators: + - family-names: Real Person + given-names: One Truly + name-particle: van der + name-suffix: IV + alias: Citey + affiliation: Excellent University, Niceplace, Arcadia + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + - name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + + --- + +--- + + Code + print_snapshot("Modify object", modify) + Output + + + ## Modify object + + cff-version: 1.2.0 + message: If you use this software, please cite it as below. + type: software + license: CC-BY-SA-4.0 + title: A new title + version: 1.0.0 + doi: 10.5281/zenodo.1003150 + abstract: This is an awesome piece of research software! + authors: + - family-names: Real Person + given-names: One Truly + name-particle: van der + name-suffix: IV + alias: Citey + affiliation: Excellent University, Niceplace, Arcadia + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + - name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + preferred-citation: + type: book + title: Book Title + abbreviation: Abbr + abstract: Description of the book. + collection-doi: 10.5281/zenodo.1003150 + collection-title: Collection Title + collection-type: Collection Type + commit: 156a04c74a8a79d40c5d705cddf9d36735feab4d + copyright: 2017 Stephan Druskat + data-type: Data Type + database: Database + date-accessed: '2017-10-31' + date-downloaded: '2017-10-31' + date-released: '2017-10-31' + date-published: '2017-10-31' + department: Department + doi: 10.5281/zenodo.1003150 + edition: 2nd edition + end: '456' + entry: Chapter 9 + filename: book.zip + format: Printed book + identifiers: + - type: doi + value: 10.5281/zenodo.1003150 + - type: swh + value: swh:1:rel:99f6850374dc6597af01bd0ee1d3fc0699301b9f + - type: url + value: https://example.com + - type: other + value: other-schema://abcd.1234.efgh.5678 + isbn: 978-1-89183-044-0 + issn: 1234-543X + issue: '123' + issue-date: December + issue-title: Special Issue on Software Citation + journal: PeerJ + keywords: + - Software + - Citation + languages: + - aaa + - zu + license: Apache-2.0 + license-url: https://spdx.org/licenses/Apache-2.0.html#licenseText + loc-start: '14' + loc-end: '54' + medium: hardcover book + month: '3' + nihmsid: Don't know what format a NIHMSID is in + notes: A field for general notes about the reference, usable in other formats such + as BibTeX. + number: A general-purpose field for accession numbers, cf. the specifications for + examples. + number-volumes: '7' + pages: '765' + patent-states: + - Germany + - ROI + - 'but also for example US states, such as:' + - IL + - RI + pmcid: PMC1234567 + repository: http://code.google.com/events/#&product=browser + repository-code: http://142.42.1.1:8080/ + repository-artifact: https://files.pythonhosted.org/packages/0a/84/10507b69a07768bc16981184b4d147a0fc84b71fbf35c03bafc8dcced8e1/cffconvert-1.3.3.tar.gz + scope: Cite this book if you want to reference the general concepts implemented + in Citation File Format 1.0.0. + section: Chapter 2 - "Reference keys" + status: advance-online + start: '123' + thesis-type: Doctoral dissertation + url: http://j.mp + version: 0.0.1423-BETA + volume: '2' + volume-title: Advances in Software Citation + year: '2017' + year-original: '2012' + conference: + name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + authors: + - family-names: Real Person + given-names: One Truly + name-particle: van der + name-suffix: IV + alias: Citey + affiliation: Excellent University, Niceplace, Arcadia + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + - name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + contact: + - family-names: Real Person + given-names: One Truly + name-particle: van der + name-suffix: IV + alias: Citey + affiliation: Excellent University, Niceplace, Arcadia + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + - name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + database-provider: + name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + editors: + - family-names: Real Person + given-names: One Truly + name-particle: van der + name-suffix: IV + alias: Citey + affiliation: Excellent University, Niceplace, Arcadia + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + - name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + editors-series: + - family-names: Real Person + given-names: One Truly + name-particle: van der + name-suffix: IV + alias: Citey + affiliation: Excellent University, Niceplace, Arcadia + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + - name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + institution: + name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + location: + name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + publisher: + name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + recipients: + - family-names: Real Person + given-names: One Truly + name-particle: van der + name-suffix: IV + alias: Citey + affiliation: Excellent University, Niceplace, Arcadia + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + - name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + senders: + - family-names: Real Person + given-names: One Truly + name-particle: van der + name-suffix: IV + alias: Citey + affiliation: Excellent University, Niceplace, Arcadia + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + - name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + translators: + - family-names: Real Person + given-names: One Truly + name-particle: van der + name-suffix: IV + alias: Citey + affiliation: Excellent University, Niceplace, Arcadia + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + - name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + repository: https://www.example.com/foo/?bar=baz&inga=42&quux + repository-artifact: https://files.pythonhosted.org/packages/0a/84/10507b69a07768bc16981184b4d147a0fc84b71fbf35c03bafc8dcced8e1/cffconvert-1.3.3.tar.gz + repository-code: http://foo.com/blah_(wikipedia)_blah#cite-1 + url: http://userid:password@example.com:8080/ + date-released: '2017-12-11' + contact: + - family-names: Real Person + given-names: One Truly + name-particle: van der + name-suffix: IV + alias: Citey + affiliation: Excellent University, Niceplace, Arcadia + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + - name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + keywords: + - One + - Two + - Three + - '4' + references: + - type: book + title: Book Title + abbreviation: Abbr + abstract: Description of the book. + collection-doi: 10.5281/zenodo.1003150 + collection-title: Collection Title + collection-type: Collection Type + commit: 156a04c74a8a79d40c5d705cddf9d36735feab4d + copyright: 2017 Stephan Druskat + data-type: Data Type + database: Database + date-accessed: '2017-10-31' + date-downloaded: '2017-10-31' + date-released: '2017-10-31' + date-published: '2017-10-31' + department: Department + doi: 10.5281/zenodo.1003150 + edition: 2nd edition + end: '123' + entry: Chapter 9 + filename: book.zip + format: Printed book + identifiers: + - type: doi + value: 10.5281/zenodo.1003150 + - type: swh + value: swh:1:rel:99f6850374dc6597af01bd0ee1d3fc0699301b9f + - type: url + value: https://example.com + - type: other + value: other-schema://abcd.1234.efgh.5678 + isbn: 978-1-89183-044-0 + issn: 1234-543X + issue: '123' + issue-date: December + issue-title: Special Issue on Software Citation + journal: PeerJ + keywords: + - Software + - Citation + languages: + - aaa + - zu + license: Apache-2.0 + license-url: https://spdx.org/licenses/Apache-2.0.html#licenseText + loc-start: '14' + loc-end: '54' + medium: hardcover book + month: '3' + nihmsid: Don't know what format a NIHMSID is in + notes: A field for general notes about the reference, usable in other formats such + as BibTeX. + number: A general-purpose field for accession numbers, cf. the specifications for + examples. + number-volumes: '7' + pages: '765' + patent-states: + - Germany + - ROI + - 'but also for example US states, such as:' + - IL + - RI + pmcid: PMC1234567 + repository: http://code.google.com/events/#&product=browser + repository-code: http://142.42.1.1:8080/ + repository-artifact: https://files.pythonhosted.org/packages/0a/84/10507b69a07768bc16981184b4d147a0fc84b71fbf35c03bafc8dcced8e1/cffconvert-1.3.3.tar.gz + scope: Cite this book if you want to reference the general concepts implemented + in Citation File Format 1.0.0. + section: Chapter 2 - "Reference keys" + status: advance-online + start: '123' + thesis-type: Doctoral dissertation + url: http://j.mp + version: 0.0.1423-BETA + volume: '2' + volume-title: Advances in Software Citation + year: '2017' + year-original: '2012' + conference: + name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + authors: + - family-names: Real Person + given-names: One Truly + name-particle: van der + name-suffix: IV + alias: Citey + affiliation: Excellent University, Niceplace, Arcadia + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + - name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + contact: + - family-names: Real Person + given-names: One Truly + name-particle: van der + name-suffix: IV + alias: Citey + affiliation: Excellent University, Niceplace, Arcadia + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + - name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + database-provider: + name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + editors: + - family-names: Real Person + given-names: One Truly + name-particle: van der + name-suffix: IV + alias: Citey + affiliation: Excellent University, Niceplace, Arcadia + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + - name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + editors-series: + - family-names: Real Person + given-names: One Truly + name-particle: van der + name-suffix: IV + alias: Citey + affiliation: Excellent University, Niceplace, Arcadia + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + - name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + institution: + name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + location: + name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + publisher: + name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + recipients: + - family-names: Real Person + given-names: One Truly + name-particle: van der + name-suffix: IV + alias: Citey + affiliation: Excellent University, Niceplace, Arcadia + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + - name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + senders: + - family-names: Real Person + given-names: One Truly + name-particle: van der + name-suffix: IV + alias: Citey + affiliation: Excellent University, Niceplace, Arcadia + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + - name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + translators: + - family-names: Real Person + given-names: One Truly + name-particle: van der + name-suffix: IV + alias: Citey + affiliation: Excellent University, Niceplace, Arcadia + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + - name: Entity Project Team Conference entity + address: 22 Acacia Avenue + city: Citationburgh + region: Renfrewshire + post-code: C13 7X7 + country: GB + orcid: https://orcid.org/0000-0001-2345-6789 + email: project@entity.com + tel: +44(0)141-323 4567 + fax: +44(0)141-323 45678 + website: https://www.entity-project-team.io + date-start: '2017-01-01' + date-end: '2017-01-31' + location: The team garage + commit: 156a04c74a8a79d40c5d705cddf9d36735feab4d + identifiers: + - type: doi + value: 10.5281/zenodo.1003150 + - type: swh + value: swh:1:rel:99f6850374dc6597af01bd0ee1d3fc0699301b9f + - type: url + value: https://example.com + - type: other + value: other-schema://abcd.1234.efgh.5678 + license-url: https://spdx.org/licenses/CC-BY-SA-4.0.html#licenseText + + --- + +# Fuzzy matching of keys on cff + + Code + print_snapshot("Fuzzy keys", cffobj) + Output + + + ## Fuzzy keys + + title: a + cff-version: 1.2.0 + version: '200' + authors: + - family-names: a + given-names: b + message: Fix my keys + + --- + diff --git a/tests/testthat/_snaps/cff_from_bibtex.md b/tests/testthat/_snaps/cff_from_bibtex.md index 65d0bb2a..aea357cb 100644 --- a/tests/testthat/_snaps/cff_from_bibtex.md +++ b/tests/testthat/_snaps/cff_from_bibtex.md @@ -1,25 +1,3 @@ -# Read from file - - Code - d - Output - type: article - title: 'cffr: Generate Citation File Format Metadata for R Packages' - authors: - - family-names: Hernangómez - given-names: Diego - year: '2021' - journal: Journal of Open Source Software - publisher: - name: The Open Journal - volume: '6' - issue: '67' - doi: 10.21105/joss.03900 - url: https://doi.org/10.21105/joss.03900 - copyright: All rights reserved - notes: 'Publisher: The Open Journal' - start: '3900' - # From Bibtex entries Code diff --git a/tests/testthat/_snaps/cff_read.md b/tests/testthat/_snaps/cff_read.md index 435bfce9..68b1787d 100644 --- a/tests/testthat/_snaps/cff_read.md +++ b/tests/testthat/_snaps/cff_read.md @@ -1,1709 +1,46 @@ -# Walk trough full lifecycle +# Test errors on cff_read Code - print_snapshot("Read object", read) - Output - - - ## Read object - - cff-version: 1.2.0 - message: If you use this software, please cite it as below. - abstract: This is an awesome piece of research software! - authors: - - family-names: Real Person - given-names: One Truly - name-particle: van der - name-suffix: IV - alias: Citey - affiliation: Excellent University, Niceplace, Arcadia - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - commit: 156a04c74a8a79d40c5d705cddf9d36735feab4d - contact: - - family-names: Real Person - given-names: One Truly - name-particle: van der - name-suffix: IV - alias: Citey - affiliation: Excellent University, Niceplace, Arcadia - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - date-released: '2017-12-11' - doi: 10.5281/zenodo.1003150 - identifiers: - - type: doi - value: 10.5281/zenodo.1003150 - - type: swh - value: swh:1:rel:99f6850374dc6597af01bd0ee1d3fc0699301b9f - - type: url - value: https://example.com - - type: other - value: other-schema://abcd.1234.efgh.5678 - keywords: - - One - - Two - - Three - - '4' - license: CC-BY-SA-4.0 - license-url: https://spdx.org/licenses/CC-BY-SA-4.0.html#licenseText - repository: https://www.example.com/foo/?bar=baz&inga=42&quux - repository-code: http://foo.com/blah_(wikipedia)_blah#cite-1 - repository-artifact: https://files.pythonhosted.org/packages/0a/84/10507b69a07768bc16981184b4d147a0fc84b71fbf35c03bafc8dcced8e1/cffconvert-1.3.3.tar.gz - title: Citation File Format 1.0.0 - type: software - url: http://userid:password@example.com:8080/ - version: 1.0.0 - preferred-citation: - type: book - title: Book Title - abbreviation: Abbr - abstract: Description of the book. - collection-doi: 10.5281/zenodo.1003150 - collection-title: Collection Title - collection-type: Collection Type - commit: 156a04c74a8a79d40c5d705cddf9d36735feab4d - copyright: 2017 Stephan Druskat - data-type: Data Type - database: Database - date-accessed: '2017-10-31' - date-downloaded: '2017-10-31' - date-released: '2017-10-31' - date-published: '2017-10-31' - department: Department - doi: 10.5281/zenodo.1003150 - edition: 2nd edition - end: '456' - entry: Chapter 9 - filename: book.zip - format: Printed book - identifiers: - - type: doi - value: 10.5281/zenodo.1003150 - - type: swh - value: swh:1:rel:99f6850374dc6597af01bd0ee1d3fc0699301b9f - - type: url - value: https://example.com - - type: other - value: other-schema://abcd.1234.efgh.5678 - isbn: 978-1-89183-044-0 - issn: 1234-543X - issue: '123' - issue-date: December - issue-title: Special Issue on Software Citation - journal: PeerJ - keywords: - - Software - - Citation - languages: - - aaa - - zu - license: Apache-2.0 - license-url: https://spdx.org/licenses/Apache-2.0.html#licenseText - loc-start: '14' - loc-end: '54' - medium: hardcover book - month: '3' - nihmsid: Don't know what format a NIHMSID is in - notes: A field for general notes about the reference, usable in other formats such - as BibTeX. - number: A general-purpose field for accession numbers, cf. the specifications for - examples. - number-volumes: '7' - pages: '765' - patent-states: - - Germany - - ROI - - 'but also for example US states, such as:' - - IL - - RI - pmcid: PMC1234567 - repository: http://code.google.com/events/#&product=browser - repository-code: http://142.42.1.1:8080/ - repository-artifact: https://files.pythonhosted.org/packages/0a/84/10507b69a07768bc16981184b4d147a0fc84b71fbf35c03bafc8dcced8e1/cffconvert-1.3.3.tar.gz - scope: Cite this book if you want to reference the general concepts implemented - in Citation File Format 1.0.0. - section: Chapter 2 - "Reference keys" - status: advance-online - start: '123' - thesis-type: Doctoral dissertation - url: http://j.mp - version: 0.0.1423-BETA - volume: '2' - volume-title: Advances in Software Citation - year: '2017' - year-original: '2012' - conference: - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - authors: - - family-names: Real Person - given-names: One Truly - name-particle: van der - name-suffix: IV - alias: Citey - affiliation: Excellent University, Niceplace, Arcadia - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - contact: - - family-names: Real Person - given-names: One Truly - name-particle: van der - name-suffix: IV - alias: Citey - affiliation: Excellent University, Niceplace, Arcadia - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - database-provider: - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - editors: - - family-names: Real Person - given-names: One Truly - name-particle: van der - name-suffix: IV - alias: Citey - affiliation: Excellent University, Niceplace, Arcadia - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - editors-series: - - family-names: Real Person - given-names: One Truly - name-particle: van der - name-suffix: IV - alias: Citey - affiliation: Excellent University, Niceplace, Arcadia - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - institution: - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - location: - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - publisher: - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - recipients: - - family-names: Real Person - given-names: One Truly - name-particle: van der - name-suffix: IV - alias: Citey - affiliation: Excellent University, Niceplace, Arcadia - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - senders: - - family-names: Real Person - given-names: One Truly - name-particle: van der - name-suffix: IV - alias: Citey - affiliation: Excellent University, Niceplace, Arcadia - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - translators: - - family-names: Real Person - given-names: One Truly - name-particle: van der - name-suffix: IV - alias: Citey - affiliation: Excellent University, Niceplace, Arcadia - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - references: - - type: book - title: Book Title - abbreviation: Abbr - abstract: Description of the book. - collection-doi: 10.5281/zenodo.1003150 - collection-title: Collection Title - collection-type: Collection Type - commit: 156a04c74a8a79d40c5d705cddf9d36735feab4d - copyright: 2017 Stephan Druskat - data-type: Data Type - database: Database - date-accessed: '2017-10-31' - date-downloaded: '2017-10-31' - date-released: '2017-10-31' - date-published: '2017-10-31' - department: Department - doi: 10.5281/zenodo.1003150 - edition: 2nd edition - end: '123' - entry: Chapter 9 - filename: book.zip - format: Printed book - identifiers: - - type: doi - value: 10.5281/zenodo.1003150 - - type: swh - value: swh:1:rel:99f6850374dc6597af01bd0ee1d3fc0699301b9f - - type: url - value: https://example.com - - type: other - value: other-schema://abcd.1234.efgh.5678 - isbn: 978-1-89183-044-0 - issn: 1234-543X - issue: '123' - issue-date: December - issue-title: Special Issue on Software Citation - journal: PeerJ - keywords: - - Software - - Citation - languages: - - aaa - - zu - license: Apache-2.0 - license-url: https://spdx.org/licenses/Apache-2.0.html#licenseText - loc-start: '14' - loc-end: '54' - medium: hardcover book - month: '3' - nihmsid: Don't know what format a NIHMSID is in - notes: A field for general notes about the reference, usable in other formats such - as BibTeX. - number: A general-purpose field for accession numbers, cf. the specifications for - examples. - number-volumes: '7' - pages: '765' - patent-states: - - Germany - - ROI - - 'but also for example US states, such as:' - - IL - - RI - pmcid: PMC1234567 - repository: http://code.google.com/events/#&product=browser - repository-code: http://142.42.1.1:8080/ - repository-artifact: https://files.pythonhosted.org/packages/0a/84/10507b69a07768bc16981184b4d147a0fc84b71fbf35c03bafc8dcced8e1/cffconvert-1.3.3.tar.gz - scope: Cite this book if you want to reference the general concepts implemented - in Citation File Format 1.0.0. - section: Chapter 2 - "Reference keys" - status: advance-online - start: '123' - thesis-type: Doctoral dissertation - url: http://j.mp - version: 0.0.1423-BETA - volume: '2' - volume-title: Advances in Software Citation - year: '2017' - year-original: '2012' - conference: - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - authors: - - family-names: Real Person - given-names: One Truly - name-particle: van der - name-suffix: IV - alias: Citey - affiliation: Excellent University, Niceplace, Arcadia - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - contact: - - family-names: Real Person - given-names: One Truly - name-particle: van der - name-suffix: IV - alias: Citey - affiliation: Excellent University, Niceplace, Arcadia - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - database-provider: - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - editors: - - family-names: Real Person - given-names: One Truly - name-particle: van der - name-suffix: IV - alias: Citey - affiliation: Excellent University, Niceplace, Arcadia - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - editors-series: - - family-names: Real Person - given-names: One Truly - name-particle: van der - name-suffix: IV - alias: Citey - affiliation: Excellent University, Niceplace, Arcadia - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - institution: - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - location: - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - publisher: - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - recipients: - - family-names: Real Person - given-names: One Truly - name-particle: van der - name-suffix: IV - alias: Citey - affiliation: Excellent University, Niceplace, Arcadia - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - senders: - - family-names: Real Person - given-names: One Truly - name-particle: van der - name-suffix: IV - alias: Citey - affiliation: Excellent University, Niceplace, Arcadia - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - translators: - - family-names: Real Person - given-names: One Truly - name-particle: van der - name-suffix: IV - alias: Citey - affiliation: Excellent University, Niceplace, Arcadia - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - - --- + cff_read(c("abcde", "b")) + Condition + Error in `cff_read()`: + ! Use a single value, `path` has length 2 --- Code - print_snapshot("Modify object", modify) - Output - - - ## Modify object - - cff-version: 1.2.0 - message: If you use this software, please cite it as below. - type: software - license: CC-BY-SA-4.0 - title: A new title - version: 1.0.0 - doi: 10.5281/zenodo.1003150 - abstract: This is an awesome piece of research software! - authors: - - family-names: Real Person - given-names: One Truly - name-particle: van der - name-suffix: IV - alias: Citey - affiliation: Excellent University, Niceplace, Arcadia - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - preferred-citation: - type: book - title: Book Title - abbreviation: Abbr - abstract: Description of the book. - collection-doi: 10.5281/zenodo.1003150 - collection-title: Collection Title - collection-type: Collection Type - commit: 156a04c74a8a79d40c5d705cddf9d36735feab4d - copyright: 2017 Stephan Druskat - data-type: Data Type - database: Database - date-accessed: '2017-10-31' - date-downloaded: '2017-10-31' - date-released: '2017-10-31' - date-published: '2017-10-31' - department: Department - doi: 10.5281/zenodo.1003150 - edition: 2nd edition - end: '456' - entry: Chapter 9 - filename: book.zip - format: Printed book - identifiers: - - type: doi - value: 10.5281/zenodo.1003150 - - type: swh - value: swh:1:rel:99f6850374dc6597af01bd0ee1d3fc0699301b9f - - type: url - value: https://example.com - - type: other - value: other-schema://abcd.1234.efgh.5678 - isbn: 978-1-89183-044-0 - issn: 1234-543X - issue: '123' - issue-date: December - issue-title: Special Issue on Software Citation - journal: PeerJ - keywords: - - Software - - Citation - languages: - - aaa - - zu - license: Apache-2.0 - license-url: https://spdx.org/licenses/Apache-2.0.html#licenseText - loc-start: '14' - loc-end: '54' - medium: hardcover book - month: '3' - nihmsid: Don't know what format a NIHMSID is in - notes: A field for general notes about the reference, usable in other formats such - as BibTeX. - number: A general-purpose field for accession numbers, cf. the specifications for - examples. - number-volumes: '7' - pages: '765' - patent-states: - - Germany - - ROI - - 'but also for example US states, such as:' - - IL - - RI - pmcid: PMC1234567 - repository: http://code.google.com/events/#&product=browser - repository-code: http://142.42.1.1:8080/ - repository-artifact: https://files.pythonhosted.org/packages/0a/84/10507b69a07768bc16981184b4d147a0fc84b71fbf35c03bafc8dcced8e1/cffconvert-1.3.3.tar.gz - scope: Cite this book if you want to reference the general concepts implemented - in Citation File Format 1.0.0. - section: Chapter 2 - "Reference keys" - status: advance-online - start: '123' - thesis-type: Doctoral dissertation - url: http://j.mp - version: 0.0.1423-BETA - volume: '2' - volume-title: Advances in Software Citation - year: '2017' - year-original: '2012' - conference: - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - authors: - - family-names: Real Person - given-names: One Truly - name-particle: van der - name-suffix: IV - alias: Citey - affiliation: Excellent University, Niceplace, Arcadia - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - contact: - - family-names: Real Person - given-names: One Truly - name-particle: van der - name-suffix: IV - alias: Citey - affiliation: Excellent University, Niceplace, Arcadia - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - database-provider: - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - editors: - - family-names: Real Person - given-names: One Truly - name-particle: van der - name-suffix: IV - alias: Citey - affiliation: Excellent University, Niceplace, Arcadia - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - editors-series: - - family-names: Real Person - given-names: One Truly - name-particle: van der - name-suffix: IV - alias: Citey - affiliation: Excellent University, Niceplace, Arcadia - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - institution: - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - location: - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - publisher: - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - recipients: - - family-names: Real Person - given-names: One Truly - name-particle: van der - name-suffix: IV - alias: Citey - affiliation: Excellent University, Niceplace, Arcadia - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - senders: - - family-names: Real Person - given-names: One Truly - name-particle: van der - name-suffix: IV - alias: Citey - affiliation: Excellent University, Niceplace, Arcadia - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - translators: - - family-names: Real Person - given-names: One Truly - name-particle: van der - name-suffix: IV - alias: Citey - affiliation: Excellent University, Niceplace, Arcadia - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - repository: https://www.example.com/foo/?bar=baz&inga=42&quux - repository-artifact: https://files.pythonhosted.org/packages/0a/84/10507b69a07768bc16981184b4d147a0fc84b71fbf35c03bafc8dcced8e1/cffconvert-1.3.3.tar.gz - repository-code: http://foo.com/blah_(wikipedia)_blah#cite-1 - url: http://userid:password@example.com:8080/ - date-released: '2017-12-11' - contact: - - family-names: Real Person - given-names: One Truly - name-particle: van der - name-suffix: IV - alias: Citey - affiliation: Excellent University, Niceplace, Arcadia - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - keywords: - - One - - Two - - Three - - '4' - references: - - type: book - title: Book Title - abbreviation: Abbr - abstract: Description of the book. - collection-doi: 10.5281/zenodo.1003150 - collection-title: Collection Title - collection-type: Collection Type - commit: 156a04c74a8a79d40c5d705cddf9d36735feab4d - copyright: 2017 Stephan Druskat - data-type: Data Type - database: Database - date-accessed: '2017-10-31' - date-downloaded: '2017-10-31' - date-released: '2017-10-31' - date-published: '2017-10-31' - department: Department - doi: 10.5281/zenodo.1003150 - edition: 2nd edition - end: '123' - entry: Chapter 9 - filename: book.zip - format: Printed book - identifiers: - - type: doi - value: 10.5281/zenodo.1003150 - - type: swh - value: swh:1:rel:99f6850374dc6597af01bd0ee1d3fc0699301b9f - - type: url - value: https://example.com - - type: other - value: other-schema://abcd.1234.efgh.5678 - isbn: 978-1-89183-044-0 - issn: 1234-543X - issue: '123' - issue-date: December - issue-title: Special Issue on Software Citation - journal: PeerJ - keywords: - - Software - - Citation - languages: - - aaa - - zu - license: Apache-2.0 - license-url: https://spdx.org/licenses/Apache-2.0.html#licenseText - loc-start: '14' - loc-end: '54' - medium: hardcover book - month: '3' - nihmsid: Don't know what format a NIHMSID is in - notes: A field for general notes about the reference, usable in other formats such - as BibTeX. - number: A general-purpose field for accession numbers, cf. the specifications for - examples. - number-volumes: '7' - pages: '765' - patent-states: - - Germany - - ROI - - 'but also for example US states, such as:' - - IL - - RI - pmcid: PMC1234567 - repository: http://code.google.com/events/#&product=browser - repository-code: http://142.42.1.1:8080/ - repository-artifact: https://files.pythonhosted.org/packages/0a/84/10507b69a07768bc16981184b4d147a0fc84b71fbf35c03bafc8dcced8e1/cffconvert-1.3.3.tar.gz - scope: Cite this book if you want to reference the general concepts implemented - in Citation File Format 1.0.0. - section: Chapter 2 - "Reference keys" - status: advance-online - start: '123' - thesis-type: Doctoral dissertation - url: http://j.mp - version: 0.0.1423-BETA - volume: '2' - volume-title: Advances in Software Citation - year: '2017' - year-original: '2012' - conference: - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - authors: - - family-names: Real Person - given-names: One Truly - name-particle: van der - name-suffix: IV - alias: Citey - affiliation: Excellent University, Niceplace, Arcadia - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - contact: - - family-names: Real Person - given-names: One Truly - name-particle: van der - name-suffix: IV - alias: Citey - affiliation: Excellent University, Niceplace, Arcadia - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - database-provider: - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - editors: - - family-names: Real Person - given-names: One Truly - name-particle: van der - name-suffix: IV - alias: Citey - affiliation: Excellent University, Niceplace, Arcadia - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - editors-series: - - family-names: Real Person - given-names: One Truly - name-particle: van der - name-suffix: IV - alias: Citey - affiliation: Excellent University, Niceplace, Arcadia - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - institution: - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - location: - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - publisher: - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - recipients: - - family-names: Real Person - given-names: One Truly - name-particle: van der - name-suffix: IV - alias: Citey - affiliation: Excellent University, Niceplace, Arcadia - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - senders: - - family-names: Real Person - given-names: One Truly - name-particle: van der - name-suffix: IV - alias: Citey - affiliation: Excellent University, Niceplace, Arcadia - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - translators: - - family-names: Real Person - given-names: One Truly - name-particle: van der - name-suffix: IV - alias: Citey - affiliation: Excellent University, Niceplace, Arcadia - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - - name: Entity Project Team Conference entity - address: 22 Acacia Avenue - city: Citationburgh - region: Renfrewshire - post-code: C13 7X7 - country: GB - orcid: https://orcid.org/0000-0001-2345-6789 - email: project@entity.com - tel: +44(0)141-323 4567 - fax: +44(0)141-323 45678 - website: https://www.entity-project-team.io - date-start: '2017-01-01' - date-end: '2017-01-31' - location: The team garage - commit: 156a04c74a8a79d40c5d705cddf9d36735feab4d - identifiers: - - type: doi - value: 10.5281/zenodo.1003150 - - type: swh - value: swh:1:rel:99f6850374dc6597af01bd0ee1d3fc0699301b9f - - type: url - value: https://example.com - - type: other - value: other-schema://abcd.1234.efgh.5678 - license-url: https://spdx.org/licenses/CC-BY-SA-4.0.html#licenseText - - --- + cff_read("abcde") + Condition + Error in `cff_read()`: + ! 'abcde' does not exist. Check the '.' directory + +--- + + Code + cff_read(f) + Condition + Error in `guess_type_file()`: + ! Don't recognize the file type of '/cloud/project/inst/schema/schema.json'. Use a specific function (e.g. `cffr:cff_read_description()` -# Fuzzy matching of keys on cff +# cff read bib Code - print_snapshot("Fuzzy keys", cffobj) + d Output - - - ## Fuzzy keys - - title: a - cff-version: 1.2.0 - version: '200' + type: article + title: 'cffr: Generate Citation File Format Metadata for R Packages' authors: - - family-names: a - given-names: b - message: Fix my keys - - --- + - family-names: Hernangómez + given-names: Diego + year: '2021' + journal: Journal of Open Source Software + publisher: + name: The Open Journal + volume: '6' + issue: '67' + doi: 10.21105/joss.03900 + url: https://doi.org/10.21105/joss.03900 + copyright: All rights reserved + notes: 'Publisher: The Open Journal' + start: '3900' diff --git a/tests/testthat/test-cff-methods.R b/tests/testthat/test-cff-methods.R index 53f7f50b..f6318558 100644 --- a/tests/testthat/test-cff-methods.R +++ b/tests/testthat/test-cff-methods.R @@ -186,3 +186,13 @@ test_that("as person with another cff", { expect_s3_class(key, "cff") expect_identical(as.person(key), person()) }) + +test_that("head and tail", { + a_cff <- cff() + expect_snapshot(a_cff) + expect_snapshot(head(a_cff, 2)) + expect_s3_class(head(a_cff, 2), "cff") + + expect_snapshot(tail(a_cff, 2)) + expect_s3_class(tail(a_cff, 2), "cff") +}) diff --git a/tests/testthat/test-cff.R b/tests/testthat/test-cff.R new file mode 100644 index 00000000..0c437b26 --- /dev/null +++ b/tests/testthat/test-cff.R @@ -0,0 +1,114 @@ +test_that("Test errors on cff", { + expect_error(cff("abcde")) + nocff <- system.file("CITATION", + package = "cffR" + ) + expect_error(cff_create(nocff)) +}) + +test_that("Compare blank cff with skeleton", { + skeleton <- system.file("examples/CITATION_skeleton.cff", + package = "cffr" + ) + + fromfile <- cff(skeleton) + fromfunction <- cff() + expect_true(all(unlist(fromfile) == unlist(fromfunction))) + + # Validate + expect_true(cff_validate(fromfunction, verbose = FALSE)) +}) + +test_that("Walk trough full lifecycle", { + complete <- system.file("examples/CITATION_complete.cff", + package = "cffr" + ) + + # Read + read <- cff_read(complete) + expect_s3_class(read, "cff") + expect_true(cff_validate(read, verbose = FALSE)) + expect_snapshot(print_snapshot("Read object", read)) + + # Modify + modify <- cff_create(read, keys = list(title = "A new title")) + expect_snapshot(print_snapshot("Modify object", modify)) + expect_true(all(unlist(read) == unlist(read))) + expect_true(length(read) == length(modify)) + expect_true(length((setdiff(names(read), names(modify)))) == 0) + expect_false(read$title == modify$title) + + + + # Write + tmp <- tempfile(fileext = ".cff") + cff_write(modify, outfile = tmp, validate = FALSE, verbose = FALSE) + stopifnotexists(tmp) + stopifnotcff(tmp) + + # Validate + expect_true(cff_validate(tmp, verbose = FALSE)) + + file.remove(tmp) +}) + +test_that("Other convertes", { + a <- cff() + expect_s3_class(a, "cff") + a <- cff(a) + expect_s3_class(a, "cff") + a <- as.cff(a) + expect_true(is_cff(a)) + expect_s3_class(a, "cff") + + expect_message(noadd <- cff(address = "New York", version = 5)) + expect_true(is_cff(noadd)) + expect_false(is_cff(list(a = 1, b = 2))) + expect_true(is_cff(as.cff(list(a = 1, b = 2)))) +}) + + +test_that("Recursive parsing", { + complete <- system.file("examples/CITATION_complete.cff", + package = "cffr" + ) + + # Read + read <- cff(complete) + + # Test all levels + expect_s3_class(read, "cff") + expect_s3_class(read$authors, "cff") + expect_s3_class(read$authors[[1]], "cff") + expect_s3_class(read$references, "cff") + expect_s3_class(read$references[[1]]$authors, "cff") + expect_s3_class(read$references[[1]]$authors[[1]], "cff") +}) + + +test_that("Fuzzy matching of keys on cff", { + expect_message(cff( + tittle = "a", + cff_version = "ar", + version = "200", + messange = "Fix my keys" + ), "messange: message") + + cffobj <- suppressMessages( + cff( + tittle = "a", + cff_version = "1.2.0", + version = "200", + anthor = list(list( + "family-names" = "a", + "given-names" = "b" + )), + messange = "Fix my keys" + ) + ) + + expect_true(is_cff(cffobj)) + expect_true(cff_validate(cffobj, verbose = FALSE)) + + expect_snapshot(print_snapshot("Fuzzy keys", cffobj)) +}) diff --git a/tests/testthat/test-cff_from_bibtex.R b/tests/testthat/test-cff_from_bibtex.R index f686b855..2ea00ae1 100644 --- a/tests/testthat/test-cff_from_bibtex.R +++ b/tests/testthat/test-cff_from_bibtex.R @@ -6,30 +6,6 @@ test_that("test errors", { expect_error(cff_from_bibtex(x)) }) -test_that("Read from file", { - skip_if_not_installed("bibtex", "0.5.0") - - x <- system.file("REFERENCES.bib", package = "cffr") - - list <- cff_from_bibtex(x) - - expect_s3_class(list, "cff") - expect_gt(length(list), 1) - - - # With encodings - - x2 <- system.file("examples/example.bib", package = "cffr") - fromfile <- cff_from_bibtex(x2) - expect_s3_class(fromfile, "cff") - expect_length(fromfile, 2) - - d <- fromfile[[2]] - - expect_snapshot(d) -}) - - test_that("From Bibtex entries", { skip_if_not_installed("bibtex", "0.5.0") diff --git a/tests/testthat/test-cff_read.R b/tests/testthat/test-cff_read.R index 0c437b26..2a5d3fc1 100644 --- a/tests/testthat/test-cff_read.R +++ b/tests/testthat/test-cff_read.R @@ -1,114 +1,78 @@ -test_that("Test errors on cff", { - expect_error(cff("abcde")) - nocff <- system.file("CITATION", - package = "cffR" - ) - expect_error(cff_create(nocff)) -}) +test_that("Test errors on cff_read", { + expect_snapshot(cff_read(c("abcde", "b")), error = TRUE) + expect_snapshot(cff_read("abcde"), error = TRUE) -test_that("Compare blank cff with skeleton", { - skeleton <- system.file("examples/CITATION_skeleton.cff", - package = "cffr" - ) + f <- system.file("schema/schema.json", package = "cffr") + expect_snapshot(cff_read(f), error = TRUE) +}) - fromfile <- cff(skeleton) - fromfunction <- cff() - expect_true(all(unlist(fromfile) == unlist(fromfunction))) +test_that("cff_read citation.cff", { + f <- system.file("examples/CITATION_complete.cff", package = "cffr") + f1 <- cff_read(f) + expect_true(cff_validate(f1, verbose = FALSE)) + expect_s3_class(f1, c("cff", "list"), exact = TRUE) - # Validate - expect_true(cff_validate(fromfunction, verbose = FALSE)) + # With the alias + f2 <- cff_read_cff_citation(f) + expect_identical(f1, f2) }) -test_that("Walk trough full lifecycle", { - complete <- system.file("examples/CITATION_complete.cff", - package = "cffr" - ) - # Read - read <- cff_read(complete) - expect_s3_class(read, "cff") - expect_true(cff_validate(read, verbose = FALSE)) - expect_snapshot(print_snapshot("Read object", read)) +test_that("cff_read DESCRIPTION", { + f <- system.file("examples/DESCRIPTION_no_URL", package = "cffr") - # Modify - modify <- cff_create(read, keys = list(title = "A new title")) - expect_snapshot(print_snapshot("Modify object", modify)) - expect_true(all(unlist(read) == unlist(read))) - expect_true(length(read) == length(modify)) - expect_true(length((setdiff(names(read), names(modify)))) == 0) - expect_false(read$title == modify$title) + f1 <- cff_read(f, gh_keywords = FALSE) + expect_true(cff_validate(f1, verbose = FALSE)) + expect_s3_class(f1, c("cff", "list"), exact = TRUE) + # With the alias + f2 <- cff_read_description(f, gh_keywords = FALSE) + expect_identical(f1, f2) - # Write - tmp <- tempfile(fileext = ".cff") - cff_write(modify, outfile = tmp, validate = FALSE, verbose = FALSE) - stopifnotexists(tmp) - stopifnotcff(tmp) + # Use other params + f1_1 <- cff_read(f, + gh_keywords = FALSE, cff_version = 3000, + authors_roles = c("aut", "cre", "ctb") + ) - # Validate - expect_true(cff_validate(tmp, verbose = FALSE)) + expect_equal(f1_1$`cff-version`, "3000") - file.remove(tmp) -}) + expect_gt(length(f1_1$authors), length(f1$authors)) -test_that("Other convertes", { - a <- cff() - expect_s3_class(a, "cff") - a <- cff(a) - expect_s3_class(a, "cff") - a <- as.cff(a) - expect_true(is_cff(a)) - expect_s3_class(a, "cff") - - expect_message(noadd <- cff(address = "New York", version = 5)) - expect_true(is_cff(noadd)) - expect_false(is_cff(list(a = 1, b = 2))) - expect_true(is_cff(as.cff(list(a = 1, b = 2)))) + f2_1 <- cff_read_description(f, + gh_keywords = FALSE, cff_version = 3000, + authors_roles = c("aut", "cre", "ctb") + ) + + expect_identical(f1_1, f2_1) }) -test_that("Recursive parsing", { - complete <- system.file("examples/CITATION_complete.cff", - package = "cffr" - ) +test_that("cff read bib", { + skip_if_not_installed("bibtex", "0.5.0") - # Read - read <- cff(complete) + f <- system.file("REFERENCES.bib", package = "cffr") - # Test all levels - expect_s3_class(read, "cff") - expect_s3_class(read$authors, "cff") - expect_s3_class(read$authors[[1]], "cff") - expect_s3_class(read$references, "cff") - expect_s3_class(read$references[[1]]$authors, "cff") - expect_s3_class(read$references[[1]]$authors[[1]], "cff") -}) + f1 <- cff_read(f) + expect_s3_class(f1, c("cff", "list"), exact = TRUE) + expect_gt(length(f1), 1) + # Specific + f2 <- cff_read_bib(f) + expect_identical(f1, f2) -test_that("Fuzzy matching of keys on cff", { - expect_message(cff( - tittle = "a", - cff_version = "ar", - version = "200", - messange = "Fix my keys" - ), "messange: message") - - cffobj <- suppressMessages( - cff( - tittle = "a", - cff_version = "1.2.0", - version = "200", - anthor = list(list( - "family-names" = "a", - "given-names" = "b" - )), - messange = "Fix my keys" - ) - ) - expect_true(is_cff(cffobj)) - expect_true(cff_validate(cffobj, verbose = FALSE)) + # With encodings + + f <- system.file("examples/example.bib", package = "cffr") + f1_2 <- cff_read(f) + expect_s3_class(f1_2, c("cff", "list"), exact = TRUE) + expect_length(f1_2, 2) + + d <- f1_2[[2]] - expect_snapshot(print_snapshot("Fuzzy keys", cffobj)) + expect_snapshot(d) + f2_2 <- cff_read_bib(f) + expect_identical(f1_2, f2_2) }) diff --git a/tests/testthat/test-cff_to_bibentry.R b/tests/testthat/test-cff_to_bibentry.R index d5438428..b4612dea 100644 --- a/tests/testthat/test-cff_to_bibentry.R +++ b/tests/testthat/test-cff_to_bibentry.R @@ -233,7 +233,10 @@ test_that("Unpublished to bibtex", { author = "John M. Aronis and Foster J. Provost", title = "Efficiently Constructing Relational Features from Background", year = 1959, - note = "Unpublished MS, Computer Science Department, University of Pittsburgh.", + note = paste0( + "Unpublished MS, Computer Science Department, ", + "University of Pittsburgh." + ), missinginfo = "Date is guess.", ) @@ -254,7 +257,9 @@ test_that("particle names", { bibparsed <- cff_parse_citation(bib) - bibparsed$authors <- cff_parse_person_bibtex("van Leunen, Mary-Claire and Davis, Jr., Sammy") + bibparsed$authors <- cff_parse_person_bibtex( + "van Leunen, Mary-Claire and Davis, Jr., Sammy" + ) cffobj <- cff_create(cff(), keys = list(references = list(bibparsed)) diff --git a/tests/testthat/test-merge_desc_cit.R b/tests/testthat/test-merge_desc_cit.R index 54bb3113..db9b29e1 100644 --- a/tests/testthat/test-merge_desc_cit.R +++ b/tests/testthat/test-merge_desc_cit.R @@ -8,7 +8,7 @@ test_that("Merge all DESCRIPTION files with CITATION_basic", { package = "cffr" ) for (i in seq_len(length(allfiles))) { - desc_parse <- cff_description(allfiles[i], gh_keywords = FALSE) + desc_parse <- cff_read_description(allfiles[i], gh_keywords = FALSE) generate_cit <- parse_r_citation(allfiles[i], citpath) parse_cit <- lapply(generate_cit, cff_parse_citation) merged <- merge_desc_cit(desc_parse, parse_cit) diff --git a/tests/testthat/test-utils-persons.R b/tests/testthat/test-utils-persons.R index 39ab5710..d1a1893b 100644 --- a/tests/testthat/test-utils-persons.R +++ b/tests/testthat/test-utils-persons.R @@ -1,4 +1,6 @@ -# Based on https://maverick.inria.fr/~Xavier.Decoret/resources/xdkbibtex/bibtex_summary.html +# Based on +# https://maverick.inria.fr/~Xavier.Decoret/resources/xdkbibtex/ >> +# bibtex_summary.html test_that("Test first von last", { x <- "AA BB" From c1beb0eb400748f69112e256ed238042643bcf42 Mon Sep 17 00:00:00 2001 From: Diego H Date: Mon, 19 Feb 2024 23:22:14 +0100 Subject: [PATCH 07/32] Finish cff_read() --- CITATION.cff | 3 - NAMESPACE | 1 + R/cff_read.R | 157 +++++++++++++++++++++++++++++- README.md | 27 ++--- codemeta.json | 4 +- data/cran_to_spdx.rda | Bin 916 -> 916 bytes inst/schemaorg.json | 2 +- man/cff_read.Rd | 27 ++++- tests/testthat/_snaps/cff_read.md | 41 +++++++- tests/testthat/test-cff_read.R | 116 ++++++++++++++++++++-- vignettes/tweet-1.png | Bin 121117 -> 24193 bytes 11 files changed, 334 insertions(+), 44 deletions(-) diff --git a/CITATION.cff b/CITATION.cff index 59675863..ae2c7213 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -138,9 +138,6 @@ references: email: jeroen@berkeley.edu orcid: https://orcid.org/0000-0002-4035-0289 year: '2024' - identifiers: - - type: url - value: https://arxiv.org/abs/1403.2805 version: '>= 1.7.2' - type: software title: jsonvalidate diff --git a/NAMESPACE b/NAMESPACE index 41017f89..62a9bd89 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -20,6 +20,7 @@ export(cff_parse_person_bibtex) export(cff_read) export(cff_read_bib) export(cff_read_cff_citation) +export(cff_read_citation) export(cff_read_description) export(cff_schema_definitions_entity) export(cff_schema_definitions_person) diff --git a/R/cff_read.R b/R/cff_read.R index b0c3af75..b84ff49f 100644 --- a/R/cff_read.R +++ b/R/cff_read.R @@ -27,10 +27,33 @@ #' @param authors_roles Roles to be considered as authors of the package when #' generating the [`cff`][cff-class] object. #' @param encoding Encoding to be assumed for `path`. See [readLines()]. +#' @param meta A list of package metadata as obtained by +#' [utils::packageDescription()] or `NULL` (the default). See **Details**. #' @param ... Arguments to be passed to other functions. #' -#' @return A [`cff`][cff-class] object. +#' @return +#' A [`cff`][cff-class] object. In the case of [cff_read_cff_citation()] and +#' [cff_read_description()] a full and (potentially) valid `cff` object. #' +#' In the case of [cff_read_bib()] and [cff_read_citation()], the result is +#' the `cff` version of a [bibentry()] object (i.e. a bibliographic reference), +#' that can be used to complement another `cff` object. +#' +#' +#' @references +#' +#' R Core Team (2023). _Writing R Extensions_. +#' +#' +#' @details +#' +#' Section 1.9 CITATION files of *Writing R Extensions* (R Core Team 2023) +#' specifies how to create dynamic `CITATION` files using `meta` object, hence +#' the `meta` argument in [cff_read_citation()] may be needed for reading +#' some files correctly. +#' +#' @examples +#' TODO #' cff_read <- function(path, ...) { if (length(path) > 1) { @@ -53,6 +76,7 @@ cff_read <- function(path, ...) { "cff_citation" = cff_read_cff_citation(path, ...), "description" = cff_read_description(path, ...), "bib" = cff_read_bib(path, ...), + "citation" = cff_read_citation(path, ...), NULL ) @@ -62,6 +86,15 @@ cff_read <- function(path, ...) { #' @export #' @rdname cff_read cff_read_cff_citation <- function(path, ...) { + if (!file.exists(path)) { + cli::cli_abort( + paste( + "{.file {path}} does not exist. ", + "Check the {.file {dirname(path)}} directory" + ) + ) + } + cffobj <- yaml::read_yaml(path) new_cff(cffobj) } @@ -71,6 +104,15 @@ cff_read_cff_citation <- function(path, ...) { cff_read_description <- function(path, cff_version = "1.2.0", gh_keywords = TRUE, authors_roles = c("aut", "cre"), ...) { + if (!file.exists(path)) { + cli::cli_abort( + paste( + "{.file {path}} does not exist. ", + "Check the {.file {dirname(path)}} directory" + ) + ) + } + pkg <- desc::desc(path) pkg$coerce_authors_at_r() @@ -108,14 +150,71 @@ cff_read_description <- function(path, cff_version = "1.2.0", #' @export #' @rdname cff_read -cff_read_cff_citation <- function(path, ...) { - cffobj <- yaml::read_yaml(path) - new_cff(cffobj) +cff_read_citation <- function(path, meta = NULL, ...) { + if (!file.exists(path)) { + cli::cli_abort( + paste( + "{.file {path}} does not exist. ", + "Check the {.file {dirname(path)}} directory" + ) + ) + } + + if (!any(is.null(meta), inherits(meta, "packageDescription"))) { + # nolint start + # Object for cli only + ex <- packageDescription("cffr") + # nolint end + + cli::cli_alert_warning( + paste0( + "{.arg meta} should be {.val NULL} or {.obj_type_friendly {ex}}", + " not {.obj_type_friendly {meta}}. Using {.arg meta = NULL}" + ) + ) + meta <- NULL + } + + new_meta <- clean_package_meta(meta) + the_cit <- try(utils::readCitationFile(path, meta = new_meta), silent = TRUE) + + # If error then new try + if (inherits(the_cit, "try-error")) { + cli::cli_alert_warning( + paste0( + "It was not possible to read {.file {path}} with the {.arg meta} ", + "provided. Trying with {.code packageDescription('base')}" + ) + ) + new_meta <- packageDescription("base") + the_cit <- try(utils::readCitationFile(path, meta = new_meta), + silent = TRUE + ) + # nocov start + if (inherits(the_cit, "try-error")) { + cli::cli_alert_danger( + "Can't read {.file path}, returning {.val NULL}" + ) + return(NULL) + } + # nocov end + } + tocff <- cff_parse_citation(the_cit) + new_cff(tocff) } #' @export #' @rdname cff_read cff_read_bib <- function(path, encoding = "UTF-8", ...) { + if (!file.exists(path)) { + cli::cli_abort( + paste( + "{.file {path}} does not exist. ", + "Check the {.file {dirname(path)}} directory" + ) + ) + } + # nocov start if (!requireNamespace("bibtex", quietly = TRUE)) { msg <- paste0( @@ -156,3 +255,53 @@ guess_type_file <- function(path) { ) ) } + +#' Parse and clean data from DESCRIPTION to create metadata +#' @noRd +clean_package_meta <- function(meta) { + if (!inherits(meta, "packageDescription")) { + return(NULL) + } + + # Convert to a desc object + + # First write to a dcf file + tmp <- tempfile("DESCRIPTION") + meta_unl <- unclass(meta) + write.dcf(meta_unl, tmp) + pkg <- desc::desc(tmp) + pkg$coerce_authors_at_r() + # Extract package data + meta <- pkg$get(desc::cran_valid_fields) + + # Clean missing and drop empty fields + meta <- drop_null(lapply(meta, clean_str)) + + # Check encoding + if (!is.null(meta$Encoding)) { + meta <- lapply(meta, iconv, from = meta$Encoding, to = "UTF-8") + } else { + meta$Encoding <- "UTF-8" + } + unlink(tmp, force = TRUE) + meta +} + +# For testing, packageDescription object from desc +test_meta <- function(x) { + src <- x + my_meta <- desc::desc(src) + + # As list + my_meta_l <- my_meta$get(desc::cran_valid_fields) + my_meta_l <- as.list(my_meta_l) + v_nas <- vapply(my_meta_l, is.na, logical(1)) + + my_meta_l <- my_meta_l[!v_nas] + meta_proto <- packageDescription("cffr") + + class(my_meta_l) <- class(meta_proto) + attr(my_meta_l, "file") <- x + + my_meta_l +} diff --git a/README.md b/README.md index b20d2624..abcd59d0 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ file and the `CITATION` file (if present) of your package. Note that **cffr** works best if your package pass `R CMD check/devtools::check()`. -As per 2024-02-16 there are at least 240 repos on GitHub using **cffr**. +As per 2024-02-19 there are at least 261 repos on GitHub using **cffr**. [Check them out here](https://github.com/search?q=cffr%20path%3A**%2FCITATION.cff&type=code). @@ -385,9 +385,6 @@ test <- cff_create("rmarkdown") email: jeroen@berkeley.edu orcid: https://orcid.org/0000-0002-4035-0289 year: '2024' - identifiers: - - type: url - value: https://arxiv.org/abs/1403.2805 - type: software title: knitr abstract: 'knitr: A General-Purpose Package for Dynamic Report Generation in R' @@ -753,18 +750,6 @@ test <- cff_create("rmarkdown") given-names: Davis email: davis@posit.co year: '2024' - - type: software - title: cleanrmd - abstract: 'cleanrmd: Clean Class-Less ''R Markdown'' HTML Documents' - notes: Suggests - url: https://pkg.garrickadenbuie.com/cleanrmd/ - repository: https://CRAN.R-project.org/package=cleanrmd - authors: - - family-names: Aden-Buie - given-names: Garrick - email: garrick@adenbuie.com - orcid: https://orcid.org/0000-0002-7111-0077 - year: '2024' - type: software title: withr abstract: 'withr: Run Code ''With'' Temporarily Modified Global State' @@ -903,9 +888,9 @@ for more info.
-Boettiger, Carl, and Maëlle Salmon. 2021a. *codemeta: A Smaller codemetar Package*. +Boettiger, Carl, and Maëlle Salmon. 2021a. +*codemeta: A Smaller +codemetar Package*. .
@@ -926,8 +911,8 @@ Among Citation Formats*.
-Dietrich, Jan Philipp, and Waldir Leoncio. 2022. *citation: Software Citation Tools*. +Dietrich, Jan Philipp, and Waldir Leoncio. 2022. +*citation: Software Citation Tools*.
diff --git a/codemeta.json b/codemeta.json index 6db1f419..fe427c25 100644 --- a/codemeta.json +++ b/codemeta.json @@ -14,7 +14,7 @@ "name": "R", "url": "https://r-project.org" }, - "runtimePlatform": "R version 4.3.2 (2023-10-31)", + "runtimePlatform": "R version 4.3.2 (2023-10-31 ucrt)", "provider": { "@id": "https://cran.r-project.org", "@type": "Organization", @@ -200,7 +200,7 @@ }, "isPartOf": "https://ropensci.org", "keywords": ["attribution", "citation", "credit", "citation-files", "cff", "metadata", "r", "r-package", "citation-file-format", "rstats", "ropensci", "cran"], - "fileSize": "1030.282KB", + "fileSize": "918.861KB", "citation": [ { "@type": "ScholarlyArticle", diff --git a/data/cran_to_spdx.rda b/data/cran_to_spdx.rda index 54267cd1b0840c06bdfc1e554391353a60cd0a13..0bd1435cae708e762b962cb7a97ec9ae0c12fef9 100644 GIT binary patch delta 19 XcmbQjK82l2zMF#q4A?eubuj|~BcB67 delta 19 XcmbQjK82l2zMF#q445}^buj|~Baj0= diff --git a/inst/schemaorg.json b/inst/schemaorg.json index 8056af78..dec7a9c6 100644 --- a/inst/schemaorg.json +++ b/inst/schemaorg.json @@ -26,6 +26,6 @@ "name": "Comprehensive R Archive Network (CRAN)", "url": "https://cran.r-project.org" }, - "runtimePlatform": "R version 4.3.2 (2023-10-31)", + "runtimePlatform": "R version 4.3.2 (2023-10-31 ucrt)", "version": "0.5.0.9000" } diff --git a/man/cff_read.Rd b/man/cff_read.Rd index 52dd0300..1a249263 100644 --- a/man/cff_read.Rd +++ b/man/cff_read.Rd @@ -4,6 +4,7 @@ \alias{cff_read} \alias{cff_read_cff_citation} \alias{cff_read_description} +\alias{cff_read_citation} \alias{cff_read_bib} \title{Read an external file as a \code{\link[=cff-class]{cff}} object} \usage{ @@ -19,7 +20,7 @@ cff_read_description( ... ) -cff_read_cff_citation(path, ...) +cff_read_citation(path, meta = NULL, ...) cff_read_bib(path, encoding = "UTF-8", ...) } @@ -37,10 +38,18 @@ GitHub, would you like to add the repo topics as keywords?} \item{authors_roles}{Roles to be considered as authors of the package when generating the \code{\link[=cff-class]{cff}} object.} +\item{meta}{A list of package metadata as obtained by +\code{\link[utils:packageDescription]{utils::packageDescription()}} or \code{NULL} (the default). See \strong{Details}.} + \item{encoding}{Encoding to be assumed for \code{path}. See \code{\link[=readLines]{readLines()}}.} } \value{ -A \code{\link[=cff-class]{cff}} object. +A \code{\link[=cff-class]{cff}} object. In the case of \code{\link[=cff_read_cff_citation]{cff_read_cff_citation()}} and +\code{\link[=cff_read_description]{cff_read_description()}} a full and (potentially) valid \code{cff} object. + +In the case of \code{\link[=cff_read_bib]{cff_read_bib()}} and \code{\link[=cff_read_citation]{cff_read_citation()}}, the result is +the \code{cff} version of a \code{\link[=bibentry]{bibentry()}} object (i.e. a bibliographic reference), +that can be used to complement another \code{cff} object. } \description{ Read files and convert them to \code{\link[=cff-class]{cff}} objects. Files supported @@ -62,3 +71,17 @@ we provide a series of alias for each specific type of file: \code{\link[bibtex:read.bib]{bibtex::read.bib()}}. } } +\details{ +Section 1.9 CITATION files of \emph{Writing R Extensions} (R Core Team 2023) +specifies how to create dynamic \code{CITATION} files using \code{meta} object, hence +the \code{meta} argument in \code{\link[=cff_read_citation]{cff_read_citation()}} may be needed for reading +some files correctly. +} +\examples{ +TODO + +} +\references{ +R Core Team (2023). \emph{Writing R Extensions}. +\url{https://cran.r-project.org/doc/manuals/r-release/R-exts.html} +} diff --git a/tests/testthat/_snaps/cff_read.md b/tests/testthat/_snaps/cff_read.md index 68b1787d..b6e4e4be 100644 --- a/tests/testthat/_snaps/cff_read.md +++ b/tests/testthat/_snaps/cff_read.md @@ -14,15 +14,31 @@ Error in `cff_read()`: ! 'abcde' does not exist. Check the '.' directory ---- +# cff_read citation.cff + + Code + cff_read_cff_citation("a") + Condition + Error in `cff_read_cff_citation()`: + ! 'a' does not exist. Check the '.' directory + +# cff_read DESCRIPTION + + Code + cff_read_description("a") + Condition + Error in `cff_read_description()`: + ! 'a' does not exist. Check the '.' directory + +# cff_read bib Code - cff_read(f) + cff_read_bib("a") Condition - Error in `guess_type_file()`: - ! Don't recognize the file type of '/cloud/project/inst/schema/schema.json'. Use a specific function (e.g. `cffr:cff_read_description()` + Error in `cff_read_bib()`: + ! 'a' does not exist. Check the '.' directory -# cff read bib +--- Code d @@ -44,3 +60,18 @@ notes: 'Publisher: The Open Journal' start: '3900' +# cff_read citation messages + + Code + cff_read_citation("a") + Condition + Error in `cff_read_citation()`: + ! 'a' does not exist. Check the '.' directory + +--- + + Code + s <- cff_read(f, meta = "aa") + Message + ! `meta` should be "NULL" or a object not a string. Using `meta = NULL` + diff --git a/tests/testthat/test-cff_read.R b/tests/testthat/test-cff_read.R index 2a5d3fc1..62407d3b 100644 --- a/tests/testthat/test-cff_read.R +++ b/tests/testthat/test-cff_read.R @@ -3,10 +3,12 @@ test_that("Test errors on cff_read", { expect_snapshot(cff_read("abcde"), error = TRUE) f <- system.file("schema/schema.json", package = "cffr") - expect_snapshot(cff_read(f), error = TRUE) + expect_error(cff_read(f), "Don't recognize the file type of") }) test_that("cff_read citation.cff", { + expect_snapshot(cff_read_cff_citation("a"), error = TRUE) + f <- system.file("examples/CITATION_complete.cff", package = "cffr") f1 <- cff_read(f) expect_true(cff_validate(f1, verbose = FALSE)) @@ -19,6 +21,9 @@ test_that("cff_read citation.cff", { test_that("cff_read DESCRIPTION", { + expect_snapshot(cff_read_description("a"), error = TRUE) + + f <- system.file("examples/DESCRIPTION_no_URL", package = "cffr") f1 <- cff_read(f, gh_keywords = FALSE) @@ -32,8 +37,8 @@ test_that("cff_read DESCRIPTION", { # Use other params f1_1 <- cff_read(f, - gh_keywords = FALSE, cff_version = 3000, - authors_roles = c("aut", "cre", "ctb") + gh_keywords = FALSE, cff_version = 3000, + authors_roles = c("aut", "cre", "ctb") ) expect_equal(f1_1$`cff-version`, "3000") @@ -41,17 +46,20 @@ test_that("cff_read DESCRIPTION", { expect_gt(length(f1_1$authors), length(f1$authors)) f2_1 <- cff_read_description(f, - gh_keywords = FALSE, cff_version = 3000, - authors_roles = c("aut", "cre", "ctb") + gh_keywords = FALSE, cff_version = 3000, + authors_roles = c("aut", "cre", "ctb") ) expect_identical(f1_1, f2_1) }) -test_that("cff read bib", { +test_that("cff_read bib", { skip_if_not_installed("bibtex", "0.5.0") + expect_snapshot(cff_read_bib("a"), error = TRUE) + + f <- system.file("REFERENCES.bib", package = "cffr") f1 <- cff_read(f) @@ -76,3 +84,99 @@ test_that("cff read bib", { f2_2 <- cff_read_bib(f) expect_identical(f1_2, f2_2) }) + +test_that("cff_read citation messages", { + expect_snapshot(cff_read_citation("a"), error = TRUE) + + + f <- system.file("examples/CITATION_basic", package = "cffr") + my_meta <- packageDescription("testthat") + + expect_snapshot(s <- cff_read(f, meta = "aa")) + expect_silent(s <- cff_read(f, meta = NULL)) + expect_silent(s <- cff_read(f, meta = my_meta)) + + # Now try with a file that needs meta + f <- system.file("examples/CITATION_auto", package = "cffr") + + expect_message(s <- cff_read(f), "Trying with") + expect_s3_class(s, c("cff", "list"), exact = TRUE) +}) + +test_that("cff_read CITATION_basic", { + a_desc <- system.file("examples/DESCRIPTION_basic", package = "cffr") + my_meta <- test_meta(a_desc) + + path <- system.file("examples/CITATION_basic", package = "cffr") + parsed <- cff_read(path, my_meta) + expect_s3_class(parsed, c("cff", "list"), exact = TRUE) + expect_equal(length(parsed), 2) + + # Identical to + meta <- as.list(read.dcf(a_desc)[1, ]) + meta$Encoding <- "UTF-8" + + id <- utils::readCitationFile(path, meta = meta) + + id <- cff_parse_citation(id) + id <- new_cff(id) + + expect_identical(parsed, id) +}) + +test_that("cff_read CITATION with no encoding", { + desc_path <- system.file("examples/DESCRIPTION_no_encoding", package = "cffr") + cit_path <- system.file("examples/CITATION_basic", package = "cffr") + + my_meta <- test_meta(desc_path) + parsed <- cff_read_citation(cit_path, my_meta) + + # Identical to + meta <- as.list(read.dcf(desc_path)[1, ]) + id <- utils::readCitationFile(cit_path, meta = meta) + + id <- cff_parse_citation(id) + id <- new_cff(id) + + expect_identical(parsed, id) +}) + +test_that("cff_read CITATION_auto", { + # Needs an installed package + desc_path <- system.file("examples/DESCRIPTION_rgeos", package = "cffr") + cit_path <- system.file("examples/CITATION_auto", package = "cffr") + my_meta <- test_meta(desc_path) + + parsed <- cff_read(cit_path, my_meta) + + expect_equal(length(parsed), 3) + + # Identical to + meta <- as.list(read.dcf(desc_path)[1, ]) + id <- utils::readCitationFile(cit_path, meta = meta) + id <- cff_parse_citation(id) + id <- new_cff(id) + + + expect_identical(parsed, id) +}) + +test_that("cff_read CITATION_rmarkdown", { + desc_path <- system.file("examples/DESCRIPTION_basic", package = "cffr") + cit_path <- system.file("examples/CITATION_rmarkdown", package = "cffr") + + my_meta <- test_meta(desc_path) + parsed <- cff_read(cit_path, my_meta) + + expect_equal(length(parsed), 3) + + # Identical to + meta <- as.list(read.dcf(desc_path)[1, ]) + meta$Encoding <- "UTF-8" + + id <- utils::readCitationFile(cit_path, meta = meta) + id <- cff_parse_citation(id) + id <- new_cff(id) + + expect_identical(parsed, id) +}) diff --git a/vignettes/tweet-1.png b/vignettes/tweet-1.png index 22dc92a4428b97de852dd402509f5f86bf9dc409..da3d6ddaf6c072c2497b000a561f58ae62a8ba69 100644 GIT binary patch literal 24193 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 literal 121117 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%0DOe)w>Bk)MXF}YqLtV)qq=twiCb4#*ZH2 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 Date: Mon, 19 Feb 2024 23:32:42 +0100 Subject: [PATCH 08/32] TODO examples --- R/cff_read.R | 2 +- man/cff_read.Rd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/R/cff_read.R b/R/cff_read.R index b84ff49f..552b0b63 100644 --- a/R/cff_read.R +++ b/R/cff_read.R @@ -53,7 +53,7 @@ #' some files correctly. #' #' @examples -#' TODO +#' # TODO #' cff_read <- function(path, ...) { if (length(path) > 1) { diff --git a/man/cff_read.Rd b/man/cff_read.Rd index 1a249263..6117a99e 100644 --- a/man/cff_read.Rd +++ b/man/cff_read.Rd @@ -78,7 +78,7 @@ the \code{meta} argument in \code{\link[=cff_read_citation]{cff_read_citation()} some files correctly. } \examples{ -TODO +# TODO } \references{ From f8673e9f19cf2292312ef9fc90cac6903388c0c0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 19 Feb 2024 23:04:02 +0000 Subject: [PATCH 09/32] revdepcheck --- revdep/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/revdep/README.md b/revdep/README.md index 4fdd6143..1226d486 100644 --- a/revdep/README.md +++ b/revdep/README.md @@ -10,7 +10,7 @@ |collate |en_US.UTF-8 | |ctype |en_US.UTF-8 | |tz |UTC | -|date |2024-02-16 | +|date |2024-02-19 | |pandoc |2.19.2 @ /usr/local/bin/pandoc | # Dependencies From d9021aff9ca3abe5185399a0b58a77ebbfc1c476 Mon Sep 17 00:00:00 2001 From: dieghernan Date: Tue, 20 Feb 2024 19:11:16 +0000 Subject: [PATCH 10/32] Move readers --- R/cff-methods.R | 46 ---------- R/cff_create.R | 4 +- R/cff_read.R | 109 ++++++++++++++++++++--- R/cff_to_bibentry.R | 2 +- R/parse_citation.R | 51 ----------- R/utils-methods.R | 48 ++++++++++ README.md | 54 +---------- codemeta.json | 4 +- data/cran_to_spdx.rda | Bin 916 -> 907 bytes inst/WORDLIST | 3 +- inst/schemaorg.json | 2 +- man/cff_read.Rd | 44 +++++++-- man/cff_to_bibentry.Rd | 2 +- tests/testthat/test-cff_parse_citation.R | 14 ++- tests/testthat/test-cff_read.R | 98 +++++++++++--------- tests/testthat/test-encoding.R | 12 +-- tests/testthat/test-merge_desc_cit.R | 5 +- tests/testthat/test-parse_citation.R | 77 ---------------- 18 files changed, 257 insertions(+), 318 deletions(-) delete mode 100644 tests/testthat/test-parse_citation.R diff --git a/R/cff-methods.R b/R/cff-methods.R index 766de4d6..e9f59eb0 100644 --- a/R/cff-methods.R +++ b/R/cff-methods.R @@ -79,49 +79,3 @@ head.cff <- function(x, n = 6L, ...) { tail.cff <- function(x, n = 6L, ...) { as.cff(NextMethod()) } - -make_r_person <- function(x) { - if (is.null(names(x))) { - return(person()) - } - checknames <- grepl("^name$|given-names|family-names", names(x)) - if (!isTRUE(any(checknames))) { - return(person()) - } - # Prepare list - # Family is special key - fam1 <- clean_str(x$name) - fam2 <- clean_str( - paste( - clean_str(x$`name-particle`), clean_str(x$`family-names`), - clean_str(x$`name-suffix`) - ) - ) - - given <- clean_str(x$`given-names`) - family <- clean_str(c(fam1, fam2)) - - # Make comments - x_comments <- x[!names(x) %in% c( - "family-names", "given-names", - "name-particle", "name-suffix", "email" - )] - - x_comments <- lapply(x_comments, clean_str) - x_comments <- unlist(x_comments, use.names = TRUE) - - # Prepare ORCID - x_comments <- gsub("^https://orcid.org/", "", x_comments) - nm <- gsub("orcid", "ORCID", names(x_comments), fixed = TRUE) - names(x_comments) <- nm - - pers_list <- list( - given = given, - family = family, - email = clean_str(x$email), - comment = x_comments - ) - - - do.call(person, pers_list) -} diff --git a/R/cff_create.R b/R/cff_create.R index d8965aad..0e0a8bcd 100644 --- a/R/cff_create.R +++ b/R/cff_create.R @@ -153,10 +153,10 @@ cff_create <- function(x, keys = list(), cff_version = "1.2.0", cit_path <- gsub("DESCRIPTION$", "CITATION", x) } if (file.exists(cit_path)) { - citobj <- parse_r_citation(desc_path, cit_path) - citobj <- lapply(citobj, cff_parse_citation) + citobj <- cff_safe_read_citation(desc_path, cit_path) if (length(citobj) == 0) citobj <- NULL citobj <- drop_null(citobj) + citobj <- unname(citobj) } } else { msg <- paste0( diff --git a/R/cff_read.R b/R/cff_read.R index 552b0b63..6149b604 100644 --- a/R/cff_read.R +++ b/R/cff_read.R @@ -29,31 +29,66 @@ #' @param encoding Encoding to be assumed for `path`. See [readLines()]. #' @param meta A list of package metadata as obtained by #' [utils::packageDescription()] or `NULL` (the default). See **Details**. -#' @param ... Arguments to be passed to other functions. +#' @param ... Arguments to be passed to other functions (i.e. to +#' [yaml::read_yaml()], [bibtex::read.bib()], etc.). #' #' @return #' A [`cff`][cff-class] object. In the case of [cff_read_cff_citation()] and #' [cff_read_description()] a full and (potentially) valid `cff` object. #' +#' #' In the case of [cff_read_bib()] and [cff_read_citation()], the result is #' the `cff` version of a [bibentry()] object (i.e. a bibliographic reference), -#' that can be used to complement another `cff` object. +#' that can be used to complement another `cff` object. See +#' `vignette("bibtex_cff", "cffr")` to get further insights on how this +#' conversion is performed. #' #' #' @references #' -#' R Core Team (2023). _Writing R Extensions_. -#' +#' - R Core Team (2023). _Writing R Extensions_. +#' +#' +#' - Hernangomez D (2022). "BibTeX and CFF, a potential crosswalk." +#' *The cffr package, Vignettes*. \doi{10.21105/joss.03900}, +#' . #' #' @details #' +#' # The `meta` object +#' #' Section 1.9 CITATION files of *Writing R Extensions* (R Core Team 2023) #' specifies how to create dynamic `CITATION` files using `meta` object, hence #' the `meta` argument in [cff_read_citation()] may be needed for reading #' some files correctly. #' #' @examples -#' # TODO +#' +#' # Create cff object from cff file +#' +#' from_cff_file <- cff_read(system.file("examples/CITATION_basic.cff", +#' package = "cffr" +#' )) +#' +#' head(from_cff_file, 7) +#' +#' # Create cff object from DESCRIPTION +#' from_desc <- cff_read(system.file("examples/DESCRIPTION_basic", package = "cffr")) +#' +#' from_desc +#' +#' # Create cff object from BibTex +#' +#' from_bib <- cff_read(system.file("examples/example.bib", package = "cffr")) +#' +#' # First item only +#' from_bib[[1]] +#' +#' # Create cff object from CITATION +#' from_citation <- cff_read(system.file("CITATION", package = "cffr")) +#' +#' # First item only +#' from_citation[[1]] #' cff_read <- function(path, ...) { if (length(path) > 1) { @@ -95,7 +130,7 @@ cff_read_cff_citation <- function(path, ...) { ) } - cffobj <- yaml::read_yaml(path) + cffobj <- yaml::read_yaml(path, ...) new_cff(cffobj) } @@ -199,8 +234,15 @@ cff_read_citation <- function(path, meta = NULL, ...) { } # nocov end } - tocff <- cff_parse_citation(the_cit) - new_cff(tocff) + tocff <- lapply(the_cit, cff_parse_citation) + make_names <- vapply(tocff, function(x) { + myname <- gsub("[^a-z]", "", tolower(x$title)) + substr(myname, 1, 10) + }, character(1)) + + names(tocff) <- make_names + tocff <- new_cff(tocff) + unname(tocff) } #' @export @@ -229,10 +271,42 @@ cff_read_bib <- function(path, encoding = "UTF-8", ...) { read_bib <- bibtex::read.bib(file = path, encoding = encoding, ...) - tocff <- cff_parse_citation(read_bib) - new_cff(tocff) + tocff <- lapply(read_bib, cff_parse_citation) + tocff <- new_cff(tocff) + unname(tocff) +} + +# Internal safe ---- +#' Internal version of cff_read_citation, safe +#' @noRd +cff_safe_read_citation <- function(desc_path, cit_path) { + if (!file.exists(cit_path) || !file.exists(desc_path)) { + return(NULL) + } + # Create meta + meta <- desc_to_meta(desc_path) + meta <- clean_package_meta(meta) + + + the_cit <- try(utils::readCitationFile(cit_path, meta = meta), silent = TRUE) + # Try + if (inherits(the_cit, "try-error")) { + return(NULL) + } + + # Need to be named here + tocff <- lapply(the_cit, cff_parse_citation) + make_names <- vapply(tocff, function(x) { + myname <- gsub("[^a-z]", "", tolower(x$title)) + substr(myname, 1, 10) + }, character(1)) + + names(tocff) <- make_names + tocff <- new_cff(tocff) + unname(tocff) } +# Helpers ---- guess_type_file <- function(path) { if (grepl("\\.cff$", path, ignore.case = TRUE)) { @@ -260,7 +334,10 @@ guess_type_file <- function(path) { #' @noRd clean_package_meta <- function(meta) { if (!inherits(meta, "packageDescription")) { - return(NULL) + # Add encoding + meta <- list() + meta$Encoding <- "UTF-8" + return(meta) } # Convert to a desc object @@ -287,17 +364,21 @@ clean_package_meta <- function(meta) { meta } -# For testing, packageDescription object from desc -test_meta <- function(x) { + + +# Convert a DESCRIPTION object to meta object using desc package +desc_to_meta <- function(x) { src <- x my_meta <- desc::desc(src) + my_meta$coerce_authors_at_r() + # As list my_meta_l <- my_meta$get(desc::cran_valid_fields) my_meta_l <- as.list(my_meta_l) v_nas <- vapply(my_meta_l, is.na, logical(1)) - my_meta_l <- my_meta_l[!v_nas] + meta_proto <- packageDescription("cffr") class(my_meta_l) <- class(meta_proto) diff --git a/R/cff_to_bibentry.R b/R/cff_to_bibentry.R index 0f8e344e..079d5ed5 100644 --- a/R/cff_to_bibentry.R +++ b/R/cff_to_bibentry.R @@ -20,7 +20,7 @@ #' *Ruby CFF Library (Version 0.9.0)* (Computer software). #' \doi{10.5281/zenodo.1184077}. #' -#' - Hernangómez D (2022). "BibTeX and CFF, a potential crosswalk." +#' - Hernangomez D (2022). "BibTeX and CFF, a potential crosswalk." #' *The cffr package, Vignettes*. \doi{10.21105/joss.03900}, #' . #' diff --git a/R/parse_citation.R b/R/parse_citation.R index 87d0d65b..8f841147 100644 --- a/R/parse_citation.R +++ b/R/parse_citation.R @@ -1,54 +1,3 @@ -## Parsers ---- - -#' Used for parsing CITATION R-native files -#' @noRd -parse_r_citation <- function(desc_path, cit_path) { - if (!file.exists(cit_path) || !file.exists(desc_path)) { - return(NULL) - } - # Create meta - meta <- parse_package_meta(desc_path) - - # First try - Would normally be enough - parsed <- tryCatch( - utils::readCitationFile(cit_path, meta = meta), - warning = function(cit_path, meta) { - # Avoid warnings - # nocov start - suppressWarnings( - utils::readCitationFile(cit_path, meta = meta) - ) - }, - error = function(x) { - return(NULL) - } - # nocov end - ) - - parsed -} - -#' Parse and clean data from DESCRIPTION to create metadata -#' @noRd -parse_package_meta <- function(desc_path) { - pkg <- desc::desc(desc_path) - pkg$coerce_authors_at_r() - # Extract package data - meta <- pkg$get(desc::cran_valid_fields) - - # Clean missing and drop empty fields - meta <- drop_null(lapply(meta, clean_str)) - - # Check encoding - if (!is.null(meta$Encoding)) { - meta <- lapply(meta, iconv, from = meta$Encoding, to = "UTF-8") - } else { - meta$Encoding <- "UTF-8" - } - - meta -} - ## Building blocks ---- #' BB for doi diff --git a/R/utils-methods.R b/R/utils-methods.R index b783d89c..41434b1e 100644 --- a/R/utils-methods.R +++ b/R/utils-methods.R @@ -1,3 +1,51 @@ +# Utils for authors---- +make_r_person <- function(x) { + if (is.null(names(x))) { + return(person()) + } + checknames <- grepl("^name$|given-names|family-names", names(x)) + if (!isTRUE(any(checknames))) { + return(person()) + } + # Prepare list + # Family is special key + fam1 <- clean_str(x$name) + fam2 <- clean_str( + paste( + clean_str(x$`name-particle`), clean_str(x$`family-names`), + clean_str(x$`name-suffix`) + ) + ) + + given <- clean_str(x$`given-names`) + family <- clean_str(c(fam1, fam2)) + + # Make comments + x_comments <- x[!names(x) %in% c( + "family-names", "given-names", + "name-particle", "name-suffix", "email" + )] + + x_comments <- lapply(x_comments, clean_str) + x_comments <- unlist(x_comments, use.names = TRUE) + + # Prepare ORCID + x_comments <- gsub("^https://orcid.org/", "", x_comments) + nm <- gsub("orcid", "ORCID", names(x_comments), fixed = TRUE) + names(x_comments) <- nm + + pers_list <- list( + given = given, + family = family, + email = clean_str(x$email), + comment = x_comments + ) + + + do.call(person, pers_list) +} + + # Utils for df ---- unnamed_to_df <- function(key, nm) { key_l <- as.integer(lengths(key)) diff --git a/README.md b/README.md index abcd59d0..1cfe3b25 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ file and the `CITATION` file (if present) of your package. Note that **cffr** works best if your package pass `R CMD check/devtools::check()`. -As per 2024-02-19 there are at least 261 repos on GitHub using **cffr**. +As per 2024-02-20 there are at least 267 repos on GitHub using **cffr**. [Check them out here](https://github.com/search?q=cffr%20path%3A**%2FCITATION.cff&type=code). @@ -560,25 +560,6 @@ test <- cff_create("rmarkdown") - family-names: Chirico. given-names: Michael year: '2024' - - type: software - title: dygraphs - abstract: 'dygraphs: Interface to ''Dygraphs'' Interactive Time Series Charting - Library' - notes: Suggests - url: https://github.com/rstudio/dygraphs - repository: https://CRAN.R-project.org/package=dygraphs - authors: - - family-names: Vanderkam - given-names: Dan - - family-names: Allaire - given-names: JJ - - family-names: Owen - given-names: Jonathan - - family-names: Gromer - given-names: Daniel - - family-names: Thieurmel - given-names: Benoit - year: '2024' - type: software title: fs abstract: 'fs: Cross-Platform File System Operations Based on ''libuv''' @@ -595,26 +576,6 @@ test <- cff_create("rmarkdown") given-names: Gábor email: csardi.gabor@gmail.com year: '2024' - - type: software - title: rsconnect - abstract: 'rsconnect: Deploy Docs, Apps, and APIs to ''Posit Connect'', ''shinyapps.io'', - and ''RPubs''' - notes: Suggests - url: https://rstudio.github.io/rsconnect/ - repository: https://CRAN.R-project.org/package=rsconnect - authors: - - family-names: Atkins - given-names: Aron - email: aron@posit.co - - family-names: Allen - given-names: Toph - - family-names: Wickham - given-names: Hadley - - family-names: McPherson - given-names: Jonathan - - family-names: Allaire - given-names: JJ - year: '2024' - type: software title: downlit abstract: 'downlit: Syntax Highlighting and Automatic Linking' @@ -627,19 +588,6 @@ test <- cff_create("rmarkdown") email: hadley@posit.co year: '2024' version: '>= 0.4.0' - - type: software - title: katex - abstract: 'katex: Rendering Math to HTML, ''MathML'', or R-Documentation Format' - notes: Suggests - url: https://docs.ropensci.org/katex/ - repository: https://CRAN.R-project.org/package=katex - authors: - - family-names: Ooms - given-names: Jeroen - email: jeroen@berkeley.edu - orcid: https://orcid.org/0000-0002-4035-0289 - year: '2024' - version: '>= 1.4.0' - type: software title: sass abstract: 'sass: Syntactically Awesome Style Sheets (''Sass'')' diff --git a/codemeta.json b/codemeta.json index fe427c25..e4c9866b 100644 --- a/codemeta.json +++ b/codemeta.json @@ -14,7 +14,7 @@ "name": "R", "url": "https://r-project.org" }, - "runtimePlatform": "R version 4.3.2 (2023-10-31 ucrt)", + "runtimePlatform": "R version 4.3.2 (2023-10-31)", "provider": { "@id": "https://cran.r-project.org", "@type": "Organization", @@ -200,7 +200,7 @@ }, "isPartOf": "https://ropensci.org", "keywords": ["attribution", "citation", "credit", "citation-files", "cff", "metadata", "r", "r-package", "citation-file-format", "rstats", "ropensci", "cran"], - "fileSize": "918.861KB", + "fileSize": "896.38KB", "citation": [ { "@type": "ScholarlyArticle", diff --git a/data/cran_to_spdx.rda b/data/cran_to_spdx.rda index 0bd1435cae708e762b962cb7a97ec9ae0c12fef9..33fae2994ac0070f09336d3d8c403ec099798a1f 100644 GIT binary patch literal 907 zcmV;619bdCT4*^jL0KkKS>9zRc>o3)f6)K`|B!TNe+NIOUueH)-|#>H0ssI3&;$PH zMJcLQXdxn$LqY0#n*AnjWXMA%O-!8VroodWVq>8hVWZpyrJl8ess_0%@QOhK3*w7>x`OkO2~jFe$2d zXwawXnjVu)lzN&O4WcqVOllbqQxUPLFe>dz=&r?fZ9@1WKjajLMimfmJ2mUYPPPXx zj*Ybg1&%$?qNxPUhn5dz<$F-rN4=W12TeQiUlnt8<0b6Yj7qp-b#>kRV}V z`Fj%x=Ccapm4j`8K(v)wkqAVgQwwED3edK<)|qCdvuV!(PmL8wD1-^BC2K`h&j3QP zL0u36=!%xXc0(x;6=KLKJiY@Gylf7R8cZa*UN&lz^7{|Bd%yVmS_cO2<5+P%UUOvX zSz@tbnf*NMPJ>Nh!Uq@-^UcwV*1cylE@5sg3T+_5-MXcG?wWWD;2vA3o z&8+4_l-WR}LCwk=!sx_DBpAAMWvtJbNN+NfJb=(2mY@It literal 916 zcmV;F18e*riwFP!000002F+JXPuoBccH=-unm|gSm3lH&4Vna`W6qYoKWTU7l z6%~ibghjRFILLPBCXZ^{hliR@wmg?9cUwC}dynSrJ>vGR3u>%N zH7^9s2jmCN)MFU+jvqQf=!^lyHV7uMxN$r@(R46&?O7nkAL|Ucu8wsheU#`}%-}Kg zct}p6f|sD3J$Vg6?f4@&g<#mx_GkbE&SM;}8efM{{dWsb-+YHfc*I(skDzQMQuc9 z1!Aohx0C}Wz@61{kAy z&Yir|26bnZ!6Z%^)E>i>qx;N-Fqt-QvgfmsNZIvBwAm0ab2)67k|eIP;A`!G#>mpM z^iZ=B9+QksbB1dyET}lE|DM>xOF{e0ntpR!*DkoCa;-+&L=l6|gqH=3{IpT6Ecq3S zYeuoij3tXyK2yb3u8OVv0aVXC*O@pWx)Nr+Lh>6AcYHg*)N{XbpuvMrDCh&%#!Z-b zl8jZht*40o7ZBOVmSVVL106tdg;alC4R?J;u`wz#?3 zjfZj`(dQt-@SQK_UFh<3UL@mGl-u{nq~C0?X$Sm-txHN&hBcFxl`GI@LE+XjzF=3V z{08ZT_c^er!9v~w*h7PCW`0WWB0K*ZUKH0qz)3MkD`ZhnY^`QN@PAw(38+v}F}WMf z%vENY4z3n27R)i3_&a+GMMZe~UGnS+3epi+%Hr$8?&+{g{tFEUC+Ffm{5BSleFUss q*`d9A?PHSyz3NXt?~+F~qDOtlUw3zRKk(1b=<^FCJhi~74*&oy4!s@# diff --git a/inst/WORDLIST b/inst/WORDLIST index 62027bb1..9e3ff374 100644 --- a/inst/WORDLIST +++ b/inst/WORDLIST @@ -20,6 +20,7 @@ FOSS Fenner GitLab Haines +Hernangomez InBook InCollection InProceedings @@ -81,10 +82,8 @@ incollection inproceedings json jsonvalidate -mastersthesis param parsers -phdthesis plaintext pre rOpenSci diff --git a/inst/schemaorg.json b/inst/schemaorg.json index dec7a9c6..8056af78 100644 --- a/inst/schemaorg.json +++ b/inst/schemaorg.json @@ -26,6 +26,6 @@ "name": "Comprehensive R Archive Network (CRAN)", "url": "https://cran.r-project.org" }, - "runtimePlatform": "R version 4.3.2 (2023-10-31 ucrt)", + "runtimePlatform": "R version 4.3.2 (2023-10-31)", "version": "0.5.0.9000" } diff --git a/man/cff_read.Rd b/man/cff_read.Rd index 6117a99e..ef8abc85 100644 --- a/man/cff_read.Rd +++ b/man/cff_read.Rd @@ -27,7 +27,8 @@ cff_read_bib(path, encoding = "UTF-8", ...) \arguments{ \item{path}{Path to a file} -\item{...}{Arguments to be passed to other functions.} +\item{...}{Arguments to be passed to other functions (i.e. to +\code{\link[yaml:read_yaml]{yaml::read_yaml()}}, \code{\link[bibtex:read.bib]{bibtex::read.bib()}}, etc.).} \item{cff_version}{The Citation File Format schema version that the \code{CITATION.cff} file adheres to for providing the citation metadata.} @@ -49,7 +50,9 @@ A \code{\link[=cff-class]{cff}} object. In the case of \code{\link[=cff_read_cff In the case of \code{\link[=cff_read_bib]{cff_read_bib()}} and \code{\link[=cff_read_citation]{cff_read_citation()}}, the result is the \code{cff} version of a \code{\link[=bibentry]{bibentry()}} object (i.e. a bibliographic reference), -that can be used to complement another \code{cff} object. +that can be used to complement another \code{cff} object. See +\code{vignette("bibtex_cff", "cffr")} to get further insights on how this +conversion is performed. } \description{ Read files and convert them to \code{\link[=cff-class]{cff}} objects. Files supported @@ -71,17 +74,48 @@ we provide a series of alias for each specific type of file: \code{\link[bibtex:read.bib]{bibtex::read.bib()}}. } } -\details{ +\section{The \code{meta} object}{ Section 1.9 CITATION files of \emph{Writing R Extensions} (R Core Team 2023) specifies how to create dynamic \code{CITATION} files using \code{meta} object, hence the \code{meta} argument in \code{\link[=cff_read_citation]{cff_read_citation()}} may be needed for reading some files correctly. } + \examples{ -# TODO + +# Create cff object from cff file + +from_cff_file <- cff_read(system.file("examples/CITATION_basic.cff", + package = "cffr" +)) + +head(from_cff_file, 7) + +# Create cff object from DESCRIPTION +from_desc <- cff_read(system.file("examples/DESCRIPTION_basic", package = "cffr")) + +from_desc + +# Create cff object from BibTex + +from_bib <- cff_read(system.file("examples/example.bib", package = "cffr")) + +# First item only +from_bib[[1]] + +# Create cff object from CITATION +from_citation <- cff_read(system.file("CITATION", package = "cffr")) + +# First item only +from_citation[[1]] } \references{ -R Core Team (2023). \emph{Writing R Extensions}. +\itemize{ +\item R Core Team (2023). \emph{Writing R Extensions}. \url{https://cran.r-project.org/doc/manuals/r-release/R-exts.html} +\item Hernangomez D (2022). "BibTeX and CFF, a potential crosswalk." +\emph{The cffr package, Vignettes}. \doi{10.21105/joss.03900}, +\url{https://docs.ropensci.org/cffr/articles/bibtex_cff.html}. +} } diff --git a/man/cff_to_bibentry.Rd b/man/cff_to_bibentry.Rd index c1c64b0d..4864e01d 100644 --- a/man/cff_to_bibentry.Rd +++ b/man/cff_to_bibentry.Rd @@ -88,7 +88,7 @@ toBibtex(desc_file) \item Haines, R., & The Ruby Citation File Format Developers. (2021). \emph{Ruby CFF Library (Version 0.9.0)} (Computer software). \doi{10.5281/zenodo.1184077}. -\item Hernangómez D (2022). "BibTeX and CFF, a potential crosswalk." +\item Hernangomez D (2022). "BibTeX and CFF, a potential crosswalk." \emph{The cffr package, Vignettes}. \doi{10.21105/joss.03900}, \url{https://docs.ropensci.org/cffr/articles/bibtex_cff.html}. } diff --git a/tests/testthat/test-cff_parse_citation.R b/tests/testthat/test-cff_parse_citation.R index 096d1487..ab9739bc 100644 --- a/tests/testthat/test-cff_parse_citation.R +++ b/tests/testthat/test-cff_parse_citation.R @@ -14,12 +14,12 @@ test_that("Test full with CITATION and (option = author)", { # Needs an installed package desc_path <- system.file("examples/DESCRIPTION_rgeos", package = "cffr") cit_path <- system.file("examples/CITATION_auto", package = "cffr") - parsed <- parse_r_citation(desc_path, cit_path) - expect_s3_class(parsed, "bibentry") + parsed <- cff_safe_read_citation(desc_path, cit_path) + expect_s3_class(parsed, "cff") # Create cff cffobj <- cff_create(desc_path, keys = list( - references = lapply(parsed, cff_parse_citation) + references = parsed )) expect_s3_class(cffobj, "cff") @@ -32,13 +32,9 @@ test_that("Parsed several citations", { # Needs an installed package desc_path <- system.file("examples/DESCRIPTION_rgeos", package = "cffr") cit_path <- system.file("examples/CITATION_auto", package = "cffr") - parsed <- parse_r_citation(desc_path, cit_path) - expect_s3_class(parsed, "bibentry") + citobj <- cff_safe_read_citation(desc_path, cit_path) + expect_s3_class(citobj, c("cff", "list"), exact = TRUE) - # Create citation obj - citobj <- cff_parse_citation(parsed) - - expect_s3_class(citobj, "cff") expect_snapshot(citobj) expect_length(citobj, 3) }) diff --git a/tests/testthat/test-cff_read.R b/tests/testthat/test-cff_read.R index 62407d3b..f4e1c9c6 100644 --- a/tests/testthat/test-cff_read.R +++ b/tests/testthat/test-cff_read.R @@ -37,8 +37,8 @@ test_that("cff_read DESCRIPTION", { # Use other params f1_1 <- cff_read(f, - gh_keywords = FALSE, cff_version = 3000, - authors_roles = c("aut", "cre", "ctb") + gh_keywords = FALSE, cff_version = 3000, + authors_roles = c("aut", "cre", "ctb") ) expect_equal(f1_1$`cff-version`, "3000") @@ -46,8 +46,8 @@ test_that("cff_read DESCRIPTION", { expect_gt(length(f1_1$authors), length(f1$authors)) f2_1 <- cff_read_description(f, - gh_keywords = FALSE, cff_version = 3000, - authors_roles = c("aut", "cre", "ctb") + gh_keywords = FALSE, cff_version = 3000, + authors_roles = c("aut", "cre", "ctb") ) expect_identical(f1_1, f2_1) @@ -105,78 +105,90 @@ test_that("cff_read citation messages", { test_that("cff_read CITATION_basic", { a_desc <- system.file("examples/DESCRIPTION_basic", package = "cffr") - my_meta <- test_meta(a_desc) + my_meta <- desc_to_meta(a_desc) path <- system.file("examples/CITATION_basic", package = "cffr") parsed <- cff_read(path, my_meta) expect_s3_class(parsed, c("cff", "list"), exact = TRUE) expect_equal(length(parsed), 2) - - # Identical to - meta <- as.list(read.dcf(a_desc)[1, ]) - meta$Encoding <- "UTF-8" - - id <- utils::readCitationFile(path, meta = meta) - - id <- cff_parse_citation(id) - id <- new_cff(id) - - expect_identical(parsed, id) }) test_that("cff_read CITATION with no encoding", { desc_path <- system.file("examples/DESCRIPTION_no_encoding", package = "cffr") cit_path <- system.file("examples/CITATION_basic", package = "cffr") - my_meta <- test_meta(desc_path) + my_meta <- desc_to_meta(desc_path) parsed <- cff_read_citation(cit_path, my_meta) - - # Identical to - meta <- as.list(read.dcf(desc_path)[1, ]) - id <- utils::readCitationFile(cit_path, meta = meta) - - id <- cff_parse_citation(id) - id <- new_cff(id) - - expect_identical(parsed, id) + expect_s3_class(parsed, c("cff", "list"), exact = TRUE) }) test_that("cff_read CITATION_auto", { # Needs an installed package desc_path <- system.file("examples/DESCRIPTION_rgeos", package = "cffr") cit_path <- system.file("examples/CITATION_auto", package = "cffr") - my_meta <- test_meta(desc_path) + my_meta <- desc_to_meta(desc_path) parsed <- cff_read(cit_path, my_meta) expect_equal(length(parsed), 3) +}) - # Identical to - meta <- as.list(read.dcf(desc_path)[1, ]) - id <- utils::readCitationFile(cit_path, meta = meta) - id <- cff_parse_citation(id) - id <- new_cff(id) +test_that("cff_read CITATION_rmarkdown", { + desc_path <- system.file("examples/DESCRIPTION_basic", package = "cffr") + cit_path <- system.file("examples/CITATION_rmarkdown", package = "cffr") + my_meta <- desc_to_meta(desc_path) + parsed <- cff_read(cit_path, my_meta) - expect_identical(parsed, id) + expect_equal(length(parsed), 3) }) -test_that("cff_read CITATION_rmarkdown", { +test_that("cff_read_safe CITATION_basic", { + desc_path <- system.file("examples/DESCRIPTION_basic", package = "cffr") + cit_path <- system.file("examples/CITATION_basic", package = "cffr") + parsed <- cff_safe_read_citation(desc_path, cit_path) + + expect_s3_class(parsed, c("cff", "list"), exact = TRUE) + expect_equal(length(parsed), 2) +}) + +test_that("cff_read_safe CITATION with no encoding", { + desc_path <- system.file("examples/DESCRIPTION_no_encoding", package = "cffr") + cit_path <- system.file("examples/CITATION_basic", package = "cffr") + + parsed <- cff_safe_read_citation(desc_path, cit_path) + + expect_s3_class(parsed, c("cff", "list"), exact = TRUE) + expect_equal(length(parsed), 2) +}) + +test_that("cff_read_safe CITATION_auto", { + # Needs an installed package + desc_path <- system.file("examples/DESCRIPTION_rgeos", package = "cffr") + cit_path <- system.file("examples/CITATION_auto", package = "cffr") + parsed <- cff_safe_read_citation(desc_path, cit_path) + + expect_s3_class(parsed, c("cff", "list"), exact = TRUE) + expect_equal(length(parsed), 3) +}) + +test_that("cff_read_safe CITATION_rmarkdown", { desc_path <- system.file("examples/DESCRIPTION_basic", package = "cffr") cit_path <- system.file("examples/CITATION_rmarkdown", package = "cffr") - my_meta <- test_meta(desc_path) - parsed <- cff_read(cit_path, my_meta) + parsed <- cff_safe_read_citation(desc_path, cit_path) + expect_s3_class(parsed, c("cff", "list"), exact = TRUE) expect_equal(length(parsed), 3) +}) - # Identical to - meta <- as.list(read.dcf(desc_path)[1, ]) - meta$Encoding <- "UTF-8" - id <- utils::readCitationFile(cit_path, meta = meta) - id <- cff_parse_citation(id) - id <- new_cff(id) +test_that("cff_read_safe CITATION NULL", { + desc_path <- system.file("x", package = "cffr") + cit_path <- system.file("y", package = "cffr") - expect_identical(parsed, id) + expect_null(cff_safe_read_citation( + desc_path, + cit_path + )) }) diff --git a/tests/testthat/test-encoding.R b/tests/testthat/test-encoding.R index 050c3290..243f7b49 100644 --- a/tests/testthat/test-encoding.R +++ b/tests/testthat/test-encoding.R @@ -8,18 +8,14 @@ test_that("Creating cff from packages encoded in latin1", { expect_true(desc::desc(desc_path)$get("Encoding") == "latin1") # Parse citation - bib <- parse_r_citation(desc_path, cit_path) - expect_false("UTF-8" %in% Encoding(unlist(bib))) + bib <- cff_safe_read_citation(desc_path, cit_path) - # Parse to cff citation - bibparsed <- lapply(bib, cff_parse_citation) - - expect_true("UTF-8" %in% Encoding(unlist(bibparsed))) - expect_false("latin1" %in% Encoding(unlist(bibparsed))) + expect_true("UTF-8" %in% Encoding(unlist(bib))) + expect_false("latin1" %in% Encoding(unlist(bib))) # Create cff cffobj <- cff_create(desc_path, keys = list( - references = bibparsed + references = bib )) expect_s3_class(cffobj, "cff") diff --git a/tests/testthat/test-merge_desc_cit.R b/tests/testthat/test-merge_desc_cit.R index db9b29e1..d38bd96f 100644 --- a/tests/testthat/test-merge_desc_cit.R +++ b/tests/testthat/test-merge_desc_cit.R @@ -9,9 +9,8 @@ test_that("Merge all DESCRIPTION files with CITATION_basic", { ) for (i in seq_len(length(allfiles))) { desc_parse <- cff_read_description(allfiles[i], gh_keywords = FALSE) - generate_cit <- parse_r_citation(allfiles[i], citpath) - parse_cit <- lapply(generate_cit, cff_parse_citation) - merged <- merge_desc_cit(desc_parse, parse_cit) + generate_cit <- cff_safe_read_citation(allfiles[i], citpath) + merged <- merge_desc_cit(desc_parse, generate_cit) merged <- as.cff(merged) expect_snapshot(merged) diff --git a/tests/testthat/test-parse_citation.R b/tests/testthat/test-parse_citation.R deleted file mode 100644 index 8ff06fea..00000000 --- a/tests/testthat/test-parse_citation.R +++ /dev/null @@ -1,77 +0,0 @@ -test_that("Parse CITATION_basic", { - desc_path <- system.file("examples/DESCRIPTION_basic", package = "cffr") - cit_path <- system.file("examples/CITATION_basic", package = "cffr") - parsed <- parse_r_citation(desc_path, cit_path) - - expect_s3_class(parsed, "citation") - expect_equal(length(parsed), 2) - - # Identical to - meta <- as.list(read.dcf(desc_path)[1, ]) - meta$Encoding <- "UTF-8" - - id <- utils::readCitationFile(cit_path, meta = meta) - - expect_identical(parsed, id) -}) - -test_that("Parse CITATION with no encoding", { - desc_path <- system.file("examples/DESCRIPTION_no_encoding", package = "cffr") - cit_path <- system.file("examples/CITATION_basic", package = "cffr") - - parsed <- parse_r_citation(desc_path, cit_path) - - expect_s3_class(parsed, "citation") - expect_equal(length(parsed), 2) - - # Identical to - meta <- as.list(read.dcf(desc_path)[1, ]) - id <- utils::readCitationFile(cit_path, meta = meta) - - expect_identical(parsed, id) -}) - -test_that("Parse CITATION_auto", { - # Needs an installed package - desc_path <- system.file("examples/DESCRIPTION_rgeos", package = "cffr") - cit_path <- system.file("examples/CITATION_auto", package = "cffr") - parsed <- parse_r_citation(desc_path, cit_path) - - expect_s3_class(parsed, "citation") - expect_equal(length(parsed), 3) - - # Identical to - meta <- as.list(read.dcf(desc_path)[1, ]) - id <- utils::readCitationFile(cit_path, meta = meta) - - expect_identical(parsed, id) -}) - -test_that("Parse CITATION_rmarkdown", { - desc_path <- system.file("examples/DESCRIPTION_basic", package = "cffr") - cit_path <- system.file("examples/CITATION_rmarkdown", package = "cffr") - - parsed <- parse_r_citation(desc_path, cit_path) - - expect_s3_class(parsed, "citation") - expect_equal(length(parsed), 3) - - # Identical to - meta <- as.list(read.dcf(desc_path)[1, ]) - meta$Encoding <- "UTF-8" - - id <- utils::readCitationFile(cit_path, meta = meta) - - expect_identical(parsed, id) -}) - - -test_that("Parse CITATION NULL", { - desc_path <- system.file("x", package = "cffr") - cit_path <- system.file("y", package = "cffr") - - expect_null(parse_r_citation( - desc_path, - cit_path - )) -}) From 40002b603e74e206c943aeaf3ac12a1b133595bc Mon Sep 17 00:00:00 2001 From: Diego H Date: Tue, 20 Feb 2024 22:00:23 +0100 Subject: [PATCH 11/32] Handle DOI and corner case for VGAM --- R/cff_parse_citation.R | 2 ++ R/utils-read-description.R | 3 +++ tests/testthat/_snaps/encoding.md | 27 +++++++++++++------------ tests/testthat/_snaps/merge_desc_cit.md | 27 +++++++++++++------------ 4 files changed, 33 insertions(+), 26 deletions(-) diff --git a/R/cff_parse_citation.R b/R/cff_parse_citation.R index c7fccc55..5a4ab9d1 100644 --- a/R/cff_parse_citation.R +++ b/R/cff_parse_citation.R @@ -100,6 +100,8 @@ cff_parse_citation <- function(bib) { # Parse BibTeX fields ---- parsed_fields <- parse_bibtex_fields(parse_cit) + # VGAM: title is a vector + parsed_fields$title <- clean_str(parsed_fields$title) ## Handle collection types ---- parsed_fields <- add_bibtex_coltype(parsed_fields) diff --git a/R/utils-read-description.R b/R/utils-read-description.R index d26b4e51..1d9935f1 100644 --- a/R/utils-read-description.R +++ b/R/utils-read-description.R @@ -8,6 +8,9 @@ parse_desc_abstract <- function(pkg) { abstract <- clean_str(abstract) abstract <- unname(abstract) + # Convert doi to url + abstract <- gsub(". + negative binomial GLR-CUSUM method of Höhle and Paul (2008) . A novel CUSUM approach combining logistic and multinomial logistic modeling is also included. The package contains several real-world data sets, the ability to simulate outbreak data, and to visualize the results of the monitoring in a temporal, spatial or spatio-temporal fashion. A recent overview of the available monitoring procedures - is given by Salmon et al. (2016) . For the retrospective - analysis of epidemic spread, the package provides three endemic-epidemic modeling - frameworks with tools for visualization, likelihood inference, and simulation. hhh4() - estimates models for (multivariate) count time series following Paul and Held (2011) - and Meyer and Held (2014) . twinSIR() - models the susceptible-infectious-recovered (SIR) event history of a fixed population, - e.g, epidemics across farms or networks, as a multivariate point process as proposed - by Höhle (2009) . twinstim() estimates self-exciting - point process models for a spatio-temporal point pattern of infective events, e.g., - time-stamped geo-referenced surveillance data, as proposed by Meyer et al. (2012) - . A recent overview of the implemented space-time - modeling frameworks for epidemic phenomena is given by Meyer et al. (2017) . + is given by Salmon et al. (2016) . For the + retrospective analysis of epidemic spread, the package provides three endemic-epidemic + modeling frameworks with tools for visualization, likelihood inference, and simulation. + hhh4() estimates models for (multivariate) count time series following Paul and + Held (2011) and Meyer and Held (2014) . + twinSIR() models the susceptible-infectious-recovered (SIR) event history of a fixed + population, e.g, epidemics across farms or networks, as a multivariate point process + as proposed by Höhle (2009) . twinstim() + estimates self-exciting point process models for a spatio-temporal point pattern + of infective events, e.g., time-stamped geo-referenced surveillance data, as proposed + by Meyer et al. (2012) . A recent + overview of the implemented space-time modeling frameworks for epidemic phenomena + is given by Meyer et al. (2017) . authors: - family-names: Höhle given-names: Michael diff --git a/tests/testthat/_snaps/merge_desc_cit.md b/tests/testthat/_snaps/merge_desc_cit.md index 4476fa32..28dc3cec 100644 --- a/tests/testthat/_snaps/merge_desc_cit.md +++ b/tests/testthat/_snaps/merge_desc_cit.md @@ -806,23 +806,24 @@ diseases, but applications could just as well originate from environmetrics, reliability engineering, econometrics, or social sciences. The package implements many typical outbreak detection procedures such as the (improved) Farrington algorithm, or the - negative binomial GLR-CUSUM method of Höhle and Paul (2008) . + negative binomial GLR-CUSUM method of Höhle and Paul (2008) . A novel CUSUM approach combining logistic and multinomial logistic modeling is also included. The package contains several real-world data sets, the ability to simulate outbreak data, and to visualize the results of the monitoring in a temporal, spatial or spatio-temporal fashion. A recent overview of the available monitoring procedures - is given by Salmon et al. (2016) . For the retrospective - analysis of epidemic spread, the package provides three endemic-epidemic modeling - frameworks with tools for visualization, likelihood inference, and simulation. hhh4() - estimates models for (multivariate) count time series following Paul and Held (2011) - and Meyer and Held (2014) . twinSIR() - models the susceptible-infectious-recovered (SIR) event history of a fixed population, - e.g, epidemics across farms or networks, as a multivariate point process as proposed - by Höhle (2009) . twinstim() estimates self-exciting - point process models for a spatio-temporal point pattern of infective events, e.g., - time-stamped geo-referenced surveillance data, as proposed by Meyer et al. (2012) - . A recent overview of the implemented space-time - modeling frameworks for epidemic phenomena is given by Meyer et al. (2017) . + is given by Salmon et al. (2016) . For the + retrospective analysis of epidemic spread, the package provides three endemic-epidemic + modeling frameworks with tools for visualization, likelihood inference, and simulation. + hhh4() estimates models for (multivariate) count time series following Paul and + Held (2011) and Meyer and Held (2014) . + twinSIR() models the susceptible-infectious-recovered (SIR) event history of a fixed + population, e.g, epidemics across farms or networks, as a multivariate point process + as proposed by Höhle (2009) . twinstim() + estimates self-exciting point process models for a spatio-temporal point pattern + of infective events, e.g., time-stamped geo-referenced surveillance data, as proposed + by Meyer et al. (2012) . A recent + overview of the implemented space-time modeling frameworks for epidemic phenomena + is given by Meyer et al. (2017) . repository: https://CRAN.R-project.org/package=surveillance url: https://surveillance.R-Forge.R-project.org/ date-released: '2021-03-30' From 2d6b569f8724836f87f574b25f080fcf90fc4ba4 Mon Sep 17 00:00:00 2001 From: Diego H Date: Mon, 26 Feb 2024 23:39:38 +0100 Subject: [PATCH 12/32] New toBibtex.cff method --- CITATION.cff | 6 +- NAMESPACE | 2 + NEWS.md | 7 +- R/cff-methods.R | 44 ++++++++ R/cff.R | 2 +- R/cff_create.R | 2 +- R/cff_from_bibtex.R | 94 ---------------- R/cff_gha_update.R | 2 +- R/cff_git_hook.R | 2 +- R/cff_parse_citation.R | 2 +- R/cff_parse_person.R | 2 +- R/cff_read.R | 26 +++-- R/cff_read_biblines.R | 86 +++++++++++++++ R/cff_to_bibentry.R | 2 +- R/cff_validate.R | 2 +- R/cff_write.R | 2 +- R/deprecated.R | 83 +++++++++++++- R/docs.R | 2 +- R/utils-bibtex.R | 2 +- R/utils-schema.R | 2 +- R/write_bib.R | 2 +- R/write_citation.R | 2 +- README.md | 66 ++++++++++- codemeta.json | 4 +- data/cran_to_spdx.rda | Bin 907 -> 916 bytes inst/WORDLIST | 3 +- inst/schemaorg.json | 2 +- man/cff-class.Rd | 1 + man/cff.Rd | 4 +- man/cff_create.Rd | 4 +- man/cff_gha_update.Rd | 4 +- man/cff_git_hook.Rd | 4 +- man/cff_parse_citation.Rd | 4 +- man/cff_parse_person.Rd | 4 +- man/cff_read.Rd | 39 +++++-- man/cff_read_biblines.Rd | 75 +++++++++++++ man/cff_schema.Rd | 2 +- man/cff_to_bibentry.Rd | 8 +- man/cff_validate.Rd | 4 +- man/cff_write.Rd | 4 +- ...m_bibtex.Rd => deprecated_cff_from_bib.Rd} | 34 ++---- ...cff_to_bib.Rd => deprecated_cff_to_bib.Rd} | 7 +- man/encoded_utf_to_latex.Rd | 8 +- man/roxygen/meta.R | 11 ++ man/roxygen2/meta.R | 7 -- man/toBibtex.cff.Rd | 47 ++++++++ man/write_bib.Rd | 8 +- man/write_citation.Rd | 8 +- pkgdown/_pkgdown.yml | 72 ++++++------ tests/testthat/_snaps/cff-methods.md | 103 ++++++++++++++++++ ...ff_from_bibtex.md => cff_read_biblines.md} | 28 ++++- tests/testthat/_snaps/deprecated.md | 18 +++ tests/testthat/test-cff-methods.R | 55 ++++++++++ ...from_bibtex.R => test-cff_read_biblines.R} | 22 ++-- tests/testthat/test-deprecated.R | 20 ++++ vignettes/bibtex_cff.Rmd | 81 +++++++------- 56 files changed, 856 insertions(+), 281 deletions(-) delete mode 100644 R/cff_from_bibtex.R create mode 100644 R/cff_read_biblines.R create mode 100644 man/cff_read_biblines.Rd rename man/{cff_from_bibtex.Rd => deprecated_cff_from_bib.Rd} (59%) rename man/{renamed_cff_to_bib.Rd => deprecated_cff_to_bib.Rd} (91%) create mode 100644 man/roxygen/meta.R delete mode 100644 man/roxygen2/meta.R create mode 100644 man/toBibtex.cff.Rd rename tests/testthat/_snaps/{cff_from_bibtex.md => cff_read_biblines.md} (53%) rename tests/testthat/{test-cff_from_bibtex.R => test-cff_read_biblines.R} (59%) diff --git a/CITATION.cff b/CITATION.cff index ae2c7213..d0826b67 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -10,9 +10,9 @@ license: GPL-3.0-or-later title: 'cffr: Generate Citation File Format (''cff'') Metadata for R Packages' version: 0.5.0.9000 doi: 10.21105/joss.03900 -abstract: The Citation File Format version 1.2.0 is a - human and machine readable file format which provides citation metadata for software. - This package provides core utilities to generate and validate this metadata. +abstract: The Citation File Format version 1.2.0 + is a human and machine readable file format which provides citation metadata for + software. This package provides core utilities to generate and validate this metadata. authors: - family-names: Hernangómez given-names: Diego diff --git a/NAMESPACE b/NAMESPACE index 62a9bd89..dc182295 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -6,6 +6,7 @@ S3method(c,cff) S3method(head,cff) S3method(print,cff) S3method(tail,cff) +S3method(toBibtex,cff) export(as.cff) export(cff) export(cff_create) @@ -19,6 +20,7 @@ export(cff_parse_person) export(cff_parse_person_bibtex) export(cff_read) export(cff_read_bib) +export(cff_read_biblines) export(cff_read_cff_citation) export(cff_read_citation) export(cff_read_description) diff --git a/NEWS.md b/NEWS.md index 94412bb8..e68f4413 100644 --- a/NEWS.md +++ b/NEWS.md @@ -13,7 +13,9 @@ - Now reading from external files is performed exclusively by `cff_read()` and additionally by the more-specific new functions `cff_read_cff_citation()`, - `cff_read_description()`, `cff_read_citation()` and `cff_read_bib()`. + `cff_read_description()`, `cff_read_citation()` and `cff_read_bib()`. It is + also possible to read BibTeX lines with `cff_read_biblines()`. Previous + function `cff_from_bibtex()` is now superseded. ### Methods @@ -27,8 +29,9 @@ [entity](https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md#definitionsentity) (e.g. authors, contacts, editors, publisher). - `head.cff()`, `tail.cff()`. + - `toBibtex.cff()` -## Changes on bibtex crosswalk +## Changes on BibTeX crosswalk - **\@inbook** and **\@book** gains a new value on [CFF]{.underline} when **series** is provided: [collection-type: book-series.]{.underline} diff --git a/R/cff-methods.R b/R/cff-methods.R index e9f59eb0..6f8321b1 100644 --- a/R/cff-methods.R +++ b/R/cff-methods.R @@ -79,3 +79,47 @@ head.cff <- function(x, n = 6L, ...) { tail.cff <- function(x, n = 6L, ...) { as.cff(NextMethod()) } + + +#' Converting [`cff`][cff-class] objects to BibTeX +#' +#' @description +#' +#' This method convert [`cff`][cff-class] objects to `Bibtex` objects as +#' provided by [utils::toBibtex()]. These objects are character vectors with +#' BibTeX markup. +#' +#' @keywords internal +#' @family s3method +#' @family bibtex +#' @export +#' @rdname toBibtex.cff +#' @seealso [utils::toBibtex()] +#' +#' @param object `cff` object. +#' @param ... Arguments passed to [utils::toBibtex()]. +#' @inheritParams cff_to_bibentry +#' +#' @examples +#' a_cff <- cff_create("cffr") +#' a_cff$`preferred-citation` +#' +#' toBibtex(a_cff, "preferred") +toBibtex.cff <- function(object, ..., + what = c("preferred", "references", "all")) { + # If a single reference... + if ("cff-version" %in% names(object)) { + # If full cff + biblist_cff <- cff_to_bibentry(x = object, what = what) + } else { + # Need to enlist if single + if ("type" %in% names(object)) { + object <- list(object) + class(object) <- c("cff", "list") + } + + bib_list <- lapply(object, cff_bibtex_parser) + biblist_cff <- do.call(c, bib_list) + } + toBibtex(biblist_cff, ...) +} diff --git a/R/cff.R b/R/cff.R index 6afd6055..82152548 100644 --- a/R/cff.R +++ b/R/cff.R @@ -9,7 +9,7 @@ #' A `cff` object. Under the hood, a `cff` object is a regular [`list`] object #' with a special [print()] method. #' -#' @family Core functions +#' @family core #' #' @param path The path to a `CITATION.cff` file. #' @param ... Named arguments to be used for creating a [`cff`] object. See diff --git a/R/cff_create.R b/R/cff_create.R index 0e0a8bcd..d43ff43c 100644 --- a/R/cff_create.R +++ b/R/cff_create.R @@ -10,7 +10,7 @@ #' #' @return A [`cff`][cff-class] object. #' -#' @family Core functions +#' @family core #' #' @export #' diff --git a/R/cff_from_bibtex.R b/R/cff_from_bibtex.R deleted file mode 100644 index 3036af33..00000000 --- a/R/cff_from_bibtex.R +++ /dev/null @@ -1,94 +0,0 @@ -#' Create a [`cff`][cff-class] object from BibTeX entries -#' -#' @description -#' -#' Extract the information of a BibTeX file or BibTeX entry and creates the -#' corresponding [`cff`][cff-class] object with [cff_parse_citation()]. -#' -#' @param x The source that would be used for generating the -#' [`cff`][cff-class] object. Must be `character` object indicating either: -#' - The path to a BibTeX file. -#' - A vector of characters with the full BibTeX string. See **Examples** -#' @param encoding Encoding to be assumed for `x`. See [readLines()]. -#' @param ... Other arguments passed to [bibtex::read.bib()]. -#' -#' @family BibTeX helpers -#' -#' @return -#' -#' ```{r child = "man/chunks/value.Rmd"} -#' ``` -#' -#' @export -#' -#' @details -#' -#' This function requires the package \CRANpkg{bibtex} (>= 0.5.0), that is -#' listed as `Suggested` by \CRANpkg{cffr}. -#' -#' @seealso -#' -#' `vignette("bibtex_cff", package = "cffr")` to learn about the mapping of -#' information between BibTeX and `CITATION.cff`. -#' -#' @examples -#' if (requireNamespace("bibtex", quietly = TRUE)) { -#' x <- c( -#' "@book{einstein1921, -#' title = {Relativity: The Special and the General Theory}, -#' author = {Einstein, Albert}, -#' year = 1920, -#' publisher = {Henry Holt and Company}, -#' address = {London, United Kingdom}, -#' isbn = 9781587340925 -#' }", -#' "@misc{misc-full, -#' title = {Handing out random pamphlets in airports}, -#' author = {Joe-Bob Missilany}, -#' year = 1984, -#' month = oct, -#' note = {This is a full MISC entry}, -#' howpublished = {Handed out at O'Hare} -#' }" -#' ) -#' -#' cff_from_bibtex(x) -#' -#' # From a file -#' -#' x2 <- system.file("examples/example.bib", package = "cffr") -#' cff_from_bibtex(x2) -#' } -cff_from_bibtex <- function(x, encoding = "UTF-8", ...) { - # nocov start - if (!requireNamespace("bibtex", quietly = TRUE)) { - msg <- paste0( - "{.pkg bibtex} package required for using this function: ", - '{.run install.packages("bibtex")}' - ) - cli::cli_abort(msg) - } - # nocov end - if (!is.character(x)) { - msg <- paste0( - "{.arg x} should be a {.cls character} object. ", - "You provided an object of class(es) {.cls {class(x)}}" - ) - cli::cli_abort(msg) - } - - if (length(x) == 1 && file.exists(x)) { - # Read bib file - file <- x - } else { - # Write x to a tempfile - file <- tempfile(fileext = ".bib") - writeLines(x, file) - } - # Read from tempfile - read_bib <- bibtex::read.bib(file = file, encoding = encoding, ...) - - - tocff <- cff_parse_citation(read_bib) - return(tocff) -} diff --git a/R/cff_gha_update.R b/R/cff_gha_update.R index 17c365b3..22ffedf7 100644 --- a/R/cff_gha_update.R +++ b/R/cff_gha_update.R @@ -26,7 +26,7 @@ #' } #' @export #' -#' @family Git helpers +#' @family git cff_gha_update <- function(path = ".", overwrite = FALSE) { destdir <- file.path(path, ".github", "workflows") diff --git a/R/cff_git_hook.R b/R/cff_git_hook.R index d04e63c5..f1c2d7aa 100644 --- a/R/cff_git_hook.R +++ b/R/cff_git_hook.R @@ -8,7 +8,7 @@ #' #' @name cff_git_hook #' -#' @family Git helpers +#' @family git #' #' @export #' diff --git a/R/cff_parse_citation.R b/R/cff_parse_citation.R index 5a4ab9d1..e2b1914b 100644 --- a/R/cff_parse_citation.R +++ b/R/cff_parse_citation.R @@ -6,7 +6,7 @@ #' #' @export #' -#' @family Parsers +#' @family parsers #' #' @param bib A `bibentry` object, either created with [bibentry()] #' (preferred) or [citEntry()]. diff --git a/R/cff_parse_person.R b/R/cff_parse_person.R index a19d87e2..bbf973e8 100644 --- a/R/cff_parse_person.R +++ b/R/cff_parse_person.R @@ -9,7 +9,7 @@ #' #' @export #' -#' @family Parsers +#' @family parsers #' #' @param person A `person` object created with [person()] or a #' character string. See **Details**. diff --git a/R/cff_read.R b/R/cff_read.R index 6149b604..7dc337b8 100644 --- a/R/cff_read.R +++ b/R/cff_read.R @@ -3,9 +3,9 @@ #' @description #' Read files and convert them to [`cff`][cff-class] objects. Files supported #' are: -#' - `CITATION.cff` files -#' - `DESCRIPTION` files -#' - **R** citation files (usually located in `inst/CITATION`) +#' - `CITATION.cff` files. +#' - `DESCRIPTION` files. +#' - **R** citation files (usually located in `inst/CITATION`). #' - BibTeX files (with extension `*.bib`). #' #' [cff_read()] would try to guess the type of file provided in `path`. However @@ -18,8 +18,16 @@ #' #' @export #' @rdname cff_read +#' @family reading +#' @seealso #' -#' @param path Path to a file +#' The underlying functions used for reading external files: +#' - [yaml::read_yaml()] for `CITATION.cff` files. +#' - [desc::desc()] for `DESCRIPTION` files. +#' - [utils::readCitationFile()] for **R** citation files. +#' - [bibtex::read.bib()] for BibTeX files (extension `*.bib`). +#' +#' @param path Path to a file. #' @param cff_version The Citation File Format schema version that the #' `CITATION.cff` file adheres to for providing the citation metadata. #' @param gh_keywords Logical `TRUE/FALSE`. If the package is hosted on @@ -79,11 +87,12 @@ #' #' # Create cff object from BibTex #' -#' from_bib <- cff_read(system.file("examples/example.bib", package = "cffr")) -#' -#' # First item only -#' from_bib[[1]] +#' if (requireNamespace("bibtex", quietly = TRUE)) { +#' from_bib <- cff_read(system.file("examples/example.bib", package = "cffr")) #' +#' # First item only +#' from_bib[[1]] +#' } #' # Create cff object from CITATION #' from_citation <- cff_read(system.file("CITATION", package = "cffr")) #' @@ -246,6 +255,7 @@ cff_read_citation <- function(path, meta = NULL, ...) { } #' @export +#' @family bibtex #' @rdname cff_read cff_read_bib <- function(path, encoding = "UTF-8", ...) { if (!file.exists(path)) { diff --git a/R/cff_read_biblines.R b/R/cff_read_biblines.R new file mode 100644 index 00000000..25735bd3 --- /dev/null +++ b/R/cff_read_biblines.R @@ -0,0 +1,86 @@ +#' Create a [`cff`][cff-class] object from BibTeX lines +#' +#' @description +#' Convert a [`character`][character()] representing a BibTeX entry to a +#' [`cff`][cff-class] object. +#' +#' @family bibtex +#' @family reading +#' @seealso +#' [cff_read_bib()] for reading `*.bib` files. +#' +#' @export +#' +#' @param x A vector of characters with the full BibTeX string. +#' @param encoding Encoding to be assumed for `x`, see [readLines()]. +#' @param ... Arguments passed on to [cff_read_bib()]. +#' @inheritDotParams cff_read_bib -path +#' +#' @return +#' +#' ```{r child = "man/chunks/value.Rmd"} +#' ``` +#' +#' @details +#' This is a helper function that writes `x` to a `*.bib` file and reads it with +#' [cff_read_bib()]. +#' +#' This function requires \CRANpkg{bibtex} (>= 0.5.0) and uses +#' [bibtex::read.bib()]. +#' +#' @examples +#' if (requireNamespace("bibtex", quietly = TRUE)) { +#' x <- c( +#' "@book{einstein1921, +#' title = {Relativity: The Special and the General Theory}, +#' author = {Einstein, Albert}, +#' year = 1920, +#' publisher = {Henry Holt and Company}, +#' address = {London, United Kingdom}, +#' isbn = 9781587340925 +#' }", +#' "@misc{misc-full, +#' title = {Handing out random pamphlets in airports}, +#' author = {Joe-Bob Missilany}, +#' year = 1984, +#' month = oct, +#' note = {This is a full MISC entry}, +#' howpublished = {Handed out at O'Hare} +#' }" +#' ) +#' +#' +#' cff_read_biblines(x) +#' } +cff_read_biblines <- function(x, encoding = "UTF-8", ...) { + # Validations + if (!inherits(x, "character")) { + cli::cli_abort( + paste0( + "{.arg x} should be a {.cls character}, not a ", + "{.cls {class(x)}}." + ) + ) + } + + if (any(grepl("\\.bib$", x, ignore.case = TRUE))) { + cli::cli_alert_warning( + paste0("{.arg x} seems to be a {.val .bib} file, not a BibTeX entry.") + ) + cli::cli_alert_info("Reading {.arg x} with {.fn cffr:cff_read_bib}") + the_cff <- cff_read_bib(x, encoding = encoding, ...) + return(the_cff) + } + + if (!any(grepl("^@", x))) { + cli::cli_alert_warning( + paste0("{.arg x} doesn't look as a BibTeX entry. Check the results.") + ) + } + # Write x to a tempfile + file <- tempfile(fileext = ".bib") + writeLines(x, file) + + the_cff <- cff_read_bib(file, encoding = encoding, ...) + the_cff +} diff --git a/R/cff_to_bibentry.R b/R/cff_to_bibentry.R index 079d5ed5..a869bc57 100644 --- a/R/cff_to_bibentry.R +++ b/R/cff_to_bibentry.R @@ -39,7 +39,7 @@ #' - `all`: A combination of the previous two options. This would extract #' both the preferred citation info and the references. #' -#' @family BibTeX helpers +#' @family bibtex #' #' @return A `bibentry` object or a list of `bibentry` objects. This could #' be parsed to BibTeX using [toBibtex()] diff --git a/R/cff_validate.R b/R/cff_validate.R index ceaa3110..488100a0 100644 --- a/R/cff_validate.R +++ b/R/cff_validate.R @@ -13,7 +13,7 @@ #' ``` #' @export #' -#' @family Core functions +#' @family core #' #' @seealso #' ```{r, echo=FALSE, results='asis'} diff --git a/R/cff_write.R b/R/cff_write.R index 31865a61..30bcd942 100644 --- a/R/cff_write.R +++ b/R/cff_write.R @@ -9,7 +9,7 @@ #' function is basically a wrapper around [cff_create()] to both create the #' [`cff`] object and writes it out to a YAML-formatted file in one command. #' -#' @family Core functions +#' @family core #' #' @param x The source that would be used for generating #' the `CITATION.cff` file. It could be: diff --git a/R/deprecated.R b/R/deprecated.R index a5079304..6f95d019 100644 --- a/R/deprecated.R +++ b/R/deprecated.R @@ -3,12 +3,13 @@ #' @description #' `r lifecycle::badge('superseded')` Please use [cff_to_bibentry()] instead. #' -#' @rdname renamed_cff_to_bib +#' @rdname deprecated_cff_to_bib #' @inheritParams cff_to_bibentry #' @export #' @keywords internal +#' @family deprecated #' -#' @return See [cff_to_bibentry()] +#' @return See [cff_to_bibentry()]. #' @examples #' \donttest{ #' # From a cff object @@ -30,7 +31,7 @@ cff_extract_to_bibtex <- function(x, cff_to_bibentry(x, what) } -#' @rdname renamed_cff_to_bib +#' @rdname deprecated_cff_to_bib #' @export #' @keywords internal cff_to_bibtex <- function(x, @@ -43,3 +44,79 @@ cff_to_bibtex <- function(x, } cff_to_bibentry(x, what) } + +#' Previous API: Create a [`cff`][cff-class] object from BibTeX entries +#' +#' @description +#' +#' `r lifecycle::badge('superseded')` Please use either [cff_read_bib()] or +#' [cff_read_biblines()] instead. +#' +#' @rdname deprecated_cff_from_bib +#' @export +#' @keywords internal +#' @family deprecated +#' +#' @param x The source that would be used for generating the +#' [`cff`][cff-class] object. Must be `character` object indicating either: +#' - The path to a BibTeX file. +#' - A vector of characters with the full BibTeX string. See **Examples**. +#' @param encoding Encoding to be assumed for `x`. See [readLines()]. +#' @param ... Other arguments passed to [bibtex::read.bib()]. +#' +#' +#' @return +#' +#' See [cff_read_bib()]. +#' +#' +#' +#' @examples +#' if (requireNamespace("bibtex", quietly = TRUE)) { +#' x <- c( +#' "@book{einstein1921, +#' title = {Relativity: The Special and the General Theory}, +#' author = {Einstein, Albert}, +#' year = 1920, +#' publisher = {Henry Holt and Company}, +#' address = {London, United Kingdom}, +#' isbn = 9781587340925 +#' }", +#' "@misc{misc-full, +#' title = {Handing out random pamphlets in airports}, +#' author = {Joe-Bob Missilany}, +#' year = 1984, +#' month = oct, +#' note = {This is a full MISC entry}, +#' howpublished = {Handed out at O'Hare} +#' }" +#' ) +#' +#' cff_read_biblines(x) +#' +#' # From a file +#' +#' x2 <- system.file("examples/example.bib", package = "cffr") +#' cff_read_bib(x2) +#' } +cff_from_bibtex <- function(x, encoding = "UTF-8", ...) { + if (length(x) == 1 && file.exists(x)) { + if (requireNamespace("lifecycle", quietly = TRUE)) { + lifecycle::deprecate_soft( + "0.5.0", "cff_from_bibtex()", + details = "Function renamed, use `cff_read_bib()` instead." + ) + } + + # Read bib file + return(cff_read_bib(x, encoding = encoding, ...)) + } + + if (requireNamespace("lifecycle", quietly = TRUE)) { + lifecycle::deprecate_soft( + "0.5.0", "cff_from_bibtex()", + details = "Function renamed, use `cff_read_biblines()` instead." + ) + } + cff_read_biblines(x, encoding = encoding, ...) +} diff --git a/R/docs.R b/R/docs.R index 98e41ea8..1a77ece0 100644 --- a/R/docs.R +++ b/R/docs.R @@ -1,7 +1,7 @@ #' The `cff` class #' #' @name cff-class -#' +#' @aliases cff_class #' @keywords internal #' #' @description diff --git a/R/utils-bibtex.R b/R/utils-bibtex.R index cf4f11d2..ceaf55f1 100644 --- a/R/utils-bibtex.R +++ b/R/utils-bibtex.R @@ -7,7 +7,7 @@ #' #' @param x A string, possibly encoded in UTF-8 encoding system. #' -#' @family BibTeX helpers +#' @family bibtex #' #' @seealso [tools::encoded_text_to_latex()] #' diff --git a/R/utils-schema.R b/R/utils-schema.R index 69e99d01..88c3c3c9 100644 --- a/R/utils-schema.R +++ b/R/utils-schema.R @@ -33,7 +33,7 @@ #' #' ``` #' -#' @family Schemas +#' @family schemas #' #' @export #' diff --git a/R/write_bib.R b/R/write_bib.R index 7623cfde..7e685ff3 100644 --- a/R/write_bib.R +++ b/R/write_bib.R @@ -13,7 +13,7 @@ #' @param ascii Whether to write the entries using ASCII characters only or not. #' #' @export -#' @family BibTeX helpers +#' @family bibtex #' #' @return Writes an `.bib` file specified on `file` parameter and the #' equivalent `Bibtex` object created with [utils::toBibtex()]. It also diff --git a/R/write_citation.R b/R/write_citation.R index 36f3f0a4..d1a33e67 100644 --- a/R/write_citation.R +++ b/R/write_citation.R @@ -20,7 +20,7 @@ #' @inheritDotParams cff_to_bibentry #' #' @export -#' @family BibTeX helpers +#' @family bibtex #' #' @return Writes an `inst/CITATION` file and (invisibly) returns the #' `bibentry` object that has been written to the file. diff --git a/README.md b/README.md index 1cfe3b25..23bb18bb 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ file and the `CITATION` file (if present) of your package. Note that **cffr** works best if your package pass `R CMD check/devtools::check()`. -As per 2024-02-20 there are at least 267 repos on GitHub using **cffr**. +As per 2024-02-26 there are at least 305 repos on GitHub using **cffr**. [Check them out here](https://github.com/search?q=cffr%20path%3A**%2FCITATION.cff&type=code). @@ -560,6 +560,25 @@ test <- cff_create("rmarkdown") - family-names: Chirico. given-names: Michael year: '2024' + - type: software + title: dygraphs + abstract: 'dygraphs: Interface to ''Dygraphs'' Interactive Time Series Charting + Library' + notes: Suggests + url: https://github.com/rstudio/dygraphs + repository: https://CRAN.R-project.org/package=dygraphs + authors: + - family-names: Vanderkam + given-names: Dan + - family-names: Allaire + given-names: JJ + - family-names: Owen + given-names: Jonathan + - family-names: Gromer + given-names: Daniel + - family-names: Thieurmel + given-names: Benoit + year: '2024' - type: software title: fs abstract: 'fs: Cross-Platform File System Operations Based on ''libuv''' @@ -576,6 +595,26 @@ test <- cff_create("rmarkdown") given-names: Gábor email: csardi.gabor@gmail.com year: '2024' + - type: software + title: rsconnect + abstract: 'rsconnect: Deploy Docs, Apps, and APIs to ''Posit Connect'', ''shinyapps.io'', + and ''RPubs''' + notes: Suggests + url: https://rstudio.github.io/rsconnect/ + repository: https://CRAN.R-project.org/package=rsconnect + authors: + - family-names: Atkins + given-names: Aron + email: aron@posit.co + - family-names: Allen + given-names: Toph + - family-names: Wickham + given-names: Hadley + - family-names: McPherson + given-names: Jonathan + - family-names: Allaire + given-names: JJ + year: '2024' - type: software title: downlit abstract: 'downlit: Syntax Highlighting and Automatic Linking' @@ -588,6 +627,19 @@ test <- cff_create("rmarkdown") email: hadley@posit.co year: '2024' version: '>= 0.4.0' + - type: software + title: katex + abstract: 'katex: Rendering Math to HTML, ''MathML'', or R-Documentation Format' + notes: Suggests + url: https://docs.ropensci.org/katex/ + repository: https://CRAN.R-project.org/package=katex + authors: + - family-names: Ooms + given-names: Jeroen + email: jeroen@berkeley.edu + orcid: https://orcid.org/0000-0002-4035-0289 + year: '2024' + version: '>= 1.4.0' - type: software title: sass abstract: 'sass: Syntactically Awesome Style Sheets (''Sass'')' @@ -698,6 +750,18 @@ test <- cff_create("rmarkdown") given-names: Davis email: davis@posit.co year: '2024' + - type: software + title: cleanrmd + abstract: 'cleanrmd: Clean Class-Less ''R Markdown'' HTML Documents' + notes: Suggests + url: https://pkg.garrickadenbuie.com/cleanrmd/ + repository: https://CRAN.R-project.org/package=cleanrmd + authors: + - family-names: Aden-Buie + given-names: Garrick + email: garrick@adenbuie.com + orcid: https://orcid.org/0000-0002-7111-0077 + year: '2024' - type: software title: withr abstract: 'withr: Run Code ''With'' Temporarily Modified Global State' diff --git a/codemeta.json b/codemeta.json index e4c9866b..0351b293 100644 --- a/codemeta.json +++ b/codemeta.json @@ -14,7 +14,7 @@ "name": "R", "url": "https://r-project.org" }, - "runtimePlatform": "R version 4.3.2 (2023-10-31)", + "runtimePlatform": "R version 4.3.2 (2023-10-31 ucrt)", "provider": { "@id": "https://cran.r-project.org", "@type": "Organization", @@ -200,7 +200,7 @@ }, "isPartOf": "https://ropensci.org", "keywords": ["attribution", "citation", "credit", "citation-files", "cff", "metadata", "r", "r-package", "citation-file-format", "rstats", "ropensci", "cran"], - "fileSize": "896.38KB", + "fileSize": "931.423KB", "citation": [ { "@type": "ScholarlyArticle", diff --git a/data/cran_to_spdx.rda b/data/cran_to_spdx.rda index 33fae2994ac0070f09336d3d8c403ec099798a1f..0bd1435cae708e762b962cb7a97ec9ae0c12fef9 100644 GIT binary patch literal 916 zcmV;F18e*riwFP!000002F+JXPuoBccH=-unm|gSm3lH&4Vna`W6qYoKWTU7l z6%~ibghjRFILLPBCXZ^{hliR@wmg?9cUwC}dynSrJ>vGR3u>%N zH7^9s2jmCN)MFU+jvqQf=!^lyHV7uMxN$r@(R46&?O7nkAL|Ucu8wsheU#`}%-}Kg zct}p6f|sD3J$Vg6?f4@&g<#mx_GkbE&SM;}8efM{{dWsb-+YHfc*I(skDzQMQuc9 z1!Aohx0C}Wz@61{kAy z&Yir|26bnZ!6Z%^)E>i>qx;N-Fqt-QvgfmsNZIvBwAm0ab2)67k|eIP;A`!G#>mpM z^iZ=B9+QksbB1dyET}lE|DM>xOF{e0ntpR!*DkoCa;-+&L=l6|gqH=3{IpT6Ecq3S zYeuoij3tXyK2yb3u8OVv0aVXC*O@pWx)Nr+Lh>6AcYHg*)N{XbpuvMrDCh&%#!Z-b zl8jZht*40o7ZBOVmSVVL106tdg;alC4R?J;u`wz#?3 zjfZj`(dQt-@SQK_UFh<3UL@mGl-u{nq~C0?X$Sm-txHN&hBcFxl`GI@LE+XjzF=3V z{08ZT_c^er!9v~w*h7PCW`0WWB0K*ZUKH0qz)3MkD`ZhnY^`QN@PAw(38+v}F}WMf z%vENY4z3n27R)i3_&a+GMMZe~UGnS+3epi+%Hr$8?&+{g{tFEUC+Ffm{5BSleFUss q*`d9A?PHSyz3NXt?~+F~qDOtlUw3zRKk(1b=<^FCJhi~74*&oy4!s@# literal 907 zcmV;619bdCT4*^jL0KkKS>9zRc>o3)f6)K`|B!TNe+NIOUueH)-|#>H0ssI3&;$PH zMJcLQXdxn$LqY0#n*AnjWXMA%O-!8VroodWVq>8hVWZpyrJl8ess_0%@QOhK3*w7>x`OkO2~jFe$2d zXwawXnjVu)lzN&O4WcqVOllbqQxUPLFe>dz=&r?fZ9@1WKjajLMimfmJ2mUYPPPXx zj*Ybg1&%$?qNxPUhn5dz<$F-rN4=W12TeQiUlnt8<0b6Yj7qp-b#>kRV}V z`Fj%x=Ccapm4j`8K(v)wkqAVgQwwED3edK<)|qCdvuV!(PmL8wD1-^BC2K`h&j3QP zL0u36=!%xXc0(x;6=KLKJiY@Gylf7R8cZa*UN&lz^7{|Bd%yVmS_cO2<5+P%UUOvX zSz@tbnf*NMPJ>Nh!Uq@-^UcwV*1cylE@5sg3T+_5-MXcG?wWWD;2vA3o z&8+4_l-WR}LCwk=!sx_DBpAAMWvtJbNN+NfJb=(2mY@It diff --git a/inst/WORDLIST b/inst/WORDLIST index 9e3ff374..ee3183f6 100644 --- a/inst/WORDLIST +++ b/inst/WORDLIST @@ -40,7 +40,6 @@ Niemeyer ORCID Oren Pandoc -Parsers Patashnik PhDThesis Philipp @@ -52,7 +51,6 @@ Rbuildignore Reproducibility Riederer SPDX -Schemas Spaaks Suárez TechReport @@ -90,6 +88,7 @@ rOpenSci repo repos rmarkdown +schemas testthat xampl zenodo diff --git a/inst/schemaorg.json b/inst/schemaorg.json index 8056af78..dec7a9c6 100644 --- a/inst/schemaorg.json +++ b/inst/schemaorg.json @@ -26,6 +26,6 @@ "name": "Comprehensive R Archive Network (CRAN)", "url": "https://cran.r-project.org" }, - "runtimePlatform": "R version 4.3.2 (2023-10-31)", + "runtimePlatform": "R version 4.3.2 (2023-10-31 ucrt)", "version": "0.5.0.9000" } diff --git a/man/cff-class.Rd b/man/cff-class.Rd index b6205b88..bde2e41a 100644 --- a/man/cff-class.Rd +++ b/man/cff-class.Rd @@ -2,6 +2,7 @@ % Please edit documentation in R/docs.R \name{cff-class} \alias{cff-class} +\alias{cff_class} \title{The \code{cff} class} \description{ \subsection{The \code{cff} class}{ diff --git a/man/cff.Rd b/man/cff.Rd index 6731e872..099cc5c7 100644 --- a/man/cff.Rd +++ b/man/cff.Rd @@ -104,9 +104,9 @@ class(cffobj) cffobj } \seealso{ -Other Core functions: +Other core functions of \CRANpkg{cffr}: \code{\link{cff_create}()}, \code{\link{cff_validate}()}, \code{\link{cff_write}()} } -\concept{Core functions} +\concept{core} diff --git a/man/cff_create.Rd b/man/cff_create.Rd index 39db6269..c3ff7f1b 100644 --- a/man/cff_create.Rd +++ b/man/cff_create.Rd @@ -112,9 +112,9 @@ cff_create(demo_file, keys = list("contact" = new_contact)) \code{vignette("cffr", "cffr")} -Other Core functions: +Other core functions of \CRANpkg{cffr}: \code{\link{cff}()}, \code{\link{cff_validate}()}, \code{\link{cff_write}()} } -\concept{Core functions} +\concept{core} diff --git a/man/cff_gha_update.Rd b/man/cff_gha_update.Rd index 9242f2f4..65568d81 100644 --- a/man/cff_gha_update.Rd +++ b/man/cff_gha_update.Rd @@ -34,7 +34,7 @@ cff_gha_update() } } \seealso{ -Other Git helpers: +Other Git/GitHub helpers: \code{\link{cff_git_hook}} } -\concept{Git helpers} +\concept{git} diff --git a/man/cff_git_hook.Rd b/man/cff_git_hook.Rd index c93875b1..2a614676 100644 --- a/man/cff_git_hook.Rd +++ b/man/cff_git_hook.Rd @@ -65,7 +65,7 @@ cff_git_hook_install() \seealso{ \code{\link[usethis:use_git_hook]{usethis::use_git_hook()}}, \code{\link[usethis:use_git]{usethis::use_git()}} -Other Git helpers: +Other Git/GitHub helpers: \code{\link{cff_gha_update}()} } -\concept{Git helpers} +\concept{git} diff --git a/man/cff_parse_citation.Rd b/man/cff_parse_citation.Rd index 02c768c7..03be7d84 100644 --- a/man/cff_parse_citation.Rd +++ b/man/cff_parse_citation.Rd @@ -81,7 +81,7 @@ cff_parse_citation(citation("rmarkdown")) \seealso{ \code{\link[=cff_create]{cff_create()}}, \code{vignette("bibtex_cff", "cffr")}, \code{\link[=bibentry]{bibentry()}} -Other Parsers: +Other parsers: \code{\link{cff_parse_person}()} } -\concept{Parsers} +\concept{parsers} diff --git a/man/cff_parse_person.Rd b/man/cff_parse_person.Rd index 3246797c..a0c03b2e 100644 --- a/man/cff_parse_person.Rd +++ b/man/cff_parse_person.Rd @@ -76,7 +76,7 @@ cff_parse_person_bibtex("Herbert von Karajan") \seealso{ \code{\link[=cff_create]{cff_create()}}, \code{vignette("cffr", "cffr")}, \code{\link[utils:person]{utils::person()}} -Other Parsers: +Other parsers: \code{\link{cff_parse_citation}()} } -\concept{Parsers} +\concept{parsers} diff --git a/man/cff_read.Rd b/man/cff_read.Rd index ef8abc85..a443541f 100644 --- a/man/cff_read.Rd +++ b/man/cff_read.Rd @@ -25,7 +25,7 @@ cff_read_citation(path, meta = NULL, ...) cff_read_bib(path, encoding = "UTF-8", ...) } \arguments{ -\item{path}{Path to a file} +\item{path}{Path to a file.} \item{...}{Arguments to be passed to other functions (i.e. to \code{\link[yaml:read_yaml]{yaml::read_yaml()}}, \code{\link[bibtex:read.bib]{bibtex::read.bib()}}, etc.).} @@ -58,9 +58,9 @@ conversion is performed. Read files and convert them to \code{\link[=cff-class]{cff}} objects. Files supported are: \itemize{ -\item \code{CITATION.cff} files -\item \code{DESCRIPTION} files -\item \strong{R} citation files (usually located in \code{inst/CITATION}) +\item \code{CITATION.cff} files. +\item \code{DESCRIPTION} files. +\item \strong{R} citation files (usually located in \code{inst/CITATION}). \item BibTeX files (with extension \verb{*.bib}). } @@ -98,11 +98,12 @@ from_desc # Create cff object from BibTex -from_bib <- cff_read(system.file("examples/example.bib", package = "cffr")) - -# First item only -from_bib[[1]] +if (requireNamespace("bibtex", quietly = TRUE)) { + from_bib <- cff_read(system.file("examples/example.bib", package = "cffr")) + # First item only + from_bib[[1]] +} # Create cff object from CITATION from_citation <- cff_read(system.file("CITATION", package = "cffr")) @@ -119,3 +120,25 @@ from_citation[[1]] \url{https://docs.ropensci.org/cffr/articles/bibtex_cff.html}. } } +\seealso{ +The underlying functions used for reading external files: +\itemize{ +\item \code{\link[yaml:read_yaml]{yaml::read_yaml()}} for \code{CITATION.cff} files. +\item \code{\link[desc:desc]{desc::desc()}} for \code{DESCRIPTION} files. +\item \code{\link[utils:citation]{utils::readCitationFile()}} for \strong{R} citation files. +\item \code{\link[bibtex:read.bib]{bibtex::read.bib()}} for BibTeX files (extension \verb{*.bib}). +} + +Reading other files as \code{cff}: +\code{\link{cff_read_biblines}()} + +Other functions for working with BibTeX format: +\code{\link{cff_read_biblines}()}, +\code{\link{cff_to_bibentry}()}, +\code{\link{encoded_utf_to_latex}()}, +\code{\link{toBibtex.cff}()}, +\code{\link{write_bib}()}, +\code{\link{write_citation}()} +} +\concept{bibtex} +\concept{reading} diff --git a/man/cff_read_biblines.Rd b/man/cff_read_biblines.Rd new file mode 100644 index 00000000..de30f007 --- /dev/null +++ b/man/cff_read_biblines.Rd @@ -0,0 +1,75 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/cff_read_biblines.R +\name{cff_read_biblines} +\alias{cff_read_biblines} +\title{Create a \code{\link[=cff-class]{cff}} object from BibTeX lines} +\usage{ +cff_read_biblines(x, encoding = "UTF-8", ...) +} +\arguments{ +\item{x}{A vector of characters with the full BibTeX string.} + +\item{encoding}{Encoding to be assumed for \code{x}, see \code{\link[=readLines]{readLines()}}.} + +\item{...}{ + Arguments passed on to \code{\link[=cff_read_bib]{cff_read_bib}} + \describe{ + \item{\code{}}{} + }} +} +\value{ +A \code{\link[=cff-class]{cff}} object ready to be used with other functions (i.e. +\code{\link[=cff_create]{cff_create()}}. +} +\description{ +Convert a \code{\link[=character]{character}} representing a BibTeX entry to a +\code{\link[=cff-class]{cff}} object. +} +\details{ +This is a helper function that writes \code{x} to a \verb{*.bib} file and reads it with +\code{\link[=cff_read_bib]{cff_read_bib()}}. + +This function requires \CRANpkg{bibtex} (>= 0.5.0) and uses +\code{\link[bibtex:read.bib]{bibtex::read.bib()}}. +} +\examples{ +if (requireNamespace("bibtex", quietly = TRUE)) { + x <- c( + "@book{einstein1921, + title = {Relativity: The Special and the General Theory}, + author = {Einstein, Albert}, + year = 1920, + publisher = {Henry Holt and Company}, + address = {London, United Kingdom}, + isbn = 9781587340925 + }", + "@misc{misc-full, + title = {Handing out random pamphlets in airports}, + author = {Joe-Bob Missilany}, + year = 1984, + month = oct, + note = {This is a full MISC entry}, + howpublished = {Handed out at O'Hare} + }" + ) + + + cff_read_biblines(x) +} +} +\seealso{ +\code{\link[=cff_read_bib]{cff_read_bib()}} for reading \verb{*.bib} files. + +Other functions for working with BibTeX format: +\code{\link{cff_read}()}, +\code{\link{cff_to_bibentry}()}, +\code{\link{encoded_utf_to_latex}()}, +\code{\link{toBibtex.cff}()}, +\code{\link{write_bib}()}, +\code{\link{write_citation}()} + +Reading other files as \code{cff}: +\code{\link{cff_read}()} +} +\concept{bibtex} +\concept{reading} diff --git a/man/cff_schema.Rd b/man/cff_schema.Rd index d55efe2a..359023a3 100644 --- a/man/cff_schema.Rd +++ b/man/cff_schema.Rd @@ -59,4 +59,4 @@ cff_schema_definitions_entity() cff_schema_definitions_refs() } -\concept{Schemas} +\concept{schemas} diff --git a/man/cff_to_bibentry.Rd b/man/cff_to_bibentry.Rd index 4864e01d..fd2cfc7b 100644 --- a/man/cff_to_bibentry.Rd +++ b/man/cff_to_bibentry.Rd @@ -94,10 +94,12 @@ toBibtex(desc_file) } } \seealso{ -Other BibTeX helpers: -\code{\link{cff_from_bibtex}()}, +Other functions for working with BibTeX format: +\code{\link{cff_read}()}, +\code{\link{cff_read_biblines}()}, \code{\link{encoded_utf_to_latex}()}, +\code{\link{toBibtex.cff}()}, \code{\link{write_bib}()}, \code{\link{write_citation}()} } -\concept{BibTeX helpers} +\concept{bibtex} diff --git a/man/cff_validate.Rd b/man/cff_validate.Rd index f44cfe85..c7a680f9 100644 --- a/man/cff_validate.Rd +++ b/man/cff_validate.Rd @@ -50,9 +50,9 @@ try(cff_validate(system.file("CITATION", package = "cffr"))) \seealso{ \href{https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md}{Guide to Citation File Format schema version 1.2.0}. -Other Core functions: +Other core functions of \CRANpkg{cffr}: \code{\link{cff}()}, \code{\link{cff_create}()}, \code{\link{cff_write}()} } -\concept{Core functions} +\concept{core} diff --git a/man/cff_write.Rd b/man/cff_write.Rd index 5678352e..034c0b67 100644 --- a/man/cff_write.Rd +++ b/man/cff_write.Rd @@ -79,9 +79,9 @@ file.remove(tmpfile) \seealso{ \href{https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md}{Guide to Citation File Format schema version 1.2.0}. -Other Core functions: +Other core functions of \CRANpkg{cffr}: \code{\link{cff}()}, \code{\link{cff_create}()}, \code{\link{cff_validate}()} } -\concept{Core functions} +\concept{core} diff --git a/man/cff_from_bibtex.Rd b/man/deprecated_cff_from_bib.Rd similarity index 59% rename from man/cff_from_bibtex.Rd rename to man/deprecated_cff_from_bib.Rd index d0c1d327..afa8367a 100644 --- a/man/cff_from_bibtex.Rd +++ b/man/deprecated_cff_from_bib.Rd @@ -1,8 +1,8 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/cff_from_bibtex.R +% Please edit documentation in R/deprecated.R \name{cff_from_bibtex} \alias{cff_from_bibtex} -\title{Create a \code{\link[=cff-class]{cff}} object from BibTeX entries} +\title{Previous API: Create a \code{\link[=cff-class]{cff}} object from BibTeX entries} \usage{ cff_from_bibtex(x, encoding = "UTF-8", ...) } @@ -11,7 +11,7 @@ cff_from_bibtex(x, encoding = "UTF-8", ...) \code{\link[=cff-class]{cff}} object. Must be \code{character} object indicating either: \itemize{ \item The path to a BibTeX file. -\item A vector of characters with the full BibTeX string. See \strong{Examples} +\item A vector of characters with the full BibTeX string. See \strong{Examples}. }} \item{encoding}{Encoding to be assumed for \code{x}. See \code{\link[=readLines]{readLines()}}.} @@ -19,16 +19,11 @@ cff_from_bibtex(x, encoding = "UTF-8", ...) \item{...}{Other arguments passed to \code{\link[bibtex:read.bib]{bibtex::read.bib()}}.} } \value{ -A \code{\link[=cff-class]{cff}} object ready to be used with other functions (i.e. -\code{\link[=cff_create]{cff_create()}}. +See \code{\link[=cff_read_bib]{cff_read_bib()}}. } \description{ -Extract the information of a BibTeX file or BibTeX entry and creates the -corresponding \code{\link[=cff-class]{cff}} object with \code{\link[=cff_parse_citation]{cff_parse_citation()}}. -} -\details{ -This function requires the package \CRANpkg{bibtex} (>= 0.5.0), that is -listed as \code{Suggested} by \CRANpkg{cffr}. +\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#superseded}{\figure{lifecycle-superseded.svg}{options: alt='[Superseded]'}}}{\strong{[Superseded]}} Please use either \code{\link[=cff_read_bib]{cff_read_bib()}} or +\code{\link[=cff_read_biblines]{cff_read_biblines()}} instead. } \examples{ if (requireNamespace("bibtex", quietly = TRUE)) { @@ -51,22 +46,17 @@ if (requireNamespace("bibtex", quietly = TRUE)) { }" ) - cff_from_bibtex(x) + cff_read_biblines(x) # From a file x2 <- system.file("examples/example.bib", package = "cffr") - cff_from_bibtex(x2) + cff_read_bib(x2) } } \seealso{ -\code{vignette("bibtex_cff", package = "cffr")} to learn about the mapping of -information between BibTeX and \code{CITATION.cff}. - -Other BibTeX helpers: -\code{\link{cff_to_bibentry}()}, -\code{\link{encoded_utf_to_latex}()}, -\code{\link{write_bib}()}, -\code{\link{write_citation}()} +Other deprecated functions: +\code{\link{cff_extract_to_bibtex}()} } -\concept{BibTeX helpers} +\concept{deprecated} +\keyword{internal} diff --git a/man/renamed_cff_to_bib.Rd b/man/deprecated_cff_to_bib.Rd similarity index 91% rename from man/renamed_cff_to_bib.Rd rename to man/deprecated_cff_to_bib.Rd index 11f1def6..f1677b42 100644 --- a/man/renamed_cff_to_bib.Rd +++ b/man/deprecated_cff_to_bib.Rd @@ -31,7 +31,7 @@ both the preferred citation info and the references. }} } \value{ -See \code{\link[=cff_to_bibentry]{cff_to_bibentry()}} +See \code{\link[=cff_to_bibentry]{cff_to_bibentry()}}. } \description{ \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#superseded}{\figure{lifecycle-superseded.svg}{options: alt='[Superseded]'}}}{\strong{[Superseded]}} Please use \code{\link[=cff_to_bibentry]{cff_to_bibentry()}} instead. @@ -47,4 +47,9 @@ cff_object bib <- cff_to_bibentry(cff_object) } } +\seealso{ +Other deprecated functions: +\code{\link{cff_from_bibtex}()} +} +\concept{deprecated} \keyword{internal} diff --git a/man/encoded_utf_to_latex.Rd b/man/encoded_utf_to_latex.Rd index 43cd3d1d..a77ad02b 100644 --- a/man/encoded_utf_to_latex.Rd +++ b/man/encoded_utf_to_latex.Rd @@ -45,11 +45,13 @@ ascii_table \seealso{ \code{\link[tools:encoded]{tools::encoded_text_to_latex()}} -Other BibTeX helpers: -\code{\link{cff_from_bibtex}()}, +Other functions for working with BibTeX format: +\code{\link{cff_read}()}, +\code{\link{cff_read_biblines}()}, \code{\link{cff_to_bibentry}()}, +\code{\link{toBibtex.cff}()}, \code{\link{write_bib}()}, \code{\link{write_citation}()} } -\concept{BibTeX helpers} +\concept{bibtex} \keyword{internal} diff --git a/man/roxygen/meta.R b/man/roxygen/meta.R new file mode 100644 index 00000000..d6e96117 --- /dev/null +++ b/man/roxygen/meta.R @@ -0,0 +1,11 @@ +list( + rd_family_title = list( + core = "Other core functions of \\CRANpkg{cffr}:", + reading = "Reading other files as \\code{cff}:", + bibtex = "Other functions for working with BibTeX format:", + schemas = "Other CFF schemas:", + git = "Other Git/GitHub helpers:", + deprecated = "Other deprecated functions:", + s3method = "Other S3 Methods for \\code{cff}:" + ) +) diff --git a/man/roxygen2/meta.R b/man/roxygen2/meta.R deleted file mode 100644 index b8aec5fc..00000000 --- a/man/roxygen2/meta.R +++ /dev/null @@ -1,7 +0,0 @@ -list( - rd_family_title = list( - bibtex = "Coercing objects from/to BibTeX:", - git = "Git utils:", - schema = "CFF schemas:" - ) -) diff --git a/man/toBibtex.cff.Rd b/man/toBibtex.cff.Rd new file mode 100644 index 00000000..01548d49 --- /dev/null +++ b/man/toBibtex.cff.Rd @@ -0,0 +1,47 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/cff-methods.R +\name{toBibtex.cff} +\alias{toBibtex.cff} +\title{Converting \code{\link[=cff-class]{cff}} objects to BibTeX} +\usage{ +\method{toBibtex}{cff}(object, ..., what = c("preferred", "references", "all")) +} +\arguments{ +\item{object}{\code{cff} object.} + +\item{...}{Arguments passed to \code{\link[utils:toLatex]{utils::toBibtex()}}.} + +\item{what}{Fields to extract. The value could be: +\itemize{ +\item \code{preferred}: This would create a single entry with the main citation +info of the package. +\item \code{references}: Extract all the entries on \code{references}. +\item \code{all}: A combination of the previous two options. This would extract +both the preferred citation info and the references. +}} +} +\description{ +This method convert \code{\link[=cff-class]{cff}} objects to \code{Bibtex} objects as +provided by \code{\link[utils:toLatex]{utils::toBibtex()}}. These objects are character vectors with +BibTeX markup. +} +\examples{ +a_cff <- cff_create("cffr") +a_cff$`preferred-citation` + +toBibtex(a_cff, "preferred") +} +\seealso{ +\code{\link[utils:toLatex]{utils::toBibtex()}} + +Other functions for working with BibTeX format: +\code{\link{cff_read}()}, +\code{\link{cff_read_biblines}()}, +\code{\link{cff_to_bibentry}()}, +\code{\link{encoded_utf_to_latex}()}, +\code{\link{write_bib}()}, +\code{\link{write_citation}()} +} +\concept{bibtex} +\concept{s3method} +\keyword{internal} diff --git a/man/write_bib.Rd b/man/write_bib.Rd index 5240e5a0..d15c769f 100644 --- a/man/write_bib.Rd +++ b/man/write_bib.Rd @@ -54,10 +54,12 @@ following packages: \item \CRANpkg{rbibutils} } -Other BibTeX helpers: -\code{\link{cff_from_bibtex}()}, +Other functions for working with BibTeX format: +\code{\link{cff_read}()}, +\code{\link{cff_read_biblines}()}, \code{\link{cff_to_bibentry}()}, \code{\link{encoded_utf_to_latex}()}, +\code{\link{toBibtex.cff}()}, \code{\link{write_citation}()} } -\concept{BibTeX helpers} +\concept{bibtex} diff --git a/man/write_citation.Rd b/man/write_citation.Rd index 55d8d8fe..63b4522c 100644 --- a/man/write_citation.Rd +++ b/man/write_citation.Rd @@ -89,10 +89,12 @@ R version 4.3.0 (2023-04-21) edition. \seealso{ \code{\link[=bibentry]{bibentry()}} and \code{style} argument. -Other BibTeX helpers: -\code{\link{cff_from_bibtex}()}, +Other functions for working with BibTeX format: +\code{\link{cff_read}()}, +\code{\link{cff_read_biblines}()}, \code{\link{cff_to_bibentry}()}, \code{\link{encoded_utf_to_latex}()}, +\code{\link{toBibtex.cff}()}, \code{\link{write_bib}()} } -\concept{BibTeX helpers} +\concept{bibtex} diff --git a/pkgdown/_pkgdown.yml b/pkgdown/_pkgdown.yml index 3c09c5f9..7c8fe68b 100644 --- a/pkgdown/_pkgdown.yml +++ b/pkgdown/_pkgdown.yml @@ -6,41 +6,43 @@ template: card: summary_large_image repo: branch: main -reference: - - title: Main function - desc: >- - The main function of the package and likely to be the only one you would - need. - contents: - - cff_write - - title: Other core functions - desc: 'Create, write and manipulate files and objects.' - contents: - - has_concept("Core functions") - - title: Citation File Format schema - desc: >- - These functions provides lists of valid keys and fields as defined by the - CFF schema.json **(v1.2.0)**. - contents: - - has_concept("Schemas") - - title: Parsers - desc: Helper functions to parse information. - contents: - - has_concept("Parsers") - - title: BibTeX - desc: Convert CFF to BibTeX and viceversa - contents: - - has_concept("BibTeX helpers") - - cff_parse_citation - - title: Datasets - contents: - - has_keyword("datasets") - - title: Continuous Integration - contents: - - has_concept("Git helpers") - - title: About the package - contents: - - cffr-package + +# reference: +# - title: Main function +# desc: >- +# The main function of the package and likely to be the only one you would +# need. +# contents: +# - cff_write +# - title: Other core functions +# desc: 'Create, write and manipulate files and objects.' +# contents: +# - has_concept("Core functions") +# - title: Citation File Format schema +# desc: >- +# These functions provides lists of valid keys and fields as defined by the +# CFF schema.json **(v1.2.0)**. +# contents: +# - has_concept("Schemas") +# - title: Parsers +# desc: Helper functions to parse information. +# contents: +# - has_concept("Parsers") +# - title: BibTeX +# desc: Convert CFF to BibTeX and viceversa +# contents: +# - has_concept("BibTeX helpers") +# - cff_parse_citation +# - title: Datasets +# contents: +# - has_keyword("datasets") +# - title: Continuous Integration +# contents: +# - has_concept("Git helpers") +# - title: About the package +# contents: +# - cffr-package + navbar: structure: left: diff --git a/tests/testthat/_snaps/cff-methods.md b/tests/testthat/_snaps/cff-methods.md index 498fef21..470b1f0e 100644 --- a/tests/testthat/_snaps/cff-methods.md +++ b/tests/testthat/_snaps/cff-methods.md @@ -935,3 +935,106 @@ message: If you use this software, please cite it using these metadata. title: My Research Software +# toBibtex + + Code + toBibtex(full_cff) + Output + @Misc{druskatspaaks:2021, + title = {Citation File Format}, + author = {Stephan Druskat and Jurriaan H. Spaaks and Neil {Chue Hong} and Robert Haines and James Baker and Spencer Bliven and Egon Willighagen and David Pérez-Suárez and Alexander Konovalov}, + year = {2021}, + month = {aug}, + doi = {10.5281/zenodo.5171937}, + url = {https://github.com/citation-file-format/citation-file-format}, + abstract = {The Citation File Format lets you provide citation metadata for software or datasets in plaintext files that are easy to read by both humans and machines.}, + urldate = {2021-11-07}, + } + +--- + + Code + toBibtex(full_cff, what = "references") + Output + @Article{hernangomez:2021, + title = {cffr: Generate Citation File Format Metadata for R Packages}, + author = {Diego Hernangómez}, + year = {2021}, + journal = {Journal of Open Source Software}, + publisher = {The Open Journal}, + volume = {6}, + number = {67}, + pages = {3900}, + doi = {10.21105/joss.03900}, + url = {https://doi.org/10.21105/joss.03900}, + note = {Publisher: The Open Journal}, + } + + @Article{boettigerchamberlain:2016, + title = {RNeXML: A Package for Reading and Writing Richly Annotated Phylogenetic, Character, and Trait Data in R}, + author = {Carl Boettiger and Scott Chamberlain and Rutger Vos and Hilmar Lapp}, + year = {2016}, + journal = {Methods in Ecology and Evolution}, + volume = {7}, + pages = {352--357}, + doi = {10.1111/2041-210X.12469}, + } + + @Book{wickham:2016, + title = {ggplot2: Elegant Graphics for Data Analysis}, + author = {Hadley Wickham}, + year = {2016}, + publisher = {Springer-Verlag New York}, + isbn = {978-3-319-24277-4}, + url = {https://ggplot2.tidyverse.org}, + } + +--- + + Code + toBibtex(full_cff, what = "all") + Output + @Misc{druskatspaaks:2021, + title = {Citation File Format}, + author = {Stephan Druskat and Jurriaan H. Spaaks and Neil {Chue Hong} and Robert Haines and James Baker and Spencer Bliven and Egon Willighagen and David Pérez-Suárez and Alexander Konovalov}, + year = {2021}, + month = {aug}, + doi = {10.5281/zenodo.5171937}, + url = {https://github.com/citation-file-format/citation-file-format}, + abstract = {The Citation File Format lets you provide citation metadata for software or datasets in plaintext files that are easy to read by both humans and machines.}, + urldate = {2021-11-07}, + } + + @Article{hernangomez:2021, + title = {cffr: Generate Citation File Format Metadata for R Packages}, + author = {Diego Hernangómez}, + year = {2021}, + journal = {Journal of Open Source Software}, + publisher = {The Open Journal}, + volume = {6}, + number = {67}, + pages = {3900}, + doi = {10.21105/joss.03900}, + url = {https://doi.org/10.21105/joss.03900}, + note = {Publisher: The Open Journal}, + } + + @Article{boettigerchamberlain:2016, + title = {RNeXML: A Package for Reading and Writing Richly Annotated Phylogenetic, Character, and Trait Data in R}, + author = {Carl Boettiger and Scott Chamberlain and Rutger Vos and Hilmar Lapp}, + year = {2016}, + journal = {Methods in Ecology and Evolution}, + volume = {7}, + pages = {352--357}, + doi = {10.1111/2041-210X.12469}, + } + + @Book{wickham:2016, + title = {ggplot2: Elegant Graphics for Data Analysis}, + author = {Hadley Wickham}, + year = {2016}, + publisher = {Springer-Verlag New York}, + isbn = {978-3-319-24277-4}, + url = {https://ggplot2.tidyverse.org}, + } + diff --git a/tests/testthat/_snaps/cff_from_bibtex.md b/tests/testthat/_snaps/cff_read_biblines.md similarity index 53% rename from tests/testthat/_snaps/cff_from_bibtex.md rename to tests/testthat/_snaps/cff_read_biblines.md index aea357cb..f6a9c7bb 100644 --- a/tests/testthat/_snaps/cff_from_bibtex.md +++ b/tests/testthat/_snaps/cff_read_biblines.md @@ -1,4 +1,22 @@ -# From Bibtex entries +# Errors and messages + + Code + cff_read_biblines(a_cff) + Condition + Error in `cff_read_biblines()`: + ! `x` should be a , not a . + +--- + + Code + cff_read_biblines("a bad line") + Message + ! `x` doesn't look as a BibTeX entry. Check the results. + Condition + Error: + ! Invalid bib file + +# Read lines Code list @@ -23,3 +41,11 @@ notes: This is a full MISC entry medium: Handed out at O'Hare +--- + + Code + fromfile <- cff_read_biblines(tmpbib) + Message + ! `x` seems to be a ".bib" file, not a BibTeX entry. + i Reading `x` with `cffr:cff_read_bib()` + diff --git a/tests/testthat/_snaps/deprecated.md b/tests/testthat/_snaps/deprecated.md index df31b200..2c99e970 100644 --- a/tests/testthat/_snaps/deprecated.md +++ b/tests/testthat/_snaps/deprecated.md @@ -16,3 +16,21 @@ `cff_extract_to_bibtex()` was deprecated in cffr 0.6.0. i Function renamed, use `cff_to_bibentry()` instead. +# cff_from_bibtex + + Code + ffile <- cff_from_bibtex(x2) + Condition + Warning: + `cff_from_bibtex()` was deprecated in cffr 0.5.0. + i Function renamed, use `cff_read_bib()` instead. + +--- + + Code + flines <- cff_from_bibtex(x) + Condition + Warning: + `cff_from_bibtex()` was deprecated in cffr 0.5.0. + i Function renamed, use `cff_read_biblines()` instead. + diff --git a/tests/testthat/test-cff-methods.R b/tests/testthat/test-cff-methods.R index f6318558..4311eb20 100644 --- a/tests/testthat/test-cff-methods.R +++ b/tests/testthat/test-cff-methods.R @@ -196,3 +196,58 @@ test_that("head and tail", { expect_snapshot(tail(a_cff, 2)) expect_s3_class(tail(a_cff, 2), "cff") }) + +test_that("toBibtex", { + # Create several alternatives + descobj <- cff_read_description(system.file("examples/DESCRIPTION_basic", + package = "cffr" + )) + + citobj <- cff_read_citation(system.file("examples/CITATION_basic", + package = "cffr" + )) + newbib <- cff_read_bib(system.file("examples/example.bib", + package = "cffr" + )) + + full_cff <- merge_desc_cit(descobj, c(newbib, citobj)) + full_cff <- new_cff(full_cff) + expect_true(cff_validate(full_cff, verbose = FALSE)) + + # A. validate extractions + expect_snapshot(toBibtex(full_cff)) + expect_snapshot(toBibtex(full_cff, what = "references")) + expect_snapshot(toBibtex(full_cff, what = "all")) + + # single entries + single <- toBibtex(full_cff$`preferred-citation`) + expect_s3_class(single, "Bibtex") + expect_equal(sum(names(single) == "title"), 1) + + # Several entries + several <- toBibtex(full_cff$references) + expect_s3_class(several, "Bibtex") + expect_equal(sum(names(several) == "title"), 3) + + + # One entry + oneent <- toBibtex(full_cff$references[[1]]) + expect_s3_class(single, "Bibtex") + expect_equal(sum(names(oneent) == "title"), 1) + + fromfile <- toBibtex(newbib) + expect_s3_class(fromfile, "Bibtex") + expect_equal(sum(names(fromfile) == "title"), 2) + + # From lines + string <- "@book{einstein1921, + title = {Relativity: The Special and the General Theory}, + author = {Einstein, A.}, + year = 1920, + publisher = {Henry Holt and Company}, + address = {London, United Kingdom}, + isbn = 9781587340925}" + + froml <- toBibtex(cff_read_biblines(string)) + expect_equal(sum(names(froml) == "title"), 1) +}) diff --git a/tests/testthat/test-cff_from_bibtex.R b/tests/testthat/test-cff_read_biblines.R similarity index 59% rename from tests/testthat/test-cff_from_bibtex.R rename to tests/testthat/test-cff_read_biblines.R index 2ea00ae1..541096e5 100644 --- a/tests/testthat/test-cff_from_bibtex.R +++ b/tests/testthat/test-cff_read_biblines.R @@ -1,12 +1,11 @@ -test_that("test errors", { - x <- c(1, 2, 3) - expect_error(cff_from_bibtex(x)) - - x <- cff() - expect_error(cff_from_bibtex(x)) +test_that("Errors and messages", { + skip_if_not_installed("bibtex", "0.5.0") + a_cff <- cff() + expect_snapshot(cff_read_biblines(a_cff), error = TRUE) + expect_snapshot(cff_read_biblines("a bad line"), error = TRUE) }) -test_that("From Bibtex entries", { +test_that("Read lines", { skip_if_not_installed("bibtex", "0.5.0") x <- c( @@ -28,10 +27,17 @@ test_that("From Bibtex entries", { }" ) - list <- cff_from_bibtex(x) + list <- cff_read_biblines(x) expect_s3_class(list, "cff") expect_length(list, 2) expect_snapshot(list) + + # Create a Bibtex file with that lines + tmpbib <- tempfile(fileext = ".bib") + writeLines(x, tmpbib) + + expect_snapshot(fromfile <- cff_read_biblines(tmpbib)) + expect_identical(fromfile, list) }) diff --git a/tests/testthat/test-deprecated.R b/tests/testthat/test-deprecated.R index 26bc33eb..e4fe8249 100644 --- a/tests/testthat/test-deprecated.R +++ b/tests/testthat/test-deprecated.R @@ -15,3 +15,23 @@ test_that("cff_to_bibtex", { expect_identical(current, old1) }) + +test_that("cff_from_bibtex", { + # From file + x2 <- system.file("examples/example.bib", package = "cffr") + expect_snapshot(ffile <- cff_from_bibtex(x2)) + expect_identical(ffile, cff_read_bib(x2)) + + # From lines + x <- "@book{einstein1921, + title = {Relativity: The Special and the General Theory}, + author = {Einstein, Albert}, + year = 1920, + publisher = {Henry Holt and Company}, + address = {London, United Kingdom}, + isbn = 9781587340925 + }" + + expect_snapshot(flines <- cff_from_bibtex(x)) + expect_identical(flines, cff_read_biblines(x)) +}) diff --git a/vignettes/bibtex_cff.Rmd b/vignettes/bibtex_cff.Rmd index c86fdeb6..04f621dc 100644 --- a/vignettes/bibtex_cff.Rmd +++ b/vignettes/bibtex_cff.Rmd @@ -5,7 +5,7 @@ bibliography: REFERENCES.bib author: Diego Hernangómez description: >- This article presents a crosswalk between BibTeX and Citation File Format - [@druskat_citation_2021], as it is performed by the cffr package + [@druskat_citation_2021], as it is performed by the **cffr** package [@hernangomez2021]. abstract: >- This article introduces a crosswalk between **BibTeX** and the Citation File @@ -130,11 +130,16 @@ The final results of the entry as a text string would be parsed as[^1]: entry ``` -Additionally, the **cffr** package [@hernangomez2021] incorporates a function -`cff_from_bibtex()` that can be used to read and transform **BibTeX** strings -into different formats: +Additionally, the **cffr** package [@hernangomez2021] incorporates the following +capabilities that can be used to read and transform **BibTeX** format into +different formats: -```{r cffbibread,comment="#>"} +- `cff_read_bib()` reads \*.bib files. +- `cff_read_biblines()` can read **BibTeX** entries that are already stored in + a variable. +- A S3 method `toBibtex.cff()` that converts from + +```{r cffbibread, comment="#>"} string <- "@book{einstein1921, title = {Relativity: The Special and the General Theory}, author = {Einstein, A.}, @@ -145,16 +150,12 @@ string <- "@book{einstein1921, # To cff library(cffr) -cff_format <- cff_from_bibtex(string) +cff_format <- cff_read_biblines(string) cff_format -# To citation R format and bibtex -citation_format <- cff_to_bibentry(cff_format) -class(citation_format) -citation_format - -toBibtex(citation_format) +# To BibTeX with S3 method +toBibtex(cff_format) ``` ## BibTeX Definitions @@ -571,13 +572,13 @@ bib <- "@article{article-full, pages = {73+}, note = {This is a full ARTICLE entry}}" -cff_from_bibtex(bib) +cff_read_biblines(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r echo=FALSE} -toBibtex(cff_to_bibentry(cff_from_bibtex(bib))) +toBibtex(cff_read_biblines(bib)) ``` ### \@book / \@inbook {#book-inbook} @@ -653,13 +654,13 @@ bib <- "@book{book-full, edition = {Second} }" -cff_from_bibtex(bib) +cff_read_biblines(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r, echo=FALSE} -toBibtex(cff_to_bibentry(cff_from_bibtex(bib))) +toBibtex(cff_read_biblines(bib)) ``` **Examples: \@inbook** @@ -703,13 +704,13 @@ bib <- "@inbook{inbook-full, chapter = {1.2} }" -cff_from_bibtex(bib) +cff_read_biblines(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r echo=FALSE,} -toBibtex(cff_to_bibentry(cff_from_bibtex(bib))) +toBibtex(cff_read_biblines(bib)) ``` ### \@booklet {#booklet} @@ -762,13 +763,13 @@ bib <- "@booklet{booklet-full, howpublished = {Vernier Art Center} }" -cff_from_bibtex(bib) +cff_read_biblines(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r echo=FALSE, } -toBibtex(cff_to_bibentry(cff_from_bibtex(bib))) +toBibtex(cff_read_biblines(bib)) ``` ### \@conference / \@inproceedings {#conf_inproc} @@ -833,13 +834,13 @@ bib <- "@inproceedings{inproceedings-full, organization = {The OX Association for Computing Machinery} }" -cff_from_bibtex(bib) +cff_read_biblines(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r echo=FALSE,} -toBibtex(cff_to_bibentry(cff_from_bibtex(bib))) +toBibtex(cff_read_biblines(bib)) ``` ### \@incollection {#incol} @@ -914,13 +915,13 @@ bib <- "@incollection{incollection-full, edition = {Third} }" -cff_from_bibtex(bib) +cff_read_biblines(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r echo=FALSE,} -toBibtex(cff_to_bibentry(cff_from_bibtex(bib))) +toBibtex(cff_read_biblines(bib)) ``` ### \@manual @@ -979,13 +980,13 @@ bib <- "@manual{manual-full, edition = {Silver} }" -cff_from_bibtex(bib) +cff_read_biblines(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r echo=FALSE,} -toBibtex(cff_to_bibentry(cff_from_bibtex(bib))) +toBibtex(cff_read_biblines(bib)) ``` ### \@mastersthesis / \@phdthesis @@ -1048,13 +1049,13 @@ bib <- "@mastersthesis{mastersthesis-full, type = {Master's project} }" -cff_from_bibtex(bib) +cff_read_biblines(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r, echo=FALSE} -toBibtex(cff_to_bibentry(cff_from_bibtex(bib))) +toBibtex(cff_read_biblines(bib)) ``` **Examples: \@phdthesis** @@ -1088,13 +1089,13 @@ bib <- "@phdthesis{phdthesis-full, type = {{PhD} Dissertation} }" -cff_from_bibtex(bib) +cff_read_biblines(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r echo=FALSE,} -toBibtex(cff_to_bibentry(cff_from_bibtex(bib))) +toBibtex(cff_read_biblines(bib)) ``` ### \@misc @@ -1151,13 +1152,13 @@ bib <- "@misc{misc-full, howpublished = {Handed out at O'Hare} }" -cff_from_bibtex(bib) +cff_read_biblines(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r echo=FALSE,} -toBibtex(cff_to_bibentry(cff_from_bibtex(bib))) +toBibtex(cff_read_biblines(bib)) ``` ### \@proceedings @@ -1225,13 +1226,13 @@ bib <- "@proceedings{proceedings-full, organization = {The OX Association for Computing Machinery} }" -cff_from_bibtex(bib) +cff_read_biblines(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r echo=FALSE,} -toBibtex(cff_to_bibentry(cff_from_bibtex(bib))) +toBibtex(cff_read_biblines(bib)) ``` ### \@techreport @@ -1288,13 +1289,13 @@ bib <- "@techreport{techreport-full, type = {Wishful Research Result} }" -cff_from_bibtex(bib) +cff_read_biblines(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r echo=FALSE,} -toBibtex(cff_to_bibentry(cff_from_bibtex(bib))) +toBibtex(cff_read_biblines(bib)) ``` ### \@unpublished @@ -1339,13 +1340,13 @@ bib <- "@unpublished{unpublished-minimal, note = {Talk at Fanstord University (this is a minimal UNPUBLISHED entry)} }" -cff_from_bibtex(bib) +cff_read_biblines(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r echo=FALSE,} -toBibtex(cff_to_bibentry(cff_from_bibtex(bib))) +toBibtex(cff_read_biblines(bib)) ``` ## Appendix A: **\@inbook** in BibTeX and BibLaTeX {#appendix_inbook} @@ -1412,13 +1413,13 @@ bib <- "@inbook{inbook-biblatex, chapter = {4.5} }" -cff_from_bibtex(bib) +cff_read_biblines(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r echo=FALSE,} -toBibtex(cff_to_bibentry(cff_from_bibtex(bib))) +toBibtex(cff_read_biblines(bib)) ``` ## Appendix B: [CFF key:type]{.underline} values {#appendix_cff_type} From aa169832921b089cd7c6ffff62bbd7949d7bdffc Mon Sep 17 00:00:00 2001 From: dieghernan Date: Tue, 27 Feb 2024 08:05:43 +0000 Subject: [PATCH 13/32] Improve linting --- R/cff_create.R | 72 ++++++++++++++++++++++++++++--------------- R/cff_gha_update.R | 14 ++++++--- R/cff_git_hook.R | 13 ++++++-- R/cff_read.R | 8 +++-- man/cff_gha_update.Rd | 8 ++--- man/cff_git_hook.Rd | 5 +-- man/cff_read.Rd | 8 +++-- 7 files changed, 87 insertions(+), 41 deletions(-) diff --git a/R/cff_create.R b/R/cff_create.R index d43ff43c..2e14f2b7 100644 --- a/R/cff_create.R +++ b/R/cff_create.R @@ -124,6 +124,48 @@ cff_create <- function(x, keys = list(), cff_version = "1.2.0", cli::cli_abort(msg) } + # Detect sources and build cff + result_parsed <- detect_sources( + x, cff_version, gh_keywords, + dependencies, authors_roles + ) + + desc_path <- result_parsed[["desc_path"]] + instpack <- result_parsed[["instpack"]] + cffobjend <- result_parsed[["cffobjend"]] + + + + # Add software dependencies + if (dependencies) { + deps <- parse_dependencies(desc_path, instpack) + + cffobjend$references <- unique(c(cffobjend$references, deps)) + } + + # Additional keys + if (!is.null(keys)) { + keys <- fuzzy_keys(keys) + cffobjendmod <- cffobjend[setdiff(names(cffobjend), names(keys))] + cffobjend <- modifyList(cffobjendmod, keys, keep.null = FALSE) + cffobjend <- as.cff(cffobjend) + } + + + # Order + cffobjend <- cffobjend[cff_schema_keys()] + + # Enhance authors info + if (!is.null(cffobjend$`preferred-citation`)) { + cffobjend$`preferred-citation`$authors <- enhance_pref_authors(cffobjend) + } + cffobjend <- as.cff(cffobjend) + cffobjend +} + +detect_sources <- function(x, cff_version = "1.2.0", + gh_keywords = TRUE, dependencies = TRUE, + authors_roles = c("aut", "cre")) { instpack <- as.character(installed.packages()[, "Package"]) # Set initially citobj to NULL @@ -183,29 +225,11 @@ cff_create <- function(x, keys = list(), cff_version = "1.2.0", cffobjend <- merge_desc_cit(cffobj, citobj) - # Add software dependencies - if (dependencies) { - deps <- parse_dependencies(desc_path, instpack) - - cffobjend$references <- unique(c(cffobjend$references, deps)) - } + # Return collected info - # Additional keys - if (!is.null(keys)) { - keys <- fuzzy_keys(keys) - cffobjendmod <- cffobjend[setdiff(names(cffobjend), names(keys))] - cffobjend <- modifyList(cffobjendmod, keys, keep.null = FALSE) - cffobjend <- as.cff(cffobjend) - } - - - # Order - cffobjend <- cffobjend[cff_schema_keys()] - - # Enhance authors info - if (!is.null(cffobjend$`preferred-citation`)) { - cffobjend$`preferred-citation`$authors <- enhance_pref_authors(cffobjend) - } - cffobjend <- as.cff(cffobjend) - cffobjend + list( + desc_path = desc_path, + instpack = instpack, + cffobjend = cffobjend + ) } diff --git a/R/cff_gha_update.R b/R/cff_gha_update.R index 22ffedf7..5d53b0bc 100644 --- a/R/cff_gha_update.R +++ b/R/cff_gha_update.R @@ -2,13 +2,14 @@ #' #' @description #' -#' This function would install a GitHub Action on your repo. The action +#' This function would install a +#' [GitHub Action](https://github.com/features/actions) on your repo. The action #' will update your `CITATION.cff` when any of these events occur: #' - You publish a new release of the package. #' - Your `DESCRIPTION` or `inst/CITATION` are modified. #' - The action can be run also manually. #' -#' @param path Project directory +#' @param path Project directory. #' @param overwrite Logical. If already present, do you want to overwrite your #' action? #' @@ -17,8 +18,13 @@ #' @details #' #' Triggers on your action can be modified, see -#' [Events that trigger -#' workflows](https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows). +#' ```{r, echo=FALSE, results='asis'} +#' +#' cat(paste0(" [Events that trigger workflows]", +#' "(https://docs.github.com/en/actions/learn-github-actions/", +#' "events-that-trigger-workflows).")) +#' +#' ``` #' #' @examples #' \dontrun{ diff --git a/R/cff_git_hook.R b/R/cff_git_hook.R index f1c2d7aa..55e1bbd9 100644 --- a/R/cff_git_hook.R +++ b/R/cff_git_hook.R @@ -2,8 +2,15 @@ #' #' @description #' -#' Install a [pre-commit -#' hook](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks#_committing_workflow_hooks) +#' Install a +#' +#' ```{r, echo=FALSE, results='asis'} +#' +#' cat(paste0(" [pre-commit hook]", +#' "(https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks", +#' "#_committing_workflow_hooks) ")) +#' +#' ``` #' that remembers you to update your `CITATION.cff` file. #' #' @name cff_git_hook @@ -26,7 +33,7 @@ #' following conditions are met: #' - You included in a commit your `DESCRIPTION` or `inst/CITATION` file, you #' are not including your `CITATION.cff` and the `CITATION.cff` file is -#' "older" than any of your `DESCRIPTION` or `inst/CITATION` file, or +#' "older" than any of your `DESCRIPTION` or `inst/CITATION` file. #' - You have updated your `CITATION.cff` but you are not including it on #' your commit. #' diff --git a/R/cff_read.R b/R/cff_read.R index 7dc337b8..0dd87517 100644 --- a/R/cff_read.R +++ b/R/cff_read.R @@ -81,14 +81,18 @@ #' head(from_cff_file, 7) #' #' # Create cff object from DESCRIPTION -#' from_desc <- cff_read(system.file("examples/DESCRIPTION_basic", package = "cffr")) +#' from_desc <- cff_read(system.file("examples/DESCRIPTION_basic", +#' package = "cffr" +#' )) #' #' from_desc #' #' # Create cff object from BibTex #' #' if (requireNamespace("bibtex", quietly = TRUE)) { -#' from_bib <- cff_read(system.file("examples/example.bib", package = "cffr")) +#' from_bib <- cff_read(system.file("examples/example.bib", +#' package = "cffr" +#' )) #' #' # First item only #' from_bib[[1]] diff --git a/man/cff_gha_update.Rd b/man/cff_gha_update.Rd index 65568d81..c45049d9 100644 --- a/man/cff_gha_update.Rd +++ b/man/cff_gha_update.Rd @@ -7,7 +7,7 @@ cff_gha_update(path = ".", overwrite = FALSE) } \arguments{ -\item{path}{Project directory} +\item{path}{Project directory.} \item{overwrite}{Logical. If already present, do you want to overwrite your action?} @@ -16,7 +16,8 @@ action?} Invisible, this function is called by its side effects. } \description{ -This function would install a GitHub Action on your repo. The action +This function would install a +\href{https://github.com/features/actions}{GitHub Action} on your repo. The action will update your \code{CITATION.cff} when any of these events occur: \itemize{ \item You publish a new release of the package. @@ -25,8 +26,7 @@ will update your \code{CITATION.cff} when any of these events occur: } } \details{ -Triggers on your action can be modified, see -\href{https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows}{Events that trigger workflows}. +Triggers on your action can be modified, see \href{https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows}{Events that trigger workflows}. } \examples{ \dontrun{ diff --git a/man/cff_git_hook.Rd b/man/cff_git_hook.Rd index 2a614676..85a7f720 100644 --- a/man/cff_git_hook.Rd +++ b/man/cff_git_hook.Rd @@ -14,7 +14,8 @@ cff_git_hook_remove() Invisible. This function is called for its side effects. } \description{ -Install a \href{https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks#_committing_workflow_hooks}{pre-commit hook} +Install a +\href{https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks#_committing_workflow_hooks}{pre-commit hook} that remembers you to update your \code{CITATION.cff} file. } \details{ @@ -27,7 +28,7 @@ following conditions are met: \itemize{ \item You included in a commit your \code{DESCRIPTION} or \code{inst/CITATION} file, you are not including your \code{CITATION.cff} and the \code{CITATION.cff} file is -"older" than any of your \code{DESCRIPTION} or \code{inst/CITATION} file, or +"older" than any of your \code{DESCRIPTION} or \code{inst/CITATION} file. \item You have updated your \code{CITATION.cff} but you are not including it on your commit. } diff --git a/man/cff_read.Rd b/man/cff_read.Rd index a443541f..6e97814b 100644 --- a/man/cff_read.Rd +++ b/man/cff_read.Rd @@ -92,14 +92,18 @@ from_cff_file <- cff_read(system.file("examples/CITATION_basic.cff", head(from_cff_file, 7) # Create cff object from DESCRIPTION -from_desc <- cff_read(system.file("examples/DESCRIPTION_basic", package = "cffr")) +from_desc <- cff_read(system.file("examples/DESCRIPTION_basic", + package = "cffr" +)) from_desc # Create cff object from BibTex if (requireNamespace("bibtex", quietly = TRUE)) { - from_bib <- cff_read(system.file("examples/example.bib", package = "cffr")) + from_bib <- cff_read(system.file("examples/example.bib", + package = "cffr" + )) # First item only from_bib[[1]] From df92a9d51987cfcda462638ce49a5ae8d3d0664a Mon Sep 17 00:00:00 2001 From: dieghernan Date: Tue, 27 Feb 2024 09:07:46 +0000 Subject: [PATCH 14/32] Update tests --- tests/testthat/_snaps/cff_to_bibentry.md | 4 +--- tests/testthat/test-cff_to_bibentry.R | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/testthat/_snaps/cff_to_bibentry.md b/tests/testthat/_snaps/cff_to_bibentry.md index 80e419b9..e60fd87a 100644 --- a/tests/testthat/_snaps/cff_to_bibentry.md +++ b/tests/testthat/_snaps/cff_to_bibentry.md @@ -12,9 +12,7 @@ number = {2}, pages = {97--111}, month = {January}, - keywords = {Some}, - keywords = {simple}, - keywords = {keywords}, + keywords = {Some, simple, keywords}, } --- diff --git a/tests/testthat/test-cff_to_bibentry.R b/tests/testthat/test-cff_to_bibentry.R index b4612dea..165dbcdf 100644 --- a/tests/testthat/test-cff_to_bibentry.R +++ b/tests/testthat/test-cff_to_bibentry.R @@ -10,7 +10,7 @@ test_that("Article to bibtex", { number = 2, pages = "97--111", month = "January", - keywords = c("Some", "simple", "keywords") + keywords = "Some, simple, keywords" ) expect_snapshot(toBibtex(bib)) x <- cff_parse_citation(bib) From 186bdb1b0d5314bab8f42f24bd7bb28f7bd1f92e Mon Sep 17 00:00:00 2001 From: Diego H Date: Tue, 27 Feb 2024 22:46:15 +0100 Subject: [PATCH 15/32] Add new cff_write funs --- NAMESPACE | 2 + R/cff.R | 4 +- R/cff_read.R | 22 ++- R/cff_write.R | 1 + R/cff_write_misc.R | 167 ++++++++++++++++++ R/deprecated.R | 3 +- man/cff_read.Rd | 1 + man/cff_read_biblines.Rd | 1 + man/cff_to_bibentry.Rd | 1 + man/cff_write.Rd | 4 + man/cff_write_misc.Rd | 128 ++++++++++++++ man/deprecated_cff_from_bib.Rd | 3 +- man/encoded_utf_to_latex.Rd | 1 + man/toBibtex.cff.Rd | 1 + man/write_bib.Rd | 1 + man/write_citation.Rd | 1 + tests/testthat/_snaps/cff_write_misc.md | 16 ++ .../testthat/_snaps/cff_write_misc/CITAT_ION | 78 ++++++++ .../testthat/_snaps/cff_write_misc/append.bib | 15 ++ .../testthat/_snaps/cff_write_misc/ascii.bib | 4 + .../testthat/_snaps/cff_write_misc/noext.bib | 4 + tests/testthat/test-cff_write_misc.R | 147 +++++++++++++++ 22 files changed, 589 insertions(+), 16 deletions(-) create mode 100644 R/cff_write_misc.R create mode 100644 man/cff_write_misc.Rd create mode 100644 tests/testthat/_snaps/cff_write_misc.md create mode 100644 tests/testthat/_snaps/cff_write_misc/CITAT_ION create mode 100644 tests/testthat/_snaps/cff_write_misc/append.bib create mode 100644 tests/testthat/_snaps/cff_write_misc/ascii.bib create mode 100644 tests/testthat/_snaps/cff_write_misc/noext.bib create mode 100644 tests/testthat/test-cff_write_misc.R diff --git a/NAMESPACE b/NAMESPACE index dc182295..dfde1c7d 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -33,6 +33,8 @@ export(cff_to_bibentry) export(cff_to_bibtex) export(cff_validate) export(cff_write) +export(cff_write_bib) +export(cff_write_citation) export(encoded_utf_to_latex) export(write_bib) export(write_citation) diff --git a/R/cff.R b/R/cff.R index 82152548..0a66e9f2 100644 --- a/R/cff.R +++ b/R/cff.R @@ -195,8 +195,8 @@ new_cff <- function(x) { # Remove NULLs x <- drop_null(x) - # Remove duplicated names - x <- x[!duplicated(names(x))] + # Remove duplicated names if named + if (!is.null(names(x))) x <- x[!duplicated(names(x))] # Now apply cff class to nested lists x <- lapply(x, rapply_cff) diff --git a/R/cff_read.R b/R/cff_read.R index 0dd87517..c8214a5d 100644 --- a/R/cff_read.R +++ b/R/cff_read.R @@ -120,6 +120,15 @@ cff_read <- function(path, ...) { } filetype <- guess_type_file(path) + if (is.null(filetype)) { + cli::cli_abort( + paste0( + "Don't recognize the file type of {.file {path}}.", + " Use a specific function (e.g. {.fn cffr:cff_read_description}" + ) + ) + } + endobj <- switch(filetype, "cff_citation" = cff_read_cff_citation(path, ...), "description" = cff_read_description(path, ...), @@ -248,12 +257,6 @@ cff_read_citation <- function(path, meta = NULL, ...) { # nocov end } tocff <- lapply(the_cit, cff_parse_citation) - make_names <- vapply(tocff, function(x) { - myname <- gsub("[^a-z]", "", tolower(x$title)) - substr(myname, 1, 10) - }, character(1)) - - names(tocff) <- make_names tocff <- new_cff(tocff) unname(tocff) } @@ -336,12 +339,7 @@ guess_type_file <- function(path) { return("description") } - cli::cli_abort( - paste0( - "Don't recognize the file type of {.file {path}}.", - " Use a specific function (e.g. {.fn cffr:cff_read_description}" - ) - ) + return(NULL) } #' Parse and clean data from DESCRIPTION to create metadata diff --git a/R/cff_write.R b/R/cff_write.R index 30bcd942..ada57c63 100644 --- a/R/cff_write.R +++ b/R/cff_write.R @@ -10,6 +10,7 @@ #' [`cff`] object and writes it out to a YAML-formatted file in one command. #' #' @family core +#' @family write #' #' @param x The source that would be used for generating #' the `CITATION.cff` file. It could be: diff --git a/R/cff_write_misc.R b/R/cff_write_misc.R new file mode 100644 index 00000000..c00ebe6e --- /dev/null +++ b/R/cff_write_misc.R @@ -0,0 +1,167 @@ +#' Export **R** objects to different file types +#' +#' @description +#' +#' Export **R** objects representing citations to specific file types: +#' - [cff_write_bib()] creates a `.bib` file. +#' - [cff_write_citation()] creates a **R** citation file as explained in +#' Section 1.9 CITATION files of *Writing R Extensions* (R Core Team 2023). +#' +#' @param x A [`bibentry`][bibentry()] or a [`cff`][cff-class] object. +#' @param file Name of the file to be created. If `NULL` it would display the +#' lines to be written. +#' @param append Whether to append the entries to an existing file or not. +#' @param verbose Display informative messages +#' @param ascii Whether to write the entries using ASCII characters only or not. +#' @inheritDotParams cff_to_bibentry +#' +#' @references +#' +#' - R Core Team (2023). _Writing R Extensions_. +#' +#' +#' @export +#' @rdname cff_write_misc +#' @family bibtex +#' @family write +#' +#' @return +#' Writes the corresponding file specified on the `file` parameter. +#' +#' @details +#' +#' When `x` is a `cff` object it would be converted to `bibentry` using +#' [cff_to_bibentry()]. +#' +#' For security reasons, if the file already exists the function would create +#' a backup copy on the same directory. +#' +#' @seealso +#' `vignette("bibtex_cff", "cffr")`, [knitr::write_bib()] and the +#' following packages: +#' - \CRANpkg{bibtex}. +#' - \CRANpkg{RefManageR} +#' - \CRANpkg{rbibutils} +#' +#' @examples +#' +#' bib <- bibentry("Misc", +#' title = "My title", +#' author = "Fran Pérez" +#' ) +#' +#' my_temp_bib <- tempfile(fileext = ".bib") +#' +#' cff_write_bib(bib, file = my_temp_bib) +#' +#' cat(readLines(my_temp_bib), sep = "\n") +#' +#' cff_write_bib(bib, file = my_temp_bib, ascii = TRUE, append = TRUE) +#' +#' cat(readLines(my_temp_bib), sep = "\n") +cff_write_bib <- function(x, file = tempfile(fileext = ".bib"), append = FALSE, + verbose = TRUE, ascii = FALSE, ...) { + if (inherits(x, "cff")) { + x <- cff_to_bibentry(x, ...) + } + + if (!inherits(x, "bibentry")) { + cli::cli_abort( + paste0( + "{.arg x} should be a {.cls bibentry} object, not a ", + "{.cls {class(x)}} object." + ) + ) + } + + btex <- toBibtex(x) + + if (ascii) { + # Base encoding as per file() + btex <- encoded_utf_to_latex(btex) + class(btex) <- "Bibtex" + } else { + btex <- enc2utf8(btex) + } + + if (tools::file_ext(file) != "bib") file <- paste0(file, ".bib") + write_lines_msg(btex, file, verbose, append) + return(invisible(NULL)) +} + +#' @export +#' @rdname cff_write_misc +#' @name cff_write_citation +#' @examples +#' +#' # Create a CITATION file +#' +#' # Use a system file +#' f <- system.file("examples/preferred-citation-book.cff", package = "cffr") +#' a_cff <- cff_read(f) +#' +#' out <- file.path(tempdir(), "CITATION") +#' cff_write_citation(a_cff, file = out) +#' +#' # Check by reading, use meta object +#' meta <- packageDescription("cffr") +#' meta$Encoding <- "UTF-8" +#' +#' utils::readCitationFile(out, meta) +cff_write_citation <- function(x, file = tempfile("CITATION_"), + append = FALSE, verbose = TRUE, ...) { + if (inherits(x, "cff")) { + x <- cff_to_bibentry(x, ...) + } + + if (!inherits(x, "bibentry")) { + cli::cli_abort( + paste0( + "{.arg x} should be a {.cls bibentry} object, not a ", + "{.cls {class(x)}} object." + ) + ) + } + + bentr <- format(x, style = "R") + bentr <- c("", bentr) + write_lines_msg(bentr, file, verbose, append) + return(invisible(NULL)) +} + + +write_lines_msg <- function(lines, file, verbose, append) { + # Check that the directory exists, if not create + dir <- dirname(path.expand(file)) + if (!dir.exists(dir)) { + if (verbose) cli::cli_alert_info("Creating directory {.path {dir}}") + dir.create(dir, recursive = TRUE) + } + + # If exists creates a backup + if (file.exists(file)) { + for (i in seq(1, 100)) { + f <- paste0(file, ".bk", i) + if (!file.exists(f)) break + } + + if (verbose) { + cli::cli_alert_info( + "Creating a backup of {.file {file}} in {.file {f}}" + ) + } + file.copy(file, f) + } + + + fh <- file(file, encoding = "UTF-8", open = ifelse(append, "a+", "w+")) + on.exit(if (isOpen(fh)) close(fh)) + if (verbose) { + cli::cli_alert_info("Writing {length(lines)} entr{?y/ies} ...") + } + + writeLines(lines, fh) + if (verbose) { + cli::cli_alert_success("Results written to {.file {file}}") + } +} diff --git a/R/deprecated.R b/R/deprecated.R index 6f95d019..230fc6c7 100644 --- a/R/deprecated.R +++ b/R/deprecated.R @@ -67,7 +67,8 @@ cff_to_bibtex <- function(x, #' #' @return #' -#' See [cff_read_bib()]. +#' See [cff_read_bib()] from reading `*.bib` files and [cff_read_biblines()] +#' for reading a `character` object representing a BibTeX entry. #' #' #' diff --git a/man/cff_read.Rd b/man/cff_read.Rd index 6e97814b..7cd78b85 100644 --- a/man/cff_read.Rd +++ b/man/cff_read.Rd @@ -139,6 +139,7 @@ Reading other files as \code{cff}: Other functions for working with BibTeX format: \code{\link{cff_read_biblines}()}, \code{\link{cff_to_bibentry}()}, +\code{\link{cff_write_bib}()}, \code{\link{encoded_utf_to_latex}()}, \code{\link{toBibtex.cff}()}, \code{\link{write_bib}()}, diff --git a/man/cff_read_biblines.Rd b/man/cff_read_biblines.Rd index de30f007..319659a6 100644 --- a/man/cff_read_biblines.Rd +++ b/man/cff_read_biblines.Rd @@ -63,6 +63,7 @@ if (requireNamespace("bibtex", quietly = TRUE)) { Other functions for working with BibTeX format: \code{\link{cff_read}()}, \code{\link{cff_to_bibentry}()}, +\code{\link{cff_write_bib}()}, \code{\link{encoded_utf_to_latex}()}, \code{\link{toBibtex.cff}()}, \code{\link{write_bib}()}, diff --git a/man/cff_to_bibentry.Rd b/man/cff_to_bibentry.Rd index fd2cfc7b..205fd873 100644 --- a/man/cff_to_bibentry.Rd +++ b/man/cff_to_bibentry.Rd @@ -97,6 +97,7 @@ toBibtex(desc_file) Other functions for working with BibTeX format: \code{\link{cff_read}()}, \code{\link{cff_read_biblines}()}, +\code{\link{cff_write_bib}()}, \code{\link{encoded_utf_to_latex}()}, \code{\link{toBibtex.cff}()}, \code{\link{write_bib}()}, diff --git a/man/cff_write.Rd b/man/cff_write.Rd index 034c0b67..627c3aa8 100644 --- a/man/cff_write.Rd +++ b/man/cff_write.Rd @@ -83,5 +83,9 @@ Other core functions of \CRANpkg{cffr}: \code{\link{cff}()}, \code{\link{cff_create}()}, \code{\link{cff_validate}()} + +Other write: +\code{\link{cff_write_bib}()} } \concept{core} +\concept{write} diff --git a/man/cff_write_misc.Rd b/man/cff_write_misc.Rd new file mode 100644 index 00000000..aa7fc245 --- /dev/null +++ b/man/cff_write_misc.Rd @@ -0,0 +1,128 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/cff_write_misc.R +\name{cff_write_bib} +\alias{cff_write_bib} +\alias{cff_write_citation} +\title{Export \strong{R} objects to different file types} +\usage{ +cff_write_bib( + x, + file = tempfile(fileext = ".bib"), + append = FALSE, + verbose = TRUE, + ascii = FALSE, + ... +) + +cff_write_citation( + x, + file = tempfile("CITATION_"), + append = FALSE, + verbose = TRUE, + ... +) +} +\arguments{ +\item{x}{A \code{\link[=bibentry]{bibentry}} or a \code{\link[=cff-class]{cff}} object.} + +\item{file}{Name of the file to be created. If \code{NULL} it would display the +lines to be written.} + +\item{append}{Whether to append the entries to an existing file or not.} + +\item{verbose}{Display informative messages} + +\item{ascii}{Whether to write the entries using ASCII characters only or not.} + +\item{...}{ + Arguments passed on to \code{\link[=cff_to_bibentry]{cff_to_bibentry}} + \describe{ + \item{\code{what}}{Fields to extract. The value could be: +\itemize{ +\item \code{preferred}: This would create a single entry with the main citation +info of the package. +\item \code{references}: Extract all the entries on \code{references}. +\item \code{all}: A combination of the previous two options. This would extract +both the preferred citation info and the references. +}} + }} +} +\value{ +Writes the corresponding file specified on the \code{file} parameter. +} +\description{ +Export \strong{R} objects representing citations to specific file types: +\itemize{ +\item \code{\link[=cff_write_bib]{cff_write_bib()}} creates a \code{.bib} file. +\item \code{\link[=cff_write_citation]{cff_write_citation()}} creates a \strong{R} citation file as explained in +Section 1.9 CITATION files of \emph{Writing R Extensions} (R Core Team 2023). +} +} +\details{ +When \code{x} is a \code{cff} object it would be converted to \code{bibentry} using +\code{\link[=cff_to_bibentry]{cff_to_bibentry()}}. + +For security reasons, if the file already exists the function would create +a backup copy on the same directory. +} +\examples{ + +bib <- bibentry("Misc", + title = "My title", + author = "Fran Pérez" +) + +my_temp_bib <- tempfile(fileext = ".bib") + +cff_write_bib(bib, file = my_temp_bib) + +cat(readLines(my_temp_bib), sep = "\n") + +cff_write_bib(bib, file = my_temp_bib, ascii = TRUE, append = TRUE) + +cat(readLines(my_temp_bib), sep = "\n") + +# Create a CITATION file + +# Use a system file +f <- system.file("examples/preferred-citation-book.cff", package = "cffr") +a_cff <- cff_read(f) + +out <- file.path(tempdir(), "CITATION") +cff_write_citation(a_cff, file = out) + +# Check by reading, use meta object +meta <- packageDescription("cffr") +meta$Encoding <- "UTF-8" + +utils::readCitationFile(out, meta) +} +\references{ +\itemize{ +\item R Core Team (2023). \emph{Writing R Extensions}. +\url{https://cran.r-project.org/doc/manuals/r-release/R-exts.html} +} +} +\seealso{ +\code{vignette("bibtex_cff", "cffr")}, \code{\link[knitr:write_bib]{knitr::write_bib()}} and the +following packages: +\itemize{ +\item \CRANpkg{bibtex}. +\item \CRANpkg{RefManageR} +\item \CRANpkg{rbibutils} +} + +Other functions for working with BibTeX format: +\code{\link{cff_read}()}, +\code{\link{cff_read_biblines}()}, +\code{\link{cff_to_bibentry}()}, +\code{\link{encoded_utf_to_latex}()}, +\code{\link{toBibtex.cff}()}, +\code{\link{write_bib}()}, +\code{\link{write_citation}()} + +Other write: +\code{\link{cff_write}()} +} +\concept{bibtex} +\concept{write} diff --git a/man/deprecated_cff_from_bib.Rd b/man/deprecated_cff_from_bib.Rd index afa8367a..98d32d82 100644 --- a/man/deprecated_cff_from_bib.Rd +++ b/man/deprecated_cff_from_bib.Rd @@ -19,7 +19,8 @@ cff_from_bibtex(x, encoding = "UTF-8", ...) \item{...}{Other arguments passed to \code{\link[bibtex:read.bib]{bibtex::read.bib()}}.} } \value{ -See \code{\link[=cff_read_bib]{cff_read_bib()}}. +See \code{\link[=cff_read_bib]{cff_read_bib()}} from reading \verb{*.bib} files and \code{\link[=cff_read_biblines]{cff_read_biblines()}} +for reading a \code{character} object representing a BibTeX entry. } \description{ \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#superseded}{\figure{lifecycle-superseded.svg}{options: alt='[Superseded]'}}}{\strong{[Superseded]}} Please use either \code{\link[=cff_read_bib]{cff_read_bib()}} or diff --git a/man/encoded_utf_to_latex.Rd b/man/encoded_utf_to_latex.Rd index a77ad02b..0dc8c4c8 100644 --- a/man/encoded_utf_to_latex.Rd +++ b/man/encoded_utf_to_latex.Rd @@ -49,6 +49,7 @@ Other functions for working with BibTeX format: \code{\link{cff_read}()}, \code{\link{cff_read_biblines}()}, \code{\link{cff_to_bibentry}()}, +\code{\link{cff_write_bib}()}, \code{\link{toBibtex.cff}()}, \code{\link{write_bib}()}, \code{\link{write_citation}()} diff --git a/man/toBibtex.cff.Rd b/man/toBibtex.cff.Rd index 01548d49..ef19173c 100644 --- a/man/toBibtex.cff.Rd +++ b/man/toBibtex.cff.Rd @@ -38,6 +38,7 @@ Other functions for working with BibTeX format: \code{\link{cff_read}()}, \code{\link{cff_read_biblines}()}, \code{\link{cff_to_bibentry}()}, +\code{\link{cff_write_bib}()}, \code{\link{encoded_utf_to_latex}()}, \code{\link{write_bib}()}, \code{\link{write_citation}()} diff --git a/man/write_bib.Rd b/man/write_bib.Rd index d15c769f..8743f9c4 100644 --- a/man/write_bib.Rd +++ b/man/write_bib.Rd @@ -58,6 +58,7 @@ Other functions for working with BibTeX format: \code{\link{cff_read}()}, \code{\link{cff_read_biblines}()}, \code{\link{cff_to_bibentry}()}, +\code{\link{cff_write_bib}()}, \code{\link{encoded_utf_to_latex}()}, \code{\link{toBibtex.cff}()}, \code{\link{write_citation}()} diff --git a/man/write_citation.Rd b/man/write_citation.Rd index 63b4522c..b93a7409 100644 --- a/man/write_citation.Rd +++ b/man/write_citation.Rd @@ -93,6 +93,7 @@ Other functions for working with BibTeX format: \code{\link{cff_read}()}, \code{\link{cff_read_biblines}()}, \code{\link{cff_to_bibentry}()}, +\code{\link{cff_write_bib}()}, \code{\link{encoded_utf_to_latex}()}, \code{\link{toBibtex.cff}()}, \code{\link{write_bib}()} diff --git a/tests/testthat/_snaps/cff_write_misc.md b/tests/testthat/_snaps/cff_write_misc.md new file mode 100644 index 00000000..3204469f --- /dev/null +++ b/tests/testthat/_snaps/cff_write_misc.md @@ -0,0 +1,16 @@ +# Errors + + Code + cff_write_bib(1:4) + Condition + Error in `cff_write_bib()`: + ! `x` should be a object, not a object. + +# Errors citation + + Code + cff_write_citation(1:4) + Condition + Error in `cff_write_citation()`: + ! `x` should be a object, not a object. + diff --git a/tests/testthat/_snaps/cff_write_misc/CITAT_ION b/tests/testthat/_snaps/cff_write_misc/CITAT_ION new file mode 100644 index 00000000..20315d10 --- /dev/null +++ b/tests/testthat/_snaps/cff_write_misc/CITAT_ION @@ -0,0 +1,78 @@ + +bibentry(bibtype = "Misc", + title = "My title", + author = person(given = "Fran", + family = "Pérez")) + +bibentry(bibtype = "InBook", + key = "vanderrealpersoniventityprojectteamconferenceentity:2017", + title = "Book Title", + author = c(person(given = "One Truly", + family = "van der Real Person IV"), + person(family = "Entity Project Team Conference entity")), + year = "2017", + month = "mar", + journal = "PeerJ", + publisher = "Entity Project Team Conference entity", + address = "22 Acacia Avenue, Citationburgh, Renfrewshire, GB", + editor = c(person(given = "One Truly", + family = "van der Real Person IV"), + person(family = "Entity Project Team Conference entity")), + series = "Collection Title", + volume = "2", + number = "123", + pages = "123--456", + doi = "10.5281/zenodo.1003150", + isbn = "978-1-89183-044-0", + issn = "1234-543X", + url = "http://j.mp", + note = "A field for general notes about the reference, usable in other formats such as BibTeX.", + chapter = "Chapter 2 - \"Reference keys\"", + edition = "2nd edition", + howpublished = "Hardcover book", + institution = "Entity Project Team Conference entity", + keywords = "Software, Citation", + abstract = "Description of the book.", + date = "2017-10-31", + file = "book.zip", + issuetitle = "Special Issue on Software Citation", + pagetotal = "765", + urldate = "2017-10-31", + version = "0.0.1423-BETA", + translator = "van der Real Person, IV, One Truly and {Entity Project Team Conference entity}") +bibentry(bibtype = "InBook", + key = "vanderrealpersoniventityprojectteamconferenceentity:2017", + title = "Book Title", + author = c(person(given = "One Truly", + family = "van der Real Person IV"), + person(family = "Entity Project Team Conference entity")), + year = "2017", + month = "mar", + journal = "PeerJ", + publisher = "Entity Project Team Conference entity", + address = "22 Acacia Avenue, Citationburgh, Renfrewshire, GB", + editor = c(person(given = "One Truly", + family = "van der Real Person IV"), + person(family = "Entity Project Team Conference entity")), + series = "Collection Title", + volume = "2", + number = "123", + pages = "123", + doi = "10.5281/zenodo.1003150", + isbn = "978-1-89183-044-0", + issn = "1234-543X", + url = "http://j.mp", + note = "A field for general notes about the reference, usable in other formats such as BibTeX.", + chapter = "Chapter 2 - \"Reference keys\"", + edition = "2nd edition", + howpublished = "Hardcover book", + institution = "Entity Project Team Conference entity", + keywords = "Software, Citation", + abstract = "Description of the book.", + date = "2017-10-31", + file = "book.zip", + issuetitle = "Special Issue on Software Citation", + pagetotal = "765", + urldate = "2017-10-31", + version = "0.0.1423-BETA", + translator = "van der Real Person, IV, One Truly and {Entity Project Team Conference entity}") diff --git a/tests/testthat/_snaps/cff_write_misc/append.bib b/tests/testthat/_snaps/cff_write_misc/append.bib new file mode 100644 index 00000000..999066a3 --- /dev/null +++ b/tests/testthat/_snaps/cff_write_misc/append.bib @@ -0,0 +1,15 @@ +@Misc{, + title = {My title}, + author = {Fran Herrero}, +} +@Misc{key2, + title = {Another title}, + author = {Ian Henderson}, +} +@Misc{basic, + title = {basicdesc: A Basic Description}, + author = {Marc Basic}, + url = {https://basic.github.io/package}, + abstract = {A very basic description. Should parse without problems.}, + version = {0.1.6}, +} diff --git a/tests/testthat/_snaps/cff_write_misc/ascii.bib b/tests/testthat/_snaps/cff_write_misc/ascii.bib new file mode 100644 index 00000000..13ec5e65 --- /dev/null +++ b/tests/testthat/_snaps/cff_write_misc/ascii.bib @@ -0,0 +1,4 @@ +@Misc{, + title = {My title}, + author = {Fran P{\'e}rez}, +} diff --git a/tests/testthat/_snaps/cff_write_misc/noext.bib b/tests/testthat/_snaps/cff_write_misc/noext.bib new file mode 100644 index 00000000..c57d7eb4 --- /dev/null +++ b/tests/testthat/_snaps/cff_write_misc/noext.bib @@ -0,0 +1,4 @@ +@Misc{, + title = {My title}, + author = {Fran Pérez}, +} diff --git a/tests/testthat/test-cff_write_misc.R b/tests/testthat/test-cff_write_misc.R new file mode 100644 index 00000000..30518f05 --- /dev/null +++ b/tests/testthat/test-cff_write_misc.R @@ -0,0 +1,147 @@ +test_that("Errors", { + expect_snapshot(cff_write_bib(1:4), error = TRUE) +}) + +test_that("Write", { + bib <- bibentry("Misc", + title = "My title", + author = "Fran Pérez" + ) + + file <- file.path(tempdir(), "noext") + expect_message(cff_write_bib(bib, file, verbose = TRUE)) + + # Fix extensions + file <- paste0(file, ".bib") + expect_true(file.exists(file)) + + expect_snapshot_file(file) + + # Check backup + expect_false(file.exists(paste0(file, ".bk1"))) + + # Check now backup exists + cff_write_bib(bib, file, append = TRUE) + expect_true(file.exists(paste0(file, ".bk1"))) + + file.remove(file) + file.remove(paste0(file, ".bk1")) +}) + + +test_that("Write ASCII", { + bib <- bibentry("Misc", + title = "My title", + author = "Fran Pérez" + ) + + file <- file.path(tempdir(), "ascii.bib") + expect_silent(cff_write_bib(bib, file, verbose = FALSE, ascii = TRUE)) + + # Fix extensions + expect_snapshot_file(file) + file.remove(file) +}) + +test_that("Test append", { + bib <- bibentry("Misc", + title = "My title", + author = "Fran Herrero" + ) + cf <- system.file("examples/DESCRIPTION_basic", package = "cffr") + + a_cff <- cff_read(cf) + + file <- file.path(tempdir(), "append.bib") + expect_silent(cff_write_bib(bib, file, verbose = FALSE, append = FALSE)) + + # Initial lines + lines1 <- readLines(file) + + # Append + bib2 <- bibentry("Misc", + key = "key2", title = "Another title", + author = "Ian Henderson" + ) + + + cff_write_bib(bib2, file, verbose = FALSE, append = TRUE) + cff_write_bib(a_cff, file, verbose = FALSE, append = TRUE) + expect_snapshot_file(file) + + lines2 <- readLines(file) + + # First lines identical, more lines on append + expect_true(all(lines1 == lines2[seq_len(length(lines1))])) + + expect_gt(length(lines2), length(lines1)) + + # Overwrite + cff_write_bib(bib2, file, verbose = FALSE, append = FALSE) + lines3 <- readLines(file) + + expect_false(all(lines1 == lines3[seq_len(length(lines1))])) + expect_lt(length(lines3), length(lines2)) +}) + + +test_that("Test dir creation", { + bib <- bibentry("Misc", + title = "My title", + author = "Fran Herrero" + ) + + file <- file.path(tempdir(), "idontexist", "append.bib") + + dir <- dirname(file) + + expect_false(dir.exists(dir)) + expect_silent(cff_write_bib(bib, file, verbose = FALSE)) + + expect_true(dir.exists(dir)) + expect_true(file.exists(file)) + + unlink(dir, recursive = TRUE, force = TRUE) + + # With messages + file <- file.path(tempdir(), "nowiamverbose", "append.bib") + dir <- dirname(file) + expect_false(dir.exists(dir)) + expect_message(cff_write_bib(bib, file, verbose = TRUE), "Creating directory") + + expect_true(dir.exists(dir)) + expect_true(file.exists(file)) + + unlink(dir, recursive = TRUE, force = TRUE) +}) + +test_that("Errors citation", { + expect_snapshot(cff_write_citation(1:4), error = TRUE) +}) + +test_that("Write CITATION", { + bib <- bibentry("Misc", + title = "My title", + author = "Fran Pérez" + ) + + file <- file.path(tempdir(), "CITAT_ION") + expect_message(cff_write_citation(bib, file, verbose = TRUE)) + + # Check backup + expect_false(file.exists(paste0(file, ".bk1"))) + + # Check now backup exists + cf <- system.file("examples/citation_complete.cff", package = "cffr") + + a_cff <- cff_read(cf) + expect_silent(cff_write_citation(a_cff, file, + verbose = FALSE, + what = "all", append = TRUE + )) + + expect_snapshot_file(file) + + file.remove(file) + file.remove(paste0(file, ".bk1")) +}) From 364a9f5f64095041528a6283bc414d867b41dd79 Mon Sep 17 00:00:00 2001 From: Diego H Date: Wed, 28 Feb 2024 00:58:00 +0100 Subject: [PATCH 16/32] Deprecate warnings and other improvements --- NEWS.md | 21 ++- R/cff-methods.R | 17 +-- R/cff_to_bibentry.R | 29 ++-- R/cff_validate.R | 10 +- R/cff_write.R | 28 ++-- R/deprecated.R | 73 +++++++++- R/write_bib.R | 106 -------------- R/write_citation.R | 113 --------------- README.md | 2 +- codemeta.json | 2 +- .../preferred-citation-book-missing.cff | 2 +- man/cff_read.Rd | 5 +- man/cff_read_biblines.Rd | 5 +- man/cff_to_bibentry.Rd | 26 +++- man/cff_validate.Rd | 10 +- man/cff_write.Rd | 15 +- man/cff_write_misc.Rd | 5 +- man/deprecated_cff_from_bib.Rd | 3 +- man/deprecated_cff_to_bib.Rd | 3 +- man/deprecated_write.Rd | 65 +++++++++ man/encoded_utf_to_latex.Rd | 5 +- man/toBibtex.cff.Rd | 48 ------- man/write_bib.Rd | 66 --------- man/write_citation.Rd | 101 -------------- tests/testthat/_snaps/bibtex-check-ruby.md | 11 ++ tests/testthat/_snaps/cff_to_bibentry.md | 9 ++ .../testthat/_snaps/cff_write_misc/CITAT_ION | 81 ++--------- tests/testthat/_snaps/deprecated.md | 49 ++++++- tests/testthat/_snaps/write_bib.md | 20 --- tests/testthat/_snaps/write_bib/append.bib | 8 -- tests/testthat/_snaps/write_bib/ascii.bib | 4 - tests/testthat/_snaps/write_bib/noext.bib | 4 - tests/testthat/_snaps/write_citation.md | 35 ----- tests/testthat/_snaps/write_citation/append | 11 -- tests/testthat/_snaps/write_citation/noext | 5 - tests/testthat/test-bibtex-check-ruby.R | 4 +- tests/testthat/test-bibtex2cff.R | 26 +++- tests/testthat/test-cff_to_bibentry.R | 33 ++++- tests/testthat/test-cff_write_misc.R | 11 +- tests/testthat/test-deprecated.R | 24 ++++ tests/testthat/test-mock-package.R | 2 +- tests/testthat/test-write_bib.R | 122 ---------------- tests/testthat/test-write_citation.R | 112 --------------- vignettes/crosswalk.Rmd | 130 +++++++----------- 44 files changed, 435 insertions(+), 1026 deletions(-) delete mode 100644 R/write_bib.R delete mode 100644 R/write_citation.R create mode 100644 man/deprecated_write.Rd delete mode 100644 man/toBibtex.cff.Rd delete mode 100644 man/write_bib.Rd delete mode 100644 man/write_citation.Rd delete mode 100644 tests/testthat/_snaps/write_bib.md delete mode 100644 tests/testthat/_snaps/write_bib/append.bib delete mode 100644 tests/testthat/_snaps/write_bib/ascii.bib delete mode 100644 tests/testthat/_snaps/write_bib/noext.bib delete mode 100644 tests/testthat/_snaps/write_citation.md delete mode 100644 tests/testthat/_snaps/write_citation/append delete mode 100644 tests/testthat/_snaps/write_citation/noext delete mode 100644 tests/testthat/test-write_bib.R delete mode 100644 tests/testthat/test-write_citation.R diff --git a/NEWS.md b/NEWS.md index e68f4413..b971751c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,10 +2,16 @@ ## Major changes -- Now `class()` of `cff` objects are `c("cff", "list")` instead of single - value (`"cff"`). +Now `class()` of `cff` objects are `c("cff", "list")` instead of single value +(`"cff"`). - ### API +### API + +The API has been reviewed to provide more clarity on names and better +maintenance. Now each function does less things but better. The change **affects +to non-core functions** that an user would rarely call, while `cff_create()`, +`cff_write()` and `cff_validate()` (core functions) hasn't been visibly +modified: - The conversion from `cff` to `bibentry` is performed now by a new function `cff_to_bibentry()`. Previous names of this function were `cff_to_bibtex()` @@ -17,7 +23,10 @@ also possible to read BibTeX lines with `cff_read_biblines()`. Previous function `cff_from_bibtex()` is now superseded. - ### Methods +- `write_bib()` and `write_citation()` superseded by `cff_write_bib()` and + `cff_write_citation()`. + +### Methods - New methods added: @@ -27,9 +36,9 @@ [person](https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md#definitionsperson) or [entity](https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md#definitionsentity) - (e.g. authors, contacts, editors, publisher). + (e.g. `authors`, `contacts`, `editors`, `publisher`). - `head.cff()`, `tail.cff()`. - - `toBibtex.cff()` + - `toBibtex.cff()`. ## Changes on BibTeX crosswalk diff --git a/R/cff-methods.R b/R/cff-methods.R index 6f8321b1..c56b2e02 100644 --- a/R/cff-methods.R +++ b/R/cff-methods.R @@ -81,30 +81,21 @@ tail.cff <- function(x, n = 6L, ...) { } -#' Converting [`cff`][cff-class] objects to BibTeX +#' @rdname cff_to_bibentry +#' @order 2 #' #' @description -#' -#' This method convert [`cff`][cff-class] objects to `Bibtex` objects as +#' Additionally, it is also provided a method for [toBibtex()], that can +#' convert [`cff`][cff-class] objects to `Bibtex` objects as #' provided by [utils::toBibtex()]. These objects are character vectors with #' BibTeX markup. #' -#' @keywords internal #' @family s3method -#' @family bibtex #' @export -#' @rdname toBibtex.cff #' @seealso [utils::toBibtex()] #' #' @param object `cff` object. #' @param ... Arguments passed to [utils::toBibtex()]. -#' @inheritParams cff_to_bibentry -#' -#' @examples -#' a_cff <- cff_create("cffr") -#' a_cff$`preferred-citation` -#' -#' toBibtex(a_cff, "preferred") toBibtex.cff <- function(object, ..., what = c("preferred", "references", "all")) { # If a single reference... diff --git a/R/cff_to_bibentry.R b/R/cff_to_bibentry.R index a869bc57..b5f6bc2f 100644 --- a/R/cff_to_bibentry.R +++ b/R/cff_to_bibentry.R @@ -1,5 +1,9 @@ #' Create BibTeX entries from several sources #' +#' @rdname cff_to_bibentry +#' @name cff_to_bibentry +#' @order 1 + #' @description #' #' This function creates [bibentry()] objects @@ -45,8 +49,6 @@ #' be parsed to BibTeX using [toBibtex()] #' #' @export -#' @name cff_to_bibentry -#' @rdname cff_to_bibentry #' #' @examples #' \donttest{ @@ -66,6 +68,10 @@ #' #' toBibtex(bib) #' +#' # Thanks to the S3 Method we can also do +#' +#' toBibtex(cff_object) +#' #' # From a CITATION.cff file with options #' #' path <- system.file("examples/CITATION_complete.cff", package = "cffr") @@ -152,7 +158,8 @@ cff_bibtex_parser <- function(x) { } # Partially based on ruby parser - # https://github.com/citation-file-format/ruby-cff/blob/main/lib/cff/formatter/bibtex_formatter.rb + # https://github.com/citation-file-format/ruby-cff/blob/main/lib/cff/ + # formatter/bibtex_formatter.rb # Create a initial empty list @@ -429,11 +436,6 @@ cff_bibtex_parser <- function(x) { tobibentry$year <- substr(x$`date-released`, 1, 4) } - # Inform - if (is.null(tobibentry$year)) { - msg <- paste0("Entry {.val {tobibentry$key}} does not have {.field year} ") - cli::cli_alert_info(msg) - } # Keywords if (!is.null(x$keywords)) { @@ -515,17 +517,16 @@ cff_bibtex_parser <- function(x) { sorted <- unique[unique %in% names(tobibentry)] tobibentry <- tobibentry[sorted] - bib <- do.call(bibentry, tobibentry) + bib <- try(do.call(bibentry, tobibentry), silent = TRUE) - # Shouldn't happen but just in case - # nocov start + # If key missing if (inherits(bib, "try-error")) { message <- attributes(bib)$condition$message - cli::cli_alert_danger(paste("Can't convert to {.fn bibentry}: ", message)) - cli::cli_alert_info("Returning {.val NULL}") + cli::cli_alert_danger(paste("Can't convert to {.fn bibentry}: ")) + cli::cli_alert_info(message) + cli::cli_alert_warning("Returning {.val NULL}") return(NULL) } - # nocov end return(bib) } diff --git a/R/cff_validate.R b/R/cff_validate.R index 488100a0..0c55820d 100644 --- a/R/cff_validate.R +++ b/R/cff_validate.R @@ -1,7 +1,7 @@ -#' Validate a `CITATION.cff` file or a [`cff`] object +#' Validate a `CITATION.cff` file or a [`cff`][cff-class] object #' #' @description -#' Validate a `CITATION.cff` file or a [`cff`] object created with +#' Validate a `CITATION.cff` file or a [`cff`][cff-class] object created with #' [cff_create()] using the corresponding validation #' ```{r, echo=FALSE, results='asis'} #' @@ -26,10 +26,10 @@ #' ``` #' #' @return A message indicating the result of the validation and an invisible -#' value `TRUE/FALSE`. On error, the results would have an attribute `"errors` -#' containing the error summary (see **Examples** and [attr()]). +#' value `TRUE/FALSE`. On error, the results would have an attribute +#' `"errors"` containing the error summary (see **Examples** and [attr()]). #' -#' @param x This is expected to be either a [`cff`] object created +#' @param x This is expected to be either a `cff` object created #' with [cff_create()] or the path to a `CITATION.cff` file to be validated. #' @inheritParams cff_write #' diff --git a/R/cff_write.R b/R/cff_write.R index ada57c63..f1ff3b75 100644 --- a/R/cff_write.R +++ b/R/cff_write.R @@ -7,22 +7,23 @@ #' #' This function writes out a `CITATION.cff` file for a given package. This #' function is basically a wrapper around [cff_create()] to both create the -#' [`cff`] object and writes it out to a YAML-formatted file in one command. +#' [`cff`][cff-class] object and writes it out to a YAML-formatted file in +#' one command. #' #' @family core #' @family write #' #' @param x The source that would be used for generating #' the `CITATION.cff` file. It could be: -#' * A missing value. That would retrieve the DESCRIPTION -#' file on your in-development package. -#' * A [`cff`] object, +#' * A missing value. That would retrieve the `DESCRIPTION` file on your +#' in-development package. +#' * A `cff` object, #' * The name of an installed package (`"jsonlite"`), or -#' * Path to a DESCRIPTION file (`"*/DESCRIPTION*"`). +#' * Path to a DESCRIPTION file (`"./DESCRIPTION"`). #' #' @param outfile The name and path of the `CITATION.cff` to be created. #' -#' @param keys List of additional keys to add to the [`cff`] object. See +#' @param keys List of additional keys to add to the `cff` object. See #' [cff_create()] for details and examples. #' #' @param validate Logical `TRUE/FALSE`. Should the new file be validated using @@ -38,7 +39,7 @@ #' #' @inheritParams cff_create #' -#' @return A `CITATION.cff` file and an (invisible) [`cff`] object. +#' @return A `CITATION.cff` file and an (invisible) `cff` object. #' #' @seealso #' ```{r, echo=FALSE, results='asis'} @@ -63,15 +64,10 @@ #' #' When creating and writing a `CITATION.cff` for the first time, the function #' adds "CITATION.cff" to ".Rbuildignore". -cff_write <- function(x, - outfile = "CITATION.cff", - keys = list(), - cff_version = "1.2.0", - gh_keywords = TRUE, - dependencies = TRUE, - validate = TRUE, - verbose = TRUE, - authors_roles = c("aut", "cre")) { +cff_write <- function(x, outfile = "CITATION.cff", keys = list(), + cff_version = "1.2.0", gh_keywords = TRUE, + dependencies = TRUE, validate = TRUE, + verbose = TRUE, authors_roles = c("aut", "cre")) { # On missing use package root if (missing(x)) x <- getwd() diff --git a/R/deprecated.R b/R/deprecated.R index 230fc6c7..8a46da0f 100644 --- a/R/deprecated.R +++ b/R/deprecated.R @@ -38,7 +38,7 @@ cff_to_bibtex <- function(x, what = c("preferred", "references", "all")) { if (requireNamespace("lifecycle", quietly = TRUE)) { lifecycle::deprecate_soft( - "0.6.0", "cff_extract_to_bibtex()", + "1.0.0", "cff_extract_to_bibtex()", details = "Function renamed, use `cff_to_bibentry()` instead." ) } @@ -104,8 +104,7 @@ cff_from_bibtex <- function(x, encoding = "UTF-8", ...) { if (length(x) == 1 && file.exists(x)) { if (requireNamespace("lifecycle", quietly = TRUE)) { lifecycle::deprecate_soft( - "0.5.0", "cff_from_bibtex()", - details = "Function renamed, use `cff_read_bib()` instead." + "1.0.0", "cff_from_bibtex()", "cff_read_bib()" ) } @@ -115,9 +114,73 @@ cff_from_bibtex <- function(x, encoding = "UTF-8", ...) { if (requireNamespace("lifecycle", quietly = TRUE)) { lifecycle::deprecate_soft( - "0.5.0", "cff_from_bibtex()", - details = "Function renamed, use `cff_read_biblines()` instead." + "1.0.0", "cff_from_bibtex()", "cff_read_biblines()" ) } cff_read_biblines(x, encoding = encoding, ...) } + + +#' Previous API: Write files +#' +#' @description +#' +#' `r lifecycle::badge('superseded')` Please use [cff_write_bib()] or +#' [cff_write_citation()] instead. +#' +#' @rdname deprecated_write +#' @export +#' @keywords internal +#' @family deprecated +#' @inheritParams cff_write_bib +#' +#' @return Write a file. +#' +#' @seealso +#' - [cff_write_bib()] for writing `*.bib` files. +#' - [cff_write_citation()] for writing **R** `CITATION` files. +#' +#' @examples +#' +#' bib <- bibentry("Misc", +#' title = "My title", +#' author = "Fran Pérez" +#' ) +#' +#' my_temp_bib <- tempfile(fileext = ".bib") +#' +#' cff_write_bib(bib, file = my_temp_bib) +#' +#' cat(readLines(my_temp_bib), sep = "\n") +#' +#' cff_write_bib(bib, file = my_temp_bib, ascii = TRUE, append = TRUE) +#' +#' cat(readLines(my_temp_bib), sep = "\n") +write_bib <- function(x, + file = tempfile(), + append = FALSE, + verbose = TRUE, + ascii = FALSE) { + if (requireNamespace("lifecycle", quietly = TRUE)) { + lifecycle::deprecate_soft( + "1.0.0", "write_bib()", "cff_write_bib()" + ) + } + + cff_write_bib(x, file, append, verbose, ascii) +} + +#' @rdname deprecated_write +#' @export +write_citation <- function(x, + file = "./inst/CITATION", + append = FALSE, + verbose = TRUE, + ...) { + if (requireNamespace("lifecycle", quietly = TRUE)) { + lifecycle::deprecate_soft( + "1.0.0", "write_citation()", "cff_write_citation()" + ) + } + cff_write_citation(x, file, append, verbose, ...) +} diff --git a/R/write_bib.R b/R/write_bib.R deleted file mode 100644 index 7e685ff3..00000000 --- a/R/write_bib.R +++ /dev/null @@ -1,106 +0,0 @@ -#' Create a `.bib` file -#' -#' Creates `a .bib` file from a `bibentry` object(s) -#' -#' @param x A `bibentry` object created with: -#' - [cff_to_bibentry()] -#' - [citation()] or [bibentry()] -#' -#' @param file Name of the file. If `NULL` it would display the lines to be -#' written. -#' @param append Whether to append the entries to an existing file or not. -#' @param verbose Display informative messages -#' @param ascii Whether to write the entries using ASCII characters only or not. -#' -#' @export -#' @family bibtex -#' -#' @return Writes an `.bib` file specified on `file` parameter and the -#' equivalent `Bibtex` object created with [utils::toBibtex()]. It also -#' (invisibly) returns the `bibentry` object that has been written to the file. -#' -#' @details -#' -#' For security reasons, if the file already exists the function would create -#' a backup copy on the same directory. -#' -#' @seealso `vignette("bibtex_cff", "cffr")`, [knitr::write_bib()] and the -#' following packages: -#' - \CRANpkg{bibtex}. -#' - \CRANpkg{RefManageR} -#' - \CRANpkg{rbibutils} -#' -#' @examples -#' -#' bib <- bibentry("Misc", -#' title = "My title", -#' author = "Fran Pérez" -#' ) -#' -#' write_bib(bib) -#' -#' write_bib(bib, ascii = TRUE) -write_bib <- function(x, - file = NULL, - append = FALSE, - verbose = TRUE, - ascii = FALSE) { - if (!inherits(x, "bibentry")) { - cli::cli_abort("{.arg x} should be a {.cls bibentry} object") - } - - btex <- toBibtex(x) - - if (ascii) { - # Base encoding as per file() - btex <- encoded_utf_to_latex(btex) - class(btex) <- "Bibtex" - } else { - btex <- enc2utf8(btex) - } - - if (is.null(file)) { - return(btex) - } - - if (tools::file_ext(file) != "bib") file <- paste0(file, ".bib") - - # Check that the directory exists, if not create - dir <- dirname(path.expand(file)) - if (!dir.exists(dir)) { - if (verbose) cli::cli_alert_info("Creating directory {.path {dir}}") - dir.create(dir, recursive = TRUE) - } - - - # If exists creates a backup - if (file.exists(file)) { - for (i in seq(1, 100)) { - f <- paste0(file, ".bk", i) - if (!file.exists(f)) break - } - - if (verbose) { - cli::cli_alert_info( - "Creating a backup of {.file {file}} in {.file {f}}" - ) - } - file.copy(file, f) - } - - - fh <- file(file, encoding = "UTF-8", open = if (append) { - "a+" - } else { - "w+" - }) - on.exit(if (isOpen(fh)) close(fh)) - if (verbose) { - cli::cli_alert_info("Writing {length(x)} BibTeX entr{?y/ies} ...") - } - writeLines(btex, fh) - if (verbose) { - cli::cli_alert_success("Results written to {.file {file}}") - } - invisible(btex) -} diff --git a/R/write_citation.R b/R/write_citation.R deleted file mode 100644 index d1a33e67..00000000 --- a/R/write_citation.R +++ /dev/null @@ -1,113 +0,0 @@ -#' Create a `inst/CITATION` file -#' -#' @description -#' -#' Creates a R CITATION file (inst/CITATION) from the metadata of a -#' CITATION.cff file or `cff` object. -#' -#' @param x It could be -#' - A `bibentry` object created with [cff_to_bibentry()], [citation()] or -#' [bibentry()] -#' - Any of the valid inputs of [cff_to_bibentry()]: -#' * A missing value. That would retrieve the DESCRIPTION file on your -#' in-development package. -#' * An existing [`cff`] object, -#' * Path to a CITATION.cff file (`"*/CITATION.cff*"`), -#' * The name of an installed package (`"jsonlite"`), or -#' * Path to a DESCRIPTION file (`"*/DESCRIPTION*"`). -#' @param file Name of the file to write. -#' @inheritParams write_bib -#' @inheritDotParams cff_to_bibentry -#' -#' @export -#' @family bibtex -#' -#' @return Writes an `inst/CITATION` file and (invisibly) returns the -#' `bibentry` object that has been written to the file. -#' -#' @seealso [bibentry()] and `style` argument. -#' -#' @details -#' -#' For security reasons, if the file already exists the function would create -#' a backup copy on the same directory. -#' -#' @references -#' -#' R Core Team (2023). "CITATION files." In Writing R Extensions, chapter 1.9, -#' R version 4.3.0 (2023-04-21) edition. -#' . -#' -#' -#' @examples -#' # Use a system file -#' f <- system.file("examples/preferred-citation-book.cff", package = "cffr") -#' -#' # Write to tmp dir -#' out <- file.path(tempdir(), "CITATION") -#' write_citation(f, file = out) -#' -#' # Check by reading, use meta object -#' -#' meta <- packageDescription("cffr") -#' meta$Encoding <- "UTF-8" -#' -#' utils::readCitationFile(out, meta) -#' -#' -#' # Append to the same file -#' bib2 <- citation() -#' write_citation(bib2, file = out, append = TRUE) -#' -#' utils::readCitationFile(out, meta) -write_citation <- function(x, - file = "./inst/CITATION", - append = FALSE, - verbose = TRUE, - ...) { - if (!inherits(x, "bibentry")) { - x <- cff_to_bibentry(x, ...) - } - - bentr <- format(x, style = "R") - - # Check that the directory exists, if not create - dir <- dirname(path.expand(file)) - if (!dir.exists(dir)) { - if (verbose) cli::cli_alert_info("Creating directory {.path {dir}}") - dir.create(dir, recursive = TRUE) - } - - - # If exists creates a backup - if (file.exists(file)) { - for (i in seq(1, 100)) { - f <- paste0(file, ".bk", i) - if (!file.exists(f)) break - } - - if (verbose) { - cli::cli_alert_info( - "Creating a backup of {.file {file}} in {.file {f}}" - ) - } - file.copy(file, f) - } - - - fh <- file(file, encoding = "UTF-8", open = if (append) { - "a+" - } else { - "w+" - }) - on.exit(if (isOpen(fh)) close(fh)) - if (verbose) { - cli::cli_alert_info("Writing {length(bentr)} entr{?y/ies} ...") - } - # Odd bug in RStudio... add empty lines - writeLines(c("", bentr), fh) - if (verbose) { - cli::cli_alert_success("Results written to {.file {file}}") - } - invisible(x) -} diff --git a/README.md b/README.md index 23bb18bb..37dcac20 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ file and the `CITATION` file (if present) of your package. Note that **cffr** works best if your package pass `R CMD check/devtools::check()`. -As per 2024-02-26 there are at least 305 repos on GitHub using **cffr**. +As per 2024-02-28 there are at least 294 repos on GitHub using **cffr**. [Check them out here](https://github.com/search?q=cffr%20path%3A**%2FCITATION.cff&type=code). diff --git a/codemeta.json b/codemeta.json index 0351b293..ce17ed10 100644 --- a/codemeta.json +++ b/codemeta.json @@ -200,7 +200,7 @@ }, "isPartOf": "https://ropensci.org", "keywords": ["attribution", "citation", "credit", "citation-files", "cff", "metadata", "r", "r-package", "citation-file-format", "rstats", "ropensci", "cran"], - "fileSize": "931.423KB", + "fileSize": "929.961KB", "citation": [ { "@type": "ScholarlyArticle", diff --git a/inst/examples/preferred-citation-book-missing.cff b/inst/examples/preferred-citation-book-missing.cff index 94080e82..84c401ff 100644 --- a/inst/examples/preferred-citation-book-missing.cff +++ b/inst/examples/preferred-citation-book-missing.cff @@ -13,5 +13,5 @@ preferred-citation: given-names: Ed title: "PETSc for Partial Differential Equations: Numerical Solutions in C and Python" url: https://github.com/bueler/p4pdes - isbn: 978111976304 + isbn: '978111976304' year: 2021 diff --git a/man/cff_read.Rd b/man/cff_read.Rd index 7cd78b85..1a97c82a 100644 --- a/man/cff_read.Rd +++ b/man/cff_read.Rd @@ -140,10 +140,7 @@ Other functions for working with BibTeX format: \code{\link{cff_read_biblines}()}, \code{\link{cff_to_bibentry}()}, \code{\link{cff_write_bib}()}, -\code{\link{encoded_utf_to_latex}()}, -\code{\link{toBibtex.cff}()}, -\code{\link{write_bib}()}, -\code{\link{write_citation}()} +\code{\link{encoded_utf_to_latex}()} } \concept{bibtex} \concept{reading} diff --git a/man/cff_read_biblines.Rd b/man/cff_read_biblines.Rd index 319659a6..ac3a1030 100644 --- a/man/cff_read_biblines.Rd +++ b/man/cff_read_biblines.Rd @@ -64,10 +64,7 @@ Other functions for working with BibTeX format: \code{\link{cff_read}()}, \code{\link{cff_to_bibentry}()}, \code{\link{cff_write_bib}()}, -\code{\link{encoded_utf_to_latex}()}, -\code{\link{toBibtex.cff}()}, -\code{\link{write_bib}()}, -\code{\link{write_citation}()} +\code{\link{encoded_utf_to_latex}()} Reading other files as \code{cff}: \code{\link{cff_read}()} diff --git a/man/cff_to_bibentry.Rd b/man/cff_to_bibentry.Rd index 205fd873..a5e21a9b 100644 --- a/man/cff_to_bibentry.Rd +++ b/man/cff_to_bibentry.Rd @@ -1,10 +1,13 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/cff_to_bibentry.R +% Please edit documentation in R/cff_to_bibentry.R, R/cff-methods.R \name{cff_to_bibentry} \alias{cff_to_bibentry} +\alias{toBibtex.cff} \title{Create BibTeX entries from several sources} \usage{ cff_to_bibentry(x, what = c("preferred", "references", "all")) + +\method{toBibtex}{cff}(object, ..., what = c("preferred", "references", "all")) } \arguments{ \item{x}{The source that would be used for generating @@ -26,6 +29,10 @@ info of the package. \item \code{all}: A combination of the previous two options. This would extract both the preferred citation info and the references. }} + +\item{object}{\code{cff} object.} + +\item{...}{Arguments passed to \code{\link[utils:toLatex]{utils::toBibtex()}}.} } \value{ A \code{bibentry} object or a list of \code{bibentry} objects. This could @@ -40,6 +47,11 @@ object and performs a mapping of the metadata to BibTeX, according to Note that a \strong{R} \code{\link[=bibentry]{bibentry()}} object is the representation of a BibTeX entry, see \strong{Examples}. + +Additionally, it is also provided a method for \code{\link[=toBibtex]{toBibtex()}}, that can +convert \code{\link[=cff-class]{cff}} objects to \code{Bibtex} objects as +provided by \code{\link[utils:toLatex]{utils::toBibtex()}}. These objects are character vectors with +BibTeX markup. } \examples{ \donttest{ @@ -59,6 +71,10 @@ bib toBibtex(bib) +# Thanks to the S3 Method we can also do + +toBibtex(cff_object) + # From a CITATION.cff file with options path <- system.file("examples/CITATION_complete.cff", package = "cffr") @@ -94,13 +110,13 @@ toBibtex(desc_file) } } \seealso{ +\code{\link[utils:toLatex]{utils::toBibtex()}} + Other functions for working with BibTeX format: \code{\link{cff_read}()}, \code{\link{cff_read_biblines}()}, \code{\link{cff_write_bib}()}, -\code{\link{encoded_utf_to_latex}()}, -\code{\link{toBibtex.cff}()}, -\code{\link{write_bib}()}, -\code{\link{write_citation}()} +\code{\link{encoded_utf_to_latex}()} } \concept{bibtex} +\concept{s3method} diff --git a/man/cff_validate.Rd b/man/cff_validate.Rd index c7a680f9..174928a2 100644 --- a/man/cff_validate.Rd +++ b/man/cff_validate.Rd @@ -2,12 +2,12 @@ % Please edit documentation in R/cff_validate.R \name{cff_validate} \alias{cff_validate} -\title{Validate a \code{CITATION.cff} file or a \code{\link{cff}} object} +\title{Validate a \code{CITATION.cff} file or a \code{\link[=cff-class]{cff}} object} \usage{ cff_validate(x = "CITATION.cff", verbose = TRUE) } \arguments{ -\item{x}{This is expected to be either a \code{\link{cff}} object created +\item{x}{This is expected to be either a \code{cff} object created with \code{\link[=cff_create]{cff_create()}} or the path to a \code{CITATION.cff} file to be validated.} \item{verbose}{Logical \code{TRUE/FALSE}. On \code{TRUE} the function would display @@ -15,11 +15,11 @@ informative messages.} } \value{ A message indicating the result of the validation and an invisible -value \code{TRUE/FALSE}. On error, the results would have an attribute \verb{"errors} -containing the error summary (see \strong{Examples} and \code{\link[=attr]{attr()}}). +value \code{TRUE/FALSE}. On error, the results would have an attribute +\code{"errors"} containing the error summary (see \strong{Examples} and \code{\link[=attr]{attr()}}). } \description{ -Validate a \code{CITATION.cff} file or a \code{\link{cff}} object created with +Validate a \code{CITATION.cff} file or a \code{\link[=cff-class]{cff}} object created with \code{\link[=cff_create]{cff_create()}} using the corresponding validation \href{https://github.com/citation-file-format/citation-file-format/blob/main/schema.json}{schema.json}. } diff --git a/man/cff_write.Rd b/man/cff_write.Rd index 627c3aa8..997e3172 100644 --- a/man/cff_write.Rd +++ b/man/cff_write.Rd @@ -20,16 +20,16 @@ cff_write( \item{x}{The source that would be used for generating the \code{CITATION.cff} file. It could be: \itemize{ -\item A missing value. That would retrieve the DESCRIPTION -file on your in-development package. -\item A \code{\link{cff}} object, +\item A missing value. That would retrieve the \code{DESCRIPTION} file on your +in-development package. +\item A \code{cff} object, \item The name of an installed package (\code{"jsonlite"}), or -\item Path to a DESCRIPTION file (\code{"*/DESCRIPTION*"}). +\item Path to a DESCRIPTION file (\code{"./DESCRIPTION"}). }} \item{outfile}{The name and path of the \code{CITATION.cff} to be created.} -\item{keys}{List of additional keys to add to the \code{\link{cff}} object. See +\item{keys}{List of additional keys to add to the \code{cff} object. See \code{\link[=cff_create]{cff_create()}} for details and examples.} \item{cff_version}{The Citation File Format schema version that the @@ -51,7 +51,7 @@ informative messages.} generating the \code{CITATION.cff} file. See \strong{Details} on \code{\link[=cff_create]{cff_create()}}.} } \value{ -A \code{CITATION.cff} file and an (invisible) \code{\link{cff}} object. +A \code{CITATION.cff} file and an (invisible) \code{cff} object. } \description{ \strong{This is the core function of the package and likely to be the only one @@ -59,7 +59,8 @@ you would need when developing a package}. This function writes out a \code{CITATION.cff} file for a given package. This function is basically a wrapper around \code{\link[=cff_create]{cff_create()}} to both create the -\code{\link{cff}} object and writes it out to a YAML-formatted file in one command. +\code{\link[=cff-class]{cff}} object and writes it out to a YAML-formatted file in +one command. } \details{ When creating and writing a \code{CITATION.cff} for the first time, the function diff --git a/man/cff_write_misc.Rd b/man/cff_write_misc.Rd index aa7fc245..3704432e 100644 --- a/man/cff_write_misc.Rd +++ b/man/cff_write_misc.Rd @@ -116,10 +116,7 @@ Other functions for working with BibTeX format: \code{\link{cff_read}()}, \code{\link{cff_read_biblines}()}, \code{\link{cff_to_bibentry}()}, -\code{\link{encoded_utf_to_latex}()}, -\code{\link{toBibtex.cff}()}, -\code{\link{write_bib}()}, -\code{\link{write_citation}()} +\code{\link{encoded_utf_to_latex}()} Other write: \code{\link{cff_write}()} diff --git a/man/deprecated_cff_from_bib.Rd b/man/deprecated_cff_from_bib.Rd index 98d32d82..99882c96 100644 --- a/man/deprecated_cff_from_bib.Rd +++ b/man/deprecated_cff_from_bib.Rd @@ -57,7 +57,8 @@ if (requireNamespace("bibtex", quietly = TRUE)) { } \seealso{ Other deprecated functions: -\code{\link{cff_extract_to_bibtex}()} +\code{\link{cff_extract_to_bibtex}()}, +\code{\link{write_bib}()} } \concept{deprecated} \keyword{internal} diff --git a/man/deprecated_cff_to_bib.Rd b/man/deprecated_cff_to_bib.Rd index f1677b42..8eaec833 100644 --- a/man/deprecated_cff_to_bib.Rd +++ b/man/deprecated_cff_to_bib.Rd @@ -49,7 +49,8 @@ bib <- cff_to_bibentry(cff_object) } \seealso{ Other deprecated functions: -\code{\link{cff_from_bibtex}()} +\code{\link{cff_from_bibtex}()}, +\code{\link{write_bib}()} } \concept{deprecated} \keyword{internal} diff --git a/man/deprecated_write.Rd b/man/deprecated_write.Rd new file mode 100644 index 00000000..6db7aa10 --- /dev/null +++ b/man/deprecated_write.Rd @@ -0,0 +1,65 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/deprecated.R +\name{write_bib} +\alias{write_bib} +\alias{write_citation} +\title{Previous API: Write files} +\usage{ +write_bib(x, file = tempfile(), append = FALSE, verbose = TRUE, ascii = FALSE) + +write_citation( + x, + file = "./inst/CITATION", + append = FALSE, + verbose = TRUE, + ... +) +} +\arguments{ +\item{x}{A \code{\link[=bibentry]{bibentry}} or a \code{\link[=cff-class]{cff}} object.} + +\item{file}{Name of the file to be created. If \code{NULL} it would display the +lines to be written.} + +\item{append}{Whether to append the entries to an existing file or not.} + +\item{verbose}{Display informative messages} + +\item{ascii}{Whether to write the entries using ASCII characters only or not.} +} +\value{ +Write a file. +} +\description{ +\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#superseded}{\figure{lifecycle-superseded.svg}{options: alt='[Superseded]'}}}{\strong{[Superseded]}} Please use \code{\link[=cff_write_bib]{cff_write_bib()}} or +\code{\link[=cff_write_citation]{cff_write_citation()}} instead. +} +\examples{ + +bib <- bibentry("Misc", + title = "My title", + author = "Fran Pérez" +) + +my_temp_bib <- tempfile(fileext = ".bib") + +cff_write_bib(bib, file = my_temp_bib) + +cat(readLines(my_temp_bib), sep = "\n") + +cff_write_bib(bib, file = my_temp_bib, ascii = TRUE, append = TRUE) + +cat(readLines(my_temp_bib), sep = "\n") +} +\seealso{ +\itemize{ +\item \code{\link[=cff_write_bib]{cff_write_bib()}} for writing \verb{*.bib} files. +\item \code{\link[=cff_write_citation]{cff_write_citation()}} for writing \strong{R} \code{CITATION} files. +} + +Other deprecated functions: +\code{\link{cff_extract_to_bibtex}()}, +\code{\link{cff_from_bibtex}()} +} +\concept{deprecated} +\keyword{internal} diff --git a/man/encoded_utf_to_latex.Rd b/man/encoded_utf_to_latex.Rd index 0dc8c4c8..8e8992cd 100644 --- a/man/encoded_utf_to_latex.Rd +++ b/man/encoded_utf_to_latex.Rd @@ -49,10 +49,7 @@ Other functions for working with BibTeX format: \code{\link{cff_read}()}, \code{\link{cff_read_biblines}()}, \code{\link{cff_to_bibentry}()}, -\code{\link{cff_write_bib}()}, -\code{\link{toBibtex.cff}()}, -\code{\link{write_bib}()}, -\code{\link{write_citation}()} +\code{\link{cff_write_bib}()} } \concept{bibtex} \keyword{internal} diff --git a/man/toBibtex.cff.Rd b/man/toBibtex.cff.Rd deleted file mode 100644 index ef19173c..00000000 --- a/man/toBibtex.cff.Rd +++ /dev/null @@ -1,48 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/cff-methods.R -\name{toBibtex.cff} -\alias{toBibtex.cff} -\title{Converting \code{\link[=cff-class]{cff}} objects to BibTeX} -\usage{ -\method{toBibtex}{cff}(object, ..., what = c("preferred", "references", "all")) -} -\arguments{ -\item{object}{\code{cff} object.} - -\item{...}{Arguments passed to \code{\link[utils:toLatex]{utils::toBibtex()}}.} - -\item{what}{Fields to extract. The value could be: -\itemize{ -\item \code{preferred}: This would create a single entry with the main citation -info of the package. -\item \code{references}: Extract all the entries on \code{references}. -\item \code{all}: A combination of the previous two options. This would extract -both the preferred citation info and the references. -}} -} -\description{ -This method convert \code{\link[=cff-class]{cff}} objects to \code{Bibtex} objects as -provided by \code{\link[utils:toLatex]{utils::toBibtex()}}. These objects are character vectors with -BibTeX markup. -} -\examples{ -a_cff <- cff_create("cffr") -a_cff$`preferred-citation` - -toBibtex(a_cff, "preferred") -} -\seealso{ -\code{\link[utils:toLatex]{utils::toBibtex()}} - -Other functions for working with BibTeX format: -\code{\link{cff_read}()}, -\code{\link{cff_read_biblines}()}, -\code{\link{cff_to_bibentry}()}, -\code{\link{cff_write_bib}()}, -\code{\link{encoded_utf_to_latex}()}, -\code{\link{write_bib}()}, -\code{\link{write_citation}()} -} -\concept{bibtex} -\concept{s3method} -\keyword{internal} diff --git a/man/write_bib.Rd b/man/write_bib.Rd deleted file mode 100644 index 8743f9c4..00000000 --- a/man/write_bib.Rd +++ /dev/null @@ -1,66 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/write_bib.R -\name{write_bib} -\alias{write_bib} -\title{Create a \code{.bib} file} -\usage{ -write_bib(x, file = NULL, append = FALSE, verbose = TRUE, ascii = FALSE) -} -\arguments{ -\item{x}{A \code{bibentry} object created with: -\itemize{ -\item \code{\link[=cff_to_bibentry]{cff_to_bibentry()}} -\item \code{\link[=citation]{citation()}} or \code{\link[=bibentry]{bibentry()}} -}} - -\item{file}{Name of the file. If \code{NULL} it would display the lines to be -written.} - -\item{append}{Whether to append the entries to an existing file or not.} - -\item{verbose}{Display informative messages} - -\item{ascii}{Whether to write the entries using ASCII characters only or not.} -} -\value{ -Writes an \code{.bib} file specified on \code{file} parameter and the -equivalent \code{Bibtex} object created with \code{\link[utils:toLatex]{utils::toBibtex()}}. It also -(invisibly) returns the \code{bibentry} object that has been written to the file. -} -\description{ -Creates \verb{a .bib} file from a \code{bibentry} object(s) -} -\details{ -For security reasons, if the file already exists the function would create -a backup copy on the same directory. -} -\examples{ - -bib <- bibentry("Misc", - title = "My title", - author = "Fran Pérez" -) - -write_bib(bib) - -write_bib(bib, ascii = TRUE) -} -\seealso{ -\code{vignette("bibtex_cff", "cffr")}, \code{\link[knitr:write_bib]{knitr::write_bib()}} and the -following packages: -\itemize{ -\item \CRANpkg{bibtex}. -\item \CRANpkg{RefManageR} -\item \CRANpkg{rbibutils} -} - -Other functions for working with BibTeX format: -\code{\link{cff_read}()}, -\code{\link{cff_read_biblines}()}, -\code{\link{cff_to_bibentry}()}, -\code{\link{cff_write_bib}()}, -\code{\link{encoded_utf_to_latex}()}, -\code{\link{toBibtex.cff}()}, -\code{\link{write_citation}()} -} -\concept{bibtex} diff --git a/man/write_citation.Rd b/man/write_citation.Rd deleted file mode 100644 index b93a7409..00000000 --- a/man/write_citation.Rd +++ /dev/null @@ -1,101 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/write_citation.R -\name{write_citation} -\alias{write_citation} -\title{Create a \code{inst/CITATION} file} -\usage{ -write_citation( - x, - file = "./inst/CITATION", - append = FALSE, - verbose = TRUE, - ... -) -} -\arguments{ -\item{x}{It could be -\itemize{ -\item A \code{bibentry} object created with \code{\link[=cff_to_bibentry]{cff_to_bibentry()}}, \code{\link[=citation]{citation()}} or -\code{\link[=bibentry]{bibentry()}} -\item Any of the valid inputs of \code{\link[=cff_to_bibentry]{cff_to_bibentry()}}: -\itemize{ -\item A missing value. That would retrieve the DESCRIPTION file on your -in-development package. -\item An existing \code{\link{cff}} object, -\item Path to a CITATION.cff file (\code{"*/CITATION.cff*"}), -\item The name of an installed package (\code{"jsonlite"}), or -\item Path to a DESCRIPTION file (\code{"*/DESCRIPTION*"}). -} -}} - -\item{file}{Name of the file to write.} - -\item{append}{Whether to append the entries to an existing file or not.} - -\item{verbose}{Display informative messages} - -\item{...}{ - Arguments passed on to \code{\link[=cff_to_bibentry]{cff_to_bibentry}} - \describe{ - \item{\code{what}}{Fields to extract. The value could be: -\itemize{ -\item \code{preferred}: This would create a single entry with the main citation -info of the package. -\item \code{references}: Extract all the entries on \code{references}. -\item \code{all}: A combination of the previous two options. This would extract -both the preferred citation info and the references. -}} - }} -} -\value{ -Writes an \code{inst/CITATION} file and (invisibly) returns the -\code{bibentry} object that has been written to the file. -} -\description{ -Creates a R CITATION file (inst/CITATION) from the metadata of a -CITATION.cff file or \code{cff} object. -} -\details{ -For security reasons, if the file already exists the function would create -a backup copy on the same directory. -} -\examples{ -# Use a system file -f <- system.file("examples/preferred-citation-book.cff", package = "cffr") - -# Write to tmp dir -out <- file.path(tempdir(), "CITATION") -write_citation(f, file = out) - -# Check by reading, use meta object - -meta <- packageDescription("cffr") -meta$Encoding <- "UTF-8" - -utils::readCitationFile(out, meta) - - -# Append to the same file -bib2 <- citation() -write_citation(bib2, file = out, append = TRUE) - -utils::readCitationFile(out, meta) -} -\references{ -R Core Team (2023). "CITATION files." In Writing R Extensions, chapter 1.9, -R version 4.3.0 (2023-04-21) edition. -\url{https://cran.r-project.org/doc/manuals/R-exts.html#CITATION-files}. -} -\seealso{ -\code{\link[=bibentry]{bibentry()}} and \code{style} argument. - -Other functions for working with BibTeX format: -\code{\link{cff_read}()}, -\code{\link{cff_read_biblines}()}, -\code{\link{cff_to_bibentry}()}, -\code{\link{cff_write_bib}()}, -\code{\link{encoded_utf_to_latex}()}, -\code{\link{toBibtex.cff}()}, -\code{\link{write_bib}()} -} -\concept{bibtex} diff --git a/tests/testthat/_snaps/bibtex-check-ruby.md b/tests/testthat/_snaps/bibtex-check-ruby.md index 931ceb2e..8942a193 100644 --- a/tests/testthat/_snaps/bibtex-check-ruby.md +++ b/tests/testthat/_snaps/bibtex-check-ruby.md @@ -1,3 +1,14 @@ +# preferred-citation-book-missing + + Code + cff_to_bibentry(x) + Message + x Can't convert to `bibentry()`: + i A bibentry of bibtype 'Book' has to specify the field: publisher + ! Returning "NULL" + Output + NULL + # preferred-citation-book Code diff --git a/tests/testthat/_snaps/cff_to_bibentry.md b/tests/testthat/_snaps/cff_to_bibentry.md index e60fd87a..4117bcb7 100644 --- a/tests/testthat/_snaps/cff_to_bibentry.md +++ b/tests/testthat/_snaps/cff_to_bibentry.md @@ -622,3 +622,12 @@ year = {2020}, } +# Corrupt entry + + Code + n <- cff_to_bibentry(x) + Message + x Can't convert to `bibentry()`: + i A bibentry of bibtype 'Article' has to specify the fields: journal, year + ! Returning "NULL" + diff --git a/tests/testthat/_snaps/cff_write_misc/CITAT_ION b/tests/testthat/_snaps/cff_write_misc/CITAT_ION index 20315d10..e3d0407a 100644 --- a/tests/testthat/_snaps/cff_write_misc/CITAT_ION +++ b/tests/testthat/_snaps/cff_write_misc/CITAT_ION @@ -4,75 +4,12 @@ bibentry(bibtype = "Misc", author = person(given = "Fran", family = "Pérez")) -bibentry(bibtype = "InBook", - key = "vanderrealpersoniventityprojectteamconferenceentity:2017", - title = "Book Title", - author = c(person(given = "One Truly", - family = "van der Real Person IV"), - person(family = "Entity Project Team Conference entity")), - year = "2017", - month = "mar", - journal = "PeerJ", - publisher = "Entity Project Team Conference entity", - address = "22 Acacia Avenue, Citationburgh, Renfrewshire, GB", - editor = c(person(given = "One Truly", - family = "van der Real Person IV"), - person(family = "Entity Project Team Conference entity")), - series = "Collection Title", - volume = "2", - number = "123", - pages = "123--456", - doi = "10.5281/zenodo.1003150", - isbn = "978-1-89183-044-0", - issn = "1234-543X", - url = "http://j.mp", - note = "A field for general notes about the reference, usable in other formats such as BibTeX.", - chapter = "Chapter 2 - \"Reference keys\"", - edition = "2nd edition", - howpublished = "Hardcover book", - institution = "Entity Project Team Conference entity", - keywords = "Software, Citation", - abstract = "Description of the book.", - date = "2017-10-31", - file = "book.zip", - issuetitle = "Special Issue on Software Citation", - pagetotal = "765", - urldate = "2017-10-31", - version = "0.0.1423-BETA", - translator = "van der Real Person, IV, One Truly and {Entity Project Team Conference entity}") -bibentry(bibtype = "InBook", - key = "vanderrealpersoniventityprojectteamconferenceentity:2017", - title = "Book Title", - author = c(person(given = "One Truly", - family = "van der Real Person IV"), - person(family = "Entity Project Team Conference entity")), - year = "2017", - month = "mar", - journal = "PeerJ", - publisher = "Entity Project Team Conference entity", - address = "22 Acacia Avenue, Citationburgh, Renfrewshire, GB", - editor = c(person(given = "One Truly", - family = "van der Real Person IV"), - person(family = "Entity Project Team Conference entity")), - series = "Collection Title", - volume = "2", - number = "123", - pages = "123", - doi = "10.5281/zenodo.1003150", - isbn = "978-1-89183-044-0", - issn = "1234-543X", - url = "http://j.mp", - note = "A field for general notes about the reference, usable in other formats such as BibTeX.", - chapter = "Chapter 2 - \"Reference keys\"", - edition = "2nd edition", - howpublished = "Hardcover book", - institution = "Entity Project Team Conference entity", - keywords = "Software, Citation", - abstract = "Description of the book.", - date = "2017-10-31", - file = "book.zip", - issuetitle = "Special Issue on Software Citation", - pagetotal = "765", - urldate = "2017-10-31", - version = "0.0.1423-BETA", - translator = "van der Real Person, IV, One Truly and {Entity Project Team Conference entity}") +bibentry(bibtype = "Misc", + key = "basic", + title = "basicdescdate: A Basic Description with Date", + author = person(given = "Marc", + family = "Basic"), + year = "1999", + url = "https://basic.github.io/package", + abstract = "A very basic description. Should parse without problems. I have a Date", + version = "0.1.6") diff --git a/tests/testthat/_snaps/deprecated.md b/tests/testthat/_snaps/deprecated.md index 2c99e970..fe340d9d 100644 --- a/tests/testthat/_snaps/deprecated.md +++ b/tests/testthat/_snaps/deprecated.md @@ -13,7 +13,7 @@ old1 <- cff_to_bibtex(a_cff) Condition Warning: - `cff_extract_to_bibtex()` was deprecated in cffr 0.6.0. + `cff_extract_to_bibtex()` was deprecated in cffr 1.0.0. i Function renamed, use `cff_to_bibentry()` instead. # cff_from_bibtex @@ -22,8 +22,8 @@ ffile <- cff_from_bibtex(x2) Condition Warning: - `cff_from_bibtex()` was deprecated in cffr 0.5.0. - i Function renamed, use `cff_read_bib()` instead. + `cff_from_bibtex()` was deprecated in cffr 1.0.0. + i Please use `cff_read_bib()` instead. --- @@ -31,6 +31,45 @@ flines <- cff_from_bibtex(x) Condition Warning: - `cff_from_bibtex()` was deprecated in cffr 0.5.0. - i Function renamed, use `cff_read_biblines()` instead. + `cff_from_bibtex()` was deprecated in cffr 1.0.0. + i Please use `cff_read_biblines()` instead. + +# write_bib + + Code + write_bib(bib, tmp, verbose = FALSE) + Condition + Warning: + `write_bib()` was deprecated in cffr 1.0.0. + i Please use `cff_write_bib()` instead. + +--- + + Code + cat(readLines(tmp), sep = "\n") + Output + @Misc{, + title = {My title}, + author = {Fran Pérez}, + } + +# write_citation + + Code + write_citation(bib, tmp, verbose = FALSE) + Condition + Warning: + `write_citation()` was deprecated in cffr 1.0.0. + i Please use `cff_write_citation()` instead. + +--- + + Code + cat(readLines(tmp), sep = "\n") + Output + + bibentry(bibtype = "Misc", + title = "My title", + author = person(given = "Fran", + family = "Pérez")) diff --git a/tests/testthat/_snaps/write_bib.md b/tests/testthat/_snaps/write_bib.md deleted file mode 100644 index 8090ffd1..00000000 --- a/tests/testthat/_snaps/write_bib.md +++ /dev/null @@ -1,20 +0,0 @@ -# Try not writing - - Code - write_bib(bib) - Output - @Misc{, - title = {My title}, - author = {Fran Pérez}, - } - ---- - - Code - write_bib(bib, ascii = TRUE) - Output - @Misc{, - title = {My title}, - author = {Fran P{\'e}rez}, - } - diff --git a/tests/testthat/_snaps/write_bib/append.bib b/tests/testthat/_snaps/write_bib/append.bib deleted file mode 100644 index d0208a19..00000000 --- a/tests/testthat/_snaps/write_bib/append.bib +++ /dev/null @@ -1,8 +0,0 @@ -@Misc{, - title = {My title}, - author = {Fran Herrero}, -} -@Misc{key2, - title = {Another title}, - author = {Ian Henderson}, -} diff --git a/tests/testthat/_snaps/write_bib/ascii.bib b/tests/testthat/_snaps/write_bib/ascii.bib deleted file mode 100644 index 13ec5e65..00000000 --- a/tests/testthat/_snaps/write_bib/ascii.bib +++ /dev/null @@ -1,4 +0,0 @@ -@Misc{, - title = {My title}, - author = {Fran P{\'e}rez}, -} diff --git a/tests/testthat/_snaps/write_bib/noext.bib b/tests/testthat/_snaps/write_bib/noext.bib deleted file mode 100644 index c57d7eb4..00000000 --- a/tests/testthat/_snaps/write_bib/noext.bib +++ /dev/null @@ -1,4 +0,0 @@ -@Misc{, - title = {My title}, - author = {Fran Pérez}, -} diff --git a/tests/testthat/_snaps/write_citation.md b/tests/testthat/_snaps/write_citation.md deleted file mode 100644 index aace09ca..00000000 --- a/tests/testthat/_snaps/write_citation.md +++ /dev/null @@ -1,35 +0,0 @@ -# Results can be parsed - - @InBook{vanderrealpersoniventityprojectteamconferenceentity:2017, - title = {Book Title}, - author = {One Truly {van der Real Person IV} and {Entity Project Team Conference entity}}, - year = {2017}, - month = {mar}, - journal = {PeerJ}, - publisher = {Entity Project Team Conference entity}, - address = {22 Acacia Avenue, Citationburgh, Renfrewshire, GB}, - editor = {One Truly {van der Real Person IV} and {Entity Project Team Conference entity}}, - series = {Collection Title}, - volume = {2}, - number = {123}, - pages = {123--456}, - doi = {10.5281/zenodo.1003150}, - isbn = {978-1-89183-044-0}, - issn = {1234-543X}, - url = {http://j.mp}, - note = {A field for general notes about the reference, usable in other formats such as BibTeX.}, - chapter = {Chapter 2 - "Reference keys"}, - edition = {2nd edition}, - howpublished = {Hardcover book}, - institution = {Entity Project Team Conference entity}, - keywords = {Software, Citation}, - abstract = {Description of the book.}, - date = {2017-10-31}, - file = {book.zip}, - issuetitle = {Special Issue on Software Citation}, - pagetotal = {765}, - urldate = {2017-10-31}, - version = {0.0.1423-BETA}, - translator = {van der Real Person, IV, One Truly and {Entity Project Team Conference entity}}, - } - diff --git a/tests/testthat/_snaps/write_citation/append b/tests/testthat/_snaps/write_citation/append deleted file mode 100644 index 1bc3229b..00000000 --- a/tests/testthat/_snaps/write_citation/append +++ /dev/null @@ -1,11 +0,0 @@ - -bibentry(bibtype = "Misc", - title = "My title", - author = person(given = "Fran", - family = "Herrero")) - -bibentry(bibtype = "Misc", - key = "key2", - title = "Another title", - author = person(given = "Ian", - family = "Henderson")) diff --git a/tests/testthat/_snaps/write_citation/noext b/tests/testthat/_snaps/write_citation/noext deleted file mode 100644 index 141b29d4..00000000 --- a/tests/testthat/_snaps/write_citation/noext +++ /dev/null @@ -1,5 +0,0 @@ - -bibentry(bibtype = "Misc", - title = "My title", - author = person(given = "Fran", - family = "Pérez")) diff --git a/tests/testthat/test-bibtex-check-ruby.R b/tests/testthat/test-bibtex-check-ruby.R index 6dd4f780..139c2a69 100644 --- a/tests/testthat/test-bibtex-check-ruby.R +++ b/tests/testthat/test-bibtex-check-ruby.R @@ -1,5 +1,5 @@ # See ´ -# https://github.com/citation-file-format/ruby-cff/tree/main/test/files/formatted +# https://github.com/citation-file-format/ruby-cff/tree/main/test/files test_that("preferred-citation-book-missing", { @@ -7,7 +7,7 @@ test_that("preferred-citation-book-missing", { package = "cffr" ) - expect_warning(expect_error(cff_to_bibentry(x))) + expect_snapshot(cff_to_bibentry(x)) }) test_that("preferred-citation-book", { diff --git a/tests/testthat/test-bibtex2cff.R b/tests/testthat/test-bibtex2cff.R index 29a49e4b..7b0f764f 100644 --- a/tests/testthat/test-bibtex2cff.R +++ b/tests/testthat/test-bibtex2cff.R @@ -108,7 +108,10 @@ test_that("Conference", { key = "inproceedings-full", author = "Alfred V. Oaho and Jeffrey D. Ullman and Mihalis Yannakakis", title = "On Notions of Information Transfer in {VLSI} Circuits", - title = "{Statistical Machine Translation: Rapid Development with Limited Resources", + title = paste( + "{Statistical Machine Translation: Rapid Development", + "with Limited Resources" + ), booktitle = "Proc. Fifteenth Annual ACM STOC", year = "1983", # Optional @@ -234,7 +237,10 @@ test_that("InProceedings", { key = "inproceedings-full", author = "Alfred V. Oaho and Jeffrey D. Ullman and Mihalis Yannakakis", title = "On Notions of Information Transfer in {VLSI} Circuits", - title = "{Statistical Machine Translation: Rapid Development with Limited Resources", + title = paste( + "{Statistical Machine Translation: Rapid", + "Development with Limited Resources" + ), booktitle = "Proc. Fifteenth Annual ACM STOC", year = "1983", # Optional @@ -436,7 +442,10 @@ test_that("TechReport", { # Optional type = "techreport", number = "3-03", - address = "Computing Laboratory, University of Kent, Canterbury, Kent, CT2 7NF", + address = paste( + "Computing Laboratory, University of Kent,", + "Canterbury, Kent, CT2 7NF" + ), month = "mar", note = "Example modified for testing purposes" ) @@ -528,7 +537,8 @@ test_that("Test entry without author", { bib <- bibentry("Proceedings", editor = "Yolande Berbers and Willy Zwaenepoel", title = "Proceedings of the 6th European Conference on Computer Systems", - booktitle = "Proceedings of the 6th European Conference on Computer Systems", + booktitle = paste("Proceedings of the 6th European", " + Conference on Computer Systems"), publisher = "ACM", venue = "Leuven, Belgium", month = "apr", @@ -557,7 +567,10 @@ test_that("Test entry without author but has a key", { bib <- bibentry("Misc", key = "I am the key", title = "Proceedings of the 6th European Conference on Computer Systems", - booktitle = "Proceedings of the 6th European Conference on Computer Systems", + booktitle = paste( + "Proceedings of the 6th European Conference", + "on Computer Systems" + ), publisher = "ACM", venue = "Leuven, Belgium", month = "apr", @@ -586,7 +599,8 @@ test_that("Test entry without author but has a key", { test_that("Test entry without author and key", { bib <- bibentry("Misc", title = "Proceedings of the 6th European Conference on Computer Systems", - booktitle = "Proceedings of the 6th European Conference on Computer Systems", + booktitle = paste("Proceedings of the 6th European", " + Conference on Computer Systems"), publisher = "ACM", venue = "Leuven, Belgium", month = "apr", diff --git a/tests/testthat/test-cff_to_bibentry.R b/tests/testthat/test-cff_to_bibentry.R index 165dbcdf..c117e41a 100644 --- a/tests/testthat/test-cff_to_bibentry.R +++ b/tests/testthat/test-cff_to_bibentry.R @@ -291,7 +291,7 @@ test_that("From plain cff with a citation", { }) test_that("From plain cff", { - expect_message(bib <- cff_to_bibentry(cff())) + expect_silent(bib <- cff_to_bibentry(cff())) expect_snapshot(toBibtex(bib)) }) @@ -316,7 +316,7 @@ test_that("Test anonymous", { ) - expect_message(back <- cff_to_bibentry(cff_parse_citation(bib))) + expect_silent(back <- cff_to_bibentry(cff_parse_citation(bib))) expect_snapshot(toBibtex(back)) @@ -325,7 +325,7 @@ test_that("Test anonymous", { ) - expect_message(back <- cff_to_bibentry(cff_parse_citation(bib))) + expect_silent(back <- cff_to_bibentry(cff_parse_citation(bib))) expect_snapshot(toBibtex(back)) bib <- bibentry("misc", @@ -333,7 +333,7 @@ test_that("Test anonymous", { ) - expect_message(back <- cff_to_bibentry(cff_parse_citation(bib))) + expect_silent(back <- cff_to_bibentry(cff_parse_citation(bib))) expect_snapshot(toBibtex(back)) bib <- bibentry("proceedings", @@ -403,7 +403,7 @@ test_that("Test BibLateX entry", { test_that("Test Fallback year", { x <- cff() - expect_message(msg <- cff_to_bibentry(x)) + expect_silent(msg <- cff_to_bibentry(x)) expect_snapshot(toBibtex(msg)) @@ -449,7 +449,7 @@ test_that("NULL references", { expect_null(cff_to_bibentry(basic, "references")) # Test all - expect_message(l <- cff_to_bibentry(basic, "all")) + expect_silent(l <- cff_to_bibentry(basic, "all")) expect_length(l, 1) }) @@ -463,3 +463,24 @@ test_that("From CITATION.cff", { expect_length(base, 1) }) + +test_that("Corrupt entry", { + bib <- bibentry("Article", + key = "knuth:1984", + author = person("R Core Team"), + title = "Literate Programming", + journal = "The Computer Journal", + year = "1984", + # Optional + volume = "27", + number = 2, + pages = "97--111", + month = "January", + keywords = "Some, simple, keywords" + ) + x <- cff_parse_citation(bib) + x$year <- NULL + x$journal <- NULL + expect_snapshot(n <- cff_to_bibentry(x)) + expect_null(n) +}) diff --git a/tests/testthat/test-cff_write_misc.R b/tests/testthat/test-cff_write_misc.R index 30518f05..53da829f 100644 --- a/tests/testthat/test-cff_write_misc.R +++ b/tests/testthat/test-cff_write_misc.R @@ -120,6 +120,10 @@ test_that("Errors citation", { }) test_that("Write CITATION", { + f <- system.file("examples", package = "cffr") + thepath <- list.files(f, pattern = "DESCRIPTION_basicdate", full.names = TRUE) + f1 <- cff_read(thepath) + bib <- bibentry("Misc", title = "My title", author = "Fran Pérez" @@ -131,11 +135,8 @@ test_that("Write CITATION", { # Check backup expect_false(file.exists(paste0(file, ".bk1"))) - # Check now backup exists - cf <- system.file("examples/citation_complete.cff", package = "cffr") - - a_cff <- cff_read(cf) - expect_silent(cff_write_citation(a_cff, file, + # Check now backup exists and use cff + expect_silent(cff_write_citation(f1, file, verbose = FALSE, what = "all", append = TRUE )) diff --git a/tests/testthat/test-deprecated.R b/tests/testthat/test-deprecated.R index e4fe8249..314a2adf 100644 --- a/tests/testthat/test-deprecated.R +++ b/tests/testthat/test-deprecated.R @@ -35,3 +35,27 @@ test_that("cff_from_bibtex", { expect_snapshot(flines <- cff_from_bibtex(x)) expect_identical(flines, cff_read_biblines(x)) }) + +test_that("write_bib", { + bib <- bibentry("Misc", + title = "My title", + author = "Fran Pérez" + ) + + tmp <- tempfile(fileext = ".bib") + expect_snapshot(write_bib(bib, tmp, verbose = FALSE)) + + expect_snapshot(cat(readLines(tmp), sep = "\n")) +}) + +test_that("write_citation", { + bib <- bibentry("Misc", + title = "My title", + author = "Fran Pérez" + ) + + tmp <- tempfile("CIT_ATION") + expect_snapshot(write_citation(bib, tmp, verbose = FALSE)) + + expect_snapshot(cat(readLines(tmp), sep = "\n")) +}) diff --git a/tests/testthat/test-mock-package.R b/tests/testthat/test-mock-package.R index 470cf02e..265f0ed7 100644 --- a/tests/testthat/test-mock-package.R +++ b/tests/testthat/test-mock-package.R @@ -24,7 +24,7 @@ test_that("Test in mock package", { ) - expect_silent(write_citation(cit, verbose = FALSE)) + expect_silent(cff_write_citation(cit, "./inst/CITATION", verbose = FALSE)) expect_true(file.exists("./inst/CITATION")) # Create Rbuildignore diff --git a/tests/testthat/test-write_bib.R b/tests/testthat/test-write_bib.R deleted file mode 100644 index 8c52b7ef..00000000 --- a/tests/testthat/test-write_bib.R +++ /dev/null @@ -1,122 +0,0 @@ -test_that("Try not writing", { - bib <- bibentry("Misc", - title = "My title", - author = "Fran Pérez" - ) - - expect_snapshot(write_bib(bib)) - - expect_snapshot(write_bib(bib, ascii = TRUE)) - - expect_error(write_bib(1)) -}) - -test_that("Write", { - bib <- bibentry("Misc", - title = "My title", - author = "Fran Pérez" - ) - - file <- file.path(tempdir(), "noext") - expect_message(write_bib(bib, file, verbose = TRUE)) - - # Fix extensions - file <- paste0(file, ".bib") - expect_true(file.exists(file)) - - expect_snapshot_file(file) - - # Check backup - expect_false(file.exists(paste0(file, ".bk1"))) - - # Check now backup exists - write_bib(bib, file, append = TRUE) - expect_true(file.exists(paste0(file, ".bk1"))) - - file.remove(file) - file.remove(paste0(file, ".bk1")) -}) - - -test_that("Write ASCII", { - bib <- bibentry("Misc", - title = "My title", - author = "Fran Pérez" - ) - - file <- file.path(tempdir(), "ascii.bib") - expect_silent(write_bib(bib, file, verbose = FALSE, ascii = TRUE)) - - # Fix extensions - expect_snapshot_file(file) - file.remove(file) -}) - -test_that("Test append", { - bib <- bibentry("Misc", - title = "My title", - author = "Fran Herrero" - ) - - file <- file.path(tempdir(), "append.bib") - expect_silent(write_bib(bib, file, verbose = FALSE, append = FALSE)) - - # Initial lines - lines1 <- readLines(file) - - # Append - bib2 <- bibentry("Misc", - key = "key2", - title = "Another title", - author = "Ian Henderson" - ) - - - write_bib(bib2, file, verbose = FALSE, append = TRUE) - expect_snapshot_file(file) - - lines2 <- readLines(file) - - # First lines identical, more lines on append - expect_true(all(lines1 == lines2[seq_len(length(lines1))])) - - expect_gt(length(lines2), length(lines1)) - - # Overwrite - write_bib(bib2, file, verbose = FALSE, append = FALSE) - lines3 <- readLines(file) - - expect_false(all(lines1 == lines3[seq_len(length(lines1))])) - expect_lt(length(lines3), length(lines2)) -}) - - -test_that("Test dir creation", { - bib <- bibentry("Misc", - title = "My title", - author = "Fran Herrero" - ) - - file <- file.path(tempdir(), "idontexist", "append.bib") - - dir <- dirname(file) - - expect_false(dir.exists(dir)) - expect_silent(write_bib(bib, file, verbose = FALSE)) - - expect_true(dir.exists(dir)) - expect_true(file.exists(file)) - - unlink(dir, recursive = TRUE, force = TRUE) - - # With messages - file <- file.path(tempdir(), "nowiamverbose", "append.bib") - dir <- dirname(file) - expect_false(dir.exists(dir)) - expect_message(write_bib(bib, file, verbose = TRUE), "Creating directory") - - expect_true(dir.exists(dir)) - expect_true(file.exists(file)) - - unlink(dir, recursive = TRUE, force = TRUE) -}) diff --git a/tests/testthat/test-write_citation.R b/tests/testthat/test-write_citation.R deleted file mode 100644 index 6189200d..00000000 --- a/tests/testthat/test-write_citation.R +++ /dev/null @@ -1,112 +0,0 @@ -test_that("Write", { - bib <- bibentry("Misc", - title = "My title", - author = "Fran Pérez" - ) - - file <- file.path(tempdir(), "noext") - expect_message(write_citation(bib, file, verbose = TRUE)) - - # Fix extensions - - expect_snapshot_file(file) - - # Check backup - expect_false(file.exists(paste0(file, ".bk1"))) - - # Check now backup exists - write_citation(bib, file, append = TRUE) - expect_true(file.exists(paste0(file, ".bk1"))) - - file.remove(file) - file.remove(paste0(file, ".bk1")) -}) - -test_that("Test append", { - bib <- bibentry("Misc", - title = "My title", - author = "Fran Herrero" - ) - - file <- file.path(tempdir(), "append") - expect_silent(write_citation(bib, file, verbose = FALSE, append = FALSE)) - - # Initial lines - lines1 <- readLines(file) - - # Append - bib2 <- bibentry("Misc", - key = "key2", - title = "Another title", - author = "Ian Henderson" - ) - - - write_citation(bib2, file, verbose = FALSE, append = TRUE) - expect_snapshot_file(file) - - lines2 <- readLines(file) - - # First lines identical, more lines on append - expect_true(all(lines1 == lines2[seq_len(length(lines1))])) - - expect_gt(length(lines2), length(lines1)) - - # Overwrite - write_citation(bib2, file, verbose = FALSE, append = FALSE) - lines3 <- readLines(file) - - expect_false(all(lines1 == lines3[seq_len(length(lines1))])) - expect_lt(length(lines3), length(lines2)) -}) - - -test_that("Test dir creation", { - bib <- bibentry("Misc", - title = "My title", - author = "Fran Herrero" - ) - - file <- file.path(tempdir(), "citdontexist", "append") - - dir <- dirname(file) - - expect_false(dir.exists(dir)) - expect_silent(write_citation(bib, file, verbose = FALSE)) - - expect_true(dir.exists(dir)) - expect_true(file.exists(file)) - - unlink(dir, recursive = TRUE, force = TRUE) - # With messages - file <- file.path(tempdir(), "citverbose", "append") - dir <- dirname(file) - expect_false(dir.exists(dir)) - expect_message( - write_citation(bib, file, verbose = TRUE), - "Creating directory" - ) - - expect_true(dir.exists(dir)) - expect_true(file.exists(file)) - - unlink(dir, recursive = TRUE, force = TRUE) -}) - - -test_that("Results can be parsed", { - file <- system.file("examples/CITATION_complete.cff", - package = "cffr" - ) - - tmp <- tempfile() - expect_silent(sil <- write_citation(file, tmp, verbose = FALSE)) - - expect_s3_class(sil, "bibentry") - - pp <- utils::readCitationFile(tmp, meta = list(Encoding = "UTF-8")) - - expect_s3_class(pp, "bibentry") - - expect_snapshot_output(toBibtex(pp)) -}) diff --git a/vignettes/crosswalk.Rmd b/vignettes/crosswalk.Rmd index e1cb1741..de1cf9bf 100644 --- a/vignettes/crosswalk.Rmd +++ b/vignettes/crosswalk.Rmd @@ -2,7 +2,7 @@ title: "From R to CFF" subtitle: "Crosswalk" description: > - A comprehenshive description of the internal mappings performed by `cffr`. + A comprehensive description of the internal mappings performed by **cffr**. author: Diego Hernangómez bibliography: REFERENCES.bib link-citations: yes @@ -82,7 +82,7 @@ knitr::kable(df, escape = FALSE) ### abstract -This key is extracted from the "Description" field of the DESCRIPTION file. +This key is extracted from the `"Description"` field of the `DESCRIPTION` file.
@@ -110,9 +110,9 @@ cat(pkg$get("Description")) ### authors -This key is parsed from the "Authors" or "Authors\@R" field of the DESCRIPTION -file. By default persons with the role "aut" or "cre" are considered, however -this can be modified via the `authors_roles` parameter. +This key is parsed from the `"Authors"` or `"Authors@R"` field of the +`DESCRIPTION` file. By default persons with the role `"aut"` or `"cre"` are +considered, however this can be modified via the `authors_roles` parameter.
@@ -169,7 +169,7 @@ v1.2.0](https://github.com/citation-file-format/citation-file-format/blob/main/s > - **description**: The commit hash or revision number of the software > version. > -> - **usage**:

+> - **usage**: > > ``` yaml > commit: 1ff847d81f29c45a3a1a5ce73d38e45c2f319bba @@ -181,8 +181,9 @@ v1.2.0](https://github.com/citation-file-format/citation-file-format/blob/main/s ### contact -This key is parsed from the "Authors" or "Authors\@R" field of the DESCRIPTION -file. Only persons with the role "cre" (i.e, the maintainer(s)) are considered. +This key is parsed from the `"Authors"` or `"Authors@R"` field of the +`DESCRIPTION` file. Only persons with the role `"cre"` (i.e, the maintainer(s)) +are considered.
@@ -203,14 +204,12 @@ pkg$get_author() ### date-released -This key is extracted following this logic: +This key is extracted from the `DESCRIPTION` file following this logic: -- "Date" field or, - -- If not present, from "Date/Publication". This is present on packages built - on CRAN and Bioconductor. Or, - -- If not present, from "Packaged", that is present on packages built by the +- `"Date"` field or, +- If not present, from `"Date/Publication"`. This is present on packages built + on **CRAN** and **Bioconductor**. or, +- If not present, from `"Packaged"`, that is present on packages built by the [r-universe](https://r-universe.dev/search/).
@@ -260,7 +259,7 @@ cat(cff_create(tmp)$`date-released`) ### doi {#doi} -This key is parsed from the "doi" field of the +This key is parsed from the `"doi"` field of the [preferred-citation](#preferred-citation) object.
@@ -283,12 +282,12 @@ cat(cff_doi$`preferred-citation`$doi) This key includes all the possible identifiers of the package: -- From the DESCRIPTION field, it includes all the urls not included in +- From the `DESCRIPTION` field, it includes all the urls not included in [url](#url) or [repository-code](#repository-code). -- From the CITATION file, it includes all the dois not included in [doi](#doi) - and the identifiers (if any) not included in the "identifiers" key of - [preferred-citation](#preferred-citation). +- From the `CITATION` file, it includes all the dois not included in + [doi](#doi) and the identifiers (if any) not included in the `"identifiers"` + key of [preferred-citation](#preferred-citation).
@@ -314,8 +313,8 @@ cff_create(file)$identifiers ### keywords -This key is extracted from the DESCRIPTION file. The keywords should appear in -the DESCRIPTION as: +This key is extracted from the `DESCRIPTION` file. The keywords should appear in +the `DESCRIPTION` as: ``` ... @@ -385,7 +384,7 @@ jsonval$`repository-code` ### license -This key is extracted from the "License" field of the DESCRIPTION file. +This key is extracted from the `"License"` field of the `DESCRIPTION` file.
@@ -415,14 +414,18 @@ v1.2.0](https://github.com/citation-file-format/citation-file-format/blob/main/s > dataset is licensed (only for non-standard licenses not included in the > [SPDX License > List](https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md#definitionslicense-enum)). -> - **usage**:

-> `yaml license-url: "https://obscure-licenses.com?id=1234"` +> +> - **usage**: +> +> ``` yaml +> license-url: "https://obscure-licenses.com?id=1234" +> ``` [Back to summary](#summary). ### message -This key is extracted from the DESCRIPTION field, specifically as: +This key is extracted from the `DESCRIPTION` field, specifically as: ```{r eval=FALSE} msg <- paste0( @@ -446,9 +449,9 @@ cat(cff_create("jsonlite")$message) ### preferred-citation {#preferred-citation} -This key is extracted from the CITATION file. If several references are -provided, it would select the first citation as the "preferred-citation" and the -rest of them as [references](#references). +This key is extracted from the `CITATION` file. If several references are +provided, it would select the first citation as the `"preferred-citation"` and +the rest of them as [references](#references).
@@ -468,11 +471,11 @@ citation("rmarkdown")[1] ### references {#references} -This key is extracted from the CITATION file if several references are provided. -The first citation is considered as the -[preferred-citation](#preferred-citation) and the rest of them as "references". -It also extracts the package dependencies and adds those to this fields using -`citation(auto = TRUE)` on each dependency. +This key is extracted from the `CITATION` file if several references are +provided. The first citation is considered as the +[preferred-citation](#preferred-citation) and the rest of them as +`"references"`. It also extracts the package dependencies and adds those to this +fields using `citation(auto = TRUE)` on each dependency.
@@ -492,20 +495,20 @@ citation("rmarkdown")[-1] ### repository -This key is extracted from the "Repository" field of the DESCRIPTION file. +This key is extracted from the `"Repository"` field of the `DESCRIPTION` file. Usually, this field is auto-populated when a package is hosted on a repo (like -CRAN or the [r-universe](https://r-universe.dev/)). For packages without this -field on the DESCRIPTION (that is the typical case for an in-development +**CRAN** or the [r-universe](https://r-universe.dev/)). For packages without +this field on the `DESCRIPTION` (that is the typical case for an in-development package), **cffr** would try to search the package on any of the default repositories specified on `options("repos")`. In the case of [Bioconductor](https://bioconductor.org/) packages, those are identified if a ["biocViews"](https://contributions.bioconductor.org/description.html#biocviews) -is present on the DESCRIPTION file. +is present on the `DESCRIPTION` file. -If **cffr** detects that the package is available on CRAN, it would return the -canonical url form of the package (i.e. +If **cffr** detects that the package is available on **CRAN**, it would return +the canonical url form of the package (i.e. ).
@@ -547,33 +550,6 @@ file.copy(norepo, tmp) desc::desc_set("Package", "ggplot2", file = tmp) cat(cff_create(tmp)[["repository"]]) - -# Show what happens if another repo is set - -# Save original config -orig_options <- options() -getOption("repos") - - -# Set new repos -options(repos = c( - tidyverse = "https://tidyverse.r-universe.dev", - CRAN = "https://cloud.r-project.org" -)) - -# Load again the library -# Repos are evaluated on load -unloadNamespace("cffr") -library(cffr) - - -cat(cff_create(tmp)[["repository"]]) - -# Now it is the tidyverse repo, due to our new config! - -# Reset original config -options(orig_options) -getOption("repos") ```
@@ -589,7 +565,7 @@ v1.2.0](https://github.com/citation-file-format/citation-file-format/blob/main/s > - **description**: The URL of the work in a build artifact/binary repository > (when the work is software). > -> - **usage**:

+> - **usage**: > > ``` yaml > repository-artifact: "https://search.maven.org/artifact/org.corpus-tools/cff-maven-plugin/0.4.0/maven-plugin" @@ -599,9 +575,9 @@ v1.2.0](https://github.com/citation-file-format/citation-file-format/blob/main/s ### repository-code {#repository-code} -This key is extracted from the "BugReports" or "URL" fields on the DESCRIPTION -file. **cffr** tries to identify the url of the source on the following -repositories: +This key is extracted from the `"BugReports"` or `"URL"` fields on the +`DESCRIPTION` file. **cffr** tries to identify the url of the source on the +following repositories: - [GitHub](https://github.com/). - [GitLab](https://about.gitlab.com/). @@ -637,7 +613,7 @@ desc::desc(gitlab) ### title -This key is extracted from the "Description" field of the DESCRIPTION file. +This key is extracted from the `"Description"` field of the `DESCRIPTION` file. ```{r eval=FALSE} title <- paste0( @@ -663,16 +639,16 @@ cat(cff_create("testthat")$title) ### type -Fixed value equal to "software". The other possible value is "dataset". See the -description on the [Guide to CFF schema +Fixed value equal to `"software"`. The other possible value is `"dataset"`. See +the description on the [Guide to CFF schema v1.2.0](https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md#type). [Back to summary](#summary). ### url {#url} -This key is extracted from the "BugReports" or "URL" fields on the DESCRIPTION -file. It corresponds to the first url that is different to +This key is extracted from the `"BugReports"` or `"URL"` fields on the +`DESCRIPTION` file. It corresponds to the first url that is different to [repository-code](#repository-code).
@@ -696,7 +672,7 @@ desc::desc(manyurls) ### version -This key is extracted from the "Version" field on the DESCRIPTION file. +This key is extracted from the `"Version"` field on the `DESCRIPTION` file. ```{r version} # Should be (>= 3.0.0) From 44dc4d47739cc7ae26a2c40c3f94e9711367ab9a Mon Sep 17 00:00:00 2001 From: Diego H Date: Wed, 28 Feb 2024 16:28:12 +0100 Subject: [PATCH 17/32] Start the review of the person conversion --- DESCRIPTION | 2 +- NAMESPACE | 1 + NEWS.md | 6 +- R/cff-methods.R | 4 + R/cff_create_person.R | 359 +++++++++++++++++++ R/cff_to_bibentry.R | 4 +- R/{utils-bibtex.R => encoded_utf_to_latex.R} | 2 +- R/utils-methods.R | 1 + R/utils-persons.R | 68 ++++ man/cff_create_person.Rd | 114 ++++++ man/cff_read.Rd | 2 +- man/cff_read_biblines.Rd | 2 +- man/cff_to_bibentry.Rd | 2 + man/cff_write.Rd | 2 +- man/cff_write_misc.Rd | 2 +- man/encoded_utf_to_latex.Rd | 4 +- man/roxygen/meta.R | 4 +- 17 files changed, 566 insertions(+), 13 deletions(-) create mode 100644 R/cff_create_person.R rename R/{utils-bibtex.R => encoded_utf_to_latex.R} (97%) create mode 100644 man/cff_create_person.Rd diff --git a/DESCRIPTION b/DESCRIPTION index b3eae1d3..fd4613e1 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -17,7 +17,7 @@ License: GPL (>= 3) URL: https://docs.ropensci.org/cffr/, https://github.com/ropensci/cffr BugReports: https://github.com/ropensci/cffr/issues Depends: - R (>= 3.6.0) + R (>= 4.0.0) Imports: cli (>= 2.0.0), desc (>= 1.3.0), diff --git a/NAMESPACE b/NAMESPACE index dfde1c7d..5468b29d 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -10,6 +10,7 @@ S3method(toBibtex,cff) export(as.cff) export(cff) export(cff_create) +export(cff_create_person) export(cff_extract_to_bibtex) export(cff_from_bibtex) export(cff_gha_update) diff --git a/NEWS.md b/NEWS.md index b971751c..4210a24a 100644 --- a/NEWS.md +++ b/NEWS.md @@ -40,7 +40,11 @@ modified: - `head.cff()`, `tail.cff()`. - `toBibtex.cff()`. -## Changes on BibTeX crosswalk +## Other changes + +- Minimum **R** version required now is **4.0.0**. + +### BibTeX crosswalk - **\@inbook** and **\@book** gains a new value on [CFF]{.underline} when **series** is provided: [collection-type: book-series.]{.underline} diff --git a/R/cff-methods.R b/R/cff-methods.R index c56b2e02..03c93cef 100644 --- a/R/cff-methods.R +++ b/R/cff-methods.R @@ -96,6 +96,10 @@ tail.cff <- function(x, n = 6L, ...) { #' #' @param object `cff` object. #' @param ... Arguments passed to [utils::toBibtex()]. +#' +#' @return +#' +#' `toBibtex.cff()` returns a `Bibtex` object. toBibtex.cff <- function(object, ..., what = c("preferred", "references", "all")) { # If a single reference... diff --git a/R/cff_create_person.R b/R/cff_create_person.R new file mode 100644 index 00000000..089eaf8a --- /dev/null +++ b/R/cff_create_person.R @@ -0,0 +1,359 @@ +#' Create a person with the corresponding [`cff`][cff-class] structure +#' +#' @description +#' +#' Create a `person` or `entity` as defined by the +#' +#' ```{r, echo=FALSE, results='asis'} +#' +#' cat(paste0(" [Citation File Format schema]", +#' "(https://github.com/citation-file-format/", +#' "citation-file-format/blob/main/schema-guide.md).")) +#' +#' +#' ``` +#' +#' [cff_create_person()] can convert the following objects: +#' - Objects with class `person` as provided by [utils::person()]. +#' - A `character` string with the definition of an author or several authors, +#' using the standard BibTeX notation. See Markey (2007) for a full +#' explanation. +#' +#' @seealso +#' Examples in `vignette("cffr", "cffr")` and [utils::person()]. +#' +#' @export +#' +#' @family coercing +#' +#' @param person It can be either: +#' - A `person` or list of `person` object created with [utils::person()]. +#' - A `character` object representing a person or persons. +#' See **Examples**. +#' @param hint Either `auto`, `person` or `bibtex`. See **Details**. +#' +#' @return +#' A list of persons or entities with class `cff` converted to the +#' ```{r, echo=FALSE, results='asis'} +#' +#' cat(paste0(" [Citation File Format schema]", +#' "(https://github.com/citation-file-format/", +#' "citation-file-format/blob/main/schema-guide.md).")) +#' +#' +#' ``` +#' +#' @details +#' +#' `cff_create_person()` would try to guess what type of object has been +#' provided in `person`. Possible options are: +#' +#' 1. If `person` is a [`person`][utils::person()] object it would be parsed +#' with a specific algorithm. +#' 2. However, if `person` is a `character` there are two options: +#' +#' 1. The `character` be a printed version of [utils::person()]. In this +#' case it may include information of email, ORCID, etc. See +#' [utils::as.person()] and **Details** in that same help page. +#' +#' 2. It can be just a person with BibTeX notation such as +#' `"von Doe, John and Smith, Jane"`. +#' +#' In **case 1** (`person = "person"`) `hint = "auto"` wouldn't affect and +#' internally the algorithm used with `hint = "person"` would be always used. +#' +#' `hint` argument would affect **case 2** (i.e. `person = "character"`). If +#' `hint = "auto"` `cff_create_person()` would try to guess the best +#' alternative, however it may be incorrect, so it is possible to specify the +#' conversion algorithm: +#' - `hint = "person"` would try to convert the `character` to `person` object +#' with [utils::as.person()] and then proceed with the algorithm for +#' `person` objects. +#' - `hint = "bibtex"` would use a special algorithm to create the `cff` +#' person. This option is better for strings like `"von Last, Jr, First"` +#' and produces improved mappings to CFF keys as `name-particle` and +#' `name-suffix`. It is also aware of protected names such as +#' `{John Doe and Jane Smith inc.}`. +#' + +#' +#' @references +#' - Patashnik, Oren. "BIBTEXTING" February 1988. +#' . +#' +#' - Markey, Nicolas. "Tame the BeaST." +#' *The B to X of BibTeX, Version 1.4* (October 2007). +#' . +#' +#' See **Examples** for more information. +#' +#' +#' @examples +#' # Create a person object +#' a_person <- person( +#' given = "First", family = "Author", +#' role = c("aut", "cre"), +#' email = "first.last@example.com", comment = c( +#' ORCID = "0000-0001-8457-4658", +#' affiliation = "An affiliation" +#' ) +#' ) +#' cff_create_person(a_person) +#' +#' # Parse a string +#' a_str <- paste0( +#' "Julio Iglesias ", +#' "()" +#' ) +#' cff_create_person(a_str) +#' +#' # Several persons +#' persons <- c(person("Clark", "Kent"), person("Lois", "Lane")) +#' +#' cff_create_person(persons) +#' +#' # Or you can use BibTeX style if you prefer +#' +#' x <- "Frank Sinatra and Dean Martin and Davis, Jr., Sammy and Joey Bishop" +#' +#' cff_create_person(x) +#' +#' cff_create_person("Herbert von Karajan") +cff_create_person <- function(person, hint = c("auto", "person", "bibtex")) { + hint <- match.arg(hint) + init_hint <- hint + hint <- guess_hint(person, hint) + + verbopt <- getOption("cffr_message_verbosity", "none") + if (all(verbopt == "debug", init_hint == "auto")) { + cli::cli_alert_info( + "In {.fn cff_create_person} using {.arg hint} = {.val {hint}}" + ) + } + + cff_obj <- switch(hint, + "person" = hint_person(person), + hint_bibtex(person) + ) + + cff_obj +} + +guess_hint <- function(person, hint) { + # On length 0 use "person" + if (length(person) == 0) { + return("person") + } + if (hint != "auto") { + return(hint) + } + if (inherits(person, "person")) { + return("person") + } + + # Rest of cases "bibtex" + return("bibtex") +} + +hint_person <- function(person) { + person <- as.person(person) + + if (length(person) == 0) { + return(NULL) + } + + if (length(person) > 1) { + person <- lapply(person, cff_parse_person) + person <- new_cff(person) + return(person) + } + + # Special case for Bioconductor + + if (is_substring(person$given, "Bioconductor")) { + person <- person( + given = paste( + clean_str(person$given), + clean_str(person$family) + ), + email = person$email, + role = person$role, + comment = person$comment + ) + } + + # Special case for R Core Team + if (all( + is_substring(clean_str(person$given), "R Core"), + is_substring(person$family, "Team") + )) { + person <- person( + given = paste( + clean_str(person$given), + clean_str(person$family) + ), + email = person$email, + role = person$role, + comment = person$comment + ) + } + + # Guess if entity of person. + is_entity <- is.null(person$family) || is.null(person$given) + + # Create my list + parsed_person <- list() + + if (is_entity) { + parsed_person$name <- clean_str(c(person$family, person$given)) + } else { + parsed_person$"family-names" <- clean_str(person$family) + parsed_person$"given-names" <- clean_str(person$given) + } + + # Check if several mails (MomTrunc 6.0) + valid_emails <- unlist(lapply(person$email, is_email)) + email <- person$email[valid_emails][1] + parsed_person$email <- clean_str(email) + + # Extract from comments + parsed_comments <- as.list(person$comment) + names(parsed_comments) <- tolower(names(parsed_comments)) + nms_com <- names(parsed_comments) + comment_as_text <- tolower(clean_str(parsed_comments)) + + # Special case when coerced from text, only can extract orcid + if (all( + any(is.na(nms_com), length(nms_com) == 0), + length(comment_as_text > 0) + ) + ) { + # guess orcid + norc <- min(unlist(regexpr("orcid.org/", comment_as_text))) + if (orc_text < 0) { + parsed_comments <- list() + } else { + orcid <- substr(comment_as_text, norc, nchar(comment_as_text)) + orcid <- clean_str(gsub("<|>", "", orcid)) + parsed_comments <- list(orcid = orcid) + } + } + + + # Extract for comments only what is not already there + parsed_comments <- parsed_comments[setdiff( + names(parsed_comments), + names(parsed_person) + )] + + + # Add url to orcid if not present + # Parse leading invalid urls + + if (!is.null(parsed_comments$orcid)) { + orcid <- gsub("^orcid.org/", "", parsed_comments$orcid) + orcid <- gsub("^https://orcid.org/", "", orcid) + orcid <- gsub("^http://orcid.org/", "", orcid) + + parsed_comments$orcid <- paste0("https://orcid.org/", orcid) + } + + # Add website + web <- parsed_comments$website + + if (!is.null(web)) { + parsed_comments$website <- clean_str(web[is_url(web)]) + } + + # Add comments + parsed_person <- c(parsed_person, parsed_comments) + + # Keep only valid tags - Would depend on entity or person + definition <- if (is_entity) { + cff_schema_definitions_entity() + } else { + cff_schema_definitions_person() + } + parsed_person <- parsed_person[names(parsed_person) %in% definition] + + parsed_person <- new_cff(parsed_person) + # Ensure it is always a list + if (!is.null(names(parsed_person))) { + parsed_person <- list(parsed_person) + class(parsed_person) <- c("cff", "list") + } + parsed_person +} + +hint_bibtex <- function(person) { + person <- trimws(person) + + # Remove role on [] as it comes from print.person by default + person <- gsub("\\[[^()]*\\]", "", person) + # Protect and on brackets {} + # Lower + protected <- gsub("(and)(?![^\\}]*(\\{|$))", "@nd@", + person, + perl = TRUE + ) + + # upper + protected <- gsub("AND(?![^\\}]*(\\{|$))", "@ND@", + protected, + perl = TRUE + ) + + # Do the same for and in comment "()" print.person by default + # Lower + protected <- gsub("(and)(?![^\\)]*(\\(|$))", "@nd@", + protected, + perl = TRUE + ) + + # upper + protected <- gsub("AND(?![^\\)]*(\\(|$))", "@ND@", + protected, + perl = TRUE + ) + + auths <- unlist(strsplit(protected, " and | AND ")) + + # Unprotec + auths_un <- gsub("@nd@", "and", auths) + auths_un <- gsub("@ND@", "AND", auths_un) + + + bibtex_auths <- lapply(auths_un, as_person_bibtex_comments) + end <- lapply(bibtex_auths, function(x) { + if (is.null(x$given)) { + ent <- c(x$von, x$family, x$jr) + ent <- clean_str(paste(ent, collapse = " ")) + l <- list(name = ent) + + # Append comments + coms <- x[setdiff(names(x), c("family", "given", "von", "jr"))] + l <- c(l, coms) + l <- new_cff(l) + l + } else { + l <- list( + "family-names" = x$family, + "given-names" = x$given, + "name-particle" = x$von, + "name-suffix" = x$jr + ) + coms <- x[setdiff(names(x), c("family", "given", "von", "jr"))] + l <- c(l, coms) + l <- new_cff(l) + l + } + }) + + # Ensure it is always a list + if (!is.null(names(end))) { + end <- list(end) + class(parsed_person) <- c("cff", "list") + } + + end <- new_cff(end) +} diff --git a/R/cff_to_bibentry.R b/R/cff_to_bibentry.R index b5f6bc2f..79768c4d 100644 --- a/R/cff_to_bibentry.R +++ b/R/cff_to_bibentry.R @@ -158,8 +158,8 @@ cff_bibtex_parser <- function(x) { } # Partially based on ruby parser - # https://github.com/citation-file-format/ruby-cff/blob/main/lib/cff/ - # formatter/bibtex_formatter.rb + # https://github.com/citation-file-format/ruby-cff/blob/main/lib/cff/ >> + # (cont) formatter/bibtex_formatter.rb # Create a initial empty list diff --git a/R/utils-bibtex.R b/R/encoded_utf_to_latex.R similarity index 97% rename from R/utils-bibtex.R rename to R/encoded_utf_to_latex.R index ceaf55f1..5258918c 100644 --- a/R/utils-bibtex.R +++ b/R/encoded_utf_to_latex.R @@ -21,7 +21,7 @@ #' @keywords internal #' @export #' -#' @examplesIf getRversion() >= "4.0.0" +#' @examples #' # Full range of supported characters on R #' library(tools) #' diff --git a/R/utils-methods.R b/R/utils-methods.R index 41434b1e..11c3b9ca 100644 --- a/R/utils-methods.R +++ b/R/utils-methods.R @@ -31,6 +31,7 @@ make_r_person <- function(x) { # Prepare ORCID x_comments <- gsub("^https://orcid.org/", "", x_comments) + x_comments <- gsub("^http://orcid.org/", "", x_comments) nm <- gsub("orcid", "ORCID", names(x_comments), fixed = TRUE) names(x_comments) <- nm diff --git a/R/utils-persons.R b/R/utils-persons.R index 441f120e..5b967749 100644 --- a/R/utils-persons.R +++ b/R/utils-persons.R @@ -260,6 +260,74 @@ bibtex_pers_first_von_last <- function(x) { return(end_list) } + +as_person_bibtex_comments <- function(x) { + # Can extract comments as R + comments_list <- NULL + locate_comment <- regexpr("<|\\(", x) + start_comment <- min(unlist(locate_comment)) + + if (start_comment > 0) { + # Has comments + the_comment <- trimws(substr(x, start_comment, nchar(x))) + + # Let R parse comments + res <- hint_person(paste0("fakename ", the_comment))[[1]] + comments_list <- res[names(res) != "name"] + + x <- trimws(substr(x, 0, start_comment - 1)) + } + + # Identify the pattern + # It may be one of: + # A. Given von Family + # B. von Family, Given + # C. von Family, Junior, Given + + # Protect commas on brackets to avoid error counting + protected <- gsub(",(?![^\\}]*(\\{|$))", "@comma@", + x, + perl = TRUE + ) + + commas <- length(grep(",", unlist(strsplit(protected, "|")))) + + if (commas == 0) { + # Case A + end_list <- bibtex_pers_first_von_last(x) + } else if (commas == 1) { + # Case B + end_list <- bibtex_pers_von_last_first(x) + } else if (commas == 2) { + # Case C + end_list <- bibtex_pers_von_last_first_jr(x) + } else { + # Not considered by BibTeX. everything to family + end_list <- list(family = paste(x, collapse = " ")) + } + + + # Clean + end_list <- lapply(end_list, function(z) { + if (is.null(z)) { + return(NULL) + } + if (any((is.na(z) | z == ""))) { + return(NULL) + } + + gsub("\\{|\\}", "", z) + }) + + if (!is.null(comments_list)) { + end_list <- c(end_list, comments_list) + } + end_list <- lapply(end_list, clean_str) + + return(end_list) +} + + as_person_bibtex <- function(x) { # Identify the pattern # It may be one of: diff --git a/man/cff_create_person.Rd b/man/cff_create_person.Rd new file mode 100644 index 00000000..5f973e29 --- /dev/null +++ b/man/cff_create_person.Rd @@ -0,0 +1,114 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/cff_create_person.R +\name{cff_create_person} +\alias{cff_create_person} +\title{Create a person with the corresponding \code{\link[=cff-class]{cff}} structure} +\usage{ +cff_create_person(person, hint = c("auto", "person", "bibtex")) +} +\arguments{ +\item{person}{It can be either: +\itemize{ +\item A \code{person} or list of \code{person} object created with \code{\link[utils:person]{utils::person()}}. +\item A \code{character} object representing a person or persons. +See \strong{Examples}. +}} + +\item{hint}{Either \code{auto}, \code{person} or \code{bibtex}. See \strong{Details}.} +} +\value{ +A list of persons or entities with class \code{cff} converted to the \href{https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md}{Citation File Format schema}. +} +\description{ +Create a \code{person} or \code{entity} as defined by the +\href{https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md}{Citation File Format schema}. + +\code{\link[=cff_create_person]{cff_create_person()}} can convert the following objects: +\itemize{ +\item Objects with class \code{person} as provided by \code{\link[utils:person]{utils::person()}}. +\item A \code{character} string with the definition of an author or several authors, +using the standard BibTeX notation. See Markey (2007) for a full +explanation. +} +} +\details{ +\code{cff_create_person()} would try to guess what type of object has been +provided in \code{person}. Possible options are: +\enumerate{ +\item If \code{person} is a \code{\link[utils:person]{person}} object it would be parsed +with a specific algorithm. +\item However, if \code{person} is a \code{character} there are two options: +\enumerate{ +\item The \code{character} be a printed version of \code{\link[utils:person]{utils::person()}}. In this +case it may include information of email, ORCID, etc. See +\code{\link[utils:person]{utils::as.person()}} and \strong{Details} in that same help page. +\item It can be just a person with BibTeX notation such as +\code{"von Doe, John and Smith, Jane"}. +} +} + +In \strong{case 1} (\code{person = "person"}) \code{hint = "auto"} wouldn't affect and +internally the algorithm used with \code{hint = "person"} would be always used. + +\code{hint} argument would affect \strong{case 2} (i.e. \code{person = "character"}). If +\code{hint = "auto"} \code{cff_create_person()} would try to guess the best +alternative, however it may be incorrect, so it is possible to specify the +conversion algorithm: +\itemize{ +\item \code{hint = "person"} would try to convert the \code{character} to \code{person} object +with \code{\link[utils:person]{utils::as.person()}} and then proceed with the algorithm for +\code{person} objects. +\item \code{hint = "bibtex"} would use a special algorithm to create the \code{cff} +person. This option is better for strings like \code{"von Last, Jr, First"} +and produces improved mappings to CFF keys as \code{name-particle} and +\code{name-suffix}. It is also aware of protected names such as +\verb{\{John Doe and Jane Smith inc.\}}. +} +} +\examples{ +# Create a person object +a_person <- person( + given = "First", family = "Author", + role = c("aut", "cre"), + email = "first.last@example.com", comment = c( + ORCID = "0000-0001-8457-4658", + affiliation = "An affiliation" + ) +) +cff_create_person(a_person) + +# Parse a string +a_str <- paste0( + "Julio Iglesias ", + "()" +) +cff_create_person(a_str) + +# Several persons +persons <- c(person("Clark", "Kent"), person("Lois", "Lane")) + +cff_create_person(persons) + +# Or you can use BibTeX style if you prefer + +x <- "Frank Sinatra and Dean Martin and Davis, Jr., Sammy and Joey Bishop" + +cff_create_person(x) + +cff_create_person("Herbert von Karajan") +} +\references{ +\itemize{ +\item Patashnik, Oren. "BIBTEXTING" February 1988. +\url{https://osl.ugr.es/CTAN/biblio/bibtex/base/btxdoc.pdf}. +\item Markey, Nicolas. "Tame the BeaST." +\emph{The B to X of BibTeX, Version 1.4} (October 2007). +\url{https://osl.ugr.es/CTAN/info/bibtex/tamethebeast/ttb_en.pdf}. +} + +See \strong{Examples} for more information. +} +\seealso{ +Examples in \code{vignette("cffr", "cffr")} and \code{\link[utils:person]{utils::person()}}. +} +\concept{coercing} diff --git a/man/cff_read.Rd b/man/cff_read.Rd index 1a97c82a..98f6efc0 100644 --- a/man/cff_read.Rd +++ b/man/cff_read.Rd @@ -133,7 +133,7 @@ The underlying functions used for reading external files: \item \code{\link[bibtex:read.bib]{bibtex::read.bib()}} for BibTeX files (extension \verb{*.bib}). } -Reading other files as \code{cff}: +Other functions for reading external files: \code{\link{cff_read_biblines}()} Other functions for working with BibTeX format: diff --git a/man/cff_read_biblines.Rd b/man/cff_read_biblines.Rd index ac3a1030..77ecb59b 100644 --- a/man/cff_read_biblines.Rd +++ b/man/cff_read_biblines.Rd @@ -66,7 +66,7 @@ Other functions for working with BibTeX format: \code{\link{cff_write_bib}()}, \code{\link{encoded_utf_to_latex}()} -Reading other files as \code{cff}: +Other functions for reading external files: \code{\link{cff_read}()} } \concept{bibtex} diff --git a/man/cff_to_bibentry.Rd b/man/cff_to_bibentry.Rd index a5e21a9b..3aa6087e 100644 --- a/man/cff_to_bibentry.Rd +++ b/man/cff_to_bibentry.Rd @@ -37,6 +37,8 @@ both the preferred citation info and the references. \value{ A \code{bibentry} object or a list of \code{bibentry} objects. This could be parsed to BibTeX using \code{\link[=toBibtex]{toBibtex()}} + +\code{toBibtex.cff()} returns a \code{Bibtex} object. } \description{ This function creates \code{\link[=bibentry]{bibentry()}} objects diff --git a/man/cff_write.Rd b/man/cff_write.Rd index 997e3172..2ad4167f 100644 --- a/man/cff_write.Rd +++ b/man/cff_write.Rd @@ -85,7 +85,7 @@ Other core functions of \CRANpkg{cffr}: \code{\link{cff_create}()}, \code{\link{cff_validate}()} -Other write: +Other functions for creating external files: \code{\link{cff_write_bib}()} } \concept{core} diff --git a/man/cff_write_misc.Rd b/man/cff_write_misc.Rd index 3704432e..5380a916 100644 --- a/man/cff_write_misc.Rd +++ b/man/cff_write_misc.Rd @@ -118,7 +118,7 @@ Other functions for working with BibTeX format: \code{\link{cff_to_bibentry}()}, \code{\link{encoded_utf_to_latex}()} -Other write: +Other functions for creating external files: \code{\link{cff_write}()} } \concept{bibtex} diff --git a/man/encoded_utf_to_latex.Rd b/man/encoded_utf_to_latex.Rd index 8e8992cd..a20328ab 100644 --- a/man/encoded_utf_to_latex.Rd +++ b/man/encoded_utf_to_latex.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/utils-bibtex.R +% Please edit documentation in R/encoded_utf_to_latex.R \name{encoded_utf_to_latex} \alias{encoded_utf_to_latex} \title{Encode UTF-8 text to LaTeX} @@ -20,7 +20,6 @@ This is a variation of \code{\link[tools:encoded]{tools::encoded_text_to_latex() additional replacements to increase compatibility. } \examples{ -\dontshow{if (getRversion() >= "4.0.0") (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} # Full range of supported characters on R library(tools) @@ -40,7 +39,6 @@ ascii_table$latex_base <- encoded_text_to_latex(ascii_table$utf8, ascii_table$latex_cffr <- encoded_utf_to_latex(ascii_table$utf8) ascii_table -\dontshow{\}) # examplesIf} } \seealso{ \code{\link[tools:encoded]{tools::encoded_text_to_latex()}} diff --git a/man/roxygen/meta.R b/man/roxygen/meta.R index d6e96117..32178d85 100644 --- a/man/roxygen/meta.R +++ b/man/roxygen/meta.R @@ -1,7 +1,9 @@ list( rd_family_title = list( core = "Other core functions of \\CRANpkg{cffr}:", - reading = "Reading other files as \\code{cff}:", + reading = "Other functions for reading external files:", + coercing = "Other functions for converting between \\strong{R} classes:", + write = "Other functions for creating external files:", bibtex = "Other functions for working with BibTeX format:", schemas = "Other CFF schemas:", git = "Other Git/GitHub helpers:", From fe36c20f6a00beb5306f33ac6253947553fb3e46 Mon Sep 17 00:00:00 2001 From: Diego H Date: Wed, 28 Feb 2024 17:37:18 +0100 Subject: [PATCH 18/32] Try fixing issues --- R/cff_create_person.R | 3 ++- R/parse_citation.R | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/R/cff_create_person.R b/R/cff_create_person.R index 089eaf8a..579cf0f7 100644 --- a/R/cff_create_person.R +++ b/R/cff_create_person.R @@ -230,7 +230,7 @@ hint_person <- function(person) { ) { # guess orcid norc <- min(unlist(regexpr("orcid.org/", comment_as_text))) - if (orc_text < 0) { + if (norc < 0) { parsed_comments <- list() } else { orcid <- substr(comment_as_text, norc, nchar(comment_as_text)) @@ -287,6 +287,7 @@ hint_person <- function(person) { hint_bibtex <- function(person) { person <- trimws(person) + person <- unname(person) # Remove role on [] as it comes from print.person by default person <- gsub("\\[[^()]*\\]", "", person) diff --git a/R/parse_citation.R b/R/parse_citation.R index 8f841147..b38f4619 100644 --- a/R/parse_citation.R +++ b/R/parse_citation.R @@ -139,6 +139,9 @@ building_other_persons <- function(parsed_fields) { toentity_pers <- others[names(others) %in% toent_pers] toentity_pers <- lapply(toentity_pers, function(x) { bibtex <- paste(x, collapse = " and ") + # Unname + names(bibtex) <- NULL + end <- cff_parse_person_bibtex(bibtex) From e5e7c7ae9fbe7ffc5cfa43779508028d6d8a150c Mon Sep 17 00:00:00 2001 From: Diego H Date: Wed, 28 Feb 2024 19:09:42 +0100 Subject: [PATCH 19/32] Fix tests for devel version --- .github/workflows/check-full.yaml | 10 ++++------ tests/testthat/test-cff_parse_citation.R | 9 +++------ 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/.github/workflows/check-full.yaml b/.github/workflows/check-full.yaml index f0008bef..a1077e05 100644 --- a/.github/workflows/check-full.yaml +++ b/.github/workflows/check-full.yaml @@ -27,16 +27,14 @@ jobs: config: - {os: windows-latest, r: 'devel'} - {os: windows-latest, r: 'release'} - # - {os: windows-latest, r: 'oldrel'} + - {os: windows-latest, r: 'oldrel'} - {os: macOS-latest, r: 'devel'} - {os: macOS-latest, r: 'release'} - #- {os: macOS-latest, r: 'oldrel'} + - {os: macOS-latest, r: 'oldrel'} - {os: ubuntu-latest, r: 'release'} - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} - #- {os: ubuntu-latest, r: 'oldrel'} - #- {os: ubuntu-latest, r: 'oldrel-2'} - #- {os: ubuntu-latest, r: '3.6'} - + - {os: ubuntu-latest, r: 'oldrel'} + - {os: ubuntu-latest, r: 'oldrel-2'} env: GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} diff --git a/tests/testthat/test-cff_parse_citation.R b/tests/testthat/test-cff_parse_citation.R index ab9739bc..76d344fd 100644 --- a/tests/testthat/test-cff_parse_citation.R +++ b/tests/testthat/test-cff_parse_citation.R @@ -151,20 +151,17 @@ test_that("Parse persons on CITATION", { title = "A Language and Environment for Statistical Computing", year = "2021", author = person("R Core Team"), - contact = c(person("A", "name"), person("A contact")), + contact = "A name and A contact", conference = person("A", "conference"), "database-provider" = person("Database", "provider"), - editors = c(person("A", "editor"), person("{Ben and Jerry}")), + editors = "A editor and {Ben and Jerry}", "editors-series" = "An {editor series} and Another", "institution" = person("A", "institution"), "address" = person("A", "location"), "publisher" = person("A", "publisher"), "recipients" = "A recipient", "senders" = "{A Sender} and Another Sender", - "translators" = c( - person("Translator", "one"), - person("Translator", "two") - ) + "translators" = "Translator one and Translator two" ) bibparsed <- cff_parse_citation(bib) From b5f6d852f230482e44c887813c67aae7f79f14b1 Mon Sep 17 00:00:00 2001 From: Diego H Date: Thu, 29 Feb 2024 14:47:14 +0100 Subject: [PATCH 20/32] Prepare new person and refactor tests and API --- CITATION.cff | 2 +- NAMESPACE | 4 +- NEWS.md | 4 +- R/{cff_to_bibentry.R => as_bibentry.R} | 109 +-- R/cff-methods.R | 28 +- R/cff_create_cff_person.R | 454 +++++++++++ R/cff_create_person.R | 360 --------- ...ff_read_biblines.R => cff_read_bib_text.R} | 6 +- R/cff_write_misc.R | 8 +- R/deprecated.R | 26 +- R/utils-methods.R | 6 +- R/utils-persons.R | 68 -- README.md | 2 +- codemeta.json | 4 +- man/{cff_to_bibentry.Rd => as_bibentry.Rd} | 46 +- man/cff-class.Rd | 4 +- ...ead_biblines.Rd => cff_create_bib_text.Rd} | 14 +- man/cff_create_person.Rd | 98 ++- man/cff_read.Rd | 6 +- man/cff_write_misc.Rd | 8 +- man/chunks/cff_create_person.Rmd | 20 + man/chunks/cffclass.Rmd | 1 - man/deprecated_cff_from_bib.Rd | 6 +- man/deprecated_cff_to_bib.Rd | 6 +- man/encoded_utf_to_latex.Rd | 4 +- .../{cff_to_bibentry.md => as_bibentry.md} | 2 +- tests/testthat/_snaps/bibtex2cff.md | 483 ------------ tests/testthat/_snaps/cff-methods.md | 30 +- tests/testthat/_snaps/cff_create.md | 395 ++++++++++ tests/testthat/_snaps/cff_description.md | 395 ---------- tests/testthat/_snaps/cff_parse_citation.md | 483 ++++++++++++ ..._read_biblines.md => cff_read_bib_text.md} | 8 +- tests/testthat/_snaps/deprecated.md | 6 +- ...heck-ruby.md => xtra-check-bibtex-ruby.md} | 2 +- tests/testthat/test-additional-authors.R | 74 -- ...t-cff_to_bibentry.R => test-as_bibentry.R} | 72 +- tests/testthat/test-bibtex2cff.R | 731 ----------------- tests/testthat/test-cff-methods.R | 4 +- tests/testthat/test-cff_create.R | 454 +++++++++++ tests/testthat/test-cff_description.R | 375 --------- tests/testthat/test-cff_parse_citation.R | 734 ++++++++++++++++++ ...ad_biblines.R => test-cff_read_bib_text.R} | 8 +- tests/testthat/test-deprecated.R | 6 +- ...k-ruby.R => test-xtra-check-bibtex-ruby.R} | 30 +- vignettes/bibtex_cff.Rmd | 62 +- vignettes/cffr.Rmd | 4 +- 46 files changed, 2890 insertions(+), 2762 deletions(-) rename R/{cff_to_bibentry.R => as_bibentry.R} (85%) create mode 100644 R/cff_create_cff_person.R delete mode 100644 R/cff_create_person.R rename R/{cff_read_biblines.R => cff_read_bib_text.R} (93%) rename man/{cff_to_bibentry.Rd => as_bibentry.Rd} (71%) rename man/{cff_read_biblines.Rd => cff_create_bib_text.Rd} (86%) create mode 100644 man/chunks/cff_create_person.Rmd rename tests/testthat/_snaps/{cff_to_bibentry.md => as_bibentry.md} (99%) delete mode 100644 tests/testthat/_snaps/bibtex2cff.md delete mode 100644 tests/testthat/_snaps/cff_description.md rename tests/testthat/_snaps/{cff_read_biblines.md => cff_read_bib_text.md} (86%) rename tests/testthat/_snaps/{bibtex-check-ruby.md => xtra-check-bibtex-ruby.md} (99%) delete mode 100644 tests/testthat/test-additional-authors.R rename tests/testthat/{test-cff_to_bibentry.R => test-as_bibentry.R} (88%) delete mode 100644 tests/testthat/test-bibtex2cff.R delete mode 100644 tests/testthat/test-cff_description.R rename tests/testthat/{test-cff_read_biblines.R => test-cff_read_bib_text.R} (81%) rename tests/testthat/{test-bibtex-check-ruby.R => test-xtra-check-bibtex-ruby.R} (85%) diff --git a/CITATION.cff b/CITATION.cff index d0826b67..f6be3074 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -96,7 +96,7 @@ references: name: R Foundation for Statistical Computing address: Vienna, Austria year: '2024' - version: '>= 3.6.0' + version: '>= 4.0.0' - type: software title: cli abstract: 'cli: Helpers for Developing Command Line Interfaces' diff --git a/NAMESPACE b/NAMESPACE index 5468b29d..066e9f38 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -8,8 +8,10 @@ S3method(print,cff) S3method(tail,cff) S3method(toBibtex,cff) export(as.cff) +export(as_bibentry) export(cff) export(cff_create) +export(cff_create_bib_text) export(cff_create_person) export(cff_extract_to_bibtex) export(cff_from_bibtex) @@ -21,7 +23,6 @@ export(cff_parse_person) export(cff_parse_person_bibtex) export(cff_read) export(cff_read_bib) -export(cff_read_biblines) export(cff_read_cff_citation) export(cff_read_citation) export(cff_read_description) @@ -30,7 +31,6 @@ export(cff_schema_definitions_person) export(cff_schema_definitions_refs) export(cff_schema_keys) export(cff_schema_keys_license) -export(cff_to_bibentry) export(cff_to_bibtex) export(cff_validate) export(cff_write) diff --git a/NEWS.md b/NEWS.md index 4210a24a..a9d84572 100644 --- a/NEWS.md +++ b/NEWS.md @@ -14,13 +14,13 @@ to non-core functions** that an user would rarely call, while `cff_create()`, modified: - The conversion from `cff` to `bibentry` is performed now by a new function - `cff_to_bibentry()`. Previous names of this function were `cff_to_bibtex()` + `as_bibentry()`. Previous names of this function were `cff_to_bibtex()` and `cff_extract_to_bibtex()` that are now superseded. - Now reading from external files is performed exclusively by `cff_read()` and additionally by the more-specific new functions `cff_read_cff_citation()`, `cff_read_description()`, `cff_read_citation()` and `cff_read_bib()`. It is - also possible to read BibTeX lines with `cff_read_biblines()`. Previous + also possible to read BibTeX lines with `cff_create_bib_text()`. Previous function `cff_from_bibtex()` is now superseded. - `write_bib()` and `write_citation()` superseded by `cff_write_bib()` and diff --git a/R/cff_to_bibentry.R b/R/as_bibentry.R similarity index 85% rename from R/cff_to_bibentry.R rename to R/as_bibentry.R index 79768c4d..8504f736 100644 --- a/R/cff_to_bibentry.R +++ b/R/as_bibentry.R @@ -1,20 +1,22 @@ -#' Create BibTeX entries from several sources +#' Create `bibentry` objects from several sources #' -#' @rdname cff_to_bibentry -#' @name cff_to_bibentry +#' @rdname as_bibentry +#' @name as_bibentry #' @order 1 #' @description #' -#' This function creates [bibentry()] objects -#' from different metadata sources (`cff` objects, `DESCRIPTION` files, etc.). +#' This function creates `bibentry` objects (see [utils::bibentry()]) from +#' different metadata sources (`cff` objects, `DESCRIPTION` files, etc.). Note +#' that a **R** `bibentry` object is the representation of a BibTeX entry, +#' see **Examples** +#' #' The function tries to map the information of the source `x` into a `cff` #' object and performs a mapping of the metadata to BibTeX, according to #' `vignette("bibtex_cff", "cffr")`. #' -#' Note that a **R** [bibentry()] object is the representation of a BibTeX -#' entry, see **Examples**. -#' +#' @seealso +#' [utils::bibentry()] #' #' @references #' - Patashnik, Oren. "BIBTEXTING" February 1988. @@ -44,9 +46,11 @@ #' both the preferred citation info and the references. #' #' @family bibtex +#' @family coercing #' -#' @return A `bibentry` object or a list of `bibentry` objects. This could -#' be parsed to BibTeX using [toBibtex()] +#' @return +#' `as_bibentry()` returns s `bibentry` object (or a list of `bibentry` +#' objects). #' #' @export #' @@ -58,7 +62,7 @@ #' cff_object #' #' # bibentry object -#' bib <- cff_to_bibentry(cff_object) +#' bib <- as_bibentry(cff_object) #' #' class(bib) #' @@ -75,13 +79,13 @@ #' # From a CITATION.cff file with options #' #' path <- system.file("examples/CITATION_complete.cff", package = "cffr") -#' cff_file <- cff_to_bibentry(path, what = "all") +#' cff_file <- as_bibentry(path, what = "all") #' #' toBibtex(cff_file) #' #' # For an installed package #' -#' installed_package <- cff_to_bibentry("jsonvalidate") +#' installed_package <- as_bibentry("jsonvalidate") #' #' toBibtex(installed_package) #' @@ -89,12 +93,12 @@ #' # Use a DESCRIPTION file #' #' path2 <- system.file("examples/DESCRIPTION_gitlab", package = "cffr") -#' desc_file <- cff_to_bibentry(path2) +#' desc_file <- as_bibentry(path2) #' #' toBibtex(desc_file) #' } -cff_to_bibentry <- function(x, - what = c("preferred", "references", "all")) { +as_bibentry <- function(x, + what = c("preferred", "references", "all")) { what <- match.arg(what) if (is.null(x)) { return(NULL) @@ -109,28 +113,52 @@ cff_to_bibentry <- function(x, } else { obj <- cff_create(x) } - if (what == "preferred") { - return(cff_bibtex_parser(obj)) - } - if (what == "references") { - if (is.null(obj$references)) { - return(NULL) + + # Three cases: + # A) Full cff reference object or + # B) Individual reference list + # C) List of references + + + # Detect case A + is_full_cff <- "cff-version" %in% names(obj) + # Detect case B + is_single_ref <- "type" %in% names(obj) + + if (is_full_cff) { + # Try to generate preferred if not present + if (!("preferred-citation" %in% names(obj))) { + prefcit <- obj + prefcit$type <- "generic" + prefcit <- prefcit[names(prefcit) %in% cff_schema_definitions_refs()] + prefcit <- new_cff(prefcit) + # And add to the object + obj$`preferred-citation` <- prefcit } - ref <- lapply(obj$references, cff_bibtex_parser) - return(do.call(c, ref)) + # Select type to extract + obj_extract <- switch(what, + "preferred" = list(obj$`preferred-citation`), + "references" = obj$references, + c(list(obj$`preferred-citation`), obj$references) + ) + } else if (is_single_ref) { + obj_extract <- list(obj) + } else { + obj_extract <- obj } - pref <- cff_bibtex_parser(obj) - - if (!is.null(obj$references)) { - ref <- lapply(obj$references, cff_bibtex_parser) - ref <- do.call(c, ref) - return(c(pref, ref)) + # Cleanup + obj_extract <- obj_extract[lengths(obj_extract) > 0] + if (length(obj_extract) == 0) { + return(NULL) } - return(pref) + ref <- lapply(obj_extract, cff_bibtex_parser) + ref <- do.call(c, ref) + + return(ref) } @@ -141,22 +169,6 @@ cff_bibtex_parser <- function(x) { stopifnotcff(x) - # Read cff of CITATION.cff file - if (!is_cff(x)) { - x <- cff(x) - } - - # Try to generate preferred if not present - if (!("preferred-citation" %in% names(x))) { - origtype <- clean_str(x$type) - - if (is.null(origtype)) { - x$type <- "misc" - } - } else { - x <- x$`preferred-citation` - } - # Partially based on ruby parser # https://github.com/citation-file-format/ruby-cff/blob/main/lib/cff/ >> # (cont) formatter/bibtex_formatter.rb @@ -528,5 +540,8 @@ cff_bibtex_parser <- function(x) { return(NULL) } + # Unlist easy to undo the do.call effect + bib <- bib[[1]] + return(bib) } diff --git a/R/cff-methods.R b/R/cff-methods.R index 03c93cef..3caeea37 100644 --- a/R/cff-methods.R +++ b/R/cff-methods.R @@ -44,10 +44,26 @@ as.data.frame.cff <- function(x, row.names = NULL, optional = FALSE, ...) { return(the_df) } -#' Persons +#' @rdname cff_create_person +#' @name as.person.cff +#' @order 2 #' -#' @noRd +#' @description +#' Additionally, it is also provided a method for [as.person()], that can +#' convert [`cff`][cff-class] objects to `person` objects as +#' provided by [utils::person()]. +#' +#' This method only works with CFF persons, not with full `cff` objects. +#' +#' @family s3method #' @export +#' @seealso [utils::person()] +#' +#' @param x `cff` object representing a person or entity. +#' +#' @return +#' +#' `as.person.cff()` returns a `person` object. as.person.cff <- function(x) { # If single enclose on a list is_single <- any(grepl("^name$|^given-names|^family-names", names(x))) @@ -81,7 +97,8 @@ tail.cff <- function(x, n = 6L, ...) { } -#' @rdname cff_to_bibentry +#' @rdname as_bibentry +#' @name toBibtex.cff #' @order 2 #' #' @description @@ -99,13 +116,14 @@ tail.cff <- function(x, n = 6L, ...) { #' #' @return #' -#' `toBibtex.cff()` returns a `Bibtex` object. +#' `toBibtex.cff()` returns a `Bibtex` object that can be printed as BibTeX +#' markup. toBibtex.cff <- function(object, ..., what = c("preferred", "references", "all")) { # If a single reference... if ("cff-version" %in% names(object)) { # If full cff - biblist_cff <- cff_to_bibentry(x = object, what = what) + biblist_cff <- as_bibentry(x = object, what = what) } else { # Need to enlist if single if ("type" %in% names(object)) { diff --git a/R/cff_create_cff_person.R b/R/cff_create_cff_person.R new file mode 100644 index 00000000..613d3516 --- /dev/null +++ b/R/cff_create_cff_person.R @@ -0,0 +1,454 @@ +#' Create a person with the corresponding [`cff`][cff-class] structure +#' +#' @description +#' +#' Create a `person` or `entity` as defined by the +#' +#' ```{r, echo=FALSE, results='asis'} +#' +#' cat(paste0(" [Citation File Format schema]", +#' "(https://github.com/citation-file-format/", +#' "citation-file-format/blob/main/schema-guide.md).")) +#' +#' +#' ``` +#' +#' [cff_create_person()] can convert the following objects: +#' - Objects with class `person` as provided by [utils::person()]. +#' - A `character` string with the definition of an author or several authors, +#' using the standard BibTeX notation. See Markey (2007) for a full +#' explanation. +#' +#' @seealso +#' Examples in `vignette("cffr", "cffr")` and [utils::person()]. +#' +#' @export +#' @rdname cff_create_person +#' @name cff_create_person +#' @order 1 +#' +#' @family coercing +#' +#' @param person It can be either: +#' - A `person` or list of `person` object created with [utils::person()]. +#' - A `character` object or vector representing a person or persons. +#' See **Examples**. +#' +#' @return +#' `cff_create_person()` returns A list of persons or entities with class `cff` +#' converted to the +#' ```{r, echo=FALSE, results='asis'} +#' +#' cat(paste0(" [Citation File Format schema]", +#' "(https://github.com/citation-file-format/", +#' "citation-file-format/blob/main/schema-guide.md).")) +#' +#' +#' ``` +#' +#' @details +#' +#' `cff_create_person()` uses a custom algorithm that tries to break a name +#' as explained in Section 11 of "Tame the BeaST" (Markey, 2007): +#' - `First von Last` +#' - `von Last, First` +#' - `von Last, Jr, First` +#' +#' `First` is mapped to the CFF field `given-names`, `von` to `name-particle`, +#' `Last` to `family-names` and `Jr` to `name-suffix`. +#' +#' In the case of entities, the whole `character` would be mapped to `name`. +#' It is a good practice to "protect" entity's names with `{}`: +#' +#' ```{r child = "man/chunks/cff_create_person.Rmd"} +#' ``` +#' `cff_create_person()` would try to add as many information as possible. On +#' `character` string coming from [`format(person())`][utils::person()] the +#' email and the ORCID would be gathered as well. +#' +#' @references +#' - Patashnik, Oren. "BIBTEXTING" February 1988. +#' . +#' +#' - Markey, Nicolas. "Tame the BeaST" +#' *The B to X of BibTeX, Version 1.4* (October 2007). +#' . +#' +#' See **Examples** for more information. +#' +#' +#' @examples +#' # Create a person object +#' a_person <- person( +#' given = "First", family = "Author", +#' role = c("aut", "cre"), +#' email = "first.last@example.com", comment = c( +#' ORCID = "0000-0001-8457-4658", +#' affiliation = "An affiliation" +#' ) +#' ) +#' +#' a_person +#' +#' cff_person <- cff_create_person(a_person) +#' +#' cff_person +#' +#' # Back to person object with S3 Method +#' as.person(cff_person) +#' +#' # Parse a string +#' a_str <- paste0( +#' "Julio Iglesias ", +#' "()" +#' ) +#' cff_create_person(a_str) +#' +#' # Several persons +#' persons <- c(person("Clark", "Kent"), person("Lois", "Lane")) +#' +#' cff_create_person(persons) +#' +#' # Or you can use BibTeX style if you prefer +#' +#' x <- "Frank Sinatra and Dean Martin and Davis, Jr., Sammy and Joey Bishop" +#' +#' cff_create_person(x) +#' +#' cff_create_person("Herbert von Karajan") +cff_create_person <- function(person) { + hint <- guess_hint(person) + + verbopt <- getOption("cffr_message_verbosity", "none") + if (verbopt == "debug") { + cli::cli_alert_info( + "In {.fn cff_create_person} using internal for {.val {hint}}." + ) + } + + if (hint == "txt") { + # Need to split the character + person_split <- split_txt_persons(person) + the_obj <- lapply(person_split, create_person_from_txt) + } else { + the_obj <- lapply(person, create_person_from_r) + } + + if (!length(the_obj) > 0) { + return(NULL) + } + the_obj <- new_cff(the_obj) + the_obj +} + +guess_hint <- function(person) { + # On length 0 use "person" + if (length(person) == 0) { + return("person") + } + if (inherits(person, "person")) { + return("person") + } + + # Rest of cases "txt" + return("txt") +} + +create_person_from_r <- function(person) { + person <- as.person(person) + + if (length(person) == 0) { + return(NULL) + } + + # Special case for Bioconductor + + if (is_substring(person$given, "Bioconductor")) { + person <- person( + given = paste( + clean_str(person$given), + clean_str(person$family) + ), + email = person$email, + role = person$role, + comment = person$comment + ) + } + + # Special case for R Core Team + if (all( + is_substring(clean_str(person$given), "R Core"), + is_substring(person$family, "Team") + )) { + person <- person( + given = paste( + clean_str(person$given), + clean_str(person$family) + ), + email = person$email, + role = person$role, + comment = person$comment + ) + } + + # Guess if entity of person. + is_entity <- is.null(person$family) || is.null(person$given) + + # Create a text version of the person, would be treated as bibtex + + if (is_entity) { + as_bib_text <- paste(c(person$family, person$given), collapse = " ") + # And protect it + as_bib_text <- paste0("{", as_bib_text, "}") + } else { + # Use von Family, Junior, Given + # Protect given + giv <- paste0(person$given, collapse = " ") + giv <- paste0("{", giv, "}") + as_bib_text <- paste0(c(person$family, giv), collapse = ", ") + } + + parsed_person <- create_person_from_txt(as_bib_text) + parsed_comments <- extract_person_comments(person) + + # Add comments + parsed_person <- c(parsed_person, parsed_comments) + + # Validate fields + parsed_person <- validate_cff_person_fields(parsed_person) + parsed_person +} + +create_person_from_txt <- function(as_bib_text) { + # Can extract comments as R + # Locate comment with R format.person default pattern + # 'first last [role] (comment) + # but we protect this if inside brackets + comments_pattern <- "<|>|\\(|\\)|\\[|\\]" + + protected <- gsub(paste0("(", comments_pattern, ")(?![^\\}]*(\\{|$))"), + "0", as_bib_text, + perl = TRUE + ) + + start_comment <- min(unlist(regexpr(comments_pattern, protected))) + + if (start_comment > 0) { + # has comments + person_only <- trimws(substr(as_bib_text, 1, start_comment - 1)) + comment_only <- trimws(substr( + as_bib_text, start_comment, + nchar(as_bib_text) + )) + + # Fake a person object to extract comments + fake_person <- paste0("{Fake} ", comment_only) + parsed_comments <- extract_person_comments(fake_person) + } else { + # Does not + person_only <- as_bib_text + parsed_comments <- list() + } + + + + # Now extract structure for person_only string + # It may be one of: + # A. Given von Family + # B. von Family, Given + # C. von Family, Junior, Given + + # Protect commas on brackets to avoid error counting + protected <- gsub(",(?![^\\}]*(\\{|$))", "@comma@", + person_only, + perl = TRUE + ) + + commas <- as.character(length(grep(",", unlist(strsplit(protected, "|"))))) + + # Assign the corresponding fun + bibtex_name_str <- switch(commas, + # Case A + "0" = bibtex_pers_first_von_last(person_only), + # Case B + "1" = bibtex_pers_von_last_first(person_only), + # Case C + "2" = bibtex_pers_von_last_first_jr(person_only), + # Empty + list(family = paste(person_only, collapse = " ")) + ) + + # Clean + bibtex_name_str <- lapply(bibtex_name_str, function(z) { + if (is.null(z)) { + return(NULL) + } + if (any((is.na(z) | z == ""))) { + return(NULL) + } + + cleaned <- gsub("\\{|\\}", "", z) + clean_str(cleaned) + }) + + # Final parsed person + if (is.null(bibtex_name_str$given)) { + ent <- c(bibtex_name_str$von, bibtex_name_str$family, bibtex_name_str$jr) + ent <- clean_str(paste(ent, collapse = " ")) + parsed_person <- list(name = ent) + } else { + parsed_person <- list( + "family-names" = bibtex_name_str$family, + "given-names" = bibtex_name_str$given, + "name-particle" = bibtex_name_str$von, + "name-suffix" = bibtex_name_str$jr + ) + } + + parsed_person <- parsed_person[!lengths(parsed_person) == 0] + + # Add comments + parsed_person <- c(parsed_person, parsed_comments) + + # Validate fields + parsed_person <- validate_cff_person_fields(parsed_person) + parsed_person +} + +split_txt_persons <- function(person) { + person <- trimws(person) + person <- paste0(person, collapse = " and ") + + # Remove role on [] as it comes from print.person by default + # We don't use it here + person <- gsub("\\[[^()]*\\]", "", person) + + # Protect 'and' on brackets {} + # Lower + protected <- gsub("(and)(?![^\\}]*(\\{|$))", "@nd@", + person, + perl = TRUE + ) + + # upper + protected <- gsub("AND(?![^\\}]*(\\{|$))", "@ND@", + protected, + perl = TRUE + ) + + # Do the same for 'and' in comments "()" as provided by print.person + # Lower + protected <- gsub("(and)(?![^\\)]*(\\(|$))", "@nd@", + protected, + perl = TRUE + ) + + # upper + protected <- gsub("AND(?![^\\)]*(\\(|$))", "@ND@", + protected, + perl = TRUE + ) + + # Do the same for 'and' in "<>". These are email, should never happen + # Lower + protected <- gsub("(and)(?![^>]*(<|$))", "@nd@", + protected, + perl = TRUE + ) + + # upper + protected <- gsub("AND(?![^>]*(<|$))", "@ND@", + protected, + perl = TRUE + ) + + auths <- unlist(strsplit(protected, " and | AND ")) + + # Unprotec + auths_un <- gsub("@nd@", "and", auths) + auths_un <- gsub("@ND@", "AND", auths_un) + + auths_un +} + +extract_person_comments <- function(person) { + # Ensure person type + person <- as.person(person) + + # Extract from comments + parsed_comments <- as.list(person$comment) + names(parsed_comments) <- tolower(names(parsed_comments)) + nms_com <- names(parsed_comments) + comment_as_text <- tolower(clean_str(parsed_comments)) + + # Special case when coerced from text, only can extract orcid and web + if (all( + any(is.na(nms_com), length(nms_com) == 0), + length(comment_as_text > 0) + ) + ) { + split_comments <- unlist(strsplit(comment_as_text, ",| |<|>")) + + # Guess that seems to be a web + url_comment <- split_comments[is_url(split_comments)] + + # guess orcid + orcid <- url_comment[grepl("orcid.org/", url_comment)] + + # Get the first non-orcid url + web <- url_comment[!grepl("orcid.org/", url_comment)][1] + + # Reset comment list + parsed_comments <- list() + + parsed_comments$orcid <- clean_str(orcid) + parsed_comments$website <- clean_str(web) + } + + # Add url to orcid if not present + # Parse leading invalid urls + + if (!is.null(parsed_comments$orcid)) { + orcid <- gsub("^orcid.org/", "", parsed_comments$orcid) + orcid <- gsub("^https://orcid.org/", "", orcid) + orcid <- gsub("^http://orcid.org/", "", orcid) + + parsed_comments$orcid <- paste0("https://orcid.org/", orcid) + } + + # Add website + web <- parsed_comments$website + + if (!is.null(web)) { + parsed_comments$website <- clean_str(web[is_url(web)]) + } + + # Add also email + # Check if several mails (MomTrunc 6.0) + valid_emails <- unlist(lapply(person$email, is_email)) + email <- person$email[valid_emails][1] + + # Final list + fin_list <- c(list(email = NULL), parsed_comments) + fin_list$email <- clean_str(email) + + fin_list +} + + +validate_cff_person_fields <- function(parsed_person) { + # Entity of person + + # Guess entity or person + is_entity <- as.character("name" %in% names(parsed_person)) + + # Keep only valid tags - Would depend on entity or person + definition <- switch(is_entity, + "TRUE" = cff_schema_definitions_entity(), + cff_schema_definitions_person() + ) + + parsed_person <- parsed_person[names(parsed_person) %in% definition] + + parsed_person +} diff --git a/R/cff_create_person.R b/R/cff_create_person.R deleted file mode 100644 index 579cf0f7..00000000 --- a/R/cff_create_person.R +++ /dev/null @@ -1,360 +0,0 @@ -#' Create a person with the corresponding [`cff`][cff-class] structure -#' -#' @description -#' -#' Create a `person` or `entity` as defined by the -#' -#' ```{r, echo=FALSE, results='asis'} -#' -#' cat(paste0(" [Citation File Format schema]", -#' "(https://github.com/citation-file-format/", -#' "citation-file-format/blob/main/schema-guide.md).")) -#' -#' -#' ``` -#' -#' [cff_create_person()] can convert the following objects: -#' - Objects with class `person` as provided by [utils::person()]. -#' - A `character` string with the definition of an author or several authors, -#' using the standard BibTeX notation. See Markey (2007) for a full -#' explanation. -#' -#' @seealso -#' Examples in `vignette("cffr", "cffr")` and [utils::person()]. -#' -#' @export -#' -#' @family coercing -#' -#' @param person It can be either: -#' - A `person` or list of `person` object created with [utils::person()]. -#' - A `character` object representing a person or persons. -#' See **Examples**. -#' @param hint Either `auto`, `person` or `bibtex`. See **Details**. -#' -#' @return -#' A list of persons or entities with class `cff` converted to the -#' ```{r, echo=FALSE, results='asis'} -#' -#' cat(paste0(" [Citation File Format schema]", -#' "(https://github.com/citation-file-format/", -#' "citation-file-format/blob/main/schema-guide.md).")) -#' -#' -#' ``` -#' -#' @details -#' -#' `cff_create_person()` would try to guess what type of object has been -#' provided in `person`. Possible options are: -#' -#' 1. If `person` is a [`person`][utils::person()] object it would be parsed -#' with a specific algorithm. -#' 2. However, if `person` is a `character` there are two options: -#' -#' 1. The `character` be a printed version of [utils::person()]. In this -#' case it may include information of email, ORCID, etc. See -#' [utils::as.person()] and **Details** in that same help page. -#' -#' 2. It can be just a person with BibTeX notation such as -#' `"von Doe, John and Smith, Jane"`. -#' -#' In **case 1** (`person = "person"`) `hint = "auto"` wouldn't affect and -#' internally the algorithm used with `hint = "person"` would be always used. -#' -#' `hint` argument would affect **case 2** (i.e. `person = "character"`). If -#' `hint = "auto"` `cff_create_person()` would try to guess the best -#' alternative, however it may be incorrect, so it is possible to specify the -#' conversion algorithm: -#' - `hint = "person"` would try to convert the `character` to `person` object -#' with [utils::as.person()] and then proceed with the algorithm for -#' `person` objects. -#' - `hint = "bibtex"` would use a special algorithm to create the `cff` -#' person. This option is better for strings like `"von Last, Jr, First"` -#' and produces improved mappings to CFF keys as `name-particle` and -#' `name-suffix`. It is also aware of protected names such as -#' `{John Doe and Jane Smith inc.}`. -#' - -#' -#' @references -#' - Patashnik, Oren. "BIBTEXTING" February 1988. -#' . -#' -#' - Markey, Nicolas. "Tame the BeaST." -#' *The B to X of BibTeX, Version 1.4* (October 2007). -#' . -#' -#' See **Examples** for more information. -#' -#' -#' @examples -#' # Create a person object -#' a_person <- person( -#' given = "First", family = "Author", -#' role = c("aut", "cre"), -#' email = "first.last@example.com", comment = c( -#' ORCID = "0000-0001-8457-4658", -#' affiliation = "An affiliation" -#' ) -#' ) -#' cff_create_person(a_person) -#' -#' # Parse a string -#' a_str <- paste0( -#' "Julio Iglesias ", -#' "()" -#' ) -#' cff_create_person(a_str) -#' -#' # Several persons -#' persons <- c(person("Clark", "Kent"), person("Lois", "Lane")) -#' -#' cff_create_person(persons) -#' -#' # Or you can use BibTeX style if you prefer -#' -#' x <- "Frank Sinatra and Dean Martin and Davis, Jr., Sammy and Joey Bishop" -#' -#' cff_create_person(x) -#' -#' cff_create_person("Herbert von Karajan") -cff_create_person <- function(person, hint = c("auto", "person", "bibtex")) { - hint <- match.arg(hint) - init_hint <- hint - hint <- guess_hint(person, hint) - - verbopt <- getOption("cffr_message_verbosity", "none") - if (all(verbopt == "debug", init_hint == "auto")) { - cli::cli_alert_info( - "In {.fn cff_create_person} using {.arg hint} = {.val {hint}}" - ) - } - - cff_obj <- switch(hint, - "person" = hint_person(person), - hint_bibtex(person) - ) - - cff_obj -} - -guess_hint <- function(person, hint) { - # On length 0 use "person" - if (length(person) == 0) { - return("person") - } - if (hint != "auto") { - return(hint) - } - if (inherits(person, "person")) { - return("person") - } - - # Rest of cases "bibtex" - return("bibtex") -} - -hint_person <- function(person) { - person <- as.person(person) - - if (length(person) == 0) { - return(NULL) - } - - if (length(person) > 1) { - person <- lapply(person, cff_parse_person) - person <- new_cff(person) - return(person) - } - - # Special case for Bioconductor - - if (is_substring(person$given, "Bioconductor")) { - person <- person( - given = paste( - clean_str(person$given), - clean_str(person$family) - ), - email = person$email, - role = person$role, - comment = person$comment - ) - } - - # Special case for R Core Team - if (all( - is_substring(clean_str(person$given), "R Core"), - is_substring(person$family, "Team") - )) { - person <- person( - given = paste( - clean_str(person$given), - clean_str(person$family) - ), - email = person$email, - role = person$role, - comment = person$comment - ) - } - - # Guess if entity of person. - is_entity <- is.null(person$family) || is.null(person$given) - - # Create my list - parsed_person <- list() - - if (is_entity) { - parsed_person$name <- clean_str(c(person$family, person$given)) - } else { - parsed_person$"family-names" <- clean_str(person$family) - parsed_person$"given-names" <- clean_str(person$given) - } - - # Check if several mails (MomTrunc 6.0) - valid_emails <- unlist(lapply(person$email, is_email)) - email <- person$email[valid_emails][1] - parsed_person$email <- clean_str(email) - - # Extract from comments - parsed_comments <- as.list(person$comment) - names(parsed_comments) <- tolower(names(parsed_comments)) - nms_com <- names(parsed_comments) - comment_as_text <- tolower(clean_str(parsed_comments)) - - # Special case when coerced from text, only can extract orcid - if (all( - any(is.na(nms_com), length(nms_com) == 0), - length(comment_as_text > 0) - ) - ) { - # guess orcid - norc <- min(unlist(regexpr("orcid.org/", comment_as_text))) - if (norc < 0) { - parsed_comments <- list() - } else { - orcid <- substr(comment_as_text, norc, nchar(comment_as_text)) - orcid <- clean_str(gsub("<|>", "", orcid)) - parsed_comments <- list(orcid = orcid) - } - } - - - # Extract for comments only what is not already there - parsed_comments <- parsed_comments[setdiff( - names(parsed_comments), - names(parsed_person) - )] - - - # Add url to orcid if not present - # Parse leading invalid urls - - if (!is.null(parsed_comments$orcid)) { - orcid <- gsub("^orcid.org/", "", parsed_comments$orcid) - orcid <- gsub("^https://orcid.org/", "", orcid) - orcid <- gsub("^http://orcid.org/", "", orcid) - - parsed_comments$orcid <- paste0("https://orcid.org/", orcid) - } - - # Add website - web <- parsed_comments$website - - if (!is.null(web)) { - parsed_comments$website <- clean_str(web[is_url(web)]) - } - - # Add comments - parsed_person <- c(parsed_person, parsed_comments) - - # Keep only valid tags - Would depend on entity or person - definition <- if (is_entity) { - cff_schema_definitions_entity() - } else { - cff_schema_definitions_person() - } - parsed_person <- parsed_person[names(parsed_person) %in% definition] - - parsed_person <- new_cff(parsed_person) - # Ensure it is always a list - if (!is.null(names(parsed_person))) { - parsed_person <- list(parsed_person) - class(parsed_person) <- c("cff", "list") - } - parsed_person -} - -hint_bibtex <- function(person) { - person <- trimws(person) - person <- unname(person) - - # Remove role on [] as it comes from print.person by default - person <- gsub("\\[[^()]*\\]", "", person) - # Protect and on brackets {} - # Lower - protected <- gsub("(and)(?![^\\}]*(\\{|$))", "@nd@", - person, - perl = TRUE - ) - - # upper - protected <- gsub("AND(?![^\\}]*(\\{|$))", "@ND@", - protected, - perl = TRUE - ) - - # Do the same for and in comment "()" print.person by default - # Lower - protected <- gsub("(and)(?![^\\)]*(\\(|$))", "@nd@", - protected, - perl = TRUE - ) - - # upper - protected <- gsub("AND(?![^\\)]*(\\(|$))", "@ND@", - protected, - perl = TRUE - ) - - auths <- unlist(strsplit(protected, " and | AND ")) - - # Unprotec - auths_un <- gsub("@nd@", "and", auths) - auths_un <- gsub("@ND@", "AND", auths_un) - - - bibtex_auths <- lapply(auths_un, as_person_bibtex_comments) - end <- lapply(bibtex_auths, function(x) { - if (is.null(x$given)) { - ent <- c(x$von, x$family, x$jr) - ent <- clean_str(paste(ent, collapse = " ")) - l <- list(name = ent) - - # Append comments - coms <- x[setdiff(names(x), c("family", "given", "von", "jr"))] - l <- c(l, coms) - l <- new_cff(l) - l - } else { - l <- list( - "family-names" = x$family, - "given-names" = x$given, - "name-particle" = x$von, - "name-suffix" = x$jr - ) - coms <- x[setdiff(names(x), c("family", "given", "von", "jr"))] - l <- c(l, coms) - l <- new_cff(l) - l - } - }) - - # Ensure it is always a list - if (!is.null(names(end))) { - end <- list(end) - class(parsed_person) <- c("cff", "list") - } - - end <- new_cff(end) -} diff --git a/R/cff_read_biblines.R b/R/cff_read_bib_text.R similarity index 93% rename from R/cff_read_biblines.R rename to R/cff_read_bib_text.R index 25735bd3..b33173dc 100644 --- a/R/cff_read_biblines.R +++ b/R/cff_read_bib_text.R @@ -11,7 +11,7 @@ #' #' @export #' -#' @param x A vector of characters with the full BibTeX string. +#' @param x A vector of `character` objects with the full BibTeX string. #' @param encoding Encoding to be assumed for `x`, see [readLines()]. #' @param ... Arguments passed on to [cff_read_bib()]. #' @inheritDotParams cff_read_bib -path @@ -50,9 +50,9 @@ #' ) #' #' -#' cff_read_biblines(x) +#' cff_create_bib_text(x) #' } -cff_read_biblines <- function(x, encoding = "UTF-8", ...) { +cff_create_bib_text <- function(x, encoding = "UTF-8", ...) { # Validations if (!inherits(x, "character")) { cli::cli_abort( diff --git a/R/cff_write_misc.R b/R/cff_write_misc.R index c00ebe6e..f59620ae 100644 --- a/R/cff_write_misc.R +++ b/R/cff_write_misc.R @@ -13,7 +13,7 @@ #' @param append Whether to append the entries to an existing file or not. #' @param verbose Display informative messages #' @param ascii Whether to write the entries using ASCII characters only or not. -#' @inheritDotParams cff_to_bibentry +#' @inheritDotParams as_bibentry #' #' @references #' @@ -31,7 +31,7 @@ #' @details #' #' When `x` is a `cff` object it would be converted to `bibentry` using -#' [cff_to_bibentry()]. +#' [as_bibentry()]. #' #' For security reasons, if the file already exists the function would create #' a backup copy on the same directory. @@ -62,7 +62,7 @@ cff_write_bib <- function(x, file = tempfile(fileext = ".bib"), append = FALSE, verbose = TRUE, ascii = FALSE, ...) { if (inherits(x, "cff")) { - x <- cff_to_bibentry(x, ...) + x <- as_bibentry(x, ...) } if (!inherits(x, "bibentry")) { @@ -111,7 +111,7 @@ cff_write_bib <- function(x, file = tempfile(fileext = ".bib"), append = FALSE, cff_write_citation <- function(x, file = tempfile("CITATION_"), append = FALSE, verbose = TRUE, ...) { if (inherits(x, "cff")) { - x <- cff_to_bibentry(x, ...) + x <- as_bibentry(x, ...) } if (!inherits(x, "bibentry")) { diff --git a/R/deprecated.R b/R/deprecated.R index 8a46da0f..a57c929e 100644 --- a/R/deprecated.R +++ b/R/deprecated.R @@ -1,15 +1,15 @@ #' Previous API: Create BibTeX entries from several sources #' #' @description -#' `r lifecycle::badge('superseded')` Please use [cff_to_bibentry()] instead. +#' `r lifecycle::badge('superseded')` Please use [as_bibentry()] instead. #' #' @rdname deprecated_cff_to_bib -#' @inheritParams cff_to_bibentry +#' @inheritParams as_bibentry #' @export #' @keywords internal #' @family deprecated #' -#' @return See [cff_to_bibentry()]. +#' @return See [as_bibentry()]. #' @examples #' \donttest{ #' # From a cff object @@ -18,17 +18,17 @@ #' cff_object #' #' # bibentry object -#' bib <- cff_to_bibentry(cff_object) +#' bib <- as_bibentry(cff_object) #' } cff_extract_to_bibtex <- function(x, what = c("preferred", "references", "all")) { if (requireNamespace("lifecycle", quietly = TRUE)) { lifecycle::deprecate_soft( "0.5.0", "cff_extract_to_bibtex()", - details = "Function renamed, use `cff_to_bibentry()` instead." + details = "Function renamed, use `as_bibentry()` instead." ) } - cff_to_bibentry(x, what) + as_bibentry(x, what) } #' @rdname deprecated_cff_to_bib @@ -39,10 +39,10 @@ cff_to_bibtex <- function(x, if (requireNamespace("lifecycle", quietly = TRUE)) { lifecycle::deprecate_soft( "1.0.0", "cff_extract_to_bibtex()", - details = "Function renamed, use `cff_to_bibentry()` instead." + details = "Function renamed, use `as_bibentry()` instead." ) } - cff_to_bibentry(x, what) + as_bibentry(x, what) } #' Previous API: Create a [`cff`][cff-class] object from BibTeX entries @@ -50,7 +50,7 @@ cff_to_bibtex <- function(x, #' @description #' #' `r lifecycle::badge('superseded')` Please use either [cff_read_bib()] or -#' [cff_read_biblines()] instead. +#' [cff_create_bib_text()] instead. #' #' @rdname deprecated_cff_from_bib #' @export @@ -67,7 +67,7 @@ cff_to_bibtex <- function(x, #' #' @return #' -#' See [cff_read_bib()] from reading `*.bib` files and [cff_read_biblines()] +#' See [cff_read_bib()] from reading `*.bib` files and [cff_create_bib_text()] #' for reading a `character` object representing a BibTeX entry. #' #' @@ -93,7 +93,7 @@ cff_to_bibtex <- function(x, #' }" #' ) #' -#' cff_read_biblines(x) +#' cff_create_bib_text(x) #' #' # From a file #' @@ -114,10 +114,10 @@ cff_from_bibtex <- function(x, encoding = "UTF-8", ...) { if (requireNamespace("lifecycle", quietly = TRUE)) { lifecycle::deprecate_soft( - "1.0.0", "cff_from_bibtex()", "cff_read_biblines()" + "1.0.0", "cff_from_bibtex()", "cff_create_bib_text()" ) } - cff_read_biblines(x, encoding = encoding, ...) + cff_create_bib_text(x, encoding = encoding, ...) } diff --git a/R/utils-methods.R b/R/utils-methods.R index 11c3b9ca..3f5591ce 100644 --- a/R/utils-methods.R +++ b/R/utils-methods.R @@ -19,11 +19,12 @@ make_r_person <- function(x) { given <- clean_str(x$`given-names`) family <- clean_str(c(fam1, fam2)) + role <- clean_str(x$role) # Make comments x_comments <- x[!names(x) %in% c( - "family-names", "given-names", - "name-particle", "name-suffix", "email" + "family-names", "given-names", "name", + "name-particle", "name-suffix", "email", "role" )] x_comments <- lapply(x_comments, clean_str) @@ -38,6 +39,7 @@ make_r_person <- function(x) { pers_list <- list( given = given, family = family, + role = role, email = clean_str(x$email), comment = x_comments ) diff --git a/R/utils-persons.R b/R/utils-persons.R index 5b967749..441f120e 100644 --- a/R/utils-persons.R +++ b/R/utils-persons.R @@ -260,74 +260,6 @@ bibtex_pers_first_von_last <- function(x) { return(end_list) } - -as_person_bibtex_comments <- function(x) { - # Can extract comments as R - comments_list <- NULL - locate_comment <- regexpr("<|\\(", x) - start_comment <- min(unlist(locate_comment)) - - if (start_comment > 0) { - # Has comments - the_comment <- trimws(substr(x, start_comment, nchar(x))) - - # Let R parse comments - res <- hint_person(paste0("fakename ", the_comment))[[1]] - comments_list <- res[names(res) != "name"] - - x <- trimws(substr(x, 0, start_comment - 1)) - } - - # Identify the pattern - # It may be one of: - # A. Given von Family - # B. von Family, Given - # C. von Family, Junior, Given - - # Protect commas on brackets to avoid error counting - protected <- gsub(",(?![^\\}]*(\\{|$))", "@comma@", - x, - perl = TRUE - ) - - commas <- length(grep(",", unlist(strsplit(protected, "|")))) - - if (commas == 0) { - # Case A - end_list <- bibtex_pers_first_von_last(x) - } else if (commas == 1) { - # Case B - end_list <- bibtex_pers_von_last_first(x) - } else if (commas == 2) { - # Case C - end_list <- bibtex_pers_von_last_first_jr(x) - } else { - # Not considered by BibTeX. everything to family - end_list <- list(family = paste(x, collapse = " ")) - } - - - # Clean - end_list <- lapply(end_list, function(z) { - if (is.null(z)) { - return(NULL) - } - if (any((is.na(z) | z == ""))) { - return(NULL) - } - - gsub("\\{|\\}", "", z) - }) - - if (!is.null(comments_list)) { - end_list <- c(end_list, comments_list) - } - end_list <- lapply(end_list, clean_str) - - return(end_list) -} - - as_person_bibtex <- function(x) { # Identify the pattern # It may be one of: diff --git a/README.md b/README.md index 37dcac20..311f541f 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ file and the `CITATION` file (if present) of your package. Note that **cffr** works best if your package pass `R CMD check/devtools::check()`. -As per 2024-02-28 there are at least 294 repos on GitHub using **cffr**. +As per 2024-02-29 there are at least 312 repos on GitHub using **cffr**. [Check them out here](https://github.com/search?q=cffr%20path%3A**%2FCITATION.cff&type=code). diff --git a/codemeta.json b/codemeta.json index ce17ed10..20b74cde 100644 --- a/codemeta.json +++ b/codemeta.json @@ -129,7 +129,7 @@ "@type": "SoftwareApplication", "identifier": "R", "name": "R", - "version": ">= 3.6.0" + "version": ">= 4.0.0" }, "2": { "@type": "SoftwareApplication", @@ -200,7 +200,7 @@ }, "isPartOf": "https://ropensci.org", "keywords": ["attribution", "citation", "credit", "citation-files", "cff", "metadata", "r", "r-package", "citation-file-format", "rstats", "ropensci", "cran"], - "fileSize": "929.961KB", + "fileSize": "947.076KB", "citation": [ { "@type": "ScholarlyArticle", diff --git a/man/cff_to_bibentry.Rd b/man/as_bibentry.Rd similarity index 71% rename from man/cff_to_bibentry.Rd rename to man/as_bibentry.Rd index 3aa6087e..25f70740 100644 --- a/man/cff_to_bibentry.Rd +++ b/man/as_bibentry.Rd @@ -1,11 +1,11 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/cff_to_bibentry.R, R/cff-methods.R -\name{cff_to_bibentry} -\alias{cff_to_bibentry} +% Please edit documentation in R/as_bibentry.R, R/cff-methods.R +\name{as_bibentry} +\alias{as_bibentry} \alias{toBibtex.cff} -\title{Create BibTeX entries from several sources} +\title{Create \code{bibentry} objects from several sources} \usage{ -cff_to_bibentry(x, what = c("preferred", "references", "all")) +as_bibentry(x, what = c("preferred", "references", "all")) \method{toBibtex}{cff}(object, ..., what = c("preferred", "references", "all")) } @@ -35,21 +35,22 @@ both the preferred citation info and the references. \item{...}{Arguments passed to \code{\link[utils:toLatex]{utils::toBibtex()}}.} } \value{ -A \code{bibentry} object or a list of \code{bibentry} objects. This could -be parsed to BibTeX using \code{\link[=toBibtex]{toBibtex()}} +\code{as_bibentry()} returns s \code{bibentry} object (or a list of \code{bibentry} +objects). -\code{toBibtex.cff()} returns a \code{Bibtex} object. +\code{toBibtex.cff()} returns a \code{Bibtex} object that can be printed as BibTeX +markup. } \description{ -This function creates \code{\link[=bibentry]{bibentry()}} objects -from different metadata sources (\code{cff} objects, \code{DESCRIPTION} files, etc.). +This function creates \code{bibentry} objects (see \code{\link[utils:bibentry]{utils::bibentry()}}) from +different metadata sources (\code{cff} objects, \code{DESCRIPTION} files, etc.). Note +that a \strong{R} \code{bibentry} object is the representation of a BibTeX entry, +see \strong{Examples} + The function tries to map the information of the source \code{x} into a \code{cff} object and performs a mapping of the metadata to BibTeX, according to \code{vignette("bibtex_cff", "cffr")}. -Note that a \strong{R} \code{\link[=bibentry]{bibentry()}} object is the representation of a BibTeX -entry, see \strong{Examples}. - Additionally, it is also provided a method for \code{\link[=toBibtex]{toBibtex()}}, that can convert \code{\link[=cff-class]{cff}} objects to \code{Bibtex} objects as provided by \code{\link[utils:toLatex]{utils::toBibtex()}}. These objects are character vectors with @@ -63,7 +64,7 @@ cff_object <- cff() cff_object # bibentry object -bib <- cff_to_bibentry(cff_object) +bib <- as_bibentry(cff_object) class(bib) @@ -80,13 +81,13 @@ toBibtex(cff_object) # From a CITATION.cff file with options path <- system.file("examples/CITATION_complete.cff", package = "cffr") -cff_file <- cff_to_bibentry(path, what = "all") +cff_file <- as_bibentry(path, what = "all") toBibtex(cff_file) # For an installed package -installed_package <- cff_to_bibentry("jsonvalidate") +installed_package <- as_bibentry("jsonvalidate") toBibtex(installed_package) @@ -94,7 +95,7 @@ toBibtex(installed_package) # Use a DESCRIPTION file path2 <- system.file("examples/DESCRIPTION_gitlab", package = "cffr") -desc_file <- cff_to_bibentry(path2) +desc_file <- as_bibentry(path2) toBibtex(desc_file) } @@ -112,13 +113,22 @@ toBibtex(desc_file) } } \seealso{ +\code{\link[utils:bibentry]{utils::bibentry()}} + \code{\link[utils:toLatex]{utils::toBibtex()}} Other functions for working with BibTeX format: +\code{\link{cff_create_bib_text}()}, \code{\link{cff_read}()}, -\code{\link{cff_read_biblines}()}, \code{\link{cff_write_bib}()}, \code{\link{encoded_utf_to_latex}()} + +Other functions for converting between \strong{R} classes: +\code{\link{cff_create_person}()} + +Other S3 Methods for \code{cff}: +\code{\link{cff_create_person}()} } \concept{bibtex} +\concept{coercing} \concept{s3method} diff --git a/man/cff-class.Rd b/man/cff-class.Rd index bde2e41a..87a02b5b 100644 --- a/man/cff-class.Rd +++ b/man/cff-class.Rd @@ -173,8 +173,8 @@ the_cff$authors #> location: The team garage as.person(the_cff$authors) -#> [1] "One Truly van der Real Person IV (Citey, Excellent University, Niceplace, Arcadia, 22 Acacia Avenue, Citationburgh, Renfrewshire, C13 7X7, GB, , +44(0)141-323 4567, +44(0)141-323 45678, https://www.entity-project-team.io)" -#> [2] "Entity Project Team Conference entity (Entity Project Team Conference entity, 22 Acacia Avenue, Citationburgh, Renfrewshire, C13 7X7, GB, , +44(0)141-323 4567, +44(0)141-323 45678, https://www.entity-project-team.io, 2017-01-01, 2017-01-31, The team garage)" +#> [1] "One Truly van der Real Person IV (Citey, Excellent University, Niceplace, Arcadia, 22 Acacia Avenue, Citationburgh, Renfrewshire, C13 7X7, GB, , +44(0)141-323 4567, +44(0)141-323 45678, https://www.entity-project-team.io)" +#> [2] "Entity Project Team Conference entity (22 Acacia Avenue, Citationburgh, Renfrewshire, C13 7X7, GB, , +44(0)141-323 4567, +44(0)141-323 45678, https://www.entity-project-team.io, 2017-01-01, 2017-01-31, The team garage)" }\if{html}{\out{
}} } } diff --git a/man/cff_read_biblines.Rd b/man/cff_create_bib_text.Rd similarity index 86% rename from man/cff_read_biblines.Rd rename to man/cff_create_bib_text.Rd index 77ecb59b..69ec913f 100644 --- a/man/cff_read_biblines.Rd +++ b/man/cff_create_bib_text.Rd @@ -1,13 +1,13 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/cff_read_biblines.R -\name{cff_read_biblines} -\alias{cff_read_biblines} +% Please edit documentation in R/cff_read_bib_text.R +\name{cff_create_bib_text} +\alias{cff_create_bib_text} \title{Create a \code{\link[=cff-class]{cff}} object from BibTeX lines} \usage{ -cff_read_biblines(x, encoding = "UTF-8", ...) +cff_create_bib_text(x, encoding = "UTF-8", ...) } \arguments{ -\item{x}{A vector of characters with the full BibTeX string.} +\item{x}{A vector of \code{character} objects with the full BibTeX string.} \item{encoding}{Encoding to be assumed for \code{x}, see \code{\link[=readLines]{readLines()}}.} @@ -54,15 +54,15 @@ if (requireNamespace("bibtex", quietly = TRUE)) { ) - cff_read_biblines(x) + cff_create_bib_text(x) } } \seealso{ \code{\link[=cff_read_bib]{cff_read_bib()}} for reading \verb{*.bib} files. Other functions for working with BibTeX format: +\code{\link{as_bibentry}()}, \code{\link{cff_read}()}, -\code{\link{cff_to_bibentry}()}, \code{\link{cff_write_bib}()}, \code{\link{encoded_utf_to_latex}()} diff --git a/man/cff_create_person.Rd b/man/cff_create_person.Rd index 5f973e29..68b55194 100644 --- a/man/cff_create_person.Rd +++ b/man/cff_create_person.Rd @@ -1,23 +1,29 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/cff_create_person.R +% Please edit documentation in R/cff_create_cff_person.R, R/cff-methods.R \name{cff_create_person} \alias{cff_create_person} +\alias{as.person.cff} \title{Create a person with the corresponding \code{\link[=cff-class]{cff}} structure} \usage{ -cff_create_person(person, hint = c("auto", "person", "bibtex")) +cff_create_person(person) + +\method{as.person}{cff}(x) } \arguments{ \item{person}{It can be either: \itemize{ \item A \code{person} or list of \code{person} object created with \code{\link[utils:person]{utils::person()}}. -\item A \code{character} object representing a person or persons. +\item A \code{character} object or vector representing a person or persons. See \strong{Examples}. }} -\item{hint}{Either \code{auto}, \code{person} or \code{bibtex}. See \strong{Details}.} +\item{x}{\code{cff} object representing a person or entity.} } \value{ -A list of persons or entities with class \code{cff} converted to the \href{https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md}{Citation File Format schema}. +\code{cff_create_person()} returns A list of persons or entities with class \code{cff} +converted to the \href{https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md}{Citation File Format schema}. + +\code{as.person.cff()} returns a \code{person} object. } \description{ Create a \code{person} or \code{entity} as defined by the @@ -30,40 +36,43 @@ Create a \code{person} or \code{entity} as defined by the using the standard BibTeX notation. See Markey (2007) for a full explanation. } + +Additionally, it is also provided a method for \code{\link[=as.person]{as.person()}}, that can +convert \code{\link[=cff-class]{cff}} objects to \code{person} objects as +provided by \code{\link[utils:person]{utils::person()}}. + +This method only works with CFF persons, not with full \code{cff} objects. } \details{ -\code{cff_create_person()} would try to guess what type of object has been -provided in \code{person}. Possible options are: -\enumerate{ -\item If \code{person} is a \code{\link[utils:person]{person}} object it would be parsed -with a specific algorithm. -\item However, if \code{person} is a \code{character} there are two options: -\enumerate{ -\item The \code{character} be a printed version of \code{\link[utils:person]{utils::person()}}. In this -case it may include information of email, ORCID, etc. See -\code{\link[utils:person]{utils::as.person()}} and \strong{Details} in that same help page. -\item It can be just a person with BibTeX notation such as -\code{"von Doe, John and Smith, Jane"}. -} +\code{cff_create_person()} uses a custom algorithm that tries to break a name +as explained in Section 11 of "Tame the BeaST" (Markey, 2007): +\itemize{ +\item \verb{First von Last} +\item \verb{von Last, First} +\item \verb{von Last, Jr, First} } -In \strong{case 1} (\code{person = "person"}) \code{hint = "auto"} wouldn't affect and -internally the algorithm used with \code{hint = "person"} would be always used. +\code{First} is mapped to the CFF field \code{given-names}, \code{von} to \code{name-particle}, +\code{Last} to \code{family-names} and \code{Jr} to \code{name-suffix}. -\code{hint} argument would affect \strong{case 2} (i.e. \code{person = "character"}). If -\code{hint = "auto"} \code{cff_create_person()} would try to guess the best -alternative, however it may be incorrect, so it is possible to specify the -conversion algorithm: -\itemize{ -\item \code{hint = "person"} would try to convert the \code{character} to \code{person} object -with \code{\link[utils:person]{utils::as.person()}} and then proceed with the algorithm for -\code{person} objects. -\item \code{hint = "bibtex"} would use a special algorithm to create the \code{cff} -person. This option is better for strings like \code{"von Last, Jr, First"} -and produces improved mappings to CFF keys as \code{name-particle} and -\code{name-suffix}. It is also aware of protected names such as -\verb{\{John Doe and Jane Smith inc.\}}. -} +In the case of entities, the whole \code{character} would be mapped to \code{name}. +It is a good practice to "protect" entity's names with \code{{}}: + +\if{html}{\out{
}}\preformatted{# Don't do +entity <- "Elephant and Castle" +cff_create_person(entity) +#> - name: Elephant +#> - name: Castle + +# Do +entity_protect <- "\{Elephant and Castle\}" +cff_create_person(entity_protect) +#> - name: Elephant and Castle +}\if{html}{\out{
}} + +\code{cff_create_person()} would try to add as many information as possible. On +\code{character} string coming from \code{\link[utils:person]{format(person())}} the +email and the ORCID would be gathered as well. } \examples{ # Create a person object @@ -75,7 +84,15 @@ a_person <- person( affiliation = "An affiliation" ) ) -cff_create_person(a_person) + +a_person + +cff_person <- cff_create_person(a_person) + +cff_person + +# Back to person object with S3 Method +as.person(cff_person) # Parse a string a_str <- paste0( @@ -101,7 +118,7 @@ cff_create_person("Herbert von Karajan") \itemize{ \item Patashnik, Oren. "BIBTEXTING" February 1988. \url{https://osl.ugr.es/CTAN/biblio/bibtex/base/btxdoc.pdf}. -\item Markey, Nicolas. "Tame the BeaST." +\item Markey, Nicolas. "Tame the BeaST" \emph{The B to X of BibTeX, Version 1.4} (October 2007). \url{https://osl.ugr.es/CTAN/info/bibtex/tamethebeast/ttb_en.pdf}. } @@ -110,5 +127,14 @@ See \strong{Examples} for more information. } \seealso{ Examples in \code{vignette("cffr", "cffr")} and \code{\link[utils:person]{utils::person()}}. + +\code{\link[utils:person]{utils::person()}} + +Other functions for converting between \strong{R} classes: +\code{\link{as_bibentry}()} + +Other S3 Methods for \code{cff}: +\code{\link{as_bibentry}()} } \concept{coercing} +\concept{s3method} diff --git a/man/cff_read.Rd b/man/cff_read.Rd index 98f6efc0..1296498e 100644 --- a/man/cff_read.Rd +++ b/man/cff_read.Rd @@ -134,11 +134,11 @@ The underlying functions used for reading external files: } Other functions for reading external files: -\code{\link{cff_read_biblines}()} +\code{\link{cff_create_bib_text}()} Other functions for working with BibTeX format: -\code{\link{cff_read_biblines}()}, -\code{\link{cff_to_bibentry}()}, +\code{\link{as_bibentry}()}, +\code{\link{cff_create_bib_text}()}, \code{\link{cff_write_bib}()}, \code{\link{encoded_utf_to_latex}()} } diff --git a/man/cff_write_misc.Rd b/man/cff_write_misc.Rd index 5380a916..adddab4e 100644 --- a/man/cff_write_misc.Rd +++ b/man/cff_write_misc.Rd @@ -35,7 +35,7 @@ lines to be written.} \item{ascii}{Whether to write the entries using ASCII characters only or not.} \item{...}{ - Arguments passed on to \code{\link[=cff_to_bibentry]{cff_to_bibentry}} + Arguments passed on to \code{\link[=as_bibentry]{as_bibentry}} \describe{ \item{\code{what}}{Fields to extract. The value could be: \itemize{ @@ -60,7 +60,7 @@ Section 1.9 CITATION files of \emph{Writing R Extensions} (R Core Team 2023). } \details{ When \code{x} is a \code{cff} object it would be converted to \code{bibentry} using -\code{\link[=cff_to_bibentry]{cff_to_bibentry()}}. +\code{\link[=as_bibentry]{as_bibentry()}}. For security reasons, if the file already exists the function would create a backup copy on the same directory. @@ -113,9 +113,9 @@ following packages: } Other functions for working with BibTeX format: +\code{\link{as_bibentry}()}, +\code{\link{cff_create_bib_text}()}, \code{\link{cff_read}()}, -\code{\link{cff_read_biblines}()}, -\code{\link{cff_to_bibentry}()}, \code{\link{encoded_utf_to_latex}()} Other functions for creating external files: diff --git a/man/chunks/cff_create_person.Rmd b/man/chunks/cff_create_person.Rmd new file mode 100644 index 00000000..269facc2 --- /dev/null +++ b/man/chunks/cff_create_person.Rmd @@ -0,0 +1,20 @@ +```{r include=FALSE} +library(cffr) +initopt <- getOption("cffr_message_verbosity", NULL) +options("cffr_message_verbosity" = NULL) +``` + +```{r} +# Don't do +entity <- "Elephant and Castle" +cff_create_person(entity) + +# Do +entity_protect <- "{Elephant and Castle}" +cff_create_person(entity_protect) +``` + +```{r include=FALSE} +# Restore +options("cffr_message_verbosity" = initopt) +``` diff --git a/man/chunks/cffclass.Rmd b/man/chunks/cffclass.Rmd index e1dda711..6edf794b 100644 --- a/man/chunks/cffclass.Rmd +++ b/man/chunks/cffclass.Rmd @@ -10,7 +10,6 @@ additional methods, most notably [print()]. library(cffr) ``` - ```{r} a_named_list <- list( first = "I", second = "am", third = "a", fourth = "list", diff --git a/man/deprecated_cff_from_bib.Rd b/man/deprecated_cff_from_bib.Rd index 99882c96..aeb35186 100644 --- a/man/deprecated_cff_from_bib.Rd +++ b/man/deprecated_cff_from_bib.Rd @@ -19,12 +19,12 @@ cff_from_bibtex(x, encoding = "UTF-8", ...) \item{...}{Other arguments passed to \code{\link[bibtex:read.bib]{bibtex::read.bib()}}.} } \value{ -See \code{\link[=cff_read_bib]{cff_read_bib()}} from reading \verb{*.bib} files and \code{\link[=cff_read_biblines]{cff_read_biblines()}} +See \code{\link[=cff_read_bib]{cff_read_bib()}} from reading \verb{*.bib} files and \code{\link[=cff_create_bib_text]{cff_create_bib_text()}} for reading a \code{character} object representing a BibTeX entry. } \description{ \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#superseded}{\figure{lifecycle-superseded.svg}{options: alt='[Superseded]'}}}{\strong{[Superseded]}} Please use either \code{\link[=cff_read_bib]{cff_read_bib()}} or -\code{\link[=cff_read_biblines]{cff_read_biblines()}} instead. +\code{\link[=cff_create_bib_text]{cff_create_bib_text()}} instead. } \examples{ if (requireNamespace("bibtex", quietly = TRUE)) { @@ -47,7 +47,7 @@ if (requireNamespace("bibtex", quietly = TRUE)) { }" ) - cff_read_biblines(x) + cff_create_bib_text(x) # From a file diff --git a/man/deprecated_cff_to_bib.Rd b/man/deprecated_cff_to_bib.Rd index 8eaec833..1a3983d1 100644 --- a/man/deprecated_cff_to_bib.Rd +++ b/man/deprecated_cff_to_bib.Rd @@ -31,10 +31,10 @@ both the preferred citation info and the references. }} } \value{ -See \code{\link[=cff_to_bibentry]{cff_to_bibentry()}}. +See \code{\link[=as_bibentry]{as_bibentry()}}. } \description{ -\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#superseded}{\figure{lifecycle-superseded.svg}{options: alt='[Superseded]'}}}{\strong{[Superseded]}} Please use \code{\link[=cff_to_bibentry]{cff_to_bibentry()}} instead. +\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#superseded}{\figure{lifecycle-superseded.svg}{options: alt='[Superseded]'}}}{\strong{[Superseded]}} Please use \code{\link[=as_bibentry]{as_bibentry()}} instead. } \examples{ \donttest{ @@ -44,7 +44,7 @@ cff_object <- cff() cff_object # bibentry object -bib <- cff_to_bibentry(cff_object) +bib <- as_bibentry(cff_object) } } \seealso{ diff --git a/man/encoded_utf_to_latex.Rd b/man/encoded_utf_to_latex.Rd index a20328ab..d0dd163d 100644 --- a/man/encoded_utf_to_latex.Rd +++ b/man/encoded_utf_to_latex.Rd @@ -44,9 +44,9 @@ ascii_table \code{\link[tools:encoded]{tools::encoded_text_to_latex()}} Other functions for working with BibTeX format: +\code{\link{as_bibentry}()}, +\code{\link{cff_create_bib_text}()}, \code{\link{cff_read}()}, -\code{\link{cff_read_biblines}()}, -\code{\link{cff_to_bibentry}()}, \code{\link{cff_write_bib}()} } \concept{bibtex} diff --git a/tests/testthat/_snaps/cff_to_bibentry.md b/tests/testthat/_snaps/as_bibentry.md similarity index 99% rename from tests/testthat/_snaps/cff_to_bibentry.md rename to tests/testthat/_snaps/as_bibentry.md index 4117bcb7..a5fb5d60 100644 --- a/tests/testthat/_snaps/cff_to_bibentry.md +++ b/tests/testthat/_snaps/as_bibentry.md @@ -625,7 +625,7 @@ # Corrupt entry Code - n <- cff_to_bibentry(x) + n <- as_bibentry(x) Message x Can't convert to `bibentry()`: i A bibentry of bibtype 'Article' has to specify the fields: journal, year diff --git a/tests/testthat/_snaps/bibtex2cff.md b/tests/testthat/_snaps/bibtex2cff.md deleted file mode 100644 index f98d5109..00000000 --- a/tests/testthat/_snaps/bibtex2cff.md +++ /dev/null @@ -1,483 +0,0 @@ -# Article - - Code - bibparsed - Output - type: article - title: Literate Programming - authors: - - name: R Core Team - journal: The Computer Journal - year: '1984' - volume: '27' - issue: '2' - month: '1' - notes: Example modified for testing purposes - start: '97' - end: '111' - -# Book - - Code - bibparsed - Output - type: book - title: The LaTeX Companion - authors: - - family-names: Mittelbach - given-names: Frank - - family-names: Gossens - given-names: Michel - - family-names: Braams - given-names: Johannes - - family-names: Carlisle - given-names: David - - family-names: Rowley - given-names: Chris - editors: - - name: Barnes and Noble - publisher: - name: Addison-Wesley Professional - address: Santa Monica - year: '2004' - volume: '3' - issue: '7' - collection-title: The LateX Books - collection-type: book - edition: Fourth - month: '8' - notes: Example modified for testing purposes - keywords: - - Two - - keyword - -# Booklet - - Code - bibparsed - Output - type: pamphlet - title: Java Booklet - authors: - - family-names: Mustermann - given-names: Max - medium: Internet - location: - name: Stuttgart - month: '2' - year: '2016' - notes: Example modified from Jabref - -# Conference - - Code - bibparsed - Output - type: conference-paper - title: On Notions of Information Transfer in VLSI Circuits - authors: - - family-names: Oaho - given-names: Alfred V. - - family-names: Ullman - given-names: Jeffrey D. - - family-names: Yannakakis - given-names: Mihalis - collection-title: Proc. Fifteenth Annual ACM STOC - collection-type: conference - year: '1983' - editors: - - family-names: Oz - given-names: Wizard V. - - family-names: Yannakakis - given-names: Mihalis - volume: '41' - issue: '17' - institution: - name: ACM - publisher: - name: Academic Press - notes: Example modified for testing purposes - start: '133' - end: '139' - conference: - name: Proc. Fifteenth Annual ACM STOC - address: Boston - -# InBook - - Code - bibparsed - Output - type: book - title: A Framework for Freeness Analysis - authors: - - family-names: King - given-names: A. - editors: - - family-names: Tick - given-names: E - - family-names: Succi - given-names: G - section: 7, 14 - publisher: - name: Kluwer Academic Publishers - address: Dordrecht - year: '1994' - volume: '27' - issue: '2' - collection-title: Implementations of Logic Programming Systems - collection-type: book - edition: Second - month: '1' - notes: Example modified for testing purposes - start: '137' - end: '149' - -# InCollection - - Code - bibparsed - Output - type: generic - title: Knowledge-Based Methods for WSD - authors: - - family-names: Mihalcea - given-names: Rada - collection-title: 'Word Sense Disambiguation: Algorithms and Applications' - collection-type: collection - publisher: - name: Springer - address: 107--132 - year: '2006' - editors: - - family-names: Agirre - given-names: Eneko - - family-names: Edmonds - given-names: Philip - volume: '23' - issue: '3' - section: '1,2,3' - edition: Third - month: '8' - notes: A note - start: '24' - end: '57' - -# InProceedings - - Code - bibparsed - Output - type: conference-paper - title: On Notions of Information Transfer in VLSI Circuits - authors: - - family-names: Oaho - given-names: Alfred V. - - family-names: Ullman - given-names: Jeffrey D. - - family-names: Yannakakis - given-names: Mihalis - collection-title: Proc. Fifteenth Annual ACM STOC - collection-type: proceedings - year: '1983' - editors: - - family-names: Oz - given-names: Wizard V. - - family-names: Yannakakis - given-names: Mihalis - volume: '41' - issue: '17' - institution: - name: ACM - publisher: - name: Academic Press - notes: Example modified for testing purposes - start: '133' - end: '139' - conference: - name: Proc. Fifteenth Annual ACM STOC - address: Boston - -# Manual - - Code - bibparsed - Output - type: manual - title: A Language and Environment for Statistical Computing - authors: - - name: R Core Team - institution: - name: R Foundation for Statistical Computing - address: Vienna, Austria - edition: Fourth - month: '8' - year: '2021' - notes: Example modified for testing purposes - -# MastersThesis - - Code - bibparsed - Output - type: thesis - title: An examination of keystroke dynamics for continuous user authentication - authors: - - family-names: Alsolami - given-names: Eesa - institution: - name: Queensland University of Technology - address: Queensland, NZ - year: '2012' - month: '8' - notes: Example modified for testing purposes - thesis-type: Master's Thesis - -# Misc - - Code - bibparsed - Output - type: generic - title: A Language and Environment for Statistical Computing - authors: - - name: R Core Team - medium: CD-ROM - month: '1' - year: '2021' - notes: A note - -# PhdThesis - - Code - bibparsed - Output - type: thesis - title: An examination of keystroke dynamics for continuous user authentication - authors: - - family-names: Alsolami - given-names: Eesa - institution: - name: Queensland University of Technology - address: Queensland, NZ - year: '2012' - month: '8' - notes: Example modified for testing purposes - thesis-type: PhD Thesis - -# Proceedings - - Code - bibparsed - Output - type: proceedings - title: Proc. Fifteenth Annual STOC - authors: - - name: anonymous - year: '1983' - editors: - - family-names: Oz - given-names: Wizard V. - - family-names: Yannakakis - given-names: Mihalis - volume: '1' - issue: '17' - collection-title: All ACM Conferences - collection-type: proceedings - month: '8' - institution: - name: The OX Association for Computing Machinery - publisher: - name: Academic Press - notes: Example modified for testing purposes - conference: - name: All ACM Conferences - address: Boston, US - -# TechReport - - Code - bibparsed - Output - type: report - title: Naive tools for studying compilation histories - authors: - - family-names: Jadud - given-names: Matthew C. - - family-names: Fincher - given-names: Sally A. - institution: - name: University of Kent Canterbury - address: Computing Laboratory, University of Kent, Canterbury, Kent, CT2 7NF - year: '2003' - issue: 3-03 - month: '3' - notes: Example modified for testing purposes - -# Unpublished - - Code - bibparsed - Output - type: unpublished - title: Demonstratives - authors: - - family-names: Kaplan - given-names: D. - notes: Unpublished manuscript, UCLA - year: '1977' - month: '8' - -# InBook with booktitle - - Code - bibparsed - Output - type: generic - title: Bibliographies and citations - authors: - - family-names: Xie - given-names: Yihui - - family-names: Dervieux - given-names: Christophe - - family-names: Riederer - given-names: Emily - year: '2020' - collection-title: R Markdown Cookbook - collection-type: collection - publisher: - name: Chapman and Hall/CRC - address: Boca Raton, Florida - isbn: '9780367563837' - url: https://bookdown.org/yihui/rmarkdown-cookbook - section: '4.5' - -# Test entry without author - - Code - bibparsed - Output - type: proceedings - title: Proceedings of the 6th European Conference on Computer Systems - authors: - - name: anonymous - editors: - - family-names: Berbers - given-names: Yolande - - family-names: Zwaenepoel - given-names: Willy - collection-title: Proceedings of the 6th European Conference on Computer Systems - collection-type: proceedings - publisher: - name: ACM - month: '4' - year: '2006' - isbn: 1-59593-322-02 - conference: - name: Proceedings of the 6th European Conference on Computer Systems - -# Test entry without author but has a key - - Code - bibparsed - Output - type: generic - title: Proceedings of the 6th European Conference on Computer Systems - authors: - - name: anonymous - collection-title: Proceedings of the 6th European Conference on Computer Systems - collection-type: misc - publisher: - name: ACM - month: '4' - year: '2006' - isbn: 1-59593-322-02 - -# Test entry without author and key - - Code - bibparsed - Output - type: generic - title: Proceedings of the 6th European Conference on Computer Systems - authors: - - name: anonymous - collection-title: Proceedings of the 6th European Conference on Computer Systems - collection-type: misc - publisher: - name: ACM - month: '4' - year: '2006' - isbn: 1-59593-322-02 - -# Skip misc without title - - Code - cffobj - Output - cff-version: 1.2.0 - message: If you use this software, please cite it using these metadata. - title: My Research Software - authors: - - family-names: Doe - given-names: John - -# Skip misc without title, not skipping the good one - - Code - cffobj - Output - cff-version: 1.2.0 - message: If you use this software, please cite it using these metadata. - title: My Research Software - authors: - - family-names: Doe - given-names: John - references: - - type: generic - title: 'rromeo: An R Client for SHERPA/RoMEO API' - authors: - - family-names: Grenié - given-names: Matthias - - family-names: Gruson - given-names: Hugo - year: '2019' - url: https://CRAN.R-project.org/package=rromeo - -# Check extended BibLatex Fields - - Code - bibparsed - Output - type: article - title: Computation of methodology hyphen independent ionic solvation free energies - from molecular simulations - authors: - - family-names: Kastenholz - given-names: M. A. - - family-names: Hünenbergerb - given-names: Philippe H. - journal: J. Chem. Phys. - year: '2006' - notes: Example modified for testing purposes - date-published: '2006-03-15' - filename: a_file.pdf - issue-title: Semantic 3D Media and Content - translators: - - family-names: Wicksteed - given-names: P. H. - - family-names: Cornford - given-names: F. M. - date-accessed: '2006-10-01' - pages: '528' - abstract: The computation of ionic solvation free energies from atomistic simulations - is a surprisingly difficult problem that has found no satisfactory solution for - more than 15 years. - doi: 10.1063/1.2172593 - isbn: 0-816-52066-6 - issn: 0097-8493 - url: http://www.ctan.org - start: '55' - end: '65' - month: '3' - diff --git a/tests/testthat/_snaps/cff-methods.md b/tests/testthat/_snaps/cff-methods.md index 470b1f0e..114dd512 100644 --- a/tests/testthat/_snaps/cff-methods.md +++ b/tests/testthat/_snaps/cff-methods.md @@ -843,19 +843,19 @@ dput(pub) Output structure(list(list(given = NULL, family = "Entity Project Team Conference entity", - role = NULL, email = "project@entity.com", comment = c(name = "Entity Project Team Conference entity", - address = "22 Acacia Avenue", city = "Citationburgh", region = "Renfrewshire", - `post-code` = "C13 7X7", country = "GB", ORCID = "0000-0001-2345-6789", - tel = "+44(0)141-323 4567", fax = "+44(0)141-323 45678", - website = "https://www.entity-project-team.io", `date-start` = "2017-01-01", - `date-end` = "2017-01-31", location = "The team garage"))), class = "person") + role = NULL, email = "project@entity.com", comment = c(address = "22 Acacia Avenue", + city = "Citationburgh", region = "Renfrewshire", `post-code` = "C13 7X7", + country = "GB", ORCID = "0000-0001-2345-6789", tel = "+44(0)141-323 4567", + fax = "+44(0)141-323 45678", website = "https://www.entity-project-team.io", + `date-start` = "2017-01-01", `date-end` = "2017-01-31", location = "The team garage" + ))), class = "person") --- Code format(pub, include = c("given", "family", "email", "role", "comment")) Output - [1] "Entity Project Team Conference entity (Entity Project Team Conference entity, 22 Acacia Avenue, Citationburgh, Renfrewshire, C13 7X7, GB, , +44(0)141-323 4567, +44(0)141-323 45678, https://www.entity-project-team.io, 2017-01-01, 2017-01-31, The team garage)" + [1] "Entity Project Team Conference entity (22 Acacia Avenue, Citationburgh, Renfrewshire, C13 7X7, GB, , +44(0)141-323 4567, +44(0)141-323 45678, https://www.entity-project-team.io, 2017-01-01, 2017-01-31, The team garage)" --- @@ -890,20 +890,20 @@ tel = "+44(0)141-323 4567", fax = "+44(0)141-323 45678", website = "https://www.entity-project-team.io")), list(given = NULL, family = "Entity Project Team Conference entity", role = NULL, - email = "project@entity.com", comment = c(name = "Entity Project Team Conference entity", - address = "22 Acacia Avenue", city = "Citationburgh", region = "Renfrewshire", - `post-code` = "C13 7X7", country = "GB", ORCID = "0000-0001-2345-6789", - tel = "+44(0)141-323 4567", fax = "+44(0)141-323 45678", - website = "https://www.entity-project-team.io", `date-start` = "2017-01-01", - `date-end` = "2017-01-31", location = "The team garage"))), class = "person") + email = "project@entity.com", comment = c(address = "22 Acacia Avenue", + city = "Citationburgh", region = "Renfrewshire", `post-code` = "C13 7X7", + country = "GB", ORCID = "0000-0001-2345-6789", tel = "+44(0)141-323 4567", + fax = "+44(0)141-323 45678", website = "https://www.entity-project-team.io", + `date-start` = "2017-01-01", `date-end` = "2017-01-31", location = "The team garage" + ))), class = "person") --- Code format(aut2, include = c("given", "family", "email", "role", "comment")) Output - [1] "One Truly van der Real Person IV (Citey, Excellent University, Niceplace, Arcadia, 22 Acacia Avenue, Citationburgh, Renfrewshire, C13 7X7, GB, , +44(0)141-323 4567, +44(0)141-323 45678, https://www.entity-project-team.io)" - [2] "Entity Project Team Conference entity (Entity Project Team Conference entity, 22 Acacia Avenue, Citationburgh, Renfrewshire, C13 7X7, GB, , +44(0)141-323 4567, +44(0)141-323 45678, https://www.entity-project-team.io, 2017-01-01, 2017-01-31, The team garage)" + [1] "One Truly van der Real Person IV (Citey, Excellent University, Niceplace, Arcadia, 22 Acacia Avenue, Citationburgh, Renfrewshire, C13 7X7, GB, , +44(0)141-323 4567, +44(0)141-323 45678, https://www.entity-project-team.io)" + [2] "Entity Project Team Conference entity (22 Acacia Avenue, Citationburgh, Renfrewshire, C13 7X7, GB, , +44(0)141-323 4567, +44(0)141-323 45678, https://www.entity-project-team.io, 2017-01-01, 2017-01-31, The team garage)" # head and tail diff --git a/tests/testthat/_snaps/cff_create.md b/tests/testthat/_snaps/cff_create.md index 2aa1451c..968ab77e 100644 --- a/tests/testthat/_snaps/cff_create.md +++ b/tests/testthat/_snaps/cff_create.md @@ -92,3 +92,398 @@ --- +# Parse date + + Code + parsed + Output + cff-version: 1.2.0 + message: 'To cite package "rgeos" in publications use:' + type: software + license: GPL-2.0-or-later + title: 'rgeos: Interface to Geometry Engine - Open Source (''GEOS'')' + version: 0.5-7 + abstract: 'Interface to Geometry Engine - Open Source (''GEOS'') using the C ''API'' + for topology operations on geometries. Please note that ''rgeos'' will be retired + by the end of 2023, plan transition to sf functions using ''GEOS'' at your earliest + convenience. The ''GEOS'' library is external to the package, and, when installing + the package from source, must be correctly installed first. Windows and Mac Intel + OS X binaries are provided on ''CRAN''. (''rgeos'' >= 0.5-1): Up to and including + ''GEOS'' 3.7.1, topological operations succeeded with some invalid geometries for + which the same operations fail from and including ''GEOS'' 3.7.2. The ''checkValidity='' + argument defaults and structure have been changed, from default FALSE to integer + default ''0L'' for ''GEOS'' < 3.7.2 (no check), ''1L'' ''GEOS'' >= 3.7.2 (check + and warn). A value of ''2L'' is also provided that may be used, assigned globally + using ''set_RGEOS_CheckValidity(2L)'', or locally using the ''checkValidity=2L'' + argument, to attempt zero-width buffer repair if invalid geometries are found. The + previous default (FALSE, now ''0L'') is fastest and used for ''GEOS'' < 3.7.2, but + will not warn users of possible problems before the failure of topological operations + that previously succeeded. From ''GEOS'' 3.8.0, repair of geometries may also be + attempted using ''gMakeValid()'', which may, however, return a collection of geometries + of different types.' + authors: + - family-names: Bivand + given-names: Roger + email: Roger.Bivand@nhh.no + orcid: https://orcid.org/0000-0003-2392-6140 + - family-names: Rundel + given-names: Colin + repository: https://CRAN.R-project.org/package=rgeos + repository-code: https://r-forge.r-project.org/projects/rgeos/ + url: https://trac.osgeo.org/geos/ + date-released: '2020-09-07' + contact: + - family-names: Bivand + given-names: Roger + email: Roger.Bivand@nhh.no + orcid: https://orcid.org/0000-0003-2392-6140 + identifiers: + - type: url + value: http://rgeos.r-forge.r-project.org/index.html + +# Parse date in another format + + Code + parsed + Output + cff-version: 1.2.0 + message: 'To cite package "basicdescdate" in publications use:' + type: software + license: GPL-3.0-only + title: 'basicdescdate: A Basic Description with Date' + version: 0.1.6 + abstract: A very basic description. Should parse without problems. I have a Date + authors: + - family-names: Basic + given-names: Marc + email: marcbasic@gmail.com + repository-code: https://github.com/basic/package + url: https://basic.github.io/package + date-released: '1999-01-01' + contact: + - family-names: Basic + given-names: Marc + email: marcbasic@gmail.com + +# Parsing many urls + + Code + parsed + Output + cff-version: 1.2.0 + message: 'To cite package "manyurls" in publications use:' + type: software + license: GPL-3.0-only + title: 'manyurls: A lot of urls' + version: 0.1.6 + abstract: This package has many urls. Specifically, 1 Bug Reports and 6 URLs. Expected + is to have 1 repository-code, 1 url and 3 URLs, since there is 1 duplicate and 1 + invalid url. + authors: + - family-names: Basic + given-names: Marc + email: marcbasic@gmail.com + repository-code: https://github.com/test/package + url: https://test.github.io/package/ + contact: + - family-names: Basic + given-names: Marc + email: marcbasic@gmail.com + identifiers: + - type: url + value: https://r-forge.r-project.org/projects/test/ + - type: url + value: http://google.ru + - type: url + value: https://gitlab.com/r-packages/behaviorchange + +# Parsing Gitlab + + Code + parsed + Output + cff-version: 1.2.0 + message: 'To cite package "codemetar" in publications use:' + type: software + license: GPL-3.0-only + title: 'codemetar: Generate ''CodeMeta'' Metadata for R Packages' + version: 0.1.6 + abstract: The 'Codemeta' Project defines a 'JSON-LD' format for describing software + metadata, as detailed at . This package provides utilities + to generate, parse, and modify 'codemeta.json' files automatically for R packages, + as well as tools and examples for working with 'codemeta.json' 'JSON-LD' more generally. + authors: + - family-names: Boettiger + given-names: Carl + email: cboettig@gmail.com + orcid: https://orcid.org/0000-0002-1642-628X + - family-names: Salmon + given-names: Maëlle + orcid: https://orcid.org/0000-0002-2815-0399 + repository: https://CRAN.R-project.org/package=codemetar + repository-code: https://gitlab.com/ninijay/methoden + url: https://ropensci.github.io/codemetar + contact: + - family-names: Boettiger + given-names: Carl + email: cboettig@gmail.com + orcid: https://orcid.org/0000-0002-1642-628X + keywords: + - metadata + - codemeta + - ropensci + - citation + - credit + - linked-data + +# Parsing many persons + + Code + parsed + Output + cff-version: 1.2.0 + message: 'To cite package "manypersons" in publications use:' + type: software + license: GPL-3.0-only + title: 'manypersons: A lot of persons' + version: 0.1.6 + abstract: Overkill desc with many persons. Try this + authors: + - family-names: Hernangómez + given-names: Diego + email: fake@gmail.com + orcid: https://orcid.org/0000-0001-8457-4658 + - family-names: Doe + given-names: Joe + affiliation: This One + country: ES + - family-names: Doe + given-names: Pepe + email: fake@gmail.com + - name: I am an entity + date-end: '2020-01-01' + repository-code: https://github.com/many/persons + url: https://many.github.io/persons + contact: + - family-names: Hernangómez + given-names: Diego + email: fake@gmail.com + orcid: https://orcid.org/0000-0001-8457-4658 + - name: I am an entity + date-end: '2020-01-01' + keywords: + - metadata + - cffr + - ropensci + - citation + - credit + - linked-data + - one + - two + +# Parsing wrong urls + + Code + parsed + Output + cff-version: 1.2.0 + message: 'To cite package "wrongurls" in publications use:' + type: software + license: MIT + title: 'wrongurls: Generate CodeMeta Metadata for R Packages' + version: 0.1.0 + abstract: Codemeta defines a 'JSON-LD' format for describing software metadata. This + package provides utilities to generate, parse, and modify codemeta.jsonld files + automatically for R packages. + authors: + - family-names: Boettiger + given-names: Carl + email: cboettig@gmail.com + orcid: https://orcid.org/0000-0002-1642-628X + url: https://httpbin.org/status/404 + contact: + - family-names: Boettiger + given-names: Carl + email: cboettig@gmail.com + orcid: https://orcid.org/0000-0002-1642-628X + keywords: + - metadata + - codemeta + - ropensci + - citation + - credit + - linked-data + identifiers: + - type: url + value: https://httpbin.org/status/429 + - type: url + value: https://www.github.es/ropensci/codemeta + +# Parsing two maintainers + + Code + parsed + Output + cff-version: 1.2.0 + message: 'To cite package "codemetar" in publications use:' + type: software + license: GPL-3.0-only + title: 'codemetar: Generate ''CodeMeta'' Metadata for R Packages' + version: 0.1.6 + abstract: The 'Codemeta' Project defines a 'JSON-LD' format for describing software + metadata, as detailed at . This package provides utilities + to generate, parse, and modify 'codemeta.json' files automatically for R packages, + as well as tools and examples for working with 'codemeta.json' 'JSON-LD' more generally. + authors: + - family-names: Ok + given-names: John + email: email@email.edu + - family-names: Doe + given-names: Jane + email: email2@email.edu + - family-names: Doo + given-names: Jane + repository: https://CRAN.R-project.org/package=codemetar + repository-code: https://github.com/ropensci/codemetar + url: https://ropensci.github.io/codemetar + contact: + - family-names: Ok + given-names: John + email: email@email.edu + - family-names: Doe + given-names: Jane + email: email2@email.edu + +# Parsing r-universe + + Code + parsed + Output + cff-version: 1.2.0 + message: 'To cite package "codemetar" in publications use:' + type: software + license: GPL-3.0-only + title: 'codemetar: Generate ''CodeMeta'' Metadata for R Packages' + version: 0.3.5 + abstract: The 'Codemeta' Project defines a 'JSON-LD' format for describing software + metadata, as detailed at . This package provides utilities + to generate, parse, and modify 'codemeta.json' files automatically for R packages, + as well as tools and examples for working with 'codemeta.json' 'JSON-LD' more generally. + authors: + - family-names: Boettiger + given-names: Carl + email: cboettig@gmail.com + orcid: https://orcid.org/0000-0002-1642-628X + - family-names: Salmon + given-names: Maëlle + orcid: https://orcid.org/0000-0002-2815-0399 + repository: https://ropensci.r-universe.dev + repository-code: https://github.com/ropensci/codemetar + url: https://docs.ropensci.org/codemetar/ + date-released: '2024-02-09' + contact: + - family-names: Boettiger + given-names: Carl + email: cboettig@gmail.com + orcid: https://orcid.org/0000-0002-1642-628X + keywords: + - metadata + - codemeta + - ropensci + - citation + - credit + - linked-data + +# Parsing Bioconductor + + Code + parsed + Output + cff-version: 1.2.0 + message: 'To cite package "GenomicRanges" in publications use:' + type: software + license: Artistic-2.0 + title: 'GenomicRanges: Representation and manipulation of genomic intervals' + version: 1.51.4 + abstract: The ability to efficiently represent and manipulate genomic annotations + and alignments is playing a central role when it comes to analyzing high-throughput + sequencing data (a.k.a. NGS data). The GenomicRanges package defines general purpose + containers for storing and manipulating genomic intervals and variables defined + along a genome. More specialized containers for representing and manipulating short + alignments against a reference genome, or a matrix-like summarization of an experiment, + are defined in the GenomicAlignments and SummarizedExperiment packages, respectively. + Both packages build on top of the GenomicRanges infrastructure. + authors: + - family-names: Aboyoun + given-names: Patrick + - family-names: Pagès + given-names: Hervé + email: hpages.on.github@gmail.com + - family-names: Lawrence + given-names: Michael + repository: https://bioconductor.org/ + repository-code: https://github.com/Bioconductor/GenomicRanges + url: https://bioconductor.org/packages/GenomicRanges + date-released: '2022-12-15' + contact: + - family-names: Pagès + given-names: Hervé + email: hpages.on.github@gmail.com + +# Parsing Posit Package Manager + + Code + parsed + Output + cff-version: 1.2.0 + message: 'To cite package "resmush" in publications use:' + type: software + license: MIT + title: 'resmush: Optimize and Compress Image Files with ''reSmush.it''' + version: 0.1.0 + abstract: Compress local and online images using the 'reSmush.it' API service . + authors: + - family-names: Hernangómez + given-names: Diego + email: diego.hernangomezherrero@gmail.com + orcid: https://orcid.org/0000-0001-8457-4658 + repository: https://CRAN.R-project.org/package=resmush + repository-code: https://github.com/dieghernan/resmush + url: https://dieghernan.github.io/resmush/ + date-released: '2024-02-02' + contact: + - family-names: Hernangómez + given-names: Diego + email: diego.hernangomezherrero@gmail.com + orcid: https://orcid.org/0000-0001-8457-4658 + keywords: + - r + - compress-images + - optimize-images + - resmushit + - api + +# Search package on CRAN + + Code + parsed + Output + cff-version: 1.2.0 + message: 'To cite package "ggplot2" in publications use:' + type: software + license: GPL-3.0-only + title: 'ggplot2: A Basic Description' + version: 0.1.6 + abstract: A very basic description. Should parse without problems. + authors: + - family-names: Basic + given-names: Marc + email: marcbasic@gmail.com + repository: https://CRAN.R-project.org/package=ggplot2 + repository-code: https://github.com/basic/package + url: https://basic.github.io/package + contact: + - family-names: Basic + given-names: Marc + email: marcbasic@gmail.com + diff --git a/tests/testthat/_snaps/cff_description.md b/tests/testthat/_snaps/cff_description.md deleted file mode 100644 index 4a203664..00000000 --- a/tests/testthat/_snaps/cff_description.md +++ /dev/null @@ -1,395 +0,0 @@ -# Parse date - - Code - parsed - Output - cff-version: 1.2.0 - message: 'To cite package "rgeos" in publications use:' - type: software - license: GPL-2.0-or-later - title: 'rgeos: Interface to Geometry Engine - Open Source (''GEOS'')' - version: 0.5-7 - abstract: 'Interface to Geometry Engine - Open Source (''GEOS'') using the C ''API'' - for topology operations on geometries. Please note that ''rgeos'' will be retired - by the end of 2023, plan transition to sf functions using ''GEOS'' at your earliest - convenience. The ''GEOS'' library is external to the package, and, when installing - the package from source, must be correctly installed first. Windows and Mac Intel - OS X binaries are provided on ''CRAN''. (''rgeos'' >= 0.5-1): Up to and including - ''GEOS'' 3.7.1, topological operations succeeded with some invalid geometries for - which the same operations fail from and including ''GEOS'' 3.7.2. The ''checkValidity='' - argument defaults and structure have been changed, from default FALSE to integer - default ''0L'' for ''GEOS'' < 3.7.2 (no check), ''1L'' ''GEOS'' >= 3.7.2 (check - and warn). A value of ''2L'' is also provided that may be used, assigned globally - using ''set_RGEOS_CheckValidity(2L)'', or locally using the ''checkValidity=2L'' - argument, to attempt zero-width buffer repair if invalid geometries are found. The - previous default (FALSE, now ''0L'') is fastest and used for ''GEOS'' < 3.7.2, but - will not warn users of possible problems before the failure of topological operations - that previously succeeded. From ''GEOS'' 3.8.0, repair of geometries may also be - attempted using ''gMakeValid()'', which may, however, return a collection of geometries - of different types.' - authors: - - family-names: Bivand - given-names: Roger - email: Roger.Bivand@nhh.no - orcid: https://orcid.org/0000-0003-2392-6140 - - family-names: Rundel - given-names: Colin - repository: https://CRAN.R-project.org/package=rgeos - repository-code: https://r-forge.r-project.org/projects/rgeos/ - url: https://trac.osgeo.org/geos/ - date-released: '2020-09-07' - contact: - - family-names: Bivand - given-names: Roger - email: Roger.Bivand@nhh.no - orcid: https://orcid.org/0000-0003-2392-6140 - identifiers: - - type: url - value: http://rgeos.r-forge.r-project.org/index.html - -# Parse date in another format - - Code - parsed - Output - cff-version: 1.2.0 - message: 'To cite package "basicdescdate" in publications use:' - type: software - license: GPL-3.0-only - title: 'basicdescdate: A Basic Description with Date' - version: 0.1.6 - abstract: A very basic description. Should parse without problems. I have a Date - authors: - - family-names: Basic - given-names: Marc - email: marcbasic@gmail.com - repository-code: https://github.com/basic/package - url: https://basic.github.io/package - date-released: '1999-01-01' - contact: - - family-names: Basic - given-names: Marc - email: marcbasic@gmail.com - -# Parsing many urls - - Code - parsed - Output - cff-version: 1.2.0 - message: 'To cite package "manyurls" in publications use:' - type: software - license: GPL-3.0-only - title: 'manyurls: A lot of urls' - version: 0.1.6 - abstract: This package has many urls. Specifically, 1 Bug Reports and 6 URLs. Expected - is to have 1 repository-code, 1 url and 3 URLs, since there is 1 duplicate and 1 - invalid url. - authors: - - family-names: Basic - given-names: Marc - email: marcbasic@gmail.com - repository-code: https://github.com/test/package - url: https://test.github.io/package/ - contact: - - family-names: Basic - given-names: Marc - email: marcbasic@gmail.com - identifiers: - - type: url - value: https://r-forge.r-project.org/projects/test/ - - type: url - value: http://google.ru - - type: url - value: https://gitlab.com/r-packages/behaviorchange - -# Parsing Gitlab - - Code - parsed - Output - cff-version: 1.2.0 - message: 'To cite package "codemetar" in publications use:' - type: software - license: GPL-3.0-only - title: 'codemetar: Generate ''CodeMeta'' Metadata for R Packages' - version: 0.1.6 - abstract: The 'Codemeta' Project defines a 'JSON-LD' format for describing software - metadata, as detailed at . This package provides utilities - to generate, parse, and modify 'codemeta.json' files automatically for R packages, - as well as tools and examples for working with 'codemeta.json' 'JSON-LD' more generally. - authors: - - family-names: Boettiger - given-names: Carl - email: cboettig@gmail.com - orcid: https://orcid.org/0000-0002-1642-628X - - family-names: Salmon - given-names: Maëlle - orcid: https://orcid.org/0000-0002-2815-0399 - repository: https://CRAN.R-project.org/package=codemetar - repository-code: https://gitlab.com/ninijay/methoden - url: https://ropensci.github.io/codemetar - contact: - - family-names: Boettiger - given-names: Carl - email: cboettig@gmail.com - orcid: https://orcid.org/0000-0002-1642-628X - keywords: - - metadata - - codemeta - - ropensci - - citation - - credit - - linked-data - -# Parsing many persons - - Code - parsed - Output - cff-version: 1.2.0 - message: 'To cite package "manypersons" in publications use:' - type: software - license: GPL-3.0-only - title: 'manypersons: A lot of persons' - version: 0.1.6 - abstract: Overkill desc with many persons. Try this - authors: - - family-names: Hernangómez - given-names: Diego - email: fake@gmail.com - orcid: https://orcid.org/0000-0001-8457-4658 - - family-names: Doe - given-names: Joe - affiliation: This One - country: ES - - family-names: Doe - given-names: Pepe - email: fake@gmail.com - - name: I am an entity - date-end: '2020-01-01' - repository-code: https://github.com/many/persons - url: https://many.github.io/persons - contact: - - family-names: Hernangómez - given-names: Diego - email: fake@gmail.com - orcid: https://orcid.org/0000-0001-8457-4658 - - name: I am an entity - date-end: '2020-01-01' - keywords: - - metadata - - cffr - - ropensci - - citation - - credit - - linked-data - - one - - two - -# Parsing wrong urls - - Code - parsed - Output - cff-version: 1.2.0 - message: 'To cite package "wrongurls" in publications use:' - type: software - license: MIT - title: 'wrongurls: Generate CodeMeta Metadata for R Packages' - version: 0.1.0 - abstract: Codemeta defines a 'JSON-LD' format for describing software metadata. This - package provides utilities to generate, parse, and modify codemeta.jsonld files - automatically for R packages. - authors: - - family-names: Boettiger - given-names: Carl - email: cboettig@gmail.com - orcid: https://orcid.org/0000-0002-1642-628X - url: https://httpbin.org/status/404 - contact: - - family-names: Boettiger - given-names: Carl - email: cboettig@gmail.com - orcid: https://orcid.org/0000-0002-1642-628X - keywords: - - metadata - - codemeta - - ropensci - - citation - - credit - - linked-data - identifiers: - - type: url - value: https://httpbin.org/status/429 - - type: url - value: https://www.github.es/ropensci/codemeta - -# Parsing two maintainers - - Code - parsed - Output - cff-version: 1.2.0 - message: 'To cite package "codemetar" in publications use:' - type: software - license: GPL-3.0-only - title: 'codemetar: Generate ''CodeMeta'' Metadata for R Packages' - version: 0.1.6 - abstract: The 'Codemeta' Project defines a 'JSON-LD' format for describing software - metadata, as detailed at . This package provides utilities - to generate, parse, and modify 'codemeta.json' files automatically for R packages, - as well as tools and examples for working with 'codemeta.json' 'JSON-LD' more generally. - authors: - - family-names: Ok - given-names: John - email: email@email.edu - - family-names: Doe - given-names: Jane - email: email2@email.edu - - family-names: Doo - given-names: Jane - repository: https://CRAN.R-project.org/package=codemetar - repository-code: https://github.com/ropensci/codemetar - url: https://ropensci.github.io/codemetar - contact: - - family-names: Ok - given-names: John - email: email@email.edu - - family-names: Doe - given-names: Jane - email: email2@email.edu - -# Parsing r-universe - - Code - parsed - Output - cff-version: 1.2.0 - message: 'To cite package "codemetar" in publications use:' - type: software - license: GPL-3.0-only - title: 'codemetar: Generate ''CodeMeta'' Metadata for R Packages' - version: 0.3.5 - abstract: The 'Codemeta' Project defines a 'JSON-LD' format for describing software - metadata, as detailed at . This package provides utilities - to generate, parse, and modify 'codemeta.json' files automatically for R packages, - as well as tools and examples for working with 'codemeta.json' 'JSON-LD' more generally. - authors: - - family-names: Boettiger - given-names: Carl - email: cboettig@gmail.com - orcid: https://orcid.org/0000-0002-1642-628X - - family-names: Salmon - given-names: Maëlle - orcid: https://orcid.org/0000-0002-2815-0399 - repository: https://ropensci.r-universe.dev - repository-code: https://github.com/ropensci/codemetar - url: https://docs.ropensci.org/codemetar/ - date-released: '2024-02-09' - contact: - - family-names: Boettiger - given-names: Carl - email: cboettig@gmail.com - orcid: https://orcid.org/0000-0002-1642-628X - keywords: - - metadata - - codemeta - - ropensci - - citation - - credit - - linked-data - -# Parsing Bioconductor - - Code - parsed - Output - cff-version: 1.2.0 - message: 'To cite package "GenomicRanges" in publications use:' - type: software - license: Artistic-2.0 - title: 'GenomicRanges: Representation and manipulation of genomic intervals' - version: 1.51.4 - abstract: The ability to efficiently represent and manipulate genomic annotations - and alignments is playing a central role when it comes to analyzing high-throughput - sequencing data (a.k.a. NGS data). The GenomicRanges package defines general purpose - containers for storing and manipulating genomic intervals and variables defined - along a genome. More specialized containers for representing and manipulating short - alignments against a reference genome, or a matrix-like summarization of an experiment, - are defined in the GenomicAlignments and SummarizedExperiment packages, respectively. - Both packages build on top of the GenomicRanges infrastructure. - authors: - - family-names: Aboyoun - given-names: Patrick - - family-names: Pagès - given-names: Hervé - email: hpages.on.github@gmail.com - - family-names: Lawrence - given-names: Michael - repository: https://bioconductor.org/ - repository-code: https://github.com/Bioconductor/GenomicRanges - url: https://bioconductor.org/packages/GenomicRanges - date-released: '2022-12-15' - contact: - - family-names: Pagès - given-names: Hervé - email: hpages.on.github@gmail.com - -# Parsing Posit Package Manager - - Code - parsed - Output - cff-version: 1.2.0 - message: 'To cite package "resmush" in publications use:' - type: software - license: MIT - title: 'resmush: Optimize and Compress Image Files with ''reSmush.it''' - version: 0.1.0 - abstract: Compress local and online images using the 'reSmush.it' API service . - authors: - - family-names: Hernangómez - given-names: Diego - email: diego.hernangomezherrero@gmail.com - orcid: https://orcid.org/0000-0001-8457-4658 - repository: https://CRAN.R-project.org/package=resmush - repository-code: https://github.com/dieghernan/resmush - url: https://dieghernan.github.io/resmush/ - date-released: '2024-02-02' - contact: - - family-names: Hernangómez - given-names: Diego - email: diego.hernangomezherrero@gmail.com - orcid: https://orcid.org/0000-0001-8457-4658 - keywords: - - r - - compress-images - - optimize-images - - resmushit - - api - -# Search package on CRAN - - Code - parsed - Output - cff-version: 1.2.0 - message: 'To cite package "ggplot2" in publications use:' - type: software - license: GPL-3.0-only - title: 'ggplot2: A Basic Description' - version: 0.1.6 - abstract: A very basic description. Should parse without problems. - authors: - - family-names: Basic - given-names: Marc - email: marcbasic@gmail.com - repository: https://CRAN.R-project.org/package=ggplot2 - repository-code: https://github.com/basic/package - url: https://basic.github.io/package - contact: - - family-names: Basic - given-names: Marc - email: marcbasic@gmail.com - diff --git a/tests/testthat/_snaps/cff_parse_citation.md b/tests/testthat/_snaps/cff_parse_citation.md index c2081654..83643cf2 100644 --- a/tests/testthat/_snaps/cff_parse_citation.md +++ b/tests/testthat/_snaps/cff_parse_citation.md @@ -282,3 +282,486 @@ publisher: name: Random House +# Article + + Code + bibparsed + Output + type: article + title: Literate Programming + authors: + - name: R Core Team + journal: The Computer Journal + year: '1984' + volume: '27' + issue: '2' + month: '1' + notes: Example modified for testing purposes + start: '97' + end: '111' + +# Book + + Code + bibparsed + Output + type: book + title: The LaTeX Companion + authors: + - family-names: Mittelbach + given-names: Frank + - family-names: Gossens + given-names: Michel + - family-names: Braams + given-names: Johannes + - family-names: Carlisle + given-names: David + - family-names: Rowley + given-names: Chris + editors: + - name: Barnes and Noble + publisher: + name: Addison-Wesley Professional + address: Santa Monica + year: '2004' + volume: '3' + issue: '7' + collection-title: The LateX Books + collection-type: book + edition: Fourth + month: '8' + notes: Example modified for testing purposes + keywords: + - Two + - keyword + +# Booklet + + Code + bibparsed + Output + type: pamphlet + title: Java Booklet + authors: + - family-names: Mustermann + given-names: Max + medium: Internet + location: + name: Stuttgart + month: '2' + year: '2016' + notes: Example modified from Jabref + +# Conference + + Code + bibparsed + Output + type: conference-paper + title: On Notions of Information Transfer in VLSI Circuits + authors: + - family-names: Oaho + given-names: Alfred V. + - family-names: Ullman + given-names: Jeffrey D. + - family-names: Yannakakis + given-names: Mihalis + collection-title: Proc. Fifteenth Annual ACM STOC + collection-type: conference + year: '1983' + editors: + - family-names: Oz + given-names: Wizard V. + - family-names: Yannakakis + given-names: Mihalis + volume: '41' + issue: '17' + institution: + name: ACM + publisher: + name: Academic Press + notes: Example modified for testing purposes + start: '133' + end: '139' + conference: + name: Proc. Fifteenth Annual ACM STOC + address: Boston + +# InBook + + Code + bibparsed + Output + type: book + title: A Framework for Freeness Analysis + authors: + - family-names: King + given-names: A. + editors: + - family-names: Tick + given-names: E + - family-names: Succi + given-names: G + section: 7, 14 + publisher: + name: Kluwer Academic Publishers + address: Dordrecht + year: '1994' + volume: '27' + issue: '2' + collection-title: Implementations of Logic Programming Systems + collection-type: book + edition: Second + month: '1' + notes: Example modified for testing purposes + start: '137' + end: '149' + +# InCollection + + Code + bibparsed + Output + type: generic + title: Knowledge-Based Methods for WSD + authors: + - family-names: Mihalcea + given-names: Rada + collection-title: 'Word Sense Disambiguation: Algorithms and Applications' + collection-type: collection + publisher: + name: Springer + address: 107--132 + year: '2006' + editors: + - family-names: Agirre + given-names: Eneko + - family-names: Edmonds + given-names: Philip + volume: '23' + issue: '3' + section: '1,2,3' + edition: Third + month: '8' + notes: A note + start: '24' + end: '57' + +# InProceedings + + Code + bibparsed + Output + type: conference-paper + title: On Notions of Information Transfer in VLSI Circuits + authors: + - family-names: Oaho + given-names: Alfred V. + - family-names: Ullman + given-names: Jeffrey D. + - family-names: Yannakakis + given-names: Mihalis + collection-title: Proc. Fifteenth Annual ACM STOC + collection-type: proceedings + year: '1983' + editors: + - family-names: Oz + given-names: Wizard V. + - family-names: Yannakakis + given-names: Mihalis + volume: '41' + issue: '17' + institution: + name: ACM + publisher: + name: Academic Press + notes: Example modified for testing purposes + start: '133' + end: '139' + conference: + name: Proc. Fifteenth Annual ACM STOC + address: Boston + +# Manual + + Code + bibparsed + Output + type: manual + title: A Language and Environment for Statistical Computing + authors: + - name: R Core Team + institution: + name: R Foundation for Statistical Computing + address: Vienna, Austria + edition: Fourth + month: '8' + year: '2021' + notes: Example modified for testing purposes + +# MastersThesis + + Code + bibparsed + Output + type: thesis + title: An examination of keystroke dynamics for continuous user authentication + authors: + - family-names: Alsolami + given-names: Eesa + institution: + name: Queensland University of Technology + address: Queensland, NZ + year: '2012' + month: '8' + notes: Example modified for testing purposes + thesis-type: Master's Thesis + +# Misc + + Code + bibparsed + Output + type: generic + title: A Language and Environment for Statistical Computing + authors: + - name: R Core Team + medium: CD-ROM + month: '1' + year: '2021' + notes: A note + +# PhdThesis + + Code + bibparsed + Output + type: thesis + title: An examination of keystroke dynamics for continuous user authentication + authors: + - family-names: Alsolami + given-names: Eesa + institution: + name: Queensland University of Technology + address: Queensland, NZ + year: '2012' + month: '8' + notes: Example modified for testing purposes + thesis-type: PhD Thesis + +# Proceedings + + Code + bibparsed + Output + type: proceedings + title: Proc. Fifteenth Annual STOC + authors: + - name: anonymous + year: '1983' + editors: + - family-names: Oz + given-names: Wizard V. + - family-names: Yannakakis + given-names: Mihalis + volume: '1' + issue: '17' + collection-title: All ACM Conferences + collection-type: proceedings + month: '8' + institution: + name: The OX Association for Computing Machinery + publisher: + name: Academic Press + notes: Example modified for testing purposes + conference: + name: All ACM Conferences + address: Boston, US + +# TechReport + + Code + bibparsed + Output + type: report + title: Naive tools for studying compilation histories + authors: + - family-names: Jadud + given-names: Matthew C. + - family-names: Fincher + given-names: Sally A. + institution: + name: University of Kent Canterbury + address: Computing Laboratory, University of Kent, Canterbury, Kent, CT2 7NF + year: '2003' + issue: 3-03 + month: '3' + notes: Example modified for testing purposes + +# Unpublished + + Code + bibparsed + Output + type: unpublished + title: Demonstratives + authors: + - family-names: Kaplan + given-names: D. + notes: Unpublished manuscript, UCLA + year: '1977' + month: '8' + +# InBook with booktitle + + Code + bibparsed + Output + type: generic + title: Bibliographies and citations + authors: + - family-names: Xie + given-names: Yihui + - family-names: Dervieux + given-names: Christophe + - family-names: Riederer + given-names: Emily + year: '2020' + collection-title: R Markdown Cookbook + collection-type: collection + publisher: + name: Chapman and Hall/CRC + address: Boca Raton, Florida + isbn: '9780367563837' + url: https://bookdown.org/yihui/rmarkdown-cookbook + section: '4.5' + +# Test entry without author + + Code + bibparsed + Output + type: proceedings + title: Proceedings of the 6th European Conference on Computer Systems + authors: + - name: anonymous + editors: + - family-names: Berbers + given-names: Yolande + - family-names: Zwaenepoel + given-names: Willy + collection-title: Proceedings of the 6th European Conference on Computer Systems + collection-type: proceedings + publisher: + name: ACM + month: '4' + year: '2006' + isbn: 1-59593-322-02 + conference: + name: Proceedings of the 6th European Conference on Computer Systems + +# Test entry without author but has a key + + Code + bibparsed + Output + type: generic + title: Proceedings of the 6th European Conference on Computer Systems + authors: + - name: anonymous + collection-title: Proceedings of the 6th European Conference on Computer Systems + collection-type: misc + publisher: + name: ACM + month: '4' + year: '2006' + isbn: 1-59593-322-02 + +# Test entry without author and key + + Code + bibparsed + Output + type: generic + title: Proceedings of the 6th European Conference on Computer Systems + authors: + - name: anonymous + collection-title: Proceedings of the 6th European Conference on Computer Systems + collection-type: misc + publisher: + name: ACM + month: '4' + year: '2006' + isbn: 1-59593-322-02 + +# Skip misc without title + + Code + cffobj + Output + cff-version: 1.2.0 + message: If you use this software, please cite it using these metadata. + title: My Research Software + authors: + - family-names: Doe + given-names: John + +# Skip misc without title, not skipping the good one + + Code + cffobj + Output + cff-version: 1.2.0 + message: If you use this software, please cite it using these metadata. + title: My Research Software + authors: + - family-names: Doe + given-names: John + references: + - type: generic + title: 'rromeo: An R Client for SHERPA/RoMEO API' + authors: + - family-names: Grenié + given-names: Matthias + - family-names: Gruson + given-names: Hugo + year: '2019' + url: https://CRAN.R-project.org/package=rromeo + +# Check extended BibLatex Fields + + Code + bibparsed + Output + type: article + title: Computation of methodology hyphen independent ionic solvation free energies + from molecular simulations + authors: + - family-names: Kastenholz + given-names: M. A. + - family-names: Hünenbergerb + given-names: Philippe H. + journal: J. Chem. Phys. + year: '2006' + notes: Example modified for testing purposes + date-published: '2006-03-15' + filename: a_file.pdf + issue-title: Semantic 3D Media and Content + translators: + - family-names: Wicksteed + given-names: P. H. + - family-names: Cornford + given-names: F. M. + date-accessed: '2006-10-01' + pages: '528' + abstract: The computation of ionic solvation free energies from atomistic simulations + is a surprisingly difficult problem that has found no satisfactory solution for + more than 15 years. + doi: 10.1063/1.2172593 + isbn: 0-816-52066-6 + issn: 0097-8493 + url: http://www.ctan.org + start: '55' + end: '65' + month: '3' + diff --git a/tests/testthat/_snaps/cff_read_biblines.md b/tests/testthat/_snaps/cff_read_bib_text.md similarity index 86% rename from tests/testthat/_snaps/cff_read_biblines.md rename to tests/testthat/_snaps/cff_read_bib_text.md index f6a9c7bb..1fa44262 100644 --- a/tests/testthat/_snaps/cff_read_biblines.md +++ b/tests/testthat/_snaps/cff_read_bib_text.md @@ -1,15 +1,15 @@ # Errors and messages Code - cff_read_biblines(a_cff) + cff_create_bib_text(a_cff) Condition - Error in `cff_read_biblines()`: + Error in `cff_create_bib_text()`: ! `x` should be a , not a . --- Code - cff_read_biblines("a bad line") + cff_create_bib_text("a bad line") Message ! `x` doesn't look as a BibTeX entry. Check the results. Condition @@ -44,7 +44,7 @@ --- Code - fromfile <- cff_read_biblines(tmpbib) + fromfile <- cff_create_bib_text(tmpbib) Message ! `x` seems to be a ".bib" file, not a BibTeX entry. i Reading `x` with `cffr:cff_read_bib()` diff --git a/tests/testthat/_snaps/deprecated.md b/tests/testthat/_snaps/deprecated.md index fe340d9d..d7b7e777 100644 --- a/tests/testthat/_snaps/deprecated.md +++ b/tests/testthat/_snaps/deprecated.md @@ -5,7 +5,7 @@ Condition Warning: `cff_extract_to_bibtex()` was deprecated in cffr 0.5.0. - i Function renamed, use `cff_to_bibentry()` instead. + i Function renamed, use `as_bibentry()` instead. # cff_to_bibtex @@ -14,7 +14,7 @@ Condition Warning: `cff_extract_to_bibtex()` was deprecated in cffr 1.0.0. - i Function renamed, use `cff_to_bibentry()` instead. + i Function renamed, use `as_bibentry()` instead. # cff_from_bibtex @@ -32,7 +32,7 @@ Condition Warning: `cff_from_bibtex()` was deprecated in cffr 1.0.0. - i Please use `cff_read_biblines()` instead. + i Please use `cff_create_bib_text()` instead. # write_bib diff --git a/tests/testthat/_snaps/bibtex-check-ruby.md b/tests/testthat/_snaps/xtra-check-bibtex-ruby.md similarity index 99% rename from tests/testthat/_snaps/bibtex-check-ruby.md rename to tests/testthat/_snaps/xtra-check-bibtex-ruby.md index 8942a193..c80cd5ed 100644 --- a/tests/testthat/_snaps/bibtex-check-ruby.md +++ b/tests/testthat/_snaps/xtra-check-bibtex-ruby.md @@ -1,7 +1,7 @@ # preferred-citation-book-missing Code - cff_to_bibentry(x) + as_bibentry(x) Message x Can't convert to `bibentry()`: i A bibentry of bibtype 'Book' has to specify the field: publisher diff --git a/tests/testthat/test-additional-authors.R b/tests/testthat/test-additional-authors.R deleted file mode 100644 index d20165bd..00000000 --- a/tests/testthat/test-additional-authors.R +++ /dev/null @@ -1,74 +0,0 @@ -test_that("Default roles", { - p <- system.file("examples/DESCRIPTION_no_URL", package = "cffr") - - cf <- cff_create(p, dependencies = FALSE) - - # Same as - cf2 <- cff_create(p, authors_roles = c("aut", "cre"), dependencies = FALSE) - expect_identical(cf, cf2) -}) - -test_that("Add new roles", { - p <- system.file("examples/DESCRIPTION_no_URL", package = "cffr") - - cf <- cff_create(p, dependencies = FALSE) - cf2 <- cff_create(p, - authors_roles = c("aut", "cre", "ctb"), - dependencies = FALSE - ) - - expect_gt(length(cf2$authors), length(cf$authors)) - expect_true(cff_validate(cf2, verbose = FALSE)) -}) - - -test_that("Default roles on write", { - p <- system.file("examples/DESCRIPTION_no_URL", package = "cffr") - - cf <- cff_create(p, dependencies = FALSE) - - # Same as - tmp <- tempfile(fileext = ".cff") - expect_silent( - cf2 <- cff_write(p, - authors_roles = c("aut", "cre"), dependencies = FALSE, - outfile = tmp, verbose = FALSE, validate = FALSE - ) - ) - - expect_identical(cf, cf2) -}) - -test_that("Add new roles", { - p <- system.file("examples/DESCRIPTION_no_URL", package = "cffr") - - cf <- cff_create(p, dependencies = FALSE) - cf2 <- cff_create(p, - authors_roles = c("aut", "cre", "ctb"), - dependencies = FALSE - ) - - expect_gt(length(cf2$authors), length(cf$authors)) - expect_true(cff_validate(cf2, verbose = FALSE)) -}) - -test_that("Add new roles on write", { - p <- system.file("examples/DESCRIPTION_no_URL", package = "cffr") - - cf_def <- cff_create(p) - cf <- cff_create(p, authors_roles = "ctb") - - expect_gt(length(cf$authors), length(cf_def$authors)) - - # Same as - tmp <- tempfile(fileext = ".cff") - expect_message( - cf2 <- cff_write(p, - authors_roles = "ctb", outfile = tmp, - validate = FALSE - ), - "generated" - ) - - expect_identical(cf, cf2) -}) diff --git a/tests/testthat/test-cff_to_bibentry.R b/tests/testthat/test-as_bibentry.R similarity index 88% rename from tests/testthat/test-cff_to_bibentry.R rename to tests/testthat/test-as_bibentry.R index c117e41a..b7bc9279 100644 --- a/tests/testthat/test-cff_to_bibentry.R +++ b/tests/testthat/test-as_bibentry.R @@ -14,7 +14,7 @@ test_that("Article to bibtex", { ) expect_snapshot(toBibtex(bib)) x <- cff_parse_citation(bib) - bib <- cff_to_bibentry(x) + bib <- as_bibentry(x) expect_snapshot(toBibtex(bib)) }) @@ -42,7 +42,7 @@ test_that("Book to bibtex", { expect_snapshot(toBibtex(bib)) bibparsed <- cff_parse_citation(bib) - bib <- cff_to_bibentry(bibparsed) + bib <- as_bibentry(bibparsed) expect_snapshot(toBibtex(bib)) }) @@ -63,7 +63,7 @@ test_that("Booklet to bibtex", { expect_snapshot(toBibtex(bib)) bibparsed <- cff_parse_citation(bib) - bib <- cff_to_bibentry(bibparsed) + bib <- as_bibentry(bibparsed) expect_snapshot(toBibtex(bib)) }) @@ -91,7 +91,7 @@ test_that("InBook to bibtex with pages", { expect_snapshot(toBibtex(bib)) bibparsed <- cff_parse_citation(bib) - bib <- cff_to_bibentry(bibparsed) + bib <- as_bibentry(bibparsed) expect_snapshot(toBibtex(bib)) }) @@ -110,7 +110,7 @@ test_that("InCollection to bibtex", { expect_snapshot(toBibtex(bib)) bibparsed <- cff_parse_citation(bib) - bib <- cff_to_bibentry(bibparsed) + bib <- as_bibentry(bibparsed) expect_snapshot(toBibtex(bib)) }) @@ -135,7 +135,7 @@ test_that("InProceedings to bibtex", { expect_snapshot(toBibtex(bib)) bibparsed <- cff_parse_citation(bib) - bib <- cff_to_bibentry(bibparsed) + bib <- as_bibentry(bibparsed) expect_snapshot(toBibtex(bib)) }) @@ -153,7 +153,7 @@ test_that("Manual to bibtex", { expect_snapshot(toBibtex(bib)) bibparsed <- cff_parse_citation(bib) - bib <- cff_to_bibentry(bibparsed) + bib <- as_bibentry(bibparsed) expect_snapshot(toBibtex(bib)) }) @@ -172,7 +172,7 @@ test_that("MastersThesis to bibtex", { expect_snapshot(toBibtex(bib)) bibparsed <- cff_parse_citation(bib) - bib <- cff_to_bibentry(bibparsed) + bib <- as_bibentry(bibparsed) expect_snapshot(toBibtex(bib)) }) @@ -189,7 +189,7 @@ test_that("PhdThesis to bibtex", { expect_snapshot(toBibtex(bib)) bibparsed <- cff_parse_citation(bib) - bib <- cff_to_bibentry(bibparsed) + bib <- as_bibentry(bibparsed) expect_snapshot(toBibtex(bib)) }) @@ -208,7 +208,7 @@ test_that("Proceedings to bibtex", { expect_snapshot(toBibtex(bib)) bibparsed <- cff_parse_citation(bib) - bib <- cff_to_bibentry(bibparsed) + bib <- as_bibentry(bibparsed) expect_snapshot(toBibtex(bib)) }) @@ -224,7 +224,7 @@ test_that("TechReport to bibtex", { expect_snapshot(toBibtex(bib)) bibparsed <- cff_parse_citation(bib) - bib <- cff_to_bibentry(bibparsed) + bib <- as_bibentry(bibparsed) expect_snapshot(toBibtex(bib)) }) @@ -242,7 +242,7 @@ test_that("Unpublished to bibtex", { expect_snapshot(toBibtex(bib)) bibparsed <- cff_parse_citation(bib) - bib <- cff_to_bibentry(bibparsed) + bib <- as_bibentry(bibparsed) expect_snapshot(toBibtex(bib)) }) @@ -269,7 +269,7 @@ test_that("particle names", { expect_snapshot(bibparsed) - bib <- cff_to_bibentry(bibparsed) + bib <- as_bibentry(bibparsed) expect_snapshot(toBibtex(bib)) }) @@ -286,12 +286,12 @@ test_that("From plain cff with a citation", { s$`preferred-citation` <- cff_parse_citation(acit) s$`preferred-citation`$editors <- list(cff_parse_person("A name")) - bib <- cff_to_bibentry(s) + bib <- as_bibentry(s) expect_snapshot(toBibtex(bib)) }) test_that("From plain cff", { - expect_silent(bib <- cff_to_bibentry(cff())) + expect_silent(bib <- as_bibentry(cff())) expect_snapshot(toBibtex(bib)) }) @@ -300,13 +300,13 @@ test_that("From file", { package = "cffr" ) - bib <- cff_to_bibentry(file) + bib <- as_bibentry(file) expect_snapshot(toBibtex(bib)) }) test_that("NULL", { s <- NULL - expect_null(cff_to_bibentry(s)) + expect_null(as_bibentry(s)) }) @@ -316,7 +316,7 @@ test_that("Test anonymous", { ) - expect_silent(back <- cff_to_bibentry(cff_parse_citation(bib))) + expect_silent(back <- as_bibentry(cff_parse_citation(bib))) expect_snapshot(toBibtex(back)) @@ -325,7 +325,7 @@ test_that("Test anonymous", { ) - expect_silent(back <- cff_to_bibentry(cff_parse_citation(bib))) + expect_silent(back <- as_bibentry(cff_parse_citation(bib))) expect_snapshot(toBibtex(back)) bib <- bibentry("misc", @@ -333,7 +333,7 @@ test_that("Test anonymous", { ) - expect_silent(back <- cff_to_bibentry(cff_parse_citation(bib))) + expect_silent(back <- as_bibentry(cff_parse_citation(bib))) expect_snapshot(toBibtex(back)) bib <- bibentry("proceedings", @@ -342,7 +342,7 @@ test_that("Test anonymous", { ) - expect_silent(back <- cff_to_bibentry(cff_parse_citation(bib))) + expect_silent(back <- as_bibentry(cff_parse_citation(bib))) expect_snapshot(toBibtex(back)) }) @@ -361,7 +361,7 @@ test_that("Fallback month", { # Delete here the month x$month <- NULL - bibback <- cff_to_bibentry(x) + bibback <- as_bibentry(x) expect_snapshot(toBibtex(bibback)) }) @@ -395,7 +395,7 @@ test_that("Test BibLateX entry", { x <- cff_parse_citation(bib) - parsed <- cff_to_bibentry(x) + parsed <- as_bibentry(x) expect_snapshot(toBibtex(parsed)) }) @@ -403,7 +403,7 @@ test_that("Test BibLateX entry", { test_that("Test Fallback year", { x <- cff() - expect_silent(msg <- cff_to_bibentry(x)) + expect_silent(msg <- as_bibentry(x)) expect_snapshot(toBibtex(msg)) @@ -412,32 +412,32 @@ test_that("Test Fallback year", { expect_true(cff_validate(x, verbose = FALSE)) - parsed <- cff_to_bibentry(x) + parsed <- as_bibentry(x) expect_snapshot(toBibtex(parsed)) }) test_that("Errors", { - expect_silent(b <- cff_to_bibentry("testthat")) + expect_silent(b <- as_bibentry("testthat")) expect_s3_class(b, "bibentry") - expect_error(cff_to_bibentry("testthat", what = "aa")) + expect_error(as_bibentry("testthat", what = "aa")) }) test_that("From package", { skip_if_not_installed("rmarkdown") - base <- cff_to_bibentry("rmarkdown") + base <- as_bibentry("rmarkdown") expect_s3_class(base, "bibentry") expect_length(base, 1) - refs <- cff_to_bibentry("rmarkdown", "references") + refs <- as_bibentry("rmarkdown", "references") expect_s3_class(refs, "bibentry") expect_gte(length(refs), 1) - all <- cff_to_bibentry("rmarkdown", "all") + all <- as_bibentry("rmarkdown", "all") expect_s3_class(all, "bibentry") expect_length(all, length(base) + length(refs)) @@ -446,10 +446,10 @@ test_that("From package", { test_that("NULL references", { basic <- cff() - expect_null(cff_to_bibentry(basic, "references")) + expect_null(as_bibentry(basic, "references")) # Test all - expect_silent(l <- cff_to_bibentry(basic, "all")) + expect_silent(l <- as_bibentry(basic, "all")) expect_length(l, 1) }) @@ -457,7 +457,7 @@ test_that("NULL references", { test_that("From CITATION.cff", { p <- system.file("examples/smith-et-al.cff", package = "cffr") - base <- cff_to_bibentry(p) + base <- as_bibentry(p) expect_s3_class(base, "bibentry") @@ -481,6 +481,10 @@ test_that("Corrupt entry", { x <- cff_parse_citation(bib) x$year <- NULL x$journal <- NULL - expect_snapshot(n <- cff_to_bibentry(x)) + expect_snapshot(n <- as_bibentry(x)) expect_null(n) }) + +test_that("Parser return nulls", { + expect_null(cff_bibtex_parser(NULL)) +}) diff --git a/tests/testthat/test-bibtex2cff.R b/tests/testthat/test-bibtex2cff.R deleted file mode 100644 index 7b0f764f..00000000 --- a/tests/testthat/test-bibtex2cff.R +++ /dev/null @@ -1,731 +0,0 @@ -test_that("Article", { - bib <- bibentry("Article", - key = "knuth:1984", - author = person("R Core Team"), - title = "Literate Programming", - journal = "The Computer Journal", - year = "1984", - # Optional - volume = "27", - number = 2, - pages = "97--111", - month = "January", - note = "Example modified for testing purposes" - ) - - bibparsed <- cff_parse_citation(bib) - expect_snapshot(bibparsed) - - cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) - ) - - expect_true(cff_validate(cffobj, verbose = FALSE)) - - # Back to bibtex and check names - tobib <- cff_to_bibentry(bibparsed) - - fld1 <- sort(names(unclass(bib)[[1]])) - fld2 <- sort(names(unclass(tobib)[[1]])) - expect_identical(fld1, fld2) -}) - - -test_that("Book", { - bib <- bibentry("Book", - key = "latex:companion", - author = "Frank Mittelbach and Michel Gossens - and Johannes Braams and David Carlisle - and Chris Rowley", - editor = "{Barnes and Noble}", - title = "The LaTeX Companion", - publisher = "Addison-Wesley Professional", - year = "2004", - # Optional - volume = "3", - number = 7, - series = "The LateX Books", - address = "Santa Monica", - edition = "Fourth", - month = "August", - note = "Example modified for testing purposes", - keywords = c("Two, keyword") - ) - - bibparsed <- cff_parse_citation(bib) - expect_snapshot(bibparsed) - - cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) - ) - - expect_true(cff_validate(cffobj, verbose = FALSE)) - - # Back to bibtex and check names - tobib <- cff_to_bibentry(bibparsed) - - fld1 <- sort(names(unclass(bib)[[1]])) - fld2 <- sort(names(unclass(tobib)[[1]])) - expect_identical(fld1, fld2) -}) - - -test_that("Booklet", { - bib <- bibentry("Booklet", - key = "Mustermann2016", - title = "Java Booklet", - # Optional - author = "Max Mustermann", - howpublished = "Internet", - address = "Stuttgart", - month = "feb", - year = "2016", - note = "Example modified from Jabref", - keywords = "java" - ) - - bibparsed <- cff_parse_citation(bib) - expect_snapshot(bibparsed) - - cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) - ) - - expect_true(cff_validate(cffobj, verbose = FALSE)) - - # Back to bibtex and check names - tobib <- cff_to_bibentry(bibparsed) - - fld1 <- sort(names(unclass(bib)[[1]])) - fld2 <- sort(names(unclass(tobib)[[1]])) - - # Keyword is not parsed - expect_identical(setdiff(fld1, fld2), "keywords") -}) - -test_that("Conference", { - bib <- bibentry("InProceedings", - key = "inproceedings-full", - author = "Alfred V. Oaho and Jeffrey D. Ullman and Mihalis Yannakakis", - title = "On Notions of Information Transfer in {VLSI} Circuits", - title = paste( - "{Statistical Machine Translation: Rapid Development", - "with Limited Resources" - ), - booktitle = "Proc. Fifteenth Annual ACM STOC", - year = "1983", - # Optional - editor = "Wizard V. Oz and Mihalis Yannakakis", - volume = "41", - number = 17, - series = "All ACM Conferences", - pages = "133--139", - address = "Boston", - organization = "ACM", - publisher = "Academic Press", - note = "Example modified for testing purposes" - ) - - # Hack to convert to conference - bib_un <- unclass(bib)[[1]] - - attr(bib_un, "bibtype") <- "Conference" - - bib <- list(bib_un) - class(bib) <- "bibentry" - - bibparsed <- cff_parse_citation(bib) - - expect_snapshot(bibparsed) - - cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) - ) - - expect_true(cff_validate(cffobj, verbose = FALSE)) - - # Back to bibtex and check names - tobib <- cff_to_bibentry(bibparsed) - - fld1 <- unique(sort(names(unclass(bib)[[1]]))) - fld2 <- unique(sort(names(unclass(tobib)[[1]]))) - - expect_identical(setdiff(fld1, fld2), "series") -}) - -test_that("InBook", { - bib <- bibentry("InBook", - key = "1326", - author = "A. King", - editor = "E Tick and G Succi", - title = "A Framework for Freeness Analysis", - chapter = "7, 14", - pages = "137--149", - publisher = "Kluwer Academic Publishers", - year = "1994", - # Optional - volume = "27", - number = 2, - series = "Implementations of Logic Programming Systems", - type = "A chapter", - address = "Dordrecht", - edition = "Second", - month = "January", - note = "Example modified for testing purposes" - ) - - bibparsed <- cff_parse_citation(bib) - expect_snapshot(bibparsed) - - cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) - ) - - expect_true(cff_validate(cffobj, verbose = FALSE)) - # Back to bibtex and check names - tobib <- cff_to_bibentry(bibparsed) - - fld1 <- unique(sort(names(unclass(bib)[[1]]))) - fld2 <- unique(sort(names(unclass(tobib)[[1]]))) - - expect_identical(setdiff(fld1, fld2), "type") -}) - -test_that("InCollection", { - bib <- bibentry("InCollection", - key = "Mihalcea:2006", - author = "Rada Mihalcea", - title = "Knowledge-Based Methods for {WSD}", - booktitle = "Word Sense Disambiguation: Algorithms - and Applications", - publisher = "Springer", - year = "2006", - # Optional - editor = "Eneko Agirre and Philip Edmonds", - volume = "23", - number = 3, - series = "The Word Sense Series", - type = "A random type", - chapter = "1,2,3", - pages = "24--57", - address = "107--132", - edition = "Third", - month = "August", - note = "A note" - ) - - bibparsed <- cff_parse_citation(bib) - expect_snapshot(bibparsed) - - cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) - ) - - expect_true(cff_validate(cffobj, verbose = FALSE)) - - # Back to bibtex and check names - tobib <- cff_to_bibentry(bibparsed) - - fld1 <- unique(sort(names(unclass(bib)[[1]]))) - fld2 <- unique(sort(names(unclass(tobib)[[1]]))) - - expect_identical(setdiff(fld1, fld2), c("series", "type")) -}) - -test_that("InProceedings", { - bib <- bibentry("InProceedings", - key = "inproceedings-full", - author = "Alfred V. Oaho and Jeffrey D. Ullman and Mihalis Yannakakis", - title = "On Notions of Information Transfer in {VLSI} Circuits", - title = paste( - "{Statistical Machine Translation: Rapid", - "Development with Limited Resources" - ), - booktitle = "Proc. Fifteenth Annual ACM STOC", - year = "1983", - # Optional - editor = "Wizard V. Oz and Mihalis Yannakakis", - volume = "41", - number = 17, - series = "All ACM Conferences", - pages = "133--139", - address = "Boston", - organization = "ACM", - publisher = "Academic Press", - note = "Example modified for testing purposes" - ) - - bibparsed <- cff_parse_citation(bib) - expect_snapshot(bibparsed) - - cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) - ) - - expect_true(cff_validate(cffobj, verbose = FALSE)) - - # Back to bibtex and check names - tobib <- cff_to_bibentry(bibparsed) - - fld1 <- unique(sort(names(unclass(bib)[[1]]))) - fld2 <- unique(sort(names(unclass(tobib)[[1]]))) - - expect_identical(setdiff(fld1, fld2), "series") -}) - -test_that("Manual", { - bib <- bibentry("Manual", - title = "A Language and Environment for Statistical Computing", - # Optional - author = person("R Core Team"), - organization = "R Foundation for Statistical Computing", - address = "Vienna, Austria", - edition = "Fourth", - month = "August", - year = "2021", - note = "Example modified for testing purposes" - ) - - bibparsed <- cff_parse_citation(bib) - expect_snapshot(bibparsed) - - cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) - ) - - expect_true(cff_validate(cffobj, verbose = FALSE)) - - # Back to bibtex and check names - tobib <- cff_to_bibentry(bibparsed) - - fld1 <- unique(sort(names(unclass(bib)[[1]]))) - fld2 <- unique(sort(names(unclass(tobib)[[1]]))) - - expect_identical(fld1, fld2) -}) - -test_that("MastersThesis", { - bib <- bibentry("MastersThesis", - key = "Master2006", - author = "Eesa Alsolami", - title = "An examination of keystroke dynamics - for continuous user authentication", - school = "Queensland University of Technology", - year = "2012", - # Optional - type = "Final thesis", - address = "Queensland, NZ", - month = "August", - note = "Example modified for testing purposes" - ) - bibparsed <- cff_parse_citation(bib) - expect_snapshot(bibparsed) - - cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) - ) - - expect_true(cff_validate(cffobj, verbose = FALSE)) - - # Back to bibtex and check names - tobib <- cff_to_bibentry(bibparsed) - - fld1 <- unique(sort(names(unclass(bib)[[1]]))) - fld2 <- unique(sort(names(unclass(tobib)[[1]]))) - - expect_identical(setdiff(fld1, fld2), "type") -}) - -test_that("Misc", { - bib <- bibentry("Misc", - # Optional - author = person("R Core Team"), - title = "A Language and Environment for Statistical Computing", - howpublished = "CD-ROM", - month = 1, - year = "2021", - note = "A note" - ) - - bibparsed <- cff_parse_citation(bib) - expect_snapshot(bibparsed) - - cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) - ) - - expect_true(cff_validate(cffobj, verbose = FALSE)) - - # Back to bibtex and check names - tobib <- cff_to_bibentry(bibparsed) - - fld1 <- unique(sort(names(unclass(bib)[[1]]))) - fld2 <- unique(sort(names(unclass(tobib)[[1]]))) - - expect_identical(fld1, fld2) -}) - - -test_that("PhdThesis", { - bib <- bibentry("PhdThesis", - author = "Eesa Alsolami", - title = "An examination of keystroke dynamics - for continuous user authentication", - school = "Queensland University of Technology", - year = "2012", - # Optional - type = "Final PhD thesis", - address = "Queensland, NZ", - month = "August", - note = "Example modified for testing purposes" - ) - - bibparsed <- cff_parse_citation(bib) - expect_snapshot(bibparsed) - - cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) - ) - - expect_true(cff_validate(cffobj, verbose = FALSE)) - - # Back to bibtex and check names - tobib <- cff_to_bibentry(bibparsed) - - fld1 <- unique(sort(names(unclass(bib)[[1]]))) - fld2 <- unique(sort(names(unclass(tobib)[[1]]))) - - expect_identical(setdiff(fld1, fld2), "type") -}) - - -test_that("Proceedings", { - bib <- bibentry("Proceedings", - title = "Proc. Fifteenth Annual STOC", - year = "1983", - # Optional - editor = "Wizard V. Oz and Mihalis Yannakakis", - volume = 1, - number = 17, - series = "All ACM Conferences", - address = "Boston, US", - month = "August", - organization = "The OX Association for Computing Machinery", - publisher = "Academic Press", - note = "Example modified for testing purposes" - ) - - bibparsed <- cff_parse_citation(bib) - expect_snapshot(bibparsed) - - cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) - ) - - expect_true(cff_validate(cffobj, verbose = FALSE)) - - # Back to bibtex and check names - tobib <- cff_to_bibentry(bibparsed) - - fld1 <- unique(sort(names(unclass(bib)[[1]]))) - fld2 <- unique(sort(names(unclass(tobib)[[1]]))) - - expect_identical(fld1, fld2) -}) - -test_that("TechReport", { - bib <- bibentry("TechReport", - author = "Matthew C. Jadud and Sally A. Fincher", - title = "Naive tools for studying compilation histories", - institution = "University of Kent Canterbury", - year = "2003", - # Optional - type = "techreport", - number = "3-03", - address = paste( - "Computing Laboratory, University of Kent,", - "Canterbury, Kent, CT2 7NF" - ), - month = "mar", - note = "Example modified for testing purposes" - ) - - bibparsed <- cff_parse_citation(bib) - expect_snapshot(bibparsed) - - cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) - ) - - expect_true(cff_validate(cffobj, verbose = FALSE)) - - # Back to bibtex and check names - tobib <- cff_to_bibentry(bibparsed) - - fld1 <- unique(sort(names(unclass(bib)[[1]]))) - fld2 <- unique(sort(names(unclass(tobib)[[1]]))) - - expect_identical(setdiff(fld1, fld2), "type") -}) - -test_that("Unpublished", { - bib <- bibentry("Unpublished", - author = "D. Kaplan", - title = "Demonstratives", - note = "Unpublished manuscript, UCLA", - # Optional - year = "1977", - month = "aug", - ) - - bibparsed <- cff_parse_citation(bib) - expect_snapshot(bibparsed) - - cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) - ) - - expect_true(cff_validate(cffobj, verbose = FALSE)) - - # Back to bibtex and check names - tobib <- cff_to_bibentry(bibparsed) - - fld1 <- unique(sort(names(unclass(bib)[[1]]))) - fld2 <- unique(sort(names(unclass(tobib)[[1]]))) - - expect_identical(fld1, fld2) -}) - -test_that("InBook with booktitle", { - bib <- bibentry("InBook", - title = "Bibliographies and citations", - year = "2020", - author = "Yihui Xie and Christophe Dervieux and Emily Riederer", - booktitle = "{R} Markdown Cookbook", - publisher = "Chapman and Hall/CRC", - address = "Boca Raton, Florida", - series = "The {R} Series", - isbn = "9780367563837", - url = "https://bookdown.org/yihui/rmarkdown-cookbook", - chapter = "4.5" - ) - - bibparsed <- cff_parse_citation(bib) - expect_snapshot(bibparsed) - - cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) - ) - - expect_true(cff_validate(cffobj, verbose = FALSE)) - - # Should be an incollection now - res <- cff_to_bibentry(bibparsed) - init_type <- attr(unclass(res)[[1]], "bibtype") - expect_identical(tolower(init_type), "incollection") - - # Back to bibtex and check names - tobib <- cff_to_bibentry(bibparsed) - - fld1 <- unique(sort(names(unclass(bib)[[1]]))) - fld2 <- unique(sort(names(unclass(tobib)[[1]]))) - - expect_identical(setdiff(fld1, fld2), "series") -}) - -test_that("Test entry without author", { - bib <- bibentry("Proceedings", - editor = "Yolande Berbers and Willy Zwaenepoel", - title = "Proceedings of the 6th European Conference on Computer Systems", - booktitle = paste("Proceedings of the 6th European", " - Conference on Computer Systems"), - publisher = "ACM", - venue = "Leuven, Belgium", - month = "apr", - year = 2006, - isbn = "1-59593-322-02", - ) - - bibparsed <- cff_parse_citation(bib) - - expect_identical( - bibparsed$authors[[1]]$name, - "anonymous" - ) - - expect_snapshot(bibparsed) - - cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) - ) - - expect_true(cff_validate(cffobj, verbose = FALSE)) -}) - - -test_that("Test entry without author but has a key", { - bib <- bibentry("Misc", - key = "I am the key", - title = "Proceedings of the 6th European Conference on Computer Systems", - booktitle = paste( - "Proceedings of the 6th European Conference", - "on Computer Systems" - ), - publisher = "ACM", - venue = "Leuven, Belgium", - month = "apr", - year = 2006, - isbn = "1-59593-322-02", - ) - - bibparsed <- cff_parse_citation(bib) - - expect_identical( - bibparsed$authors[[1]]$name, - "anonymous" - ) - - - expect_snapshot(bibparsed) - - cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) - ) - - expect_true(cff_validate(cffobj, verbose = FALSE)) -}) - - -test_that("Test entry without author and key", { - bib <- bibentry("Misc", - title = "Proceedings of the 6th European Conference on Computer Systems", - booktitle = paste("Proceedings of the 6th European", " - Conference on Computer Systems"), - publisher = "ACM", - venue = "Leuven, Belgium", - month = "apr", - year = 2006, - isbn = "1-59593-322-02", - ) - - bibparsed <- cff_parse_citation(bib) - - expect_identical( - bibparsed$authors[[1]]$name, - "anonymous" - ) - - - expect_snapshot(bibparsed) - - cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) - ) - - expect_true(cff_validate(cffobj, verbose = FALSE)) -}) - - -test_that("Skip misc without title", { - bib <- bibentry( - bibtype = "misc", - author = c(person("SHERPA/RoMEO")), - url = "http://www.sherpa.ac.uk/romeo/", - year = 2018 - ) - - expect_message(bibparsed <- cff_parse_citation(bib), "Skipping") - - expect_null(bibparsed) - - cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) - ) - - expect_snapshot(cffobj) - - expect_true(cff_validate(cffobj, verbose = FALSE)) -}) - - -test_that("Skip misc without title, not skipping the good one", { - bib <- c( - bibentry( - bibtype = "misc", - author = c(person("SHERPA/RoMEO")), - url = "http://www.sherpa.ac.uk/romeo/", - year = 2018 - ), - bibentry( - bibtype = "misc", - title = "{rromeo}: An {R} Client for {SHERPA/RoMEO} {API}", - author = c( - person("Matthias", "Grenié"), - person("Hugo", "Gruson") - ), - year = 2019, - header = "To cite this package in publications, please use:", - url = "https://CRAN.R-project.org/package=rromeo" - ) - ) - - - - expect_message(bibparsed <- cff_parse_citation(bib), "SHERPA/RoMEO") - - expect_length(bibparsed, 2) - - expect_null(bibparsed[[1]]) - - expect_s3_class(bibparsed[[2]], "cff") - - cffobj <- cff_create(cff(), - keys = list(references = bibparsed) - ) - - expect_snapshot(cffobj) - - expect_equal( - cffobj$references[[1]]$title, - "rromeo: An R Client for SHERPA/RoMEO API" - ) - - expect_true(cff_validate(cffobj, verbose = FALSE)) -}) - - -test_that("Check extended BibLatex Fields", { - bib <- bibentry("Article", - author = "M. A. Kastenholz, and Philippe H. Hünenbergerb", - title = "Computation of methodology hyphen independent ionic solvation - free energies from molecular simulations", - journal = "J. Chem. Phys.", - year = 2006, - note = "Example modified for testing purposes", - pages = "55--65", - - # Additional BibLatex Fields - date = "2006-03-15", - file = "a_file.pdf", - issuetitle = "Semantic {3D} Media and Content", - translator = "Wicksteed, P. H. and Cornford, F. M.", - urldate = "2006-10-01", - pagetotal = 528, - abstract = "The computation of ionic solvation free energies from - atomistic simulations is a surprisingly difficult problem that - has found no satisfactory solution for more than 15 years.", - doi = "10.1063/1.2172593", - isbn = "0-816-52066-6", - issn = "0097-8493", - url = "http://www.ctan.org" - ) - - bibparsed <- cff_parse_citation(bib) - expect_snapshot(bibparsed) - - cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) - ) - - expect_true(cff_validate(cffobj, verbose = FALSE)) -}) diff --git a/tests/testthat/test-cff-methods.R b/tests/testthat/test-cff-methods.R index 4311eb20..0d610c4b 100644 --- a/tests/testthat/test-cff-methods.R +++ b/tests/testthat/test-cff-methods.R @@ -55,7 +55,7 @@ test_that("as data frame partial", { test_that("Convert a citation only", { path <- system.file("examples/DESCRIPTION_many_persons", package = "cffr") - a_cit <- cff_to_bibentry(cff_create(path)) + a_cit <- as_bibentry(cff_create(path)) the_cff <- cff_parse_citation(a_cit) @@ -248,6 +248,6 @@ test_that("toBibtex", { address = {London, United Kingdom}, isbn = 9781587340925}" - froml <- toBibtex(cff_read_biblines(string)) + froml <- toBibtex(cff_create_bib_text(string)) expect_equal(sum(names(froml) == "title"), 1) }) diff --git a/tests/testthat/test-cff_create.R b/tests/testthat/test-cff_create.R index 7c5776f2..883aca28 100644 --- a/tests/testthat/test-cff_create.R +++ b/tests/testthat/test-cff_create.R @@ -86,3 +86,457 @@ test_that("Test installed packages vs call to file", { expect_identical(call1, call2) }) + +# Additional authors ---- + +test_that("Default roles", { + p <- system.file("examples/DESCRIPTION_no_URL", package = "cffr") + + cf <- cff_create(p, dependencies = FALSE) + + # Same as + cf2 <- cff_create(p, authors_roles = c("aut", "cre"), dependencies = FALSE) + expect_identical(cf, cf2) +}) + +test_that("Add new roles", { + p <- system.file("examples/DESCRIPTION_no_URL", package = "cffr") + + cf <- cff_create(p, dependencies = FALSE) + cf2 <- cff_create(p, + authors_roles = c("aut", "cre", "ctb"), + dependencies = FALSE + ) + + expect_gt(length(cf2$authors), length(cf$authors)) + expect_true(cff_validate(cf2, verbose = FALSE)) +}) + + +test_that("Default roles on write", { + p <- system.file("examples/DESCRIPTION_no_URL", package = "cffr") + + cf <- cff_create(p, dependencies = FALSE) + + # Same as + tmp <- tempfile(fileext = ".cff") + expect_silent( + cf2 <- cff_write(p, + authors_roles = c("aut", "cre"), dependencies = FALSE, + outfile = tmp, verbose = FALSE, validate = FALSE + ) + ) + + expect_identical(cf, cf2) +}) + +test_that("Add new roles", { + p <- system.file("examples/DESCRIPTION_no_URL", package = "cffr") + + cf <- cff_create(p, dependencies = FALSE) + cf2 <- cff_create(p, + authors_roles = c("aut", "cre", "ctb"), + dependencies = FALSE + ) + + expect_gt(length(cf2$authors), length(cf$authors)) + expect_true(cff_validate(cf2, verbose = FALSE)) +}) + +test_that("Add new roles on write", { + p <- system.file("examples/DESCRIPTION_no_URL", package = "cffr") + + cf_def <- cff_create(p) + cf <- cff_create(p, authors_roles = "ctb") + + expect_gt(length(cf$authors), length(cf_def$authors)) + + # Same as + tmp <- tempfile(fileext = ".cff") + expect_message( + cf2 <- cff_write(p, + authors_roles = "ctb", outfile = tmp, + validate = FALSE + ), + "generated" + ) + + expect_identical(cf, cf2) +}) + +# Check DESCRIPTION ---- +test_that("Parse date", { + desc_path <- system.file("examples/DESCRIPTION_rgeos", package = "cffr") + + parsed <- cff_create(desc_path, + gh_keywords = FALSE, + keys = list(references = NULL) + ) + + expect_false(is.null(parsed$`date-released`)) + + expect_s3_class(parsed, "cff") + expect_snapshot(parsed) + expect_true(cff_validate(parsed, verbose = FALSE)) +}) + +test_that("Parse date in another format", { + desc_path <- system.file("examples/DESCRIPTION_basicdate", package = "cffr") + + parsed <- cff_create(desc_path, + gh_keywords = FALSE, + keys = list(references = NULL) + ) + + expect_false(is.null(parsed$`date-released`)) + + expect_s3_class(parsed, "cff") + expect_snapshot(parsed) + expect_true(cff_validate(parsed, verbose = FALSE)) +}) + + +test_that("No date parsed in DESCRIPTION without it", { + desc_path <- system.file("examples/DESCRIPTION_basic", package = "cffr") + + parsed <- cff_create(desc_path, + keys = list(references = NULL) + ) + + expect_true(is.null(parsed$`date-released`)) + + expect_s3_class(parsed, "cff") + expect_true(cff_validate(parsed, verbose = FALSE)) +}) + +test_that("Parsing many urls", { + desc_path <- system.file("examples/DESCRIPTION_many_urls", package = "cffr") + + parsed <- cff_create(desc_path, + gh_keywords = FALSE, + keys = list(references = NULL) + ) + + expect_length(parsed$`repository-code`, 1) + expect_length(parsed$url, 1) + expect_length(parsed$identifiers, 3) + expect_s3_class(parsed, "cff") + expect_snapshot(parsed) + expect_true(cff_validate(parsed, verbose = FALSE)) +}) + + +test_that("Parsing Gitlab", { + desc_path <- system.file("examples/DESCRIPTION_gitlab", package = "cffr") + + parsed <- cff_create(desc_path, + keys = list(references = NULL) + ) + + expect_length(parsed$`repository-code`, 1) + expect_length(parsed$url, 1) + expect_length(parsed$identifiers, 0) + expect_s3_class(parsed, "cff") + expect_snapshot(parsed) + expect_true(cff_validate(parsed, verbose = FALSE)) +}) + +test_that("Parsing many persons", { + desc_path <- system.file("examples/DESCRIPTION_many_persons", + package = "cffr" + ) + + parsed <- cff_create(desc_path, + gh_keywords = FALSE, + keys = list(references = NULL) + ) + + + expect_length(parsed$authors, 4) + + + authors <- unlist(parsed$authors) + + + expect_length(grep("erro", authors), 0) + names <- unlist(lapply(parsed$authors, names)) + + expect_s3_class(parsed, "cff") + expect_snapshot(parsed) + expect_true(cff_validate(parsed, verbose = FALSE)) +}) + + +test_that("Parsing wrong urls", { + desc_path <- system.file("examples/DESCRIPTION_wrong_urls", package = "cffr") + + parsed <- cff_create(desc_path, + gh_keywords = FALSE, + keys = list(references = NULL) + ) + + expect_null(parsed$`repository-code`) + expect_length(parsed$url, 1) + expect_length(parsed$identifiers, 2) + + expect_s3_class(parsed, "cff") + expect_snapshot(parsed) + expect_true(cff_validate(parsed, verbose = FALSE)) +}) + + + +test_that("Parsing two maintainers", { + desc_path <- system.file("examples/DESCRIPTION_twomaintainers", + package = "cffr" + ) + + parsed <- cff_create(desc_path, + gh_keywords = FALSE, + keys = list(references = NULL) + ) + + expect_length(parsed$authors, 3) + expect_length(parsed$contact, 2) + + expect_s3_class(parsed, "cff") + expect_snapshot(parsed) + expect_true(cff_validate(parsed, verbose = FALSE)) +}) + +test_that("Parsing r-universe", { + desc_path <- system.file("examples/DESCRIPTION_r_universe", + package = "cffr" + ) + + parsed <- cff_create(desc_path, + gh_keywords = FALSE, + keys = list(references = NULL) + ) + + expect_length(parsed$repository, 1) + + expect_s3_class(parsed, "cff") + expect_snapshot(parsed) + expect_true(cff_validate(parsed, verbose = FALSE)) +}) + + +test_that("Parsing Bioconductor", { + desc_path <- system.file("examples/DESCRIPTION_bioconductor", + package = "cffr" + ) + + parsed <- cff_create(desc_path, + gh_keywords = FALSE, + keys = list(references = NULL) + ) + + expect_length(parsed$repository, 1) + + expect_s3_class(parsed, "cff") + expect_snapshot(parsed) + expect_true(cff_validate(parsed, verbose = FALSE)) +}) + +test_that("Parsing Posit Package Manager", { + desc_path <- system.file("examples/DESCRIPTION_posit_package_manager", + package = "cffr" + ) + + parsed <- cff_create(desc_path, + gh_keywords = FALSE, + keys = list(references = NULL) + ) + + expect_length(parsed$repository, 1) + expect_identical( + parsed$repository, + "https://CRAN.R-project.org/package=resmush" + ) + expect_s3_class(parsed, "cff") + expect_snapshot(parsed) + expect_true(cff_validate(parsed, verbose = FALSE)) +}) + +test_that("Search package on CRAN", { + basic_path <- system.file("examples/DESCRIPTION_basic", + package = "cffr" + ) + + tmp <- tempfile("DESCRIPTION_basic") + # Create a temporary file + file.copy(basic_path, tmp) + + newfile <- desc::desc_set("Package", "ggplot2", file = tmp) + + parsed <- cff_create(tmp, gh_keywords = FALSE) + expect_length(parsed$repository, 1) + expect_equal(clean_str(newfile$get("Package")), "ggplot2") + expect_equal(parsed$repository, "https://CRAN.R-project.org/package=ggplot2") + + + + expect_s3_class(parsed, "cff") + expect_snapshot(parsed) + expect_true(cff_validate(parsed, verbose = FALSE)) +}) + + +test_that("Search package on r-universe", { + skip_on_cran() + skip_if_offline() + + basic_path <- system.file("examples/DESCRIPTION_basic", + package = "cffr" + ) + + tmp <- tempfile("DESCRIPTION_basic") + # Create a temporary file + file.copy(basic_path, tmp) + + + # Get packages from my r-universe + dhh <- unlist(jsonlite::read_json( + "https://dieghernan.r-universe.dev/packages" + ))[1] + + + newpack <- desc::desc(tmp) + + oldtitle <- clean_str(newpack$get("Package")) + + newtitle <- desc::desc_set("Package", dhh, file = tmp) + + expect_false(oldtitle == clean_str(newtitle$get("Package"))) + + + # Configure to search on r-universe + newrepos <- c( + dieghernan = "https://dieghernan.r-universe.dev", + CRAN = "https://cloud.r-project.org" + ) + + runiverse <- as.data.frame(available.packages( + repos = newrepos + )) + + expect_equal( + search_on_repos(dhh, runiverse), + "https://dieghernan.r-universe.dev/" + ) + + + # Search now ggplot2, should be canonical url + + expect_equal( + search_on_repos("ggplot2", runiverse, newrepos), + "https://CRAN.R-project.org/package=ggplot2" + ) +}) + + +test_that("Validate keywords", { + desc_path <- system.file("examples/DESCRIPTION_basic", + package = "cffr" + ) + + tmp <- tempfile("DESCRIPTION_keyword") + + copy <- file.copy(desc_path, tmp) + + cffobj <- cff_create(tmp) + expect_null(cffobj$keywords) + + expect_true(cff_validate(cffobj, verbose = FALSE)) + + # Add keywords + silent <- desc::desc_set("X-schema.org-keywords", + "keyword1, keyword1, keyword3", + file = tmp + ) + cffobj2 <- cff_create(tmp) + expect_length(cffobj2$keywords, 2) + expect_equal(cffobj2$keywords, c("keyword1", "keyword3")) + expect_true(cff_validate(cffobj2, verbose = FALSE)) + + # Single keyword + silent <- desc::desc_set("X-schema.org-keywords", + "keyword1, keyword1", + file = tmp + ) + cffobj3 <- cff_create(tmp) + expect_length(cffobj3$keywords, 2) + expect_equal(cffobj3$keywords, c("keyword1", "r-package")) + expect_true(cff_validate(cffobj3, verbose = FALSE)) + + # NULL case keyword + silent <- desc::desc_set("X-schema.org-keywords", + "r-package", + file = tmp + ) + cffobj4 <- cff_create(tmp) + expect_null(cffobj4$keywords) + expect_true(cff_validate(cffobj4, verbose = FALSE)) +}) + + +test_that("Parse keywords from GH", { + skip_on_cran() + skip_if_offline() + skip_if( + nchar(Sys.getenv("GITHUB_TOKEN")) == 0, + "No GITHUB_TOKEN environment variable found" + ) + + desc_path <- system.file("examples/DESCRIPTION_basic", + package = "cffr" + ) + + tmp <- tempfile("DESCRIPTION_keyword_gh") + + copy <- file.copy(desc_path, tmp) + + cffobj <- cff_create(tmp) + expect_null(cffobj$keywords) + + expect_true(cff_validate(cffobj, verbose = FALSE)) + + # A site with no topics + silent <- desc::desc_set("BugReports", + "https://github.com/dieghernan/cfftest/issues", + file = tmp + ) + + cffobjnokeys <- cff_create(tmp) + expect_true(cff_validate(cffobjnokeys, verbose = FALSE)) + expect_null(cffobjnokeys$keywords) + + # Add keywords from url + silent <- desc::desc_set("URL", + "https://github.com/ropensci/cffr", + file = tmp + ) + + silent <- desc::desc_set("BugReports", + "https://github.com/ropensci/cffr/issues", + file = tmp + ) + + cffobj1 <- cff_create(tmp) + expect_true(cff_validate(cffobj1, verbose = FALSE)) + expect_false(is.null(cffobj1$keywords)) + + # Concatenate keywords of both sources + + # Add keywords + silent <- desc::desc_set("X-schema.org-keywords", + "keyword1", + file = tmp + ) + + cffobj2 <- cff_create(tmp) + expect_true(cff_validate(cffobj2, verbose = FALSE)) + + expect_true(length(cffobj2$keywords) > length(cffobj1$keywords)) +}) diff --git a/tests/testthat/test-cff_description.R b/tests/testthat/test-cff_description.R deleted file mode 100644 index 2a6f8f1d..00000000 --- a/tests/testthat/test-cff_description.R +++ /dev/null @@ -1,375 +0,0 @@ -test_that("Parse date", { - desc_path <- system.file("examples/DESCRIPTION_rgeos", package = "cffr") - - parsed <- cff_create(desc_path, - gh_keywords = FALSE, - keys = list(references = NULL) - ) - - expect_false(is.null(parsed$`date-released`)) - - expect_s3_class(parsed, "cff") - expect_snapshot(parsed) - expect_true(cff_validate(parsed, verbose = FALSE)) -}) - -test_that("Parse date in another format", { - desc_path <- system.file("examples/DESCRIPTION_basicdate", package = "cffr") - - parsed <- cff_create(desc_path, - gh_keywords = FALSE, - keys = list(references = NULL) - ) - - expect_false(is.null(parsed$`date-released`)) - - expect_s3_class(parsed, "cff") - expect_snapshot(parsed) - expect_true(cff_validate(parsed, verbose = FALSE)) -}) - - -test_that("No date parsed in DESCRIPTION without it", { - desc_path <- system.file("examples/DESCRIPTION_basic", package = "cffr") - - parsed <- cff_create(desc_path, - keys = list(references = NULL) - ) - - expect_true(is.null(parsed$`date-released`)) - - expect_s3_class(parsed, "cff") - expect_true(cff_validate(parsed, verbose = FALSE)) -}) - -test_that("Parsing many urls", { - desc_path <- system.file("examples/DESCRIPTION_many_urls", package = "cffr") - - parsed <- cff_create(desc_path, - gh_keywords = FALSE, - keys = list(references = NULL) - ) - - expect_length(parsed$`repository-code`, 1) - expect_length(parsed$url, 1) - expect_length(parsed$identifiers, 3) - expect_s3_class(parsed, "cff") - expect_snapshot(parsed) - expect_true(cff_validate(parsed, verbose = FALSE)) -}) - - -test_that("Parsing Gitlab", { - desc_path <- system.file("examples/DESCRIPTION_gitlab", package = "cffr") - - parsed <- cff_create(desc_path, - keys = list(references = NULL) - ) - - expect_length(parsed$`repository-code`, 1) - expect_length(parsed$url, 1) - expect_length(parsed$identifiers, 0) - expect_s3_class(parsed, "cff") - expect_snapshot(parsed) - expect_true(cff_validate(parsed, verbose = FALSE)) -}) - -test_that("Parsing many persons", { - desc_path <- system.file("examples/DESCRIPTION_many_persons", - package = "cffr" - ) - - parsed <- cff_create(desc_path, - gh_keywords = FALSE, - keys = list(references = NULL) - ) - - - expect_length(parsed$authors, 4) - - - authors <- unlist(parsed$authors) - - - expect_length(grep("erro", authors), 0) - names <- unlist(lapply(parsed$authors, names)) - - expect_s3_class(parsed, "cff") - expect_snapshot(parsed) - expect_true(cff_validate(parsed, verbose = FALSE)) -}) - - -test_that("Parsing wrong urls", { - desc_path <- system.file("examples/DESCRIPTION_wrong_urls", package = "cffr") - - parsed <- cff_create(desc_path, - gh_keywords = FALSE, - keys = list(references = NULL) - ) - - expect_null(parsed$`repository-code`) - expect_length(parsed$url, 1) - expect_length(parsed$identifiers, 2) - - expect_s3_class(parsed, "cff") - expect_snapshot(parsed) - expect_true(cff_validate(parsed, verbose = FALSE)) -}) - - - -test_that("Parsing two maintainers", { - desc_path <- system.file("examples/DESCRIPTION_twomaintainers", - package = "cffr" - ) - - parsed <- cff_create(desc_path, - gh_keywords = FALSE, - keys = list(references = NULL) - ) - - expect_length(parsed$authors, 3) - expect_length(parsed$contact, 2) - - expect_s3_class(parsed, "cff") - expect_snapshot(parsed) - expect_true(cff_validate(parsed, verbose = FALSE)) -}) - -test_that("Parsing r-universe", { - desc_path <- system.file("examples/DESCRIPTION_r_universe", - package = "cffr" - ) - - parsed <- cff_create(desc_path, - gh_keywords = FALSE, - keys = list(references = NULL) - ) - - expect_length(parsed$repository, 1) - - expect_s3_class(parsed, "cff") - expect_snapshot(parsed) - expect_true(cff_validate(parsed, verbose = FALSE)) -}) - - -test_that("Parsing Bioconductor", { - desc_path <- system.file("examples/DESCRIPTION_bioconductor", - package = "cffr" - ) - - parsed <- cff_create(desc_path, - gh_keywords = FALSE, - keys = list(references = NULL) - ) - - expect_length(parsed$repository, 1) - - expect_s3_class(parsed, "cff") - expect_snapshot(parsed) - expect_true(cff_validate(parsed, verbose = FALSE)) -}) - -test_that("Parsing Posit Package Manager", { - desc_path <- system.file("examples/DESCRIPTION_posit_package_manager", - package = "cffr" - ) - - parsed <- cff_create(desc_path, - gh_keywords = FALSE, - keys = list(references = NULL) - ) - - expect_length(parsed$repository, 1) - expect_identical( - parsed$repository, - "https://CRAN.R-project.org/package=resmush" - ) - expect_s3_class(parsed, "cff") - expect_snapshot(parsed) - expect_true(cff_validate(parsed, verbose = FALSE)) -}) - -test_that("Search package on CRAN", { - basic_path <- system.file("examples/DESCRIPTION_basic", - package = "cffr" - ) - - tmp <- tempfile("DESCRIPTION_basic") - # Create a temporary file - file.copy(basic_path, tmp) - - newfile <- desc::desc_set("Package", "ggplot2", file = tmp) - - parsed <- cff_create(tmp, gh_keywords = FALSE) - expect_length(parsed$repository, 1) - expect_equal(clean_str(newfile$get("Package")), "ggplot2") - expect_equal(parsed$repository, "https://CRAN.R-project.org/package=ggplot2") - - - - expect_s3_class(parsed, "cff") - expect_snapshot(parsed) - expect_true(cff_validate(parsed, verbose = FALSE)) -}) - - -test_that("Search package on r-universe", { - skip_on_cran() - skip_if_offline() - - basic_path <- system.file("examples/DESCRIPTION_basic", - package = "cffr" - ) - - tmp <- tempfile("DESCRIPTION_basic") - # Create a temporary file - file.copy(basic_path, tmp) - - - # Get packages from my r-universe - dhh <- unlist(jsonlite::read_json( - "https://dieghernan.r-universe.dev/packages" - ))[1] - - - newpack <- desc::desc(tmp) - - oldtitle <- clean_str(newpack$get("Package")) - - newtitle <- desc::desc_set("Package", dhh, file = tmp) - - expect_false(oldtitle == clean_str(newtitle$get("Package"))) - - - # Configure to search on r-universe - newrepos <- c( - dieghernan = "https://dieghernan.r-universe.dev", - CRAN = "https://cloud.r-project.org" - ) - - runiverse <- as.data.frame(available.packages( - repos = newrepos - )) - - expect_equal( - search_on_repos(dhh, runiverse), - "https://dieghernan.r-universe.dev/" - ) - - - # Search now ggplot2, should be canonical url - - expect_equal( - search_on_repos("ggplot2", runiverse, newrepos), - "https://CRAN.R-project.org/package=ggplot2" - ) -}) - - -test_that("Validate keywords", { - desc_path <- system.file("examples/DESCRIPTION_basic", - package = "cffr" - ) - - tmp <- tempfile("DESCRIPTION_keyword") - - copy <- file.copy(desc_path, tmp) - - cffobj <- cff_create(tmp) - expect_null(cffobj$keywords) - - expect_true(cff_validate(cffobj, verbose = FALSE)) - - # Add keywords - silent <- desc::desc_set("X-schema.org-keywords", - "keyword1, keyword1, keyword3", - file = tmp - ) - cffobj2 <- cff_create(tmp) - expect_length(cffobj2$keywords, 2) - expect_equal(cffobj2$keywords, c("keyword1", "keyword3")) - expect_true(cff_validate(cffobj2, verbose = FALSE)) - - # Single keyword - silent <- desc::desc_set("X-schema.org-keywords", - "keyword1, keyword1", - file = tmp - ) - cffobj3 <- cff_create(tmp) - expect_length(cffobj3$keywords, 2) - expect_equal(cffobj3$keywords, c("keyword1", "r-package")) - expect_true(cff_validate(cffobj3, verbose = FALSE)) - - # NULL case keyword - silent <- desc::desc_set("X-schema.org-keywords", - "r-package", - file = tmp - ) - cffobj4 <- cff_create(tmp) - expect_null(cffobj4$keywords) - expect_true(cff_validate(cffobj4, verbose = FALSE)) -}) - - -test_that("Parse keywords from GH", { - skip_on_cran() - skip_if_offline() - skip_if( - nchar(Sys.getenv("GITHUB_TOKEN")) == 0, - "No GITHUB_TOKEN environment variable found" - ) - - desc_path <- system.file("examples/DESCRIPTION_basic", - package = "cffr" - ) - - tmp <- tempfile("DESCRIPTION_keyword_gh") - - copy <- file.copy(desc_path, tmp) - - cffobj <- cff_create(tmp) - expect_null(cffobj$keywords) - - expect_true(cff_validate(cffobj, verbose = FALSE)) - - # A site with no topics - silent <- desc::desc_set("BugReports", - "https://github.com/dieghernan/cfftest/issues", - file = tmp - ) - - cffobjnokeys <- cff_create(tmp) - expect_true(cff_validate(cffobjnokeys, verbose = FALSE)) - expect_null(cffobjnokeys$keywords) - - # Add keywords from url - silent <- desc::desc_set("URL", - "https://github.com/ropensci/cffr", - file = tmp - ) - - silent <- desc::desc_set("BugReports", - "https://github.com/ropensci/cffr/issues", - file = tmp - ) - - cffobj1 <- cff_create(tmp) - expect_true(cff_validate(cffobj1, verbose = FALSE)) - expect_false(is.null(cffobj1$keywords)) - - # Concatenate keywords of both sources - - # Add keywords - silent <- desc::desc_set("X-schema.org-keywords", - "keyword1", - file = tmp - ) - - cffobj2 <- cff_create(tmp) - expect_true(cff_validate(cffobj2, verbose = FALSE)) - - expect_true(length(cffobj2$keywords) > length(cffobj1$keywords)) -}) diff --git a/tests/testthat/test-cff_parse_citation.R b/tests/testthat/test-cff_parse_citation.R index 76d344fd..b6e85f44 100644 --- a/tests/testthat/test-cff_parse_citation.R +++ b/tests/testthat/test-cff_parse_citation.R @@ -206,3 +206,737 @@ test_that("NULL bibs and others strange errors", { bib <- NULL expect_null(cff_parse_citation(bib)) }) + +# Parse citation from BibTeX ---- + +test_that("Article", { + bib <- bibentry("Article", + key = "knuth:1984", + author = person("R Core Team"), + title = "Literate Programming", + journal = "The Computer Journal", + year = "1984", + # Optional + volume = "27", + number = 2, + pages = "97--111", + month = "January", + note = "Example modified for testing purposes" + ) + + bibparsed <- cff_parse_citation(bib) + expect_snapshot(bibparsed) + + cffobj <- cff_create(cff(), + keys = list(references = list(bibparsed)) + ) + + expect_true(cff_validate(cffobj, verbose = FALSE)) + + # Back to bibtex and check names + tobib <- as_bibentry(bibparsed) + + fld1 <- sort(names(unclass(bib)[[1]])) + fld2 <- sort(names(unclass(tobib)[[1]])) + expect_identical(fld1, fld2) +}) + + +test_that("Book", { + bib <- bibentry("Book", + key = "latex:companion", + author = "Frank Mittelbach and Michel Gossens + and Johannes Braams and David Carlisle + and Chris Rowley", + editor = "{Barnes and Noble}", + title = "The LaTeX Companion", + publisher = "Addison-Wesley Professional", + year = "2004", + # Optional + volume = "3", + number = 7, + series = "The LateX Books", + address = "Santa Monica", + edition = "Fourth", + month = "August", + note = "Example modified for testing purposes", + keywords = c("Two, keyword") + ) + + bibparsed <- cff_parse_citation(bib) + expect_snapshot(bibparsed) + + cffobj <- cff_create(cff(), + keys = list(references = list(bibparsed)) + ) + + expect_true(cff_validate(cffobj, verbose = FALSE)) + + # Back to bibtex and check names + tobib <- as_bibentry(bibparsed) + + fld1 <- sort(names(unclass(bib)[[1]])) + fld2 <- sort(names(unclass(tobib)[[1]])) + expect_identical(fld1, fld2) +}) + + +test_that("Booklet", { + bib <- bibentry("Booklet", + key = "Mustermann2016", + title = "Java Booklet", + # Optional + author = "Max Mustermann", + howpublished = "Internet", + address = "Stuttgart", + month = "feb", + year = "2016", + note = "Example modified from Jabref", + keywords = "java" + ) + + bibparsed <- cff_parse_citation(bib) + expect_snapshot(bibparsed) + + cffobj <- cff_create(cff(), + keys = list(references = list(bibparsed)) + ) + + expect_true(cff_validate(cffobj, verbose = FALSE)) + + # Back to bibtex and check names + tobib <- as_bibentry(bibparsed) + + fld1 <- sort(names(unclass(bib)[[1]])) + fld2 <- sort(names(unclass(tobib)[[1]])) + + # Keyword is not parsed + expect_identical(setdiff(fld1, fld2), "keywords") +}) + +test_that("Conference", { + bib <- bibentry("InProceedings", + key = "inproceedings-full", + author = "Alfred V. Oaho and Jeffrey D. Ullman and Mihalis Yannakakis", + title = "On Notions of Information Transfer in {VLSI} Circuits", + title = paste( + "{Statistical Machine Translation: Rapid Development", + "with Limited Resources" + ), + booktitle = "Proc. Fifteenth Annual ACM STOC", + year = "1983", + # Optional + editor = "Wizard V. Oz and Mihalis Yannakakis", + volume = "41", + number = 17, + series = "All ACM Conferences", + pages = "133--139", + address = "Boston", + organization = "ACM", + publisher = "Academic Press", + note = "Example modified for testing purposes" + ) + + # Hack to convert to conference + bib_un <- unclass(bib)[[1]] + + attr(bib_un, "bibtype") <- "Conference" + + bib <- list(bib_un) + class(bib) <- "bibentry" + + bibparsed <- cff_parse_citation(bib) + + expect_snapshot(bibparsed) + + cffobj <- cff_create(cff(), + keys = list(references = list(bibparsed)) + ) + + expect_true(cff_validate(cffobj, verbose = FALSE)) + + # Back to bibtex and check names + tobib <- as_bibentry(bibparsed) + + fld1 <- unique(sort(names(unclass(bib)[[1]]))) + fld2 <- unique(sort(names(unclass(tobib)[[1]]))) + + expect_identical(setdiff(fld1, fld2), "series") +}) + +test_that("InBook", { + bib <- bibentry("InBook", + key = "1326", + author = "A. King", + editor = "E Tick and G Succi", + title = "A Framework for Freeness Analysis", + chapter = "7, 14", + pages = "137--149", + publisher = "Kluwer Academic Publishers", + year = "1994", + # Optional + volume = "27", + number = 2, + series = "Implementations of Logic Programming Systems", + type = "A chapter", + address = "Dordrecht", + edition = "Second", + month = "January", + note = "Example modified for testing purposes" + ) + + bibparsed <- cff_parse_citation(bib) + expect_snapshot(bibparsed) + + cffobj <- cff_create(cff(), + keys = list(references = list(bibparsed)) + ) + + expect_true(cff_validate(cffobj, verbose = FALSE)) + # Back to bibtex and check names + tobib <- as_bibentry(bibparsed) + + fld1 <- unique(sort(names(unclass(bib)[[1]]))) + fld2 <- unique(sort(names(unclass(tobib)[[1]]))) + + expect_identical(setdiff(fld1, fld2), "type") +}) + +test_that("InCollection", { + bib <- bibentry("InCollection", + key = "Mihalcea:2006", + author = "Rada Mihalcea", + title = "Knowledge-Based Methods for {WSD}", + booktitle = "Word Sense Disambiguation: Algorithms + and Applications", + publisher = "Springer", + year = "2006", + # Optional + editor = "Eneko Agirre and Philip Edmonds", + volume = "23", + number = 3, + series = "The Word Sense Series", + type = "A random type", + chapter = "1,2,3", + pages = "24--57", + address = "107--132", + edition = "Third", + month = "August", + note = "A note" + ) + + bibparsed <- cff_parse_citation(bib) + expect_snapshot(bibparsed) + + cffobj <- cff_create(cff(), + keys = list(references = list(bibparsed)) + ) + + expect_true(cff_validate(cffobj, verbose = FALSE)) + + # Back to bibtex and check names + tobib <- as_bibentry(bibparsed) + + fld1 <- unique(sort(names(unclass(bib)[[1]]))) + fld2 <- unique(sort(names(unclass(tobib)[[1]]))) + + expect_identical(setdiff(fld1, fld2), c("series", "type")) +}) + +test_that("InProceedings", { + bib <- bibentry("InProceedings", + key = "inproceedings-full", + author = "Alfred V. Oaho and Jeffrey D. Ullman and Mihalis Yannakakis", + title = "On Notions of Information Transfer in {VLSI} Circuits", + title = paste( + "{Statistical Machine Translation: Rapid", + "Development with Limited Resources" + ), + booktitle = "Proc. Fifteenth Annual ACM STOC", + year = "1983", + # Optional + editor = "Wizard V. Oz and Mihalis Yannakakis", + volume = "41", + number = 17, + series = "All ACM Conferences", + pages = "133--139", + address = "Boston", + organization = "ACM", + publisher = "Academic Press", + note = "Example modified for testing purposes" + ) + + bibparsed <- cff_parse_citation(bib) + expect_snapshot(bibparsed) + + cffobj <- cff_create(cff(), + keys = list(references = list(bibparsed)) + ) + + expect_true(cff_validate(cffobj, verbose = FALSE)) + + # Back to bibtex and check names + tobib <- as_bibentry(bibparsed) + + fld1 <- unique(sort(names(unclass(bib)[[1]]))) + fld2 <- unique(sort(names(unclass(tobib)[[1]]))) + + expect_identical(setdiff(fld1, fld2), "series") +}) + +test_that("Manual", { + bib <- bibentry("Manual", + title = "A Language and Environment for Statistical Computing", + # Optional + author = person("R Core Team"), + organization = "R Foundation for Statistical Computing", + address = "Vienna, Austria", + edition = "Fourth", + month = "August", + year = "2021", + note = "Example modified for testing purposes" + ) + + bibparsed <- cff_parse_citation(bib) + expect_snapshot(bibparsed) + + cffobj <- cff_create(cff(), + keys = list(references = list(bibparsed)) + ) + + expect_true(cff_validate(cffobj, verbose = FALSE)) + + # Back to bibtex and check names + tobib <- as_bibentry(bibparsed) + + fld1 <- unique(sort(names(unclass(bib)[[1]]))) + fld2 <- unique(sort(names(unclass(tobib)[[1]]))) + + expect_identical(fld1, fld2) +}) + +test_that("MastersThesis", { + bib <- bibentry("MastersThesis", + key = "Master2006", + author = "Eesa Alsolami", + title = "An examination of keystroke dynamics + for continuous user authentication", + school = "Queensland University of Technology", + year = "2012", + # Optional + type = "Final thesis", + address = "Queensland, NZ", + month = "August", + note = "Example modified for testing purposes" + ) + bibparsed <- cff_parse_citation(bib) + expect_snapshot(bibparsed) + + cffobj <- cff_create(cff(), + keys = list(references = list(bibparsed)) + ) + + expect_true(cff_validate(cffobj, verbose = FALSE)) + + # Back to bibtex and check names + tobib <- as_bibentry(bibparsed) + + fld1 <- unique(sort(names(unclass(bib)[[1]]))) + fld2 <- unique(sort(names(unclass(tobib)[[1]]))) + + expect_identical(setdiff(fld1, fld2), "type") +}) + +test_that("Misc", { + bib <- bibentry("Misc", + # Optional + author = person("R Core Team"), + title = "A Language and Environment for Statistical Computing", + howpublished = "CD-ROM", + month = 1, + year = "2021", + note = "A note" + ) + + bibparsed <- cff_parse_citation(bib) + expect_snapshot(bibparsed) + + cffobj <- cff_create(cff(), + keys = list(references = list(bibparsed)) + ) + + expect_true(cff_validate(cffobj, verbose = FALSE)) + + # Back to bibtex and check names + tobib <- as_bibentry(bibparsed) + + fld1 <- unique(sort(names(unclass(bib)[[1]]))) + fld2 <- unique(sort(names(unclass(tobib)[[1]]))) + + expect_identical(fld1, fld2) +}) + + +test_that("PhdThesis", { + bib <- bibentry("PhdThesis", + author = "Eesa Alsolami", + title = "An examination of keystroke dynamics + for continuous user authentication", + school = "Queensland University of Technology", + year = "2012", + # Optional + type = "Final PhD thesis", + address = "Queensland, NZ", + month = "August", + note = "Example modified for testing purposes" + ) + + bibparsed <- cff_parse_citation(bib) + expect_snapshot(bibparsed) + + cffobj <- cff_create(cff(), + keys = list(references = list(bibparsed)) + ) + + expect_true(cff_validate(cffobj, verbose = FALSE)) + + # Back to bibtex and check names + tobib <- as_bibentry(bibparsed) + + fld1 <- unique(sort(names(unclass(bib)[[1]]))) + fld2 <- unique(sort(names(unclass(tobib)[[1]]))) + + expect_identical(setdiff(fld1, fld2), "type") +}) + + +test_that("Proceedings", { + bib <- bibentry("Proceedings", + title = "Proc. Fifteenth Annual STOC", + year = "1983", + # Optional + editor = "Wizard V. Oz and Mihalis Yannakakis", + volume = 1, + number = 17, + series = "All ACM Conferences", + address = "Boston, US", + month = "August", + organization = "The OX Association for Computing Machinery", + publisher = "Academic Press", + note = "Example modified for testing purposes" + ) + + bibparsed <- cff_parse_citation(bib) + expect_snapshot(bibparsed) + + cffobj <- cff_create(cff(), + keys = list(references = list(bibparsed)) + ) + + expect_true(cff_validate(cffobj, verbose = FALSE)) + + # Back to bibtex and check names + tobib <- as_bibentry(bibparsed) + + fld1 <- unique(sort(names(unclass(bib)[[1]]))) + fld2 <- unique(sort(names(unclass(tobib)[[1]]))) + + expect_identical(fld1, fld2) +}) + +test_that("TechReport", { + bib <- bibentry("TechReport", + author = "Matthew C. Jadud and Sally A. Fincher", + title = "Naive tools for studying compilation histories", + institution = "University of Kent Canterbury", + year = "2003", + # Optional + type = "techreport", + number = "3-03", + address = paste( + "Computing Laboratory, University of Kent,", + "Canterbury, Kent, CT2 7NF" + ), + month = "mar", + note = "Example modified for testing purposes" + ) + + bibparsed <- cff_parse_citation(bib) + expect_snapshot(bibparsed) + + cffobj <- cff_create(cff(), + keys = list(references = list(bibparsed)) + ) + + expect_true(cff_validate(cffobj, verbose = FALSE)) + + # Back to bibtex and check names + tobib <- as_bibentry(bibparsed) + + fld1 <- unique(sort(names(unclass(bib)[[1]]))) + fld2 <- unique(sort(names(unclass(tobib)[[1]]))) + + expect_identical(setdiff(fld1, fld2), "type") +}) + +test_that("Unpublished", { + bib <- bibentry("Unpublished", + author = "D. Kaplan", + title = "Demonstratives", + note = "Unpublished manuscript, UCLA", + # Optional + year = "1977", + month = "aug", + ) + + bibparsed <- cff_parse_citation(bib) + expect_snapshot(bibparsed) + + cffobj <- cff_create(cff(), + keys = list(references = list(bibparsed)) + ) + + expect_true(cff_validate(cffobj, verbose = FALSE)) + + # Back to bibtex and check names + tobib <- as_bibentry(bibparsed) + + fld1 <- unique(sort(names(unclass(bib)[[1]]))) + fld2 <- unique(sort(names(unclass(tobib)[[1]]))) + + expect_identical(fld1, fld2) +}) + +test_that("InBook with booktitle", { + bib <- bibentry("InBook", + title = "Bibliographies and citations", + year = "2020", + author = "Yihui Xie and Christophe Dervieux and Emily Riederer", + booktitle = "{R} Markdown Cookbook", + publisher = "Chapman and Hall/CRC", + address = "Boca Raton, Florida", + series = "The {R} Series", + isbn = "9780367563837", + url = "https://bookdown.org/yihui/rmarkdown-cookbook", + chapter = "4.5" + ) + + bibparsed <- cff_parse_citation(bib) + expect_snapshot(bibparsed) + + cffobj <- cff_create(cff(), + keys = list(references = list(bibparsed)) + ) + + expect_true(cff_validate(cffobj, verbose = FALSE)) + + # Should be an incollection now + res <- as_bibentry(bibparsed) + init_type <- attr(unclass(res)[[1]], "bibtype") + expect_identical(tolower(init_type), "incollection") + + # Back to bibtex and check names + tobib <- as_bibentry(bibparsed) + + fld1 <- unique(sort(names(unclass(bib)[[1]]))) + fld2 <- unique(sort(names(unclass(tobib)[[1]]))) + + expect_identical(setdiff(fld1, fld2), "series") +}) + +test_that("Test entry without author", { + bib <- bibentry("Proceedings", + editor = "Yolande Berbers and Willy Zwaenepoel", + title = "Proceedings of the 6th European Conference on Computer Systems", + booktitle = paste("Proceedings of the 6th European", " + Conference on Computer Systems"), + publisher = "ACM", + venue = "Leuven, Belgium", + month = "apr", + year = 2006, + isbn = "1-59593-322-02", + ) + + bibparsed <- cff_parse_citation(bib) + + expect_identical( + bibparsed$authors[[1]]$name, + "anonymous" + ) + + expect_snapshot(bibparsed) + + cffobj <- cff_create(cff(), + keys = list(references = list(bibparsed)) + ) + + expect_true(cff_validate(cffobj, verbose = FALSE)) +}) + + +test_that("Test entry without author but has a key", { + bib <- bibentry("Misc", + key = "I am the key", + title = "Proceedings of the 6th European Conference on Computer Systems", + booktitle = paste( + "Proceedings of the 6th European Conference", + "on Computer Systems" + ), + publisher = "ACM", + venue = "Leuven, Belgium", + month = "apr", + year = 2006, + isbn = "1-59593-322-02", + ) + + bibparsed <- cff_parse_citation(bib) + + expect_identical( + bibparsed$authors[[1]]$name, + "anonymous" + ) + + + expect_snapshot(bibparsed) + + cffobj <- cff_create(cff(), + keys = list(references = list(bibparsed)) + ) + + expect_true(cff_validate(cffobj, verbose = FALSE)) +}) + + +test_that("Test entry without author and key", { + bib <- bibentry("Misc", + title = "Proceedings of the 6th European Conference on Computer Systems", + booktitle = paste("Proceedings of the 6th European", " + Conference on Computer Systems"), + publisher = "ACM", + venue = "Leuven, Belgium", + month = "apr", + year = 2006, + isbn = "1-59593-322-02", + ) + + bibparsed <- cff_parse_citation(bib) + + expect_identical( + bibparsed$authors[[1]]$name, + "anonymous" + ) + + + expect_snapshot(bibparsed) + + cffobj <- cff_create(cff(), + keys = list(references = list(bibparsed)) + ) + + expect_true(cff_validate(cffobj, verbose = FALSE)) +}) + + +test_that("Skip misc without title", { + bib <- bibentry( + bibtype = "misc", + author = c(person("SHERPA/RoMEO")), + url = "http://www.sherpa.ac.uk/romeo/", + year = 2018 + ) + + expect_message(bibparsed <- cff_parse_citation(bib), "Skipping") + + expect_null(bibparsed) + + cffobj <- cff_create(cff(), + keys = list(references = list(bibparsed)) + ) + + expect_snapshot(cffobj) + + expect_true(cff_validate(cffobj, verbose = FALSE)) +}) + + +test_that("Skip misc without title, not skipping the good one", { + bib <- c( + bibentry( + bibtype = "misc", + author = c(person("SHERPA/RoMEO")), + url = "http://www.sherpa.ac.uk/romeo/", + year = 2018 + ), + bibentry( + bibtype = "misc", + title = "{rromeo}: An {R} Client for {SHERPA/RoMEO} {API}", + author = c( + person("Matthias", "Grenié"), + person("Hugo", "Gruson") + ), + year = 2019, + header = "To cite this package in publications, please use:", + url = "https://CRAN.R-project.org/package=rromeo" + ) + ) + + + + expect_message(bibparsed <- cff_parse_citation(bib), "SHERPA/RoMEO") + + expect_length(bibparsed, 2) + + expect_null(bibparsed[[1]]) + + expect_s3_class(bibparsed[[2]], "cff") + + cffobj <- cff_create(cff(), + keys = list(references = bibparsed) + ) + + expect_snapshot(cffobj) + + expect_equal( + cffobj$references[[1]]$title, + "rromeo: An R Client for SHERPA/RoMEO API" + ) + + expect_true(cff_validate(cffobj, verbose = FALSE)) +}) + + +test_that("Check extended BibLatex Fields", { + bib <- bibentry("Article", + author = "M. A. Kastenholz, and Philippe H. Hünenbergerb", + title = "Computation of methodology hyphen independent ionic solvation + free energies from molecular simulations", + journal = "J. Chem. Phys.", + year = 2006, + note = "Example modified for testing purposes", + pages = "55--65", + + # Additional BibLatex Fields + date = "2006-03-15", + file = "a_file.pdf", + issuetitle = "Semantic {3D} Media and Content", + translator = "Wicksteed, P. H. and Cornford, F. M.", + urldate = "2006-10-01", + pagetotal = 528, + abstract = "The computation of ionic solvation free energies from + atomistic simulations is a surprisingly difficult problem that + has found no satisfactory solution for more than 15 years.", + doi = "10.1063/1.2172593", + isbn = "0-816-52066-6", + issn = "0097-8493", + url = "http://www.ctan.org" + ) + + bibparsed <- cff_parse_citation(bib) + expect_snapshot(bibparsed) + + cffobj <- cff_create(cff(), + keys = list(references = list(bibparsed)) + ) + + expect_true(cff_validate(cffobj, verbose = FALSE)) +}) diff --git a/tests/testthat/test-cff_read_biblines.R b/tests/testthat/test-cff_read_bib_text.R similarity index 81% rename from tests/testthat/test-cff_read_biblines.R rename to tests/testthat/test-cff_read_bib_text.R index 541096e5..fbc1f847 100644 --- a/tests/testthat/test-cff_read_biblines.R +++ b/tests/testthat/test-cff_read_bib_text.R @@ -1,8 +1,8 @@ test_that("Errors and messages", { skip_if_not_installed("bibtex", "0.5.0") a_cff <- cff() - expect_snapshot(cff_read_biblines(a_cff), error = TRUE) - expect_snapshot(cff_read_biblines("a bad line"), error = TRUE) + expect_snapshot(cff_create_bib_text(a_cff), error = TRUE) + expect_snapshot(cff_create_bib_text("a bad line"), error = TRUE) }) test_that("Read lines", { @@ -27,7 +27,7 @@ test_that("Read lines", { }" ) - list <- cff_read_biblines(x) + list <- cff_create_bib_text(x) expect_s3_class(list, "cff") expect_length(list, 2) @@ -38,6 +38,6 @@ test_that("Read lines", { tmpbib <- tempfile(fileext = ".bib") writeLines(x, tmpbib) - expect_snapshot(fromfile <- cff_read_biblines(tmpbib)) + expect_snapshot(fromfile <- cff_create_bib_text(tmpbib)) expect_identical(fromfile, list) }) diff --git a/tests/testthat/test-deprecated.R b/tests/testthat/test-deprecated.R index 314a2adf..f4c758a9 100644 --- a/tests/testthat/test-deprecated.R +++ b/tests/testthat/test-deprecated.R @@ -1,7 +1,7 @@ test_that("cff_extract_to_bibtex", { a_cff <- cff_create("cffr") - current <- cff_to_bibentry(a_cff) + current <- as_bibentry(a_cff) expect_snapshot(old1 <- cff_extract_to_bibtex(a_cff)) expect_identical(current, old1) @@ -10,7 +10,7 @@ test_that("cff_extract_to_bibtex", { test_that("cff_to_bibtex", { a_cff <- cff_create("cffr") - current <- cff_to_bibentry(a_cff) + current <- as_bibentry(a_cff) expect_snapshot(old1 <- cff_to_bibtex(a_cff)) expect_identical(current, old1) @@ -33,7 +33,7 @@ test_that("cff_from_bibtex", { }" expect_snapshot(flines <- cff_from_bibtex(x)) - expect_identical(flines, cff_read_biblines(x)) + expect_identical(flines, cff_create_bib_text(x)) }) test_that("write_bib", { diff --git a/tests/testthat/test-bibtex-check-ruby.R b/tests/testthat/test-xtra-check-bibtex-ruby.R similarity index 85% rename from tests/testthat/test-bibtex-check-ruby.R rename to tests/testthat/test-xtra-check-bibtex-ruby.R index 139c2a69..f49ceed3 100644 --- a/tests/testthat/test-bibtex-check-ruby.R +++ b/tests/testthat/test-xtra-check-bibtex-ruby.R @@ -7,7 +7,7 @@ test_that("preferred-citation-book-missing", { package = "cffr" ) - expect_snapshot(cff_to_bibentry(x)) + expect_snapshot(as_bibentry(x)) }) test_that("preferred-citation-book", { @@ -15,7 +15,7 @@ test_that("preferred-citation-book", { package = "cffr" ) - bib <- cff_to_bibentry(x) + bib <- as_bibentry(x) expect_snapshot(toBibtex(bib)) }) @@ -24,7 +24,7 @@ test_that("preferred-citation-conference-paper-2", { package = "cffr" ) - bib <- cff_to_bibentry(x) + bib <- as_bibentry(x) expect_snapshot(toBibtex(bib)) }) @@ -33,7 +33,7 @@ test_that("preferred-citation-conference-paper-missing", { package = "cffr" ) - bib <- cff_to_bibentry(x) + bib <- as_bibentry(x) expect_snapshot(toBibtex(bib)) }) @@ -42,7 +42,7 @@ test_that("preferred-citation-conference-paper", { package = "cffr" ) - bib <- cff_to_bibentry(x) + bib <- as_bibentry(x) expect_snapshot(toBibtex(bib)) }) @@ -51,7 +51,7 @@ test_that("preferred-citation-manual", { package = "cffr" ) - bib <- cff_to_bibentry(x) + bib <- as_bibentry(x) expect_snapshot(toBibtex(bib)) }) @@ -60,7 +60,7 @@ test_that("preferred-citation-no-month", { package = "cffr" ) - bib <- cff_to_bibentry(x) + bib <- as_bibentry(x) expect_snapshot(toBibtex(bib)) }) @@ -69,7 +69,7 @@ test_that("preferred-citation-no-vol", { package = "cffr" ) - bib <- cff_to_bibentry(x) + bib <- as_bibentry(x) expect_snapshot(toBibtex(bib)) }) @@ -79,7 +79,7 @@ test_that("preferred-citation-pamphlet", { package = "cffr" ) - bib <- cff_to_bibentry(x) + bib <- as_bibentry(x) expect_snapshot(toBibtex(bib)) }) @@ -88,7 +88,7 @@ test_that("preferred-citation-report-no-institution", { package = "cffr" ) - bib <- cff_to_bibentry(x) + bib <- as_bibentry(x) expect_snapshot(toBibtex(bib)) }) @@ -97,7 +97,7 @@ test_that("preferred-citation-report", { package = "cffr" ) - bib <- cff_to_bibentry(x) + bib <- as_bibentry(x) expect_snapshot(toBibtex(bib)) }) @@ -107,7 +107,7 @@ test_that("preferred-citation-unpublished", { package = "cffr" ) - bib <- cff_to_bibentry(x) + bib <- as_bibentry(x) expect_snapshot(toBibtex(bib)) }) @@ -116,7 +116,7 @@ test_that("reprozip", { package = "cffr" ) - bib <- cff_to_bibentry(x) + bib <- as_bibentry(x) expect_snapshot(toBibtex(bib)) }) @@ -126,7 +126,7 @@ test_that("smith-et-al", { package = "cffr" ) - bib <- cff_to_bibentry(x) + bib <- as_bibentry(x) expect_snapshot(toBibtex(bib)) }) @@ -135,6 +135,6 @@ test_that("tidyverse-joss-paper", { package = "cffr" ) - bib <- cff_to_bibentry(x) + bib <- as_bibentry(x) expect_snapshot(toBibtex(bib)) }) diff --git a/vignettes/bibtex_cff.Rmd b/vignettes/bibtex_cff.Rmd index 04f621dc..78512e50 100644 --- a/vignettes/bibtex_cff.Rmd +++ b/vignettes/bibtex_cff.Rmd @@ -135,8 +135,8 @@ capabilities that can be used to read and transform **BibTeX** format into different formats: - `cff_read_bib()` reads \*.bib files. -- `cff_read_biblines()` can read **BibTeX** entries that are already stored in - a variable. +- `cff_create_bib_text()` can read **BibTeX** entries that are already stored + in a variable. - A S3 method `toBibtex.cff()` that converts from ```{r cffbibread, comment="#>"} @@ -150,7 +150,7 @@ string <- "@book{einstein1921, # To cff library(cffr) -cff_format <- cff_read_biblines(string) +cff_format <- cff_create_bib_text(string) cff_format @@ -572,13 +572,13 @@ bib <- "@article{article-full, pages = {73+}, note = {This is a full ARTICLE entry}}" -cff_read_biblines(bib) +cff_create_bib_text(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r echo=FALSE} -toBibtex(cff_read_biblines(bib)) +toBibtex(cff_create_bib_text(bib)) ``` ### \@book / \@inbook {#book-inbook} @@ -654,13 +654,13 @@ bib <- "@book{book-full, edition = {Second} }" -cff_read_biblines(bib) +cff_create_bib_text(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r, echo=FALSE} -toBibtex(cff_read_biblines(bib)) +toBibtex(cff_create_bib_text(bib)) ``` **Examples: \@inbook** @@ -704,13 +704,13 @@ bib <- "@inbook{inbook-full, chapter = {1.2} }" -cff_read_biblines(bib) +cff_create_bib_text(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r echo=FALSE,} -toBibtex(cff_read_biblines(bib)) +toBibtex(cff_create_bib_text(bib)) ``` ### \@booklet {#booklet} @@ -763,13 +763,13 @@ bib <- "@booklet{booklet-full, howpublished = {Vernier Art Center} }" -cff_read_biblines(bib) +cff_create_bib_text(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r echo=FALSE, } -toBibtex(cff_read_biblines(bib)) +toBibtex(cff_create_bib_text(bib)) ``` ### \@conference / \@inproceedings {#conf_inproc} @@ -834,13 +834,13 @@ bib <- "@inproceedings{inproceedings-full, organization = {The OX Association for Computing Machinery} }" -cff_read_biblines(bib) +cff_create_bib_text(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r echo=FALSE,} -toBibtex(cff_read_biblines(bib)) +toBibtex(cff_create_bib_text(bib)) ``` ### \@incollection {#incol} @@ -915,13 +915,13 @@ bib <- "@incollection{incollection-full, edition = {Third} }" -cff_read_biblines(bib) +cff_create_bib_text(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r echo=FALSE,} -toBibtex(cff_read_biblines(bib)) +toBibtex(cff_create_bib_text(bib)) ``` ### \@manual @@ -980,13 +980,13 @@ bib <- "@manual{manual-full, edition = {Silver} }" -cff_read_biblines(bib) +cff_create_bib_text(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r echo=FALSE,} -toBibtex(cff_read_biblines(bib)) +toBibtex(cff_create_bib_text(bib)) ``` ### \@mastersthesis / \@phdthesis @@ -1049,13 +1049,13 @@ bib <- "@mastersthesis{mastersthesis-full, type = {Master's project} }" -cff_read_biblines(bib) +cff_create_bib_text(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r, echo=FALSE} -toBibtex(cff_read_biblines(bib)) +toBibtex(cff_create_bib_text(bib)) ``` **Examples: \@phdthesis** @@ -1089,13 +1089,13 @@ bib <- "@phdthesis{phdthesis-full, type = {{PhD} Dissertation} }" -cff_read_biblines(bib) +cff_create_bib_text(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r echo=FALSE,} -toBibtex(cff_read_biblines(bib)) +toBibtex(cff_create_bib_text(bib)) ``` ### \@misc @@ -1152,13 +1152,13 @@ bib <- "@misc{misc-full, howpublished = {Handed out at O'Hare} }" -cff_read_biblines(bib) +cff_create_bib_text(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r echo=FALSE,} -toBibtex(cff_read_biblines(bib)) +toBibtex(cff_create_bib_text(bib)) ``` ### \@proceedings @@ -1226,13 +1226,13 @@ bib <- "@proceedings{proceedings-full, organization = {The OX Association for Computing Machinery} }" -cff_read_biblines(bib) +cff_create_bib_text(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r echo=FALSE,} -toBibtex(cff_read_biblines(bib)) +toBibtex(cff_create_bib_text(bib)) ``` ### \@techreport @@ -1289,13 +1289,13 @@ bib <- "@techreport{techreport-full, type = {Wishful Research Result} }" -cff_read_biblines(bib) +cff_create_bib_text(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r echo=FALSE,} -toBibtex(cff_read_biblines(bib)) +toBibtex(cff_create_bib_text(bib)) ``` ### \@unpublished @@ -1340,13 +1340,13 @@ bib <- "@unpublished{unpublished-minimal, note = {Talk at Fanstord University (this is a minimal UNPUBLISHED entry)} }" -cff_read_biblines(bib) +cff_create_bib_text(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r echo=FALSE,} -toBibtex(cff_read_biblines(bib)) +toBibtex(cff_create_bib_text(bib)) ``` ## Appendix A: **\@inbook** in BibTeX and BibLaTeX {#appendix_inbook} @@ -1413,13 +1413,13 @@ bib <- "@inbook{inbook-biblatex, chapter = {4.5} }" -cff_read_biblines(bib) +cff_create_bib_text(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r echo=FALSE,} -toBibtex(cff_read_biblines(bib)) +toBibtex(cff_create_bib_text(bib)) ``` ## Appendix B: [CFF key:type]{.underline} values {#appendix_cff_type} diff --git a/vignettes/cffr.Rmd b/vignettes/cffr.Rmd index 030c065a..413617a0 100644 --- a/vignettes/cffr.Rmd +++ b/vignettes/cffr.Rmd @@ -1,5 +1,5 @@ --- -title: "Manipulating Citations with cffr" +title: "Manipulating Citations with **cffr**" description: > Learn how to modify `cff` objects. output: @@ -8,7 +8,7 @@ output: bibliography: REFERENCES.bib link-citations: yes vignette: > - %\VignetteIndexEntry{Manipulating Citations with cffr} + %\VignetteIndexEntry{Manipulating Citations with **cffr**} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- From 3192a404a7c00606f97864f5c201c027d2813c96 Mon Sep 17 00:00:00 2001 From: Diego H Date: Thu, 29 Feb 2024 23:08:12 +0100 Subject: [PATCH 21/32] Add cff_create_cff_person --- NAMESPACE | 4 +- NEWS.md | 14 +- R/as_bibentry.R | 65 ++--- R/cff-methods.R | 2 +- R/cff.R | 2 +- R/cff_create.R | 12 +- R/cff_create_cff_person.R | 99 +++---- R/cff_parse_citation.R | 6 +- R/cff_parse_person.R | 238 ---------------- R/cff_read_bib_text.R | 6 +- R/deprecated.R | 93 ++++++- R/parse_citation.R | 13 +- R/utils-methods.R | 25 +- R/utils-persons.R | 53 +--- R/utils-read-description.R | 4 +- README.md | 6 +- codemeta.json | 2 +- man/as_bibentry.Rd | 6 +- man/cff.Rd | 2 +- man/cff_create.Rd | 12 +- ...ate_person.Rd => cff_create_cff_person.Rd} | 32 +-- man/cff_parse_citation.Rd | 3 - man/cff_parse_person.Rd | 82 ------ man/cff_read.Rd | 4 +- ...reate_bib_text.Rd => cff_read_bib_text.Rd} | 10 +- man/cff_write_misc.Rd | 2 +- .../{cff_create_person.Rmd => person.Rmd} | 4 +- man/deprecated_cff_from_bib.Rd | 9 +- man/deprecated_cff_person.Rd | 75 +++++ man/deprecated_cff_to_bib.Rd | 3 +- man/deprecated_write.Rd | 5 +- man/encoded_utf_to_latex.Rd | 2 +- tests/testthat/_snaps/cff-methods.md | 38 +-- .../testthat/_snaps/cff_create_cff_person.md | 97 +++++++ tests/testthat/_snaps/cff_parse_person.md | 72 ----- tests/testthat/_snaps/cff_read_bib_text.md | 8 +- .../testthat/_snaps/cff_write_misc/CITAT_ION | 3 +- tests/testthat/_snaps/deprecated.md | 20 +- tests/testthat/_snaps/utils-persons.md | 260 +++++++++--------- tests/testthat/test-as_bibentry.R | 4 +- tests/testthat/test-cff-methods.R | 4 +- tests/testthat/test-cff_create_cff_person.R | 188 +++++++++++++ tests/testthat/test-cff_parse_person.R | 161 ----------- tests/testthat/test-cff_read_bib_text.R | 8 +- tests/testthat/test-cff_write.R | 22 +- tests/testthat/test-deprecated.R | 17 +- tests/testthat/test-utils-persons.R | 90 +++--- vignettes/bibtex_cff.Rmd | 65 ++--- vignettes/cffr.Rmd | 5 +- 49 files changed, 906 insertions(+), 1051 deletions(-) delete mode 100644 R/cff_parse_person.R rename man/{cff_create_person.Rd => cff_create_cff_person.Rd} (79%) delete mode 100644 man/cff_parse_person.Rd rename man/{cff_create_bib_text.Rd => cff_read_bib_text.Rd} (90%) rename man/chunks/{cff_create_person.Rmd => person.Rmd} (82%) create mode 100644 man/deprecated_cff_person.Rd create mode 100644 tests/testthat/_snaps/cff_create_cff_person.md delete mode 100644 tests/testthat/_snaps/cff_parse_person.md create mode 100644 tests/testthat/test-cff_create_cff_person.R delete mode 100644 tests/testthat/test-cff_parse_person.R diff --git a/NAMESPACE b/NAMESPACE index 066e9f38..f36d34a7 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -11,8 +11,7 @@ export(as.cff) export(as_bibentry) export(cff) export(cff_create) -export(cff_create_bib_text) -export(cff_create_person) +export(cff_create_cff_person) export(cff_extract_to_bibtex) export(cff_from_bibtex) export(cff_gha_update) @@ -23,6 +22,7 @@ export(cff_parse_person) export(cff_parse_person_bibtex) export(cff_read) export(cff_read_bib) +export(cff_read_bib_text) export(cff_read_cff_citation) export(cff_read_citation) export(cff_read_description) diff --git a/NEWS.md b/NEWS.md index a9d84572..70bfcf12 100644 --- a/NEWS.md +++ b/NEWS.md @@ -14,17 +14,17 @@ to non-core functions** that an user would rarely call, while `cff_create()`, modified: - The conversion from `cff` to `bibentry` is performed now by a new function - `as_bibentry()`. Previous names of this function were `cff_to_bibtex()` - and `cff_extract_to_bibtex()` that are now superseded. - + `as_bibentry()`. Previous names of this function were `cff_to_bibtex()` and + `cff_extract_to_bibtex()` that are now deprecated. - Now reading from external files is performed exclusively by `cff_read()` and additionally by the more-specific new functions `cff_read_cff_citation()`, `cff_read_description()`, `cff_read_citation()` and `cff_read_bib()`. It is - also possible to read BibTeX lines with `cff_create_bib_text()`. Previous - function `cff_from_bibtex()` is now superseded. - -- `write_bib()` and `write_citation()` superseded by `cff_write_bib()` and + also possible to read BibTeX lines with `cff_read_bib_text()`. Previous + function `cff_from_bibtex()` is now deprecated. +- `write_bib()` and `write_citation()` deprecated by `cff_write_bib()` and `cff_write_citation()`. +- `cff_parse_person()` and `cff_parse_person_bibtex()` are deprecated. Use + `cff_create_cff_person()` instead. ### Methods diff --git a/R/as_bibentry.R b/R/as_bibentry.R index 8504f736..d7e3b553 100644 --- a/R/as_bibentry.R +++ b/R/as_bibentry.R @@ -252,23 +252,8 @@ cff_bibtex_parser <- function(x) { # author---- aut <- x$authors - - author <- lapply(aut, function(y) { - if ("name" %in% names(y)) { - # Person protected on family - person(family = clean_str(y$name)) - } else { - person( - given = clean_str(y$`given-names`), - family = clean_str(paste( - clean_str(y$`name-particle`), - clean_str(y$`family-names`), - clean_str(y$`name-suffix`) - )) - ) - } - }) - tobibentry$author <- do.call(c, author) + author <- as.person(aut) + tobibentry$author <- author # booktitle ---- @@ -292,27 +277,8 @@ cff_bibtex_parser <- function(x) { # editor---- # Same case than authors - editors <- x["editors"][[1]] - - if (!is.null(editors)) { - # Same - editor <- lapply(editors, function(y) { - if (!is.null(y$name)) { - # Person protected on family - person(family = clean_str(y$name)) - } else { - person( - given = clean_str(y$`given-names`), - family = clean_str(paste( - clean_str(y$`name-particle`), - clean_str(y$`family-names`), - clean_str(y$`name-suffix`) - )) - ) - } - }) - tobibentry$editor <- do.call(c, editor) - } + editors <- as.person(x$editors) + tobibentry$editor <- editors # howpublished---- @@ -336,6 +302,7 @@ cff_bibtex_parser <- function(x) { "inproceedings", "proceedings", "manual" )) { + # Just name tobibentry$organization <- x$institution$name } else { tobibentry$institution <- x$institution$name @@ -350,8 +317,21 @@ cff_bibtex_parser <- function(x) { # journal---- tobibentry$journal <- x$journal - # key First two given of author and year---- - aut_sur <- lapply(tobibentry$author$family[1:2], clean_str) + # key: First two given of author and year---- + # Bear in mind institutions has only given + # Use the first two authors + aut_sur <- lapply(tobibentry$author[1:2], function(z) { + unz <- unlist(z) + if ("family" %in% names(unz)) { + r <- unz["family"] + return(clean_str(r)) + } + + r <- unz["given"] + return(clean_str(r)) + }) + + aut_sur <- tolower(paste0(unlist(aut_sur), collapse = "")) aut_sur <- gsub("\\s*", "", aut_sur) @@ -491,6 +471,7 @@ cff_bibtex_parser <- function(x) { tobibentry$urldate <- x$`date-accessed` tobibentry$version <- x$version # Translators + trns <- x$translators trnsbib <- lapply(trns, function(y) { @@ -512,10 +493,6 @@ cff_bibtex_parser <- function(x) { tobibentry$translator <- paste(unlist(trnsbib), collapse = " and ") - - - - # sort ---- # based on default by # https://flamingtempura.github.io/bibtex-tidy/ diff --git a/R/cff-methods.R b/R/cff-methods.R index 3caeea37..81a80f02 100644 --- a/R/cff-methods.R +++ b/R/cff-methods.R @@ -44,7 +44,7 @@ as.data.frame.cff <- function(x, row.names = NULL, optional = FALSE, ...) { return(the_df) } -#' @rdname cff_create_person +#' @rdname cff_create_cff_person #' @name as.person.cff #' @order 2 #' diff --git a/R/cff.R b/R/cff.R index 0a66e9f2..791e9d8b 100644 --- a/R/cff.R +++ b/R/cff.R @@ -52,7 +52,7 @@ #' test <- cff( #' title = "Manipulating files", #' keywords = c("A", "new", "list", "of", "keywords"), -#' authors = list(cff_parse_person("New author")) +#' authors = cff_create_cff_person("New author") #' ) #' test #' \donttest{ diff --git a/R/cff_create.R b/R/cff_create.R index 2e14f2b7..6f3367a4 100644 --- a/R/cff_create.R +++ b/R/cff_create.R @@ -90,7 +90,7 @@ #' message = "This overwrites fields", #' abstract = "New abstract", #' keywords = c("A", "new", "list", "of", "keywords"), -#' authors = list(cff_parse_person("New author")) +#' authors = cff_create_cff_person("New author") #' ) #' #' cff_create(demo_file, keys = newkeys) @@ -102,12 +102,10 @@ #' #' new_contact <- append( #' old$contact, -#' list( -#' cff_parse_person(person( -#' given = "I am", -#' family = "New Contact" -#' )) -#' ) +#' cff_create_cff_person(person( +#' given = "I am", +#' family = "New Contact" +#' )) #' ) #' #' diff --git a/R/cff_create_cff_person.R b/R/cff_create_cff_person.R index 613d3516..19681508 100644 --- a/R/cff_create_cff_person.R +++ b/R/cff_create_cff_person.R @@ -13,7 +13,7 @@ #' #' ``` #' -#' [cff_create_person()] can convert the following objects: +#' [cff_create_cff_person()] can convert the following objects: #' - Objects with class `person` as provided by [utils::person()]. #' - A `character` string with the definition of an author or several authors, #' using the standard BibTeX notation. See Markey (2007) for a full @@ -23,8 +23,8 @@ #' Examples in `vignette("cffr", "cffr")` and [utils::person()]. #' #' @export -#' @rdname cff_create_person -#' @name cff_create_person +#' @rdname cff_create_cff_person +#' @name cff_create_cff_person #' @order 1 #' #' @family coercing @@ -35,8 +35,8 @@ #' See **Examples**. #' #' @return -#' `cff_create_person()` returns A list of persons or entities with class `cff` -#' converted to the +#' `cff_create_cff_person()` returns A list of persons or entities with class +#' `cff` converted to the #' ```{r, echo=FALSE, results='asis'} #' #' cat(paste0(" [Citation File Format schema]", @@ -48,7 +48,7 @@ #' #' @details #' -#' `cff_create_person()` uses a custom algorithm that tries to break a name +#' `cff_create_cff_person()` uses a custom algorithm that tries to break a name #' as explained in Section 11 of "Tame the BeaST" (Markey, 2007): #' - `First von Last` #' - `von Last, First` @@ -60,10 +60,10 @@ #' In the case of entities, the whole `character` would be mapped to `name`. #' It is a good practice to "protect" entity's names with `{}`: #' -#' ```{r child = "man/chunks/cff_create_person.Rmd"} +#' ```{r child = "man/chunks/person.Rmd"} #' ``` -#' `cff_create_person()` would try to add as many information as possible. On -#' `character` string coming from [`format(person())`][utils::person()] the +#' `cff_create_cff_person()` would try to add as many information as possible. +#' On `character` string coming from [`format(person())`][utils::person()] the #' email and the ORCID would be gathered as well. #' #' @references @@ -90,7 +90,7 @@ #' #' a_person #' -#' cff_person <- cff_create_person(a_person) +#' cff_person <- cff_create_cff_person(a_person) #' #' cff_person #' @@ -102,27 +102,31 @@ #' "Julio Iglesias ", #' "()" #' ) -#' cff_create_person(a_str) +#' cff_create_cff_person(a_str) #' #' # Several persons #' persons <- c(person("Clark", "Kent"), person("Lois", "Lane")) #' -#' cff_create_person(persons) +#' cff_create_cff_person(persons) #' #' # Or you can use BibTeX style if you prefer #' #' x <- "Frank Sinatra and Dean Martin and Davis, Jr., Sammy and Joey Bishop" #' -#' cff_create_person(x) +#' cff_create_cff_person(x) #' -#' cff_create_person("Herbert von Karajan") -cff_create_person <- function(person) { +#' cff_create_cff_person("Herbert von Karajan") +cff_create_cff_person <- function(person) { + if (any(is.null(person), is.na(person), length(person) == 0)) { + return(NULL) + } + hint <- guess_hint(person) verbopt <- getOption("cffr_message_verbosity", "none") if (verbopt == "debug") { cli::cli_alert_info( - "In {.fn cff_create_person} using internal for {.val {hint}}." + "In {.fn cff_create_cff_person} using internal for {.val {hint}}." ) } @@ -141,26 +145,9 @@ cff_create_person <- function(person) { the_obj } -guess_hint <- function(person) { - # On length 0 use "person" - if (length(person) == 0) { - return("person") - } - if (inherits(person, "person")) { - return("person") - } - - # Rest of cases "txt" - return("txt") -} - create_person_from_r <- function(person) { person <- as.person(person) - if (length(person) == 0) { - return(NULL) - } - # Special case for Bioconductor if (is_substring(person$given, "Bioconductor")) { @@ -250,6 +237,14 @@ create_person_from_txt <- function(as_bib_text) { parsed_comments <- list() } + # Special case for Bioconductor + if (is_substring(tolower(person_only), "bioconductor")) { + person_only <- paste0("{", person_only, "}") + } + # Special case for R Core Team + if (is_substring(tolower(person_only), "r core")) { + person_only <- paste0("{", person_only, "}") + } # Now extract structure for person_only string @@ -315,6 +310,16 @@ create_person_from_txt <- function(as_bib_text) { parsed_person } +guess_hint <- function(person) { + if (inherits(person, "person")) { + return("person") + } + + # Rest of cases "txt" + return("txt") +} + + split_txt_persons <- function(person) { person <- trimws(person) person <- paste0(person, collapse = " and ") @@ -425,30 +430,16 @@ extract_person_comments <- function(person) { # Add also email # Check if several mails (MomTrunc 6.0) - valid_emails <- unlist(lapply(person$email, is_email)) - email <- person$email[valid_emails][1] + look_emails <- c(unlist(person$email), parsed_comments$email) + valid_emails <- unlist(lapply(look_emails, is_email)) + email <- look_emails[valid_emails][1] # Final list - fin_list <- c(list(email = NULL), parsed_comments) + fin_list <- c( + list(email = NULL), + parsed_comments["email" != names(parsed_comments)] + ) fin_list$email <- clean_str(email) fin_list } - - -validate_cff_person_fields <- function(parsed_person) { - # Entity of person - - # Guess entity or person - is_entity <- as.character("name" %in% names(parsed_person)) - - # Keep only valid tags - Would depend on entity or person - definition <- switch(is_entity, - "TRUE" = cff_schema_definitions_entity(), - cff_schema_definitions_person() - ) - - parsed_person <- parsed_person[names(parsed_person) %in% definition] - - parsed_person -} diff --git a/R/cff_parse_citation.R b/R/cff_parse_citation.R index e2b1914b..eed68a52 100644 --- a/R/cff_parse_citation.R +++ b/R/cff_parse_citation.R @@ -123,9 +123,7 @@ cff_parse_citation <- function(bib) { } ## authors ---- - parse_all_authors <- drop_null( - lapply(parsed_fields$authors, cff_parse_person) - ) + parse_all_authors <- cff_create_cff_person(parsed_fields$authors) parsed_fields$authors <- unique(parse_all_authors) ## other persons---- @@ -315,7 +313,7 @@ parse_bibtex_fields <- function(parse_cit) { loc <- parse_cit$location - if (!is.null(loc)) parse_cit$location <- person(family = loc) + if (!is.null(loc)) parse_cit$location <- loc diff --git a/R/cff_parse_person.R b/R/cff_parse_person.R deleted file mode 100644 index bbf973e8..00000000 --- a/R/cff_parse_person.R +++ /dev/null @@ -1,238 +0,0 @@ -#' Parse a person to `cff` -#' -#' @description -#' Parse a person or string to a valid format for a `CITATION.cff` file. This -#' is a helper function designed to help on adding or replacing the -#' auto-generated authors of the package. -#' -#' @seealso [cff_create()], `vignette("cffr", "cffr")`, [utils::person()] -#' -#' @export -#' -#' @family parsers -#' -#' @param person A `person` object created with [person()] or a -#' character string. See **Details**. -#' -#' @return A [`cff`] object ready to be used on [cff_create()]. -#' -#' @details -#' The `person` parameter of the function could be: -#' -#' * For `cff_parse_person()`: A `person` object or a character coercible to -#' `person`. See [person()] for details. -#' * For `cff_parse_person_bibtex()`: A string with the definition of an author -#' or several authors, using the standard BibTeX notation. See Markey (2007) -#' for a full explanation. -#' -#' See **Examples** for more information. -#' -#' @references -#' - Patashnik, Oren. "BIBTEXTING" February 1988. -#' . -#' -#' - Markey, Nicolas. "Tame the BeaST." -#' *The B to X of BibTeX, Version 1.4* (October 2007). -#' . -#' -#' @examples -#' # Parse a person object -#' -#' cff_parse_person(person( -#' given = "First", -#' family = "Author", -#' role = c("aut", "cre"), -#' email = "first.last@example.com", -#' comment = c( -#' ORCID = "0000-0001-8457-4658", -#' affiliation = "An affiliation" -#' ) -#' )) -#' -#' # Parse a string -#' -#' cff_parse_person("Julio Iglesias ") -#' -#' # Several persons -#' persons <- c(person("Clark", "Kent"), person("Lois", "Lane")) -#' -#' cff_parse_person(persons) -cff_parse_person <- function(person) { - person <- as.person(person) - - if (length(person) == 0) { - return(NULL) - } - - if (length(person) > 1) { - person <- lapply(person, cff_parse_person) - class(person) <- "cff" - return(person) - } - - # Special case for Bioconductor - - if (is_substring(person$given, "Bioconductor")) { - person <- person( - given = paste( - clean_str(person$given), - clean_str(person$family) - ), - email = person$email, - role = person$role, - comment = person$comment - ) - } - - # Special case for R Core Team - if (all( - is_substring(clean_str(person$given), "R Core"), - is_substring(person$family, "Team") - )) { - person <- person( - given = paste( - clean_str(person$given), - clean_str(person$family) - ), - email = person$email, - role = person$role, - comment = person$comment - ) - } - - # Guess if entity of person. - is_entity <- is.null(person$family) || is.null(person$given) - - # Create my list - parsed_person <- list() - - if (is_entity) { - parsed_person$name <- clean_str(c(person$family, person$given)) - } else { - parsed_person$"family-names" <- clean_str(person$family) - parsed_person$"given-names" <- clean_str(person$given) - } - - # Check if several mails (MomTrunc 6.0) - valid_emails <- unlist(lapply(person$email, is_email)) - email <- person$email[valid_emails][1] - parsed_person$email <- clean_str(email) - - # Extract from comments - parsed_comments <- as.list(person$comment) - names(parsed_comments) <- tolower(names(parsed_comments)) - - # Extract for comments only what is not already there - parsed_comments <- parsed_comments[setdiff( - names(parsed_comments), - names(parsed_person) - )] - - - # Add url to orcid if not present - # Parse leading invalid urls - - if (!is.null(parsed_comments$orcid)) { - orcid <- gsub("^orcid.org/", "", parsed_comments$orcid) - orcid <- gsub("^https://orcid.org/", "", orcid) - orcid <- gsub("^http://orcid.org/", "", orcid) - - parsed_comments$orcid <- paste0( - "https://orcid.org/", - orcid - ) - } - - # Add website - web <- parsed_comments$website - - if (!is.null(web)) { - parsed_comments$website <- clean_str(web[is_url(web)]) - } - - # Add comments - parsed_person <- c(parsed_person, parsed_comments) - - - - # Keep only valid tags - Would depend on entity or person - definition <- if (is_entity) { - cff_schema_definitions_entity() - } else { - cff_schema_definitions_person() - } - parsed_person <- parsed_person[names(parsed_person) %in% definition] - - parsed_person <- as.cff(parsed_person) - parsed_person -} - -#' @rdname cff_parse_person -#' -#' @export -#' -#' @examples -#' -#' # Or you can use BibTeX style if you prefer -#' -#' x <- "Frank Sinatra and Dean Martin and Davis, Jr., Sammy and Joey Bishop" -#' -#' cff_parse_person_bibtex(x) -#' -#' cff_parse_person_bibtex("Herbert von Karajan") -cff_parse_person_bibtex <- function(person) { - person <- trimws(person) - - # Protect and on brackets - # Lower - protected <- gsub("(and)(?![^\\}]*(\\{|$))", "@nd@", - person, - perl = TRUE - ) - - # upper - protected <- gsub("AND(?![^\\}]*(\\{|$))", "@ND@", - protected, - perl = TRUE - ) - - - auths <- unlist(strsplit(protected, " and | AND ")) - - # Unprotec - auths_un <- gsub("@nd@", "and", auths) - auths_un <- gsub("@ND@", "AND", auths_un) - - - bibtex_auths <- lapply(auths_un, as_person_bibtex) - - - end <- lapply(bibtex_auths, function(x) { - if (is.null(x$given)) { - ent <- c(x$von, x$family, x$jr) - ent <- clean_str(paste(ent, collapse = " ")) - - l <- list(name = ent) - l <- as.cff(l) - } else { - l <- list( - "family-names" = x$family, - "given-names" = x$given, - "name-particle" = x$von, - "name-suffix" = x$jr - ) - - l <- as.cff(l) - } - }) - - # Handle single author - if (length(end) == 1) { - end <- end[[1]] - } else { - # Need the class - class(end) <- "cff" - } - - return(end) -} diff --git a/R/cff_read_bib_text.R b/R/cff_read_bib_text.R index b33173dc..e8bb75d7 100644 --- a/R/cff_read_bib_text.R +++ b/R/cff_read_bib_text.R @@ -1,4 +1,4 @@ -#' Create a [`cff`][cff-class] object from BibTeX lines +#' Read BibTeX markup as a [`cff`][cff-class] object #' #' @description #' Convert a [`character`][character()] representing a BibTeX entry to a @@ -50,9 +50,9 @@ #' ) #' #' -#' cff_create_bib_text(x) +#' cff_read_bib_text(x) #' } -cff_create_bib_text <- function(x, encoding = "UTF-8", ...) { +cff_read_bib_text <- function(x, encoding = "UTF-8", ...) { # Validations if (!inherits(x, "character")) { cli::cli_abort( diff --git a/R/deprecated.R b/R/deprecated.R index a57c929e..d9cdd529 100644 --- a/R/deprecated.R +++ b/R/deprecated.R @@ -1,7 +1,7 @@ #' Previous API: Create BibTeX entries from several sources #' #' @description -#' `r lifecycle::badge('superseded')` Please use [as_bibentry()] instead. +#' `r lifecycle::badge('deprecated')` Please use [as_bibentry()] instead. #' #' @rdname deprecated_cff_to_bib #' @inheritParams as_bibentry @@ -49,8 +49,8 @@ cff_to_bibtex <- function(x, #' #' @description #' -#' `r lifecycle::badge('superseded')` Please use either [cff_read_bib()] or -#' [cff_create_bib_text()] instead. +#' `r lifecycle::badge('deprecated')` Please use either [cff_read_bib()] or +#' [cff_read_bib_text()] instead. #' #' @rdname deprecated_cff_from_bib #' @export @@ -67,7 +67,7 @@ cff_to_bibtex <- function(x, #' #' @return #' -#' See [cff_read_bib()] from reading `*.bib` files and [cff_create_bib_text()] +#' See [cff_read_bib()] from reading `*.bib` files and [cff_read_bib_text()] #' for reading a `character` object representing a BibTeX entry. #' #' @@ -93,7 +93,7 @@ cff_to_bibtex <- function(x, #' }" #' ) #' -#' cff_create_bib_text(x) +#' cff_read_bib_text(x) #' #' # From a file #' @@ -114,10 +114,10 @@ cff_from_bibtex <- function(x, encoding = "UTF-8", ...) { if (requireNamespace("lifecycle", quietly = TRUE)) { lifecycle::deprecate_soft( - "1.0.0", "cff_from_bibtex()", "cff_create_bib_text()" + "1.0.0", "cff_from_bibtex()", "cff_read_bib_text()" ) } - cff_create_bib_text(x, encoding = encoding, ...) + cff_read_bib_text(x, encoding = encoding, ...) } @@ -125,7 +125,7 @@ cff_from_bibtex <- function(x, encoding = "UTF-8", ...) { #' #' @description #' -#' `r lifecycle::badge('superseded')` Please use [cff_write_bib()] or +#' `r lifecycle::badge('deprecated')` Please use [cff_write_bib()] or #' [cff_write_citation()] instead. #' #' @rdname deprecated_write @@ -184,3 +184,80 @@ write_citation <- function(x, } cff_write_citation(x, file, append, verbose, ...) } + + +#' Previous API: Parse a person to [`cff`][cff-class] +#' +#' @description +#' +#' `r lifecycle::badge('deprecated')` Please use [cff_create_cff_person()] +#' +#' @rdname deprecated_cff_person +#' @export +#' @keywords internal +#' @family deprecated +#' +#' @inheritParams cff_create_cff_person +#' @return A person in format `cff`. +#' +#' @seealso [cff_create_cff_person()] +#' +#' @examples +#' # Create a person object +#' a_person <- person( +#' given = "First", family = "Author", +#' role = c("aut", "cre"), +#' email = "first.last@example.com", comment = c( +#' ORCID = "0000-0001-8457-4658", +#' affiliation = "An affiliation" +#' ) +#' ) +#' +#' a_person +#' +#' cff_person <- cff_create_cff_person(a_person) +#' +#' cff_person +#' +#' # Back to person object with S3 Method +#' as.person(cff_person) +#' +#' # Parse a string +#' a_str <- paste0( +#' "Julio Iglesias ", +#' "()" +#' ) +#' cff_create_cff_person(a_str) +#' +#' # Several persons +#' persons <- c(person("Clark", "Kent"), person("Lois", "Lane")) +#' +#' cff_create_cff_person(persons) +#' +#' # Or you can use BibTeX style if you prefer +#' +#' x <- "Frank Sinatra and Dean Martin and Davis, Jr., Sammy and Joey Bishop" +#' +#' cff_create_cff_person(x) +#' +#' cff_create_cff_person("Herbert von Karajan") +cff_parse_person <- function(person) { + if (requireNamespace("lifecycle", quietly = TRUE)) { + lifecycle::deprecate_soft( + "1.0.0", "cff_parse_person()", "cff_create_cff_person()" + ) + } + cff_create_cff_person(person) +} + +#' @rdname deprecated_cff_person +#' @export +#' +cff_parse_person_bibtex <- function(person) { + if (requireNamespace("lifecycle", quietly = TRUE)) { + lifecycle::deprecate_soft( + "1.0.0", "cff_parse_person_bibtex()", "cff_create_cff_person()" + ) + } + cff_create_cff_person(person) +} diff --git a/R/parse_citation.R b/R/parse_citation.R index b38f4619..0a31afdb 100644 --- a/R/parse_citation.R +++ b/R/parse_citation.R @@ -143,7 +143,7 @@ building_other_persons <- function(parsed_fields) { names(bibtex) <- NULL - end <- cff_parse_person_bibtex(bibtex) + end <- cff_create_cff_person(bibtex) # If has names then it should be moved to a lower level on a list if (!is.null(names(end))) end <- list(end) @@ -153,16 +153,7 @@ building_other_persons <- function(parsed_fields) { toperson <- others[names(others) %in% toauto_end] - toperson <- lapply(toperson, cff_parse_person_bibtex) - # This should be vectors, so include on lists - toperson <- lapply(toperson, function(x) { - if (!is.null(names(x))) { - x <- list(x) - } else { - x - } - }) - + toperson <- lapply(toperson, cff_create_cff_person) # Bind and reorder diff --git a/R/utils-methods.R b/R/utils-methods.R index 3f5591ce..8a0a0d9e 100644 --- a/R/utils-methods.R +++ b/R/utils-methods.R @@ -8,17 +8,24 @@ make_r_person <- function(x) { return(person()) } # Prepare list + x <- unclass(x) + # Family is special key - fam1 <- clean_str(x$name) - fam2 <- clean_str( - paste( - clean_str(x$`name-particle`), clean_str(x$`family-names`), - clean_str(x$`name-suffix`) - ) - ) + # R Core uses for entity 'given', so do I + # see in citation() + is_entity <- any(grepl("^name$", names(x))) + if (is_entity) { + given <- clean_str(x[["name"]]) + # Extra protect + family <- NULL + } else { + given <- clean_str(x[["given-names"]]) + family <- clean_str(paste( + clean_str(x[["name-particle"]]), clean_str(x[["family-names"]]), + clean_str(x[["name-suffix"]]) + )) + } - given <- clean_str(x$`given-names`) - family <- clean_str(c(fam1, fam2)) role <- clean_str(x$role) # Make comments diff --git a/R/utils-persons.R b/R/utils-persons.R index 441f120e..d45f389b 100644 --- a/R/utils-persons.R +++ b/R/utils-persons.R @@ -260,49 +260,22 @@ bibtex_pers_first_von_last <- function(x) { return(end_list) } -as_person_bibtex <- function(x) { - # Identify the pattern - # It may be one of: - # A. Given von Family - # B. von Family, Given - # C. von Family, Junior, Given - - # Protect commas on brackets to avoid error counting - protected <- gsub(",(?![^\\}]*(\\{|$))", "@comma@", - x, - perl = TRUE - ) +validate_cff_person_fields <- function(parsed_person) { + # Entity of person - commas <- length(grep(",", unlist(strsplit(protected, "|")))) - - if (commas == 0) { - # Case A - end_list <- bibtex_pers_first_von_last(x) - } else if (commas == 1) { - # Case B - end_list <- bibtex_pers_von_last_first(x) - } else if (commas == 2) { - # Case C - end_list <- bibtex_pers_von_last_first_jr(x) - } else { - # Not considered by BibTeX. everything to family - end_list <- list(family = paste(x, collapse = " ")) - } + # Guess entity or person + is_entity <- as.character("name" %in% names(parsed_person)) - # Clean - end_list <- lapply(end_list, function(z) { - if (is.null(z)) { - return(NULL) - } - if (any((is.na(z) | z == ""))) { - return(NULL) - } - - gsub("\\{|\\}", "", z) - }) + # Keep only valid tags - Would depend on entity or person + definition <- switch(is_entity, + "TRUE" = cff_schema_definitions_entity(), + cff_schema_definitions_person() + ) - end_list <- lapply(end_list, clean_str) + parsed_person <- parsed_person[names(parsed_person) %in% definition] + # Duplicates removed + parsed_person <- parsed_person[!duplicated(names(parsed_person))] - return(end_list) + parsed_person } diff --git a/R/utils-read-description.R b/R/utils-read-description.R index 1d9935f1..ecffb718 100644 --- a/R/utils-read-description.R +++ b/R/utils-read-description.R @@ -26,7 +26,7 @@ parse_desc_authors <- function(pkg, authors_roles = c("aut", "cre")) { any(x$role %in% r) }, logical(1))] - parse_all_authors <- lapply(authors, cff_parse_person) + parse_all_authors <- cff_create_cff_person(authors) parse_all_authors <- unique(parse_all_authors) parse_all_authors @@ -42,7 +42,7 @@ parse_desc_contacts <- function(pkg) { "cre" %in% x$role }, logical(1))] - parse_all_contacts <- lapply(contact, cff_parse_person) + parse_all_contacts <- cff_create_cff_person(contact) parse_all_contacts <- unique(parse_all_contacts) parse_all_contacts } diff --git a/README.md b/README.md index 311f541f..5f2cf148 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ file and the `CITATION` file (if present) of your package. Note that **cffr** works best if your package pass `R CMD check/devtools::check()`. -As per 2024-02-29 there are at least 312 repos on GitHub using **cffr**. +As per 2024-02-29 there are at least 306 repos on GitHub using **cffr**. [Check them out here](https://github.com/search?q=cffr%20path%3A**%2FCITATION.cff&type=code). @@ -545,8 +545,9 @@ test <- cff_create("rmarkdown") given-names: Radford - family-names: Bell given-names: Kendon - - family-names: de Queljoe + - family-names: Queljoe given-names: Matthew + name-particle: de - family-names: Suruceanu given-names: Ion - family-names: Denney @@ -570,6 +571,7 @@ test <- cff_create("rmarkdown") authors: - family-names: Vanderkam given-names: Dan + website: http://dygraphs.com/ - family-names: Allaire given-names: JJ - family-names: Owen diff --git a/codemeta.json b/codemeta.json index 20b74cde..b7324a74 100644 --- a/codemeta.json +++ b/codemeta.json @@ -200,7 +200,7 @@ }, "isPartOf": "https://ropensci.org", "keywords": ["attribution", "citation", "credit", "citation-files", "cff", "metadata", "r", "r-package", "citation-file-format", "rstats", "ropensci", "cran"], - "fileSize": "947.076KB", + "fileSize": "944.651KB", "citation": [ { "@type": "ScholarlyArticle", diff --git a/man/as_bibentry.Rd b/man/as_bibentry.Rd index 25f70740..50e9d4e6 100644 --- a/man/as_bibentry.Rd +++ b/man/as_bibentry.Rd @@ -118,16 +118,16 @@ toBibtex(desc_file) \code{\link[utils:toLatex]{utils::toBibtex()}} Other functions for working with BibTeX format: -\code{\link{cff_create_bib_text}()}, \code{\link{cff_read}()}, +\code{\link{cff_read_bib_text}()}, \code{\link{cff_write_bib}()}, \code{\link{encoded_utf_to_latex}()} Other functions for converting between \strong{R} classes: -\code{\link{cff_create_person}()} +\code{\link{cff_create_cff_person}()} Other S3 Methods for \code{cff}: -\code{\link{cff_create_person}()} +\code{\link{cff_create_cff_person}()} } \concept{bibtex} \concept{coercing} diff --git a/man/cff.Rd b/man/cff.Rd index 099cc5c7..e8e856da 100644 --- a/man/cff.Rd +++ b/man/cff.Rd @@ -72,7 +72,7 @@ cff_read(system.file("examples/CITATION_basic.cff", test <- cff( title = "Manipulating files", keywords = c("A", "new", "list", "of", "keywords"), - authors = list(cff_parse_person("New author")) + authors = cff_create_cff_person("New author") ) test \donttest{ diff --git a/man/cff_create.Rd b/man/cff_create.Rd index c3ff7f1b..a05ee649 100644 --- a/man/cff_create.Rd +++ b/man/cff_create.Rd @@ -83,7 +83,7 @@ newkeys <- list( message = "This overwrites fields", abstract = "New abstract", keywords = c("A", "new", "list", "of", "keywords"), - authors = list(cff_parse_person("New author")) + authors = cff_create_cff_person("New author") ) cff_create(demo_file, keys = newkeys) @@ -95,12 +95,10 @@ old <- cff_create(demo_file) new_contact <- append( old$contact, - list( - cff_parse_person(person( - given = "I am", - family = "New Contact" - )) - ) + cff_create_cff_person(person( + given = "I am", + family = "New Contact" + )) ) diff --git a/man/cff_create_person.Rd b/man/cff_create_cff_person.Rd similarity index 79% rename from man/cff_create_person.Rd rename to man/cff_create_cff_person.Rd index 68b55194..39a6c259 100644 --- a/man/cff_create_person.Rd +++ b/man/cff_create_cff_person.Rd @@ -1,11 +1,11 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/cff_create_cff_person.R, R/cff-methods.R -\name{cff_create_person} -\alias{cff_create_person} +\name{cff_create_cff_person} +\alias{cff_create_cff_person} \alias{as.person.cff} \title{Create a person with the corresponding \code{\link[=cff-class]{cff}} structure} \usage{ -cff_create_person(person) +cff_create_cff_person(person) \method{as.person}{cff}(x) } @@ -20,8 +20,8 @@ See \strong{Examples}. \item{x}{\code{cff} object representing a person or entity.} } \value{ -\code{cff_create_person()} returns A list of persons or entities with class \code{cff} -converted to the \href{https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md}{Citation File Format schema}. +\code{cff_create_cff_person()} returns A list of persons or entities with class +\code{cff} converted to the \href{https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md}{Citation File Format schema}. \code{as.person.cff()} returns a \code{person} object. } @@ -29,7 +29,7 @@ converted to the \href{https://github.com/citation-file-format/citation-file-for Create a \code{person} or \code{entity} as defined by the \href{https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md}{Citation File Format schema}. -\code{\link[=cff_create_person]{cff_create_person()}} can convert the following objects: +\code{\link[=cff_create_cff_person]{cff_create_cff_person()}} can convert the following objects: \itemize{ \item Objects with class \code{person} as provided by \code{\link[utils:person]{utils::person()}}. \item A \code{character} string with the definition of an author or several authors, @@ -44,7 +44,7 @@ provided by \code{\link[utils:person]{utils::person()}}. This method only works with CFF persons, not with full \code{cff} objects. } \details{ -\code{cff_create_person()} uses a custom algorithm that tries to break a name +\code{cff_create_cff_person()} uses a custom algorithm that tries to break a name as explained in Section 11 of "Tame the BeaST" (Markey, 2007): \itemize{ \item \verb{First von Last} @@ -60,18 +60,18 @@ It is a good practice to "protect" entity's names with \code{{}}: \if{html}{\out{
}}\preformatted{# Don't do entity <- "Elephant and Castle" -cff_create_person(entity) +cff_create_cff_person(entity) #> - name: Elephant #> - name: Castle # Do entity_protect <- "\{Elephant and Castle\}" -cff_create_person(entity_protect) +cff_create_cff_person(entity_protect) #> - name: Elephant and Castle }\if{html}{\out{
}} -\code{cff_create_person()} would try to add as many information as possible. On -\code{character} string coming from \code{\link[utils:person]{format(person())}} the +\code{cff_create_cff_person()} would try to add as many information as possible. +On \code{character} string coming from \code{\link[utils:person]{format(person())}} the email and the ORCID would be gathered as well. } \examples{ @@ -87,7 +87,7 @@ a_person <- person( a_person -cff_person <- cff_create_person(a_person) +cff_person <- cff_create_cff_person(a_person) cff_person @@ -99,20 +99,20 @@ a_str <- paste0( "Julio Iglesias ", "()" ) -cff_create_person(a_str) +cff_create_cff_person(a_str) # Several persons persons <- c(person("Clark", "Kent"), person("Lois", "Lane")) -cff_create_person(persons) +cff_create_cff_person(persons) # Or you can use BibTeX style if you prefer x <- "Frank Sinatra and Dean Martin and Davis, Jr., Sammy and Joey Bishop" -cff_create_person(x) +cff_create_cff_person(x) -cff_create_person("Herbert von Karajan") +cff_create_cff_person("Herbert von Karajan") } \references{ \itemize{ diff --git a/man/cff_parse_citation.Rd b/man/cff_parse_citation.Rd index 03be7d84..a9b1891a 100644 --- a/man/cff_parse_citation.Rd +++ b/man/cff_parse_citation.Rd @@ -80,8 +80,5 @@ cff_parse_citation(citation("rmarkdown")) } \seealso{ \code{\link[=cff_create]{cff_create()}}, \code{vignette("bibtex_cff", "cffr")}, \code{\link[=bibentry]{bibentry()}} - -Other parsers: -\code{\link{cff_parse_person}()} } \concept{parsers} diff --git a/man/cff_parse_person.Rd b/man/cff_parse_person.Rd deleted file mode 100644 index a0c03b2e..00000000 --- a/man/cff_parse_person.Rd +++ /dev/null @@ -1,82 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/cff_parse_person.R -\name{cff_parse_person} -\alias{cff_parse_person} -\alias{cff_parse_person_bibtex} -\title{Parse a person to \code{cff}} -\usage{ -cff_parse_person(person) - -cff_parse_person_bibtex(person) -} -\arguments{ -\item{person}{A \code{person} object created with \code{\link[=person]{person()}} or a -character string. See \strong{Details}.} -} -\value{ -A \code{\link{cff}} object ready to be used on \code{\link[=cff_create]{cff_create()}}. -} -\description{ -Parse a person or string to a valid format for a \code{CITATION.cff} file. This -is a helper function designed to help on adding or replacing the -auto-generated authors of the package. -} -\details{ -The \code{person} parameter of the function could be: -\itemize{ -\item For \code{cff_parse_person()}: A \code{person} object or a character coercible to -\code{person}. See \code{\link[=person]{person()}} for details. -\item For \code{cff_parse_person_bibtex()}: A string with the definition of an author -or several authors, using the standard BibTeX notation. See Markey (2007) -for a full explanation. -} - -See \strong{Examples} for more information. -} -\examples{ -# Parse a person object - -cff_parse_person(person( - given = "First", - family = "Author", - role = c("aut", "cre"), - email = "first.last@example.com", - comment = c( - ORCID = "0000-0001-8457-4658", - affiliation = "An affiliation" - ) -)) - -# Parse a string - -cff_parse_person("Julio Iglesias ") - -# Several persons -persons <- c(person("Clark", "Kent"), person("Lois", "Lane")) - -cff_parse_person(persons) - -# Or you can use BibTeX style if you prefer - -x <- "Frank Sinatra and Dean Martin and Davis, Jr., Sammy and Joey Bishop" - -cff_parse_person_bibtex(x) - -cff_parse_person_bibtex("Herbert von Karajan") -} -\references{ -\itemize{ -\item Patashnik, Oren. "BIBTEXTING" February 1988. -\url{https://osl.ugr.es/CTAN/biblio/bibtex/base/btxdoc.pdf}. -\item Markey, Nicolas. "Tame the BeaST." -\emph{The B to X of BibTeX, Version 1.4} (October 2007). -\url{https://osl.ugr.es/CTAN/info/bibtex/tamethebeast/ttb_en.pdf}. -} -} -\seealso{ -\code{\link[=cff_create]{cff_create()}}, \code{vignette("cffr", "cffr")}, \code{\link[utils:person]{utils::person()}} - -Other parsers: -\code{\link{cff_parse_citation}()} -} -\concept{parsers} diff --git a/man/cff_read.Rd b/man/cff_read.Rd index 1296498e..b36fc1cb 100644 --- a/man/cff_read.Rd +++ b/man/cff_read.Rd @@ -134,11 +134,11 @@ The underlying functions used for reading external files: } Other functions for reading external files: -\code{\link{cff_create_bib_text}()} +\code{\link{cff_read_bib_text}()} Other functions for working with BibTeX format: \code{\link{as_bibentry}()}, -\code{\link{cff_create_bib_text}()}, +\code{\link{cff_read_bib_text}()}, \code{\link{cff_write_bib}()}, \code{\link{encoded_utf_to_latex}()} } diff --git a/man/cff_create_bib_text.Rd b/man/cff_read_bib_text.Rd similarity index 90% rename from man/cff_create_bib_text.Rd rename to man/cff_read_bib_text.Rd index 69ec913f..ede3aa44 100644 --- a/man/cff_create_bib_text.Rd +++ b/man/cff_read_bib_text.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/cff_read_bib_text.R -\name{cff_create_bib_text} -\alias{cff_create_bib_text} -\title{Create a \code{\link[=cff-class]{cff}} object from BibTeX lines} +\name{cff_read_bib_text} +\alias{cff_read_bib_text} +\title{Read BibTeX markup as a \code{\link[=cff-class]{cff}} object} \usage{ -cff_create_bib_text(x, encoding = "UTF-8", ...) +cff_read_bib_text(x, encoding = "UTF-8", ...) } \arguments{ \item{x}{A vector of \code{character} objects with the full BibTeX string.} @@ -54,7 +54,7 @@ if (requireNamespace("bibtex", quietly = TRUE)) { ) - cff_create_bib_text(x) + cff_read_bib_text(x) } } \seealso{ diff --git a/man/cff_write_misc.Rd b/man/cff_write_misc.Rd index adddab4e..06cc23ed 100644 --- a/man/cff_write_misc.Rd +++ b/man/cff_write_misc.Rd @@ -114,8 +114,8 @@ following packages: Other functions for working with BibTeX format: \code{\link{as_bibentry}()}, -\code{\link{cff_create_bib_text}()}, \code{\link{cff_read}()}, +\code{\link{cff_read_bib_text}()}, \code{\link{encoded_utf_to_latex}()} Other functions for creating external files: diff --git a/man/chunks/cff_create_person.Rmd b/man/chunks/person.Rmd similarity index 82% rename from man/chunks/cff_create_person.Rmd rename to man/chunks/person.Rmd index 269facc2..d20b3f61 100644 --- a/man/chunks/cff_create_person.Rmd +++ b/man/chunks/person.Rmd @@ -7,11 +7,11 @@ options("cffr_message_verbosity" = NULL) ```{r} # Don't do entity <- "Elephant and Castle" -cff_create_person(entity) +cff_create_cff_person(entity) # Do entity_protect <- "{Elephant and Castle}" -cff_create_person(entity_protect) +cff_create_cff_person(entity_protect) ``` ```{r include=FALSE} diff --git a/man/deprecated_cff_from_bib.Rd b/man/deprecated_cff_from_bib.Rd index aeb35186..20e1e261 100644 --- a/man/deprecated_cff_from_bib.Rd +++ b/man/deprecated_cff_from_bib.Rd @@ -19,12 +19,12 @@ cff_from_bibtex(x, encoding = "UTF-8", ...) \item{...}{Other arguments passed to \code{\link[bibtex:read.bib]{bibtex::read.bib()}}.} } \value{ -See \code{\link[=cff_read_bib]{cff_read_bib()}} from reading \verb{*.bib} files and \code{\link[=cff_create_bib_text]{cff_create_bib_text()}} +See \code{\link[=cff_read_bib]{cff_read_bib()}} from reading \verb{*.bib} files and \code{\link[=cff_read_bib_text]{cff_read_bib_text()}} for reading a \code{character} object representing a BibTeX entry. } \description{ -\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#superseded}{\figure{lifecycle-superseded.svg}{options: alt='[Superseded]'}}}{\strong{[Superseded]}} Please use either \code{\link[=cff_read_bib]{cff_read_bib()}} or -\code{\link[=cff_create_bib_text]{cff_create_bib_text()}} instead. +\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Please use either \code{\link[=cff_read_bib]{cff_read_bib()}} or +\code{\link[=cff_read_bib_text]{cff_read_bib_text()}} instead. } \examples{ if (requireNamespace("bibtex", quietly = TRUE)) { @@ -47,7 +47,7 @@ if (requireNamespace("bibtex", quietly = TRUE)) { }" ) - cff_create_bib_text(x) + cff_read_bib_text(x) # From a file @@ -58,6 +58,7 @@ if (requireNamespace("bibtex", quietly = TRUE)) { \seealso{ Other deprecated functions: \code{\link{cff_extract_to_bibtex}()}, +\code{\link{cff_parse_person}()}, \code{\link{write_bib}()} } \concept{deprecated} diff --git a/man/deprecated_cff_person.Rd b/man/deprecated_cff_person.Rd new file mode 100644 index 00000000..f94f0e11 --- /dev/null +++ b/man/deprecated_cff_person.Rd @@ -0,0 +1,75 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/deprecated.R +\name{cff_parse_person} +\alias{cff_parse_person} +\alias{cff_parse_person_bibtex} +\title{Previous API: Parse a person to \code{\link[=cff-class]{cff}}} +\usage{ +cff_parse_person(person) + +cff_parse_person_bibtex(person) +} +\arguments{ +\item{person}{It can be either: +\itemize{ +\item A \code{person} or list of \code{person} object created with \code{\link[utils:person]{utils::person()}}. +\item A \code{character} object or vector representing a person or persons. +See \strong{Examples}. +}} +} +\value{ +A person in format \code{cff}. +} +\description{ +\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Please use \code{\link[=cff_create_cff_person]{cff_create_cff_person()}} +} +\examples{ +# Create a person object +a_person <- person( + given = "First", family = "Author", + role = c("aut", "cre"), + email = "first.last@example.com", comment = c( + ORCID = "0000-0001-8457-4658", + affiliation = "An affiliation" + ) +) + +a_person + +cff_person <- cff_create_cff_person(a_person) + +cff_person + +# Back to person object with S3 Method +as.person(cff_person) + +# Parse a string +a_str <- paste0( + "Julio Iglesias ", + "()" +) +cff_create_cff_person(a_str) + +# Several persons +persons <- c(person("Clark", "Kent"), person("Lois", "Lane")) + +cff_create_cff_person(persons) + +# Or you can use BibTeX style if you prefer + +x <- "Frank Sinatra and Dean Martin and Davis, Jr., Sammy and Joey Bishop" + +cff_create_cff_person(x) + +cff_create_cff_person("Herbert von Karajan") +} +\seealso{ +\code{\link[=cff_create_cff_person]{cff_create_cff_person()}} + +Other deprecated functions: +\code{\link{cff_extract_to_bibtex}()}, +\code{\link{cff_from_bibtex}()}, +\code{\link{write_bib}()} +} +\concept{deprecated} +\keyword{internal} diff --git a/man/deprecated_cff_to_bib.Rd b/man/deprecated_cff_to_bib.Rd index 1a3983d1..cf7a0221 100644 --- a/man/deprecated_cff_to_bib.Rd +++ b/man/deprecated_cff_to_bib.Rd @@ -34,7 +34,7 @@ both the preferred citation info and the references. See \code{\link[=as_bibentry]{as_bibentry()}}. } \description{ -\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#superseded}{\figure{lifecycle-superseded.svg}{options: alt='[Superseded]'}}}{\strong{[Superseded]}} Please use \code{\link[=as_bibentry]{as_bibentry()}} instead. +\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Please use \code{\link[=as_bibentry]{as_bibentry()}} instead. } \examples{ \donttest{ @@ -50,6 +50,7 @@ bib <- as_bibentry(cff_object) \seealso{ Other deprecated functions: \code{\link{cff_from_bibtex}()}, +\code{\link{cff_parse_person}()}, \code{\link{write_bib}()} } \concept{deprecated} diff --git a/man/deprecated_write.Rd b/man/deprecated_write.Rd index 6db7aa10..90efe6a6 100644 --- a/man/deprecated_write.Rd +++ b/man/deprecated_write.Rd @@ -31,7 +31,7 @@ lines to be written.} Write a file. } \description{ -\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#superseded}{\figure{lifecycle-superseded.svg}{options: alt='[Superseded]'}}}{\strong{[Superseded]}} Please use \code{\link[=cff_write_bib]{cff_write_bib()}} or +\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Please use \code{\link[=cff_write_bib]{cff_write_bib()}} or \code{\link[=cff_write_citation]{cff_write_citation()}} instead. } \examples{ @@ -59,7 +59,8 @@ cat(readLines(my_temp_bib), sep = "\n") Other deprecated functions: \code{\link{cff_extract_to_bibtex}()}, -\code{\link{cff_from_bibtex}()} +\code{\link{cff_from_bibtex}()}, +\code{\link{cff_parse_person}()} } \concept{deprecated} \keyword{internal} diff --git a/man/encoded_utf_to_latex.Rd b/man/encoded_utf_to_latex.Rd index d0dd163d..339e1068 100644 --- a/man/encoded_utf_to_latex.Rd +++ b/man/encoded_utf_to_latex.Rd @@ -45,8 +45,8 @@ ascii_table Other functions for working with BibTeX format: \code{\link{as_bibentry}()}, -\code{\link{cff_create_bib_text}()}, \code{\link{cff_read}()}, +\code{\link{cff_read_bib_text}()}, \code{\link{cff_write_bib}()} } \concept{bibtex} diff --git a/tests/testthat/_snaps/cff-methods.md b/tests/testthat/_snaps/cff-methods.md index 114dd512..5c784a72 100644 --- a/tests/testthat/_snaps/cff-methods.md +++ b/tests/testthat/_snaps/cff-methods.md @@ -800,14 +800,17 @@ Output [1] "type" "title" [3] "authors.00.family_names" "authors.00.given_names" - [5] "authors.01.family_names" "authors.01.given_names" - [7] "authors.02.family_names" "authors.02.given_names" - [9] "authors.03.name" "url" - [11] "keywords.00" "keywords.01" - [13] "keywords.02" "keywords.03" - [15] "keywords.04" "keywords.05" - [17] "keywords.06" "keywords.07" - [19] "abstract" "version" + [5] "authors.00.email" "authors.00.orcid" + [7] "authors.01.family_names" "authors.01.given_names" + [9] "authors.01.affiliation" "authors.01.country" + [11] "authors.02.family_names" "authors.02.given_names" + [13] "authors.02.email" "authors.03.name" + [15] "authors.03.date_end" "url" + [17] "keywords.00" "keywords.01" + [19] "keywords.02" "keywords.03" + [21] "keywords.04" "keywords.05" + [23] "keywords.06" "keywords.07" + [25] "abstract" "version" # Convert authors only @@ -842,10 +845,11 @@ Code dput(pub) Output - structure(list(list(given = NULL, family = "Entity Project Team Conference entity", - role = NULL, email = "project@entity.com", comment = c(address = "22 Acacia Avenue", - city = "Citationburgh", region = "Renfrewshire", `post-code` = "C13 7X7", - country = "GB", ORCID = "0000-0001-2345-6789", tel = "+44(0)141-323 4567", + structure(list(list(given = "Entity Project Team Conference entity", + family = NULL, role = NULL, email = "project@entity.com", + comment = c(address = "22 Acacia Avenue", city = "Citationburgh", + region = "Renfrewshire", `post-code` = "C13 7X7", country = "GB", + ORCID = "0000-0001-2345-6789", tel = "+44(0)141-323 4567", fax = "+44(0)141-323 45678", website = "https://www.entity-project-team.io", `date-start` = "2017-01-01", `date-end` = "2017-01-31", location = "The team garage" ))), class = "person") @@ -888,11 +892,11 @@ address = "22 Acacia Avenue", city = "Citationburgh", region = "Renfrewshire", `post-code` = "C13 7X7", country = "GB", ORCID = "0000-0001-2345-6789", tel = "+44(0)141-323 4567", fax = "+44(0)141-323 45678", - website = "https://www.entity-project-team.io")), list(given = NULL, - family = "Entity Project Team Conference entity", role = NULL, - email = "project@entity.com", comment = c(address = "22 Acacia Avenue", - city = "Citationburgh", region = "Renfrewshire", `post-code` = "C13 7X7", - country = "GB", ORCID = "0000-0001-2345-6789", tel = "+44(0)141-323 4567", + website = "https://www.entity-project-team.io")), list(given = "Entity Project Team Conference entity", + family = NULL, role = NULL, email = "project@entity.com", + comment = c(address = "22 Acacia Avenue", city = "Citationburgh", + region = "Renfrewshire", `post-code` = "C13 7X7", country = "GB", + ORCID = "0000-0001-2345-6789", tel = "+44(0)141-323 4567", fax = "+44(0)141-323 45678", website = "https://www.entity-project-team.io", `date-start` = "2017-01-01", `date-end` = "2017-01-31", location = "The team garage" ))), class = "person") diff --git a/tests/testthat/_snaps/cff_create_cff_person.md b/tests/testthat/_snaps/cff_create_cff_person.md new file mode 100644 index 00000000..8e1a13ab --- /dev/null +++ b/tests/testthat/_snaps/cff_create_cff_person.md @@ -0,0 +1,97 @@ +# debugging messages + + Code + a <- cff_create_cff_person(ap) + Message + i In `cff_create_cff_person()` using internal for "person". + +--- + + Code + b <- cff_create_cff_person("Example") + Message + i In `cff_create_cff_person()` using internal for "txt". + +# Parse one person + + Code + cff_create_cff_person(p) + Output + - family-names: person + given-names: one + +# Parse several persons + + Code + cff_create_cff_person(p) + Output + - family-names: person + given-names: one + - family-names: human + given-names: another + - family-names: more + given-names: and one + +# Parse bibtex persons + + Code + cff_create_cff_person(s) + Output + - family-names: Wright + given-names: Frank Edwin + name-suffix: III + +--- + + Code + cff_create_cff_person(s) + Output + - family-names: person + given-names: A + - name: another + - family-names: one + given-names: Another + +# Parse bibtex persons with masks + + Code + cff_create_cff_person(s) + Output + - name: Elephant + - name: Castle + +--- + + Code + cff_create_cff_person(s) + Output + - name: Elephant and Castle + +--- + + Code + cff_create_cff_person(s) + Output + - name: Elephant and Castle + - name: this + - name: Ltd. + +--- + + Code + cff_create_cff_person(s) + Output + - name: Elephant and Castle + - name: this AND Ltd. + +# Can extract comments from format + + Code + pp2 + Output + - family-names: Doe + given-names: John + email: first_mail@gmail.com + orcid: https://orcid.org/0000-0001-8457-4658 + website: https://www.google.com/ + diff --git a/tests/testthat/_snaps/cff_parse_person.md b/tests/testthat/_snaps/cff_parse_person.md deleted file mode 100644 index 648f980e..00000000 --- a/tests/testthat/_snaps/cff_parse_person.md +++ /dev/null @@ -1,72 +0,0 @@ -# Parse one person - - Code - cff_parse_person(p) - Output - family-names: person - given-names: one - -# Parse several persons - - Code - cff_parse_person(p) - Output - - family-names: person - given-names: one - - family-names: human - given-names: another - - family-names: more - given-names: and one - -# Parse bibtex persons - - Code - cff_parse_person_bibtex(s) - Output - family-names: Wright - given-names: Frank Edwin - name-suffix: III - ---- - - Code - cff_parse_person_bibtex(s) - Output - - family-names: person - given-names: A - - name: another - - family-names: one - given-names: Another - -# Parse bibtex persons with masks - - Code - cff_parse_person_bibtex(s) - Output - - name: Elephant - - name: Castle - ---- - - Code - cff_parse_person_bibtex(s) - Output - name: Elephant and Castle - ---- - - Code - cff_parse_person_bibtex(s) - Output - - name: Elephant and Castle - - name: this - - name: Ltd. - ---- - - Code - cff_parse_person_bibtex(s) - Output - - name: Elephant and Castle - - name: this AND Ltd. - diff --git a/tests/testthat/_snaps/cff_read_bib_text.md b/tests/testthat/_snaps/cff_read_bib_text.md index 1fa44262..8bfc4150 100644 --- a/tests/testthat/_snaps/cff_read_bib_text.md +++ b/tests/testthat/_snaps/cff_read_bib_text.md @@ -1,15 +1,15 @@ # Errors and messages Code - cff_create_bib_text(a_cff) + cff_read_bib_text(a_cff) Condition - Error in `cff_create_bib_text()`: + Error in `cff_read_bib_text()`: ! `x` should be a , not a . --- Code - cff_create_bib_text("a bad line") + cff_read_bib_text("a bad line") Message ! `x` doesn't look as a BibTeX entry. Check the results. Condition @@ -44,7 +44,7 @@ --- Code - fromfile <- cff_create_bib_text(tmpbib) + fromfile <- cff_read_bib_text(tmpbib) Message ! `x` seems to be a ".bib" file, not a BibTeX entry. i Reading `x` with `cffr:cff_read_bib()` diff --git a/tests/testthat/_snaps/cff_write_misc/CITAT_ION b/tests/testthat/_snaps/cff_write_misc/CITAT_ION index e3d0407a..7473bbd2 100644 --- a/tests/testthat/_snaps/cff_write_misc/CITAT_ION +++ b/tests/testthat/_snaps/cff_write_misc/CITAT_ION @@ -8,7 +8,8 @@ bibentry(bibtype = "Misc", key = "basic", title = "basicdescdate: A Basic Description with Date", author = person(given = "Marc", - family = "Basic"), + family = "Basic", + email = "marcbasic@gmail.com"), year = "1999", url = "https://basic.github.io/package", abstract = "A very basic description. Should parse without problems. I have a Date", diff --git a/tests/testthat/_snaps/deprecated.md b/tests/testthat/_snaps/deprecated.md index d7b7e777..55d49f2a 100644 --- a/tests/testthat/_snaps/deprecated.md +++ b/tests/testthat/_snaps/deprecated.md @@ -32,7 +32,7 @@ Condition Warning: `cff_from_bibtex()` was deprecated in cffr 1.0.0. - i Please use `cff_create_bib_text()` instead. + i Please use `cff_read_bib_text()` instead. # write_bib @@ -73,3 +73,21 @@ author = person(given = "Fran", family = "PĂ©rez")) +# cff_parse_person + + Code + pend <- cff_parse_person(p) + Condition + Warning: + `cff_parse_person()` was deprecated in cffr 1.0.0. + i Please use `cff_create_cff_person()` instead. + +# cff_parse_person_bibtex + + Code + pend <- cff_parse_person_bibtex(p) + Condition + Warning: + `cff_parse_person_bibtex()` was deprecated in cffr 1.0.0. + i Please use `cff_create_cff_person()` instead. + diff --git a/tests/testthat/_snaps/utils-persons.md b/tests/testthat/_snaps/utils-persons.md index 7e6b7143..a6d1a97f 100644 --- a/tests/testthat/_snaps/utils-persons.md +++ b/tests/testthat/_snaps/utils-persons.md @@ -3,358 +3,358 @@ Code unlist(res) Output - given family - "AA" "BB" + family-names given-names + "BB" "AA" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - family - "AA" + name + "AA" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - given family - "AA" "bb" + family-names given-names + "bb" "AA" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - family - "aa" + name + "aa" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - given von family - "AA" "bb" "CC" + family-names given-names name-particle + "CC" "AA" "bb" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - given von family - "AA" "bb CC dd" "EE" + family-names given-names name-particle + "EE" "AA" "bb CC dd" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - given von family - "AA bB" "cc" "dd" + family-names given-names name-particle + "dd" "AA bB" "cc" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - given von family - "AA \\BBb" "cc" "dd" + family-names given-names name-particle + "dd" "AA \\BBb" "cc" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - given von family - "AA bb" "cc" "DD" + family-names given-names name-particle + "DD" "AA bb" "cc" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - given von family - "AA" "bb" "cc DD" + family-names given-names name-particle + "cc DD" "AA" "bb" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - given family - "AA bb" "CC" + family-names given-names + "CC" "AA bb" # Testing with random names First von Last Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - given von family - "Jean" "de" "La Fontaine" + family-names given-names name-particle + "La Fontaine" "Jean" "de" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - given family - "Diego" "Hernandez Sanz" + family-names given-names + "Hernandez Sanz" "Diego" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - given family - "Juan Manuel" "Miramontes" + family-names given-names + "Miramontes" "Juan Manuel" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - given family - "Juan Manuel" "Miramontes Garcia" + family-names given-names + "Miramontes Garcia" "Juan Manuel" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - given von family - "Juan Manuel" "van" "Halen" + family-names given-names name-particle + "Halen" "Juan Manuel" "van" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - given family - "Bosco" "de la Cruz y Ochoa" + family-names given-names + "de la Cruz y Ochoa" "Bosco" # Test von Last, First Code unlist(res) Output - given von family - "AA" "bb" "CC" + family-names given-names name-particle + "CC" "AA" "bb" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - given von family - "aa" "bb" "CC" + family-names given-names name-particle + "CC" "aa" "bb" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - given von family - "AA" "bb CC dd" "EE" + family-names given-names name-particle + "EE" "AA" "bb CC dd" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - given family - "AA" "bb" + family-names given-names + "bb" "AA" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - family - "BB" + name + "BB" # Test von Last, First with brackets, etc Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - given von family - "Ana" "de" "Armas" + family-names given-names name-particle + "Armas" "Ana" "de" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - given family - "Ana" "de Armas" + family-names given-names + "de Armas" "Ana" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - given family - "Ana" "de Armas, Aguero" + family-names given-names + "de Armas, Aguero" "Ana" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - given family - "Ana Maria" "de Armas, Aguero" + family-names given-names + "de Armas, Aguero" "Ana Maria" # Test von Last, Jr, First Code unlist(res) Output - given von family jr - "AA" "bb" "CC" "XX" + family-names given-names name-particle name-suffix + "CC" "AA" "bb" "XX" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - given family - "AA" "BB" + family-names given-names + "BB" "AA" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - family jr - "BB" "AA" + name + "BB AA" # Test von Last, Jr, First with masking Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - given family jr - "Sammy" "Davis" "Jr" + family-names given-names name-suffix + "Davis" "Sammy" "Jr" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - given family jr - "Sammy" "Davis, and" "Jr, another" + family-names given-names name-suffix + "Davis, and" "Sammy" "Jr, another" # Rest of cases Code unlist(res) Output - family + name "David, and, Jr, another, Sammy" # tames da beast Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - von family - "jean de la" "fontaine" + name + "jean de la fontaine" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - given von family - "Jean" "de la" "fontaine" + family-names given-names name-particle + "fontaine" "Jean" "de la" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - given von family - "Jean de" "la" "fontaine" + family-names given-names name-particle + "fontaine" "Jean de" "la" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - von family - "jean" "de la fontaine" + name + "jean de la fontaine" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - given family - "Jean de la" "fontaine" + family-names given-names + "fontaine" "Jean de la" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - given family - "Jean De La" "Fontaine" + family-names given-names + "Fontaine" "Jean De La" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - von family - "jean De la" "Fontaine" + name + "jean De la Fontaine" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - given von family - "Jean" "de" "La Fontaine" + family-names given-names name-particle + "La Fontaine" "Jean" "de" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - von family - "jean de la" "fontaine" + name + "jean de la fontaine" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - given von family - "Jean" "de la" "fontaine" + family-names given-names name-particle + "fontaine" "Jean" "de la" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - given family - "Jean" "De La Fontaine" + family-names given-names + "De La Fontaine" "Jean" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - given von family - "Jean" "De la" "Fontaine" + family-names given-names name-particle + "Fontaine" "Jean" "De la" --- Code - unlist(as_person_bibtex(x)) + unlist(create_person_from_txt(x)) Output - given von family - "Jean" "de" "La Fontaine" + family-names given-names name-particle + "La Fontaine" "Jean" "de" diff --git a/tests/testthat/test-as_bibentry.R b/tests/testthat/test-as_bibentry.R index b7bc9279..5628be47 100644 --- a/tests/testthat/test-as_bibentry.R +++ b/tests/testthat/test-as_bibentry.R @@ -257,7 +257,7 @@ test_that("particle names", { bibparsed <- cff_parse_citation(bib) - bibparsed$authors <- cff_parse_person_bibtex( + bibparsed$authors <- cff_create_cff_person( "van Leunen, Mary-Claire and Davis, Jr., Sammy" ) @@ -284,7 +284,7 @@ test_that("From plain cff with a citation", { ) s$`preferred-citation` <- cff_parse_citation(acit) - s$`preferred-citation`$editors <- list(cff_parse_person("A name")) + s$`preferred-citation`$editors <- cff_create_cff_person("A name") bib <- as_bibentry(s) expect_snapshot(toBibtex(bib)) diff --git a/tests/testthat/test-cff-methods.R b/tests/testthat/test-cff-methods.R index 0d610c4b..56f48b67 100644 --- a/tests/testthat/test-cff-methods.R +++ b/tests/testthat/test-cff-methods.R @@ -82,7 +82,7 @@ test_that("Convert a citation only", { test_that("Convert authors only", { - a_pers_list <- cff_parse_person_bibtex( + a_pers_list <- cff_create_cff_person( "A person and {A Entity inc.} and {One person} more" ) @@ -248,6 +248,6 @@ test_that("toBibtex", { address = {London, United Kingdom}, isbn = 9781587340925}" - froml <- toBibtex(cff_create_bib_text(string)) + froml <- toBibtex(cff_read_bib_text(string)) expect_equal(sum(names(froml) == "title"), 1) }) diff --git a/tests/testthat/test-cff_create_cff_person.R b/tests/testthat/test-cff_create_cff_person.R new file mode 100644 index 00000000..cd6620d5 --- /dev/null +++ b/tests/testthat/test-cff_create_cff_person.R @@ -0,0 +1,188 @@ +test_that("NULL gives NULL", { + expect_null(cff_create_cff_person(NULL)) + expect_null(cff_create_cff_person(NA)) + expect_null(cff_create_cff_person(person())) + expect_null(cff_create_cff_person(list())) + expect_null(cff_create_cff_person("")) +}) + +test_that("debugging messages", { + initopt <- getOption("cffr_message_verbosity", NULL) + options("cffr_message_verbosity" = "debug") + ap <- person("Example") + expect_snapshot(a <- cff_create_cff_person(ap)) + expect_snapshot(b <- cff_create_cff_person("Example")) + # Disconnect + options("cffr_message_verbosity" = NULL) + expect_silent(a <- cff_create_cff_person(ap)) + expect_silent(b <- cff_create_cff_person("Example")) + + # Restore init + options("cffr_message_verbosity" = initopt) +}) + +test_that("Parse one person", { + p <- person("one", "person") + expect_snapshot(cff_create_cff_person(p)) +}) + +test_that("Parse several persons", { + p <- c( + person("one", "person"), person("another", "human"), + person("and one", "more") + ) + expect_snapshot(cff_create_cff_person(p)) +}) + + +test_that("Parse bibtex persons", { + s <- "Wright, III, Frank Edwin" + + expect_snapshot(cff_create_cff_person(s)) + + s <- "A person and another and Another one" + + expect_snapshot(cff_create_cff_person(s)) + + # As vector + s2 <- c("A person", "another", "Another one") + expect_identical(cff_create_cff_person(s), cff_create_cff_person(s2)) +}) + +test_that("Parse bibtex persons with masks", { + s <- "Elephant and Castle" + + expect_snapshot(cff_create_cff_person(s)) + + s <- "{Elephant and Castle}" + + expect_snapshot(cff_create_cff_person(s)) + + s <- "{Elephant and Castle} and this AND Ltd." + + expect_snapshot(cff_create_cff_person(s)) + + s <- "{Elephant and Castle} and {this AND Ltd.}" + + expect_snapshot(cff_create_cff_person(s)) +}) + + +test_that("Get same results with both", { + s3 <- cff_create_cff_person( + c(person(family = "Entity"), person("A", "person")) + ) + + b3 <- cff_create_cff_person("Entity and A person") + + expect_identical(s3, b3) + + s4 <- cff_create_cff_person(person(family = "A big fat company")) + b4 <- cff_create_cff_person("{A big fat company}") + + expect_identical(s4, b4) +}) + +test_that("R Core Team", { + p <- cff_create_cff_person(person("R Core", "Team")) + + expect_equal(p[[1]]$name, "R Core Team") + + p <- cff_create_cff_person("R Core Team") + + expect_equal(p[[1]]$name, "R Core Team") +}) + +test_that("Bioconductor", { + # Several tastes of Bioconductor + bio <- person("Bioconductor Package Maintainer", + role = "cre", + email = "maintainer@bioconductor.org" + ) + p <- cff_create_cff_person(bio) + + expect_equal(p[[1]]$name, "Bioconductor Package Maintainer") + + p <- cff_create_cff_person("Bioconductor Package Maintainer") + + expect_equal(p[[1]]$name, "Bioconductor Package Maintainer") + + p <- cff_create_cff_person(person("The Bioconductor", "Package Maintainer")) + expect_equal(p[[1]]$name, "The Bioconductor Package Maintainer") +}) + +test_that("Several emails, select first", { + pp <- person( + given = "John", + family = "Doe", + role = c("aut", "cre", "trl"), + email = c("first_mail@gmail.com", "second_mail@espol.edu.ec") + ) + + p <- cff_create_cff_person(pp) + + expect_equal(p[[1]]$email, "first_mail@gmail.com") +}) + + +test_that("Several emails, select second if first no valid", { + pp <- person( + given = "John", + family = "Doe", + role = c("aut", "cre", "trl"), + email = c("first_mail_gmail.com", "second_mail@espol.edu.ec") + ) + + p <- cff_create_cff_person(pp) + + expect_equal(p[[1]]$email, "second_mail@espol.edu.ec") +}) + + + +test_that("No valid emails", { + pp <- person( + given = "John", + family = "Doe", + role = c("aut", "cre", "trl"), + email = c("first_mail_gmail.com", "second_mail__espol.edu.ec") + ) + + p <- cff_create_cff_person(pp) + + expect_equal(p, cff_create_cff_person(person( + given = "John", + family = "Doe" + ))) + + pp2 <- person( + given = "John", + family = "Doe" + ) + + p2 <- cff_create_cff_person(pp2) + + expect_equal(p, cff_create_cff_person(person( + given = "John", + family = "Doe" + ))) +}) + +test_that("Can extract comments from format", { + pp <- person( + given = "John", + family = "Doe", + email = "first_mail@gmail.com", + comment = c( + ORCID = "https://orcid.org/0000-0001-8457-4658", + website = "https://www.google.com/" + ) + ) + + pp1 <- cff_create_cff_person(pp) + txt <- format(pp) + pp2 <- cff_create_cff_person(txt) + expect_identical(pp1, pp2) + + expect_snapshot(pp2) +}) diff --git a/tests/testthat/test-cff_parse_person.R b/tests/testthat/test-cff_parse_person.R deleted file mode 100644 index 2e169564..00000000 --- a/tests/testthat/test-cff_parse_person.R +++ /dev/null @@ -1,161 +0,0 @@ -test_that("NULL gives NULL", { - expect_null(cff_parse_person(NULL)) - expect_null(cff_parse_person(NA)) -}) - -test_that("Parse one person", { - p <- person("one", "person") - expect_snapshot(cff_parse_person(p)) -}) - -test_that("Parse several persons", { - p <- c( - person("one", "person"), person("another", "human"), - person("and one", "more") - ) - expect_snapshot(cff_parse_person(p)) -}) - - -test_that("Parse bibtex persons", { - s <- "Wright, III, Frank Edwin" - - expect_snapshot(cff_parse_person_bibtex(s)) - - s <- "A person and another and Another one" - - expect_snapshot(cff_parse_person_bibtex(s)) -}) - -test_that("Parse bibtex persons with masks", { - s <- "Elephant and Castle" - - expect_snapshot(cff_parse_person_bibtex(s)) - - s <- "{Elephant and Castle}" - - expect_snapshot(cff_parse_person_bibtex(s)) - - s <- "{Elephant and Castle} and this AND Ltd." - - expect_snapshot(cff_parse_person_bibtex(s)) - - s <- "{Elephant and Castle} and {this AND Ltd.}" - - expect_snapshot(cff_parse_person_bibtex(s)) -}) - - -test_that("Get same results with both", { - s <- "John Foo" - - r <- cff_parse_person(s) - b <- cff_parse_person_bibtex(s) - - expect_identical(r, b) - - ex2 <- "John Foo and Mary Li" - s2 <- cff_parse_person(ex2) - - b2 <- cff_parse_person_bibtex(ex2) - - expect_identical(s2, b2) - - s3 <- cff_parse_person( - c(person(family = "Entity"), person("A", "person")) - ) - - b3 <- cff_parse_person_bibtex("Entity and A person") - - expect_identical(s3, b3) - - s4 <- cff_parse_person(person(family = "A big fat company")) - b4 <- cff_parse_person_bibtex("{A big fat company}") - - expect_identical(s4, b4) -}) - -test_that("R Core Team", { - p <- cff_parse_person(person("R Core", "Team")) - - expect_equal(p$name, "R Core Team") - - p <- cff_parse_person("R Core Team") - - expect_equal(p$name, "R Core Team") -}) - -test_that("Bioconductor", { - # Several tastes of Bioconductor - bio <- person("Bioconductor Package Maintainer", - role = "cre", - email = "maintainer@bioconductor.org" - ) - p <- cff_parse_person(bio) - - expect_equal(p$name, "Bioconductor Package Maintainer") - - p <- cff_parse_person("Bioconductor Package Maintainer") - - expect_equal(p$name, "Bioconductor Package Maintainer") - - p <- cff_parse_person(person("The Bioconductor", "Package Maintainer")) - expect_equal(p$name, "The Bioconductor Package Maintainer") -}) - -test_that("Several emails, select first", { - pp <- person( - given = "John", - family = "Doe", - role = c("aut", "cre", "trl"), - email = c("first_mail@gmail.com", "second_mail@espol.edu.ec") - ) - - p <- cff_parse_person(pp) - - expect_equal(p$email, "first_mail@gmail.com") -}) - - -test_that("Several emails, select second if first no valid", { - pp <- person( - given = "John", - family = "Doe", - role = c("aut", "cre", "trl"), - email = c("first_mail_gmail.com", "second_mail@espol.edu.ec") - ) - - p <- cff_parse_person(pp) - - expect_equal(p$email, "second_mail@espol.edu.ec") -}) - - - -test_that("No valid emails", { - pp <- person( - given = "John", - family = "Doe", - role = c("aut", "cre", "trl"), - email = c("first_mail_gmail.com", "second_mail__espol.edu.ec") - ) - - p <- cff_parse_person(pp) - - expect_equal(p, cff_parse_person(person( - given = "John", - family = "Doe" - ))) - - pp2 <- person( - given = "John", - family = "Doe" - ) - - p2 <- cff_parse_person(pp2) - - expect_equal(p, cff_parse_person(person( - given = "John", - family = "Doe" - ))) -}) diff --git a/tests/testthat/test-cff_read_bib_text.R b/tests/testthat/test-cff_read_bib_text.R index fbc1f847..174fe4c9 100644 --- a/tests/testthat/test-cff_read_bib_text.R +++ b/tests/testthat/test-cff_read_bib_text.R @@ -1,8 +1,8 @@ test_that("Errors and messages", { skip_if_not_installed("bibtex", "0.5.0") a_cff <- cff() - expect_snapshot(cff_create_bib_text(a_cff), error = TRUE) - expect_snapshot(cff_create_bib_text("a bad line"), error = TRUE) + expect_snapshot(cff_read_bib_text(a_cff), error = TRUE) + expect_snapshot(cff_read_bib_text("a bad line"), error = TRUE) }) test_that("Read lines", { @@ -27,7 +27,7 @@ test_that("Read lines", { }" ) - list <- cff_create_bib_text(x) + list <- cff_read_bib_text(x) expect_s3_class(list, "cff") expect_length(list, 2) @@ -38,6 +38,6 @@ test_that("Read lines", { tmpbib <- tempfile(fileext = ".bib") writeLines(x, tmpbib) - expect_snapshot(fromfile <- cff_create_bib_text(tmpbib)) + expect_snapshot(fromfile <- cff_read_bib_text(tmpbib)) expect_identical(fromfile, list) }) diff --git a/tests/testthat/test-cff_write.R b/tests/testthat/test-cff_write.R index 84adad4a..3d725ba4 100644 --- a/tests/testthat/test-cff_write.R +++ b/tests/testthat/test-cff_write.R @@ -75,10 +75,9 @@ test_that("Add new keys", { message = "This overwrites fields", abstract = "New abstract", keywords = c("A", "new", "list", "of", "keywords"), - authors = list(cff_parse_person(person( - "Don", "Nadie", - comment = c(website = "error") - ))), + authors = cff_create_cff_person( + person("Don", "Nadie", comment = c(website = "error")) + ), "date-released" = "1900-01-01", "error" = "This is an error" ) @@ -117,15 +116,14 @@ test_that("Append keys", { # It should be a list new_aut <- append( old_aut, - list(cff_parse_person(person( - "New", - "author", - comment = c( - "error" = 123, - website = "https://stackoverflow.com/", - country = "IT" + cff_create_cff_person( + person("New", "author", + comment = c( + "error" = 123, website = "https://stackoverflow.com/", + country = "IT" + ) ) - ))) + ) ) tmp <- tempfile(fileext = ".cff") diff --git a/tests/testthat/test-deprecated.R b/tests/testthat/test-deprecated.R index f4c758a9..05f2ef57 100644 --- a/tests/testthat/test-deprecated.R +++ b/tests/testthat/test-deprecated.R @@ -33,7 +33,7 @@ test_that("cff_from_bibtex", { }" expect_snapshot(flines <- cff_from_bibtex(x)) - expect_identical(flines, cff_create_bib_text(x)) + expect_identical(flines, cff_read_bib_text(x)) }) test_that("write_bib", { @@ -59,3 +59,18 @@ test_that("write_citation", { expect_snapshot(cat(readLines(tmp), sep = "\n")) }) + + +test_that("cff_parse_person", { + p <- person("A", "person") + expect_snapshot(pend <- cff_parse_person(p)) + + expect_identical(pend, cff_create_cff_person(p)) +}) + +test_that("cff_parse_person_bibtex", { + p <- "{Elephant and Castle}" + expect_snapshot(pend <- cff_parse_person_bibtex(p)) + + expect_identical(pend, cff_create_cff_person(p)) +}) diff --git a/tests/testthat/test-utils-persons.R b/tests/testthat/test-utils-persons.R index d1a1893b..17187dbb 100644 --- a/tests/testthat/test-utils-persons.R +++ b/tests/testthat/test-utils-persons.R @@ -5,130 +5,130 @@ test_that("Test first von last", { x <- "AA BB" - res <- as_person_bibtex(x) + res <- create_person_from_txt(x) expect_true(is.list(res)) expect_snapshot(unlist(res)) x <- "AA" - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) x <- "AA bb" - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) x <- "aa" - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) x <- "AA bb CC" - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) x <- "AA bb CC dd EE" - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) x <- "AA {b}B cc dd" - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) x <- "AA \\BB{b} cc dd" - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) x <- "AA {bb} cc DD" - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) x <- "AA bb {cc} DD" - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) x <- "AA {bb} CC" - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) }) test_that("Testing with random names First von Last", { x <- "Jean de La Fontaine" - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) x <- "Diego {Hernandez Sanz}" - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) x <- "Juan Manuel Miramontes" - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) x <- "Juan Manuel {Miramontes Garcia}" - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) x <- "Juan Manuel van Halen" - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) x <- "Bosco {de la Cruz y Ochoa}" - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) }) test_that("Test von Last, First", { x <- "bb CC, AA" - res <- as_person_bibtex(x) + res <- create_person_from_txt(x) expect_true(is.list(res)) expect_snapshot(unlist(res)) x <- "bb CC, aa" - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) x <- "bb CC dd EE, AA" - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) x <- "bb, AA" - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) x <- "BB," - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) }) test_that("Test von Last, First with brackets, etc", { x <- "de Armas, Ana" - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) x <- "{de Armas}, Ana" - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) x <- "{de Armas, Aguero}, Ana" - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) x <- "{de Armas, Aguero}, Ana Maria" - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) }) test_that("Test von Last, Jr, First", { x <- "bb CC,XX, AA" - res <- as_person_bibtex(x) + res <- create_person_from_txt(x) expect_true(is.list(res)) expect_snapshot(unlist(res)) x <- "BB,, AA" - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) x <- "BB, AA," - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) }) test_that("Test von Last, Jr, First with masking", { x <- "Davis, Jr, Sammy" - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) x <- "{Davis, and}, {Jr, another}, Sammy" - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) }) test_that("Rest of cases", { x <- "David, and, Jr, another, Sammy" - res <- as_person_bibtex(x) + res <- create_person_from_txt(x) expect_true(is.list(res)) expect_length(res, 1) @@ -140,41 +140,41 @@ test_that("tames da beast", { # http://tug.ctan.org/info/bibtex/tamethebeast/ttb_en.pdf x <- "jean de la fontaine" - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) x <- "Jean de la fontaine " - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) x <- "Jean {de} la fontaine " - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) x <- "jean {de} {la} fontaine " - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) x <- "Jean {de} {la} fontaine " - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) x <- "Jean De La Fontaine " - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) x <- "jean De la Fontaine " - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) x <- "Jean de La Fontaine " - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) x <- "jean de la fontaine," - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) x <- "de la fontaine, Jean " - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) x <- "De La Fontaine, Jean" - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) x <- "De la Fontaine, Jean" - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) x <- "de La Fontaine, Jean" - expect_snapshot(unlist(as_person_bibtex(x))) + expect_snapshot(unlist(create_person_from_txt(x))) }) diff --git a/vignettes/bibtex_cff.Rmd b/vignettes/bibtex_cff.Rmd index 78512e50..a4e27540 100644 --- a/vignettes/bibtex_cff.Rmd +++ b/vignettes/bibtex_cff.Rmd @@ -135,9 +135,10 @@ capabilities that can be used to read and transform **BibTeX** format into different formats: - `cff_read_bib()` reads \*.bib files. -- `cff_create_bib_text()` can read **BibTeX** entries that are already stored - in a variable. -- A S3 method `toBibtex.cff()` that converts from +- `cff_read_bib_text()` can read **BibTeX** entries that are already stored in + a variable. +- A S3 method `toBibtex.cff()` that converts from `cff` objects to Bibtex + objects (see `utils::tpBibtex()`). ```{r cffbibread, comment="#>"} string <- "@book{einstein1921, @@ -150,7 +151,7 @@ string <- "@book{einstein1921, # To cff library(cffr) -cff_format <- cff_create_bib_text(string) +cff_format <- cff_read_bib_text(string) cff_format @@ -572,13 +573,13 @@ bib <- "@article{article-full, pages = {73+}, note = {This is a full ARTICLE entry}}" -cff_create_bib_text(bib) +cff_read_bib_text(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r echo=FALSE} -toBibtex(cff_create_bib_text(bib)) +toBibtex(cff_read_bib_text(bib)) ``` ### \@book / \@inbook {#book-inbook} @@ -654,13 +655,13 @@ bib <- "@book{book-full, edition = {Second} }" -cff_create_bib_text(bib) +cff_read_bib_text(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r, echo=FALSE} -toBibtex(cff_create_bib_text(bib)) +toBibtex(cff_read_bib_text(bib)) ``` **Examples: \@inbook** @@ -704,13 +705,13 @@ bib <- "@inbook{inbook-full, chapter = {1.2} }" -cff_create_bib_text(bib) +cff_read_bib_text(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r echo=FALSE,} -toBibtex(cff_create_bib_text(bib)) +toBibtex(cff_read_bib_text(bib)) ``` ### \@booklet {#booklet} @@ -763,13 +764,13 @@ bib <- "@booklet{booklet-full, howpublished = {Vernier Art Center} }" -cff_create_bib_text(bib) +cff_read_bib_text(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r echo=FALSE, } -toBibtex(cff_create_bib_text(bib)) +toBibtex(cff_read_bib_text(bib)) ``` ### \@conference / \@inproceedings {#conf_inproc} @@ -834,13 +835,13 @@ bib <- "@inproceedings{inproceedings-full, organization = {The OX Association for Computing Machinery} }" -cff_create_bib_text(bib) +cff_read_bib_text(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r echo=FALSE,} -toBibtex(cff_create_bib_text(bib)) +toBibtex(cff_read_bib_text(bib)) ``` ### \@incollection {#incol} @@ -915,13 +916,13 @@ bib <- "@incollection{incollection-full, edition = {Third} }" -cff_create_bib_text(bib) +cff_read_bib_text(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r echo=FALSE,} -toBibtex(cff_create_bib_text(bib)) +toBibtex(cff_read_bib_text(bib)) ``` ### \@manual @@ -980,13 +981,13 @@ bib <- "@manual{manual-full, edition = {Silver} }" -cff_create_bib_text(bib) +cff_read_bib_text(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r echo=FALSE,} -toBibtex(cff_create_bib_text(bib)) +toBibtex(cff_read_bib_text(bib)) ``` ### \@mastersthesis / \@phdthesis @@ -1049,13 +1050,13 @@ bib <- "@mastersthesis{mastersthesis-full, type = {Master's project} }" -cff_create_bib_text(bib) +cff_read_bib_text(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r, echo=FALSE} -toBibtex(cff_create_bib_text(bib)) +toBibtex(cff_read_bib_text(bib)) ``` **Examples: \@phdthesis** @@ -1089,13 +1090,13 @@ bib <- "@phdthesis{phdthesis-full, type = {{PhD} Dissertation} }" -cff_create_bib_text(bib) +cff_read_bib_text(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r echo=FALSE,} -toBibtex(cff_create_bib_text(bib)) +toBibtex(cff_read_bib_text(bib)) ``` ### \@misc @@ -1152,13 +1153,13 @@ bib <- "@misc{misc-full, howpublished = {Handed out at O'Hare} }" -cff_create_bib_text(bib) +cff_read_bib_text(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r echo=FALSE,} -toBibtex(cff_create_bib_text(bib)) +toBibtex(cff_read_bib_text(bib)) ``` ### \@proceedings @@ -1226,13 +1227,13 @@ bib <- "@proceedings{proceedings-full, organization = {The OX Association for Computing Machinery} }" -cff_create_bib_text(bib) +cff_read_bib_text(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r echo=FALSE,} -toBibtex(cff_create_bib_text(bib)) +toBibtex(cff_read_bib_text(bib)) ``` ### \@techreport @@ -1289,13 +1290,13 @@ bib <- "@techreport{techreport-full, type = {Wishful Research Result} }" -cff_create_bib_text(bib) +cff_read_bib_text(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r echo=FALSE,} -toBibtex(cff_create_bib_text(bib)) +toBibtex(cff_read_bib_text(bib)) ``` ### \@unpublished @@ -1340,13 +1341,13 @@ bib <- "@unpublished{unpublished-minimal, note = {Talk at Fanstord University (this is a minimal UNPUBLISHED entry)} }" -cff_create_bib_text(bib) +cff_read_bib_text(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r echo=FALSE,} -toBibtex(cff_create_bib_text(bib)) +toBibtex(cff_read_bib_text(bib)) ``` ## Appendix A: **\@inbook** in BibTeX and BibLaTeX {#appendix_inbook} @@ -1413,13 +1414,13 @@ bib <- "@inbook{inbook-biblatex, chapter = {4.5} }" -cff_create_bib_text(bib) +cff_read_bib_text(bib) ``` From [CFF]{.underline} to **BibTeX** ```{r echo=FALSE,} -toBibtex(cff_create_bib_text(bib)) +toBibtex(cff_read_bib_text(bib)) ``` ## Appendix B: [CFF key:type]{.underline} values {#appendix_cff_type} diff --git a/vignettes/cffr.Rmd b/vignettes/cffr.Rmd index 413617a0..d7f4db89 100644 --- a/vignettes/cffr.Rmd +++ b/vignettes/cffr.Rmd @@ -158,14 +158,13 @@ chiquito <- person("Gregorio", chiquito # Parse it -chiquito_parsed <- cff_parse_person(chiquito) +chiquito_parsed <- cff_create_cff_person(chiquito) chiquito_parsed # Append to previous authors -# Needs to be append as a list -newauthors <- c(modobject$authors, list(chiquito_parsed)) +newauthors <- c(modobject$authors, chiquito_parsed) newauthors newauthorobject <- cff_create(modobject, keys = list(authors = newauthors)) From b42646227be7b200f937adba877c7b72cfcde1a3 Mon Sep 17 00:00:00 2001 From: dieghernan Date: Thu, 29 Feb 2024 23:52:04 +0000 Subject: [PATCH 22/32] Fix regex for older versions of R --- R/cff_create_cff_person.R | 5 ++++- codemeta.json | 4 ++-- data/cran_to_spdx.rda | Bin 916 -> 907 bytes inst/schemaorg.json | 2 +- pkgdown/_pkgdown.yml | 3 +++ 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/R/cff_create_cff_person.R b/R/cff_create_cff_person.R index 19681508..2c51cccf 100644 --- a/R/cff_create_cff_person.R +++ b/R/cff_create_cff_person.R @@ -259,7 +259,10 @@ create_person_from_txt <- function(as_bib_text) { perl = TRUE ) - commas <- as.character(length(grep(",", unlist(strsplit(protected, "|"))))) + commas <- as.character(lengths(regmatches( + protected, + gregexpr(",", protected) + ))) # Assign the corresponding fun bibtex_name_str <- switch(commas, diff --git a/codemeta.json b/codemeta.json index b7324a74..cde6037e 100644 --- a/codemeta.json +++ b/codemeta.json @@ -14,7 +14,7 @@ "name": "R", "url": "https://r-project.org" }, - "runtimePlatform": "R version 4.3.2 (2023-10-31 ucrt)", + "runtimePlatform": "R version 4.3.2 (2023-10-31)", "provider": { "@id": "https://cran.r-project.org", "@type": "Organization", @@ -200,7 +200,7 @@ }, "isPartOf": "https://ropensci.org", "keywords": ["attribution", "citation", "credit", "citation-files", "cff", "metadata", "r", "r-package", "citation-file-format", "rstats", "ropensci", "cran"], - "fileSize": "944.651KB", + "fileSize": "928.953KB", "citation": [ { "@type": "ScholarlyArticle", diff --git a/data/cran_to_spdx.rda b/data/cran_to_spdx.rda index 0bd1435cae708e762b962cb7a97ec9ae0c12fef9..33fae2994ac0070f09336d3d8c403ec099798a1f 100644 GIT binary patch literal 907 zcmV;619bdCT4*^jL0KkKS>9zRc>o3)f6)K`|B!TNe+NIOUueH)-|#>H0ssI3&;$PH zMJcLQXdxn$LqY0#n*AnjWXMA%O-!8VroodWVq>8hVWZpyrJl8ess_0%@QOhK3*w7>x`OkO2~jFe$2d zXwawXnjVu)lzN&O4WcqVOllbqQxUPLFe>dz=&r?fZ9@1WKjajLMimfmJ2mUYPPPXx zj*Ybg1&%$?qNxPUhn5dz<$F-rN4=W12TeQiUlnt8<0b6Yj7qp-b#>kRV}V z`Fj%x=Ccapm4j`8K(v)wkqAVgQwwED3edK<)|qCdvuV!(PmL8wD1-^BC2K`h&j3QP zL0u36=!%xXc0(x;6=KLKJiY@Gylf7R8cZa*UN&lz^7{|Bd%yVmS_cO2<5+P%UUOvX zSz@tbnf*NMPJ>Nh!Uq@-^UcwV*1cylE@5sg3T+_5-MXcG?wWWD;2vA3o z&8+4_l-WR}LCwk=!sx_DBpAAMWvtJbNN+NfJb=(2mY@It literal 916 zcmV;F18e*riwFP!000002F+JXPuoBccH=-unm|gSm3lH&4Vna`W6qYoKWTU7l z6%~ibghjRFILLPBCXZ^{hliR@wmg?9cUwC}dynSrJ>vGR3u>%N zH7^9s2jmCN)MFU+jvqQf=!^lyHV7uMxN$r@(R46&?O7nkAL|Ucu8wsheU#`}%-}Kg zct}p6f|sD3J$Vg6?f4@&g<#mx_GkbE&SM;}8efM{{dWsb-+YHfc*I(skDzQMQuc9 z1!Aohx0C}Wz@61{kAy z&Yir|26bnZ!6Z%^)E>i>qx;N-Fqt-QvgfmsNZIvBwAm0ab2)67k|eIP;A`!G#>mpM z^iZ=B9+QksbB1dyET}lE|DM>xOF{e0ntpR!*DkoCa;-+&L=l6|gqH=3{IpT6Ecq3S zYeuoij3tXyK2yb3u8OVv0aVXC*O@pWx)Nr+Lh>6AcYHg*)N{XbpuvMrDCh&%#!Z-b zl8jZht*40o7ZBOVmSVVL106tdg;alC4R?J;u`wz#?3 zjfZj`(dQt-@SQK_UFh<3UL@mGl-u{nq~C0?X$Sm-txHN&hBcFxl`GI@LE+XjzF=3V z{08ZT_c^er!9v~w*h7PCW`0WWB0K*ZUKH0qz)3MkD`ZhnY^`QN@PAw(38+v}F}WMf z%vENY4z3n27R)i3_&a+GMMZe~UGnS+3epi+%Hr$8?&+{g{tFEUC+Ffm{5BSleFUss q*`d9A?PHSyz3NXt?~+F~qDOtlUw3zRKk(1b=<^FCJhi~74*&oy4!s@# diff --git a/inst/schemaorg.json b/inst/schemaorg.json index dec7a9c6..8056af78 100644 --- a/inst/schemaorg.json +++ b/inst/schemaorg.json @@ -26,6 +26,6 @@ "name": "Comprehensive R Archive Network (CRAN)", "url": "https://cran.r-project.org" }, - "runtimePlatform": "R version 4.3.2 (2023-10-31 ucrt)", + "runtimePlatform": "R version 4.3.2 (2023-10-31)", "version": "0.5.0.9000" } diff --git a/pkgdown/_pkgdown.yml b/pkgdown/_pkgdown.yml index 7c8fe68b..60e9eab7 100644 --- a/pkgdown/_pkgdown.yml +++ b/pkgdown/_pkgdown.yml @@ -7,6 +7,9 @@ template: repo: branch: main +development: + mode: auto + # reference: # - title: Main function # desc: >- From 3d54999731e75533064c80a8cee7385aed5021f5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 1 Mar 2024 00:07:29 +0000 Subject: [PATCH 23/32] revdepcheck --- revdep/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/revdep/README.md b/revdep/README.md index 1226d486..3048da07 100644 --- a/revdep/README.md +++ b/revdep/README.md @@ -10,8 +10,8 @@ |collate |en_US.UTF-8 | |ctype |en_US.UTF-8 | |tz |UTC | -|date |2024-02-19 | -|pandoc |2.19.2 @ /usr/local/bin/pandoc | +|date |2024-03-01 | +|pandoc |3.1.11 @ /usr/local/bin/pandoc | # Dependencies From b5b468eb222ed6da2a029e6418ee68e0fd0e8563 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 1 Mar 2024 00:24:02 +0000 Subject: [PATCH 24/32] Update docs with pkgdev --- CITATION.cff | 3 +++ README.md | 11 +++++++---- codemeta.json | 2 +- data/cran_to_spdx.rda | Bin 907 -> 916 bytes 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/CITATION.cff b/CITATION.cff index f6be3074..b81cdae3 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -138,6 +138,9 @@ references: email: jeroen@berkeley.edu orcid: https://orcid.org/0000-0002-4035-0289 year: '2024' + identifiers: + - type: url + value: https://arxiv.org/abs/1403.2805 version: '>= 1.7.2' - type: software title: jsonvalidate diff --git a/README.md b/README.md index 5f2cf148..c6e8b3d8 100644 --- a/README.md +++ b/README.md @@ -72,9 +72,8 @@ file and the `CITATION` file (if present) of your package. Note that **cffr** works best if your package pass `R CMD check/devtools::check()`. -As per 2024-02-29 there are at least 306 repos on GitHub using **cffr**. -[Check them out -here](https://github.com/search?q=cffr%20path%3A**%2FCITATION.cff&type=code). +See [some projects already using +**cffr**](https://github.com/search?q=cffr%20path%3A**%2FCITATION.cff&type=code). ### Installation @@ -385,6 +384,9 @@ test <- cff_create("rmarkdown") email: jeroen@berkeley.edu orcid: https://orcid.org/0000-0002-4035-0289 year: '2024' + identifiers: + - type: url + value: https://arxiv.org/abs/1403.2805 - type: software title: knitr abstract: 'knitr: A General-Purpose Package for Dynamic Report Generation in R' @@ -898,7 +900,8 @@ for more info. ## References -
+
diff --git a/codemeta.json b/codemeta.json index cde6037e..95c061eb 100644 --- a/codemeta.json +++ b/codemeta.json @@ -200,7 +200,7 @@ }, "isPartOf": "https://ropensci.org", "keywords": ["attribution", "citation", "credit", "citation-files", "cff", "metadata", "r", "r-package", "citation-file-format", "rstats", "ropensci", "cran"], - "fileSize": "928.953KB", + "fileSize": "931.177KB", "citation": [ { "@type": "ScholarlyArticle", diff --git a/data/cran_to_spdx.rda b/data/cran_to_spdx.rda index 33fae2994ac0070f09336d3d8c403ec099798a1f..54267cd1b0840c06bdfc1e554391353a60cd0a13 100644 GIT binary patch literal 916 zcmV;F18e*riwFP!000001I<@UPuoBccH=-unm|gSm3lH&4Vna`W6qYoKWTU7l z6%~ibghjRFILLPBCXZ^{hliR@wmg?9cUwC}dynSrJ>vGR3u>%N zH7^9s2jmCN)MFU+jvqQf=!^lyHV7uMxN$r@(R46&?O7nkAL|Ucu8wsheU#`}%-}Kg zct}p6f|sD3J$Vg6?f4@&g<#mx_GkbE&SM;}8efM{{dWsb-+YHfc*I(skDzQMQuc9 z1!Aohx0C}Wz@61{kAy z&Yir|26bnZ!6Z%^)E>i>qx;N-Fqt-QvgfmsNZIvBwAm0ab2)67k|eIP;A`!G#>mpM z^iZ=B9+QksbB1dyET}lE|DM>xOF{e0ntpR!*DkoCa;-+&L=l6|gqH=3{IpT6Ecq3S zYeuoij3tXyK2yb3u8OVv0aVXC*O@pWx)Nr+Lh>6AcYHg*)N{XbpuvMrDCh&%#!Z-b zl8jZht*40o7ZBOVmSVVL106tdg;alC4R?J;u`wz#?3 zjfZj`(dQt-@SQK_UFh<3UL@mGl-u{nq~C0?X$Sm-txHN&hBcFxl`GI@LE+XjzF=3V z{08ZT_c^er!9v~w*h7PCW`0WWB0K*ZUKH0qz)3MkD`ZhnY^`QN@PAw(38+v}F}WMf z%vENY4z3n27R)i3_&a+GMMZe~UGnS+3epi+%Hr$8?&+{g{tFEUC+Ffm{5BSleFUss q*`d9A?PHSyz3NXt?~+F~qDOtlUw3zRKk(1b=<^FCJhi~74*&onZM_=+ literal 907 zcmV;619bdCT4*^jL0KkKS>9zRc>o3)f6)K`|B!TNe+NIOUueH)-|#>H0ssI3&;$PH zMJcLQXdxn$LqY0#n*AnjWXMA%O-!8VroodWVq>8hVWZpyrJl8ess_0%@QOhK3*w7>x`OkO2~jFe$2d zXwawXnjVu)lzN&O4WcqVOllbqQxUPLFe>dz=&r?fZ9@1WKjajLMimfmJ2mUYPPPXx zj*Ybg1&%$?qNxPUhn5dz<$F-rN4=W12TeQiUlnt8<0b6Yj7qp-b#>kRV}V z`Fj%x=Ccapm4j`8K(v)wkqAVgQwwED3edK<)|qCdvuV!(PmL8wD1-^BC2K`h&j3QP zL0u36=!%xXc0(x;6=KLKJiY@Gylf7R8cZa*UN&lz^7{|Bd%yVmS_cO2<5+P%UUOvX zSz@tbnf*NMPJ>Nh!Uq@-^UcwV*1cylE@5sg3T+_5-MXcG?wWWD;2vA3o z&8+4_l-WR}LCwk=!sx_DBpAAMWvtJbNN+NfJb=(2mY@It From b6a79d86bdba928a2de51cc535862bb87ab91643 Mon Sep 17 00:00:00 2001 From: dieghernan Date: Fri, 1 Mar 2024 11:20:52 +0000 Subject: [PATCH 25/32] New API review and NEWS --- CITATION.cff | 3 - NAMESPACE | 2 +- NEWS.md | 97 +++++++++--------- R/as_bibentry.R | 3 + ...ff_create_cff_person.R => as_cff_person.R} | 53 ++++++---- R/cff-methods.R | 15 ++- R/cff.R | 2 +- R/cff_create.R | 4 +- R/cff_parse_citation.R | 2 +- R/deprecated.R | 24 ++--- R/parse_citation.R | 4 +- R/utils-read-description.R | 4 +- README.md | 76 +------------- codemeta.json | 2 +- data/cran_to_spdx.rda | Bin 916 -> 907 bytes inst/WORDLIST | 5 +- man/as_bibentry.Rd | 12 ++- ..._create_cff_person.Rd => as_cff_person.Rd} | 58 ++++++----- man/cff.Rd | 2 +- man/cff_create.Rd | 4 +- man/chunks/person.Rmd | 4 +- man/deprecated_cff_person.Rd | 14 +-- ..._create_cff_person.md => as_cff_person.md} | 24 ++--- tests/testthat/_snaps/deprecated.md | 4 +- tests/testthat/test-as_bibentry.R | 4 +- ...eate_cff_person.R => test-as_cff_person.R} | 70 ++++++------- tests/testthat/test-cff-methods.R | 2 +- tests/testthat/test-cff_write.R | 4 +- tests/testthat/test-deprecated.R | 4 +- vignettes/cffr.Rmd | 2 +- 30 files changed, 232 insertions(+), 272 deletions(-) rename R/{cff_create_cff_person.R => as_cff_person.R} (88%) rename man/{cff_create_cff_person.Rd => as_cff_person.Rd} (65%) rename tests/testthat/_snaps/{cff_create_cff_person.md => as_cff_person.md} (73%) rename tests/testthat/{test-cff_create_cff_person.R => test-as_cff_person.R} (63%) diff --git a/CITATION.cff b/CITATION.cff index b81cdae3..f6be3074 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -138,9 +138,6 @@ references: email: jeroen@berkeley.edu orcid: https://orcid.org/0000-0002-4035-0289 year: '2024' - identifiers: - - type: url - value: https://arxiv.org/abs/1403.2805 version: '>= 1.7.2' - type: software title: jsonvalidate diff --git a/NAMESPACE b/NAMESPACE index f36d34a7..3aaf4a84 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -9,9 +9,9 @@ S3method(tail,cff) S3method(toBibtex,cff) export(as.cff) export(as_bibentry) +export(as_cff_person) export(cff) export(cff_create) -export(cff_create_cff_person) export(cff_extract_to_bibtex) export(cff_from_bibtex) export(cff_gha_update) diff --git a/NEWS.md b/NEWS.md index 70bfcf12..a5af4acf 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,35 +1,51 @@ # cffr (development version) -## Major changes +This is a major release with some notable changes. The change mainly **affects +to non-core functions**, hence the natural workflow (`cff_create()` → +`cff_write()` → `cff_validate()`) shouldn't be affected. -Now `class()` of `cff` objects are `c("cff", "list")` instead of single value -(`"cff"`). +## Major changes ### API -The API has been reviewed to provide more clarity on names and better -maintenance. Now each function does less things but better. The change **affects -to non-core functions** that an user would rarely call, while `cff_create()`, -`cff_write()` and `cff_validate()` (core functions) hasn't been visibly -modified: - +The API has been completely reviewed to provide more clarity on functions naming +and to facilitate internal maintenance. This change **only** **affects to +non-core functions**. Now each function does less things but does it better. The +old API [has been +deprecated](https://lifecycle.r-lib.org/articles/stages.html#deprecated) and it +would warn when used, providing advice on the replacement function. + +#### Deprecations + +- `cff_to_bibtex()` and `cff_extract_to_bibtex()`: replaced by + `as_bibentry()`. +- `cff_from_bibtex()`: replaced by `cff_read_bib()` (for `*.bib` files) and + `cff_read_bib_text()` (for character strings). +- `write_bib()` and `write_citation()` : replaced by `cff_write_bib()` and + `cff_write_citation()` respectively. +- `cff_parse_person()` and `cff_parse_person_bibtex()`: replacedy by + `as_cff_person()`. - The conversion from `cff` to `bibentry` is performed now by a new function `as_bibentry()`. Previous names of this function were `cff_to_bibtex()` and `cff_extract_to_bibtex()` that are now deprecated. -- Now reading from external files is performed exclusively by `cff_read()` and - additionally by the more-specific new functions `cff_read_cff_citation()`, - `cff_read_description()`, `cff_read_citation()` and `cff_read_bib()`. It is - also possible to read BibTeX lines with `cff_read_bib_text()`. Previous - function `cff_from_bibtex()` is now deprecated. -- `write_bib()` and `write_citation()` deprecated by `cff_write_bib()` and - `cff_write_citation()`. -- `cff_parse_person()` and `cff_parse_person_bibtex()` are deprecated. Use - `cff_create_cff_person()` instead. - -### Methods -- New methods added: +#### New capabilities + +- Now reading from external files is performed exclusively by `cff_read()` + (that is designed to fit all supported file types on a single entry point) + and the new specific readers (that are used under the hood by `cff_read()`), + namely: + - `cff_read_cff_citation()`, + - `cff_read_description()`, + - `cff_read_citation()` + - `cff_read_bib()`. + +## Other changes +- Minimum **R** version required now is **4.0.0**. +- Now `class()` of `cff` objects are `c("cff", "list")` instead of single + value (`"cff"`). +- New methods added: - `as.data.frame.cff().` - `as.person.cff()`, that provides results **only** for CFF keys defined as @@ -39,16 +55,13 @@ modified: (e.g. `authors`, `contacts`, `editors`, `publisher`). - `head.cff()`, `tail.cff()`. - `toBibtex.cff()`. - -## Other changes - -- Minimum **R** version required now is **4.0.0**. - -### BibTeX crosswalk - -- **\@inbook** and **\@book** gains a new value on [CFF]{.underline} when - **series** is provided: [collection-type: book-series.]{.underline} -- Review and update `vignette("bibtex_cff", package = "cffr")`. +- Update of BibTeX crosswalk (see `vignette("bibtex_cff", package = "cffr")`) + and consecuently changes in the mapping performed by `as_bibtex()` + `cff_parse_citation()`: + - **\@inbook** and **\@book** gains a new value on [CFF]{.underline} when + **series** is provided: [collection-type: book-series.]{.underline} + - Can handle BibLaTeX **\@inbook**, that differs significantly from BibTeX + **\@inbook**. # cffr 0.5.0 @@ -65,7 +78,7 @@ modified: ## Enhancements - Additional authors of a **R** package can be now included based on the role - on the DESCRIPTION file, via the parameter `authors_roles` (#49). + on the `DESCRIPTION` file, via the parameter `authors_roles` (#49). - New message interface based on [**cli**](https://cli.r-lib.org/) capabilities. @@ -93,7 +106,7 @@ modified: # cffr 0.3.0 -- `preferred-citation` is only produced when a CITATION (**R**) file has been +- `preferred-citation` is only produced when a `CITATION` **R** file has been provided with the package (#37). - Improve email handling on authors. - Add `cff_read()` function. This functionality was already implemented on @@ -110,7 +123,6 @@ modified: # cffr 0.2.1 - GitHub Action now runs only on `master` or `main`branch. - - Better handling of references # cffr 0.2.0 @@ -118,31 +130,22 @@ modified: - Now **cffr** extracts also information of the package dependencies and adds the main citation of the dependencies to the `references` field, using `citation(auto = TRUE)`. - - New `dependencies` parameter on `cff_create()` and `cff_write()`. - - Other improvements on `cff_parse_citation():` - - `cff_parse_citation()` extracts more information of authors, based on - the fields provided on the DESCRIPTION file. - + the fields provided on the `DESCRIPTION` file. - `cff_parse_citation()` does a better job extracting information from `bibentry()` /BibTeX and mapping it to `preferred-citation/references` fields of CFF. - - Add new functions for working with git pre-commit hooks [![Experimental](https://lifecycle.r-lib.org/articles/figures/lifecycle-experimental.svg)](https://lifecycle.r-lib.org/articles/stages.html#experimental): - - `cff_git_hook_install()` - `cff_git_hook_remove()` - - New BibTeX functions: - - `cff_extract_to_bibtex()` - `cff_to_bibtex()` - `cff_parse_person_bibtex()` - `write_bib()` - - Add a new dependency: `lifecycle`. # cffr 0.1.1 @@ -159,16 +162,16 @@ modified: # cffr 0.0.2 -- `cffr` is part now of rOpenSci. +- **cffr** is part now of rOpenSci. - Update on docs and README. - Add fuzzy match on `keys` parameter. - New dataset: `cran_to_spdx`. - Add DOI - Citation of installed packages extracted using `citation().` -- Auto-generating `preferred-citation` key from DESCRIPTION. +- Auto-generating `preferred-citation` key from `DESCRIPTION`. - Rename `cff_schema_definitions_reference()` to `cff_schema_definitions_refs()`. -- "repository" key is supported. +- `repository` key is supported. - Added vignette: `vignette("crosswalk", package = "cffr")`. - Add support to Bioconductor packages. - New function: `cff_gha_update()`. diff --git a/R/as_bibentry.R b/R/as_bibentry.R index d7e3b553..e12f873b 100644 --- a/R/as_bibentry.R +++ b/R/as_bibentry.R @@ -15,6 +15,9 @@ #' object and performs a mapping of the metadata to BibTeX, according to #' `vignette("bibtex_cff", "cffr")`. #' +#' The inverse transformation (`bibtex` object to `cff` reference) can be done +#' with [cff_parse_citation()]. +#' #' @seealso #' [utils::bibentry()] #' diff --git a/R/cff_create_cff_person.R b/R/as_cff_person.R similarity index 88% rename from R/cff_create_cff_person.R rename to R/as_cff_person.R index 2c51cccf..8739eff3 100644 --- a/R/cff_create_cff_person.R +++ b/R/as_cff_person.R @@ -13,18 +13,21 @@ #' #' ``` #' -#' [cff_create_cff_person()] can convert the following objects: +#' [as_cff_person()] can convert the following objects: #' - Objects with class `person` as provided by [utils::person()]. #' - A `character` string with the definition of an author or several authors, #' using the standard BibTeX notation. See Markey (2007) for a full #' explanation. #' +#' [as_cff_person()] would recognize if the input should be converted using the +#' CFF reference `person` or `entity`. +#' #' @seealso #' Examples in `vignette("cffr", "cffr")` and [utils::person()]. #' #' @export -#' @rdname cff_create_cff_person -#' @name cff_create_cff_person +#' @rdname as_cff_person +#' @name as_cff_person #' @order 1 #' #' @family coercing @@ -35,7 +38,7 @@ #' See **Examples**. #' #' @return -#' `cff_create_cff_person()` returns A list of persons or entities with class +#' `as_cff_person()` returns A list of persons or entities with class #' `cff` converted to the #' ```{r, echo=FALSE, results='asis'} #' @@ -48,21 +51,26 @@ #' #' @details #' -#' `cff_create_cff_person()` uses a custom algorithm that tries to break a name -#' as explained in Section 11 of "Tame the BeaST" (Markey, 2007): -#' - `First von Last` -#' - `von Last, First` -#' - `von Last, Jr, First` +#' `as_cff_person()` uses a custom algorithm that tries to break a name as +#' explained in Section 11 of "Tame the BeaST" (Markey, 2007) (see also +#' Decoret, 2007): +#' +#' - `First von Last`. +#' - `von Last, First`. +#' - `von Last, Jr, First`. #' -#' `First` is mapped to the CFF field `given-names`, `von` to `name-particle`, -#' `Last` to `family-names` and `Jr` to `name-suffix`. +#' Mapping is performed as follows: +#' - `First` is mapped to the CFF field `given-names`. +#' - `von` is mapped to the CFF field `name-particle`. +#' - `Last` is mapped to the CFF field `family-names`. +#' - `Jr` is mapped to the CFF field `name-suffix`. #' #' In the case of entities, the whole `character` would be mapped to `name`. #' It is a good practice to "protect" entity's names with `{}`: #' #' ```{r child = "man/chunks/person.Rmd"} #' ``` -#' `cff_create_cff_person()` would try to add as many information as possible. +#' `as_cff_person()` would try to add as many information as possible. #' On `character` string coming from [`format(person())`][utils::person()] the #' email and the ORCID would be gathered as well. #' @@ -73,6 +81,13 @@ #' - Markey, Nicolas. "Tame the BeaST" #' *The B to X of BibTeX, Version 1.4* (October 2007). #' . +#' - Decoret X (2007). "A summary of BibTex." +#' ```{r, echo=FALSE, results='asis'} +#' +#' cat(paste0("")) +#' +#' ``` #' #' See **Examples** for more information. #' @@ -90,7 +105,7 @@ #' #' a_person #' -#' cff_person <- cff_create_cff_person(a_person) +#' cff_person <- as_cff_person(a_person) #' #' cff_person #' @@ -102,21 +117,21 @@ #' "Julio Iglesias ", #' "()" #' ) -#' cff_create_cff_person(a_str) +#' as_cff_person(a_str) #' #' # Several persons #' persons <- c(person("Clark", "Kent"), person("Lois", "Lane")) #' -#' cff_create_cff_person(persons) +#' as_cff_person(persons) #' #' # Or you can use BibTeX style if you prefer #' #' x <- "Frank Sinatra and Dean Martin and Davis, Jr., Sammy and Joey Bishop" #' -#' cff_create_cff_person(x) +#' as_cff_person(x) #' -#' cff_create_cff_person("Herbert von Karajan") -cff_create_cff_person <- function(person) { +#' as_cff_person("Herbert von Karajan") +as_cff_person <- function(person) { if (any(is.null(person), is.na(person), length(person) == 0)) { return(NULL) } @@ -126,7 +141,7 @@ cff_create_cff_person <- function(person) { verbopt <- getOption("cffr_message_verbosity", "none") if (verbopt == "debug") { cli::cli_alert_info( - "In {.fn cff_create_cff_person} using internal for {.val {hint}}." + "In {.fn as_cff_person} using internal for {.val {hint}}." ) } diff --git a/R/cff-methods.R b/R/cff-methods.R index 81a80f02..93607b70 100644 --- a/R/cff-methods.R +++ b/R/cff-methods.R @@ -44,16 +44,16 @@ as.data.frame.cff <- function(x, row.names = NULL, optional = FALSE, ...) { return(the_df) } -#' @rdname cff_create_cff_person +#' @rdname as_cff_person #' @name as.person.cff #' @order 2 #' #' @description -#' Additionally, it is also provided a method for [as.person()], that can -#' convert [`cff`][cff-class] objects to `person` objects as -#' provided by [utils::person()]. #' -#' This method only works with CFF persons, not with full `cff` objects. +#' The inverse transformation (`cff` person to [`person`][utils::as.person()]) +#' object can be done through the [as.person.cff()] method. Note that this is +#' expected to be used with a `cff` person, not with a complete `cff` object. +#' #' #' @family s3method #' @export @@ -103,9 +103,8 @@ tail.cff <- function(x, n = 6L, ...) { #' #' @description #' Additionally, it is also provided a method for [toBibtex()], that can -#' convert [`cff`][cff-class] objects to `Bibtex` objects as -#' provided by [utils::toBibtex()]. These objects are character vectors with -#' BibTeX markup. +#' convert [`cff`][cff-class] objects to `Bibtex` objects as provided by +#' [utils::toBibtex()]. These objects are character vectors with BibTeX markup. #' #' @family s3method #' @export diff --git a/R/cff.R b/R/cff.R index 791e9d8b..d061270e 100644 --- a/R/cff.R +++ b/R/cff.R @@ -52,7 +52,7 @@ #' test <- cff( #' title = "Manipulating files", #' keywords = c("A", "new", "list", "of", "keywords"), -#' authors = cff_create_cff_person("New author") +#' authors = as_cff_person("New author") #' ) #' test #' \donttest{ diff --git a/R/cff_create.R b/R/cff_create.R index 6f3367a4..c30c59a0 100644 --- a/R/cff_create.R +++ b/R/cff_create.R @@ -90,7 +90,7 @@ #' message = "This overwrites fields", #' abstract = "New abstract", #' keywords = c("A", "new", "list", "of", "keywords"), -#' authors = cff_create_cff_person("New author") +#' authors = as_cff_person("New author") #' ) #' #' cff_create(demo_file, keys = newkeys) @@ -102,7 +102,7 @@ #' #' new_contact <- append( #' old$contact, -#' cff_create_cff_person(person( +#' as_cff_person(person( #' given = "I am", #' family = "New Contact" #' )) diff --git a/R/cff_parse_citation.R b/R/cff_parse_citation.R index eed68a52..1a8671a0 100644 --- a/R/cff_parse_citation.R +++ b/R/cff_parse_citation.R @@ -123,7 +123,7 @@ cff_parse_citation <- function(bib) { } ## authors ---- - parse_all_authors <- cff_create_cff_person(parsed_fields$authors) + parse_all_authors <- as_cff_person(parsed_fields$authors) parsed_fields$authors <- unique(parse_all_authors) ## other persons---- diff --git a/R/deprecated.R b/R/deprecated.R index d9cdd529..f81a1028 100644 --- a/R/deprecated.R +++ b/R/deprecated.R @@ -190,17 +190,17 @@ write_citation <- function(x, #' #' @description #' -#' `r lifecycle::badge('deprecated')` Please use [cff_create_cff_person()] +#' `r lifecycle::badge('deprecated')` Please use [as_cff_person()] #' #' @rdname deprecated_cff_person #' @export #' @keywords internal #' @family deprecated #' -#' @inheritParams cff_create_cff_person +#' @inheritParams as_cff_person #' @return A person in format `cff`. #' -#' @seealso [cff_create_cff_person()] +#' @seealso [as_cff_person()] #' #' @examples #' # Create a person object @@ -215,7 +215,7 @@ write_citation <- function(x, #' #' a_person #' -#' cff_person <- cff_create_cff_person(a_person) +#' cff_person <- as_cff_person(a_person) #' #' cff_person #' @@ -227,27 +227,27 @@ write_citation <- function(x, #' "Julio Iglesias ", #' "()" #' ) -#' cff_create_cff_person(a_str) +#' as_cff_person(a_str) #' #' # Several persons #' persons <- c(person("Clark", "Kent"), person("Lois", "Lane")) #' -#' cff_create_cff_person(persons) +#' as_cff_person(persons) #' #' # Or you can use BibTeX style if you prefer #' #' x <- "Frank Sinatra and Dean Martin and Davis, Jr., Sammy and Joey Bishop" #' -#' cff_create_cff_person(x) +#' as_cff_person(x) #' -#' cff_create_cff_person("Herbert von Karajan") +#' as_cff_person("Herbert von Karajan") cff_parse_person <- function(person) { if (requireNamespace("lifecycle", quietly = TRUE)) { lifecycle::deprecate_soft( - "1.0.0", "cff_parse_person()", "cff_create_cff_person()" + "1.0.0", "cff_parse_person()", "as_cff_person()" ) } - cff_create_cff_person(person) + as_cff_person(person) } #' @rdname deprecated_cff_person @@ -256,8 +256,8 @@ cff_parse_person <- function(person) { cff_parse_person_bibtex <- function(person) { if (requireNamespace("lifecycle", quietly = TRUE)) { lifecycle::deprecate_soft( - "1.0.0", "cff_parse_person_bibtex()", "cff_create_cff_person()" + "1.0.0", "cff_parse_person_bibtex()", "as_cff_person()" ) } - cff_create_cff_person(person) + as_cff_person(person) } diff --git a/R/parse_citation.R b/R/parse_citation.R index 0a31afdb..802e8014 100644 --- a/R/parse_citation.R +++ b/R/parse_citation.R @@ -143,7 +143,7 @@ building_other_persons <- function(parsed_fields) { names(bibtex) <- NULL - end <- cff_create_cff_person(bibtex) + end <- as_cff_person(bibtex) # If has names then it should be moved to a lower level on a list if (!is.null(names(end))) end <- list(end) @@ -153,7 +153,7 @@ building_other_persons <- function(parsed_fields) { toperson <- others[names(others) %in% toauto_end] - toperson <- lapply(toperson, cff_create_cff_person) + toperson <- lapply(toperson, as_cff_person) # Bind and reorder diff --git a/R/utils-read-description.R b/R/utils-read-description.R index ecffb718..798dd6ac 100644 --- a/R/utils-read-description.R +++ b/R/utils-read-description.R @@ -26,7 +26,7 @@ parse_desc_authors <- function(pkg, authors_roles = c("aut", "cre")) { any(x$role %in% r) }, logical(1))] - parse_all_authors <- cff_create_cff_person(authors) + parse_all_authors <- as_cff_person(authors) parse_all_authors <- unique(parse_all_authors) parse_all_authors @@ -42,7 +42,7 @@ parse_desc_contacts <- function(pkg) { "cre" %in% x$role }, logical(1))] - parse_all_contacts <- cff_create_cff_person(contact) + parse_all_contacts <- as_cff_person(contact) parse_all_contacts <- unique(parse_all_contacts) parse_all_contacts } diff --git a/README.md b/README.md index c6e8b3d8..348f5085 100644 --- a/README.md +++ b/README.md @@ -72,8 +72,9 @@ file and the `CITATION` file (if present) of your package. Note that **cffr** works best if your package pass `R CMD check/devtools::check()`. -See [some projects already using -**cffr**](https://github.com/search?q=cffr%20path%3A**%2FCITATION.cff&type=code). +As per 2024-03-01 there are at least 306 repos on GitHub using **cffr**. +[Check them out +here](https://github.com/search?q=cffr%20path%3A**%2FCITATION.cff&type=code). ### Installation @@ -384,9 +385,6 @@ test <- cff_create("rmarkdown") email: jeroen@berkeley.edu orcid: https://orcid.org/0000-0002-4035-0289 year: '2024' - identifiers: - - type: url - value: https://arxiv.org/abs/1403.2805 - type: software title: knitr abstract: 'knitr: A General-Purpose Package for Dynamic Report Generation in R' @@ -563,26 +561,6 @@ test <- cff_create("rmarkdown") - family-names: Chirico. given-names: Michael year: '2024' - - type: software - title: dygraphs - abstract: 'dygraphs: Interface to ''Dygraphs'' Interactive Time Series Charting - Library' - notes: Suggests - url: https://github.com/rstudio/dygraphs - repository: https://CRAN.R-project.org/package=dygraphs - authors: - - family-names: Vanderkam - given-names: Dan - website: http://dygraphs.com/ - - family-names: Allaire - given-names: JJ - - family-names: Owen - given-names: Jonathan - - family-names: Gromer - given-names: Daniel - - family-names: Thieurmel - given-names: Benoit - year: '2024' - type: software title: fs abstract: 'fs: Cross-Platform File System Operations Based on ''libuv''' @@ -599,26 +577,6 @@ test <- cff_create("rmarkdown") given-names: Gábor email: csardi.gabor@gmail.com year: '2024' - - type: software - title: rsconnect - abstract: 'rsconnect: Deploy Docs, Apps, and APIs to ''Posit Connect'', ''shinyapps.io'', - and ''RPubs''' - notes: Suggests - url: https://rstudio.github.io/rsconnect/ - repository: https://CRAN.R-project.org/package=rsconnect - authors: - - family-names: Atkins - given-names: Aron - email: aron@posit.co - - family-names: Allen - given-names: Toph - - family-names: Wickham - given-names: Hadley - - family-names: McPherson - given-names: Jonathan - - family-names: Allaire - given-names: JJ - year: '2024' - type: software title: downlit abstract: 'downlit: Syntax Highlighting and Automatic Linking' @@ -631,19 +589,6 @@ test <- cff_create("rmarkdown") email: hadley@posit.co year: '2024' version: '>= 0.4.0' - - type: software - title: katex - abstract: 'katex: Rendering Math to HTML, ''MathML'', or R-Documentation Format' - notes: Suggests - url: https://docs.ropensci.org/katex/ - repository: https://CRAN.R-project.org/package=katex - authors: - - family-names: Ooms - given-names: Jeroen - email: jeroen@berkeley.edu - orcid: https://orcid.org/0000-0002-4035-0289 - year: '2024' - version: '>= 1.4.0' - type: software title: sass abstract: 'sass: Syntactically Awesome Style Sheets (''Sass'')' @@ -754,18 +699,6 @@ test <- cff_create("rmarkdown") given-names: Davis email: davis@posit.co year: '2024' - - type: software - title: cleanrmd - abstract: 'cleanrmd: Clean Class-Less ''R Markdown'' HTML Documents' - notes: Suggests - url: https://pkg.garrickadenbuie.com/cleanrmd/ - repository: https://CRAN.R-project.org/package=cleanrmd - authors: - - family-names: Aden-Buie - given-names: Garrick - email: garrick@adenbuie.com - orcid: https://orcid.org/0000-0002-7111-0077 - year: '2024' - type: software title: withr abstract: 'withr: Run Code ''With'' Temporarily Modified Global State' @@ -900,8 +833,7 @@ for more info. ## References -
+
diff --git a/codemeta.json b/codemeta.json index 95c061eb..fc6f3580 100644 --- a/codemeta.json +++ b/codemeta.json @@ -200,7 +200,7 @@ }, "isPartOf": "https://ropensci.org", "keywords": ["attribution", "citation", "credit", "citation-files", "cff", "metadata", "r", "r-package", "citation-file-format", "rstats", "ropensci", "cran"], - "fileSize": "931.177KB", + "fileSize": "929.883KB", "citation": [ { "@type": "ScholarlyArticle", diff --git a/data/cran_to_spdx.rda b/data/cran_to_spdx.rda index 54267cd1b0840c06bdfc1e554391353a60cd0a13..33fae2994ac0070f09336d3d8c403ec099798a1f 100644 GIT binary patch literal 907 zcmV;619bdCT4*^jL0KkKS>9zRc>o3)f6)K`|B!TNe+NIOUueH)-|#>H0ssI3&;$PH zMJcLQXdxn$LqY0#n*AnjWXMA%O-!8VroodWVq>8hVWZpyrJl8ess_0%@QOhK3*w7>x`OkO2~jFe$2d zXwawXnjVu)lzN&O4WcqVOllbqQxUPLFe>dz=&r?fZ9@1WKjajLMimfmJ2mUYPPPXx zj*Ybg1&%$?qNxPUhn5dz<$F-rN4=W12TeQiUlnt8<0b6Yj7qp-b#>kRV}V z`Fj%x=Ccapm4j`8K(v)wkqAVgQwwED3edK<)|qCdvuV!(PmL8wD1-^BC2K`h&j3QP zL0u36=!%xXc0(x;6=KLKJiY@Gylf7R8cZa*UN&lz^7{|Bd%yVmS_cO2<5+P%UUOvX zSz@tbnf*NMPJ>Nh!Uq@-^UcwV*1cylE@5sg3T+_5-MXcG?wWWD;2vA3o z&8+4_l-WR}LCwk=!sx_DBpAAMWvtJbNN+NfJb=(2mY@It literal 916 zcmV;F18e*riwFP!000001I<@UPuoBccH=-unm|gSm3lH&4Vna`W6qYoKWTU7l z6%~ibghjRFILLPBCXZ^{hliR@wmg?9cUwC}dynSrJ>vGR3u>%N zH7^9s2jmCN)MFU+jvqQf=!^lyHV7uMxN$r@(R46&?O7nkAL|Ucu8wsheU#`}%-}Kg zct}p6f|sD3J$Vg6?f4@&g<#mx_GkbE&SM;}8efM{{dWsb-+YHfc*I(skDzQMQuc9 z1!Aohx0C}Wz@61{kAy z&Yir|26bnZ!6Z%^)E>i>qx;N-Fqt-QvgfmsNZIvBwAm0ab2)67k|eIP;A`!G#>mpM z^iZ=B9+QksbB1dyET}lE|DM>xOF{e0ntpR!*DkoCa;-+&L=l6|gqH=3{IpT6Ecq3S zYeuoij3tXyK2yb3u8OVv0aVXC*O@pWx)Nr+Lh>6AcYHg*)N{XbpuvMrDCh&%#!Z-b zl8jZht*40o7ZBOVmSVVL106tdg;alC4R?J;u`wz#?3 zjfZj`(dQt-@SQK_UFh<3UL@mGl-u{nq~C0?X$Sm-txHN&hBcFxl`GI@LE+XjzF=3V z{08ZT_c^er!9v~w*h7PCW`0WWB0K*ZUKH0qz)3MkD`ZhnY^`QN@PAw(38+v}F}WMf z%vENY4z3n27R)i3_&a+GMMZe~UGnS+3epi+%Hr$8?&+{g{tFEUC+Ffm{5BSleFUss q*`d9A?PHSyz3NXt?~+F~qDOtlUw3zRKk(1b=<^FCJhi~74*&onZM_=+ diff --git a/inst/WORDLIST b/inst/WORDLIST index ee3183f6..83eac8ca 100644 --- a/inst/WORDLIST +++ b/inst/WORDLIST @@ -3,16 +3,18 @@ Arfon BIBTEXTING BeaST BibLaTeX +BibTex +Bibtex Bitbucket Bliven Boettiger -BugReports CFF CMD Cabunoc Chue CodeMeta DOI +Decoret Druskat Easi Egon @@ -69,7 +71,6 @@ cli codecov codemeta codemetar -coercible crossref doi dois diff --git a/man/as_bibentry.Rd b/man/as_bibentry.Rd index 50e9d4e6..53317ed5 100644 --- a/man/as_bibentry.Rd +++ b/man/as_bibentry.Rd @@ -51,10 +51,12 @@ The function tries to map the information of the source \code{x} into a \code{cf object and performs a mapping of the metadata to BibTeX, according to \code{vignette("bibtex_cff", "cffr")}. +The inverse transformation (\code{bibtex} object to \code{cff} reference) can be done +with \code{\link[=cff_parse_citation]{cff_parse_citation()}}. + Additionally, it is also provided a method for \code{\link[=toBibtex]{toBibtex()}}, that can -convert \code{\link[=cff-class]{cff}} objects to \code{Bibtex} objects as -provided by \code{\link[utils:toLatex]{utils::toBibtex()}}. These objects are character vectors with -BibTeX markup. +convert \code{\link[=cff-class]{cff}} objects to \code{Bibtex} objects as provided by +\code{\link[utils:toLatex]{utils::toBibtex()}}. These objects are character vectors with BibTeX markup. } \examples{ \donttest{ @@ -124,10 +126,10 @@ Other functions for working with BibTeX format: \code{\link{encoded_utf_to_latex}()} Other functions for converting between \strong{R} classes: -\code{\link{cff_create_cff_person}()} +\code{\link{as_cff_person}()} Other S3 Methods for \code{cff}: -\code{\link{cff_create_cff_person}()} +\code{\link{as_cff_person}()} } \concept{bibtex} \concept{coercing} diff --git a/man/cff_create_cff_person.Rd b/man/as_cff_person.Rd similarity index 65% rename from man/cff_create_cff_person.Rd rename to man/as_cff_person.Rd index 39a6c259..9da93bc8 100644 --- a/man/cff_create_cff_person.Rd +++ b/man/as_cff_person.Rd @@ -1,11 +1,11 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/cff_create_cff_person.R, R/cff-methods.R -\name{cff_create_cff_person} -\alias{cff_create_cff_person} +% Please edit documentation in R/as_cff_person.R, R/cff-methods.R +\name{as_cff_person} +\alias{as_cff_person} \alias{as.person.cff} \title{Create a person with the corresponding \code{\link[=cff-class]{cff}} structure} \usage{ -cff_create_cff_person(person) +as_cff_person(person) \method{as.person}{cff}(x) } @@ -20,7 +20,7 @@ See \strong{Examples}. \item{x}{\code{cff} object representing a person or entity.} } \value{ -\code{cff_create_cff_person()} returns A list of persons or entities with class +\code{as_cff_person()} returns A list of persons or entities with class \code{cff} converted to the \href{https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md}{Citation File Format schema}. \code{as.person.cff()} returns a \code{person} object. @@ -29,7 +29,7 @@ See \strong{Examples}. Create a \code{person} or \code{entity} as defined by the \href{https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md}{Citation File Format schema}. -\code{\link[=cff_create_cff_person]{cff_create_cff_person()}} can convert the following objects: +\code{\link[=as_cff_person]{as_cff_person()}} can convert the following objects: \itemize{ \item Objects with class \code{person} as provided by \code{\link[utils:person]{utils::person()}}. \item A \code{character} string with the definition of an author or several authors, @@ -37,40 +37,47 @@ using the standard BibTeX notation. See Markey (2007) for a full explanation. } -Additionally, it is also provided a method for \code{\link[=as.person]{as.person()}}, that can -convert \code{\link[=cff-class]{cff}} objects to \code{person} objects as -provided by \code{\link[utils:person]{utils::person()}}. +\code{\link[=as_cff_person]{as_cff_person()}} would recognize if the input should be converted using the +CFF reference \code{person} or \code{entity}. -This method only works with CFF persons, not with full \code{cff} objects. +The inverse transformation (\code{cff} person to \code{\link[utils:person]{person}}) +object can be done through the \code{\link[=as.person.cff]{as.person.cff()}} method. Note that this is +expected to be used with a \code{cff} person, not with a complete \code{cff} object. } \details{ -\code{cff_create_cff_person()} uses a custom algorithm that tries to break a name -as explained in Section 11 of "Tame the BeaST" (Markey, 2007): +\code{as_cff_person()} uses a custom algorithm that tries to break a name as +explained in Section 11 of "Tame the BeaST" (Markey, 2007) (see also +Decoret, 2007): \itemize{ -\item \verb{First von Last} -\item \verb{von Last, First} -\item \verb{von Last, Jr, First} +\item \verb{First von Last}. +\item \verb{von Last, First}. +\item \verb{von Last, Jr, First}. } -\code{First} is mapped to the CFF field \code{given-names}, \code{von} to \code{name-particle}, -\code{Last} to \code{family-names} and \code{Jr} to \code{name-suffix}. +Mapping is performed as follows: +\itemize{ +\item \code{First} is mapped to the CFF field \code{given-names}. +\item \code{von} is mapped to the CFF field \code{name-particle}. +\item \code{Last} is mapped to the CFF field \code{family-names}. +\item \code{Jr} is mapped to the CFF field \code{name-suffix}. +} In the case of entities, the whole \code{character} would be mapped to \code{name}. It is a good practice to "protect" entity's names with \code{{}}: \if{html}{\out{
}}\preformatted{# Don't do entity <- "Elephant and Castle" -cff_create_cff_person(entity) +as_cff_person(entity) #> - name: Elephant #> - name: Castle # Do entity_protect <- "\{Elephant and Castle\}" -cff_create_cff_person(entity_protect) +as_cff_person(entity_protect) #> - name: Elephant and Castle }\if{html}{\out{
}} -\code{cff_create_cff_person()} would try to add as many information as possible. +\code{as_cff_person()} would try to add as many information as possible. On \code{character} string coming from \code{\link[utils:person]{format(person())}} the email and the ORCID would be gathered as well. } @@ -87,7 +94,7 @@ a_person <- person( a_person -cff_person <- cff_create_cff_person(a_person) +cff_person <- as_cff_person(a_person) cff_person @@ -99,20 +106,20 @@ a_str <- paste0( "Julio Iglesias ", "()" ) -cff_create_cff_person(a_str) +as_cff_person(a_str) # Several persons persons <- c(person("Clark", "Kent"), person("Lois", "Lane")) -cff_create_cff_person(persons) +as_cff_person(persons) # Or you can use BibTeX style if you prefer x <- "Frank Sinatra and Dean Martin and Davis, Jr., Sammy and Joey Bishop" -cff_create_cff_person(x) +as_cff_person(x) -cff_create_cff_person("Herbert von Karajan") +as_cff_person("Herbert von Karajan") } \references{ \itemize{ @@ -121,6 +128,7 @@ cff_create_cff_person("Herbert von Karajan") \item Markey, Nicolas. "Tame the BeaST" \emph{The B to X of BibTeX, Version 1.4} (October 2007). \url{https://osl.ugr.es/CTAN/info/bibtex/tamethebeast/ttb_en.pdf}. +\item Decoret X (2007). "A summary of BibTex."\url{https://maverick.inria.fr/~Xavier.Decoret/resources/xdkbibtex/bibtex_summary.html#names} } See \strong{Examples} for more information. diff --git a/man/cff.Rd b/man/cff.Rd index e8e856da..cdce2784 100644 --- a/man/cff.Rd +++ b/man/cff.Rd @@ -72,7 +72,7 @@ cff_read(system.file("examples/CITATION_basic.cff", test <- cff( title = "Manipulating files", keywords = c("A", "new", "list", "of", "keywords"), - authors = cff_create_cff_person("New author") + authors = as_cff_person("New author") ) test \donttest{ diff --git a/man/cff_create.Rd b/man/cff_create.Rd index a05ee649..44ac7d40 100644 --- a/man/cff_create.Rd +++ b/man/cff_create.Rd @@ -83,7 +83,7 @@ newkeys <- list( message = "This overwrites fields", abstract = "New abstract", keywords = c("A", "new", "list", "of", "keywords"), - authors = cff_create_cff_person("New author") + authors = as_cff_person("New author") ) cff_create(demo_file, keys = newkeys) @@ -95,7 +95,7 @@ old <- cff_create(demo_file) new_contact <- append( old$contact, - cff_create_cff_person(person( + as_cff_person(person( given = "I am", family = "New Contact" )) diff --git a/man/chunks/person.Rmd b/man/chunks/person.Rmd index d20b3f61..38a02d67 100644 --- a/man/chunks/person.Rmd +++ b/man/chunks/person.Rmd @@ -7,11 +7,11 @@ options("cffr_message_verbosity" = NULL) ```{r} # Don't do entity <- "Elephant and Castle" -cff_create_cff_person(entity) +as_cff_person(entity) # Do entity_protect <- "{Elephant and Castle}" -cff_create_cff_person(entity_protect) +as_cff_person(entity_protect) ``` ```{r include=FALSE} diff --git a/man/deprecated_cff_person.Rd b/man/deprecated_cff_person.Rd index f94f0e11..3a0b3857 100644 --- a/man/deprecated_cff_person.Rd +++ b/man/deprecated_cff_person.Rd @@ -21,7 +21,7 @@ See \strong{Examples}. A person in format \code{cff}. } \description{ -\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Please use \code{\link[=cff_create_cff_person]{cff_create_cff_person()}} +\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Please use \code{\link[=as_cff_person]{as_cff_person()}} } \examples{ # Create a person object @@ -36,7 +36,7 @@ a_person <- person( a_person -cff_person <- cff_create_cff_person(a_person) +cff_person <- as_cff_person(a_person) cff_person @@ -48,23 +48,23 @@ a_str <- paste0( "Julio Iglesias ", "()" ) -cff_create_cff_person(a_str) +as_cff_person(a_str) # Several persons persons <- c(person("Clark", "Kent"), person("Lois", "Lane")) -cff_create_cff_person(persons) +as_cff_person(persons) # Or you can use BibTeX style if you prefer x <- "Frank Sinatra and Dean Martin and Davis, Jr., Sammy and Joey Bishop" -cff_create_cff_person(x) +as_cff_person(x) -cff_create_cff_person("Herbert von Karajan") +as_cff_person("Herbert von Karajan") } \seealso{ -\code{\link[=cff_create_cff_person]{cff_create_cff_person()}} +\code{\link[=as_cff_person]{as_cff_person()}} Other deprecated functions: \code{\link{cff_extract_to_bibtex}()}, diff --git a/tests/testthat/_snaps/cff_create_cff_person.md b/tests/testthat/_snaps/as_cff_person.md similarity index 73% rename from tests/testthat/_snaps/cff_create_cff_person.md rename to tests/testthat/_snaps/as_cff_person.md index 8e1a13ab..86569528 100644 --- a/tests/testthat/_snaps/cff_create_cff_person.md +++ b/tests/testthat/_snaps/as_cff_person.md @@ -1,21 +1,21 @@ # debugging messages Code - a <- cff_create_cff_person(ap) + a <- as_cff_person(ap) Message - i In `cff_create_cff_person()` using internal for "person". + i In `as_cff_person()` using internal for "person". --- Code - b <- cff_create_cff_person("Example") + b <- as_cff_person("Example") Message - i In `cff_create_cff_person()` using internal for "txt". + i In `as_cff_person()` using internal for "txt". # Parse one person Code - cff_create_cff_person(p) + as_cff_person(p) Output - family-names: person given-names: one @@ -23,7 +23,7 @@ # Parse several persons Code - cff_create_cff_person(p) + as_cff_person(p) Output - family-names: person given-names: one @@ -35,7 +35,7 @@ # Parse bibtex persons Code - cff_create_cff_person(s) + as_cff_person(s) Output - family-names: Wright given-names: Frank Edwin @@ -44,7 +44,7 @@ --- Code - cff_create_cff_person(s) + as_cff_person(s) Output - family-names: person given-names: A @@ -55,7 +55,7 @@ # Parse bibtex persons with masks Code - cff_create_cff_person(s) + as_cff_person(s) Output - name: Elephant - name: Castle @@ -63,14 +63,14 @@ --- Code - cff_create_cff_person(s) + as_cff_person(s) Output - name: Elephant and Castle --- Code - cff_create_cff_person(s) + as_cff_person(s) Output - name: Elephant and Castle - name: this @@ -79,7 +79,7 @@ --- Code - cff_create_cff_person(s) + as_cff_person(s) Output - name: Elephant and Castle - name: this AND Ltd. diff --git a/tests/testthat/_snaps/deprecated.md b/tests/testthat/_snaps/deprecated.md index 55d49f2a..af075c14 100644 --- a/tests/testthat/_snaps/deprecated.md +++ b/tests/testthat/_snaps/deprecated.md @@ -80,7 +80,7 @@ Condition Warning: `cff_parse_person()` was deprecated in cffr 1.0.0. - i Please use `cff_create_cff_person()` instead. + i Please use `as_cff_person()` instead. # cff_parse_person_bibtex @@ -89,5 +89,5 @@ Condition Warning: `cff_parse_person_bibtex()` was deprecated in cffr 1.0.0. - i Please use `cff_create_cff_person()` instead. + i Please use `as_cff_person()` instead. diff --git a/tests/testthat/test-as_bibentry.R b/tests/testthat/test-as_bibentry.R index 5628be47..4a0b5c6a 100644 --- a/tests/testthat/test-as_bibentry.R +++ b/tests/testthat/test-as_bibentry.R @@ -257,7 +257,7 @@ test_that("particle names", { bibparsed <- cff_parse_citation(bib) - bibparsed$authors <- cff_create_cff_person( + bibparsed$authors <- as_cff_person( "van Leunen, Mary-Claire and Davis, Jr., Sammy" ) @@ -284,7 +284,7 @@ test_that("From plain cff with a citation", { ) s$`preferred-citation` <- cff_parse_citation(acit) - s$`preferred-citation`$editors <- cff_create_cff_person("A name") + s$`preferred-citation`$editors <- as_cff_person("A name") bib <- as_bibentry(s) expect_snapshot(toBibtex(bib)) diff --git a/tests/testthat/test-cff_create_cff_person.R b/tests/testthat/test-as_cff_person.R similarity index 63% rename from tests/testthat/test-cff_create_cff_person.R rename to tests/testthat/test-as_cff_person.R index cd6620d5..745ca610 100644 --- a/tests/testthat/test-cff_create_cff_person.R +++ b/tests/testthat/test-as_cff_person.R @@ -1,21 +1,21 @@ test_that("NULL gives NULL", { - expect_null(cff_create_cff_person(NULL)) - expect_null(cff_create_cff_person(NA)) - expect_null(cff_create_cff_person(person())) - expect_null(cff_create_cff_person(list())) - expect_null(cff_create_cff_person("")) + expect_null(as_cff_person(NULL)) + expect_null(as_cff_person(NA)) + expect_null(as_cff_person(person())) + expect_null(as_cff_person(list())) + expect_null(as_cff_person("")) }) test_that("debugging messages", { initopt <- getOption("cffr_message_verbosity", NULL) options("cffr_message_verbosity" = "debug") ap <- person("Example") - expect_snapshot(a <- cff_create_cff_person(ap)) - expect_snapshot(b <- cff_create_cff_person("Example")) + expect_snapshot(a <- as_cff_person(ap)) + expect_snapshot(b <- as_cff_person("Example")) # Disconnect options("cffr_message_verbosity" = NULL) - expect_silent(a <- cff_create_cff_person(ap)) - expect_silent(b <- cff_create_cff_person("Example")) + expect_silent(a <- as_cff_person(ap)) + expect_silent(b <- as_cff_person("Example")) # Restore init options("cffr_message_verbosity" = initopt) @@ -23,7 +23,7 @@ test_that("debugging messages", { test_that("Parse one person", { p <- person("one", "person") - expect_snapshot(cff_create_cff_person(p)) + expect_snapshot(as_cff_person(p)) }) test_that("Parse several persons", { @@ -31,64 +31,64 @@ test_that("Parse several persons", { person("one", "person"), person("another", "human"), person("and one", "more") ) - expect_snapshot(cff_create_cff_person(p)) + expect_snapshot(as_cff_person(p)) }) test_that("Parse bibtex persons", { s <- "Wright, III, Frank Edwin" - expect_snapshot(cff_create_cff_person(s)) + expect_snapshot(as_cff_person(s)) s <- "A person and another and Another one" - expect_snapshot(cff_create_cff_person(s)) + expect_snapshot(as_cff_person(s)) # As vector s2 <- c("A person", "another", "Another one") - expect_identical(cff_create_cff_person(s), cff_create_cff_person(s2)) + expect_identical(as_cff_person(s), as_cff_person(s2)) }) test_that("Parse bibtex persons with masks", { s <- "Elephant and Castle" - expect_snapshot(cff_create_cff_person(s)) + expect_snapshot(as_cff_person(s)) s <- "{Elephant and Castle}" - expect_snapshot(cff_create_cff_person(s)) + expect_snapshot(as_cff_person(s)) s <- "{Elephant and Castle} and this AND Ltd." - expect_snapshot(cff_create_cff_person(s)) + expect_snapshot(as_cff_person(s)) s <- "{Elephant and Castle} and {this AND Ltd.}" - expect_snapshot(cff_create_cff_person(s)) + expect_snapshot(as_cff_person(s)) }) test_that("Get same results with both", { - s3 <- cff_create_cff_person( + s3 <- as_cff_person( c(person(family = "Entity"), person("A", "person")) ) - b3 <- cff_create_cff_person("Entity and A person") + b3 <- as_cff_person("Entity and A person") expect_identical(s3, b3) - s4 <- cff_create_cff_person(person(family = "A big fat company")) - b4 <- cff_create_cff_person("{A big fat company}") + s4 <- as_cff_person(person(family = "A big fat company")) + b4 <- as_cff_person("{A big fat company}") expect_identical(s4, b4) }) test_that("R Core Team", { - p <- cff_create_cff_person(person("R Core", "Team")) + p <- as_cff_person(person("R Core", "Team")) expect_equal(p[[1]]$name, "R Core Team") - p <- cff_create_cff_person("R Core Team") + p <- as_cff_person("R Core Team") expect_equal(p[[1]]$name, "R Core Team") }) @@ -99,15 +99,15 @@ test_that("Bioconductor", { role = "cre", email = "maintainer@bioconductor.org" ) - p <- cff_create_cff_person(bio) + p <- as_cff_person(bio) expect_equal(p[[1]]$name, "Bioconductor Package Maintainer") - p <- cff_create_cff_person("Bioconductor Package Maintainer") + p <- as_cff_person("Bioconductor Package Maintainer") expect_equal(p[[1]]$name, "Bioconductor Package Maintainer") - p <- cff_create_cff_person(person("The Bioconductor", "Package Maintainer")) + p <- as_cff_person(person("The Bioconductor", "Package Maintainer")) expect_equal(p[[1]]$name, "The Bioconductor Package Maintainer") }) @@ -119,7 +119,7 @@ test_that("Several emails, select first", { email = c("first_mail@gmail.com", "second_mail@espol.edu.ec") ) - p <- cff_create_cff_person(pp) + p <- as_cff_person(pp) expect_equal(p[[1]]$email, "first_mail@gmail.com") }) @@ -133,7 +133,7 @@ test_that("Several emails, select second if first no valid", { email = c("first_mail_gmail.com", "second_mail@espol.edu.ec") ) - p <- cff_create_cff_person(pp) + p <- as_cff_person(pp) expect_equal(p[[1]]$email, "second_mail@espol.edu.ec") }) @@ -148,9 +148,9 @@ test_that("No valid emails", { email = c("first_mail_gmail.com", "second_mail__espol.edu.ec") ) - p <- cff_create_cff_person(pp) + p <- as_cff_person(pp) - expect_equal(p, cff_create_cff_person(person( + expect_equal(p, as_cff_person(person( given = "John", family = "Doe" ))) @@ -160,9 +160,9 @@ test_that("No valid emails", { family = "Doe" ) - p2 <- cff_create_cff_person(pp2) + p2 <- as_cff_person(pp2) - expect_equal(p, cff_create_cff_person(person( + expect_equal(p, as_cff_person(person( given = "John", family = "Doe" ))) @@ -179,9 +179,9 @@ test_that("Can extract comments from format", { ) ) - pp1 <- cff_create_cff_person(pp) + pp1 <- as_cff_person(pp) txt <- format(pp) - pp2 <- cff_create_cff_person(txt) + pp2 <- as_cff_person(txt) expect_identical(pp1, pp2) expect_snapshot(pp2) diff --git a/tests/testthat/test-cff-methods.R b/tests/testthat/test-cff-methods.R index 56f48b67..29bc1052 100644 --- a/tests/testthat/test-cff-methods.R +++ b/tests/testthat/test-cff-methods.R @@ -82,7 +82,7 @@ test_that("Convert a citation only", { test_that("Convert authors only", { - a_pers_list <- cff_create_cff_person( + a_pers_list <- as_cff_person( "A person and {A Entity inc.} and {One person} more" ) diff --git a/tests/testthat/test-cff_write.R b/tests/testthat/test-cff_write.R index 3d725ba4..f27746cd 100644 --- a/tests/testthat/test-cff_write.R +++ b/tests/testthat/test-cff_write.R @@ -75,7 +75,7 @@ test_that("Add new keys", { message = "This overwrites fields", abstract = "New abstract", keywords = c("A", "new", "list", "of", "keywords"), - authors = cff_create_cff_person( + authors = as_cff_person( person("Don", "Nadie", comment = c(website = "error")) ), "date-released" = "1900-01-01", @@ -116,7 +116,7 @@ test_that("Append keys", { # It should be a list new_aut <- append( old_aut, - cff_create_cff_person( + as_cff_person( person("New", "author", comment = c( "error" = 123, website = "https://stackoverflow.com/", diff --git a/tests/testthat/test-deprecated.R b/tests/testthat/test-deprecated.R index 05f2ef57..fa0dd9fc 100644 --- a/tests/testthat/test-deprecated.R +++ b/tests/testthat/test-deprecated.R @@ -65,12 +65,12 @@ test_that("cff_parse_person", { p <- person("A", "person") expect_snapshot(pend <- cff_parse_person(p)) - expect_identical(pend, cff_create_cff_person(p)) + expect_identical(pend, as_cff_person(p)) }) test_that("cff_parse_person_bibtex", { p <- "{Elephant and Castle}" expect_snapshot(pend <- cff_parse_person_bibtex(p)) - expect_identical(pend, cff_create_cff_person(p)) + expect_identical(pend, as_cff_person(p)) }) diff --git a/vignettes/cffr.Rmd b/vignettes/cffr.Rmd index d7f4db89..684a47e5 100644 --- a/vignettes/cffr.Rmd +++ b/vignettes/cffr.Rmd @@ -158,7 +158,7 @@ chiquito <- person("Gregorio", chiquito # Parse it -chiquito_parsed <- cff_create_cff_person(chiquito) +chiquito_parsed <- as_cff_person(chiquito) chiquito_parsed From 3245b7019919132d5452ec21cb837468f095b079 Mon Sep 17 00:00:00 2001 From: Diego H Date: Fri, 1 Mar 2024 16:59:04 +0100 Subject: [PATCH 26/32] Update pkgdown --- NEWS.md | 6 +-- R/cff_create.R | 4 +- R/cff_parse_citation.R | 2 +- R/cff_write.R | 6 ++- R/cff_write_misc.R | 2 +- R/data.R | 2 + R/docs.R | 6 +++ README.md | 65 +++++++++++++++++++++++++++ codemeta.json | 4 +- data/cran_to_spdx.rda | Bin 907 -> 916 bytes man/as_bibentry.Rd | 6 ++- man/as_cff_person.Rd | 6 ++- man/cff-class.Rd | 8 ++++ man/cff.Rd | 3 +- man/cff_create.Rd | 7 ++- man/cff_parse_citation.Rd | 6 ++- man/cff_validate.Rd | 3 +- man/cff_write.Rd | 10 ++--- man/cff_write_misc.Rd | 2 +- man/cran_to_spdx.Rd | 1 + man/roxygen/meta.R | 2 +- pkgdown/_pkgdown.yml | 90 ++++++++++++++++++++++---------------- vignettes/bibtex_cff.Rmd | 5 +-- vignettes/crosswalk.Rmd | 5 ++- 24 files changed, 176 insertions(+), 75 deletions(-) diff --git a/NEWS.md b/NEWS.md index a5af4acf..b8045cb4 100644 --- a/NEWS.md +++ b/NEWS.md @@ -15,7 +15,7 @@ old API [has been deprecated](https://lifecycle.r-lib.org/articles/stages.html#deprecated) and it would warn when used, providing advice on the replacement function. -#### Deprecations +#### Deprecation - `cff_to_bibtex()` and `cff_extract_to_bibtex()`: replaced by `as_bibentry()`. @@ -23,7 +23,7 @@ would warn when used, providing advice on the replacement function. `cff_read_bib_text()` (for character strings). - `write_bib()` and `write_citation()` : replaced by `cff_write_bib()` and `cff_write_citation()` respectively. -- `cff_parse_person()` and `cff_parse_person_bibtex()`: replacedy by +- `cff_parse_person()` and `cff_parse_person_bibtex()`: replaced by `as_cff_person()`. - The conversion from `cff` to `bibentry` is performed now by a new function `as_bibentry()`. Previous names of this function were `cff_to_bibtex()` and @@ -56,7 +56,7 @@ would warn when used, providing advice on the replacement function. - `head.cff()`, `tail.cff()`. - `toBibtex.cff()`. - Update of BibTeX crosswalk (see `vignette("bibtex_cff", package = "cffr")`) - and consecuently changes in the mapping performed by `as_bibtex()` + and consequently changes in the mapping performed by `as_bibtex()` `cff_parse_citation()`: - **\@inbook** and **\@book** gains a new value on [CFF]{.underline} when **series** is provided: [collection-type: book-series.]{.underline} diff --git a/R/cff_create.R b/R/cff_create.R index c30c59a0..785ae763 100644 --- a/R/cff_create.R +++ b/R/cff_create.R @@ -43,8 +43,8 @@ #' #' #' ``` -#' -#' `vignette("cffr", "cffr")` +#' See also [cff_write()] for creating a CFF file. `vignette("cffr", "cffr")` +#' shows an introduction on how manipulate `cff` objects. #' #' @details #' diff --git a/R/cff_parse_citation.R b/R/cff_parse_citation.R index 1a8671a0..620f5326 100644 --- a/R/cff_parse_citation.R +++ b/R/cff_parse_citation.R @@ -6,7 +6,7 @@ #' #' @export #' -#' @family parsers +#' @family coercing #' #' @param bib A `bibentry` object, either created with [bibentry()] #' (preferred) or [citEntry()]. diff --git a/R/cff_write.R b/R/cff_write.R index f1ff3b75..386f2a0c 100644 --- a/R/cff_write.R +++ b/R/cff_write.R @@ -10,8 +10,7 @@ #' [`cff`][cff-class] object and writes it out to a YAML-formatted file in #' one command. #' -#' @family core -#' @family write +#' @family writing #' #' @param x The source that would be used for generating #' the `CITATION.cff` file. It could be: @@ -50,6 +49,9 @@ #' #' #' ``` +#' This function unifies the workflow [cff_create()] + [cff_write()] + +#' [cff_validate()]. +#' #' @examples #' \donttest{ #' tmpfile <- tempfile(fileext = ".cff") diff --git a/R/cff_write_misc.R b/R/cff_write_misc.R index f59620ae..13187d58 100644 --- a/R/cff_write_misc.R +++ b/R/cff_write_misc.R @@ -23,7 +23,7 @@ #' @export #' @rdname cff_write_misc #' @family bibtex -#' @family write +#' @family writing #' #' @return #' Writes the corresponding file specified on the `file` parameter. diff --git a/R/data.R b/R/data.R index 3fbf9ac6..34096b41 100644 --- a/R/data.R +++ b/R/data.R @@ -4,6 +4,8 @@ #' on CRAN packages and its (approximate) match on the #' [SPDX License List](https://spdx.org/licenses/). #' +#' @family datasets +#' #' @format A data frame with `r nrow(cran_to_spdx)` rows and 2 variables: #' #' * `r names(cran_to_spdx)[1]`: A valid `License` string on CRAN. diff --git a/R/docs.R b/R/docs.R index 1a77ece0..547745fb 100644 --- a/R/docs.R +++ b/R/docs.R @@ -4,8 +4,14 @@ #' @aliases cff_class #' @keywords internal #' +#' @family s3method +#' #' @description #' #' ```{r child = "man/chunks/cffclass.Rmd"} #' ``` +#' +#' @seealso +#' +#' `vignette("cffr", "cffr")` for a hands-on example. NULL diff --git a/README.md b/README.md index 348f5085..255fe1ff 100644 --- a/README.md +++ b/README.md @@ -561,6 +561,26 @@ test <- cff_create("rmarkdown") - family-names: Chirico. given-names: Michael year: '2024' + - type: software + title: dygraphs + abstract: 'dygraphs: Interface to ''Dygraphs'' Interactive Time Series Charting + Library' + notes: Suggests + url: https://github.com/rstudio/dygraphs + repository: https://CRAN.R-project.org/package=dygraphs + authors: + - family-names: Vanderkam + given-names: Dan + website: http://dygraphs.com/ + - family-names: Allaire + given-names: JJ + - family-names: Owen + given-names: Jonathan + - family-names: Gromer + given-names: Daniel + - family-names: Thieurmel + given-names: Benoit + year: '2024' - type: software title: fs abstract: 'fs: Cross-Platform File System Operations Based on ''libuv''' @@ -577,6 +597,26 @@ test <- cff_create("rmarkdown") given-names: Gábor email: csardi.gabor@gmail.com year: '2024' + - type: software + title: rsconnect + abstract: 'rsconnect: Deploy Docs, Apps, and APIs to ''Posit Connect'', ''shinyapps.io'', + and ''RPubs''' + notes: Suggests + url: https://rstudio.github.io/rsconnect/ + repository: https://CRAN.R-project.org/package=rsconnect + authors: + - family-names: Atkins + given-names: Aron + email: aron@posit.co + - family-names: Allen + given-names: Toph + - family-names: Wickham + given-names: Hadley + - family-names: McPherson + given-names: Jonathan + - family-names: Allaire + given-names: JJ + year: '2024' - type: software title: downlit abstract: 'downlit: Syntax Highlighting and Automatic Linking' @@ -589,6 +629,19 @@ test <- cff_create("rmarkdown") email: hadley@posit.co year: '2024' version: '>= 0.4.0' + - type: software + title: katex + abstract: 'katex: Rendering Math to HTML, ''MathML'', or R-Documentation Format' + notes: Suggests + url: https://docs.ropensci.org/katex/ + repository: https://CRAN.R-project.org/package=katex + authors: + - family-names: Ooms + given-names: Jeroen + email: jeroen@berkeley.edu + orcid: https://orcid.org/0000-0002-4035-0289 + year: '2024' + version: '>= 1.4.0' - type: software title: sass abstract: 'sass: Syntactically Awesome Style Sheets (''Sass'')' @@ -699,6 +752,18 @@ test <- cff_create("rmarkdown") given-names: Davis email: davis@posit.co year: '2024' + - type: software + title: cleanrmd + abstract: 'cleanrmd: Clean Class-Less ''R Markdown'' HTML Documents' + notes: Suggests + url: https://pkg.garrickadenbuie.com/cleanrmd/ + repository: https://CRAN.R-project.org/package=cleanrmd + authors: + - family-names: Aden-Buie + given-names: Garrick + email: garrick@adenbuie.com + orcid: https://orcid.org/0000-0002-7111-0077 + year: '2024' - type: software title: withr abstract: 'withr: Run Code ''With'' Temporarily Modified Global State' diff --git a/codemeta.json b/codemeta.json index fc6f3580..1df75929 100644 --- a/codemeta.json +++ b/codemeta.json @@ -14,7 +14,7 @@ "name": "R", "url": "https://r-project.org" }, - "runtimePlatform": "R version 4.3.2 (2023-10-31)", + "runtimePlatform": "R version 4.3.2 (2023-10-31 ucrt)", "provider": { "@id": "https://cran.r-project.org", "@type": "Organization", @@ -200,7 +200,7 @@ }, "isPartOf": "https://ropensci.org", "keywords": ["attribution", "citation", "credit", "citation-files", "cff", "metadata", "r", "r-package", "citation-file-format", "rstats", "ropensci", "cran"], - "fileSize": "929.883KB", + "fileSize": "947.576KB", "citation": [ { "@type": "ScholarlyArticle", diff --git a/data/cran_to_spdx.rda b/data/cran_to_spdx.rda index 33fae2994ac0070f09336d3d8c403ec099798a1f..0bd1435cae708e762b962cb7a97ec9ae0c12fef9 100644 GIT binary patch literal 916 zcmV;F18e*riwFP!000002F+JXPuoBccH=-unm|gSm3lH&4Vna`W6qYoKWTU7l z6%~ibghjRFILLPBCXZ^{hliR@wmg?9cUwC}dynSrJ>vGR3u>%N zH7^9s2jmCN)MFU+jvqQf=!^lyHV7uMxN$r@(R46&?O7nkAL|Ucu8wsheU#`}%-}Kg zct}p6f|sD3J$Vg6?f4@&g<#mx_GkbE&SM;}8efM{{dWsb-+YHfc*I(skDzQMQuc9 z1!Aohx0C}Wz@61{kAy z&Yir|26bnZ!6Z%^)E>i>qx;N-Fqt-QvgfmsNZIvBwAm0ab2)67k|eIP;A`!G#>mpM z^iZ=B9+QksbB1dyET}lE|DM>xOF{e0ntpR!*DkoCa;-+&L=l6|gqH=3{IpT6Ecq3S zYeuoij3tXyK2yb3u8OVv0aVXC*O@pWx)Nr+Lh>6AcYHg*)N{XbpuvMrDCh&%#!Z-b zl8jZht*40o7ZBOVmSVVL106tdg;alC4R?J;u`wz#?3 zjfZj`(dQt-@SQK_UFh<3UL@mGl-u{nq~C0?X$Sm-txHN&hBcFxl`GI@LE+XjzF=3V z{08ZT_c^er!9v~w*h7PCW`0WWB0K*ZUKH0qz)3MkD`ZhnY^`QN@PAw(38+v}F}WMf z%vENY4z3n27R)i3_&a+GMMZe~UGnS+3epi+%Hr$8?&+{g{tFEUC+Ffm{5BSleFUss q*`d9A?PHSyz3NXt?~+F~qDOtlUw3zRKk(1b=<^FCJhi~74*&oy4!s@# literal 907 zcmV;619bdCT4*^jL0KkKS>9zRc>o3)f6)K`|B!TNe+NIOUueH)-|#>H0ssI3&;$PH zMJcLQXdxn$LqY0#n*AnjWXMA%O-!8VroodWVq>8hVWZpyrJl8ess_0%@QOhK3*w7>x`OkO2~jFe$2d zXwawXnjVu)lzN&O4WcqVOllbqQxUPLFe>dz=&r?fZ9@1WKjajLMimfmJ2mUYPPPXx zj*Ybg1&%$?qNxPUhn5dz<$F-rN4=W12TeQiUlnt8<0b6Yj7qp-b#>kRV}V z`Fj%x=Ccapm4j`8K(v)wkqAVgQwwED3edK<)|qCdvuV!(PmL8wD1-^BC2K`h&j3QP zL0u36=!%xXc0(x;6=KLKJiY@Gylf7R8cZa*UN&lz^7{|Bd%yVmS_cO2<5+P%UUOvX zSz@tbnf*NMPJ>Nh!Uq@-^UcwV*1cylE@5sg3T+_5-MXcG?wWWD;2vA3o z&8+4_l-WR}LCwk=!sx_DBpAAMWvtJbNN+NfJb=(2mY@It diff --git a/man/as_bibentry.Rd b/man/as_bibentry.Rd index 53317ed5..0c9e7019 100644 --- a/man/as_bibentry.Rd +++ b/man/as_bibentry.Rd @@ -126,10 +126,12 @@ Other functions for working with BibTeX format: \code{\link{encoded_utf_to_latex}()} Other functions for converting between \strong{R} classes: -\code{\link{as_cff_person}()} +\code{\link{as_cff_person}()}, +\code{\link{cff_parse_citation}()} Other S3 Methods for \code{cff}: -\code{\link{as_cff_person}()} +\code{\link{as_cff_person}()}, +\code{\link{cff-class}} } \concept{bibtex} \concept{coercing} diff --git a/man/as_cff_person.Rd b/man/as_cff_person.Rd index 9da93bc8..a4d8e05a 100644 --- a/man/as_cff_person.Rd +++ b/man/as_cff_person.Rd @@ -139,10 +139,12 @@ Examples in \code{vignette("cffr", "cffr")} and \code{\link[utils:person]{utils: \code{\link[utils:person]{utils::person()}} Other functions for converting between \strong{R} classes: -\code{\link{as_bibentry}()} +\code{\link{as_bibentry}()}, +\code{\link{cff_parse_citation}()} Other S3 Methods for \code{cff}: -\code{\link{as_bibentry}()} +\code{\link{as_bibentry}()}, +\code{\link{cff-class}} } \concept{coercing} \concept{s3method} diff --git a/man/cff-class.Rd b/man/cff-class.Rd index 87a02b5b..1d886499 100644 --- a/man/cff-class.Rd +++ b/man/cff-class.Rd @@ -178,4 +178,12 @@ as.person(the_cff$authors) }\if{html}{\out{
}} } } +\seealso{ +\code{vignette("cffr", "cffr")} for a hands-on example. + +Other S3 Methods for \code{cff}: +\code{\link{as_bibentry}()}, +\code{\link{as_cff_person}()} +} +\concept{s3method} \keyword{internal} diff --git a/man/cff.Rd b/man/cff.Rd index cdce2784..b835c587 100644 --- a/man/cff.Rd +++ b/man/cff.Rd @@ -106,7 +106,6 @@ cffobj \seealso{ Other core functions of \CRANpkg{cffr}: \code{\link{cff_create}()}, -\code{\link{cff_validate}()}, -\code{\link{cff_write}()} +\code{\link{cff_validate}()} } \concept{core} diff --git a/man/cff_create.Rd b/man/cff_create.Rd index 44ac7d40..ea829a90 100644 --- a/man/cff_create.Rd +++ b/man/cff_create.Rd @@ -107,12 +107,11 @@ cff_create(demo_file, keys = list("contact" = new_contact)) } \seealso{ \href{https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md}{Guide to Citation File Format schema version 1.2.0}. - -\code{vignette("cffr", "cffr")} +See also \code{\link[=cff_write]{cff_write()}} for creating a CFF file. \code{vignette("cffr", "cffr")} +shows an introduction on how manipulate \code{cff} objects. Other core functions of \CRANpkg{cffr}: \code{\link{cff}()}, -\code{\link{cff_validate}()}, -\code{\link{cff_write}()} +\code{\link{cff_validate}()} } \concept{core} diff --git a/man/cff_parse_citation.Rd b/man/cff_parse_citation.Rd index a9b1891a..ae8a90a9 100644 --- a/man/cff_parse_citation.Rd +++ b/man/cff_parse_citation.Rd @@ -80,5 +80,9 @@ cff_parse_citation(citation("rmarkdown")) } \seealso{ \code{\link[=cff_create]{cff_create()}}, \code{vignette("bibtex_cff", "cffr")}, \code{\link[=bibentry]{bibentry()}} + +Other functions for converting between \strong{R} classes: +\code{\link{as_bibentry}()}, +\code{\link{as_cff_person}()} } -\concept{parsers} +\concept{coercing} diff --git a/man/cff_validate.Rd b/man/cff_validate.Rd index 174928a2..d3ed8aa0 100644 --- a/man/cff_validate.Rd +++ b/man/cff_validate.Rd @@ -52,7 +52,6 @@ try(cff_validate(system.file("CITATION", package = "cffr"))) Other core functions of \CRANpkg{cffr}: \code{\link{cff}()}, -\code{\link{cff_create}()}, -\code{\link{cff_write}()} +\code{\link{cff_create}()} } \concept{core} diff --git a/man/cff_write.Rd b/man/cff_write.Rd index 2ad4167f..bf4a6daf 100644 --- a/man/cff_write.Rd +++ b/man/cff_write.Rd @@ -79,14 +79,10 @@ file.remove(tmpfile) } \seealso{ \href{https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md}{Guide to Citation File Format schema version 1.2.0}. - -Other core functions of \CRANpkg{cffr}: -\code{\link{cff}()}, -\code{\link{cff_create}()}, -\code{\link{cff_validate}()} +This function unifies the workflow \code{\link[=cff_create]{cff_create()}} + \code{\link[=cff_write]{cff_write()}} + +\code{\link[=cff_validate]{cff_validate()}}. Other functions for creating external files: \code{\link{cff_write_bib}()} } -\concept{core} -\concept{write} +\concept{writing} diff --git a/man/cff_write_misc.Rd b/man/cff_write_misc.Rd index 06cc23ed..37030074 100644 --- a/man/cff_write_misc.Rd +++ b/man/cff_write_misc.Rd @@ -122,4 +122,4 @@ Other functions for creating external files: \code{\link{cff_write}()} } \concept{bibtex} -\concept{write} +\concept{writing} diff --git a/man/cran_to_spdx.Rd b/man/cran_to_spdx.Rd index 5ad515ee..9ad8e40f 100644 --- a/man/cran_to_spdx.Rd +++ b/man/cran_to_spdx.Rd @@ -31,4 +31,5 @@ head(cran_to_spdx, 20) \seealso{ \emph{Writing R Extensions}, \href{https://cran.r-project.org/doc/manuals/r-release/R-exts.html#Licensing}{Licensing section}. } +\concept{datasets} \keyword{datasets} diff --git a/man/roxygen/meta.R b/man/roxygen/meta.R index 32178d85..54a0daf6 100644 --- a/man/roxygen/meta.R +++ b/man/roxygen/meta.R @@ -3,7 +3,7 @@ list( core = "Other core functions of \\CRANpkg{cffr}:", reading = "Other functions for reading external files:", coercing = "Other functions for converting between \\strong{R} classes:", - write = "Other functions for creating external files:", + writing = "Other functions for creating external files:", bibtex = "Other functions for working with BibTeX format:", schemas = "Other CFF schemas:", git = "Other Git/GitHub helpers:", diff --git a/pkgdown/_pkgdown.yml b/pkgdown/_pkgdown.yml index 60e9eab7..dca1dbd3 100644 --- a/pkgdown/_pkgdown.yml +++ b/pkgdown/_pkgdown.yml @@ -1,50 +1,64 @@ url: 'https://docs.ropensci.org/cffr' + template: opengraph: twitter: creator: '@dhernangomez' card: summary_large_image + repo: branch: main development: mode: auto -# reference: -# - title: Main function -# desc: >- -# The main function of the package and likely to be the only one you would -# need. -# contents: -# - cff_write -# - title: Other core functions -# desc: 'Create, write and manipulate files and objects.' -# contents: -# - has_concept("Core functions") -# - title: Citation File Format schema -# desc: >- -# These functions provides lists of valid keys and fields as defined by the -# CFF schema.json **(v1.2.0)**. -# contents: -# - has_concept("Schemas") -# - title: Parsers -# desc: Helper functions to parse information. -# contents: -# - has_concept("Parsers") -# - title: BibTeX -# desc: Convert CFF to BibTeX and viceversa -# contents: -# - has_concept("BibTeX helpers") -# - cff_parse_citation -# - title: Datasets -# contents: -# - has_keyword("datasets") -# - title: Continuous Integration -# contents: -# - has_concept("Git helpers") -# - title: About the package -# contents: -# - cffr-package +home: + title: cffr | Generate Citation File Format (CFF) files for R packages + description: >- + Utilities to generate and validate CFF files with R. + +reference: + - title: The whole game + - subtitle: The function + desc: >- + The main function of the package and likely to be the only one you would + need. + contents: cff_write + - subtitle: Other core functions + desc: 'Create, modify and write and `cff` objects.' + contents: has_concept("core") + - subtitle: The `cff` class + desc: Brief introduction to the `cff` class and S3 Methods available. + contents: + - cff-class + - has_concept("s3method") + - subtitle: Citation File Format schema + desc: >- + These functions provides lists of valid keys and fields as defined by the + CFF schema.json **(v1.2.0)**. + contents: has_concept("schemas") + - title: Continuous Integration + contents: has_concept("git") + - title: Additional features + - subtitle: Read and write files + desc: >- + Read and write local files on different formats (CFF files, BibTeX, + etc.). + contents: + - has_concept("reading") + - has_concept("writing") + - subtitle: Coercing objects + desc: >- + Coerce **R** objects into different classes (`cff`, `person`, `bibentry`) + and more: + contents: has_concept("coercing") + - subtitle: BibTeX helpers + desc: Functions that works with BibTeX markup language. + contents: has_concept("bibtex") + - title: Datasets + contents: has_concept("datasets") + - title: About the package + contents: cffr-package navbar: structure: @@ -69,12 +83,14 @@ navbar: href: 'https://ropensci.org/blog/2021/11/23/how-i-test-cffr/' - text: '---' - text: Blogs - - text: 'How To Call Package Citations in R programming' - href: 'https://medium.com/analytics-vidhya/how-to-call-package-citations-in-r-programming-6f01f1176301' + - text: How To Call Package Citations in R programming + href: >- + https://medium.com/analytics-vidhya/how-to-call-package-citations-in-r-programming-6f01f1176301 - text: '---' - text: Podcasts - text: 'R Weekly Highlights: Issue 2021-W48 Highlights' href: 'https://share.fireside.fm/episode/87RSVeFz+4R-j8_xW' + authors: Diego Hernangómez: href: 'https://dieghernan.github.io/' diff --git a/vignettes/bibtex_cff.Rmd b/vignettes/bibtex_cff.Rmd index a4e27540..247e6ed7 100644 --- a/vignettes/bibtex_cff.Rmd +++ b/vignettes/bibtex_cff.Rmd @@ -4,9 +4,8 @@ subtitle: A potential crosswalk bibliography: REFERENCES.bib author: Diego Hernangómez description: >- - This article presents a crosswalk between BibTeX and Citation File Format - [@druskat_citation_2021], as it is performed by the **cffr** package - [@hernangomez2021]. + This article presents a crosswalk between BibTeX and Citation File Format, as + it is performed by the cffr package. abstract: >- This article introduces a crosswalk between **BibTeX** and the Citation File Format (CFF) [@druskat_citation_2021], as implemented by the **cffr** package diff --git a/vignettes/crosswalk.Rmd b/vignettes/crosswalk.Rmd index de1cf9bf..cf0bdbd8 100644 --- a/vignettes/crosswalk.Rmd +++ b/vignettes/crosswalk.Rmd @@ -1,8 +1,9 @@ --- title: "From R to CFF" subtitle: "Crosswalk" -description: > - A comprehensive description of the internal mappings performed by **cffr**. +description: >- + A comprehensive description of the internal mappings performed by + the cffr package. author: Diego Hernangómez bibliography: REFERENCES.bib link-citations: yes From 7b8430ad156e00003a363deac25a703c8617fdf2 Mon Sep 17 00:00:00 2001 From: Diego H Date: Fri, 1 Mar 2024 20:14:21 +0100 Subject: [PATCH 27/32] add_cff method --- NAMESPACE | 5 + R/as_cff.R | 49 ++ R/as_cff_bibentry.R | 423 +++++++++++ R/cff_parse_citation.R | 419 +---------- R/cff_read.R | 13 +- R/utils-create.R | 4 +- man/as_cff.Rd | 37 + man/cff_parse_citation.Rd | 2 +- tests/testthat/_snaps/as_bibentry.md | 24 +- tests/testthat/_snaps/cff-methods.md | 39 +- tests/testthat/_snaps/cff_parse_citation.md | 767 ++++++++++---------- tests/testthat/test-as_bibentry.R | 8 +- tests/testthat/test-cff_parse_citation.R | 79 +- 13 files changed, 991 insertions(+), 878 deletions(-) create mode 100644 R/as_cff.R create mode 100644 R/as_cff_bibentry.R create mode 100644 man/as_cff.Rd diff --git a/NAMESPACE b/NAMESPACE index 3aaf4a84..b12413d2 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -2,6 +2,10 @@ S3method(as.data.frame,cff) S3method(as.person,cff) +S3method(as_cff,bibentry) +S3method(as_cff,default) +S3method(as_cff,list) +S3method(as_cff,person) S3method(c,cff) S3method(head,cff) S3method(print,cff) @@ -9,6 +13,7 @@ S3method(tail,cff) S3method(toBibtex,cff) export(as.cff) export(as_bibentry) +export(as_cff) export(as_cff_person) export(cff) export(cff_create) diff --git a/R/as_cff.R b/R/as_cff.R new file mode 100644 index 00000000..99a960a6 --- /dev/null +++ b/R/as_cff.R @@ -0,0 +1,49 @@ +#' Coerce lists, `person` and `bibentry` objects to [`cff`][cff-class] +#' +#' @description +#' `as_cff()` turns an existing list-like **R** object into a so-called +#' [`cff`][cff-class], a list with class `cff`. +#' +#' `as_cff` is an S3 generic, with methods for: +#' - `person` objects as produced by `utils::person()`. +#' - `bibentry` objects as produced by `utils::bibentry()`. +#' - Default: Other inputs are first coerced with [as.list()]. +#' +#' @param x A `person`, `bibentry` or other object that could be coerced to a +#' list. +#' @param ... Additional arguments to be passed on to other methods. +#' +#' @rdname as_cff +#' @export +as_cff <- function(x, ...) { + UseMethod("as_cff") +} + +#' @rdname as_cff +#' @export +as_cff.default <- function(x, ...) { + as_cff(as.list(x), ...) +} + + +#' @rdname as_cff +#' @export +as_cff.list <- function(x, ...) { + # Clean up empty values on top + clean_up <- vapply(x, is.null, FUN.VALUE = logical(1)) + x_clean <- x[!clean_up] + new_cff(x_clean) +} + +#' @rdname as_cff +#' @export +as_cff.person <- function(x, ...) { + as_cff(as_cff_person(x), ...) +} + + +#' @rdname as_cff +#' @export +as_cff.bibentry <- function(x, ...) { + as_cff(as_cff_bibentry(x), ...) +} diff --git a/R/as_cff_bibentry.R b/R/as_cff_bibentry.R new file mode 100644 index 00000000..f9028a62 --- /dev/null +++ b/R/as_cff_bibentry.R @@ -0,0 +1,423 @@ +#' Internal functions for `as_cff.bibentry` method +#' +#' @noRd +as_cff_bibentry <- function(x) { + # Need always to be unnamed bibentry + bib <- unname(x) + + + # Return always a list + the_list <- lapply(bib, make_cff_reference) + the_list +} + +make_cff_reference <- function(bib) { + # Parse BibTeX entry ---- + parse_cit <- parse_bibtex_entry(bib) + + ## If no title (case of some Misc) then return null + if (!("title" %in% names(parse_cit))) { + entry <- capture.output(print(bib, bibtex = FALSE)) + entry <- as.character(entry) + + cli::cli_alert_warning("Entry {.val {entry}} without title. Skipping") + + return(NULL) + } + + # Parse BibTeX fields ---- + parsed_fields <- parse_bibtex_fields(parse_cit) + # VGAM: title is a vector + parsed_fields$title <- clean_str(parsed_fields$title) + + ## Handle collection types ---- + parsed_fields <- add_bibtex_coltype(parsed_fields) + + ## Add conference + parsed_fields <- add_conference(parsed_fields) + + # Create BibTeX to CFF institution logic ---- + parsed_fields <- parse_bibtex_to_inst(parsed_fields) + + # Parse persons ---- + # Special case: authors + # Some keys does not strictly require authors, so we create one for cff + # https://github.com/citation-file-format/citation-file-format/blob/main/ + # (cont) schema-guide.md#how-to-deal-with-unknown-individual-authors + + if (is.null(parsed_fields$authors)) { + parsed_fields$authors <- person(family = "anonymous") + } + + ## authors ---- + parse_all_authors <- as_cff_person(parsed_fields$authors) + parsed_fields$authors <- unique(parse_all_authors) + + ## other persons---- + parse_other_persons <- building_other_persons(parsed_fields) + + # Keep order here, we would use it later + init_ord <- names(parsed_fields) + + parse_cit <- c( + parsed_fields[!names(parsed_fields) %in% names(parse_other_persons)], + parse_other_persons + ) + + # Building blocks---- + + # Fallback for year and month: use date-published ---- + parse_cit <- fallback_dates(parse_cit) + + ## doi---- + bb_doi <- building_doi(parse_cit) + parse_cit$doi <- bb_doi$doi + + ### identifiers ---- + if (!is.null(bb_doi$identifiers)) parse_cit$identifiers <- bb_doi$identifiers + + + ## url---- + bb_url <- building_url(parse_cit) + parse_cit$url <- bb_url$url + + ### final identifiers---- + # Identifies (additional dois and urls) + if (!is.null(bb_url$identifiers)) { + parse_cit$identifiers <- append( + parse_cit$identifiers, + bb_url$identifiers + ) + } + + ## Add thesis type ---- + parse_cit <- add_thesis(parse_cit) + + ## Handle location ---- + parse_cit <- add_address(parse_cit) + + + # Last step---- + + # Initial order but starting with type, title, authors + final_order <- unique(c( + "type", "title", "authors", + init_ord, + names(parse_cit) + )) + + parse_cit <- parse_cit[final_order] + + # Remove non-valid names + validnames <- cff_schema_definitions_refs() + parse_cit <- parse_cit[names(parse_cit) %in% validnames] + + parse_cit <- drop_null(parse_cit) + parse_cit_result <- as.cff(parse_cit) + + return(parse_cit_result) +} + +#' Extract and map BibTeX entry +#' @noRd +parse_bibtex_entry <- function(bib) { + # Unclass and manage entry type + # Extract type from BibTeX + init_type <- attr(unclass(bib)[[1]], "bibtype") + init_type <- clean_str(tolower(init_type)) + + + parse_cit <- drop_null(unclass(bib)[[1]]) + + # Add fields + parse_cit$bibtex_entry <- init_type + + # Manage type from BibTeX and convert to CFF + # This overwrite the BibTeX type field. Not parsed by this function + parse_cit$type <- switch(init_type, + "article" = "article", + "book" = "book", + "booklet" = "pamphlet", + "conference" = "conference-paper", + "inbook" = "book", + # "incollection" = , + "inproceedings" = "conference-paper", + "manual" = "manual", + "mastersthesis" = "thesis", + "misc" = "generic", + "phdthesis" = "thesis", + "proceedings" = "proceedings", + "techreport" = "report", + "unpublished" = "unpublished", + "generic" + ) + + + # Check if it an inbook with booktitle (BibLaTeX style) + if (all(init_type == "inbook", "booktitle" %in% names(parse_cit))) { + # Make it incollection + parse_cit$bibtex_entry <- "incollection" + parse_cit$type <- "generic" + } + + + return(parse_cit) +} + +#' Adapt names from R citation()/BibTeX to cff format +#' @noRd +parse_bibtex_fields <- function(parse_cit) { + # to lowercase + names(parse_cit) <- tolower(names(parse_cit)) + nm <- names(parse_cit) + # Standard BibTeX fields: + # address annote author booktitle chapter crossref edition editor + # howpublished institution journal key month note number organization pages + # publisher school series title type year + + # No mapping needed (direct mapping) + # edition journal month publisher title volume year + + # Mapped: + # author booktitle series chapter editor howpublished note number + + nm[nm == "author"] <- "authors" + # Make collection title + # booktitle takes precedence over series + nm[nm == "booktitle"] <- "collection-title" + if (!"collection-title" %in% nm) { + nm[nm == "series"] <- "collection-title" + } + nm[nm == "chapter"] <- "section" + nm[nm == "editor"] <- "editors" + nm[nm == "howpublished"] <- "medium" + nm[nm == "note"] <- "notes" + nm[nm == "number"] <- "issue" + nm[nm == "address"] <- "location" + nm[nm == "pages"] <- "bibtex_pages" # This would be removed later + + # Parse some fields from BibLaTeX + nm[nm == "date"] <- "date-published" + nm[nm == "file"] <- "filename" + nm[nm == "issuetitle"] <- "issue-title" + nm[nm == "translator"] <- "translators" + nm[nm == "urldate"] <- "date-accessed" + nm[nm == "pagetotal"] <- "pages" + + # Other BibLaTeX fields that does not require any mapping + # abstract, doi, isbn, issn, url, version + + + # Keywords may be duplicated, unify + if ("keywords" %in% nm) { + kwords <- unlist(parse_cit["keywords" == nm]) + kwords <- clean_str(paste(kwords, collapse = ", ")) + kwords <- trimws(unique(unlist(strsplit(kwords, ",|;")))) + parse_cit$keywords <- unique(kwords) + } + + # Not mapped: + # annote crossref key organization series type + # + # Fields address, organization, series and type are treated on + # main function + # key is a special field, treated apart + # Fields ignored: annote, crossref + + names(parse_cit) <- nm + + # Remove all instances of keywords except the first one + index <- which(nm == "keywords") + if (length(index) > 1) parse_cit <- parse_cit[-index[-1]] + + # Additionally, need to delete keywords if length is less than 2, + # errors on validation + if (length(parse_cit$keywords) < 2) { + parse_cit$keywords <- NULL + } + + # Treat location ---- + + loc <- parse_cit$location + + if (!is.null(loc)) parse_cit$location <- loc + + + + # Treat dates---- + datpub <- parse_cit$`date-published` + + + if (!is.null(datpub)) { + datepub <- as.Date(as.character(datpub), optional = TRUE) + if (is.na(datepub)) { + parse_cit$`date-published` <- NULL + } else { + parse_cit$`date-published` <- as.character(datepub) + } + } + + datacc <- parse_cit$`date-accessed` + + + if (!is.null(datacc)) { + datacc <- as.Date(as.character(datacc), optional = TRUE) + if (is.na(datacc)) { + parse_cit$`date-accessed` <- NULL + } else { + parse_cit$`date-accessed` <- as.character(datacc) + } + } + + # Treat pages + + pages <- parse_cit$bibtex_pages + if (!is.null(pages)) { + spl <- unlist(strsplit(pages, "--")) + + parse_cit$start <- spl[1] + + if (length(spl) > 1) parse_cit$end <- paste(spl[-1], collapse = "--") + } + + return(parse_cit) +} + +#' Modify mapping of some org. fields on BibTeX to CFF +#' @noRd +parse_bibtex_to_inst <- function(parsed_fields) { + # Initial values + bibtex_entry <- parsed_fields$bibtex_entry + to_replace <- switch(bibtex_entry, + "mastersthesis" = "school", + "phdthesis" = "school", + "conference" = "organization", + "inproceedings" = "organization", + "manual" = "organization", + "proceedings" = "organization", + "institution" + ) + + if (to_replace == "institution") { + return(parsed_fields) + } + + # Rest of cases remove bibtex institution and rename + nms <- names(parsed_fields) + + parsed_fields <- parsed_fields["institution" != nms] + + # Rename + nms2 <- names(parsed_fields) + nms2[nms2 == to_replace] <- "institution" + names(parsed_fields) <- nms2 + + parsed_fields +} + +add_conference <- function(parsed_fields) { + bibtex_entry <- parsed_fields$bibtex_entry + + if (bibtex_entry %in% c("conference", "inproceedings", "proceedings")) { + parsed_fields$conference <- parsed_fields$`collection-title` + } + return(parsed_fields) +} + + + + +#' Adapt cff keys to bibtex entries +#' @noRd +add_thesis <- function(parse_cit) { + bibtex_entry <- parse_cit$bibtex_entry + if (!bibtex_entry %in% c("phdthesis", "mastersthesis")) { + return(parse_cit) + } + + parse_cit$`thesis-type` <- switch(bibtex_entry, + phdthesis = "PhD Thesis", + "Master's Thesis" + ) + + parse_cit +} + +add_address <- function(parse_cit) { + loc <- parse_cit$location$name + # If available + if (is.null(loc)) { + return(parse_cit) + } + + # At this point is in location, see to move + + # Logic order. + # 1. To conference + # 2. To institution + # 3. To publisher + # Otherwise leave on location + + nms <- names(parse_cit) + has_conf <- "conference" %in% nms + has_inst <- "institution" %in% nms + has_publish <- "publisher" %in% nms + + if (!any(has_conf, has_inst, has_publish)) { + return(parse_cit) + } + + if (has_conf) { + parse_cit$conference$address <- loc + parse_cit$location <- NULL + } else if (has_inst) { + parse_cit$institution$address <- loc + parse_cit$location <- NULL + } else { + parse_cit$publisher$address <- loc + parse_cit$location <- NULL + } + + return(parse_cit) +} + +add_bibtex_coltype <- function(parsed_fields) { + # Add collection-type if applicable and rearrange fields + nms <- names(parsed_fields) + + if (!"collection-title" %in% nms) { + return(parsed_fields) + } + + # Made collection-type if we create collection-title + bibtex_type <- parsed_fields$bibtex_entry + + # Remove `in` at init: inbook, incollection affected + coltype <- clean_str(gsub("^in", "", bibtex_type)) + parsed_fields$`collection-type` <- coltype + + # Rearrange to make both collection keys together + nm_first <- nms[seq(1, match("collection-title", nms))] + + nms_end <- unique(c(nm_first, "collection-type", nms)) + + parsed_fields <- parsed_fields[nms_end] + + return(parsed_fields) +} + +fallback_dates <- function(parse_cit) { + # Fallback for year and month: use date-published + if (is.null(parse_cit$month) && !is.null(parse_cit$`date-published`)) { + parse_cit$month <- format(as.Date(parse_cit$`date-published`), "%m") + } + + if (is.null(parse_cit$year) && !is.null(parse_cit$`date-published`)) { + parse_cit$year <- format(as.Date(parse_cit$`date-published`), "%Y") + } + + ## month ---- + parse_cit$month <- building_month(parse_cit) + + return(parse_cit) +} diff --git a/R/cff_parse_citation.R b/R/cff_parse_citation.R index 620f5326..b6056b13 100644 --- a/R/cff_parse_citation.R +++ b/R/cff_parse_citation.R @@ -11,7 +11,7 @@ #' @param bib A `bibentry` object, either created with [bibentry()] #' (preferred) or [citEntry()]. #' -#' @return A [`cff`] object ready to be used on [cff_create()]. +#' @return A list [`cff`] object ready to be used on [cff_create()]. #' #' @details #' This is a helper function designed to help on adding or @@ -79,418 +79,13 @@ cff_parse_citation <- function(bib) { ## Unname bib <- unname(bib) - if (length(bib) > 1) { - bib <- lapply(bib, cff_parse_citation) - class(bib) <- "cff" - return(bib) - } - - # Parse BibTeX entry ---- - parse_cit <- parse_bibtex_entry(bib) - - ## If no title (case of some Misc) then return null - if (!("title" %in% names(parse_cit))) { - entry <- capture.output(print(bib, bibtex = FALSE)) - entry <- as.character(entry) - - cli::cli_alert_warning("Entry {.val {entry}} without title. Skipping") + the_obj <- lapply(bib, make_cff_reference) + clean_up <- vapply(the_obj, is.null, FUN.VALUE = logical(1)) + # No results + if (all(clean_up)) { return(NULL) } - - # Parse BibTeX fields ---- - parsed_fields <- parse_bibtex_fields(parse_cit) - # VGAM: title is a vector - parsed_fields$title <- clean_str(parsed_fields$title) - - ## Handle collection types ---- - parsed_fields <- add_bibtex_coltype(parsed_fields) - - ## Add conference - parsed_fields <- add_conference(parsed_fields) - - # Create BibTeX to CFF institution logic ---- - parsed_fields <- parse_bibtex_to_inst(parsed_fields) - - # Parse persons ---- - # Special case: authors - # Some keys does not strictly require authors, so we create one for cff - # https://github.com/citation-file-format/citation-file-format/blob/main/ - # (cont) schema-guide.md#how-to-deal-with-unknown-individual-authors - - if (is.null(parsed_fields$authors)) { - parsed_fields$authors <- person(family = "anonymous") - } - - ## authors ---- - parse_all_authors <- as_cff_person(parsed_fields$authors) - parsed_fields$authors <- unique(parse_all_authors) - - ## other persons---- - parse_other_persons <- building_other_persons(parsed_fields) - - # Keep order here, we would use it later - init_ord <- names(parsed_fields) - - parse_cit <- c( - parsed_fields[!names(parsed_fields) %in% names(parse_other_persons)], - parse_other_persons - ) - - # Building blocks---- - - # Fallback for year and month: use date-published ---- - parse_cit <- fallback_dates(parse_cit) - - ## doi---- - bb_doi <- building_doi(parse_cit) - parse_cit$doi <- bb_doi$doi - - ### identifiers ---- - if (!is.null(bb_doi$identifiers)) parse_cit$identifiers <- bb_doi$identifiers - - - ## url---- - bb_url <- building_url(parse_cit) - parse_cit$url <- bb_url$url - - ### final identifiers---- - # Identifies (additional dois and urls) - if (!is.null(bb_url$identifiers)) { - parse_cit$identifiers <- append( - parse_cit$identifiers, - bb_url$identifiers - ) - } - - ## Add thesis type ---- - parse_cit <- add_thesis(parse_cit) - - ## Handle location ---- - parse_cit <- add_address(parse_cit) - - - # Last step---- - - # Initial order but starting with type, title, authors - final_order <- unique(c( - "type", "title", "authors", - init_ord, - names(parse_cit) - )) - - parse_cit <- parse_cit[final_order] - - # Remove non-valid names - validnames <- cff_schema_definitions_refs() - parse_cit <- parse_cit[names(parse_cit) %in% validnames] - - parse_cit <- drop_null(parse_cit) - parse_cit_result <- as.cff(parse_cit) - - return(parse_cit_result) -} - -#' Extract and map BibTeX entry -#' @noRd -parse_bibtex_entry <- function(bib) { - # Unclass and manage entry type - # Extract type from BibTeX - init_type <- attr(unclass(bib)[[1]], "bibtype") - init_type <- clean_str(tolower(init_type)) - - - parse_cit <- drop_null(unclass(bib)[[1]]) - - # Add fields - parse_cit$bibtex_entry <- init_type - - # Manage type from BibTeX and convert to CFF - # This overwrite the BibTeX type field. Not parsed by this function - parse_cit$type <- switch(init_type, - "article" = "article", - "book" = "book", - "booklet" = "pamphlet", - "conference" = "conference-paper", - "inbook" = "book", - # "incollection" = , - "inproceedings" = "conference-paper", - "manual" = "manual", - "mastersthesis" = "thesis", - "misc" = "generic", - "phdthesis" = "thesis", - "proceedings" = "proceedings", - "techreport" = "report", - "unpublished" = "unpublished", - "generic" - ) - - - # Check if it an inbook with booktitle (BibLaTeX style) - if (all(init_type == "inbook", "booktitle" %in% names(parse_cit))) { - # Make it incollection - parse_cit$bibtex_entry <- "incollection" - parse_cit$type <- "generic" - } - - - return(parse_cit) -} - -#' Adapt names from R citation()/BibTeX to cff format -#' @noRd -parse_bibtex_fields <- function(parse_cit) { - # to lowercase - names(parse_cit) <- tolower(names(parse_cit)) - nm <- names(parse_cit) - # Standard BibTeX fields: - # address annote author booktitle chapter crossref edition editor - # howpublished institution journal key month note number organization pages - # publisher school series title type year - - # No mapping needed (direct mapping) - # edition journal month publisher title volume year - - # Mapped: - # author booktitle series chapter editor howpublished note number - - nm[nm == "author"] <- "authors" - # Make collection title - # booktitle takes precedence over series - nm[nm == "booktitle"] <- "collection-title" - if (!"collection-title" %in% nm) { - nm[nm == "series"] <- "collection-title" - } - nm[nm == "chapter"] <- "section" - nm[nm == "editor"] <- "editors" - nm[nm == "howpublished"] <- "medium" - nm[nm == "note"] <- "notes" - nm[nm == "number"] <- "issue" - nm[nm == "address"] <- "location" - nm[nm == "pages"] <- "bibtex_pages" # This would be removed later - - # Parse some fields from BibLaTeX - nm[nm == "date"] <- "date-published" - nm[nm == "file"] <- "filename" - nm[nm == "issuetitle"] <- "issue-title" - nm[nm == "translator"] <- "translators" - nm[nm == "urldate"] <- "date-accessed" - nm[nm == "pagetotal"] <- "pages" - - # Other BibLaTeX fields that does not require any mapping - # abstract, doi, isbn, issn, url, version - - - # Keywords may be duplicated, unify - if ("keywords" %in% nm) { - kwords <- unlist(parse_cit["keywords" == nm]) - kwords <- clean_str(paste(kwords, collapse = ", ")) - kwords <- trimws(unique(unlist(strsplit(kwords, ",|;")))) - parse_cit$keywords <- unique(kwords) - } - - # Not mapped: - # annote crossref key organization series type - # - # Fields address, organization, series and type are treated on - # main function - # key is a special field, treated apart - # Fields ignored: annote, crossref - - names(parse_cit) <- nm - - # Remove all instances of keywords except the first one - index <- which(nm == "keywords") - if (length(index) > 1) parse_cit <- parse_cit[-index[-1]] - - # Additionally, need to delete keywords if length is less than 2, - # errors on validation - if (length(parse_cit$keywords) < 2) { - parse_cit$keywords <- NULL - } - - # Treat location ---- - - loc <- parse_cit$location - - if (!is.null(loc)) parse_cit$location <- loc - - - - # Treat dates---- - datpub <- parse_cit$`date-published` - - - if (!is.null(datpub)) { - datepub <- as.Date(as.character(datpub), optional = TRUE) - if (is.na(datepub)) { - parse_cit$`date-published` <- NULL - } else { - parse_cit$`date-published` <- as.character(datepub) - } - } - - datacc <- parse_cit$`date-accessed` - - - if (!is.null(datacc)) { - datacc <- as.Date(as.character(datacc), optional = TRUE) - if (is.na(datacc)) { - parse_cit$`date-accessed` <- NULL - } else { - parse_cit$`date-accessed` <- as.character(datacc) - } - } - - # Treat pages - - pages <- parse_cit$bibtex_pages - if (!is.null(pages)) { - spl <- unlist(strsplit(pages, "--")) - - parse_cit$start <- spl[1] - - if (length(spl) > 1) parse_cit$end <- paste(spl[-1], collapse = "--") - } - - return(parse_cit) -} - -#' Modify mapping of some org. fields on BibTeX to CFF -#' @noRd -parse_bibtex_to_inst <- function(parsed_fields) { - # Initial values - bibtex_entry <- parsed_fields$bibtex_entry - to_replace <- switch(bibtex_entry, - "mastersthesis" = "school", - "phdthesis" = "school", - "conference" = "organization", - "inproceedings" = "organization", - "manual" = "organization", - "proceedings" = "organization", - "institution" - ) - - if (to_replace == "institution") { - return(parsed_fields) - } - - # Rest of cases remove bibtex institution and rename - nms <- names(parsed_fields) - - parsed_fields <- parsed_fields["institution" != nms] - - # Rename - nms2 <- names(parsed_fields) - nms2[nms2 == to_replace] <- "institution" - names(parsed_fields) <- nms2 - - parsed_fields -} - -add_conference <- function(parsed_fields) { - bibtex_entry <- parsed_fields$bibtex_entry - - if (bibtex_entry %in% c("conference", "inproceedings", "proceedings")) { - parsed_fields$conference <- parsed_fields$`collection-title` - } - return(parsed_fields) -} - - - - -#' Adapt cff keys to bibtex entries -#' @noRd -add_thesis <- function(parse_cit) { - bibtex_entry <- parse_cit$bibtex_entry - if (!bibtex_entry %in% c("phdthesis", "mastersthesis")) { - return(parse_cit) - } - - parse_cit$`thesis-type` <- switch(bibtex_entry, - phdthesis = "PhD Thesis", - "Master's Thesis" - ) - - parse_cit -} - -add_address <- function(parse_cit) { - loc <- parse_cit$location$name - # If available - if (is.null(loc)) { - return(parse_cit) - } - - # At this point is in location, see to move - - # Logic order. - # 1. To conference - # 2. To institution - # 3. To publisher - # Otherwise leave on location - - nms <- names(parse_cit) - has_conf <- "conference" %in% nms - has_inst <- "institution" %in% nms - has_publish <- "publisher" %in% nms - - if (!any(has_conf, has_inst, has_publish)) { - return(parse_cit) - } - - if (has_conf) { - parse_cit$conference$address <- loc - parse_cit$location <- NULL - } else if (has_inst) { - parse_cit$institution$address <- loc - parse_cit$location <- NULL - } else { - parse_cit$publisher$address <- loc - parse_cit$location <- NULL - } - - return(parse_cit) -} - -add_bibtex_coltype <- function(parsed_fields) { - # Add collection-type if applicable and rearrange fields - nms <- names(parsed_fields) - - if (!"collection-title" %in% nms) { - return(parsed_fields) - } - - # Made collection-type if we create collection-title - bibtex_type <- parsed_fields$bibtex_entry - - # Remove `in` at init: inbook, incollection affected - coltype <- clean_str(gsub("^in", "", bibtex_type)) - parsed_fields$`collection-type` <- coltype - - # Rearrange to make both collection keys together - nm_first <- nms[seq(1, match("collection-title", nms))] - - nms_end <- unique(c(nm_first, "collection-type", nms)) - - parsed_fields <- parsed_fields[nms_end] - - return(parsed_fields) -} - -fallback_dates <- function(parse_cit) { - # Fallback for year and month: use date-published - if (is.null(parse_cit$month) && !is.null(parse_cit$`date-published`)) { - parse_cit$month <- format(as.Date(parse_cit$`date-published`), "%m") - } - - if (is.null(parse_cit$year) && !is.null(parse_cit$`date-published`)) { - parse_cit$year <- format(as.Date(parse_cit$`date-published`), "%Y") - } - - ## month ---- - parse_cit$month <- building_month(parse_cit) - - return(parse_cit) + the_obj <- the_obj[!clean_up] + new_cff(the_obj) } diff --git a/R/cff_read.R b/R/cff_read.R index c8214a5d..c5f3f55a 100644 --- a/R/cff_read.R +++ b/R/cff_read.R @@ -256,7 +256,7 @@ cff_read_citation <- function(path, meta = NULL, ...) { } # nocov end } - tocff <- lapply(the_cit, cff_parse_citation) + tocff <- cff_parse_citation(the_cit) tocff <- new_cff(tocff) unname(tocff) } @@ -288,7 +288,7 @@ cff_read_bib <- function(path, encoding = "UTF-8", ...) { read_bib <- bibtex::read.bib(file = path, encoding = encoding, ...) - tocff <- lapply(read_bib, cff_parse_citation) + tocff <- cff_parse_citation(read_bib) tocff <- new_cff(tocff) unname(tocff) } @@ -312,15 +312,8 @@ cff_safe_read_citation <- function(desc_path, cit_path) { } # Need to be named here - tocff <- lapply(the_cit, cff_parse_citation) - make_names <- vapply(tocff, function(x) { - myname <- gsub("[^a-z]", "", tolower(x$title)) - substr(myname, 1, 10) - }, character(1)) - - names(tocff) <- make_names + tocff <- cff_parse_citation(the_cit) tocff <- new_cff(tocff) - unname(tocff) } # Helpers ---- diff --git a/R/utils-create.R b/R/utils-create.R index 45678633..2f1040e6 100644 --- a/R/utils-create.R +++ b/R/utils-create.R @@ -117,10 +117,10 @@ parse_dependencies <- function(desc_path, n <- av_deps[y, ] if (n$package == "R") { - mod <- cff_parse_citation(citation()[1]) + mod <- cff_parse_citation(citation())[[1]] mod$year <- format(Sys.Date(), "%Y") } else { - mod <- try(cff_parse_citation(citation(n$package, auto = TRUE)[1]), + mod <- try(cff_parse_citation(citation(n$package, auto = TRUE)[1])[[1]], silent = TRUE ) diff --git a/man/as_cff.Rd b/man/as_cff.Rd new file mode 100644 index 00000000..dc3185d0 --- /dev/null +++ b/man/as_cff.Rd @@ -0,0 +1,37 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/as_cff.R +\name{as_cff} +\alias{as_cff} +\alias{as_cff.default} +\alias{as_cff.list} +\alias{as_cff.person} +\alias{as_cff.bibentry} +\title{Coerce lists, \code{person} and \code{bibentry} objects to \code{\link[=cff-class]{cff}}} +\usage{ +as_cff(x, ...) + +\method{as_cff}{default}(x, ...) + +\method{as_cff}{list}(x, ...) + +\method{as_cff}{person}(x, ...) + +\method{as_cff}{bibentry}(x, ...) +} +\arguments{ +\item{x}{A \code{person}, \code{bibentry} or other object that could be coerced to a +list.} + +\item{...}{Additional arguments to be passed on to other methods.} +} +\description{ +\code{as_cff()} turns an existing list-like \strong{R} object into a so-called +\code{\link[=cff-class]{cff}}, a list with class \code{cff}. + +\code{as_cff} is an S3 generic, with methods for: +\itemize{ +\item \code{person} objects as produced by \code{utils::person()}. +\item \code{bibentry} objects as produced by \code{utils::bibentry()}. +\item Default: Other inputs are first coerced with \code{\link[=as.list]{as.list()}}. +} +} diff --git a/man/cff_parse_citation.Rd b/man/cff_parse_citation.Rd index ae8a90a9..97b9f323 100644 --- a/man/cff_parse_citation.Rd +++ b/man/cff_parse_citation.Rd @@ -11,7 +11,7 @@ cff_parse_citation(bib) (preferred) or \code{\link[=citEntry]{citEntry()}}.} } \value{ -A \code{\link{cff}} object ready to be used on \code{\link[=cff_create]{cff_create()}}. +A list \code{\link{cff}} object ready to be used on \code{\link[=cff_create]{cff_create()}}. } \description{ Parse a \code{bibentry} object to a valid format for a \code{CITATION.cff} file. diff --git a/tests/testthat/_snaps/as_bibentry.md b/tests/testthat/_snaps/as_bibentry.md index a5fb5d60..063a6a62 100644 --- a/tests/testthat/_snaps/as_bibentry.md +++ b/tests/testthat/_snaps/as_bibentry.md @@ -397,18 +397,18 @@ Code bibparsed Output - type: book - title: A Handbook for Scholars - authors: - - family-names: Leunen - given-names: Mary-Claire - name-particle: van - - family-names: Davis - given-names: Sammy - name-suffix: Jr. - year: '1979' - publisher: - name: Knopf + - type: book + title: A Handbook for Scholars + authors: + - family-names: Leunen + given-names: Mary-Claire + name-particle: van + - family-names: Davis + given-names: Sammy + name-suffix: Jr. + year: '1979' + publisher: + name: Knopf --- diff --git a/tests/testthat/_snaps/cff-methods.md b/tests/testthat/_snaps/cff-methods.md index 5c784a72..f3a34f2f 100644 --- a/tests/testthat/_snaps/cff-methods.md +++ b/tests/testthat/_snaps/cff-methods.md @@ -798,19 +798,32 @@ Code names(the_df) Output - [1] "type" "title" - [3] "authors.00.family_names" "authors.00.given_names" - [5] "authors.00.email" "authors.00.orcid" - [7] "authors.01.family_names" "authors.01.given_names" - [9] "authors.01.affiliation" "authors.01.country" - [11] "authors.02.family_names" "authors.02.given_names" - [13] "authors.02.email" "authors.03.name" - [15] "authors.03.date_end" "url" - [17] "keywords.00" "keywords.01" - [19] "keywords.02" "keywords.03" - [21] "keywords.04" "keywords.05" - [23] "keywords.06" "keywords.07" - [25] "abstract" "version" + [1] "reference.00.type" + [2] "reference.00.title" + [3] "reference.00.authors.00.family_names" + [4] "reference.00.authors.00.given_names" + [5] "reference.00.authors.00.email" + [6] "reference.00.authors.00.orcid" + [7] "reference.00.authors.01.family_names" + [8] "reference.00.authors.01.given_names" + [9] "reference.00.authors.01.affiliation" + [10] "reference.00.authors.01.country" + [11] "reference.00.authors.02.family_names" + [12] "reference.00.authors.02.given_names" + [13] "reference.00.authors.02.email" + [14] "reference.00.authors.03.name" + [15] "reference.00.authors.03.date_end" + [16] "reference.00.url" + [17] "reference.00.keywords.00" + [18] "reference.00.keywords.01" + [19] "reference.00.keywords.02" + [20] "reference.00.keywords.03" + [21] "reference.00.keywords.04" + [22] "reference.00.keywords.05" + [23] "reference.00.keywords.06" + [24] "reference.00.keywords.07" + [25] "reference.00.abstract" + [26] "reference.00.version" # Convert authors only diff --git a/tests/testthat/_snaps/cff_parse_citation.md b/tests/testthat/_snaps/cff_parse_citation.md index 83643cf2..430c42f4 100644 --- a/tests/testthat/_snaps/cff_parse_citation.md +++ b/tests/testthat/_snaps/cff_parse_citation.md @@ -230,472 +230,479 @@ Code bibparsed Output - type: manual - title: A Language and Environment for Statistical Computing - authors: - - name: R Core Team - year: '2021' - contact: - - family-names: name - given-names: A - - family-names: contact - given-names: A - conference: - name: A conference - address: A location - database-provider: - name: Database provider - editors: - - family-names: editor - given-names: A - - name: Ben and Jerry - editors-series: - - family-names: editor series - given-names: An - - name: Another - publisher: - name: A publisher - recipients: - - family-names: recipient - given-names: A - senders: - - name: A Sender - - family-names: Sender - given-names: Another - translators: - - family-names: one - given-names: Translator - - family-names: two - given-names: Translator + - type: manual + title: A Language and Environment for Statistical Computing + authors: + - name: R Core Team + year: '2021' + contact: + - family-names: name + given-names: A + - family-names: contact + given-names: A + conference: + name: A conference + address: A location + database-provider: + name: Database provider + editors: + - family-names: editor + given-names: A + - name: Ben and Jerry + editors-series: + - family-names: editor series + given-names: An + - name: Another + publisher: + name: A publisher + recipients: + - family-names: recipient + given-names: A + senders: + - name: A Sender + - family-names: Sender + given-names: Another + translators: + - family-names: one + given-names: Translator + - family-names: two + given-names: Translator # Test inputs Code bibparsed Output - type: book - title: Test - authors: - - family-names: Jean - given-names: Billy - year: '2021' - publisher: - name: Random House + - type: book + title: Test + authors: + - family-names: Jean + given-names: Billy + year: '2021' + publisher: + name: Random House # Article Code bibparsed Output - type: article - title: Literate Programming - authors: - - name: R Core Team - journal: The Computer Journal - year: '1984' - volume: '27' - issue: '2' - month: '1' - notes: Example modified for testing purposes - start: '97' - end: '111' + - type: article + title: Literate Programming + authors: + - name: R Core Team + journal: The Computer Journal + year: '1984' + volume: '27' + issue: '2' + month: '1' + notes: Example modified for testing purposes + start: '97' + end: '111' # Book Code bibparsed Output - type: book - title: The LaTeX Companion - authors: - - family-names: Mittelbach - given-names: Frank - - family-names: Gossens - given-names: Michel - - family-names: Braams - given-names: Johannes - - family-names: Carlisle - given-names: David - - family-names: Rowley - given-names: Chris - editors: - - name: Barnes and Noble - publisher: - name: Addison-Wesley Professional - address: Santa Monica - year: '2004' - volume: '3' - issue: '7' - collection-title: The LateX Books - collection-type: book - edition: Fourth - month: '8' - notes: Example modified for testing purposes - keywords: - - Two - - keyword + - type: book + title: The LaTeX Companion + authors: + - family-names: Mittelbach + given-names: Frank + - family-names: Gossens + given-names: Michel + - family-names: Braams + given-names: Johannes + - family-names: Carlisle + given-names: David + - family-names: Rowley + given-names: Chris + editors: + - name: Barnes and Noble + publisher: + name: Addison-Wesley Professional + address: Santa Monica + year: '2004' + volume: '3' + issue: '7' + collection-title: The LateX Books + collection-type: book + edition: Fourth + month: '8' + notes: Example modified for testing purposes + keywords: + - Two + - keyword # Booklet Code bibparsed Output - type: pamphlet - title: Java Booklet - authors: - - family-names: Mustermann - given-names: Max - medium: Internet - location: - name: Stuttgart - month: '2' - year: '2016' - notes: Example modified from Jabref + - type: pamphlet + title: Java Booklet + authors: + - family-names: Mustermann + given-names: Max + medium: Internet + location: + name: Stuttgart + month: '2' + year: '2016' + notes: Example modified from Jabref # Conference Code bibparsed Output - type: conference-paper - title: On Notions of Information Transfer in VLSI Circuits - authors: - - family-names: Oaho - given-names: Alfred V. - - family-names: Ullman - given-names: Jeffrey D. - - family-names: Yannakakis - given-names: Mihalis - collection-title: Proc. Fifteenth Annual ACM STOC - collection-type: conference - year: '1983' - editors: - - family-names: Oz - given-names: Wizard V. - - family-names: Yannakakis - given-names: Mihalis - volume: '41' - issue: '17' - institution: - name: ACM - publisher: - name: Academic Press - notes: Example modified for testing purposes - start: '133' - end: '139' - conference: - name: Proc. Fifteenth Annual ACM STOC - address: Boston + - type: conference-paper + title: On Notions of Information Transfer in VLSI Circuits + authors: + - family-names: Oaho + given-names: Alfred V. + - family-names: Ullman + given-names: Jeffrey D. + - family-names: Yannakakis + given-names: Mihalis + collection-title: Proc. Fifteenth Annual ACM STOC + collection-type: conference + year: '1983' + editors: + - family-names: Oz + given-names: Wizard V. + - family-names: Yannakakis + given-names: Mihalis + volume: '41' + issue: '17' + institution: + name: ACM + publisher: + name: Academic Press + notes: Example modified for testing purposes + start: '133' + end: '139' + conference: + name: Proc. Fifteenth Annual ACM STOC + address: Boston # InBook Code bibparsed Output - type: book - title: A Framework for Freeness Analysis - authors: - - family-names: King - given-names: A. - editors: - - family-names: Tick - given-names: E - - family-names: Succi - given-names: G - section: 7, 14 - publisher: - name: Kluwer Academic Publishers - address: Dordrecht - year: '1994' - volume: '27' - issue: '2' - collection-title: Implementations of Logic Programming Systems - collection-type: book - edition: Second - month: '1' - notes: Example modified for testing purposes - start: '137' - end: '149' + - type: book + title: A Framework for Freeness Analysis + authors: + - family-names: King + given-names: A. + editors: + - family-names: Tick + given-names: E + - family-names: Succi + given-names: G + section: 7, 14 + publisher: + name: Kluwer Academic Publishers + address: Dordrecht + year: '1994' + volume: '27' + issue: '2' + collection-title: Implementations of Logic Programming Systems + collection-type: book + edition: Second + month: '1' + notes: Example modified for testing purposes + start: '137' + end: '149' # InCollection Code bibparsed Output - type: generic - title: Knowledge-Based Methods for WSD - authors: - - family-names: Mihalcea - given-names: Rada - collection-title: 'Word Sense Disambiguation: Algorithms and Applications' - collection-type: collection - publisher: - name: Springer - address: 107--132 - year: '2006' - editors: - - family-names: Agirre - given-names: Eneko - - family-names: Edmonds - given-names: Philip - volume: '23' - issue: '3' - section: '1,2,3' - edition: Third - month: '8' - notes: A note - start: '24' - end: '57' + - type: generic + title: Knowledge-Based Methods for WSD + authors: + - family-names: Mihalcea + given-names: Rada + collection-title: 'Word Sense Disambiguation: Algorithms and Applications' + collection-type: collection + publisher: + name: Springer + address: 107--132 + year: '2006' + editors: + - family-names: Agirre + given-names: Eneko + - family-names: Edmonds + given-names: Philip + volume: '23' + issue: '3' + section: '1,2,3' + edition: Third + month: '8' + notes: A note + start: '24' + end: '57' # InProceedings Code bibparsed Output - type: conference-paper - title: On Notions of Information Transfer in VLSI Circuits - authors: - - family-names: Oaho - given-names: Alfred V. - - family-names: Ullman - given-names: Jeffrey D. - - family-names: Yannakakis - given-names: Mihalis - collection-title: Proc. Fifteenth Annual ACM STOC - collection-type: proceedings - year: '1983' - editors: - - family-names: Oz - given-names: Wizard V. - - family-names: Yannakakis - given-names: Mihalis - volume: '41' - issue: '17' - institution: - name: ACM - publisher: - name: Academic Press - notes: Example modified for testing purposes - start: '133' - end: '139' - conference: - name: Proc. Fifteenth Annual ACM STOC - address: Boston + - type: conference-paper + title: On Notions of Information Transfer in VLSI Circuits + authors: + - family-names: Oaho + given-names: Alfred V. + - family-names: Ullman + given-names: Jeffrey D. + - family-names: Yannakakis + given-names: Mihalis + collection-title: Proc. Fifteenth Annual ACM STOC + collection-type: proceedings + year: '1983' + editors: + - family-names: Oz + given-names: Wizard V. + - family-names: Yannakakis + given-names: Mihalis + volume: '41' + issue: '17' + institution: + name: ACM + publisher: + name: Academic Press + notes: Example modified for testing purposes + start: '133' + end: '139' + conference: + name: Proc. Fifteenth Annual ACM STOC + address: Boston # Manual Code bibparsed Output - type: manual - title: A Language and Environment for Statistical Computing - authors: - - name: R Core Team - institution: - name: R Foundation for Statistical Computing - address: Vienna, Austria - edition: Fourth - month: '8' - year: '2021' - notes: Example modified for testing purposes + - type: manual + title: A Language and Environment for Statistical Computing + authors: + - name: R Core Team + institution: + name: R Foundation for Statistical Computing + address: Vienna, Austria + edition: Fourth + month: '8' + year: '2021' + notes: Example modified for testing purposes # MastersThesis Code bibparsed Output - type: thesis - title: An examination of keystroke dynamics for continuous user authentication - authors: - - family-names: Alsolami - given-names: Eesa - institution: - name: Queensland University of Technology - address: Queensland, NZ - year: '2012' - month: '8' - notes: Example modified for testing purposes - thesis-type: Master's Thesis + - type: thesis + title: An examination of keystroke dynamics for continuous user authentication + authors: + - family-names: Alsolami + given-names: Eesa + institution: + name: Queensland University of Technology + address: Queensland, NZ + year: '2012' + month: '8' + notes: Example modified for testing purposes + thesis-type: Master's Thesis # Misc Code bibparsed Output - type: generic - title: A Language and Environment for Statistical Computing - authors: - - name: R Core Team - medium: CD-ROM - month: '1' - year: '2021' - notes: A note + - type: generic + title: A Language and Environment for Statistical Computing + authors: + - name: R Core Team + medium: CD-ROM + month: '1' + year: '2021' + notes: A note # PhdThesis Code bibparsed Output - type: thesis - title: An examination of keystroke dynamics for continuous user authentication - authors: - - family-names: Alsolami - given-names: Eesa - institution: - name: Queensland University of Technology - address: Queensland, NZ - year: '2012' - month: '8' - notes: Example modified for testing purposes - thesis-type: PhD Thesis + - type: thesis + title: An examination of keystroke dynamics for continuous user authentication + authors: + - family-names: Alsolami + given-names: Eesa + institution: + name: Queensland University of Technology + address: Queensland, NZ + year: '2012' + month: '8' + notes: Example modified for testing purposes + thesis-type: PhD Thesis # Proceedings Code bibparsed Output - type: proceedings - title: Proc. Fifteenth Annual STOC - authors: - - name: anonymous - year: '1983' - editors: - - family-names: Oz - given-names: Wizard V. - - family-names: Yannakakis - given-names: Mihalis - volume: '1' - issue: '17' - collection-title: All ACM Conferences - collection-type: proceedings - month: '8' - institution: - name: The OX Association for Computing Machinery - publisher: - name: Academic Press - notes: Example modified for testing purposes - conference: - name: All ACM Conferences - address: Boston, US + - type: proceedings + title: Proc. Fifteenth Annual STOC + authors: + - name: anonymous + year: '1983' + editors: + - family-names: Oz + given-names: Wizard V. + - family-names: Yannakakis + given-names: Mihalis + volume: '1' + issue: '17' + collection-title: All ACM Conferences + collection-type: proceedings + month: '8' + institution: + name: The OX Association for Computing Machinery + publisher: + name: Academic Press + notes: Example modified for testing purposes + conference: + name: All ACM Conferences + address: Boston, US # TechReport Code bibparsed Output - type: report - title: Naive tools for studying compilation histories - authors: - - family-names: Jadud - given-names: Matthew C. - - family-names: Fincher - given-names: Sally A. - institution: - name: University of Kent Canterbury - address: Computing Laboratory, University of Kent, Canterbury, Kent, CT2 7NF - year: '2003' - issue: 3-03 - month: '3' - notes: Example modified for testing purposes + - type: report + title: Naive tools for studying compilation histories + authors: + - family-names: Jadud + given-names: Matthew C. + - family-names: Fincher + given-names: Sally A. + institution: + name: University of Kent Canterbury + address: Computing Laboratory, University of Kent, Canterbury, Kent, CT2 7NF + year: '2003' + issue: 3-03 + month: '3' + notes: Example modified for testing purposes # Unpublished Code bibparsed Output - type: unpublished - title: Demonstratives - authors: - - family-names: Kaplan - given-names: D. - notes: Unpublished manuscript, UCLA - year: '1977' - month: '8' + - type: unpublished + title: Demonstratives + authors: + - family-names: Kaplan + given-names: D. + notes: Unpublished manuscript, UCLA + year: '1977' + month: '8' # InBook with booktitle Code bibparsed Output - type: generic - title: Bibliographies and citations - authors: - - family-names: Xie - given-names: Yihui - - family-names: Dervieux - given-names: Christophe - - family-names: Riederer - given-names: Emily - year: '2020' - collection-title: R Markdown Cookbook - collection-type: collection - publisher: - name: Chapman and Hall/CRC - address: Boca Raton, Florida - isbn: '9780367563837' - url: https://bookdown.org/yihui/rmarkdown-cookbook - section: '4.5' + - type: generic + title: Bibliographies and citations + authors: + - family-names: Xie + given-names: Yihui + - family-names: Dervieux + given-names: Christophe + - family-names: Riederer + given-names: Emily + year: '2020' + collection-title: R Markdown Cookbook + collection-type: collection + publisher: + name: Chapman and Hall/CRC + address: Boca Raton, Florida + isbn: '9780367563837' + url: https://bookdown.org/yihui/rmarkdown-cookbook + section: '4.5' # Test entry without author Code bibparsed Output - type: proceedings - title: Proceedings of the 6th European Conference on Computer Systems - authors: - - name: anonymous - editors: - - family-names: Berbers - given-names: Yolande - - family-names: Zwaenepoel - given-names: Willy - collection-title: Proceedings of the 6th European Conference on Computer Systems - collection-type: proceedings - publisher: - name: ACM - month: '4' - year: '2006' - isbn: 1-59593-322-02 - conference: - name: Proceedings of the 6th European Conference on Computer Systems + - type: proceedings + title: Proceedings of the 6th European Conference on Computer Systems + authors: + - name: anonymous + editors: + - family-names: Berbers + given-names: Yolande + - family-names: Zwaenepoel + given-names: Willy + collection-title: Proceedings of the 6th European Conference on Computer Systems + collection-type: proceedings + publisher: + name: ACM + month: '4' + year: '2006' + isbn: 1-59593-322-02 + conference: + name: Proceedings of the 6th European Conference on Computer Systems # Test entry without author but has a key Code bibparsed Output - type: generic - title: Proceedings of the 6th European Conference on Computer Systems - authors: - - name: anonymous - collection-title: Proceedings of the 6th European Conference on Computer Systems - collection-type: misc - publisher: - name: ACM - month: '4' - year: '2006' - isbn: 1-59593-322-02 + - type: generic + title: Proceedings of the 6th European Conference on Computer Systems + authors: + - name: anonymous + collection-title: Proceedings of the 6th European Conference on Computer Systems + collection-type: misc + publisher: + name: ACM + month: '4' + year: '2006' + isbn: 1-59593-322-02 # Test entry without author and key Code bibparsed Output - type: generic - title: Proceedings of the 6th European Conference on Computer Systems - authors: - - name: anonymous - collection-title: Proceedings of the 6th European Conference on Computer Systems - collection-type: misc - publisher: - name: ACM - month: '4' - year: '2006' - isbn: 1-59593-322-02 + - type: generic + title: Proceedings of the 6th European Conference on Computer Systems + authors: + - name: anonymous + collection-title: Proceedings of the 6th European Conference on Computer Systems + collection-type: misc + publisher: + name: ACM + month: '4' + year: '2006' + isbn: 1-59593-322-02 # Skip misc without title + Code + bibparsed <- cff_parse_citation(bib) + Message + ! Entry "SHERPA/RoMEO (2018). ." without title. Skipping + +--- + Code cffobj Output @@ -733,35 +740,35 @@ Code bibparsed Output - type: article - title: Computation of methodology hyphen independent ionic solvation free energies - from molecular simulations - authors: - - family-names: Kastenholz - given-names: M. A. - - family-names: Hünenbergerb - given-names: Philippe H. - journal: J. Chem. Phys. - year: '2006' - notes: Example modified for testing purposes - date-published: '2006-03-15' - filename: a_file.pdf - issue-title: Semantic 3D Media and Content - translators: - - family-names: Wicksteed - given-names: P. H. - - family-names: Cornford - given-names: F. M. - date-accessed: '2006-10-01' - pages: '528' - abstract: The computation of ionic solvation free energies from atomistic simulations - is a surprisingly difficult problem that has found no satisfactory solution for - more than 15 years. - doi: 10.1063/1.2172593 - isbn: 0-816-52066-6 - issn: 0097-8493 - url: http://www.ctan.org - start: '55' - end: '65' - month: '3' + - type: article + title: Computation of methodology hyphen independent ionic solvation free energies + from molecular simulations + authors: + - family-names: Kastenholz + given-names: M. A. + - family-names: Hünenbergerb + given-names: Philippe H. + journal: J. Chem. Phys. + year: '2006' + notes: Example modified for testing purposes + date-published: '2006-03-15' + filename: a_file.pdf + issue-title: Semantic 3D Media and Content + translators: + - family-names: Wicksteed + given-names: P. H. + - family-names: Cornford + given-names: F. M. + date-accessed: '2006-10-01' + pages: '528' + abstract: The computation of ionic solvation free energies from atomistic simulations + is a surprisingly difficult problem that has found no satisfactory solution for + more than 15 years. + doi: 10.1063/1.2172593 + isbn: 0-816-52066-6 + issn: 0097-8493 + url: http://www.ctan.org + start: '55' + end: '65' + month: '3' diff --git a/tests/testthat/test-as_bibentry.R b/tests/testthat/test-as_bibentry.R index 4a0b5c6a..a38c9169 100644 --- a/tests/testthat/test-as_bibentry.R +++ b/tests/testthat/test-as_bibentry.R @@ -257,12 +257,12 @@ test_that("particle names", { bibparsed <- cff_parse_citation(bib) - bibparsed$authors <- as_cff_person( + bibparsed[[1]]$authors <- as_cff_person( "van Leunen, Mary-Claire and Davis, Jr., Sammy" ) cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) + keys = list(references = bibparsed) ) expect_true(cff_validate(cffobj, verbose = FALSE)) @@ -283,7 +283,7 @@ test_that("From plain cff with a citation", { month = 3 ) - s$`preferred-citation` <- cff_parse_citation(acit) + s$`preferred-citation` <- cff_parse_citation(acit)[[1]] s$`preferred-citation`$editors <- as_cff_person("A name") bib <- as_bibentry(s) @@ -478,7 +478,7 @@ test_that("Corrupt entry", { month = "January", keywords = "Some, simple, keywords" ) - x <- cff_parse_citation(bib) + x <- cff_parse_citation(bib)[[1]] x$year <- NULL x$journal <- NULL expect_snapshot(n <- as_bibentry(x)) diff --git a/tests/testthat/test-cff_parse_citation.R b/tests/testthat/test-cff_parse_citation.R index b6e85f44..812e5428 100644 --- a/tests/testthat/test-cff_parse_citation.R +++ b/tests/testthat/test-cff_parse_citation.R @@ -55,7 +55,7 @@ test_that("Add wrong field to citation", { cffobj <- cff_create(cff(), keys = list( - references = list(bibparsed) + references = bibparsed ) ) @@ -81,9 +81,7 @@ test_that("Fix wrong orcid", { expect_s3_class(bibparsed, "cff") cffobj <- cff_create(cff(), - keys = list( - references = list(bibparsed) - ) + keys = list(references = bibparsed) ) expect_snapshot(cffobj) @@ -113,7 +111,7 @@ test_that("Several identifiers and duplicates", { cffobj <- cff_create(cff(), keys = list( - references = list(bibparsed) + references = bibparsed ) ) @@ -138,7 +136,7 @@ test_that("Test keywords and urls", { cffobj <- cff_create(cff(), keys = list( - references = list(bibparsed) + references = bibparsed ) ) @@ -168,7 +166,7 @@ test_that("Parse persons on CITATION", { expect_snapshot(bibparsed) cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) + keys = list(references = bibparsed) ) expect_true(cff_validate(cffobj, verbose = FALSE)) @@ -193,7 +191,7 @@ test_that("Test inputs", { expect_snapshot(bibparsed) cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) + keys = list(references = bibparsed) ) expect_true(cff_validate(cffobj, verbose = FALSE)) @@ -228,7 +226,7 @@ test_that("Article", { expect_snapshot(bibparsed) cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) + keys = list(references = bibparsed) ) expect_true(cff_validate(cffobj, verbose = FALSE)) @@ -267,7 +265,7 @@ test_that("Book", { expect_snapshot(bibparsed) cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) + keys = list(references = bibparsed) ) expect_true(cff_validate(cffobj, verbose = FALSE)) @@ -299,7 +297,7 @@ test_that("Booklet", { expect_snapshot(bibparsed) cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) + keys = list(references = bibparsed) ) expect_true(cff_validate(cffobj, verbose = FALSE)) @@ -350,7 +348,7 @@ test_that("Conference", { expect_snapshot(bibparsed) cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) + keys = list(references = bibparsed) ) expect_true(cff_validate(cffobj, verbose = FALSE)) @@ -389,7 +387,7 @@ test_that("InBook", { expect_snapshot(bibparsed) cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) + keys = list(references = bibparsed) ) expect_true(cff_validate(cffobj, verbose = FALSE)) @@ -429,7 +427,7 @@ test_that("InCollection", { expect_snapshot(bibparsed) cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) + keys = list(references = bibparsed) ) expect_true(cff_validate(cffobj, verbose = FALSE)) @@ -470,7 +468,7 @@ test_that("InProceedings", { expect_snapshot(bibparsed) cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) + keys = list(references = bibparsed) ) expect_true(cff_validate(cffobj, verbose = FALSE)) @@ -501,7 +499,7 @@ test_that("Manual", { expect_snapshot(bibparsed) cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) + keys = list(references = bibparsed) ) expect_true(cff_validate(cffobj, verbose = FALSE)) @@ -533,7 +531,7 @@ test_that("MastersThesis", { expect_snapshot(bibparsed) cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) + keys = list(references = bibparsed) ) expect_true(cff_validate(cffobj, verbose = FALSE)) @@ -562,7 +560,7 @@ test_that("Misc", { expect_snapshot(bibparsed) cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) + keys = list(references = bibparsed) ) expect_true(cff_validate(cffobj, verbose = FALSE)) @@ -595,7 +593,7 @@ test_that("PhdThesis", { expect_snapshot(bibparsed) cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) + keys = list(references = bibparsed) ) expect_true(cff_validate(cffobj, verbose = FALSE)) @@ -630,7 +628,7 @@ test_that("Proceedings", { expect_snapshot(bibparsed) cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) + keys = list(references = bibparsed) ) expect_true(cff_validate(cffobj, verbose = FALSE)) @@ -665,7 +663,7 @@ test_that("TechReport", { expect_snapshot(bibparsed) cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) + keys = list(references = bibparsed) ) expect_true(cff_validate(cffobj, verbose = FALSE)) @@ -693,7 +691,7 @@ test_that("Unpublished", { expect_snapshot(bibparsed) cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) + keys = list(references = bibparsed) ) expect_true(cff_validate(cffobj, verbose = FALSE)) @@ -725,7 +723,7 @@ test_that("InBook with booktitle", { expect_snapshot(bibparsed) cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) + keys = list(references = bibparsed) ) expect_true(cff_validate(cffobj, verbose = FALSE)) @@ -760,14 +758,14 @@ test_that("Test entry without author", { bibparsed <- cff_parse_citation(bib) expect_identical( - bibparsed$authors[[1]]$name, + bibparsed[[1]]$authors[[1]]$name, "anonymous" ) expect_snapshot(bibparsed) cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) + keys = list(references = bibparsed) ) expect_true(cff_validate(cffobj, verbose = FALSE)) @@ -792,7 +790,7 @@ test_that("Test entry without author but has a key", { bibparsed <- cff_parse_citation(bib) expect_identical( - bibparsed$authors[[1]]$name, + bibparsed[[1]]$authors[[1]]$name, "anonymous" ) @@ -800,7 +798,7 @@ test_that("Test entry without author but has a key", { expect_snapshot(bibparsed) cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) + keys = list(references = bibparsed) ) expect_true(cff_validate(cffobj, verbose = FALSE)) @@ -822,7 +820,7 @@ test_that("Test entry without author and key", { bibparsed <- cff_parse_citation(bib) expect_identical( - bibparsed$authors[[1]]$name, + bibparsed[[1]]$authors[[1]]$name, "anonymous" ) @@ -830,7 +828,7 @@ test_that("Test entry without author and key", { expect_snapshot(bibparsed) cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) + keys = list(references = bibparsed) ) expect_true(cff_validate(cffobj, verbose = FALSE)) @@ -845,12 +843,12 @@ test_that("Skip misc without title", { year = 2018 ) - expect_message(bibparsed <- cff_parse_citation(bib), "Skipping") + expect_snapshot(bibparsed <- cff_parse_citation(bib)) expect_null(bibparsed) cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) + keys = list(references = bibparsed) ) expect_snapshot(cffobj) @@ -862,18 +860,13 @@ test_that("Skip misc without title", { test_that("Skip misc without title, not skipping the good one", { bib <- c( bibentry( - bibtype = "misc", - author = c(person("SHERPA/RoMEO")), - url = "http://www.sherpa.ac.uk/romeo/", - year = 2018 + bibtype = "misc", author = c(person("SHERPA/RoMEO")), + url = "http://www.sherpa.ac.uk/romeo/", year = 2018 ), bibentry( bibtype = "misc", title = "{rromeo}: An {R} Client for {SHERPA/RoMEO} {API}", - author = c( - person("Matthias", "Grenié"), - person("Hugo", "Gruson") - ), + author = c(person("Matthias", "Grenié"), person("Hugo", "Gruson")), year = 2019, header = "To cite this package in publications, please use:", url = "https://CRAN.R-project.org/package=rromeo" @@ -884,11 +877,9 @@ test_that("Skip misc without title, not skipping the good one", { expect_message(bibparsed <- cff_parse_citation(bib), "SHERPA/RoMEO") - expect_length(bibparsed, 2) - - expect_null(bibparsed[[1]]) + expect_length(bibparsed, 1) - expect_s3_class(bibparsed[[2]], "cff") + expect_s3_class(bibparsed[[1]], "cff") cffobj <- cff_create(cff(), keys = list(references = bibparsed) @@ -935,7 +926,7 @@ test_that("Check extended BibLatex Fields", { expect_snapshot(bibparsed) cffobj <- cff_create(cff(), - keys = list(references = list(bibparsed)) + keys = list(references = bibparsed) ) expect_true(cff_validate(cffobj, verbose = FALSE)) From e8cff76a08165ba07e54940d965de1caa4b20f49 Mon Sep 17 00:00:00 2001 From: Diego H Date: Sat, 2 Mar 2024 11:32:27 +0100 Subject: [PATCH 28/32] Add as_cff, still left core functions --- NAMESPACE | 1 + NEWS.md | 19 ++- R/as_bibentry.R | 4 +- R/as_cff.R | 135 +++++++++++++++++- R/{as_cff_bibentry.R => as_cff_reference.R} | 5 +- R/cff-methods.R | 6 +- R/cff.R | 113 +-------------- R/cff_create.R | 4 +- R/cff_parse_citation.R | 91 ------------ R/cff_read.R | 15 +- R/cff_read_bib_text.R | 2 +- R/deprecated.R | 37 ++++- R/utils-create.R | 6 +- README.md | 2 +- codemeta.json | 2 +- man/as_bibentry.Rd | 9 +- man/as_cff.Rd | 67 ++++++++- man/as_cff_person.Rd | 8 +- man/cff-class.Rd | 14 +- man/cff.Rd | 24 +--- man/cff_parse_citation.Rd | 88 ------------ man/chunks/cffclass.Rmd | 8 +- man/deprecated_cff_bibentry.Rd | 39 +++++ man/deprecated_cff_from_bib.Rd | 1 + man/deprecated_cff_person.Rd | 3 +- man/deprecated_cff_to_bib.Rd | 1 + man/deprecated_write.Rd | 1 + pkgdown/_pkgdown.yml | 3 - tests/testthat/_snaps/as_cff.md | 8 ++ ..._parse_citation.md => as_cff_reference.md} | 7 - tests/testthat/_snaps/cff_read_bib_text.md | 2 +- tests/testthat/_snaps/deprecated.md | 9 ++ tests/testthat/test-as_bibentry.R | 42 +++--- tests/testthat/test-as_cff.R | 28 ++++ ...rse_citation.R => test-as_cff_reference.R} | 65 ++++----- tests/testthat/test-cff-methods.R | 2 +- tests/testthat/test-cff.R | 16 --- tests/testthat/test-deprecated.R | 8 ++ tests/testthat/test-merge_desc_cit.R | 2 +- vignettes/cffr.Rmd | 4 +- 40 files changed, 448 insertions(+), 453 deletions(-) rename R/{as_cff_bibentry.R => as_cff_reference.R} (99%) delete mode 100644 R/cff_parse_citation.R delete mode 100644 man/cff_parse_citation.Rd create mode 100644 man/deprecated_cff_bibentry.Rd create mode 100644 tests/testthat/_snaps/as_cff.md rename tests/testthat/_snaps/{cff_parse_citation.md => as_cff_reference.md} (99%) create mode 100644 tests/testthat/test-as_cff.R rename tests/testthat/{test-cff_parse_citation.R => test-as_cff_reference.R} (94%) diff --git a/NAMESPACE b/NAMESPACE index b12413d2..53a933d0 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -2,6 +2,7 @@ S3method(as.data.frame,cff) S3method(as.person,cff) +S3method(as_cff,Bibtex) S3method(as_cff,bibentry) S3method(as_cff,default) S3method(as_cff,list) diff --git a/NEWS.md b/NEWS.md index b8045cb4..8898b58b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -28,9 +28,18 @@ would warn when used, providing advice on the replacement function. - The conversion from `cff` to `bibentry` is performed now by a new function `as_bibentry()`. Previous names of this function were `cff_to_bibtex()` and `cff_extract_to_bibtex()` that are now deprecated. - -#### New capabilities - +- `cff_parse_citation()`: replaced by `as_cff()`, see **New capabilities**. + +### New capabilities + +- New `as_cff()` S3 generic method (replacing `as.cff()`): This method coerces + **R** objects to `cff-class` format. Current methods provided are: + - `as_cff.Bibtex()`. + - `as_cff.bibentry()`, replacing cff_parse_citation(). + - `as_cff.person()`, similar to `as_cff_person()` but only for `person` + objects. We recommend using `as_cff_person()` since it can parse also + string representing authors in BibTeX markup (`"{von Neumen}, James"`), + that can't be captured properly via methods. - Now reading from external files is performed exclusively by `cff_read()` (that is designed to fit all supported file types on a single entry point) and the new specific readers (that are used under the hood by `cff_read()`), @@ -45,14 +54,14 @@ would warn when used, providing advice on the replacement function. - Minimum **R** version required now is **4.0.0**. - Now `class()` of `cff` objects are `c("cff", "list")` instead of single value (`"cff"`). -- New methods added: +- New S3 **base** and **utils** methods added: - `as.data.frame.cff().` - `as.person.cff()`, that provides results **only** for CFF keys defined as [person](https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md#definitionsperson) or [entity](https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md#definitionsentity) - (e.g. `authors`, `contacts`, `editors`, `publisher`). + (e.g. `authors`, `contacts`, `editors`, `publisher,` etc.). - `head.cff()`, `tail.cff()`. - `toBibtex.cff()`. - Update of BibTeX crosswalk (see `vignette("bibtex_cff", package = "cffr")`) diff --git a/R/as_bibentry.R b/R/as_bibentry.R index e12f873b..88dfc462 100644 --- a/R/as_bibentry.R +++ b/R/as_bibentry.R @@ -15,8 +15,8 @@ #' object and performs a mapping of the metadata to BibTeX, according to #' `vignette("bibtex_cff", "cffr")`. #' -#' The inverse transformation (`bibtex` object to `cff` reference) can be done -#' with [cff_parse_citation()]. +#' The inverse transformation (`bibentry` object to `cff` reference) can +#' be done with the corresponding [as_cff()] method. #' #' @seealso #' [utils::bibentry()] diff --git a/R/as_cff.R b/R/as_cff.R index 99a960a6..f7d8db66 100644 --- a/R/as_cff.R +++ b/R/as_cff.R @@ -5,8 +5,9 @@ #' [`cff`][cff-class], a list with class `cff`. #' #' `as_cff` is an S3 generic, with methods for: -#' - `person` objects as produced by `utils::person()`. -#' - `bibentry` objects as produced by `utils::bibentry()`. +#' - `person` objects as produced by [utils::person()]. +#' - `bibentry` objects as produced by [utils::bibentry()]. +#' - `Bibtex` object as produced by [toBibtex()]. #' - Default: Other inputs are first coerced with [as.list()]. #' #' @param x A `person`, `bibentry` or other object that could be coerced to a @@ -14,7 +15,55 @@ #' @param ... Additional arguments to be passed on to other methods. #' #' @rdname as_cff +#' +#' @returns +#' +#' A `cff` object. These objects are rarely [valid][cff_validate()], but can +#' be used to complement or modify complete `cff` objects. +#' +#' @family coercing +#' @family s3method +#' +#' @details +#' For `as_cff.bibentry()` / `as_cff.Bibtex()` see +#' `vignette("bibtex_cff", "cffr")` to understand how the mapping is performed. +#' +#' +#' @seealso +#' - [cff()]: Create a full `cff` object from scratch. +#' - [cff_modify()]: Modify a `cff` object. +#' - [cff_create()]: Create a `cff` object of a **R** package. +#' - [cff_read()]: Create a `cff` object from a external file. +#' #' @export +#' +#' @examples +#' +#' # Convert a list to "cff" object +#' cffobj <- as_cff(list( +#' "cff-version" = "1.2.0", +#' title = "Manipulating files" +#' )) +#' +#' class(cffobj) +#' +#' # Nice display thanks to yaml package +#' cffobj +#' +#' # bibentry method +#' a_cit <- citation("cffr")[[1]] +#' +#' a_cit +#' +#' as_cff(a_cit) +#' +#' # Bibtex method +#' a_bib <- toBibtex(a_cit) +#' +#' a_bib +#' +#' as_cff(a_cit) +#' as_cff <- function(x, ...) { UseMethod("as_cff") } @@ -35,7 +84,8 @@ as_cff.list <- function(x, ...) { new_cff(x_clean) } -#' @rdname as_cff +#' @rdname as_cff_person +#' @order 2 #' @export as_cff.person <- function(x, ...) { as_cff(as_cff_person(x), ...) @@ -45,5 +95,82 @@ as_cff.person <- function(x, ...) { #' @rdname as_cff #' @export as_cff.bibentry <- function(x, ...) { - as_cff(as_cff_bibentry(x), ...) + cff_ref <- as_cff_reference(x) + clean_up <- vapply(cff_ref, is.null, FUN.VALUE = logical(1)) + if (all(clean_up)) { + return(NULL) + } + + as_cff(cff_ref, ...) +} + +#' @rdname as_cff +#' @export +as_cff.Bibtex <- function(x, ...) { + tmp <- tempfile(fileext = ".bib") + writeLines(x, tmp) + abib <- cff_read_bib(tmp) + as_cff(abib, ...) +} + +# nolint start +#' @export +#' @rdname as_cff +#' @usage NULL +as.cff <- function(x) { + as_cff(x) +} +# nolint end + +# Helper---- + +#' Recursively clean lists and assign cff classes +#' to all nested lists +#' +#' +#' @noRd +rapply_cff <- function(x) { + if (inherits(x, "cff")) { + return(x) + } + + if (is.list(x) && length(x) > 0) { + x <- drop_null(x) + x <- lapply(x, rapply_cff) + return(structure(x, class = c("cff", "list"))) + } else { + return(x) + } +} + +# https://adv-r.hadley.nz/s3.html#s3-constructor +# Constructor +new_cff <- function(x) { + if (is_cff(x)) { + class(x) <- c("cff", "list") + return(x) + } + + # Clean all strings recursively + + x <- rapply(x, function(x) { + if (is.list(x) || length(x) > 1) { + return(x) + } + return(clean_str(x)) + }, + how = "list" + ) + + # Remove NULLs + x <- drop_null(x) + + # Remove duplicated names if named + if (!is.null(names(x))) x <- x[!duplicated(names(x))] + + # Now apply cff class to nested lists + x <- lapply(x, rapply_cff) + + class(x) <- c("cff", "list") + x } diff --git a/R/as_cff_bibentry.R b/R/as_cff_reference.R similarity index 99% rename from R/as_cff_bibentry.R rename to R/as_cff_reference.R index f9028a62..e13b0729 100644 --- a/R/as_cff_bibentry.R +++ b/R/as_cff_reference.R @@ -1,7 +1,7 @@ #' Internal functions for `as_cff.bibentry` method #' #' @noRd -as_cff_bibentry <- function(x) { +as_cff_reference <- function(x) { # Need always to be unnamed bibentry bib <- unname(x) @@ -113,9 +113,8 @@ make_cff_reference <- function(bib) { parse_cit <- parse_cit[names(parse_cit) %in% validnames] parse_cit <- drop_null(parse_cit) - parse_cit_result <- as.cff(parse_cit) - return(parse_cit_result) + return(parse_cit) } #' Extract and map BibTeX entry diff --git a/R/cff-methods.R b/R/cff-methods.R index 93607b70..1c02bb78 100644 --- a/R/cff-methods.R +++ b/R/cff-methods.R @@ -46,7 +46,7 @@ as.data.frame.cff <- function(x, row.names = NULL, optional = FALSE, ...) { #' @rdname as_cff_person #' @name as.person.cff -#' @order 2 +#' @order 3 #' #' @description #' @@ -85,7 +85,7 @@ as.person.cff <- function(x) { #' @noRd #' @export head.cff <- function(x, n = 6L, ...) { - as.cff(NextMethod()) + as_cff(NextMethod()) } #' Tail @@ -93,7 +93,7 @@ head.cff <- function(x, n = 6L, ...) { #' @noRd #' @export tail.cff <- function(x, n = 6L, ...) { - as.cff(NextMethod()) + as_cff(NextMethod()) } diff --git a/R/cff.R b/R/cff.R index d061270e..acb0a556 100644 --- a/R/cff.R +++ b/R/cff.R @@ -1,18 +1,19 @@ #' Read and manipulate `cff` objects #' #' A class and utility methods for reading, creating and holding CFF -#' information. +#' information. See [`cff-class`] to learn more about `cff` objects. #' #' @rdname cff #' @name cff +#' @aliases cff_modify #' @return -#' A `cff` object. Under the hood, a `cff` object is a regular [`list`] object +#' A `cff` object. Under the hood, a `cff` object is a regular `list` object #' with a special [print()] method. #' #' @family core #' #' @param path The path to a `CITATION.cff` file. -#' @param ... Named arguments to be used for creating a [`cff`] object. See +#' @param ... Named arguments to be used for creating a `cff` object. See #' **Details**. #' #' @details @@ -96,111 +97,7 @@ cff <- function(path, ...) { } cffobj <- drop_null(cffobj) - cffobj <- as.cff(cffobj) + cffobj <- as_cff(cffobj) return(cffobj) } - - - - -#' @rdname cff -#' -#' @param x a character string for the [`as.cff`] default method -#' -#' @export -#' -#' @examples -#' -#' -#' # Convert a list to "cff" object -#' cffobj <- as.cff(list( -#' "cff-version" = "1.2.0", -#' title = "Manipulating files" -#' )) -#' -#' class(cffobj) -#' -#' # Nice display thanks to yaml package -#' cffobj -as.cff <- function(x) { - if (is_cff(x)) { - return(x) - } - - # Clean all strings recursively - - x <- rapply(x, function(x) { - if (is.list(x) || length(x) > 1) { - return(x) - } - return(clean_str(x)) - }, - how = "list" - ) - - # Remove NULLs - x <- drop_null(x) - - # Remove duplicated names - x <- x[!duplicated(names(x))] - - # Now apply cff class to nested lists - x <- lapply(x, rapply_cff) - - class(x) <- "cff" - x -} - -# Helper---- - -#' Recursively clean lists and assign cff classes -#' to all nested lists -#' -#' -#' @noRd -rapply_cff <- function(x) { - if (inherits(x, "cff")) { - return(x) - } - - if (is.list(x) && length(x) > 0) { - x <- drop_null(x) - x <- lapply(x, rapply_cff) - return(structure(x, class = c("cff", "list"))) - } else { - return(x) - } -} - -# https://adv-r.hadley.nz/s3.html#s3-constructor -# Constructor -new_cff <- function(x) { - if (is_cff(x)) { - class(x) <- c("cff", "list") - return(x) - } - - # Clean all strings recursively - - x <- rapply(x, function(x) { - if (is.list(x) || length(x) > 1) { - return(x) - } - return(clean_str(x)) - }, - how = "list" - ) - - # Remove NULLs - x <- drop_null(x) - - # Remove duplicated names if named - if (!is.null(names(x))) x <- x[!duplicated(names(x))] - - # Now apply cff class to nested lists - x <- lapply(x, rapply_cff) - - class(x) <- c("cff", "list") - x -} diff --git a/R/cff_create.R b/R/cff_create.R index 785ae763..5003fab4 100644 --- a/R/cff_create.R +++ b/R/cff_create.R @@ -146,7 +146,7 @@ cff_create <- function(x, keys = list(), cff_version = "1.2.0", keys <- fuzzy_keys(keys) cffobjendmod <- cffobjend[setdiff(names(cffobjend), names(keys))] cffobjend <- modifyList(cffobjendmod, keys, keep.null = FALSE) - cffobjend <- as.cff(cffobjend) + cffobjend <- as_cff(cffobjend) } @@ -157,7 +157,7 @@ cff_create <- function(x, keys = list(), cff_version = "1.2.0", if (!is.null(cffobjend$`preferred-citation`)) { cffobjend$`preferred-citation`$authors <- enhance_pref_authors(cffobjend) } - cffobjend <- as.cff(cffobjend) + cffobjend <- as_cff(cffobjend) cffobjend } diff --git a/R/cff_parse_citation.R b/R/cff_parse_citation.R deleted file mode 100644 index b6056b13..00000000 --- a/R/cff_parse_citation.R +++ /dev/null @@ -1,91 +0,0 @@ -#' Parse a `bibentry` to `cff` -#' -#' Parse a `bibentry` object to a valid format for a `CITATION.cff` file. -#' -#' @seealso [cff_create()], `vignette("bibtex_cff", "cffr")`, [bibentry()] -#' -#' @export -#' -#' @family coercing -#' -#' @param bib A `bibentry` object, either created with [bibentry()] -#' (preferred) or [citEntry()]. -#' -#' @return A list [`cff`] object ready to be used on [cff_create()]. -#' -#' @details -#' This is a helper function designed to help on adding or -#' replacing the auto-generated authors of the package. See **Examples**. -#' -#' This function tries to adapt a `bibentry` object (generated with [bibentry()] -#' or [citEntry()]) to the CFF standard. -#' -#' ## Entry types considered -#' -#' - **Article**, **Book**, **Booklet**, **InBook**, **InCollection**, -#' **InProceedings**, **Manual**, **MastersThesis**, **Misc**, **PhDThesis**, -#' **Proceedings**, **TechReport**, **Unpublished**. See [bibentry()] -#' for more information. -#' -#' Note that **Conference** is not implemented in -#' [bibentry()], however is equivalent to **InProceedings** (Patashnik (1988)). -#' -#' ## Fields considered -#' -#' - **address**, **author**, **booktitle**, **chapter**, **edition**, -#' **editor**, **howpublished**, **institution**, **journal**, **key**, -#' **month**, **note**, **number**, **organization**, **pages**, -#' **publisher**, **school**, **series**, **title**, **type**, **year**. -#' -#' **annote** and **crossref** fields are ignored. -#' -#' -#' @references -#' - Patashnik, Oren. "BIBTEXTING" February 1988. -#' . -#' -#' - Haines, R., & The Ruby Citation File Format Developers. (2021). -#' *Ruby CFF Library (Version 0.9.0)* (Computer software). -#' \doi{10.5281/zenodo.1184077}. -#' -#' @examples -#' \donttest{ -#' bib <- citation("base") -#' bib -#' -#' -#' # To cff -#' bib_to_cff <- cff_parse_citation(bib) -#' bib_to_cff -#' -#' # Create the object -#' new_cff <- cff() -#' -#' full <- cff_create(new_cff, keys = list("preferred-citation" = bib_to_cff)) -#' -#' full -#' # Validate -#' cff_validate(full) -#' -#' # Several citations -#' -#' cff_parse_citation(citation("rmarkdown")) -#' } -cff_parse_citation <- function(bib) { - if (!inherits(bib, "bibentry")) { - return(NULL) - } - - ## Unname - bib <- unname(bib) - - the_obj <- lapply(bib, make_cff_reference) - clean_up <- vapply(the_obj, is.null, FUN.VALUE = logical(1)) - - # No results - if (all(clean_up)) { - return(NULL) - } - the_obj <- the_obj[!clean_up] - new_cff(the_obj) -} diff --git a/R/cff_read.R b/R/cff_read.R index c5f3f55a..e360b371 100644 --- a/R/cff_read.R +++ b/R/cff_read.R @@ -256,9 +256,9 @@ cff_read_citation <- function(path, meta = NULL, ...) { } # nocov end } - tocff <- cff_parse_citation(the_cit) - tocff <- new_cff(tocff) - unname(tocff) + tocff <- as_cff(the_cit) + + tocff } #' @export @@ -288,9 +288,8 @@ cff_read_bib <- function(path, encoding = "UTF-8", ...) { read_bib <- bibtex::read.bib(file = path, encoding = encoding, ...) - tocff <- cff_parse_citation(read_bib) - tocff <- new_cff(tocff) - unname(tocff) + tocff <- as_cff(read_bib) + tocff } # Internal safe ---- @@ -312,8 +311,8 @@ cff_safe_read_citation <- function(desc_path, cit_path) { } # Need to be named here - tocff <- cff_parse_citation(the_cit) - tocff <- new_cff(tocff) + tocff <- as_cff(the_cit) + tocff } # Helpers ---- diff --git a/R/cff_read_bib_text.R b/R/cff_read_bib_text.R index e8bb75d7..05447461 100644 --- a/R/cff_read_bib_text.R +++ b/R/cff_read_bib_text.R @@ -80,7 +80,7 @@ cff_read_bib_text <- function(x, encoding = "UTF-8", ...) { # Write x to a tempfile file <- tempfile(fileext = ".bib") writeLines(x, file) - the_cff <- cff_read_bib(file, encoding = encoding, ...) + unlink(file) the_cff } diff --git a/R/deprecated.R b/R/deprecated.R index f81a1028..1120e54b 100644 --- a/R/deprecated.R +++ b/R/deprecated.R @@ -186,7 +186,7 @@ write_citation <- function(x, } -#' Previous API: Parse a person to [`cff`][cff-class] +#' Previous API: Parse a `person` to [`cff`][cff-class] #' #' @description #' @@ -261,3 +261,38 @@ cff_parse_person_bibtex <- function(person) { } as_cff_person(person) } + +#' Previous API: Parse a `bibentry` to `cff` +#' +#' @description +#' +#' `r lifecycle::badge('deprecated')` Please use [as_cff.bibentry()] method +#' +#' @rdname deprecated_cff_bibentry +#' @export +#' @keywords internal +#' @family deprecated +#' +#' @param bib A `bibentry` object. +#' @return A `bibentry` in format `cff`. +#' +#' @seealso [as_cff.bibentry()] +#' +#' @examples +#' +#' bib <- citation("base") +#' bib +#' +#' +#' # To cff +#' bib_to_cff <- as_cff(bib) +#' bib_to_cff +#' +cff_parse_citation <- function(bib) { + if (requireNamespace("lifecycle", quietly = TRUE)) { + lifecycle::deprecate_soft( + "1.0.0", "cff_parse_citation()", "as_cff.bibentry()" + ) + } + as_cff(bib) +} diff --git a/R/utils-create.R b/R/utils-create.R index 2f1040e6..951dbb11 100644 --- a/R/utils-create.R +++ b/R/utils-create.R @@ -117,10 +117,10 @@ parse_dependencies <- function(desc_path, n <- av_deps[y, ] if (n$package == "R") { - mod <- cff_parse_citation(citation())[[1]] + mod <- as_cff(citation())[[1]] mod$year <- format(Sys.Date(), "%Y") } else { - mod <- try(cff_parse_citation(citation(n$package, auto = TRUE)[1])[[1]], + mod <- try(as_cff(citation(n$package, auto = TRUE)[1])[[1]], silent = TRUE ) @@ -172,7 +172,7 @@ parse_dependencies <- function(desc_path, )] ) - mod <- as.cff(mod) + mod <- as_cff(mod) }) cff_deps <- drop_null(cff_deps) diff --git a/README.md b/README.md index 255fe1ff..74cf4226 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ file and the `CITATION` file (if present) of your package. Note that **cffr** works best if your package pass `R CMD check/devtools::check()`. -As per 2024-03-01 there are at least 306 repos on GitHub using **cffr**. +As per 2024-03-02 there are at least 306 repos on GitHub using **cffr**. [Check them out here](https://github.com/search?q=cffr%20path%3A**%2FCITATION.cff&type=code). diff --git a/codemeta.json b/codemeta.json index 1df75929..44a5fd98 100644 --- a/codemeta.json +++ b/codemeta.json @@ -200,7 +200,7 @@ }, "isPartOf": "https://ropensci.org", "keywords": ["attribution", "citation", "credit", "citation-files", "cff", "metadata", "r", "r-package", "citation-file-format", "rstats", "ropensci", "cran"], - "fileSize": "947.576KB", + "fileSize": "947.529KB", "citation": [ { "@type": "ScholarlyArticle", diff --git a/man/as_bibentry.Rd b/man/as_bibentry.Rd index 0c9e7019..d3959205 100644 --- a/man/as_bibentry.Rd +++ b/man/as_bibentry.Rd @@ -51,8 +51,8 @@ The function tries to map the information of the source \code{x} into a \code{cf object and performs a mapping of the metadata to BibTeX, according to \code{vignette("bibtex_cff", "cffr")}. -The inverse transformation (\code{bibtex} object to \code{cff} reference) can be done -with \code{\link[=cff_parse_citation]{cff_parse_citation()}}. +The inverse transformation (\code{bibentry} object to \code{cff} reference) can +be done with the corresponding \code{\link[=as_cff]{as_cff()}} method. Additionally, it is also provided a method for \code{\link[=toBibtex]{toBibtex()}}, that can convert \code{\link[=cff-class]{cff}} objects to \code{Bibtex} objects as provided by @@ -126,10 +126,11 @@ Other functions for working with BibTeX format: \code{\link{encoded_utf_to_latex}()} Other functions for converting between \strong{R} classes: -\code{\link{as_cff_person}()}, -\code{\link{cff_parse_citation}()} +\code{\link{as_cff}()}, +\code{\link{as_cff_person}()} Other S3 Methods for \code{cff}: +\code{\link{as_cff}()}, \code{\link{as_cff_person}()}, \code{\link{cff-class}} } diff --git a/man/as_cff.Rd b/man/as_cff.Rd index dc3185d0..1b523a9e 100644 --- a/man/as_cff.Rd +++ b/man/as_cff.Rd @@ -4,8 +4,9 @@ \alias{as_cff} \alias{as_cff.default} \alias{as_cff.list} -\alias{as_cff.person} \alias{as_cff.bibentry} +\alias{as_cff.Bibtex} +\alias{as.cff} \title{Coerce lists, \code{person} and \code{bibentry} objects to \code{\link[=cff-class]{cff}}} \usage{ as_cff(x, ...) @@ -14,9 +15,9 @@ as_cff(x, ...) \method{as_cff}{list}(x, ...) -\method{as_cff}{person}(x, ...) - \method{as_cff}{bibentry}(x, ...) + +\method{as_cff}{Bibtex}(x, ...) } \arguments{ \item{x}{A \code{person}, \code{bibentry} or other object that could be coerced to a @@ -24,14 +25,70 @@ list.} \item{...}{Additional arguments to be passed on to other methods.} } +\value{ +A \code{cff} object. These objects are rarely \link[=cff_validate]{valid}, but can +be used to complement or modify complete \code{cff} objects. +} \description{ \code{as_cff()} turns an existing list-like \strong{R} object into a so-called \code{\link[=cff-class]{cff}}, a list with class \code{cff}. \code{as_cff} is an S3 generic, with methods for: \itemize{ -\item \code{person} objects as produced by \code{utils::person()}. -\item \code{bibentry} objects as produced by \code{utils::bibentry()}. +\item \code{person} objects as produced by \code{\link[utils:person]{utils::person()}}. +\item \code{bibentry} objects as produced by \code{\link[utils:bibentry]{utils::bibentry()}}. +\item \code{Bibtex} object as produced by \code{\link[=toBibtex]{toBibtex()}}. \item Default: Other inputs are first coerced with \code{\link[=as.list]{as.list()}}. } } +\details{ +For \code{as_cff.bibentry()} / \code{as_cff.Bibtex()} see +\code{vignette("bibtex_cff", "cffr")} to understand how the mapping is performed. +} +\examples{ + +# Convert a list to "cff" object +cffobj <- as_cff(list( + "cff-version" = "1.2.0", + title = "Manipulating files" +)) + +class(cffobj) + +# Nice display thanks to yaml package +cffobj + +# bibentry method +a_cit <- citation("cffr")[[1]] + +a_cit + +as_cff(a_cit) + +# Bibtex method +a_bib <- toBibtex(a_cit) + +a_bib + +as_cff(a_cit) + +} +\seealso{ +\itemize{ +\item \code{\link[=cff]{cff()}}: Create a full \code{cff} object from scratch. +\item \code{\link[=cff_modify]{cff_modify()}}: Modify a \code{cff} object. +\item \code{\link[=cff_create]{cff_create()}}: Create a \code{cff} object of a \strong{R} package. +\item \code{\link[=cff_read]{cff_read()}}: Create a \code{cff} object from a external file. +} + +Other functions for converting between \strong{R} classes: +\code{\link{as_bibentry}()}, +\code{\link{as_cff_person}()} + +Other S3 Methods for \code{cff}: +\code{\link{as_bibentry}()}, +\code{\link{as_cff_person}()}, +\code{\link{cff-class}} +} +\concept{coercing} +\concept{s3method} diff --git a/man/as_cff_person.Rd b/man/as_cff_person.Rd index a4d8e05a..0f4f53bf 100644 --- a/man/as_cff_person.Rd +++ b/man/as_cff_person.Rd @@ -1,12 +1,15 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/as_cff_person.R, R/cff-methods.R +% Please edit documentation in R/as_cff_person.R, R/as_cff.R, R/cff-methods.R \name{as_cff_person} \alias{as_cff_person} +\alias{as_cff.person} \alias{as.person.cff} \title{Create a person with the corresponding \code{\link[=cff-class]{cff}} structure} \usage{ as_cff_person(person) +\method{as_cff}{person}(x, ...) + \method{as.person}{cff}(x) } \arguments{ @@ -140,10 +143,11 @@ Examples in \code{vignette("cffr", "cffr")} and \code{\link[utils:person]{utils: Other functions for converting between \strong{R} classes: \code{\link{as_bibentry}()}, -\code{\link{cff_parse_citation}()} +\code{\link{as_cff}()} Other S3 Methods for \code{cff}: \code{\link{as_bibentry}()}, +\code{\link{as_cff}()}, \code{\link{cff-class}} } \concept{coercing} diff --git a/man/cff-class.Rd b/man/cff-class.Rd index 1d886499..82cba487 100644 --- a/man/cff-class.Rd +++ b/man/cff-class.Rd @@ -10,8 +10,8 @@ \strong{cffr} implements a S3 object with \code{\link[=class]{class()}} \code{cff}, that it is used to represent the information of a \verb{*.cff} file in \strong{R}. -Under the hood, a \code{cff} object is simply a named \code{\link[=list]{list()}} to which we added -additional methods, most notably \code{\link[=print]{print()}}. +Under the hood, a \code{cff} object is simply a named \code{\link{list}} to which we added +additional methods, most notably \code{\link[=print]{print()}} and \code{\link[=as_cff]{as_cff()}}. \if{html}{\out{
}}\preformatted{a_named_list <- list( first = "I", second = "am", third = "a", fourth = "list", @@ -47,10 +47,10 @@ a_named_list #> NULL # But -a_cff_object <- as.cff(a_named_list) +a_cff_object <- as_cff(a_named_list) class(a_cff_object) -#> [1] "cff" +#> [1] "cff" "list" a_cff_object #> first: I @@ -62,10 +62,11 @@ a_cff_object dput(a_cff_object) #> structure(list(first = "I", second = "am", third = "a", fourth = "list", -#> fifth = "with", sixth = "names"), class = "cff") +#> fifth = "with", sixth = "names"), class = c("cff", "list" +#> )) }\if{html}{\out{
}} -\code{\link[=as.cff]{as.cff()}} not only converts a \code{\link[=list]{list()}} to \code{cff} but also removes items (known +\code{\link[=as_cff]{as_cff()}} not only converts a \code{list} to \code{cff} but also removes items (known as \code{keys} in CFF terminology) that are \code{NULL} or \code{NA}. } @@ -183,6 +184,7 @@ as.person(the_cff$authors) Other S3 Methods for \code{cff}: \code{\link{as_bibentry}()}, +\code{\link{as_cff}()}, \code{\link{as_cff_person}()} } \concept{s3method} diff --git a/man/cff.Rd b/man/cff.Rd index b835c587..8b604fe9 100644 --- a/man/cff.Rd +++ b/man/cff.Rd @@ -2,28 +2,24 @@ % Please edit documentation in R/cff.R \name{cff} \alias{cff} -\alias{as.cff} +\alias{cff_modify} \title{Read and manipulate \code{cff} objects} \usage{ cff(path, ...) - -as.cff(x) } \arguments{ \item{path}{The path to a \code{CITATION.cff} file.} -\item{...}{Named arguments to be used for creating a \code{\link{cff}} object. See +\item{...}{Named arguments to be used for creating a \code{cff} object. See \strong{Details}.} - -\item{x}{a character string for the \code{\link{as.cff}} default method} } \value{ -A \code{cff} object. Under the hood, a \code{cff} object is a regular \code{\link{list}} object +A \code{cff} object. Under the hood, a \code{cff} object is a regular \code{list} object with a special \code{\link[=print]{print()}} method. } \description{ A class and utility methods for reading, creating and holding CFF -information. +information. See \code{\linkS4class{cff}} to learn more about \code{cff} objects. } \details{ This object can be manipulated using \code{\link[=cff_create]{cff_create()}}. @@ -90,18 +86,6 @@ new # Would pass cff_validate(new) } - - -# Convert a list to "cff" object -cffobj <- as.cff(list( - "cff-version" = "1.2.0", - title = "Manipulating files" -)) - -class(cffobj) - -# Nice display thanks to yaml package -cffobj } \seealso{ Other core functions of \CRANpkg{cffr}: diff --git a/man/cff_parse_citation.Rd b/man/cff_parse_citation.Rd deleted file mode 100644 index 97b9f323..00000000 --- a/man/cff_parse_citation.Rd +++ /dev/null @@ -1,88 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/cff_parse_citation.R -\name{cff_parse_citation} -\alias{cff_parse_citation} -\title{Parse a \code{bibentry} to \code{cff}} -\usage{ -cff_parse_citation(bib) -} -\arguments{ -\item{bib}{A \code{bibentry} object, either created with \code{\link[=bibentry]{bibentry()}} -(preferred) or \code{\link[=citEntry]{citEntry()}}.} -} -\value{ -A list \code{\link{cff}} object ready to be used on \code{\link[=cff_create]{cff_create()}}. -} -\description{ -Parse a \code{bibentry} object to a valid format for a \code{CITATION.cff} file. -} -\details{ -This is a helper function designed to help on adding or -replacing the auto-generated authors of the package. See \strong{Examples}. - -This function tries to adapt a \code{bibentry} object (generated with \code{\link[=bibentry]{bibentry()}} -or \code{\link[=citEntry]{citEntry()}}) to the CFF standard. -\subsection{Entry types considered}{ -\itemize{ -\item \strong{Article}, \strong{Book}, \strong{Booklet}, \strong{InBook}, \strong{InCollection}, -\strong{InProceedings}, \strong{Manual}, \strong{MastersThesis}, \strong{Misc}, \strong{PhDThesis}, -\strong{Proceedings}, \strong{TechReport}, \strong{Unpublished}. See \code{\link[=bibentry]{bibentry()}} -for more information. -} - -Note that \strong{Conference} is not implemented in -\code{\link[=bibentry]{bibentry()}}, however is equivalent to \strong{InProceedings} (Patashnik (1988)). -} - -\subsection{Fields considered}{ -\itemize{ -\item \strong{address}, \strong{author}, \strong{booktitle}, \strong{chapter}, \strong{edition}, -\strong{editor}, \strong{howpublished}, \strong{institution}, \strong{journal}, \strong{key}, -\strong{month}, \strong{note}, \strong{number}, \strong{organization}, \strong{pages}, -\strong{publisher}, \strong{school}, \strong{series}, \strong{title}, \strong{type}, \strong{year}. -} - -\strong{annote} and \strong{crossref} fields are ignored. -} -} -\examples{ -\donttest{ -bib <- citation("base") -bib - - -# To cff -bib_to_cff <- cff_parse_citation(bib) -bib_to_cff - -# Create the object -new_cff <- cff() - -full <- cff_create(new_cff, keys = list("preferred-citation" = bib_to_cff)) - -full -# Validate -cff_validate(full) - -# Several citations - -cff_parse_citation(citation("rmarkdown")) -} -} -\references{ -\itemize{ -\item Patashnik, Oren. "BIBTEXTING" February 1988. -\url{https://osl.ugr.es/CTAN/biblio/bibtex/base/btxdoc.pdf}. -\item Haines, R., & The Ruby Citation File Format Developers. (2021). -\emph{Ruby CFF Library (Version 0.9.0)} (Computer software). -\doi{10.5281/zenodo.1184077}. -} -} -\seealso{ -\code{\link[=cff_create]{cff_create()}}, \code{vignette("bibtex_cff", "cffr")}, \code{\link[=bibentry]{bibentry()}} - -Other functions for converting between \strong{R} classes: -\code{\link{as_bibentry}()}, -\code{\link{as_cff_person}()} -} -\concept{coercing} diff --git a/man/chunks/cffclass.Rmd b/man/chunks/cffclass.Rmd index 6edf794b..0c549fc6 100644 --- a/man/chunks/cffclass.Rmd +++ b/man/chunks/cffclass.Rmd @@ -3,8 +3,8 @@ **cffr** implements a S3 object with [class()] `cff`, that it is used to represent the information of a `*.cff` file in **R**. -Under the hood, a `cff` object is simply a named [list()] to which we added -additional methods, most notably [print()]. +Under the hood, a `cff` object is simply a named [`list`] to which we added +additional methods, most notably [print()] and [as_cff()]. ```{r include=FALSE} library(cffr) @@ -23,7 +23,7 @@ dput(a_named_list) a_named_list # But -a_cff_object <- as.cff(a_named_list) +a_cff_object <- as_cff(a_named_list) class(a_cff_object) @@ -32,7 +32,7 @@ a_cff_object dput(a_cff_object) ``` -[as.cff()] not only converts a [list()] to `cff` but also removes items (known +[as_cff()] not only converts a `list` to `cff` but also removes items (known as `keys` in CFF terminology) that are `NULL` or `NA`. ## Valid `cff` objects diff --git a/man/deprecated_cff_bibentry.Rd b/man/deprecated_cff_bibentry.Rd new file mode 100644 index 00000000..543209c7 --- /dev/null +++ b/man/deprecated_cff_bibentry.Rd @@ -0,0 +1,39 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/deprecated.R +\name{cff_parse_citation} +\alias{cff_parse_citation} +\title{Previous API: Parse a \code{bibentry} to \code{cff}} +\usage{ +cff_parse_citation(bib) +} +\arguments{ +\item{bib}{A \code{bibentry} object.} +} +\value{ +A \code{bibentry} in format \code{cff}. +} +\description{ +\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Please use \code{\link[=as_cff.bibentry]{as_cff.bibentry()}} method +} +\examples{ + +bib <- citation("base") +bib + + +# To cff +bib_to_cff <- as_cff(bib) +bib_to_cff + +} +\seealso{ +\code{\link[=as_cff.bibentry]{as_cff.bibentry()}} + +Other deprecated functions: +\code{\link{cff_extract_to_bibtex}()}, +\code{\link{cff_from_bibtex}()}, +\code{\link{cff_parse_person}()}, +\code{\link{write_bib}()} +} +\concept{deprecated} +\keyword{internal} diff --git a/man/deprecated_cff_from_bib.Rd b/man/deprecated_cff_from_bib.Rd index 20e1e261..7a95e177 100644 --- a/man/deprecated_cff_from_bib.Rd +++ b/man/deprecated_cff_from_bib.Rd @@ -58,6 +58,7 @@ if (requireNamespace("bibtex", quietly = TRUE)) { \seealso{ Other deprecated functions: \code{\link{cff_extract_to_bibtex}()}, +\code{\link{cff_parse_citation}()}, \code{\link{cff_parse_person}()}, \code{\link{write_bib}()} } diff --git a/man/deprecated_cff_person.Rd b/man/deprecated_cff_person.Rd index 3a0b3857..1cae6865 100644 --- a/man/deprecated_cff_person.Rd +++ b/man/deprecated_cff_person.Rd @@ -3,7 +3,7 @@ \name{cff_parse_person} \alias{cff_parse_person} \alias{cff_parse_person_bibtex} -\title{Previous API: Parse a person to \code{\link[=cff-class]{cff}}} +\title{Previous API: Parse a \code{person} to \code{\link[=cff-class]{cff}}} \usage{ cff_parse_person(person) @@ -69,6 +69,7 @@ as_cff_person("Herbert von Karajan") Other deprecated functions: \code{\link{cff_extract_to_bibtex}()}, \code{\link{cff_from_bibtex}()}, +\code{\link{cff_parse_citation}()}, \code{\link{write_bib}()} } \concept{deprecated} diff --git a/man/deprecated_cff_to_bib.Rd b/man/deprecated_cff_to_bib.Rd index cf7a0221..3d6cca79 100644 --- a/man/deprecated_cff_to_bib.Rd +++ b/man/deprecated_cff_to_bib.Rd @@ -50,6 +50,7 @@ bib <- as_bibentry(cff_object) \seealso{ Other deprecated functions: \code{\link{cff_from_bibtex}()}, +\code{\link{cff_parse_citation}()}, \code{\link{cff_parse_person}()}, \code{\link{write_bib}()} } diff --git a/man/deprecated_write.Rd b/man/deprecated_write.Rd index 90efe6a6..1c3a7347 100644 --- a/man/deprecated_write.Rd +++ b/man/deprecated_write.Rd @@ -60,6 +60,7 @@ cat(readLines(my_temp_bib), sep = "\n") Other deprecated functions: \code{\link{cff_extract_to_bibtex}()}, \code{\link{cff_from_bibtex}()}, +\code{\link{cff_parse_citation}()}, \code{\link{cff_parse_person}()} } \concept{deprecated} diff --git a/pkgdown/_pkgdown.yml b/pkgdown/_pkgdown.yml index dca1dbd3..ca293aec 100644 --- a/pkgdown/_pkgdown.yml +++ b/pkgdown/_pkgdown.yml @@ -9,9 +9,6 @@ template: repo: branch: main -development: - mode: auto - home: title: cffr | Generate Citation File Format (CFF) files for R packages description: >- diff --git a/tests/testthat/_snaps/as_cff.md b/tests/testthat/_snaps/as_cff.md new file mode 100644 index 00000000..5cc45245 --- /dev/null +++ b/tests/testthat/_snaps/as_cff.md @@ -0,0 +1,8 @@ +# as.cff still works + + Code + l2 + Output + cff-version: 1.2.0 + title: Manipulating files + diff --git a/tests/testthat/_snaps/cff_parse_citation.md b/tests/testthat/_snaps/as_cff_reference.md similarity index 99% rename from tests/testthat/_snaps/cff_parse_citation.md rename to tests/testthat/_snaps/as_cff_reference.md index 430c42f4..909d91e2 100644 --- a/tests/testthat/_snaps/cff_parse_citation.md +++ b/tests/testthat/_snaps/as_cff_reference.md @@ -696,13 +696,6 @@ # Skip misc without title - Code - bibparsed <- cff_parse_citation(bib) - Message - ! Entry "SHERPA/RoMEO (2018). ." without title. Skipping - ---- - Code cffobj Output diff --git a/tests/testthat/_snaps/cff_read_bib_text.md b/tests/testthat/_snaps/cff_read_bib_text.md index 8bfc4150..44a59827 100644 --- a/tests/testthat/_snaps/cff_read_bib_text.md +++ b/tests/testthat/_snaps/cff_read_bib_text.md @@ -4,7 +4,7 @@ cff_read_bib_text(a_cff) Condition Error in `cff_read_bib_text()`: - ! `x` should be a , not a . + ! `x` should be a , not a . --- diff --git a/tests/testthat/_snaps/deprecated.md b/tests/testthat/_snaps/deprecated.md index af075c14..225a91d2 100644 --- a/tests/testthat/_snaps/deprecated.md +++ b/tests/testthat/_snaps/deprecated.md @@ -91,3 +91,12 @@ `cff_parse_person_bibtex()` was deprecated in cffr 1.0.0. i Please use `as_cff_person()` instead. +# cff_parse_citation + + Code + pend <- cff_parse_citation(p) + Condition + Warning: + `cff_parse_citation()` was deprecated in cffr 1.0.0. + i Please use `as_cff.bibentry()` instead. + diff --git a/tests/testthat/test-as_bibentry.R b/tests/testthat/test-as_bibentry.R index a38c9169..1d7efcd5 100644 --- a/tests/testthat/test-as_bibentry.R +++ b/tests/testthat/test-as_bibentry.R @@ -13,7 +13,7 @@ test_that("Article to bibtex", { keywords = "Some, simple, keywords" ) expect_snapshot(toBibtex(bib)) - x <- cff_parse_citation(bib) + x <- as_cff(bib) bib <- as_bibentry(x) expect_snapshot(toBibtex(bib)) }) @@ -41,7 +41,7 @@ test_that("Book to bibtex", { ) expect_snapshot(toBibtex(bib)) - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) bib <- as_bibentry(bibparsed) expect_snapshot(toBibtex(bib)) }) @@ -62,7 +62,7 @@ test_that("Booklet to bibtex", { ) expect_snapshot(toBibtex(bib)) - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) bib <- as_bibentry(bibparsed) expect_snapshot(toBibtex(bib)) }) @@ -90,7 +90,7 @@ test_that("InBook to bibtex with pages", { ) expect_snapshot(toBibtex(bib)) - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) bib <- as_bibentry(bibparsed) expect_snapshot(toBibtex(bib)) }) @@ -109,7 +109,7 @@ test_that("InCollection to bibtex", { ) expect_snapshot(toBibtex(bib)) - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) bib <- as_bibentry(bibparsed) expect_snapshot(toBibtex(bib)) }) @@ -134,7 +134,7 @@ test_that("InProceedings to bibtex", { ) expect_snapshot(toBibtex(bib)) - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) bib <- as_bibentry(bibparsed) expect_snapshot(toBibtex(bib)) }) @@ -152,7 +152,7 @@ test_that("Manual to bibtex", { ) expect_snapshot(toBibtex(bib)) - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) bib <- as_bibentry(bibparsed) expect_snapshot(toBibtex(bib)) }) @@ -171,7 +171,7 @@ test_that("MastersThesis to bibtex", { ) expect_snapshot(toBibtex(bib)) - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) bib <- as_bibentry(bibparsed) expect_snapshot(toBibtex(bib)) }) @@ -188,7 +188,7 @@ test_that("PhdThesis to bibtex", { ) expect_snapshot(toBibtex(bib)) - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) bib <- as_bibentry(bibparsed) expect_snapshot(toBibtex(bib)) }) @@ -207,7 +207,7 @@ test_that("Proceedings to bibtex", { ) expect_snapshot(toBibtex(bib)) - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) bib <- as_bibentry(bibparsed) expect_snapshot(toBibtex(bib)) }) @@ -223,7 +223,7 @@ test_that("TechReport to bibtex", { ) expect_snapshot(toBibtex(bib)) - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) bib <- as_bibentry(bibparsed) expect_snapshot(toBibtex(bib)) }) @@ -241,7 +241,7 @@ test_that("Unpublished to bibtex", { ) expect_snapshot(toBibtex(bib)) - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) bib <- as_bibentry(bibparsed) expect_snapshot(toBibtex(bib)) }) @@ -256,7 +256,7 @@ test_that("particle names", { ) - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) bibparsed[[1]]$authors <- as_cff_person( "van Leunen, Mary-Claire and Davis, Jr., Sammy" ) @@ -283,7 +283,7 @@ test_that("From plain cff with a citation", { month = 3 ) - s$`preferred-citation` <- cff_parse_citation(acit)[[1]] + s$`preferred-citation` <- as_cff(acit)[[1]] s$`preferred-citation`$editors <- as_cff_person("A name") bib <- as_bibentry(s) @@ -316,7 +316,7 @@ test_that("Test anonymous", { ) - expect_silent(back <- as_bibentry(cff_parse_citation(bib))) + expect_silent(back <- as_bibentry(as_cff(bib))) expect_snapshot(toBibtex(back)) @@ -325,7 +325,7 @@ test_that("Test anonymous", { ) - expect_silent(back <- as_bibentry(cff_parse_citation(bib))) + expect_silent(back <- as_bibentry(as_cff(bib))) expect_snapshot(toBibtex(back)) bib <- bibentry("misc", @@ -333,7 +333,7 @@ test_that("Test anonymous", { ) - expect_silent(back <- as_bibentry(cff_parse_citation(bib))) + expect_silent(back <- as_bibentry(as_cff(bib))) expect_snapshot(toBibtex(back)) bib <- bibentry("proceedings", @@ -342,7 +342,7 @@ test_that("Test anonymous", { ) - expect_silent(back <- as_bibentry(cff_parse_citation(bib))) + expect_silent(back <- as_bibentry(as_cff(bib))) expect_snapshot(toBibtex(back)) }) @@ -356,7 +356,7 @@ test_that("Fallback month", { ) expect_snapshot(toBibtex(bib)) - x <- cff_parse_citation(bib) + x <- as_cff(bib) # Delete here the month x$month <- NULL @@ -392,7 +392,7 @@ test_that("Test BibLateX entry", { url = "http://www.ctan.org" ) expect_snapshot(toBibtex(bib)) - x <- cff_parse_citation(bib) + x <- as_cff(bib) parsed <- as_bibentry(x) @@ -478,7 +478,7 @@ test_that("Corrupt entry", { month = "January", keywords = "Some, simple, keywords" ) - x <- cff_parse_citation(bib)[[1]] + x <- as_cff(bib)[[1]] x$year <- NULL x$journal <- NULL expect_snapshot(n <- as_bibentry(x)) diff --git a/tests/testthat/test-as_cff.R b/tests/testthat/test-as_cff.R new file mode 100644 index 00000000..cfa689a5 --- /dev/null +++ b/tests/testthat/test-as_cff.R @@ -0,0 +1,28 @@ +test_that("as.cff still works", { + l <- list( + "cff-version" = "1.2.0", + title = "Manipulating files" + ) + + expect_silent(l1 <- as_cff(l)) + expect_silent(l2 <- as.cff(l)) + expect_s3_class(l1, c("cff", "list")) + + expect_snapshot(l2) +}) + + +test_that("Other convertes", { + a <- cff() + expect_s3_class(a, "cff") + a <- cff(a) + expect_s3_class(a, "cff") + a <- as_cff(a) + expect_true(is_cff(a)) + expect_s3_class(a, "cff") + + expect_message(noadd <- cff(address = "New York", version = 5)) + expect_true(is_cff(noadd)) + expect_false(is_cff(list(a = 1, b = 2))) + expect_true(is_cff(as_cff(list(a = 1, b = 2)))) +}) diff --git a/tests/testthat/test-cff_parse_citation.R b/tests/testthat/test-as_cff_reference.R similarity index 94% rename from tests/testthat/test-cff_parse_citation.R rename to tests/testthat/test-as_cff_reference.R index 812e5428..d43e0bef 100644 --- a/tests/testthat/test-cff_parse_citation.R +++ b/tests/testthat/test-as_cff_reference.R @@ -49,7 +49,7 @@ test_that("Add wrong field to citation", { type = "I should be removed" ) - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) expect_s3_class(bibparsed, "cff") @@ -76,7 +76,7 @@ test_that("Fix wrong orcid", { ) ) - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) expect_s3_class(bibparsed, "cff") @@ -105,7 +105,7 @@ test_that("Several identifiers and duplicates", { identifiers = "a,b" ) - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) expect_s3_class(bibparsed, "cff") @@ -130,7 +130,7 @@ test_that("Test keywords and urls", { keywords = "Some, random keywords, in, here, here" ) - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) expect_s3_class(bibparsed, "cff") @@ -162,7 +162,7 @@ test_that("Parse persons on CITATION", { "translators" = "Translator one and Translator two" ) - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) expect_snapshot(bibparsed) cffobj <- cff_create(cff(), @@ -173,9 +173,6 @@ test_that("Parse persons on CITATION", { }) test_that("Test inputs", { - bib <- c(1:5) - expect_null(cff_parse_citation(bib)) - # Remove type bib <- bibentry("Book", @@ -187,7 +184,7 @@ test_that("Test inputs", { ) - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) expect_snapshot(bibparsed) cffobj <- cff_create(cff(), @@ -197,14 +194,6 @@ test_that("Test inputs", { expect_true(cff_validate(cffobj, verbose = FALSE)) }) -test_that("NULL bibs and others strange errors", { - bib <- 1 - expect_null(cff_parse_citation(bib)) - class(bib) <- "bibentry" - bib <- NULL - expect_null(cff_parse_citation(bib)) -}) - # Parse citation from BibTeX ---- test_that("Article", { @@ -222,7 +211,7 @@ test_that("Article", { note = "Example modified for testing purposes" ) - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) expect_snapshot(bibparsed) cffobj <- cff_create(cff(), @@ -261,7 +250,7 @@ test_that("Book", { keywords = c("Two, keyword") ) - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) expect_snapshot(bibparsed) cffobj <- cff_create(cff(), @@ -293,7 +282,7 @@ test_that("Booklet", { keywords = "java" ) - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) expect_snapshot(bibparsed) cffobj <- cff_create(cff(), @@ -343,7 +332,7 @@ test_that("Conference", { bib <- list(bib_un) class(bib) <- "bibentry" - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) expect_snapshot(bibparsed) @@ -383,7 +372,7 @@ test_that("InBook", { note = "Example modified for testing purposes" ) - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) expect_snapshot(bibparsed) cffobj <- cff_create(cff(), @@ -423,7 +412,7 @@ test_that("InCollection", { note = "A note" ) - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) expect_snapshot(bibparsed) cffobj <- cff_create(cff(), @@ -464,7 +453,7 @@ test_that("InProceedings", { note = "Example modified for testing purposes" ) - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) expect_snapshot(bibparsed) cffobj <- cff_create(cff(), @@ -495,7 +484,7 @@ test_that("Manual", { note = "Example modified for testing purposes" ) - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) expect_snapshot(bibparsed) cffobj <- cff_create(cff(), @@ -527,7 +516,7 @@ test_that("MastersThesis", { month = "August", note = "Example modified for testing purposes" ) - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) expect_snapshot(bibparsed) cffobj <- cff_create(cff(), @@ -556,7 +545,7 @@ test_that("Misc", { note = "A note" ) - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) expect_snapshot(bibparsed) cffobj <- cff_create(cff(), @@ -589,7 +578,7 @@ test_that("PhdThesis", { note = "Example modified for testing purposes" ) - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) expect_snapshot(bibparsed) cffobj <- cff_create(cff(), @@ -624,7 +613,7 @@ test_that("Proceedings", { note = "Example modified for testing purposes" ) - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) expect_snapshot(bibparsed) cffobj <- cff_create(cff(), @@ -659,7 +648,7 @@ test_that("TechReport", { note = "Example modified for testing purposes" ) - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) expect_snapshot(bibparsed) cffobj <- cff_create(cff(), @@ -687,7 +676,7 @@ test_that("Unpublished", { month = "aug", ) - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) expect_snapshot(bibparsed) cffobj <- cff_create(cff(), @@ -719,7 +708,7 @@ test_that("InBook with booktitle", { chapter = "4.5" ) - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) expect_snapshot(bibparsed) cffobj <- cff_create(cff(), @@ -755,7 +744,7 @@ test_that("Test entry without author", { isbn = "1-59593-322-02", ) - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) expect_identical( bibparsed[[1]]$authors[[1]]$name, @@ -787,7 +776,7 @@ test_that("Test entry without author but has a key", { isbn = "1-59593-322-02", ) - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) expect_identical( bibparsed[[1]]$authors[[1]]$name, @@ -817,7 +806,7 @@ test_that("Test entry without author and key", { isbn = "1-59593-322-02", ) - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) expect_identical( bibparsed[[1]]$authors[[1]]$name, @@ -843,7 +832,7 @@ test_that("Skip misc without title", { year = 2018 ) - expect_snapshot(bibparsed <- cff_parse_citation(bib)) + expect_message(bibparsed <- as_cff(bib), "Skipping") expect_null(bibparsed) @@ -875,7 +864,7 @@ test_that("Skip misc without title, not skipping the good one", { - expect_message(bibparsed <- cff_parse_citation(bib), "SHERPA/RoMEO") + expect_message(bibparsed <- as_cff(bib), "SHERPA/RoMEO") expect_length(bibparsed, 1) @@ -922,7 +911,7 @@ test_that("Check extended BibLatex Fields", { url = "http://www.ctan.org" ) - bibparsed <- cff_parse_citation(bib) + bibparsed <- as_cff(bib) expect_snapshot(bibparsed) cffobj <- cff_create(cff(), diff --git a/tests/testthat/test-cff-methods.R b/tests/testthat/test-cff-methods.R index 29bc1052..e1423439 100644 --- a/tests/testthat/test-cff-methods.R +++ b/tests/testthat/test-cff-methods.R @@ -57,7 +57,7 @@ test_that("Convert a citation only", { path <- system.file("examples/DESCRIPTION_many_persons", package = "cffr") a_cit <- as_bibentry(cff_create(path)) - the_cff <- cff_parse_citation(a_cit) + the_cff <- as_cff(a_cit) # To df the_df <- as.data.frame(the_cff) diff --git a/tests/testthat/test-cff.R b/tests/testthat/test-cff.R index 0c437b26..e345a39f 100644 --- a/tests/testthat/test-cff.R +++ b/tests/testthat/test-cff.R @@ -52,22 +52,6 @@ test_that("Walk trough full lifecycle", { file.remove(tmp) }) -test_that("Other convertes", { - a <- cff() - expect_s3_class(a, "cff") - a <- cff(a) - expect_s3_class(a, "cff") - a <- as.cff(a) - expect_true(is_cff(a)) - expect_s3_class(a, "cff") - - expect_message(noadd <- cff(address = "New York", version = 5)) - expect_true(is_cff(noadd)) - expect_false(is_cff(list(a = 1, b = 2))) - expect_true(is_cff(as.cff(list(a = 1, b = 2)))) -}) - - test_that("Recursive parsing", { complete <- system.file("examples/CITATION_complete.cff", package = "cffr" diff --git a/tests/testthat/test-deprecated.R b/tests/testthat/test-deprecated.R index fa0dd9fc..92b21978 100644 --- a/tests/testthat/test-deprecated.R +++ b/tests/testthat/test-deprecated.R @@ -74,3 +74,11 @@ test_that("cff_parse_person_bibtex", { expect_identical(pend, as_cff_person(p)) }) + + +test_that("cff_parse_citation", { + p <- citation("testthat") + expect_snapshot(pend <- cff_parse_citation(p)) + + expect_identical(pend, as_cff(p)) +}) diff --git a/tests/testthat/test-merge_desc_cit.R b/tests/testthat/test-merge_desc_cit.R index d38bd96f..545c5df7 100644 --- a/tests/testthat/test-merge_desc_cit.R +++ b/tests/testthat/test-merge_desc_cit.R @@ -11,7 +11,7 @@ test_that("Merge all DESCRIPTION files with CITATION_basic", { desc_parse <- cff_read_description(allfiles[i], gh_keywords = FALSE) generate_cit <- cff_safe_read_citation(allfiles[i], citpath) merged <- merge_desc_cit(desc_parse, generate_cit) - merged <- as.cff(merged) + merged <- as_cff(merged) expect_snapshot(merged) diff --git a/vignettes/cffr.Rmd b/vignettes/cffr.Rmd index 684a47e5..9ebf198a 100644 --- a/vignettes/cffr.Rmd +++ b/vignettes/cffr.Rmd @@ -188,7 +188,7 @@ On the following example, we would add two references, one of each type: cff_schema_definitions_refs() # Auto parsed from another R package -base_r <- cff_parse_citation(citation("base")) +base_r <- as_cff(citation("base")) base_r @@ -205,7 +205,7 @@ bib # Now parse it -bookparsed <- cff_parse_citation(bib) +bookparsed <- as_cff(bib) bookparsed ``` From de412dd4ff6e085b4ce21dfb0839a7e37c4acb5b Mon Sep 17 00:00:00 2001 From: Diego H Date: Sat, 2 Mar 2024 14:51:05 +0100 Subject: [PATCH 29/32] Update docs --- NAMESPACE | 1 + R/as_cff.R | 39 ++++++++++++++-- R/cff-methods.R | 8 +++- R/utils.R | 4 ++ man/as_cff.Rd | 8 ++++ man/as_cff_person.Rd | 5 +- tests/testthat/_snaps/as_cff.md | 31 +++++++++++++ tests/testthat/_snaps/cff-methods.md | 52 ++++++++++----------- tests/testthat/test-as_cff.R | 64 ++++++++++++++++++++++++++ tests/testthat/test-as_cff_reference.R | 2 +- tests/testthat/test-cff_read.R | 18 ++++---- 11 files changed, 186 insertions(+), 46 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index 53a933d0..3e5ecbc6 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,5 +1,6 @@ # Generated by roxygen2: do not edit by hand +S3method("[",cff_ref_list) S3method(as.data.frame,cff) S3method(as.person,cff) S3method(as_cff,Bibtex) diff --git a/R/as_cff.R b/R/as_cff.R index f7d8db66..4a896d24 100644 --- a/R/as_cff.R +++ b/R/as_cff.R @@ -28,12 +28,16 @@ #' For `as_cff.bibentry()` / `as_cff.Bibtex()` see #' `vignette("bibtex_cff", "cffr")` to understand how the mapping is performed. #' +#' [as_cff_person()] is preferred over `as_cff.person()` since it can handle +#' `character` person such as `"Davis, Jr., Sammy"`. For `person` objects both +#' functions are similar. #' #' @seealso #' - [cff()]: Create a full `cff` object from scratch. #' - [cff_modify()]: Modify a `cff` object. #' - [cff_create()]: Create a `cff` object of a **R** package. #' - [cff_read()]: Create a `cff` object from a external file. +#' - [as_cff_person()]: Recommended way for creating persons in CFF format. #' #' @export #' @@ -84,8 +88,7 @@ as_cff.list <- function(x, ...) { new_cff(x_clean) } -#' @rdname as_cff_person -#' @order 2 +#' @rdname as_cff #' @export as_cff.person <- function(x, ...) { as_cff(as_cff_person(x), ...) @@ -101,7 +104,16 @@ as_cff.bibentry <- function(x, ...) { return(NULL) } - as_cff(cff_ref, ...) + cff_refs <- as_cff(cff_ref, ...) + + # Add clases + cff_refs_class <- lapply(cff_refs, function(x) { + class(x) <- unique(c("cff_ref", "cff", class(x))) + x + }) + + class(cff_refs_class) <- c("cff_ref_list", "cff", "list") + cff_refs_class } #' @rdname as_cff @@ -110,7 +122,16 @@ as_cff.Bibtex <- function(x, ...) { tmp <- tempfile(fileext = ".bib") writeLines(x, tmp) abib <- cff_read_bib(tmp) - as_cff(abib, ...) + cff_refs <- as_cff(abib, ...) + + # Add clases + cff_refs_class <- lapply(cff_refs, function(x) { + class(x) <- unique(c("cff_ref", "cff", class(x))) + x + }) + + class(cff_refs_class) <- c("cff_ref_list", "cff", "list") + cff_refs_class } # nolint start @@ -174,3 +195,13 @@ new_cff <- function(x) { class(x) <- c("cff", "list") x } + + +# Based in person method +# https://github.com/wch/r-source/blob/trunk/src/library/utils/R/citation.R +#' @export +`[.cff_ref_list` <- function(x, i) { + rval <- unclass(x)[i] + class(rval) <- class(x[[i]]) + return(rval) +} diff --git a/R/cff-methods.R b/R/cff-methods.R index 1c02bb78..6a6941db 100644 --- a/R/cff-methods.R +++ b/R/cff-methods.R @@ -29,8 +29,11 @@ c.cff <- function(..., recursive = FALSE) { #' @noRd #' @export as.data.frame.cff <- function(x, row.names = NULL, optional = FALSE, ...) { - # If the cff is unnamed is a list of persons/references - if (is.null(names(x))) { + # List of references + if (inherits(x, "cff_ref_list")) { + x_n <- list("references" = x) + the_df <- cff_to_df(x_n) + } else if (is.null(names(x))) { the_df <- cff_list_to_df(x) } else { the_df <- cff_to_df(x) @@ -44,6 +47,7 @@ as.data.frame.cff <- function(x, row.names = NULL, optional = FALSE, ...) { return(the_df) } + #' @rdname as_cff_person #' @name as.person.cff #' @order 3 diff --git a/R/utils.R b/R/utils.R index 2b6e07e4..edc60723 100644 --- a/R/utils.R +++ b/R/utils.R @@ -37,6 +37,10 @@ clean_str <- function(str) { #' @param x The list to be cleaned #' @noRd drop_null <- function(x) { + # Already been here + if (inherits(x, "cff")) { + return(x) + } x[lengths(x) != 0] } diff --git a/man/as_cff.Rd b/man/as_cff.Rd index 1b523a9e..865fc745 100644 --- a/man/as_cff.Rd +++ b/man/as_cff.Rd @@ -4,6 +4,7 @@ \alias{as_cff} \alias{as_cff.default} \alias{as_cff.list} +\alias{as_cff.person} \alias{as_cff.bibentry} \alias{as_cff.Bibtex} \alias{as.cff} @@ -15,6 +16,8 @@ as_cff(x, ...) \method{as_cff}{list}(x, ...) +\method{as_cff}{person}(x, ...) + \method{as_cff}{bibentry}(x, ...) \method{as_cff}{Bibtex}(x, ...) @@ -44,6 +47,10 @@ be used to complement or modify complete \code{cff} objects. \details{ For \code{as_cff.bibentry()} / \code{as_cff.Bibtex()} see \code{vignette("bibtex_cff", "cffr")} to understand how the mapping is performed. + +\code{\link[=as_cff_person]{as_cff_person()}} is preferred over \code{as_cff.person()} since it can handle +\code{character} person such as \code{"Davis, Jr., Sammy"}. For \code{person} objects both +functions are similar. } \examples{ @@ -79,6 +86,7 @@ as_cff(a_cit) \item \code{\link[=cff_modify]{cff_modify()}}: Modify a \code{cff} object. \item \code{\link[=cff_create]{cff_create()}}: Create a \code{cff} object of a \strong{R} package. \item \code{\link[=cff_read]{cff_read()}}: Create a \code{cff} object from a external file. +\item \code{\link[=as_cff_person]{as_cff_person()}}: Recommended way for creating persons in CFF format. } Other functions for converting between \strong{R} classes: diff --git a/man/as_cff_person.Rd b/man/as_cff_person.Rd index 0f4f53bf..636f8e6f 100644 --- a/man/as_cff_person.Rd +++ b/man/as_cff_person.Rd @@ -1,15 +1,12 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/as_cff_person.R, R/as_cff.R, R/cff-methods.R +% Please edit documentation in R/as_cff_person.R, R/cff-methods.R \name{as_cff_person} \alias{as_cff_person} -\alias{as_cff.person} \alias{as.person.cff} \title{Create a person with the corresponding \code{\link[=cff-class]{cff}} structure} \usage{ as_cff_person(person) -\method{as_cff}{person}(x, ...) - \method{as.person}{cff}(x) } \arguments{ diff --git a/tests/testthat/_snaps/as_cff.md b/tests/testthat/_snaps/as_cff.md index 5cc45245..9c131a6b 100644 --- a/tests/testthat/_snaps/as_cff.md +++ b/tests/testthat/_snaps/as_cff.md @@ -6,3 +6,34 @@ cff-version: 1.2.0 title: Manipulating files +# as_cff.person + + Code + as_cff(pers) + Output + - family-names: person + given-names: A + email: fake@gmail.com + orcid: https://orcid.org/0000-0000-0000-0000 + affiliation: Real Madrid + website: https://www.google.com/ + +# as_cff.bibentry, toBibtex + + Code + bbb + Output + - type: generic + title: title + authors: + - name: Author + editors: + - name: Editor + +# as_cff.default + + Code + as_cff(b) + Output + a: '1' + diff --git a/tests/testthat/_snaps/cff-methods.md b/tests/testthat/_snaps/cff-methods.md index f3a34f2f..15a36553 100644 --- a/tests/testthat/_snaps/cff-methods.md +++ b/tests/testthat/_snaps/cff-methods.md @@ -798,32 +798,32 @@ Code names(the_df) Output - [1] "reference.00.type" - [2] "reference.00.title" - [3] "reference.00.authors.00.family_names" - [4] "reference.00.authors.00.given_names" - [5] "reference.00.authors.00.email" - [6] "reference.00.authors.00.orcid" - [7] "reference.00.authors.01.family_names" - [8] "reference.00.authors.01.given_names" - [9] "reference.00.authors.01.affiliation" - [10] "reference.00.authors.01.country" - [11] "reference.00.authors.02.family_names" - [12] "reference.00.authors.02.given_names" - [13] "reference.00.authors.02.email" - [14] "reference.00.authors.03.name" - [15] "reference.00.authors.03.date_end" - [16] "reference.00.url" - [17] "reference.00.keywords.00" - [18] "reference.00.keywords.01" - [19] "reference.00.keywords.02" - [20] "reference.00.keywords.03" - [21] "reference.00.keywords.04" - [22] "reference.00.keywords.05" - [23] "reference.00.keywords.06" - [24] "reference.00.keywords.07" - [25] "reference.00.abstract" - [26] "reference.00.version" + [1] "references.00.type" + [2] "references.00.title" + [3] "references.00.authors.00.family_names" + [4] "references.00.authors.00.given_names" + [5] "references.00.authors.00.email" + [6] "references.00.authors.00.orcid" + [7] "references.00.authors.01.family_names" + [8] "references.00.authors.01.given_names" + [9] "references.00.authors.01.affiliation" + [10] "references.00.authors.01.country" + [11] "references.00.authors.02.family_names" + [12] "references.00.authors.02.given_names" + [13] "references.00.authors.02.email" + [14] "references.00.authors.03.name" + [15] "references.00.authors.03.date_end" + [16] "references.00.url" + [17] "references.00.keywords.00" + [18] "references.00.keywords.01" + [19] "references.00.keywords.02" + [20] "references.00.keywords.03" + [21] "references.00.keywords.04" + [22] "references.00.keywords.05" + [23] "references.00.keywords.06" + [24] "references.00.keywords.07" + [25] "references.00.abstract" + [26] "references.00.version" # Convert authors only diff --git a/tests/testthat/test-as_cff.R b/tests/testthat/test-as_cff.R index cfa689a5..9ccccf9a 100644 --- a/tests/testthat/test-as_cff.R +++ b/tests/testthat/test-as_cff.R @@ -12,6 +12,49 @@ test_that("as.cff still works", { }) +test_that("as_cff.person", { + pers <- person("A", "person", + email = "fake@gmail.com", + comment = c( + ORCID = "0000-0000-0000-0000", + affiliation = "Real Madrid", + website = "https://www.google.com/" + ) + ) + + expect_identical(as_cff(pers), as_cff_person(pers)) + expect_snapshot(as_cff(pers)) +}) + +test_that("as_cff.bibentry, toBibtex", { + b <- bibentry("Misc", + title = "title", author = "Author", + editor = "Editor" + ) + + bbb <- as_cff(b) + + expect_s3_class(bbb, c("cff_ref_list", "cff", "list"), exact = TRUE) + expect_s3_class(bbb[[1]], c("cff_ref", "cff", "list"), exact = TRUE) + expect_snapshot(bbb) + + b_bib <- toBibtex(b) + expect_s3_class(b_bib, "Bibtex") + + bbbb <- as_cff(b_bib) + + expect_identical(bbb, bbbb) +}) + + +test_that("as_cff.default", { + b <- c(a = 1) + + expect_identical(as_cff(b), as_cff(as.list(b))) + + expect_snapshot(as_cff(b)) +}) + test_that("Other convertes", { a <- cff() expect_s3_class(a, "cff") @@ -26,3 +69,24 @@ test_that("Other convertes", { expect_false(is_cff(list(a = 1, b = 2))) expect_true(is_cff(as_cff(list(a = 1, b = 2)))) }) + + +test_that("]] cff_ref", { + b1 <- bibentry("Misc", + title = "title", author = "Author", + editor = "Editor" + ) + b2 <- bibentry("Manual", author = "Another", title = "another title") + + b_all <- c(b1, b2) + + expect_s3_class(b_all, "bibentry", exact = TRUE) + bbb <- as_cff(b_all) + + expect_s3_class(bbb, c("cff_ref_list", "cff", "list"), exact = TRUE) + expect_length(bbb, 2) + + b2_reg <- bbb[2] + expect_length(b2_reg, 1) + expect_s3_class(b2_reg, c("cff_ref", "cff", "list"), exact = TRUE) +}) diff --git a/tests/testthat/test-as_cff_reference.R b/tests/testthat/test-as_cff_reference.R index d43e0bef..6806bf89 100644 --- a/tests/testthat/test-as_cff_reference.R +++ b/tests/testthat/test-as_cff_reference.R @@ -33,7 +33,7 @@ test_that("Parsed several citations", { desc_path <- system.file("examples/DESCRIPTION_rgeos", package = "cffr") cit_path <- system.file("examples/CITATION_auto", package = "cffr") citobj <- cff_safe_read_citation(desc_path, cit_path) - expect_s3_class(citobj, c("cff", "list"), exact = TRUE) + expect_s3_class(citobj, c("cff_ref_list", "cff", "list"), exact = TRUE) expect_snapshot(citobj) expect_length(citobj, 3) diff --git a/tests/testthat/test-cff_read.R b/tests/testthat/test-cff_read.R index f4e1c9c6..475a83dc 100644 --- a/tests/testthat/test-cff_read.R +++ b/tests/testthat/test-cff_read.R @@ -63,7 +63,7 @@ test_that("cff_read bib", { f <- system.file("REFERENCES.bib", package = "cffr") f1 <- cff_read(f) - expect_s3_class(f1, c("cff", "list"), exact = TRUE) + expect_s3_class(f1, c("cff_ref_list", "cff", "list"), exact = TRUE) expect_gt(length(f1), 1) # Specific @@ -75,7 +75,7 @@ test_that("cff_read bib", { f <- system.file("examples/example.bib", package = "cffr") f1_2 <- cff_read(f) - expect_s3_class(f1_2, c("cff", "list"), exact = TRUE) + expect_s3_class(f1_2, c("cff_ref_list", "cff", "list"), exact = TRUE) expect_length(f1_2, 2) d <- f1_2[[2]] @@ -100,7 +100,7 @@ test_that("cff_read citation messages", { f <- system.file("examples/CITATION_auto", package = "cffr") expect_message(s <- cff_read(f), "Trying with") - expect_s3_class(s, c("cff", "list"), exact = TRUE) + expect_s3_class(s, c("cff_ref_list", "cff", "list"), exact = TRUE) }) test_that("cff_read CITATION_basic", { @@ -109,7 +109,7 @@ test_that("cff_read CITATION_basic", { path <- system.file("examples/CITATION_basic", package = "cffr") parsed <- cff_read(path, my_meta) - expect_s3_class(parsed, c("cff", "list"), exact = TRUE) + expect_s3_class(parsed, c("cff_ref_list", "cff", "list"), exact = TRUE) expect_equal(length(parsed), 2) }) @@ -119,7 +119,7 @@ test_that("cff_read CITATION with no encoding", { my_meta <- desc_to_meta(desc_path) parsed <- cff_read_citation(cit_path, my_meta) - expect_s3_class(parsed, c("cff", "list"), exact = TRUE) + expect_s3_class(parsed, c("cff_ref_list", "cff", "list"), exact = TRUE) }) test_that("cff_read CITATION_auto", { @@ -148,7 +148,7 @@ test_that("cff_read_safe CITATION_basic", { cit_path <- system.file("examples/CITATION_basic", package = "cffr") parsed <- cff_safe_read_citation(desc_path, cit_path) - expect_s3_class(parsed, c("cff", "list"), exact = TRUE) + expect_s3_class(parsed, c("cff_ref_list", "cff", "list"), exact = TRUE) expect_equal(length(parsed), 2) }) @@ -158,7 +158,7 @@ test_that("cff_read_safe CITATION with no encoding", { parsed <- cff_safe_read_citation(desc_path, cit_path) - expect_s3_class(parsed, c("cff", "list"), exact = TRUE) + expect_s3_class(parsed, c("cff_ref_list", "cff", "list"), exact = TRUE) expect_equal(length(parsed), 2) }) @@ -168,7 +168,7 @@ test_that("cff_read_safe CITATION_auto", { cit_path <- system.file("examples/CITATION_auto", package = "cffr") parsed <- cff_safe_read_citation(desc_path, cit_path) - expect_s3_class(parsed, c("cff", "list"), exact = TRUE) + expect_s3_class(parsed, c("cff_ref_list", "cff", "list"), exact = TRUE) expect_equal(length(parsed), 3) }) @@ -178,7 +178,7 @@ test_that("cff_read_safe CITATION_rmarkdown", { parsed <- cff_safe_read_citation(desc_path, cit_path) - expect_s3_class(parsed, c("cff", "list"), exact = TRUE) + expect_s3_class(parsed, c("cff_ref_list", "cff", "list"), exact = TRUE) expect_equal(length(parsed), 3) }) From 0247fbc262b0592945ed04641d0d2e27526f3e2e Mon Sep 17 00:00:00 2001 From: Diego H Date: Sat, 2 Mar 2024 17:13:08 +0100 Subject: [PATCH 30/32] Deprecate arguments in cff() --- NAMESPACE | 1 + NEWS.md | 1 + R/as_cff.R | 10 ++- R/as_cff_person.R | 11 +++- R/cff.R | 85 +++++++++++++++---------- R/utils.R | 17 +++-- codemeta.json | 2 +- man/cff.Rd | 27 +++----- tests/testthat/_snaps/cff.md | 118 +++++++++++++++++++++++++++-------- tests/testthat/test-as_cff.R | 19 +++++- tests/testthat/test-cff.R | 84 ++++++++++++++----------- 11 files changed, 254 insertions(+), 121 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index 3e5ecbc6..9e16a807 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,5 +1,6 @@ # Generated by roxygen2: do not edit by hand +S3method("[",cff_pers_list) S3method("[",cff_ref_list) S3method(as.data.frame,cff) S3method(as.person,cff) diff --git a/NEWS.md b/NEWS.md index 8898b58b..d7b810cf 100644 --- a/NEWS.md +++ b/NEWS.md @@ -29,6 +29,7 @@ would warn when used, providing advice on the replacement function. `as_bibentry()`. Previous names of this function were `cff_to_bibtex()` and `cff_extract_to_bibtex()` that are now deprecated. - `cff_parse_citation()`: replaced by `as_cff()`, see **New capabilities**. +- Argument `path` in `cff()` is also deprecated, use `cff_read()`. ### New capabilities diff --git a/R/as_cff.R b/R/as_cff.R index 4a896d24..3524ee93 100644 --- a/R/as_cff.R +++ b/R/as_cff.R @@ -91,7 +91,7 @@ as_cff.list <- function(x, ...) { #' @rdname as_cff #' @export as_cff.person <- function(x, ...) { - as_cff(as_cff_person(x), ...) + as_cff_person(x) } @@ -196,6 +196,7 @@ new_cff <- function(x) { x } +# Just for pretty printing on extract # Based in person method # https://github.com/wch/r-source/blob/trunk/src/library/utils/R/citation.R @@ -205,3 +206,10 @@ new_cff <- function(x) { class(rval) <- class(x[[i]]) return(rval) } + +#' @export +`[.cff_pers_list` <- function(x, i) { + rval <- unclass(x)[i] + class(rval) <- class(x[[i]]) + return(rval) +} diff --git a/R/as_cff_person.R b/R/as_cff_person.R index 8739eff3..9fea81fc 100644 --- a/R/as_cff_person.R +++ b/R/as_cff_person.R @@ -157,7 +157,16 @@ as_cff_person <- function(person) { return(NULL) } the_obj <- new_cff(the_obj) - the_obj + + # Add classes + cff_pers_class <- lapply(the_obj, function(x) { + class(x) <- unique(c("cff_pers", "cff", class(x))) + x + }) + + class(cff_pers_class) <- c("cff_pers_list", "cff", "list") + + cff_pers_class } create_person_from_r <- function(person) { diff --git a/R/cff.R b/R/cff.R index acb0a556..61210dcf 100644 --- a/R/cff.R +++ b/R/cff.R @@ -7,26 +7,25 @@ #' @name cff #' @aliases cff_modify #' @return +#' #' A `cff` object. Under the hood, a `cff` object is a regular `list` object #' with a special [print()] method. #' #' @family core #' -#' @param path The path to a `CITATION.cff` file. +#' @param path `r lifecycle::badge("deprecated")` `path` is no longer supported, +#' use [cff_read_cff_citation()] instead. #' @param ... Named arguments to be used for creating a `cff` object. See #' **Details**. #' #' @details #' -#' This object can be manipulated using [cff_create()]. -#' -#' **Note that** this function reads `CITATION.cff` files. If you want to -#' create `cff` objects from DESCRIPTION files use [cff_create()]. -#' #' If no additional `...` parameters are supplied (the default behavior), -#' a minimal valid `cff` object is created. Valid parameters are those -#' specified on [cff_schema_keys()]: +#' a minimal valid `cff` object is created. `cff` would convert `_` in the name +#' of the argument to `-` (e.g, `cff_version = "1.2.0'` would be converted to +#' `cff-version = "1.2.0'`) #' +#' Valid parameters are those specified on [cff_schema_keys()]: #' #' #' ```{r, echo=FALSE} @@ -40,15 +39,9 @@ #' ``` #' @export #' @examples -#' #' # Blank cff #' cff() #' -#' # From file -#' cff_read(system.file("examples/CITATION_basic.cff", -#' package = "cffr" -#' )) -#' #' # Use custom params #' test <- cff( #' title = "Manipulating files", @@ -59,43 +52,71 @@ #' \donttest{ #' # Would fail #' cff_validate(test) -#' +#' } #' #' # Modify with cff_create #' new <- cff_create(test, keys = list( -#' "cff-version" = "1.2.0", +#' "cff_version" = "1.2.0", #' message = "A blank file" #' )) #' new #' #' # Would pass #' cff_validate(new) -#' } +#' #' @export cff <- function(path, ...) { - if (!missing(path) && is_cff(path)) { - return(path) + if (!missing(path)) { + if (is_cff_file(path)) { + lifecycle::deprecate_soft( + "1.0.0", "cff(path)", "cff_read_cff_citation()" + ) + return(cff_read_cff_citation(path)) + } else { + lifecycle::deprecate_soft( + "1.0.0", "cff(path)", + details = "Argument ignored." + ) + } } - # Capture args cffobj <- list(...) - - if (!missing(path)) { - stopifnotexists(path) - stopifnotcff(path) - cffobj <- yaml::read_yaml(path) - } else if (length(cffobj) != 0) { - cffobj <- fuzzy_keys(cffobj) - - cffobj <- cffobj - } else { + if (length(cffobj) == 0) { # If nothing is provided use a minimal cff path <- system.file("examples/CITATION_skeleton.cff", package = "cffr" ) - cffobj <- yaml::read_yaml(path) + + return(cff_read_cff_citation(path)) + } + + # Check names + + has_names <- names(cffobj) + if (is.null(has_names)) { + cli::cli_abort( + "Elements in {.arg ...} should be named." + ) + } + + if (any(has_names == "")) { + index <- as.character(which(has_names %in% "")) + + cli::cli_alert_warning( + "Found {length(index)} not-named argument{?s} in position{?s} {index}." + ) + cli::cli_alert_info("Removing unnamed arguments") + cffobj <- cffobj[has_names != ""] + } + + + + cffobj <- fuzzy_keys(cffobj) + + if (any(duplicated(names(cffobj)))) { + cli::cli_alert_warning("Removing duplicated keys.") + cffobj <- cffobj[!duplicated(names(cffobj))] } - cffobj <- drop_null(cffobj) cffobj <- as_cff(cffobj) diff --git a/R/utils.R b/R/utils.R index edc60723..5689e8e7 100644 --- a/R/utils.R +++ b/R/utils.R @@ -114,6 +114,8 @@ detect_repos <- function(repos = getOption("repos")) { #' @inheritParams cff_create #' @noRd fuzzy_keys <- function(keys) { + nm <- names(keys) + names(keys) <- gsub("_", "-", nm, fixed = TRUE) valid_keys <- cff_schema_keys() names <- names(keys) @@ -137,20 +139,23 @@ fuzzy_keys <- function(keys) { keys_match, function(x) { if (length(x) == 0) { - return("No match") + return("No match, removing.") } return(x[1]) } )) # Message - ll <- paste0("{.dt ", names_fuzzy, "}{.dl ", keys_match, "}\n", - collapse = "" - ) - cli::cli_alert_warning( - paste0("Found misspelled keys. Trying to map:\n", ll) + ll <- paste0("{.dt ", names_fuzzy, "}{.dl ", keys_match, "}") + + bullets <- rep("v", length(ll)) + bullets[keys_match == "No match, removing."] <- "x" + names(ll) <- bullets + cli::cli_alert_info( + paste0("Found misspelled keys. Trying to map:") ) + cli::cli_bullets(ll) # Modify names names[!is_valid_key] <- keys_match } diff --git a/codemeta.json b/codemeta.json index 44a5fd98..974131e4 100644 --- a/codemeta.json +++ b/codemeta.json @@ -200,7 +200,7 @@ }, "isPartOf": "https://ropensci.org", "keywords": ["attribution", "citation", "credit", "citation-files", "cff", "metadata", "r", "r-package", "citation-file-format", "rstats", "ropensci", "cran"], - "fileSize": "947.529KB", + "fileSize": "954.018KB", "citation": [ { "@type": "ScholarlyArticle", diff --git a/man/cff.Rd b/man/cff.Rd index 8b604fe9..efa6d7d3 100644 --- a/man/cff.Rd +++ b/man/cff.Rd @@ -8,7 +8,8 @@ cff(path, ...) } \arguments{ -\item{path}{The path to a \code{CITATION.cff} file.} +\item{path}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} \code{path} is no longer supported, +use \code{\link[=cff_read_cff_citation]{cff_read_cff_citation()}} instead.} \item{...}{Named arguments to be used for creating a \code{cff} object. See \strong{Details}.} @@ -22,14 +23,12 @@ A class and utility methods for reading, creating and holding CFF information. See \code{\linkS4class{cff}} to learn more about \code{cff} objects. } \details{ -This object can be manipulated using \code{\link[=cff_create]{cff_create()}}. - -\strong{Note that} this function reads \code{CITATION.cff} files. If you want to -create \code{cff} objects from DESCRIPTION files use \code{\link[=cff_create]{cff_create()}}. - If no additional \code{...} parameters are supplied (the default behavior), -a minimal valid \code{cff} object is created. Valid parameters are those -specified on \code{\link[=cff_schema_keys]{cff_schema_keys()}}:\tabular{l}{ +a minimal valid \code{cff} object is created. \code{cff} would convert \verb{_} in the name +of the argument to \code{-} (e.g, \verb{cff_version = "1.2.0'} would be converted to +\verb{cff-version = "1.2.0'}) + +Valid parameters are those specified on \code{\link[=cff_schema_keys]{cff_schema_keys()}}:\tabular{l}{ \strong{valid cff keys} \cr cff-version \cr message \cr @@ -55,15 +54,9 @@ specified on \code{\link[=cff_schema_keys]{cff_schema_keys()}}:\tabular{l}{ } } \examples{ - # Blank cff cff() -# From file -cff_read(system.file("examples/CITATION_basic.cff", - package = "cffr" -)) - # Use custom params test <- cff( title = "Manipulating files", @@ -74,18 +67,18 @@ test \donttest{ # Would fail cff_validate(test) - +} # Modify with cff_create new <- cff_create(test, keys = list( - "cff-version" = "1.2.0", + "cff_version" = "1.2.0", message = "A blank file" )) new # Would pass cff_validate(new) -} + } \seealso{ Other core functions of \CRANpkg{cffr}: diff --git a/tests/testthat/_snaps/cff.md b/tests/testthat/_snaps/cff.md index 435bfce9..86490b28 100644 --- a/tests/testthat/_snaps/cff.md +++ b/tests/testthat/_snaps/cff.md @@ -1,12 +1,26 @@ +# Test message on cff + + Code + def <- cff("abcde") + Condition + Warning: + The `path` argument of `cff()` is deprecated as of cffr 1.0.0. + i Argument ignored. + +--- + + Code + afile <- cff(nocff) + Condition + Warning: + The `path` argument of `cff()` is deprecated as of cffr 1.0.0. + i Argument ignored. + # Walk trough full lifecycle Code - print_snapshot("Read object", read) + read Output - - - ## Read object - cff-version: 1.2.0 message: If you use this software, please cite it as below. abstract: This is an awesome piece of research software! @@ -840,18 +854,12 @@ date-start: '2017-01-01' date-end: '2017-01-31' location: The team garage - - --- --- Code - print_snapshot("Modify object", modify) + modify Output - - - ## Modify object - cff-version: 1.2.0 message: If you use this software, please cite it as below. type: software @@ -1685,25 +1693,85 @@ - type: other value: other-schema://abcd.1234.efgh.5678 license-url: https://spdx.org/licenses/CC-BY-SA-4.0.html#licenseText - - --- + +# Recursive parsing + + Code + read <- cff(complete) + Condition + Warning: + The `path` argument of `cff()` is deprecated as of cffr 1.0.0. + i Please use `cff_read_cff_citation()` instead. # Fuzzy matching of keys on cff Code - print_snapshot("Fuzzy keys", cffobj) + cff(tittle = "a", cff_version = "ar", version = "200", messange = "Fix my keys") + Message + i Found misspelled keys. Trying to map: + v tittle: title + v messange: message Output - - - ## Fuzzy keys - title: a - cff-version: 1.2.0 + cff-version: ar version: '200' - authors: - - family-names: a - given-names: b message: Fix my keys - - --- + +--- + + Code + cffobj <- cff(tittle = "a", cff_version = "1.2.0", version = "200", messange = "aa", + anthor = list(list(`family-names` = "a", `given-names` = "b"))) + Message + i Found misspelled keys. Trying to map: + v tittle: title + v messange: message + v anthor: authors + +# duplicated + + Code + ss <- cff(tittle = "a", tittle = "ar", version = "200", messange = "Fix my keys") + Message + i Found misspelled keys. Trying to map: + v tittle: title + v tittle: title + v messange: message + ! Removing duplicated keys. + +# unnamed + + Code + ss <- cff(path = "a", "200", "Fix my keys") + Condition + Warning: + The `path` argument of `cff()` is deprecated as of cffr 1.0.0. + i Argument ignored. + Error in `cff()`: + ! Elements in `...` should be named. + +--- + + Code + s1 <- cff(path = NULL, title = "a", "b", version = 1) + Condition + Warning: + The `path` argument of `cff()` is deprecated as of cffr 1.0.0. + i Argument ignored. + Message + ! Found 1 not-named argument in position 2. + i Removing unnamed arguments + +--- + + Code + s2 <- cff(path = NULL, title = "a", "aa", "bb", "cc", "b", version = 1, "h", + "j") + Condition + Warning: + The `path` argument of `cff()` is deprecated as of cffr 1.0.0. + i Argument ignored. + Message + ! Found 6 not-named arguments in positions 2, 3, 4, 5, 7, and 8. + i Removing unnamed arguments diff --git a/tests/testthat/test-as_cff.R b/tests/testthat/test-as_cff.R index 9ccccf9a..b31c7359 100644 --- a/tests/testthat/test-as_cff.R +++ b/tests/testthat/test-as_cff.R @@ -58,8 +58,6 @@ test_that("as_cff.default", { test_that("Other convertes", { a <- cff() expect_s3_class(a, "cff") - a <- cff(a) - expect_s3_class(a, "cff") a <- as_cff(a) expect_true(is_cff(a)) expect_s3_class(a, "cff") @@ -90,3 +88,20 @@ test_that("]] cff_ref", { expect_length(b2_reg, 1) expect_s3_class(b2_reg, c("cff_ref", "cff", "list"), exact = TRUE) }) + +test_that("]] cff_pers", { + b1 <- person("One", "person") + b2 <- person("ntity") + + b_all <- c(b1, b2) + + expect_s3_class(b_all, "person", exact = TRUE) + bbb <- as_cff(b_all) + + expect_s3_class(bbb, c("cff_pers_list", "cff", "list"), exact = TRUE) + expect_length(bbb, 2) + + b2_reg <- bbb[2] + expect_length(b2_reg, 1) + expect_s3_class(b2_reg, c("cff_pers", "cff", "list"), exact = TRUE) +}) diff --git a/tests/testthat/test-cff.R b/tests/testthat/test-cff.R index e345a39f..02a12d64 100644 --- a/tests/testthat/test-cff.R +++ b/tests/testthat/test-cff.R @@ -1,24 +1,16 @@ -test_that("Test errors on cff", { - expect_error(cff("abcde")) - nocff <- system.file("CITATION", - package = "cffR" - ) - expect_error(cff_create(nocff)) -}) +test_that("Test message on cff", { + expect_snapshot(def <- cff("abcde")) -test_that("Compare blank cff with skeleton", { - skeleton <- system.file("examples/CITATION_skeleton.cff", - package = "cffr" + expect_identical(def, cff()) + nocff <- system.file("examples/CITATION_skeleton.cff", + package = "cffR" ) + expect_snapshot(afile <- cff(nocff)) - fromfile <- cff(skeleton) - fromfunction <- cff() - expect_true(all(unlist(fromfile) == unlist(fromfunction))) - - # Validate - expect_true(cff_validate(fromfunction, verbose = FALSE)) + expect_identical(afile, cff()) }) + test_that("Walk trough full lifecycle", { complete <- system.file("examples/CITATION_complete.cff", package = "cffr" @@ -28,12 +20,11 @@ test_that("Walk trough full lifecycle", { read <- cff_read(complete) expect_s3_class(read, "cff") expect_true(cff_validate(read, verbose = FALSE)) - expect_snapshot(print_snapshot("Read object", read)) + expect_snapshot(read) # Modify modify <- cff_create(read, keys = list(title = "A new title")) - expect_snapshot(print_snapshot("Modify object", modify)) - expect_true(all(unlist(read) == unlist(read))) + expect_snapshot(modify) expect_true(length(read) == length(modify)) expect_true(length((setdiff(names(read), names(modify)))) == 0) expect_false(read$title == modify$title) @@ -58,7 +49,8 @@ test_that("Recursive parsing", { ) # Read - read <- cff(complete) + expect_snapshot(read <- cff(complete)) + read <- cff_read(complete) # Test all levels expect_s3_class(read, "cff") @@ -71,28 +63,48 @@ test_that("Recursive parsing", { test_that("Fuzzy matching of keys on cff", { - expect_message(cff( + expect_snapshot(cff( tittle = "a", cff_version = "ar", version = "200", messange = "Fix my keys" - ), "messange: message") - - cffobj <- suppressMessages( - cff( - tittle = "a", - cff_version = "1.2.0", - version = "200", - anthor = list(list( - "family-names" = "a", - "given-names" = "b" - )), - messange = "Fix my keys" - ) - ) + )) + + expect_snapshot(cffobj <- cff( + tittle = "a", + cff_version = "1.2.0", version = "200", + messange = "aa", + anthor = list(list( + "family-names" = "a", + "given-names" = "b" + )) + )) expect_true(is_cff(cffobj)) expect_true(cff_validate(cffobj, verbose = FALSE)) +}) + +test_that("duplicated", { + expect_snapshot(ss <- cff( + tittle = "a", tittle = "ar", + version = "200", + messange = "Fix my keys" + )) + + expect_s3_class(ss, "cff") + expect_length(ss, 3) +}) + +test_that("unnamed", { + expect_snapshot(ss <- cff( + path = "a", "200", "Fix my keys" + ), error = TRUE) + + expect_snapshot(s1 <- cff(path = NULL, title = "a", "b", version = 1)) + expect_snapshot(s2 <- cff( + path = NULL, title = "a", "aa", "bb", "cc", + "b", version = 1, "h", "j" + )) - expect_snapshot(print_snapshot("Fuzzy keys", cffobj)) + expect_identical(s1, s2) }) From e9247435ea7cd857a9abf7d6e9a0612b7a65a24c Mon Sep 17 00:00:00 2001 From: Diego H Date: Sun, 3 Mar 2024 18:00:19 +0100 Subject: [PATCH 31/32] Add cff_modify --- NAMESPACE | 1 + NEWS.md | 1 + R/as_cff.R | 4 +- R/as_cff_person.R | 2 +- R/cff-methods.R | 2 +- R/cff.R | 21 +------ R/cff_create.R | 22 +++---- R/cff_modify.R | 105 +++++++++++++++++++++++++++++++++ R/cff_read.R | 8 +-- R/cff_read_bib_text.R | 4 +- R/cff_validate.R | 4 +- R/cff_write.R | 2 +- R/cff_write_misc.R | 2 +- R/deprecated.R | 6 +- R/docs.R | 1 - man/as_bibentry.Rd | 2 +- man/as_cff.Rd | 4 +- man/as_cff_person.Rd | 2 +- man/cff-class.Rd | 1 - man/cff.Rd | 2 +- man/cff_create.Rd | 13 ++-- man/cff_modify.Rd | 56 ++++++++++++++++++ man/cff_read.Rd | 8 +-- man/cff_read_bib_text.Rd | 4 +- man/cff_validate.Rd | 7 ++- man/cff_write.Rd | 2 +- man/cff_write_misc.Rd | 2 +- man/deprecated_cff_from_bib.Rd | 4 +- man/deprecated_cff_person.Rd | 2 +- man/deprecated_write.Rd | 2 +- pkgdown/_pkgdown.yml | 84 +++++++++++++------------- tests/testthat/_snaps/cff.md | 2 +- vignettes/cffr.Rmd | 89 +++++++++++++--------------- 33 files changed, 301 insertions(+), 170 deletions(-) create mode 100644 R/cff_modify.R create mode 100644 man/cff_modify.Rd diff --git a/NAMESPACE b/NAMESPACE index 9e16a807..259686bd 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -25,6 +25,7 @@ export(cff_from_bibtex) export(cff_gha_update) export(cff_git_hook_install) export(cff_git_hook_remove) +export(cff_modify) export(cff_parse_citation) export(cff_parse_person) export(cff_parse_person_bibtex) diff --git a/NEWS.md b/NEWS.md index d7b810cf..1b4080bc 100644 --- a/NEWS.md +++ b/NEWS.md @@ -49,6 +49,7 @@ would warn when used, providing advice on the replacement function. - `cff_read_description()`, - `cff_read_citation()` - `cff_read_bib()`. +- New `cff_modify()` function for updating and modifying `cff` objects easily. ## Other changes diff --git a/R/as_cff.R b/R/as_cff.R index 3524ee93..cfef6fe9 100644 --- a/R/as_cff.R +++ b/R/as_cff.R @@ -1,8 +1,8 @@ -#' Coerce lists, `person` and `bibentry` objects to [`cff`][cff-class] +#' Coerce lists, `person` and `bibentry` objects to [`cff`] #' #' @description #' `as_cff()` turns an existing list-like **R** object into a so-called -#' [`cff`][cff-class], a list with class `cff`. +#' [`cff`], a list with class `cff`. #' #' `as_cff` is an S3 generic, with methods for: #' - `person` objects as produced by [utils::person()]. diff --git a/R/as_cff_person.R b/R/as_cff_person.R index 9fea81fc..60a17e0e 100644 --- a/R/as_cff_person.R +++ b/R/as_cff_person.R @@ -1,4 +1,4 @@ -#' Create a person with the corresponding [`cff`][cff-class] structure +#' Create a person with the corresponding [`cff`] structure #' #' @description #' diff --git a/R/cff-methods.R b/R/cff-methods.R index 6a6941db..a3cd772c 100644 --- a/R/cff-methods.R +++ b/R/cff-methods.R @@ -107,7 +107,7 @@ tail.cff <- function(x, n = 6L, ...) { #' #' @description #' Additionally, it is also provided a method for [toBibtex()], that can -#' convert [`cff`][cff-class] objects to `Bibtex` objects as provided by +#' convert [`cff`] objects to `Bibtex` objects as provided by #' [utils::toBibtex()]. These objects are character vectors with BibTeX markup. #' #' @family s3method diff --git a/R/cff.R b/R/cff.R index 61210dcf..3d619158 100644 --- a/R/cff.R +++ b/R/cff.R @@ -5,7 +5,6 @@ #' #' @rdname cff #' @name cff -#' @aliases cff_modify #' @return #' #' A `cff` object. Under the hood, a `cff` object is a regular `list` object @@ -92,25 +91,7 @@ cff <- function(path, ...) { # Check names - has_names <- names(cffobj) - if (is.null(has_names)) { - cli::cli_abort( - "Elements in {.arg ...} should be named." - ) - } - - if (any(has_names == "")) { - index <- as.character(which(has_names %in% "")) - - cli::cli_alert_warning( - "Found {length(index)} not-named argument{?s} in position{?s} {index}." - ) - cli::cli_alert_info("Removing unnamed arguments") - cffobj <- cffobj[has_names != ""] - } - - - + cffobj <- validate_extra_keys(cffobj) cffobj <- fuzzy_keys(cffobj) if (any(duplicated(names(cffobj)))) { diff --git a/R/cff_create.R b/R/cff_create.R index 5003fab4..b8792efb 100644 --- a/R/cff_create.R +++ b/R/cff_create.R @@ -1,29 +1,29 @@ -#' Create a [`cff`][cff-class] object +#' Create a [`cff`] object #' #' @description #' -#' Create a [`cff`][cff-class] object from a given source for further +#' Create a [`cff`] object from a given source for further #' manipulation. This object can be written to a `*.cff ` file with #' [cff_write()], see **Examples**. #' #' Most of the heavy lifting of \CRANpkg{cffr} is done via this function. #' -#' @return A [`cff`][cff-class] object. +#' @return A [`cff`] object. #' #' @family core #' #' @export #' #' @param x The source that would be used for generating -#' the [`cff`][cff-class] object. It could be: +#' the [`cff`] object. It could be: #' * A missing value. That would retrieve the `DESCRIPTION` file on your #' in-development **R** package. -#' * An existing [`cff`][cff-class] object, +#' * An existing [`cff`] object, #' * The name of an installed package (`"jsonlite"`), or #' * Path to a `DESCRIPTION` file (`"./DESCRIPTION"`). #' #' @param keys -#' List of additional keys to add to the [`cff`][cff-class] object. See +#' List of additional keys to add to the [`cff`] object. See #' **Details**. #' @param cff_version The Citation File Format schema version that the #' `CITATION.cff` file adheres to for providing the citation metadata. @@ -141,14 +141,8 @@ cff_create <- function(x, keys = list(), cff_version = "1.2.0", cffobjend$references <- unique(c(cffobjend$references, deps)) } - # Additional keys - if (!is.null(keys)) { - keys <- fuzzy_keys(keys) - cffobjendmod <- cffobjend[setdiff(names(cffobjend), names(keys))] - cffobjend <- modifyList(cffobjendmod, keys, keep.null = FALSE) - cffobjend <- as_cff(cffobjend) - } - + # Additional keys, using internals of cff_modify + cffobjend <- modify_cff(cffobjend, keys, "keys") # Order cffobjend <- cffobjend[cff_schema_keys()] diff --git a/R/cff_modify.R b/R/cff_modify.R new file mode 100644 index 00000000..757940df --- /dev/null +++ b/R/cff_modify.R @@ -0,0 +1,105 @@ +#' Modify a [`cff`] object +#' +#' Add new keys or modify existing ones on a [`cff`] object. +#' +#' @param x A [`cff`] object. +#' @param ... Named arguments to be used for modifying `x`. See also [cff()]. +#' +#' @details +#' +#' If any key provided in `...` is present in `x`, the result would have the +#' key provided in `...`. +#' +#' @returns +#' +#' A [`cff`] object. +#' +#' @family core +#' @export +#' @seealso +#' This function is wrapper of [utils::modifyList()]. +#' +#' See [cff()] for creating [`cff`] objects from scratch. +#' +#' +#' +#' @export +#' @family core +#' @examples +#' x <- cff() +#' x +#' +#' cff_validate(x) +#' +#' +#' x_mod <- cff_modify(x, +#' contact = as_cff_person("A contact"), +#' message = "This overwrites fields", +#' title = "New Title", +#' abstract = "New abstract", +#' doi = "10.21105/joss.03900" +#' ) +#' cff_validate(x_mod) +#' +cff_modify <- function(x, ...) { + if (!inherits(x, "cff")) { + cli::cli_abort( + "{.arg x} should be a {.cls cff} object, not {.cls {class(x)}}." + ) + } + new_keys <- list(...) + if (length(new_keys) == 0) { + cli::cli_alert_info("Args {.arg ...} empty. Returning {.arg x}.") + return(x) + } + + modify_cff(x, new_keys, "...") +} + +modify_cff <- function(x, keys, argname = "...") { + # Don't throw message here, these cases are coming from cff_create + if (all(argname == "keys", length(keys) == 0)) { + return(x) + } + + new_keys <- validate_extra_keys(keys, argname) + new_keys <- fuzzy_keys(new_keys) + if (any(duplicated(names(new_keys)))) { + cli::cli_alert_warning("Removing duplicated keys.") + new_keys <- new_keys[!duplicated(names(new_keys))] + } + + init_ord <- names(x) + + xmod <- x[setdiff(names(x), names(new_keys))] + xend <- modifyList(xmod, new_keys, keep.null = FALSE) + + # Name order + sorted_nm <- unique(c(init_ord, names(xend))) + as_cff(xend[sorted_nm]) +} + + +# Check names +validate_extra_keys <- function(cffobj, argname = "...") { + has_names <- names(cffobj) + if (is.null(has_names)) { + cli::cli_abort( + "Elements in {.arg {argname}} should be named." + ) + } + + if (any(has_names == "")) { + # nolint start + # For printing only + index <- as.character(which(has_names %in% "")) + # nolint end + + cli::cli_alert_warning( + "Found {length(index)} not-named argument{?s} in position{?s} {index}." + ) + cli::cli_alert_info("Removing unnamed arguments") + cffobj <- cffobj[has_names != ""] + } + cffobj +} diff --git a/R/cff_read.R b/R/cff_read.R index e360b371..202c0572 100644 --- a/R/cff_read.R +++ b/R/cff_read.R @@ -1,7 +1,7 @@ -#' Read an external file as a [`cff`][cff-class] object +#' Read an external file as a [`cff`] object #' #' @description -#' Read files and convert them to [`cff`][cff-class] objects. Files supported +#' Read files and convert them to [`cff`] objects. Files supported #' are: #' - `CITATION.cff` files. #' - `DESCRIPTION` files. @@ -33,7 +33,7 @@ #' @param gh_keywords Logical `TRUE/FALSE`. If the package is hosted on #' GitHub, would you like to add the repo topics as keywords? #' @param authors_roles Roles to be considered as authors of the package when -#' generating the [`cff`][cff-class] object. +#' generating the [`cff`] object. #' @param encoding Encoding to be assumed for `path`. See [readLines()]. #' @param meta A list of package metadata as obtained by #' [utils::packageDescription()] or `NULL` (the default). See **Details**. @@ -41,7 +41,7 @@ #' [yaml::read_yaml()], [bibtex::read.bib()], etc.). #' #' @return -#' A [`cff`][cff-class] object. In the case of [cff_read_cff_citation()] and +#' A [`cff`] object. In the case of [cff_read_cff_citation()] and #' [cff_read_description()] a full and (potentially) valid `cff` object. #' #' diff --git a/R/cff_read_bib_text.R b/R/cff_read_bib_text.R index 05447461..e615632a 100644 --- a/R/cff_read_bib_text.R +++ b/R/cff_read_bib_text.R @@ -1,8 +1,8 @@ -#' Read BibTeX markup as a [`cff`][cff-class] object +#' Read BibTeX markup as a [`cff`] object #' #' @description #' Convert a [`character`][character()] representing a BibTeX entry to a -#' [`cff`][cff-class] object. +#' [`cff`] object. #' #' @family bibtex #' @family reading diff --git a/R/cff_validate.R b/R/cff_validate.R index 0c55820d..cbf73f30 100644 --- a/R/cff_validate.R +++ b/R/cff_validate.R @@ -1,7 +1,7 @@ -#' Validate a `CITATION.cff` file or a [`cff`][cff-class] object +#' Validate a `CITATION.cff` file or a [`cff`] object #' #' @description -#' Validate a `CITATION.cff` file or a [`cff`][cff-class] object created with +#' Validate a `CITATION.cff` file or a [`cff`] object created with #' [cff_create()] using the corresponding validation #' ```{r, echo=FALSE, results='asis'} #' diff --git a/R/cff_write.R b/R/cff_write.R index 386f2a0c..d1dd21e2 100644 --- a/R/cff_write.R +++ b/R/cff_write.R @@ -7,7 +7,7 @@ #' #' This function writes out a `CITATION.cff` file for a given package. This #' function is basically a wrapper around [cff_create()] to both create the -#' [`cff`][cff-class] object and writes it out to a YAML-formatted file in +#' [`cff`] object and writes it out to a YAML-formatted file in #' one command. #' #' @family writing diff --git a/R/cff_write_misc.R b/R/cff_write_misc.R index 13187d58..ac957920 100644 --- a/R/cff_write_misc.R +++ b/R/cff_write_misc.R @@ -7,7 +7,7 @@ #' - [cff_write_citation()] creates a **R** citation file as explained in #' Section 1.9 CITATION files of *Writing R Extensions* (R Core Team 2023). #' -#' @param x A [`bibentry`][bibentry()] or a [`cff`][cff-class] object. +#' @param x A [`bibentry`][bibentry()] or a [`cff`] object. #' @param file Name of the file to be created. If `NULL` it would display the #' lines to be written. #' @param append Whether to append the entries to an existing file or not. diff --git a/R/deprecated.R b/R/deprecated.R index 1120e54b..f468f415 100644 --- a/R/deprecated.R +++ b/R/deprecated.R @@ -45,7 +45,7 @@ cff_to_bibtex <- function(x, as_bibentry(x, what) } -#' Previous API: Create a [`cff`][cff-class] object from BibTeX entries +#' Previous API: Create a [`cff`] object from BibTeX entries #' #' @description #' @@ -58,7 +58,7 @@ cff_to_bibtex <- function(x, #' @family deprecated #' #' @param x The source that would be used for generating the -#' [`cff`][cff-class] object. Must be `character` object indicating either: +#' [`cff`] object. Must be `character` object indicating either: #' - The path to a BibTeX file. #' - A vector of characters with the full BibTeX string. See **Examples**. #' @param encoding Encoding to be assumed for `x`. See [readLines()]. @@ -186,7 +186,7 @@ write_citation <- function(x, } -#' Previous API: Parse a `person` to [`cff`][cff-class] +#' Previous API: Parse a `person` to [`cff`] #' #' @description #' diff --git a/R/docs.R b/R/docs.R index 547745fb..a9db5e3c 100644 --- a/R/docs.R +++ b/R/docs.R @@ -1,7 +1,6 @@ #' The `cff` class #' #' @name cff-class -#' @aliases cff_class #' @keywords internal #' #' @family s3method diff --git a/man/as_bibentry.Rd b/man/as_bibentry.Rd index d3959205..162367b4 100644 --- a/man/as_bibentry.Rd +++ b/man/as_bibentry.Rd @@ -55,7 +55,7 @@ The inverse transformation (\code{bibentry} object to \code{cff} reference) can be done with the corresponding \code{\link[=as_cff]{as_cff()}} method. Additionally, it is also provided a method for \code{\link[=toBibtex]{toBibtex()}}, that can -convert \code{\link[=cff-class]{cff}} objects to \code{Bibtex} objects as provided by +convert \code{\link{cff}} objects to \code{Bibtex} objects as provided by \code{\link[utils:toLatex]{utils::toBibtex()}}. These objects are character vectors with BibTeX markup. } \examples{ diff --git a/man/as_cff.Rd b/man/as_cff.Rd index 865fc745..74bf9ee7 100644 --- a/man/as_cff.Rd +++ b/man/as_cff.Rd @@ -8,7 +8,7 @@ \alias{as_cff.bibentry} \alias{as_cff.Bibtex} \alias{as.cff} -\title{Coerce lists, \code{person} and \code{bibentry} objects to \code{\link[=cff-class]{cff}}} +\title{Coerce lists, \code{person} and \code{bibentry} objects to \code{\link{cff}}} \usage{ as_cff(x, ...) @@ -34,7 +34,7 @@ be used to complement or modify complete \code{cff} objects. } \description{ \code{as_cff()} turns an existing list-like \strong{R} object into a so-called -\code{\link[=cff-class]{cff}}, a list with class \code{cff}. +\code{\link{cff}}, a list with class \code{cff}. \code{as_cff} is an S3 generic, with methods for: \itemize{ diff --git a/man/as_cff_person.Rd b/man/as_cff_person.Rd index 636f8e6f..6b11aa1f 100644 --- a/man/as_cff_person.Rd +++ b/man/as_cff_person.Rd @@ -3,7 +3,7 @@ \name{as_cff_person} \alias{as_cff_person} \alias{as.person.cff} -\title{Create a person with the corresponding \code{\link[=cff-class]{cff}} structure} +\title{Create a person with the corresponding \code{\link{cff}} structure} \usage{ as_cff_person(person) diff --git a/man/cff-class.Rd b/man/cff-class.Rd index 82cba487..602b2020 100644 --- a/man/cff-class.Rd +++ b/man/cff-class.Rd @@ -2,7 +2,6 @@ % Please edit documentation in R/docs.R \name{cff-class} \alias{cff-class} -\alias{cff_class} \title{The \code{cff} class} \description{ \subsection{The \code{cff} class}{ diff --git a/man/cff.Rd b/man/cff.Rd index efa6d7d3..46cfd6db 100644 --- a/man/cff.Rd +++ b/man/cff.Rd @@ -2,7 +2,6 @@ % Please edit documentation in R/cff.R \name{cff} \alias{cff} -\alias{cff_modify} \title{Read and manipulate \code{cff} objects} \usage{ cff(path, ...) @@ -83,6 +82,7 @@ cff_validate(new) \seealso{ Other core functions of \CRANpkg{cffr}: \code{\link{cff_create}()}, +\code{\link{cff_modify}()}, \code{\link{cff_validate}()} } \concept{core} diff --git a/man/cff_create.Rd b/man/cff_create.Rd index ea829a90..ab413b1e 100644 --- a/man/cff_create.Rd +++ b/man/cff_create.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/cff_create.R \name{cff_create} \alias{cff_create} -\title{Create a \code{\link[=cff-class]{cff}} object} +\title{Create a \code{\link{cff}} object} \usage{ cff_create( x, @@ -15,16 +15,16 @@ cff_create( } \arguments{ \item{x}{The source that would be used for generating -the \code{\link[=cff-class]{cff}} object. It could be: +the \code{\link{cff}} object. It could be: \itemize{ \item A missing value. That would retrieve the \code{DESCRIPTION} file on your in-development \strong{R} package. -\item An existing \code{\link[=cff-class]{cff}} object, +\item An existing \code{\link{cff}} object, \item The name of an installed package (\code{"jsonlite"}), or \item Path to a \code{DESCRIPTION} file (\code{"./DESCRIPTION"}). }} -\item{keys}{List of additional keys to add to the \code{\link[=cff-class]{cff}} object. See +\item{keys}{List of additional keys to add to the \code{\link{cff}} object. See \strong{Details}.} \item{cff_version}{The Citation File Format schema version that the @@ -40,10 +40,10 @@ of your package to the \code{references} CFF key?} generating the \code{CITATION.cff} file. See \strong{Details}.} } \value{ -A \code{\link[=cff-class]{cff}} object. +A \code{\link{cff}} object. } \description{ -Create a \code{\link[=cff-class]{cff}} object from a given source for further +Create a \code{\link{cff}} object from a given source for further manipulation. This object can be written to a \verb{*.cff } file with \code{\link[=cff_write]{cff_write()}}, see \strong{Examples}. @@ -112,6 +112,7 @@ shows an introduction on how manipulate \code{cff} objects. Other core functions of \CRANpkg{cffr}: \code{\link{cff}()}, +\code{\link{cff_modify}()}, \code{\link{cff_validate}()} } \concept{core} diff --git a/man/cff_modify.Rd b/man/cff_modify.Rd new file mode 100644 index 00000000..69173071 --- /dev/null +++ b/man/cff_modify.Rd @@ -0,0 +1,56 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/cff_modify.R +\name{cff_modify} +\alias{cff_modify} +\title{Modify a \code{\link{cff}} object} +\usage{ +cff_modify(x, ...) +} +\arguments{ +\item{x}{A \code{\link{cff}} object.} + +\item{...}{Named arguments to be used for modifying \code{x}. See also \code{\link[=cff]{cff()}}.} +} +\value{ +A \code{\link{cff}} object. +} +\description{ +Add new keys or modify existing ones on a \code{\link{cff}} object. +} +\details{ +If any key provided in \code{...} is present in \code{x}, the result would have the +key provided in \code{...}. +} +\examples{ +x <- cff() +x + +cff_validate(x) + + +x_mod <- cff_modify(x, + contact = as_cff_person("A contact"), + message = "This overwrites fields", + title = "New Title", + abstract = "New abstract", + doi = "10.21105/joss.03900" +) +cff_validate(x_mod) + +} +\seealso{ +This function is wrapper of \code{\link[utils:modifyList]{utils::modifyList()}}. + +See \code{\link[=cff]{cff()}} for creating \code{\link{cff}} objects from scratch. + +Other core functions of \CRANpkg{cffr}: +\code{\link{cff}()}, +\code{\link{cff_create}()}, +\code{\link{cff_validate}()} + +Other core functions of \CRANpkg{cffr}: +\code{\link{cff}()}, +\code{\link{cff_create}()}, +\code{\link{cff_validate}()} +} +\concept{core} diff --git a/man/cff_read.Rd b/man/cff_read.Rd index b36fc1cb..5a7fef5e 100644 --- a/man/cff_read.Rd +++ b/man/cff_read.Rd @@ -6,7 +6,7 @@ \alias{cff_read_description} \alias{cff_read_citation} \alias{cff_read_bib} -\title{Read an external file as a \code{\link[=cff-class]{cff}} object} +\title{Read an external file as a \code{\link{cff}} object} \usage{ cff_read(path, ...) @@ -37,7 +37,7 @@ cff_read_bib(path, encoding = "UTF-8", ...) GitHub, would you like to add the repo topics as keywords?} \item{authors_roles}{Roles to be considered as authors of the package when -generating the \code{\link[=cff-class]{cff}} object.} +generating the \code{\link{cff}} object.} \item{meta}{A list of package metadata as obtained by \code{\link[utils:packageDescription]{utils::packageDescription()}} or \code{NULL} (the default). See \strong{Details}.} @@ -45,7 +45,7 @@ generating the \code{\link[=cff-class]{cff}} object.} \item{encoding}{Encoding to be assumed for \code{path}. See \code{\link[=readLines]{readLines()}}.} } \value{ -A \code{\link[=cff-class]{cff}} object. In the case of \code{\link[=cff_read_cff_citation]{cff_read_cff_citation()}} and +A \code{\link{cff}} object. In the case of \code{\link[=cff_read_cff_citation]{cff_read_cff_citation()}} and \code{\link[=cff_read_description]{cff_read_description()}} a full and (potentially) valid \code{cff} object. In the case of \code{\link[=cff_read_bib]{cff_read_bib()}} and \code{\link[=cff_read_citation]{cff_read_citation()}}, the result is @@ -55,7 +55,7 @@ that can be used to complement another \code{cff} object. See conversion is performed. } \description{ -Read files and convert them to \code{\link[=cff-class]{cff}} objects. Files supported +Read files and convert them to \code{\link{cff}} objects. Files supported are: \itemize{ \item \code{CITATION.cff} files. diff --git a/man/cff_read_bib_text.Rd b/man/cff_read_bib_text.Rd index ede3aa44..f101d62c 100644 --- a/man/cff_read_bib_text.Rd +++ b/man/cff_read_bib_text.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/cff_read_bib_text.R \name{cff_read_bib_text} \alias{cff_read_bib_text} -\title{Read BibTeX markup as a \code{\link[=cff-class]{cff}} object} +\title{Read BibTeX markup as a \code{\link{cff}} object} \usage{ cff_read_bib_text(x, encoding = "UTF-8", ...) } @@ -23,7 +23,7 @@ A \code{\link[=cff-class]{cff}} object ready to be used with other functions (i. } \description{ Convert a \code{\link[=character]{character}} representing a BibTeX entry to a -\code{\link[=cff-class]{cff}} object. +\code{\link{cff}} object. } \details{ This is a helper function that writes \code{x} to a \verb{*.bib} file and reads it with diff --git a/man/cff_validate.Rd b/man/cff_validate.Rd index d3ed8aa0..6b6079ff 100644 --- a/man/cff_validate.Rd +++ b/man/cff_validate.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/cff_validate.R \name{cff_validate} \alias{cff_validate} -\title{Validate a \code{CITATION.cff} file or a \code{\link[=cff-class]{cff}} object} +\title{Validate a \code{CITATION.cff} file or a \code{\link{cff}} object} \usage{ cff_validate(x = "CITATION.cff", verbose = TRUE) } @@ -19,7 +19,7 @@ value \code{TRUE/FALSE}. On error, the results would have an attribute \code{"errors"} containing the error summary (see \strong{Examples} and \code{\link[=attr]{attr()}}). } \description{ -Validate a \code{CITATION.cff} file or a \code{\link[=cff-class]{cff}} object created with +Validate a \code{CITATION.cff} file or a \code{\link{cff}} object created with \code{\link[=cff_create]{cff_create()}} using the corresponding validation \href{https://github.com/citation-file-format/citation-file-format/blob/main/schema.json}{schema.json}. } @@ -52,6 +52,7 @@ try(cff_validate(system.file("CITATION", package = "cffr"))) Other core functions of \CRANpkg{cffr}: \code{\link{cff}()}, -\code{\link{cff_create}()} +\code{\link{cff_create}()}, +\code{\link{cff_modify}()} } \concept{core} diff --git a/man/cff_write.Rd b/man/cff_write.Rd index bf4a6daf..2dd826b1 100644 --- a/man/cff_write.Rd +++ b/man/cff_write.Rd @@ -59,7 +59,7 @@ you would need when developing a package}. This function writes out a \code{CITATION.cff} file for a given package. This function is basically a wrapper around \code{\link[=cff_create]{cff_create()}} to both create the -\code{\link[=cff-class]{cff}} object and writes it out to a YAML-formatted file in +\code{\link{cff}} object and writes it out to a YAML-formatted file in one command. } \details{ diff --git a/man/cff_write_misc.Rd b/man/cff_write_misc.Rd index 37030074..68eccee6 100644 --- a/man/cff_write_misc.Rd +++ b/man/cff_write_misc.Rd @@ -23,7 +23,7 @@ cff_write_citation( ) } \arguments{ -\item{x}{A \code{\link[=bibentry]{bibentry}} or a \code{\link[=cff-class]{cff}} object.} +\item{x}{A \code{\link[=bibentry]{bibentry}} or a \code{\link{cff}} object.} \item{file}{Name of the file to be created. If \code{NULL} it would display the lines to be written.} diff --git a/man/deprecated_cff_from_bib.Rd b/man/deprecated_cff_from_bib.Rd index 7a95e177..24063661 100644 --- a/man/deprecated_cff_from_bib.Rd +++ b/man/deprecated_cff_from_bib.Rd @@ -2,13 +2,13 @@ % Please edit documentation in R/deprecated.R \name{cff_from_bibtex} \alias{cff_from_bibtex} -\title{Previous API: Create a \code{\link[=cff-class]{cff}} object from BibTeX entries} +\title{Previous API: Create a \code{\link{cff}} object from BibTeX entries} \usage{ cff_from_bibtex(x, encoding = "UTF-8", ...) } \arguments{ \item{x}{The source that would be used for generating the -\code{\link[=cff-class]{cff}} object. Must be \code{character} object indicating either: +\code{\link{cff}} object. Must be \code{character} object indicating either: \itemize{ \item The path to a BibTeX file. \item A vector of characters with the full BibTeX string. See \strong{Examples}. diff --git a/man/deprecated_cff_person.Rd b/man/deprecated_cff_person.Rd index 1cae6865..cd4f1140 100644 --- a/man/deprecated_cff_person.Rd +++ b/man/deprecated_cff_person.Rd @@ -3,7 +3,7 @@ \name{cff_parse_person} \alias{cff_parse_person} \alias{cff_parse_person_bibtex} -\title{Previous API: Parse a \code{person} to \code{\link[=cff-class]{cff}}} +\title{Previous API: Parse a \code{person} to \code{\link{cff}}} \usage{ cff_parse_person(person) diff --git a/man/deprecated_write.Rd b/man/deprecated_write.Rd index 1c3a7347..645d0b02 100644 --- a/man/deprecated_write.Rd +++ b/man/deprecated_write.Rd @@ -16,7 +16,7 @@ write_citation( ) } \arguments{ -\item{x}{A \code{\link[=bibentry]{bibentry}} or a \code{\link[=cff-class]{cff}} object.} +\item{x}{A \code{\link[=bibentry]{bibentry}} or a \code{\link{cff}} object.} \item{file}{Name of the file to be created. If \code{NULL} it would display the lines to be written.} diff --git a/pkgdown/_pkgdown.yml b/pkgdown/_pkgdown.yml index ca293aec..8d2d778f 100644 --- a/pkgdown/_pkgdown.yml +++ b/pkgdown/_pkgdown.yml @@ -14,48 +14,48 @@ home: description: >- Utilities to generate and validate CFF files with R. -reference: - - title: The whole game - - subtitle: The function - desc: >- - The main function of the package and likely to be the only one you would - need. - contents: cff_write - - subtitle: Other core functions - desc: 'Create, modify and write and `cff` objects.' - contents: has_concept("core") - - subtitle: The `cff` class - desc: Brief introduction to the `cff` class and S3 Methods available. - contents: - - cff-class - - has_concept("s3method") - - subtitle: Citation File Format schema - desc: >- - These functions provides lists of valid keys and fields as defined by the - CFF schema.json **(v1.2.0)**. - contents: has_concept("schemas") - - title: Continuous Integration - contents: has_concept("git") - - title: Additional features - - subtitle: Read and write files - desc: >- - Read and write local files on different formats (CFF files, BibTeX, - etc.). - contents: - - has_concept("reading") - - has_concept("writing") - - subtitle: Coercing objects - desc: >- - Coerce **R** objects into different classes (`cff`, `person`, `bibentry`) - and more: - contents: has_concept("coercing") - - subtitle: BibTeX helpers - desc: Functions that works with BibTeX markup language. - contents: has_concept("bibtex") - - title: Datasets - contents: has_concept("datasets") - - title: About the package - contents: cffr-package +# reference: +# - title: The whole game +# - subtitle: The function +# desc: >- +# The main function of the package and likely to be the only one you would +# need. +# contents: cff_write +# - subtitle: Other core functions +# desc: 'Create, modify and write and `cff` objects.' +# contents: has_concept("core") +# - subtitle: The `cff` class +# desc: Brief introduction to the `cff` class and S3 Methods available. +# contents: +# - cff-class +# - has_concept("s3method") +# - subtitle: Citation File Format schema +# desc: >- +# These functions provides lists of valid keys and fields as defined by the +# CFF schema.json **(v1.2.0)**. +# contents: has_concept("schemas") +# - title: Continuous Integration +# contents: has_concept("git") +# - title: Additional features +# - subtitle: Read and write files +# desc: >- +# Read and write local files on different formats (CFF files, BibTeX, +# etc.). +# contents: +# - has_concept("reading") +# - has_concept("writing") +# - subtitle: Coercing objects +# desc: >- +# Coerce **R** objects into different classes (`cff`, `person`, `bibentry`) +# and more: +# contents: has_concept("coercing") +# - subtitle: BibTeX helpers +# desc: Functions that works with BibTeX markup language. +# contents: has_concept("bibtex") +# - title: Datasets +# contents: has_concept("datasets") +# - title: About the package +# contents: cffr-package navbar: structure: diff --git a/tests/testthat/_snaps/cff.md b/tests/testthat/_snaps/cff.md index 86490b28..ee022981 100644 --- a/tests/testthat/_snaps/cff.md +++ b/tests/testthat/_snaps/cff.md @@ -1747,7 +1747,7 @@ Warning: The `path` argument of `cff()` is deprecated as of cffr 1.0.0. i Argument ignored. - Error in `cff()`: + Error in `validate_extra_keys()`: ! Elements in `...` should be named. --- diff --git a/vignettes/cffr.Rmd b/vignettes/cffr.Rmd index 9ebf198a..bbed3bd7 100644 --- a/vignettes/cffr.Rmd +++ b/vignettes/cffr.Rmd @@ -1,5 +1,5 @@ --- -title: "Manipulating Citations with **cffr**" +title: "Manipulating Citations with cffr" description: > Learn how to modify `cff` objects. output: @@ -8,7 +8,7 @@ output: bibliography: REFERENCES.bib link-citations: yes vignette: > - %\VignetteIndexEntry{Manipulating Citations with **cffr**} + %\VignetteIndexEntry{Manipulating Citations with cffr} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- @@ -35,7 +35,7 @@ information of the following files: [Citation File Format (CFF](https://citation-file-format.github.io/)) [@druskat_citation_2021] (v1.2.0) are plain text files with human- and -machine-readable citation information for software (and datasets). Code +machine-readable citation information for software (and data sets). Code developers can include them in their repositories to let others know how to correctly cite their software. @@ -73,6 +73,7 @@ cff_write() Under the hood, `cff_write()` performs the following tasks: - It extracts the metadata using `cff_create()`. +- Optionally modifies it with `cff_modify()`. - Writes a `CITATION.cff` file using `yaml::write_yaml()`. - Validates the result using `cff_validate()`. @@ -89,10 +90,7 @@ would add or modify contents of it. ### Adding new fields ```{r newfields} -newobject <- cff_create(cff()) - -# For modifying your auto-generated object, run this line instead: -# newoobject <- cff_create() +newobject <- cff() newobject ``` @@ -106,20 +104,18 @@ cff_schema_keys() ``` In this case, we are going to add `url`, `version` and `repository`. We would -also overwrite the `title` key. We just need to create a list and pass it to the -`keys` argument of `cff_create()`: +also overwrite the `title` key. We just need to add those parameters to +`cff_modify()`: ```{r modify} -newkeys <- list( - "url" = "https://ropensci.org/", - "version" = "0.0.1", - "repository" = "https://github.com/ropensci/cffr", +modobject <- cff_modify(newobject, + url = "https://ropensci.org/", + version = "0.0.1", + repository = "https://github.com/ropensci/cffr", # If the field is already present, it would be overridden title = "Modifying a 'cff' object" ) -modobject <- cff_create(newobject, keys = newkeys) - modobject # Validate against the schema @@ -127,11 +123,11 @@ modobject cff_validate(modobject) ``` -### Parsing persons and citations +### Persons and references -**cffr** provides two functions that parse `person` objects and `bibentry` -objects (See `?person` and `?bibentry`). These objects are included in the -**utils** package and are a core part of the metadata of any **R** package. +**cffr** provides two functions that convert `person` and `bibentry` objects +(see `?person` and `?bibentry`) according to the [Citation File Format +schema](https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md). Following the previous example, we are going to add a new author first. For doing that, we need first to extract the current author of the package and @@ -157,7 +153,7 @@ chiquito <- person("Gregorio", chiquito -# Parse it +# To cff chiquito_parsed <- as_cff_person(chiquito) chiquito_parsed @@ -167,20 +163,16 @@ chiquito_parsed newauthors <- c(modobject$authors, chiquito_parsed) newauthors -newauthorobject <- cff_create(modobject, keys = list(authors = newauthors)) +newauthorobject <- cff_modify(modobject, authors = newauthors) newauthorobject cff_validate(newauthorobject) ``` -Now, we may want to add `references` to our data. **cffr** supports two types of -references: - -- References created with `bibentry()` -- References extracted from packages using `citation()` - -On the following example, we would add two references, one of each type: +Now, we may want to add `references` to our data. On the following example, we +would add two references, one created with `bibentry()` and another with +`citation()`: ```{r parsingcits} # Valid reference keys @@ -188,11 +180,7 @@ On the following example, we would add two references, one of each type: cff_schema_definitions_refs() # Auto parsed from another R package -base_r <- as_cff(citation("base")) - -base_r - -# Create with bibentry +base_r <- citation("base") bib <- bibentry("Book", title = "This is a book", @@ -201,24 +189,23 @@ bib <- bibentry("Book", publisher = "McGraw Hill", volume = 2 ) -bib -# Now parse it +refs <- c(base_r, bib) -bookparsed <- as_cff(bib) +refs -bookparsed -``` +# Now to cff -Now the process is similar to the example with `person`: we append both -references (as lists) and add them to our object: +refs_cff <- as_cff(refs) -```{r references} -refkeys <- list(references = c(list(base_r), list(bookparsed))) +refs_cff +``` -refkeys +Now the process is similar to the example with `person`: we just modify our +`cff` object: -finalobject <- cff_create(newauthorobject, keys = refkeys) +```{r references} +finalobject <- cff_modify(newauthorobject, references = refs_cff) finalobject @@ -235,13 +222,13 @@ tmp <- tempfile(fileext = ".cff") see_res <- cff_write(finalobject, outfile = tmp) -see_res +cat(readLines(tmp), sep = "\n") ``` -And finally we can read our created `CITATION.cff` file using `cff()`: +And finally we can read our created `CITATION.cff` file using `cff_read()`: ```{r read} -reading <- cff(tmp) +reading <- cff_read(tmp) reading ``` @@ -257,7 +244,7 @@ allkeys <- list( # If the field is already present, it would be overridden title = "Modifying a 'cff' object", authors = newauthors, - references = c(list(base_r), list(bookparsed)) + references = refs_cff ) tmp2 <- tempfile(fileext = ".cff") @@ -267,4 +254,10 @@ res <- cff_write(cff(), outfile = tmp2, keys = allkeys) res ``` +```{r include=FALSE} +# Clean temps +unlink(tmp) +unlink(tmp2) +``` + ## References From 8c751c90930309ac6085c4c64b2ae4044246d1bb Mon Sep 17 00:00:00 2001 From: Diego H Date: Sun, 3 Mar 2024 20:19:23 +0100 Subject: [PATCH 32/32] Finish API --- R/as_bibentry.R | 280 +++++++++--------- R/assertions.R | 19 +- R/cff-methods.R | 4 +- R/utils.R | 43 +++ README.md | 2 +- codemeta.json | 2 +- inst/schemaorg.json | 2 +- pkgdown/_pkgdown.yml | 84 +++--- tests/testthat/_snaps/as_bibentry.md | 31 +- .../testthat/_snaps/cff_write_misc/CITAT_ION | 2 +- .../testthat/_snaps/xtra-check-bibtex-ruby.md | 2 +- tests/testthat/test-as_bibentry.R | 17 +- 12 files changed, 296 insertions(+), 192 deletions(-) diff --git a/R/as_bibentry.R b/R/as_bibentry.R index 88dfc462..3274ad20 100644 --- a/R/as_bibentry.R +++ b/R/as_bibentry.R @@ -118,18 +118,10 @@ as_bibentry <- function(x, } - # Three cases: - # A) Full cff reference object or - # B) Individual reference list - # C) List of references + # Guess case + cff_type <- guess_cff_part(obj) - - # Detect case A - is_full_cff <- "cff-version" %in% names(obj) - # Detect case B - is_single_ref <- "type" %in% names(obj) - - if (is_full_cff) { + if (cff_type == "cff_full") { # Try to generate preferred if not present if (!("preferred-citation" %in% names(obj))) { prefcit <- obj @@ -146,7 +138,7 @@ as_bibentry <- function(x, "references" = obj$references, c(list(obj$`preferred-citation`), obj$references) ) - } else if (is_single_ref) { + } else if (cff_type == "cff_ref") { obj_extract <- list(obj) } else { obj_extract <- obj @@ -158,20 +150,18 @@ as_bibentry <- function(x, return(NULL) } - ref <- lapply(obj_extract, cff_bibtex_parser) + ref <- lapply(obj_extract, make_bibentry) ref <- do.call(c, ref) return(ref) } -cff_bibtex_parser <- function(x) { +make_bibentry <- function(x) { if (is.null(x)) { return(NULL) } - stopifnotcff(x) - # Partially based on ruby parser # https://github.com/citation-file-format/ruby-cff/blob/main/lib/cff/ >> # (cont) formatter/bibtex_formatter.rb @@ -184,79 +174,13 @@ cff_bibtex_parser <- function(x) { # edition institution journal month number pages publisher title volume year # Guess type of entry---- - tobibentry$bibtype <- switch(tolower(x$type), - "article" = "article", - "book" = "book", - "manual" = "manual", - "unpublished" = "unpublished", - "conference" = "inproceedings", - "conference-paper" = "inproceedings", - "proceedings" = "proceedings", - "magazine-article" = "article", - "newspaper-article" = "article", - "pamphlet" = "booklet", - "report" = "techreport", - "thesis" = "mastersthesis", - # We would need to guess - "misc" - ) - # Try guess thesis - ttype <- clean_str(gsub("[[:punct:]]", "", - x$`thesis-type`, - perl = TRUE - )) - - if (!is.null(ttype) && x$type == "thesis") { - if (grepl("Phd", ttype, ignore.case = TRUE)) { - tobibentry$bibtype <- "phdthesis" - } - } - - # Check if it may be an incollection - # Hint: is misc with collection-title and publisher - - if (all( - tobibentry$bibtype == "misc", !is.null(x$`collection-title`), - !is.null(x$publisher), !is.null(x$year) - )) { - tobibentry$bibtype <- "incollection" - } - + tobibentry$bibtype <- guess_bibtype(x) # address---- - # BibTeX 'address' is taken from the publisher (book, others) or the - # conference (inproceedings). - # Set logic: conference > institution > publisher - if (!is.null(x$conference)) { - addr_search <- x$conference - } else if (!is.null(x$institution)) { - addr_search <- x$institution - } else { - addr_search <- x$publisher - } - - - tobibentry$address <- clean_str(paste( - c( - addr_search$address, - addr_search$city, - addr_search$region, - addr_search$country - ), - collapse = ", " - )) - - # As a fallback, use also location - if (is.null(tobibentry$address) && !is.null(x$location)) { - tobibentry$address <- x$location$name - } - - + tobibentry$address <- guess_address(x) # author---- - aut <- x$authors - author <- as.person(aut) - tobibentry$author <- author + tobibentry$author <- as.person(x$authors) # booktitle ---- @@ -268,7 +192,10 @@ cff_bibtex_parser <- function(x) { # Fallback to conference name - if (tobibentry$bibtype == "inproceedings" && is.null(tobibentry$booktitle)) { + if (all( + tobibentry$bibtype == "inproceedings", + is.null(tobibentry$booktitle) + )) { tobibentry$booktitle <- x$conference$name } @@ -280,21 +207,10 @@ cff_bibtex_parser <- function(x) { # editor---- # Same case than authors - editors <- as.person(x$editors) - tobibentry$editor <- editors + tobibentry$editor <- as.person(x$editors) # howpublished---- - - howpublished <- x$medium - - if (!is.null(howpublished)) { - # Capitalize first letter - letts <- unlist(strsplit(howpublished, "|")) - howpublished <- - clean_str(paste0(c(toupper(letts[1]), letts[-1]), collapse = "")) - - tobibentry$howpublished <- howpublished - } + tobibentry$howpublished <- make_howpublised(x) # institution/organization ---- @@ -320,41 +236,6 @@ cff_bibtex_parser <- function(x) { # journal---- tobibentry$journal <- x$journal - # key: First two given of author and year---- - # Bear in mind institutions has only given - # Use the first two authors - aut_sur <- lapply(tobibentry$author[1:2], function(z) { - unz <- unlist(z) - if ("family" %in% names(unz)) { - r <- unz["family"] - return(clean_str(r)) - } - - r <- unz["given"] - return(clean_str(r)) - }) - - - aut_sur <- tolower(paste0(unlist(aut_sur), collapse = "")) - aut_sur <- gsub("\\s*", "", aut_sur) - - # Try hard to remove accents - # First with iconv - aut_sur <- iconv(aut_sur, - from = "UTF-8", to = "ASCII//TRANSLIT", - sub = "?" - ) - - # Next to latex - aut_sur <- encoded_utf_to_latex(aut_sur) - - # Finally keep only a-z letters for key - aut_sur <- gsub("[^_a-z]", "", aut_sur) - - y <- x$year - - tobibentry$key <- paste(c(aut_sur, y), collapse = ":") - # month---- m <- x$month @@ -446,6 +327,10 @@ cff_bibtex_parser <- function(x) { tobibentry$bibtype <- "inbook" } + # key: First two given of author and year---- + tobibentry$key <- make_bibkey(tobibentry) + + # Handle anonymous author---- # If anonymous and not needed, then not use it @@ -525,3 +410,130 @@ cff_bibtex_parser <- function(x) { return(bib) } + + +guess_bibtype <- function(x) { + init_guess <- switch(tolower(x$type), + "article" = "article", + "book" = "book", + "manual" = "manual", + "unpublished" = "unpublished", + "conference" = "inproceedings", + "conference-paper" = "inproceedings", + "proceedings" = "proceedings", + "magazine-article" = "article", + "newspaper-article" = "article", + "pamphlet" = "booklet", + "report" = "techreport", + "thesis" = "mastersthesis", + # We would need to guess + "misc" + ) + + + # Try guess thesis + ttype <- clean_str(gsub("[[:punct:]]", "", + x$`thesis-type`, + perl = TRUE + )) + + if (!is.null(ttype) && x$type == "thesis") { + if (grepl("Phd", ttype, ignore.case = TRUE)) { + init_guess <- "phdthesis" + } + } + + # Check if it may be an incollection + # Hint: is misc with collection-title and publisher + + if (all( + init_guess == "misc", !is.null(x$`collection-title`), + !is.null(x$publisher), !is.null(x$year) + )) { + init_guess <- "incollection" + } + + init_guess +} + +guess_address <- function(x) { + # BibTeX 'address' is taken from the publisher (book, others) or the + # conference (inproceedings). + # Set logic: conference > institution > publisher + if (!is.null(x$conference)) { + addr_search <- x$conference + } else if (!is.null(x$institution)) { + addr_search <- x$institution + } else { + addr_search <- x$publisher + } + + + address <- clean_str(paste( + c( + addr_search$address, + addr_search$city, + addr_search$region, + addr_search$country + ), + collapse = ", " + )) + + # As a fallback, use also location + if (is.null(address) && !is.null(x$location)) { + address <- clean_str(x$location$name) + } + + address +} + + +make_bibkey <- function(tobibentry) { + # Bear in mind institutions has only given + # Use the first two authors + aut_sur <- lapply(tobibentry$author[1:2], function(z) { + unz <- unlist(z) + if ("family" %in% names(unz)) { + r <- unz["family"] + return(clean_str(r)) + } + + r <- unz["given"] + return(clean_str(r)) + }) + + + aut_sur <- tolower(paste0(unlist(aut_sur), collapse = "")) + aut_sur <- gsub("\\s*", "", aut_sur) + + # Try hard to remove accents + # First with iconv + aut_sur <- iconv(aut_sur, + from = "UTF-8", to = "ASCII//TRANSLIT", + sub = "?" + ) + + # Next to latex + aut_sur <- encoded_utf_to_latex(aut_sur) + + # Finally keep only a-z letters for key + aut_sur <- gsub("[^_a-z]", "", aut_sur) + + y <- tobibentry$year + + key <- paste(c(aut_sur, y), collapse = ":") + key +} + +make_howpublised <- function(x) { + howpublished <- x$medium + + if (!is.null(howpublished)) { + # Capitalize first letter + letts <- unlist(strsplit(howpublished, "|")) + howpublished <- + clean_str(paste0(c(toupper(letts[1]), letts[-1]), collapse = "")) + } + + clean_str(howpublished) +} diff --git a/R/assertions.R b/R/assertions.R index ddf705f7..245dc9e1 100644 --- a/R/assertions.R +++ b/R/assertions.R @@ -44,15 +44,11 @@ is_substring <- function(x, sub) { } } -#' Check if a object is cff +#' Check if a object is `cff` #' @param x object to be evaluated #' @noRd is_cff <- function(x) { - if (inherits(x, "cff")) { - return(TRUE) - } else { - return(FALSE) - } + inherits(x, "cff") } #' Check if a object is cff file @@ -71,7 +67,7 @@ is_cff_file <- function(x) { return(TRUE) } -#' Check if a object is cff +#' Check if an url is from GitHub #' @param x object to be evaluated #' @noRd is_github <- function(x) { @@ -83,7 +79,7 @@ is_github <- function(x) { return(res) } -#' Error if it is not a cff file +#' Error if it is not a `cff` file or object #' @param x file to be evaluated #' @noRd stopifnotcff <- function(x) { @@ -114,3 +110,10 @@ stopifnotexists <- function(x) { } return(invisible(NULL)) } + +#' Check if `x` has names +#' @param x object to be evaluated +#' @noRd +is_named <- function(x) { + !is.null(names(x)) +} diff --git a/R/cff-methods.R b/R/cff-methods.R index a3cd772c..d2c80e05 100644 --- a/R/cff-methods.R +++ b/R/cff-methods.R @@ -24,6 +24,7 @@ c.cff <- function(..., recursive = FALSE) { } +# nolint start #' Coerce to a Data Frame #' #' @noRd @@ -47,6 +48,7 @@ as.data.frame.cff <- function(x, row.names = NULL, optional = FALSE, ...) { return(the_df) } +# nolint end #' @rdname as_cff_person #' @name as.person.cff @@ -134,7 +136,7 @@ toBibtex.cff <- function(object, ..., class(object) <- c("cff", "list") } - bib_list <- lapply(object, cff_bibtex_parser) + bib_list <- lapply(object, make_bibentry) biblist_cff <- do.call(c, bib_list) } toBibtex(biblist_cff, ...) diff --git a/R/utils.R b/R/utils.R index 5689e8e7..1125fcf4 100644 --- a/R/utils.R +++ b/R/utils.R @@ -167,3 +167,46 @@ fuzzy_keys <- function(keys) { return(new_keys) } + +guess_cff_named_part <- function(x) { + nms <- names(x) + # Search for names + is_person <- any(grepl("^name$|family|given|particle", nms)) + if (is_person) { + return("cff_pers") + } + + # VALID full cff file + is_full <- any(grepl("cff-version|message", nms)) + if (is_full) { + return("cff_full") + } + + # Reference + is_ref <- any(grepl("title|type", nms)) + if (is_ref) { + return("cff_ref") + } + + # Else + return("unclear") +} + + +guess_cff_part <- function(x) { + named <- is_named(x) + if (named) { + return(guess_cff_named_part(x)) + } + + # Look to first element + guess <- guess_cff_named_part(x[[1]]) + + fin <- switch(guess, + "cff_pers" = "cff_pers_list", + "cff_ref" = "cff_ref_list", + "unclear" + ) + + fin +} diff --git a/README.md b/README.md index 74cf4226..2b438aed 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ file and the `CITATION` file (if present) of your package. Note that **cffr** works best if your package pass `R CMD check/devtools::check()`. -As per 2024-03-02 there are at least 306 repos on GitHub using **cffr**. +As per 2024-03-03 there are at least 306 repos on GitHub using **cffr**. [Check them out here](https://github.com/search?q=cffr%20path%3A**%2FCITATION.cff&type=code). diff --git a/codemeta.json b/codemeta.json index 974131e4..8ba3d173 100644 --- a/codemeta.json +++ b/codemeta.json @@ -200,7 +200,7 @@ }, "isPartOf": "https://ropensci.org", "keywords": ["attribution", "citation", "credit", "citation-files", "cff", "metadata", "r", "r-package", "citation-file-format", "rstats", "ropensci", "cran"], - "fileSize": "954.018KB", + "fileSize": "961.96KB", "citation": [ { "@type": "ScholarlyArticle", diff --git a/inst/schemaorg.json b/inst/schemaorg.json index 8056af78..dec7a9c6 100644 --- a/inst/schemaorg.json +++ b/inst/schemaorg.json @@ -26,6 +26,6 @@ "name": "Comprehensive R Archive Network (CRAN)", "url": "https://cran.r-project.org" }, - "runtimePlatform": "R version 4.3.2 (2023-10-31)", + "runtimePlatform": "R version 4.3.2 (2023-10-31 ucrt)", "version": "0.5.0.9000" } diff --git a/pkgdown/_pkgdown.yml b/pkgdown/_pkgdown.yml index 8d2d778f..ca293aec 100644 --- a/pkgdown/_pkgdown.yml +++ b/pkgdown/_pkgdown.yml @@ -14,48 +14,48 @@ home: description: >- Utilities to generate and validate CFF files with R. -# reference: -# - title: The whole game -# - subtitle: The function -# desc: >- -# The main function of the package and likely to be the only one you would -# need. -# contents: cff_write -# - subtitle: Other core functions -# desc: 'Create, modify and write and `cff` objects.' -# contents: has_concept("core") -# - subtitle: The `cff` class -# desc: Brief introduction to the `cff` class and S3 Methods available. -# contents: -# - cff-class -# - has_concept("s3method") -# - subtitle: Citation File Format schema -# desc: >- -# These functions provides lists of valid keys and fields as defined by the -# CFF schema.json **(v1.2.0)**. -# contents: has_concept("schemas") -# - title: Continuous Integration -# contents: has_concept("git") -# - title: Additional features -# - subtitle: Read and write files -# desc: >- -# Read and write local files on different formats (CFF files, BibTeX, -# etc.). -# contents: -# - has_concept("reading") -# - has_concept("writing") -# - subtitle: Coercing objects -# desc: >- -# Coerce **R** objects into different classes (`cff`, `person`, `bibentry`) -# and more: -# contents: has_concept("coercing") -# - subtitle: BibTeX helpers -# desc: Functions that works with BibTeX markup language. -# contents: has_concept("bibtex") -# - title: Datasets -# contents: has_concept("datasets") -# - title: About the package -# contents: cffr-package +reference: + - title: The whole game + - subtitle: The function + desc: >- + The main function of the package and likely to be the only one you would + need. + contents: cff_write + - subtitle: Other core functions + desc: 'Create, modify and write and `cff` objects.' + contents: has_concept("core") + - subtitle: The `cff` class + desc: Brief introduction to the `cff` class and S3 Methods available. + contents: + - cff-class + - has_concept("s3method") + - subtitle: Citation File Format schema + desc: >- + These functions provides lists of valid keys and fields as defined by the + CFF schema.json **(v1.2.0)**. + contents: has_concept("schemas") + - title: Continuous Integration + contents: has_concept("git") + - title: Additional features + - subtitle: Read and write files + desc: >- + Read and write local files on different formats (CFF files, BibTeX, + etc.). + contents: + - has_concept("reading") + - has_concept("writing") + - subtitle: Coercing objects + desc: >- + Coerce **R** objects into different classes (`cff`, `person`, `bibentry`) + and more: + contents: has_concept("coercing") + - subtitle: BibTeX helpers + desc: Functions that works with BibTeX markup language. + contents: has_concept("bibtex") + - title: Datasets + contents: has_concept("datasets") + - title: About the package + contents: cffr-package navbar: structure: diff --git a/tests/testthat/_snaps/as_bibentry.md b/tests/testthat/_snaps/as_bibentry.md index 063a6a62..6bb63ce4 100644 --- a/tests/testthat/_snaps/as_bibentry.md +++ b/tests/testthat/_snaps/as_bibentry.md @@ -224,6 +224,23 @@ organization = {IJCAI}, } +--- + + Code + toBibtex(bib) + Output + @InProceedings{aberdeenbayer:1999, + title = {Implementing Practical Dialogue Systems with the DARPA Communicator Architecture}, + author = {John Aberdeen and Samuel Bayer and Sasha Caskey and Laurie Damianos and Alan Goldschen and Lynette Hirschman and Dan Loehr and Hugo Trapper}, + year = {1999}, + booktitle = {I Am a conference}, + publisher = {International Joint Conference on Artificial Intelligence}, + address = {Murray Hill, New Jersey}, + editor = {Jan Alexandersson}, + pages = {81--86}, + organization = {IJCAI}, + } + # Manual to bibtex Code @@ -392,6 +409,18 @@ note = {Unpublished MS, Computer Science Department, University of Pittsburgh.}, } +--- + + Code + toBibtex(bib) + Output + @Unpublished{aronisprovost:1959, + title = {Efficiently Constructing Relational Features from Background}, + author = {John M. Aronis and Foster J. Provost}, + year = {1959}, + note = {Extracted with cffr R package}, + } + # particle names Code @@ -616,7 +645,7 @@ Code toBibtex(parsed) Output - @Misc{doe, + @Misc{doe:2020, title = {My Research Software}, author = {John Doe}, year = {2020}, diff --git a/tests/testthat/_snaps/cff_write_misc/CITAT_ION b/tests/testthat/_snaps/cff_write_misc/CITAT_ION index 7473bbd2..e54d5050 100644 --- a/tests/testthat/_snaps/cff_write_misc/CITAT_ION +++ b/tests/testthat/_snaps/cff_write_misc/CITAT_ION @@ -5,7 +5,7 @@ bibentry(bibtype = "Misc", family = "PĂ©rez")) bibentry(bibtype = "Misc", - key = "basic", + key = "basic:1999", title = "basicdescdate: A Basic Description with Date", author = person(given = "Marc", family = "Basic", diff --git a/tests/testthat/_snaps/xtra-check-bibtex-ruby.md b/tests/testthat/_snaps/xtra-check-bibtex-ruby.md index c80cd5ed..4e11e51f 100644 --- a/tests/testthat/_snaps/xtra-check-bibtex-ruby.md +++ b/tests/testthat/_snaps/xtra-check-bibtex-ruby.md @@ -102,7 +102,7 @@ Code toBibtex(bib) Output - @Article{hartmannwong, + @Article{hartmannwong:2020, title = {An image-based data-driven analysis of cellular architecture in a developing tissue}, author = {Jonas Hartmann and Mie Wong and Elisa Gallo and Darren Gilmour}, year = {2020}, diff --git a/tests/testthat/test-as_bibentry.R b/tests/testthat/test-as_bibentry.R index 1d7efcd5..0baf3dc6 100644 --- a/tests/testthat/test-as_bibentry.R +++ b/tests/testthat/test-as_bibentry.R @@ -1,3 +1,4 @@ +# Test Bibtex ---- test_that("Article to bibtex", { bib <- bibentry("Article", key = "knuth:1984", @@ -137,6 +138,12 @@ test_that("InProceedings to bibtex", { bibparsed <- as_cff(bib) bib <- as_bibentry(bibparsed) expect_snapshot(toBibtex(bib)) + + # If we remove collection title use conference + bibparsed[[1]]$`collection-title` <- NULL + bibparsed[[1]]$conference$name <- "I Am a conference" + bib <- as_bibentry(bibparsed) + expect_snapshot(toBibtex(bib)) }) @@ -244,8 +251,14 @@ test_that("Unpublished to bibtex", { bibparsed <- as_cff(bib) bib <- as_bibentry(bibparsed) expect_snapshot(toBibtex(bib)) + + # With custom note + bibparsed[[1]]$notes <- NULL + bib <- as_bibentry(bibparsed) + expect_snapshot(toBibtex(bib)) }) +# Other testers ---- test_that("particle names", { bib <- bibentry("Book", @@ -486,5 +499,7 @@ test_that("Corrupt entry", { }) test_that("Parser return nulls", { - expect_null(cff_bibtex_parser(NULL)) + expect_null(make_bibentry(NULL)) }) + +# Classes ----