From e2e7a52df3b7f44980639dce9c9c03a89e18977f Mon Sep 17 00:00:00 2001 From: Jon Harmon Date: Sat, 16 May 2026 10:15:29 -0500 Subject: [PATCH 1/3] Prepare to auto-document internal functions --- R/aaa-shared_params.R | 1 + R/as_bk_data.R | 3 +- R/beekeeper-package.R | 6 +- R/generate_pkg-paths.R | 98 +++++++++--------- R/generate_pkg-security.R | 83 +++++++++------ R/generate_pkg-setup.R | 16 +-- R/use_beekeeper.R | 21 ++-- R/utils.R | 126 +++++++++++++++++++++-- inst/templates/paths.R | 4 +- man/as_bk_data.Rd | 3 +- man/dot-collapse_comma.Rd | 18 ++++ man/dot-collapse_comma_newline.Rd | 18 ++++ man/dot-collapse_comma_self_equal.Rd | 15 +++ man/dot-collapse_quote_comma.Rd | 18 ++++ man/dot-flatten_df.Rd | 20 ++++ man/dot-shared-params.Rd | 2 + man/op-lengthless-default.Rd | 28 +++++ man/op-no-char-default.Rd | 37 +++++++ man/op-null-continuation.Rd | 28 +++++ man/op-null-default.Rd | 28 +++++ tests/testthat/test-generate_pkg-paths.R | 4 +- tests/testthat/test-utils.R | 24 ++++- 22 files changed, 484 insertions(+), 117 deletions(-) create mode 100644 man/dot-collapse_comma.Rd create mode 100644 man/dot-collapse_comma_newline.Rd create mode 100644 man/dot-collapse_comma_self_equal.Rd create mode 100644 man/dot-collapse_quote_comma.Rd create mode 100644 man/dot-flatten_df.Rd create mode 100644 man/op-lengthless-default.Rd create mode 100644 man/op-no-char-default.Rd create mode 100644 man/op-null-continuation.Rd create mode 100644 man/op-null-default.Rd diff --git a/R/aaa-shared_params.R b/R/aaa-shared_params.R index 04d4908..0ea641a 100644 --- a/R/aaa-shared_params.R +++ b/R/aaa-shared_params.R @@ -16,6 +16,7 @@ #' files. #' @param rapid_file (`character(1)` or `fs_path`) The path to the R API #' definition (rapid) file. +#' @param to_collapse (`character`) The character vector to collapse. #' #' @name .shared-params #' @keywords internal diff --git a/R/as_bk_data.R b/R/as_bk_data.R index c9e0ac7..44a10d2 100644 --- a/R/as_bk_data.R +++ b/R/as_bk_data.R @@ -3,8 +3,7 @@ #' Convert `rapid` objects to lists of properties to use in beekeeper templates. #' #' @inheritParams rlang::args_dots_empty -#' @param x The object to coerce. Currently supports conversion of -#' [rapid::class_security_schemes()] objects. +#' @param x The object to coerce. #' #' @returns A list. #' @keywords internal diff --git a/R/beekeeper-package.R b/R/beekeeper-package.R index b29f37b..ea74e1f 100644 --- a/R/beekeeper-package.R +++ b/R/beekeeper-package.R @@ -1,11 +1,12 @@ #' @keywords internal "_PACKAGE" +## Force installation: nectar, testthat +## S7 classes: rapid, S7 +## Use as pronoun: rlang::.data ## usethis namespace: start -## Force installation #' @importFrom nectar req_prepare #' @importFrom testthat test_that -## S7 classes #' @importFrom rapid class_api_key_security_scheme #' @importFrom rapid class_paths #' @importFrom rapid class_security_scheme_details @@ -13,7 +14,6 @@ #' @importFrom S7 class_any #' @importFrom S7 class_data.frame #' @importFrom S7 class_list -## Use as pronoun #' @importFrom rlang .data ## usethis namespace: end NULL diff --git a/R/generate_pkg-paths.R b/R/generate_pkg-paths.R index e5f041b..8f816dc 100644 --- a/R/generate_pkg-paths.R +++ b/R/generate_pkg-paths.R @@ -30,34 +30,42 @@ S7::method(as_bk_data, class_paths) <- function(x) { if (!length(x)) { return(list()) } - paths_df <- .paths_to_clean_df(x) - result <- purrr::pmap(paths_df, .path_row_to_list) - names(result) <- paths_df$operation_id + operations_df <- .paths_to_clean_df(x) + result <- purrr::pmap(operations_df, .path_row_to_list) + names(result) <- operations_df$operation_id result } -.paths_to_clean_df <- function(x) { - x <- tibble::as_tibble(x) |> +.paths_to_clean_df <- function(paths) { + operations_df <- tibble::as_tibble(paths) |> tidyr::unnest("operations") - if (length(x$deprecated)) { - x <- x[!x$deprecated, ] + if (length(operations_df$deprecated)) { + operations_df <- operations_df[!operations_df$deprecated, ] } - x$deprecated <- NULL - x$tags <- .paths_fill_tags(x$tags) - x$operation_id <- .paths_fill_operation_id( - x$operation_id, - x$endpoint, - x$operation + operations_df$deprecated <- NULL + operations_df$tags <- .paths_fill_tags(operations_df$tags) + operations_df$operation_id <- .paths_fill_operation_id( + operations_df$operation_id, + operations_df$endpoint, + operations_df$operation ) - x$summary <- .paths_fill_summary( - x$summary, - x$endpoint, - x$operation + operations_df$operation_summary <- .paths_fill_summary( + operations_df$summary, + operations_df$endpoint, + operations_df$operation ) - x$description <- .paths_fill_descriptions(x$description, x$summary) + operations_df$summary <- NULL + operations_df$operation_description <- .paths_fill_descriptions( + operations_df$description, + operations_df$operation_summary + ) + operations_df$description <- NULL # TODO: Deal with x$global_parameters if present - x$parameters <- purrr::map(x$parameters, .prepare_params_df) - return(x) + operations_df$parameters <- purrr::map( + operations_df$parameters, + .prepare_params_df + ) + return(operations_df) } ### fill data ------------------------------------------------------------------ @@ -68,25 +76,27 @@ S7::method(as_bk_data, class_paths) <- function(x) { return(.to_snake(tags)) } -.paths_fill_operation_id <- function(operation_id, endpoint, method) { - .coalesce( - .to_snake(operation_id), - glue::glue("{method}_{.to_snake(endpoint)}") - ) +.paths_fill_operation_id <- function(operation_ids, endpoints, methods) { + .to_snake(operation_ids) %|% glue::glue("{methods}_{.to_snake(endpoints)}") } -.paths_fill_summary <- function(summary, endpoint, method) { - endpoint_spaced <- stringr::str_replace_all(.to_snake(endpoint), "_", " ") - .coalesce( - stringr::str_squish(summary), - stringr::str_to_sentence(glue::glue("{method} {endpoint_spaced}")) - ) +.paths_fill_summary <- function(operation_summaries, endpoints, methods) { + endpoints_spaced <- stringr::str_replace_all(.to_snake(endpoints), "_", " ") + stringr::str_squish(operation_summaries) %|% + stringr::str_to_sentence(glue::glue("{methods} {endpoints_spaced}")) } -.paths_fill_descriptions <- function(descriptions, summaries) { - descriptions[is.na(descriptions)] <- summaries[is.na(descriptions)] - descriptions[is.na(descriptions)] <- "" - return(stringr::str_squish(descriptions)) +.paths_fill_descriptions <- function( + operation_descriptions, + operation_summaries +) { + operation_descriptions[is.na( + operation_descriptions + )] <- operation_summaries[is.na( + operation_descriptions + )] + operation_descriptions[is.na(operation_descriptions)] <- "" + return(stringr::str_squish(operation_descriptions)) } ### create template data ------------------------------------------------------- @@ -95,8 +105,8 @@ S7::method(as_bk_data, class_paths) <- function(x) { operation_id, endpoint, operation, - summary, - description, + operation_summary, + operation_description, tags, parameters, ... @@ -106,8 +116,8 @@ S7::method(as_bk_data, class_paths) <- function(x) { tag = tags, path = .path_as_arg(endpoint, parameters), method = operation, - summary = summary, - description = description, + operation_summary = operation_summary, + operation_description = operation_description, params = .params_to_list(parameters), params_query_raw = .extract_params_by_location(parameters, "query"), params_header_raw = .extract_params_by_location(parameters, "header"), @@ -219,10 +229,6 @@ S7::method(as_bk_data, class_paths) <- function(x) { return(glue::glue('c("{path}", {params})')) } -.collapse_comma_self_equal <- function(x) { - .collapse_comma(glue::glue("{x} = {x}")) -} - # generate files --------------------------------------------------------------- .generate_paths_files <- function( @@ -275,11 +281,11 @@ S7::method(as_bk_data, class_paths) <- function(x) { } .params_to_args <- function(params) { - .collapse_comma(purrr::map_chr(params, "name")) %|"|% character() + .collapse_comma(purrr::map_chr(params, "name")) %|a|% character() } .params_to_named_args <- function(params) { - .collapse_comma_self_equal(purrr::map_chr(params, "name")) %|"|% character() + .collapse_comma_self_equal(purrr::map_chr(params, "name")) %|a|% character() } .remove_security_args <- function(params, security_args) { @@ -292,7 +298,7 @@ S7::method(as_bk_data, class_paths) <- function(x) { } .prep_param_args <- function(params, security_args) { - .collapse_comma_self_equal(setdiff(params, security_args)) %|"|% character() + .collapse_comma_self_equal(setdiff(params, security_args)) %|a|% character() } .params_to_validations <- function(params) { diff --git a/R/generate_pkg-security.R b/R/generate_pkg-security.R index bb461b6..6c960f7 100644 --- a/R/generate_pkg-security.R +++ b/R/generate_pkg-security.R @@ -26,47 +26,55 @@ S7::method(as_bk_data, class_security_schemes) <- function(x) { if (!length(x)) { return(list()) } - security_schemes <- .security_schemes_collect(x) - return(.security_scheme_collection_finalize(security_schemes)) + security_scheme_collection <- .security_schemes_collect(x) + return(.security_scheme_collection_finalize(security_scheme_collection)) } -.security_schemes_collect <- function(x) { +.security_schemes_collect <- function(security_schemes) { purrr::pmap( list( - x@name, - x@details, - x@description %|0|% rep(NA_character_, length(x@name)) + security_schemes@name, + security_schemes@details, + security_schemes@description %|0|% + rep(NA_character_, length(security_schemes@name)) ), .security_scheme_rotate ) } -.security_scheme_rotate <- function(name, details, description) { - security_scheme <- c( +.security_scheme_rotate <- function( + security_scheme_name, + security_scheme_details, + security_scheme_description +) { + security_scheme_list <- c( list( - name = .to_snake(name), - description = description + name = .to_snake(security_scheme_name), + description = security_scheme_description ), - as_bk_data(details) + as_bk_data(security_scheme_details) ) - security_scheme$description <- .security_scheme_description_fill( - description, - security_scheme$type + security_scheme_list$description <- .security_scheme_description_fill( + security_scheme_description, + security_scheme_list$type ) - return(security_scheme) + return(security_scheme_list) } -.security_scheme_description_fill <- function(description, type) { - if (is.na(description)) { +.security_scheme_description_fill <- function( + security_scheme_description, + security_scheme_type +) { + if (is.na(security_scheme_description)) { return( switch( - type, + security_scheme_type, api_key = .security_scheme_description_api_key, NA_character_ ) ) } - return(description) # nocov + return(security_scheme_description) # nocov } .security_scheme_description_api_key <- paste( @@ -75,49 +83,58 @@ S7::method(as_bk_data, class_security_schemes) <- function(x) { "Check the API documentation for details." ) -.security_scheme_collection_finalize <- function(security_schemes) { +.security_scheme_collection_finalize <- function(security_scheme_collection) { security_scheme_data <- c( list( has_security = TRUE, - security_schemes = security_schemes + security_schemes = security_scheme_collection ), - .security_args_compile(security_schemes) + .security_args_compile(security_scheme_collection) ) return(security_scheme_data) } -.security_args_compile <- function(security_schemes) { - security_args <- sort(unique(purrr::map_chr(security_schemes, "arg_name"))) +.security_args_compile <- function(security_scheme_collection) { + security_args <- sort(unique(purrr::map_chr( + security_scheme_collection, + "arg_name" + ))) return(list( security_arg_names = security_args, security_arg_list = .collapse_comma(glue::glue( "{security_args} = {security_args}" )), security_arg_helps = .generate_security_arg_help( - security_schemes, + security_scheme_collection, security_args ), security_arg_nulls = .collapse_comma(glue::glue("{security_args} = NULL")) )) } -.generate_security_arg_help <- function(security_schemes, security_args) { - security_arg_description <- rlang::set_names( - purrr::map_chr(security_schemes, "description"), - purrr::map_chr(security_schemes, "arg_name") +.generate_security_arg_help <- function( + security_scheme_collection, + security_args +) { + security_arg_descriptions <- rlang::set_names( + purrr::map_chr(security_scheme_collection, "description"), + purrr::map_chr(security_scheme_collection, "arg_name") ) - security_arg_description <- unname(security_arg_description[security_args]) + security_arg_descriptions <- unname(security_arg_descriptions[security_args]) return( purrr::map2( - security_arg_description, + security_arg_descriptions, security_args, .security_arg_description_clean ) ) } -.security_arg_description_clean <- function(arg_description, arg_name) { - list(name = arg_name, description = arg_description) +.security_arg_description_clean <- function( + security_arg_description, + security_arg_name +) { + list(name = security_arg_name, description = security_arg_description) } S7::method(as_bk_data, class_security_scheme_details) <- function(x) { diff --git a/R/generate_pkg-setup.R b/R/generate_pkg-setup.R index 40fc050..e5a4eee 100644 --- a/R/generate_pkg-setup.R +++ b/R/generate_pkg-setup.R @@ -62,13 +62,17 @@ read_config <- function(pkg_dir = ".", config_file = "_beekeeper.yml") { .stabilize_config <- function(config) { config$api_title <- stbl::stabilize_character_scalar(config$api_title) config$api_abbr <- stbl::stabilize_character_scalar(config$api_abbr) - config$api_version <- stbl::stabilize_character_scalar(config$api_version) - config$rapid_file <- stbl::stabilize_character_scalar(config$rapid_file) - config$updated_on <- strptime( - config$updated_on, - format = "%Y-%m-%d %H:%M:%S", - tz = "UTC" + config$api_version <- stbl::stabilize_character_scalar( + config$api_version, + allow_null = TRUE ) + config$rapid_file <- stbl::stabilize_character_scalar(config$rapid_file) + config$updated_on <- config$updated_on %&&% + strptime( + config$updated_on, + format = "%Y-%m-%d %H:%M:%S", + tz = "UTC" + ) return(config) } diff --git a/R/use_beekeeper.R b/R/use_beekeeper.R index c6ce189..ea8cdde 100644 --- a/R/use_beekeeper.R +++ b/R/use_beekeeper.R @@ -22,28 +22,33 @@ use_beekeeper <- function( config_file = "_beekeeper.yml", rapid_file = "_beekeeper_rapid.rds" ) { - x <- rapid::as_rapid(x) - rapid_file <- .write_rapid(x, rapid_file) - config_file <- .write_config(x, api_abbr, rapid_file, config_file) + api_definition <- rapid::as_rapid(x) + rapid_file <- .write_rapid(api_definition, rapid_file) + config_file <- .write_config( + api_definition, + api_abbr, + rapid_file, + config_file + ) return(invisible(config_file)) } -.write_rapid <- function(x, rapid_file) { +.write_rapid <- function(api_definition, rapid_file) { rapid_file <- stbl::stabilize_character_scalar(rapid_file) - saveRDS(x, rapid_file) + saveRDS(api_definition, rapid_file) usethis::use_build_ignore(rapid_file) return(rapid_file) } -.write_config <- function(x, api_abbr, rapid_file, config_file) { +.write_config <- function(api_definition, api_abbr, rapid_file, config_file) { config_file <- stbl::stabilize_character_scalar(config_file) update_time <- strptime(Sys.time(), format = "%Y-%m-%d %H:%M:%S", tz = "UTC") yaml::write_yaml( list( - api_title = x@info@title, + api_title = api_definition@info@title, api_abbr = stbl::stabilize_character_scalar(api_abbr), - api_version = x@info@version, + api_version = api_definition@info@version, rapid_file = fs::path_rel(rapid_file, fs::path_dir(config_file)), updated_on = as.character(update_time) ), diff --git a/R/utils.R b/R/utils.R index 7d20f57..e46a301 100644 --- a/R/utils.R +++ b/R/utils.R @@ -1,17 +1,116 @@ -`%||%` <- function(x, y) if (is.null(x)) y else x +#' Default value for `NULL` +#' +#' @param x (`any`) Object to check. +#' @param y (`any`) Default value for `x`. +#' @returns If `x` is `NULL`, will return `y`; otherwise returns `x`. +#' @name op-null-default +#' @family empty operators +#' @keywords internal +`%||%` <- function(x, y) { + if (is.null(x)) y else x +} -`%|0|%` <- function(x, y) if (!length(x)) y else x +# Reexport from base on newer versions of R to avoid conflict messages +if (exists("%||%", envir = baseenv())) { + `%||%` <- get("%||%", envir = baseenv()) +} -`%|"|%` <- function(x, y) if (!nzchar(x)) y else x +#' Default value for non-`NULL` +#' +#' @param x (`any`) Object to check. +#' @param y (`any`) Default value for non-`NULL` `x`. +#' @returns If `x` is `NULL`, will return `x`; otherwise returns `y`. +#' @name op-null-continuation +#' @family empty operators +#' @keywords internal +`%&&%` <- function(x, y) { + if (!is.null(x)) y else x +} -.coalesce <- function(x, y) ifelse(is.na(x), y, x) +#' Default value for length 0 +#' +#' @param x (`any`) Object to check. +#' @param y (`any`) Default value for `x`. +#' @returns If `!length(x)`, will return `x`; otherwise returns `y`. +#' @keywords internal +#' @name op-lengthless-default +#' @family empty operators +#' @keywords internal +`%|0|%` <- function(x, y) { + if (!length(x)) y else x +} -.collapse_comma <- function(x) glue::glue_collapse(x, sep = ", ") +#' Default value for empty strings +#' +#' @param x (`any`) Object to check. +#' @param y (`character`) Default value for `x`. +#' @returns If `!nzchar(x)`, will return `y`; otherwise returns `x`. +#' @keywords internal +#' @name op-no-char-default +#' @family empty operators +#' @keywords internal +`%|a|%` <- function( + x, + y, + x_arg = rlang::caller_arg(x), + call = rlang::caller_env() +) { + if (!is.character(x) || !nzchar(x)) { + stbl::to_chr(y, x_arg = x_arg, call = call) + } else { + x + } +} -.collapse_comma_newline <- function(x) glue::glue_collapse(x, sep = ",\n") +#' Default value for NA elements in vectors +#' +#' @param x (`any`) A vector that may contain `NA` elements. +#' @param y (`any`, coercible to the same class as `x`) A value or vector to +#' replace `NA` elements in `x`. Will be recycled to the same length as `x`. +#' @returns A vector of the same length as `x`, where each `NA` element in `x` +#' is replaced by the corresponding element in `y`. +#' @keywords internal +#' @name op-no-char-default +#' @family empty operators +#' @keywords internal +`%|%` <- function(x, y) { + ifelse(is.na(x), y, x) +} -.collapse_quote_comma <- function(x) { - stringr::str_flatten_comma(paste0('"', x, '"')) +#' Collapse to a comma-separated string +#' +#' @inheritParams .shared-params +#' @returns A length-1, comma-separated glue object. +#' @keywords internal +.collapse_comma <- function(to_collapse) { + glue::glue_collapse(to_collapse, sep = ", ") +} + +#' Collapse to a comma-separated vertical string +#' +#' @inheritParams .shared-params +#' @returns A length-1, comma-separated glue object with newlines. +#' @keywords internal +.collapse_comma_newline <- function(to_collapse) { + glue::glue_collapse(to_collapse, sep = ",\n") +} + +#' Collapse to a comma-separated quoted string +#' +#' @inheritParams .shared-params +#' @returns A length-1, comma-separated glue object. +#' @keywords internal +.collapse_quote_comma <- function(to_collapse) { + stringr::str_flatten_comma(paste0('"', to_collapse, '"')) +} + +#' Collapse to a comma-separated x = x string +#' +#' @inheritParams .shared-params +#' @returns A length-1, comma-separated glue object. +#' @keywords internal +.collapse_comma_self_equal <- function(x) { + .collapse_comma(glue::glue("{x} = {x}")) } .paste0_if <- function(original, test, addition) { @@ -22,8 +121,17 @@ glue::glue(..., .open = "|{", .close = "}|", .envir = .envir) } -.to_snake <- function(x) snakecase::to_snake_case(x, parsing_option = 3) +.to_snake <- function(x) { + snakecase::to_snake_case(x, parsing_option = 3) +} +#' Flatten a data frame or list of data frames +#' +#' @param x (`data.frame`, `list`, or `NULL`) The object to flatten. +#' @returns A single `data.frame`. Lists of data frames are flattened with +#' [purrr::list_rbind()], and `NULL` values are converted to empty data +#' frames. +#' @keywords internal .flatten_df <- S7::new_generic(".flatten_df", dispatch_args = "x") S7::method(.flatten_df, class_data.frame) <- function(x) x diff --git a/inst/templates/paths.R b/inst/templates/paths.R index afb8938..5073515 100644 --- a/inst/templates/paths.R +++ b/inst/templates/paths.R @@ -2,9 +2,9 @@ # element from the source API description. You should carefully review these # functions. -#' {{summary}} +#' {{operation_summary}} #' -#' {{description}} +#' {{operation_description}} #' {{#params}} #' @param {{name}} ({{{class}}}) {{{description}}}{{/params}} #' @inheritParams .shared-params diff --git a/man/as_bk_data.Rd b/man/as_bk_data.Rd index f1a8476..17a1d1c 100644 --- a/man/as_bk_data.Rd +++ b/man/as_bk_data.Rd @@ -7,8 +7,7 @@ as_bk_data(x, ...) } \arguments{ -\item{x}{The object to coerce. Currently supports conversion of -\code{\link[rapid:class_security_schemes]{rapid::class_security_schemes()}} objects.} +\item{x}{The object to coerce.} \item{...}{These dots are for future extensions and must be empty.} } diff --git a/man/dot-collapse_comma.Rd b/man/dot-collapse_comma.Rd new file mode 100644 index 0000000..4cfed87 --- /dev/null +++ b/man/dot-collapse_comma.Rd @@ -0,0 +1,18 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{.collapse_comma} +\alias{.collapse_comma} +\title{Collapse to a comma-separated string} +\usage{ +.collapse_comma(to_collapse) +} +\arguments{ +\item{to_collapse}{(\code{character}) The character vector to collapse.} +} +\value{ +A length-1, comma-separated glue object. +} +\description{ +Collapse to a comma-separated string +} +\keyword{internal} diff --git a/man/dot-collapse_comma_newline.Rd b/man/dot-collapse_comma_newline.Rd new file mode 100644 index 0000000..88f9634 --- /dev/null +++ b/man/dot-collapse_comma_newline.Rd @@ -0,0 +1,18 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{.collapse_comma_newline} +\alias{.collapse_comma_newline} +\title{Collapse to a comma-separated vertical string} +\usage{ +.collapse_comma_newline(to_collapse) +} +\arguments{ +\item{to_collapse}{(\code{character}) The character vector to collapse.} +} +\value{ +A length-1, comma-separated glue object with newlines. +} +\description{ +Collapse to a comma-separated vertical string +} +\keyword{internal} diff --git a/man/dot-collapse_comma_self_equal.Rd b/man/dot-collapse_comma_self_equal.Rd new file mode 100644 index 0000000..12748ed --- /dev/null +++ b/man/dot-collapse_comma_self_equal.Rd @@ -0,0 +1,15 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{.collapse_comma_self_equal} +\alias{.collapse_comma_self_equal} +\title{Collapse to a comma-separated x = x string} +\usage{ +.collapse_comma_self_equal(x) +} +\value{ +A length-1, comma-separated glue object. +} +\description{ +Collapse to a comma-separated x = x string +} +\keyword{internal} diff --git a/man/dot-collapse_quote_comma.Rd b/man/dot-collapse_quote_comma.Rd new file mode 100644 index 0000000..4a10e8c --- /dev/null +++ b/man/dot-collapse_quote_comma.Rd @@ -0,0 +1,18 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{.collapse_quote_comma} +\alias{.collapse_quote_comma} +\title{Collapse to a comma-separated quoted string} +\usage{ +.collapse_quote_comma(to_collapse) +} +\arguments{ +\item{to_collapse}{(\code{character}) The character vector to collapse.} +} +\value{ +A length-1, comma-separated glue object. +} +\description{ +Collapse to a comma-separated quoted string +} +\keyword{internal} diff --git a/man/dot-flatten_df.Rd b/man/dot-flatten_df.Rd new file mode 100644 index 0000000..2d5ad8f --- /dev/null +++ b/man/dot-flatten_df.Rd @@ -0,0 +1,20 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{.flatten_df} +\alias{.flatten_df} +\title{Flatten a data frame or list of data frames} +\usage{ +.flatten_df(x, ...) +} +\arguments{ +\item{x}{(\code{data.frame}, \code{list}, or \code{NULL}) The object to flatten.} +} +\value{ +A single \code{data.frame}. Lists of data frames are flattened with +\code{\link[purrr:list_rbind]{purrr::list_rbind()}}, and \code{NULL} values are converted to empty data +frames. +} +\description{ +Flatten a data frame or list of data frames +} +\keyword{internal} diff --git a/man/dot-shared-params.Rd b/man/dot-shared-params.Rd index 5bf2b64..ac04ee7 100644 --- a/man/dot-shared-params.Rd +++ b/man/dot-shared-params.Rd @@ -23,6 +23,8 @@ files.} \item{rapid_file}{(\code{character(1)} or \code{fs_path}) The path to the R API definition (rapid) file.} + +\item{to_collapse}{(\code{character}) The character vector to collapse.} } \description{ These parameters are used in multiple functions. They are defined here to diff --git a/man/op-lengthless-default.Rd b/man/op-lengthless-default.Rd new file mode 100644 index 0000000..55d19bc --- /dev/null +++ b/man/op-lengthless-default.Rd @@ -0,0 +1,28 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{op-lengthless-default} +\alias{op-lengthless-default} +\alias{\%|0|\%} +\title{Default value for length 0} +\usage{ +x \%|0|\% y +} +\arguments{ +\item{x}{(\code{any}) Object to check.} + +\item{y}{(\code{any}) Default value for \code{x}.} +} +\value{ +If \code{!length(x)}, will return \code{x}; otherwise returns \code{y}. +} +\description{ +Default value for length 0 +} +\seealso{ +Other empty operators: +\code{\link{op-no-char-default}}, +\code{\link{op-null-continuation}}, +\code{\link{op-null-default}} +} +\concept{empty operators} +\keyword{internal} diff --git a/man/op-no-char-default.Rd b/man/op-no-char-default.Rd new file mode 100644 index 0000000..ddea4c7 --- /dev/null +++ b/man/op-no-char-default.Rd @@ -0,0 +1,37 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{op-no-char-default} +\alias{op-no-char-default} +\alias{\%|a|\%} +\alias{\%|\%} +\title{Default value for empty strings} +\usage{ +x \%|a|\% y + +x \%|\% y +} +\arguments{ +\item{x}{(\code{any}) A vector that may contain \code{NA} elements.} + +\item{y}{(\code{any}, coercible to the same class as \code{x}) A value or vector to +replace \code{NA} elements in \code{x}. Will be recycled to the same length as \code{x}.} +} +\value{ +If \code{!nzchar(x)}, will return \code{y}; otherwise returns \code{x}. + +A vector of the same length as \code{x}, where each \code{NA} element in \code{x} +is replaced by the corresponding element in \code{y}. +} +\description{ +Default value for empty strings + +Default value for NA elements in vectors +} +\seealso{ +Other empty operators: +\code{\link{op-lengthless-default}}, +\code{\link{op-null-continuation}}, +\code{\link{op-null-default}} +} +\concept{empty operators} +\keyword{internal} diff --git a/man/op-null-continuation.Rd b/man/op-null-continuation.Rd new file mode 100644 index 0000000..2a8930c --- /dev/null +++ b/man/op-null-continuation.Rd @@ -0,0 +1,28 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{op-null-continuation} +\alias{op-null-continuation} +\alias{\%&&\%} +\title{Default value for non-\code{NULL}} +\usage{ +x \%&&\% y +} +\arguments{ +\item{x}{(\code{any}) Object to check.} + +\item{y}{(\code{any}) Default value for non-\code{NULL} \code{x}.} +} +\value{ +If \code{x} is \code{NULL}, will return \code{x}; otherwise returns \code{y}. +} +\description{ +Default value for non-\code{NULL} +} +\seealso{ +Other empty operators: +\code{\link{op-lengthless-default}}, +\code{\link{op-no-char-default}}, +\code{\link{op-null-default}} +} +\concept{empty operators} +\keyword{internal} diff --git a/man/op-null-default.Rd b/man/op-null-default.Rd new file mode 100644 index 0000000..a5282ef --- /dev/null +++ b/man/op-null-default.Rd @@ -0,0 +1,28 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{op-null-default} +\alias{op-null-default} +\alias{\%||\%} +\title{Default value for \code{NULL}} +\usage{ +x \%||\% y +} +\arguments{ +\item{x}{(\code{any}) Object to check.} + +\item{y}{(\code{any}) Default value for \code{x}.} +} +\value{ +If \code{x} is \code{NULL}, will return \code{y}; otherwise returns \code{x}. +} +\description{ +Default value for \code{NULL} +} +\seealso{ +Other empty operators: +\code{\link{op-lengthless-default}}, +\code{\link{op-no-char-default}}, +\code{\link{op-null-continuation}} +} +\concept{empty operators} +\keyword{internal} diff --git a/tests/testthat/test-generate_pkg-paths.R b/tests/testthat/test-generate_pkg-paths.R index 0757ed7..a219faa 100644 --- a/tests/testthat/test-generate_pkg-paths.R +++ b/tests/testthat/test-generate_pkg-paths.R @@ -272,8 +272,8 @@ test_that(".generate_paths_file() renders header and cookie params correctly (#8 tag = "things", path = '"/things"', method = "get", - summary = "Search things", - description = "Search for things.", + operation_summary = "Search things", + operation_description = "Search for things.", params = list( list( name = "x_auth_token", diff --git a/tests/testthat/test-utils.R b/tests/testthat/test-utils.R index dc30f62..3e32401 100644 --- a/tests/testthat/test-utils.R +++ b/tests/testthat/test-utils.R @@ -1,15 +1,27 @@ +test_that("%||% works (#noissue)", { + expect_identical(NULL %||% "foo", "foo") + expect_identical("foo" %||% "bar", "foo") +}) + +test_that("%&&% works (#noissue)", { + expect_null(NULL %&&% "foo") + expect_identical("foo" %&&% "bar", "bar") +}) + test_that("%|0|% works (#noissue)", { expect_identical(character() %|0|% "foo", "foo") expect_identical("foo" %|0|% "bar", "foo") }) test_that("%|\"|% works (#noissue)", { - expect_identical("" %|"|% "foo", "foo") - expect_identical("foo" %|"|% "bar", "foo") + expect_identical("" %|a|% "foo", "foo") + expect_identical("foo" %|a|% "bar", "foo") }) -test_that(".coalesce() works (#52)", { - expect_identical(.coalesce(c("a", NA), c("x", "y")), c("a", "y")) +test_that("%|% works (#52)", { + expect_identical((c("a", NA) %|% "y"), c("a", "y")) + expect_identical((c(NA, NA) %|% "y"), c("y", "y")) + expect_identical((c(NA, NA) %|% c("x", "y")), c("x", "y")) }) test_that(".collapse_comma() works (#noissue)", { @@ -24,6 +36,10 @@ test_that(".collapse_quote_comma() works (#noissue)", { expect_identical(.collapse_quote_comma(c("a", "b")), '"a", "b"') }) +test_that(".collapse_comma_self_equal() works (#noissue)", { + expect_identical(.collapse_comma_self_equal(c("a", "b")), "a = a, b = b") +}) + test_that(".paste0_if() works (#noissue)", { expect_identical(.paste0_if("x", TRUE, "!"), "x!") expect_identical(.paste0_if("x", FALSE, "!"), "x") From bb6ef4cda562cdaab223f542d1acef3cdadf38e2 Mon Sep 17 00:00:00 2001 From: Jon Harmon Date: Sat, 16 May 2026 13:30:58 -0500 Subject: [PATCH 2/3] Fix coalesce docs --- R/aaa-shared_params.R | 2 ++ R/utils.R | 3 ++- man/dot-shared-params.Rd | 3 +++ man/op-lengthless-default.Rd | 1 + man/op-na-coalesce.Rd | 31 +++++++++++++++++++++++++++++++ man/op-no-char-default.Rd | 19 ++++++++----------- man/op-null-continuation.Rd | 1 + man/op-null-default.Rd | 1 + 8 files changed, 49 insertions(+), 12 deletions(-) create mode 100644 man/op-na-coalesce.Rd diff --git a/R/aaa-shared_params.R b/R/aaa-shared_params.R index 0ea641a..73ef648 100644 --- a/R/aaa-shared_params.R +++ b/R/aaa-shared_params.R @@ -17,6 +17,8 @@ #' @param rapid_file (`character(1)` or `fs_path`) The path to the R API #' definition (rapid) file. #' @param to_collapse (`character`) The character vector to collapse. +#' @param x_arg (`character(1)`) The name of the `x` argument for error +#' messages. #' #' @name .shared-params #' @keywords internal diff --git a/R/utils.R b/R/utils.R index e46a301..4825810 100644 --- a/R/utils.R +++ b/R/utils.R @@ -44,6 +44,7 @@ if (exists("%||%", envir = baseenv())) { #' #' @param x (`any`) Object to check. #' @param y (`character`) Default value for `x`. +#' @inheritParams .shared-params #' @returns If `!nzchar(x)`, will return `y`; otherwise returns `x`. #' @keywords internal #' @name op-no-char-default @@ -70,7 +71,7 @@ if (exists("%||%", envir = baseenv())) { #' @returns A vector of the same length as `x`, where each `NA` element in `x` #' is replaced by the corresponding element in `y`. #' @keywords internal -#' @name op-no-char-default +#' @name op-na-coalesce #' @family empty operators #' @keywords internal `%|%` <- function(x, y) { diff --git a/man/dot-shared-params.Rd b/man/dot-shared-params.Rd index ac04ee7..302ad33 100644 --- a/man/dot-shared-params.Rd +++ b/man/dot-shared-params.Rd @@ -25,6 +25,9 @@ files.} definition (rapid) file.} \item{to_collapse}{(\code{character}) The character vector to collapse.} + +\item{x_arg}{(\code{character(1)}) The name of the \code{x} argument for error +messages.} } \description{ These parameters are used in multiple functions. They are defined here to diff --git a/man/op-lengthless-default.Rd b/man/op-lengthless-default.Rd index 55d19bc..8091178 100644 --- a/man/op-lengthless-default.Rd +++ b/man/op-lengthless-default.Rd @@ -20,6 +20,7 @@ Default value for length 0 } \seealso{ Other empty operators: +\code{\link{op-na-coalesce}}, \code{\link{op-no-char-default}}, \code{\link{op-null-continuation}}, \code{\link{op-null-default}} diff --git a/man/op-na-coalesce.Rd b/man/op-na-coalesce.Rd new file mode 100644 index 0000000..f64e065 --- /dev/null +++ b/man/op-na-coalesce.Rd @@ -0,0 +1,31 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{op-na-coalesce} +\alias{op-na-coalesce} +\alias{\%|\%} +\title{Default value for NA elements in vectors} +\usage{ +x \%|\% y +} +\arguments{ +\item{x}{(\code{any}) A vector that may contain \code{NA} elements.} + +\item{y}{(\code{any}, coercible to the same class as \code{x}) A value or vector to +replace \code{NA} elements in \code{x}. Will be recycled to the same length as \code{x}.} +} +\value{ +A vector of the same length as \code{x}, where each \code{NA} element in \code{x} +is replaced by the corresponding element in \code{y}. +} +\description{ +Default value for NA elements in vectors +} +\seealso{ +Other empty operators: +\code{\link{op-lengthless-default}}, +\code{\link{op-no-char-default}}, +\code{\link{op-null-continuation}}, +\code{\link{op-null-default}} +} +\concept{empty operators} +\keyword{internal} diff --git a/man/op-no-char-default.Rd b/man/op-no-char-default.Rd index ddea4c7..c2c2c6e 100644 --- a/man/op-no-char-default.Rd +++ b/man/op-no-char-default.Rd @@ -3,33 +3,30 @@ \name{op-no-char-default} \alias{op-no-char-default} \alias{\%|a|\%} -\alias{\%|\%} \title{Default value for empty strings} \usage{ x \%|a|\% y - -x \%|\% y } \arguments{ -\item{x}{(\code{any}) A vector that may contain \code{NA} elements.} +\item{x}{(\code{any}) Object to check.} + +\item{y}{(\code{character}) Default value for \code{x}.} + +\item{x_arg}{(\code{character(1)}) The name of the \code{x} argument for error +messages.} -\item{y}{(\code{any}, coercible to the same class as \code{x}) A value or vector to -replace \code{NA} elements in \code{x}. Will be recycled to the same length as \code{x}.} +\item{call}{(\code{environment}) The caller environment for error messages.} } \value{ If \code{!nzchar(x)}, will return \code{y}; otherwise returns \code{x}. - -A vector of the same length as \code{x}, where each \code{NA} element in \code{x} -is replaced by the corresponding element in \code{y}. } \description{ Default value for empty strings - -Default value for NA elements in vectors } \seealso{ Other empty operators: \code{\link{op-lengthless-default}}, +\code{\link{op-na-coalesce}}, \code{\link{op-null-continuation}}, \code{\link{op-null-default}} } diff --git a/man/op-null-continuation.Rd b/man/op-null-continuation.Rd index 2a8930c..1b1e0f0 100644 --- a/man/op-null-continuation.Rd +++ b/man/op-null-continuation.Rd @@ -21,6 +21,7 @@ Default value for non-\code{NULL} \seealso{ Other empty operators: \code{\link{op-lengthless-default}}, +\code{\link{op-na-coalesce}}, \code{\link{op-no-char-default}}, \code{\link{op-null-default}} } diff --git a/man/op-null-default.Rd b/man/op-null-default.Rd index a5282ef..fea2a8c 100644 --- a/man/op-null-default.Rd +++ b/man/op-null-default.Rd @@ -21,6 +21,7 @@ Default value for \code{NULL} \seealso{ Other empty operators: \code{\link{op-lengthless-default}}, +\code{\link{op-na-coalesce}}, \code{\link{op-no-char-default}}, \code{\link{op-null-continuation}} } From a9f8a104cdde8f92a8b96c9a7f207c1a4cc0da70 Mon Sep 17 00:00:00 2001 From: Jon Harmon Date: Sat, 16 May 2026 13:39:21 -0500 Subject: [PATCH 3/3] Fix docs --- DESCRIPTION | 1 - R/aaa-shared_params.R | 2 -- R/utils.R | 10 ++-------- man/dot-shared-params.Rd | 3 --- man/op-no-char-default.Rd | 5 ----- 5 files changed, 2 insertions(+), 19 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 025c358..baa3542 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -34,7 +34,6 @@ Imports: tibble, tidyr, usethis, - utils, yaml Suggests: covr, diff --git a/R/aaa-shared_params.R b/R/aaa-shared_params.R index 73ef648..0ea641a 100644 --- a/R/aaa-shared_params.R +++ b/R/aaa-shared_params.R @@ -17,8 +17,6 @@ #' @param rapid_file (`character(1)` or `fs_path`) The path to the R API #' definition (rapid) file. #' @param to_collapse (`character`) The character vector to collapse. -#' @param x_arg (`character(1)`) The name of the `x` argument for error -#' messages. #' #' @name .shared-params #' @keywords internal diff --git a/R/utils.R b/R/utils.R index 4825810..9f44022 100644 --- a/R/utils.R +++ b/R/utils.R @@ -44,20 +44,14 @@ if (exists("%||%", envir = baseenv())) { #' #' @param x (`any`) Object to check. #' @param y (`character`) Default value for `x`. -#' @inheritParams .shared-params #' @returns If `!nzchar(x)`, will return `y`; otherwise returns `x`. #' @keywords internal #' @name op-no-char-default #' @family empty operators #' @keywords internal -`%|a|%` <- function( - x, - y, - x_arg = rlang::caller_arg(x), - call = rlang::caller_env() -) { +`%|a|%` <- function(x, y) { if (!is.character(x) || !nzchar(x)) { - stbl::to_chr(y, x_arg = x_arg, call = call) + stbl::to_chr(y) } else { x } diff --git a/man/dot-shared-params.Rd b/man/dot-shared-params.Rd index 302ad33..ac04ee7 100644 --- a/man/dot-shared-params.Rd +++ b/man/dot-shared-params.Rd @@ -25,9 +25,6 @@ files.} definition (rapid) file.} \item{to_collapse}{(\code{character}) The character vector to collapse.} - -\item{x_arg}{(\code{character(1)}) The name of the \code{x} argument for error -messages.} } \description{ These parameters are used in multiple functions. They are defined here to diff --git a/man/op-no-char-default.Rd b/man/op-no-char-default.Rd index c2c2c6e..03f015e 100644 --- a/man/op-no-char-default.Rd +++ b/man/op-no-char-default.Rd @@ -11,11 +11,6 @@ x \%|a|\% y \item{x}{(\code{any}) Object to check.} \item{y}{(\code{character}) Default value for \code{x}.} - -\item{x_arg}{(\code{character(1)}) The name of the \code{x} argument for error -messages.} - -\item{call}{(\code{environment}) The caller environment for error messages.} } \value{ If \code{!nzchar(x)}, will return \code{y}; otherwise returns \code{x}.