diff --git a/DESCRIPTION b/DESCRIPTION index e04b156..ece63c5 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: scribe Title: Command Argument Parsing -Version: 0.3.0.9000 +Version: 0.3.0.9001 Authors@R: person( given = "Jordan Mark", @@ -16,7 +16,7 @@ License: MIT + file LICENSE Encoding: UTF-8 Language: en-US Roxygen: list(markdown = TRUE) -RoxygenNote: 7.2.3 +RoxygenNote: 7.3.0 Depends: R (>= 3.6) Imports: @@ -33,3 +33,4 @@ Config/testthat/edition: 3 VignetteBuilder: knitr URL: https://jmbarbone.github.io/scribe/, https://github.com/jmbarbone/scribe BugReports: https://github.com/jmbarbone/scribe/issues +Config/testthat/parallel: true diff --git a/NEWS.md b/NEWS.md index 4c39679..c247dd8 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,7 +1,20 @@ # scribe (development version) -- improves `ca$help()` printing +- `---help`, `---version` _super_ arguments (`scribeSuperArg` objects) are now included with `scribeCommandArgs`. +This are objects that (by default) can be called within any `scribeCommandArgs` and intended to hold additional information about the `{scribe}` package. +These are not meant to be user accessible. + - `scribeCommandArgs` gain a new `field`, `supers`; a list of `scribeSuperArg` objects + - `command_args(super = included)` added + - internal `scribe_version_arg()` function (used with `command_args(include = "verison")` and called with `--version`) is now deprecated. Using `---version` now returns the version of `{scribe}` + - internal `scribe_help_super()` now available via `---help` and returns information about `{scribe}` + `ca$get_args(super = TRUE)` returns `scribeSuerArg`s +- `ca$help()` printing has been improved - corrects issue with `ca$parse()` +- `ca$get_values(empty, super, included)` added to prevent filtering of specific argument types and values +- internal linting improvements +- `new_arg()` now throws a more helpful error when _value doesn't convert to itself_ +- `arg$show()` now denotes if the argument is resolves by display an `"R"` before the value +- `arg$show()` now prints values of class `"scribe_empty_value"` as `` # scribe 0.3.0 diff --git a/R/arg.R b/R/arg.R index 7176737..31c4f91 100644 --- a/R/arg.R +++ b/R/arg.R @@ -50,17 +50,12 @@ scribe_help_arg <- function() { ca$help() return(exit()) } - - if (isFALSE(self$get_value())) { - # remove 'help' - values <- ca$get_values() - ca$field("values", values[-match("help", names(values))]) - } } ) } scribe_version_arg <- function() { + # TODO deprecate for ---version new_arg( aliases = "--version", action = "flag", @@ -71,32 +66,28 @@ scribe_version_arg <- function() { stop = "hard", execute = function(self, ca) { if (isTRUE(self$get_value())) { + .Deprecated("For {scribe} package version, use ---version instead") ca$version() return(exit()) } - - if (isFALSE(self$get_value())) { - # remove 'version' - values <- ca$get_values() - ca$field("values", values[-match("version", names(values))]) - } } ) } # wrappers ---------------------------------------------------------------- -arg_initialize <- function( # nolint: cyclocomp_linter. - self, - aliases = "", - action = arg_actions(), - default = NULL, - convert = scribe_convert(), - n = NA_integer_, - info = NA_character_, - options = list(), - stop = c("none", "hard", "soft"), - execute = invisible +# nolint next: cyclocomp_linter. +arg_initialize <- function( + self, + aliases = "", + action = arg_actions(), + default = NULL, + convert = scribe_convert(), + n = NA_integer_, + info = NA_character_, + options = list(), + stop = c("none", "hard", "soft"), + execute = invisible ) { action <- match.arg(action, arg_actions()) info <- info %||% NA_character_ @@ -181,9 +172,21 @@ arg_initialize <- function( # nolint: cyclocomp_linter. } ) - if (!is.null(default)) { - if (!identical(value_convert(default, to = convert), default)) { - stop("default value doesn't convert to itself", call. = FALSE) + if (!is.null(default) && !is.null(convert)) { + attempt <- value_convert(default, to = convert) + if (!identical(attempt, default)) { + stop( + "default value doesn't convert to itself:\n", + "default:\n", + utils::capture.output(utils::str(default)), + "\nconverted:\n", + utils::capture.output(utils::str(attempt)), + "\n\n", + "to disable this check, use", + " `new_arg(convert = scribe_convert(\"none\"))`", + " or review the `convert` argument in `?scribe_arg`", + call. = FALSE + ) } } @@ -213,9 +216,17 @@ arg_initialize <- function( # nolint: cyclocomp_linter. positional <- !dash_args if (action == "flag" && isTRUE(options$no)) { - dash2 <- grep("^--", aliases) - if (dash2 && length(dash2)) { - aliases <- c(aliases, paste0("--no-", sub("^--", "", aliases[dash2]))) + dashes <- n_dashes(aliases) + ok <- dashes >= 2L + if (any(ok)) { + aliases <- c( + aliases, + paste0( + strrep("-", dashes[ok]), + "no-", + sub("^-{2,3}", "", aliases[ok]) + ) + ) } else if (positional) { aliases <- c(aliases, paste0("no-", aliases)) } @@ -251,18 +262,30 @@ arg_initialize <- function( # nolint: cyclocomp_linter. } arg_show <- function(self) { - value <- self$get_default() + value <- + if (self$is_resolved()) { + self$get_value() + } else { + self$get_default() + } + value <- if (is.null(value)) { "" + } else if (inherits(value, "scribe_empty_value")) { + "" } else { paste(vapply(value, format, NA_character_), collapse = " ") } aliases <- self$get_aliases() aliases <- to_string(aliases) - - print_line(sprintf("Argument [%s] : %s", aliases, value)) + print_line(sprintf( + "Argument [%s] %s: %s", + aliases, + if (self$is_resolved()) "R " else "", + value + )) invisible(self) } @@ -340,7 +363,12 @@ arg_get_name <- function(self, clean = TRUE) { nm <- aliases[n] if (clean) { - nm <- sub("^--?", "", nm) + nm <- + if (startsWith(nm, "---")) { + paste0("_", substr(nm, 4L, nchar(nm))) + } else { + sub("^-{1,2}", "", nm) + } } nm @@ -372,12 +400,13 @@ arg_is_resolved <- function(self) { # internal ---------------------------------------------------------------- -arg_parse_value <- function(self, ca) { # nolint: cyclocomp_linter. +# nolint next: cyclocomp_linter. +arg_parse_value <- function(self, ca) { default <- if (is_arg(self$default)) { self$default$get_value() } else { - self$default + self$get_default() } if (ca$stop == "soft") { @@ -442,9 +471,9 @@ arg_parse_value <- function(self, ca) { # nolint: cyclocomp_linter. }, list = { m <- m + seq.int(0L, self$n) - value <- ca_get_working(ca)[m[-off]] + value <- ca_get_working(ca, m[-off]) - if (self$positional && is.na(value)) { + if (self$positional && !isFALSE(is.na(value))) { default <- TRUE value <- self$get_default() } @@ -452,8 +481,8 @@ arg_parse_value <- function(self, ca) { # nolint: cyclocomp_linter. ca_remove_working(ca, m) }, flag = { - value <- !grepl("^--?no-", ca_get_working(ca)[m + off]) - ca_remove_working(ca, m) + value <- !grepl("^-{2,3}no-", ca_get_working(ca, m + off)) + ca_remove_working(ca, m + off) } ) @@ -479,7 +508,7 @@ is_arg <- function(x) { methods::is(x, "scribeArg") } -ARG_PAT <- "^-[a-z]$|^--[a-z]+$|^--[a-z](+[-]?[a-z]+)+$" # nolint: object_name_linter, line_length_linter. +ARG_PAT <- "^-[a-z]$|^---?[a-z]+$|^--?[a-z](+[-]?[a-z]+)+$" # nolint: object_name_linter, line_length_linter. arg_actions <- function() { c("default", "list", "flag", "dots") @@ -488,3 +517,9 @@ arg_actions <- function() { scribe_empty_value <- function() { structure(list(), class = c("scribe_empty_value")) } + +n_dashes <- function(x) { + n <- attr(regexpr("^-+", x), "match.length") + n[n == -1] <- 0L + n +} diff --git a/R/class-args.R b/R/class-args.R index ca4369b..f6a58b9 100644 --- a/R/class-args.R +++ b/R/class-args.R @@ -72,7 +72,8 @@ #' new_arg("...", info = "list of values") # defaults when alias is "..." #' @family scribe #' @export -scribeArg <- methods::setRefClass( # nolint: object_name_linter. +# nolint next: object_name_linter. +scribeArg <- methods::setRefClass( "scribeArg", fields = list( aliases = "character", @@ -171,3 +172,23 @@ scribeArg$methods( arg_is_resolved(.self) } ) + +# nolint next: object_name_linter. +scribeSuperArg <- methods::setRefClass("scribeSuperArg", contains = "scribeArg") + +scribeSuperArg$methods( + initialize = function( + aliases = "", + ... + ) { + if (!all(startsWith(aliases, "---"))) { + stop("super args aliases must start with ---") + } + + arg_initialize( + self = .self, + aliases = aliases, + ... + ) + } +) diff --git a/R/class-command-args.R b/R/class-command-args.R index 3565aea..978013c 100644 --- a/R/class-command-args.R +++ b/R/class-command-args.R @@ -39,7 +39,7 @@ #' See also [command_args()] #' @field values `[list]`\cr A named `list` of values. Empty on initialization #' and populated during argument resolving. -#' @field args `[list]`\cr a List of [scribeArg]s +#' @field args `[list]`\cr A List of [scribeArg]s #' @field description `[character]`\cr Additional help information #' @field included `[character]`\cr Default [scribeArg]s to include #' @field examples `[character]`\cr Examples to print with help @@ -49,6 +49,7 @@ #' @field working `[character]`\cr A copy of `input`. Note: this is used to #' track parsing progress and is not meant to be accessed directly. #' @field stop `[character]`\cr Determines parsing +#' @field supers `[list]`\cr A list of `scribeSuperArgs`s #' #' @examples #' # command_args() is recommended over direct use of scribeCommandArgs$new() @@ -84,7 +85,8 @@ #' do.call(my_function, args) #' @family scribe #' @export -scribeCommandArgs <- methods::setRefClass( # nolint: object_name_linter. +# nolint next: object_name_linter. +scribeCommandArgs <- methods::setRefClass( "scribeCommandArgs", fields = list( input = "character", @@ -96,7 +98,8 @@ scribeCommandArgs <- methods::setRefClass( # nolint: object_name_linter. comments = "character", resolved = "logical", working = "character", - stop = "character" + stop = "character", + supers = "list" ) ) @@ -104,7 +107,8 @@ scribeCommandArgs$methods( # creates the object initialize = function( input = "", - include = c("help", "version", NA_character_) + include = c("help", "version", NA_character_), + supers = include ) { "Initialize the \\link{scribeCommandArgs} object. The wrapper \\code{\\link[=command_args]{command_args()}} is recommended rather than @@ -115,8 +119,11 @@ scribeCommandArgs$methods( to parse} \\item{\\code{include}}{A character vector denoting which default \\link{scribeArg}s to include in \\code{args}} + \\item{\\code{supers}}{A character vector denoting which default + \\code{scribeSuperArg}s to include in \\code{supers} (i.e., arguments + called with `---` prefixes)} }" - ca_initialize(.self, input = input, include = include) + ca_initialize(.self, input = input, include = include, supers = supers) }, show = function(...) { @@ -155,15 +162,24 @@ scribeCommandArgs$methods( "Set \\code{input}. Note: when called, \\code{resolved} is (re)set to \\code{FALSE} and values need to be parsed again. - \\describe{ - \\item{\\code{value}}{Value to set} - }" + \\describe{\\item{\\code{value}}{Value to set}} + " ca_set_input(.self, value = value) }, - get_values = function() { - "Retrieve \\code{values}" - ca_get_values(.self) + get_values = function( + empty = FALSE, + super = FALSE, + included = FALSE + ) { + "Retrieve \\code{values} + \\describe{ + \\item{\\code{{empty}}}{If \\code{TRUE} returns empty values, too} + \\item{\\code{super}}{If \\code{TRUE} also returns values from super args} + \\item{\\code{included}}{If \\code{TRUE} also returns included default + \\link{scribeArg}s defined in \\code{$initialize()}} + }" + ca_get_values(.self, empty = empty, super = super, included = included) }, set_values = function(i = TRUE, value) { @@ -176,14 +192,15 @@ scribeCommandArgs$methods( ca_set_values(.self, i = i, value = value) }, - get_args = function(included = TRUE) { + get_args = function(included = TRUE, super = FALSE) { "Retrieve \\code{args} \\describe{ \\item{\\code{included}}{If \\code{TRUE} also returns included default \\link{scribeArg}s defined in \\code{$initialize()}} + \\item{\\code{super}}{If \\code{TRUE} also returns super args} }" - ca_get_args(.self, included = included) + ca_get_args(.self, included = included, super = super) }, add_argument = function( diff --git a/R/command-args.R b/R/command-args.R index de1acef..c9b136e 100644 --- a/R/command-args.R +++ b/R/command-args.R @@ -9,8 +9,10 @@ #' Otherwise the value of `x` is converted to a `character`. If `string` is #' not `NULL`, [scan()] will be used to split the value into a `character` #' vector. -#' @param include Special default arguments to included. See `$initialize()` -#' in [scribeCommandArgs] for more details. +#' @param include Special default arguments to included. See `$initialize()` in +#' [scribeCommandArgs] for more details. +#' @param super When `TRUE` the [scribeCommandArgs] object will be initialized +#' with standard _super_ arguments (e.g., `---help`, `---version`) #' @examples #' command_args() #' command_args(c("-a", 1, "-b", 2)) @@ -21,7 +23,8 @@ command_args <- function( x = NULL, include = getOption("scribe.include", c("help", "version", NA_character_)), - string = NULL + string = NULL, + super = include ) { if (is.null(string)) { if (is.null(x)) { @@ -37,7 +40,7 @@ command_args <- function( x <- scan(text = string, what = "character", quiet = TRUE) } - scribeCommandArgs(input = x, include = include) + scribeCommandArgs(input = x, include = include, super = super) } # wrappers ---------------------------------------------------------------- @@ -45,7 +48,8 @@ command_args <- function( ca_initialize <- function( self, input = NULL, - include = c("help", "version", NA_character_) + include = c("help", "version", NA_character_), + supers = include ) { # default values self$initFields( @@ -62,6 +66,12 @@ ca_initialize <- function( several.ok = TRUE ) + supers <- match.arg( + as.character(supers), + c("help", "version", NA_character_), + several.ok = TRUE + ) + include <- include[!is.na(include)] if (!length(include)) { include <- character() @@ -75,6 +85,11 @@ ca_initialize <- function( self$add_argument(scribe_version_arg()) } + self$field("supers", c( + if ("help" %in% supers) scribe_help_super(), + if ("version" %in% supers) scribe_version_super() + ) %||% list()) + self$field("input", as.character(input) %||% character()) self$field("working", self$input) self$field("included", include) @@ -82,7 +97,7 @@ ca_initialize <- function( invisible(self) } -ca_show <- function(self, ...) { +ca_show <- function(self, all_values = FALSE, ...) { print_line("Initial call: ", to_string(self$get_input())) if (!self$resolved) { @@ -178,7 +193,7 @@ ca_resolve <- function(self) { # dropped. Single value arguments are easier to match, should always have # that value present if arg is present. Non-multiple value arguments have at # least a limit to the number of values that can be found. - args <- self$get_args() + args <- self$get_args(included = TRUE, super = TRUE) arg_order <- unique( c( @@ -187,23 +202,24 @@ ca_resolve <- function(self) { seq_along(args), # dots must always be parsed last wapply(args, function(i) i$positional), - wapply(args, function(i) i$action == "dots") + wapply(args, function(i) i$action == "dots"), + wapply(args, function(i) inherits(i, "scribeSuperArg")) ), fromLast = TRUE ) # move stops earlier - arg_order <- unique( - c( - wapply(args, function(i) i$stop == "hard"), - wapply(args, function(i) i$stop == "soft"), - arg_order - ) - ) + arg_order <- unique(c( + wapply(args, function(i) i$stop == "hard"), + wapply(args, function(i) i$stop == "soft"), + arg_order + )) arg_names <- vapply(args, function(arg) arg$get_name(), NA_character_) - self$field("values", vector("list", length(arg_order))) - names(self$values) <- arg_names[arg_order] + self$field("values", structure( + vector("list", length(arg_order)), + names = arg_names[arg_order] + )) for (arg in args[arg_order]) { self$set_values(arg$get_name(), arg_parse_value(arg, self)) @@ -225,15 +241,15 @@ ca_resolve <- function(self) { ca_parse <- function(self) { self$resolve() - for (arg in self$get_args()) { + for (arg in self$get_args(super = TRUE, included = TRUE)) { ca_do_execute(self, arg) } # clean up names - values <- self$get_values() - regmatches(names(values), regexpr("^-+", names(values))) <- "" - regmatches(names(values), gregexpr("-", names(values))) <- "_" - values + res <- self$get_values() + regmatches(names(res), regexpr("^-+", names(res))) <- "" + regmatches(names(res), gregexpr("-", names(res))) <- "_" + res } ca_get_input <- function(self) { @@ -247,8 +263,27 @@ ca_set_input <- function(self, value) { invisible(self) } -ca_get_values <- function(self) { - Filter(function(x) !inherits(x, "scribe_empty_value"), self$values) +ca_get_values <- function( + self, + empty = FALSE, + super = FALSE, + included = FALSE +) { + values <- self$values + + if (!included) { + values <- values[setdiff(names(values), self$included)] + } + + if (!super && !is.null(names(values))) { + values <- values[!startsWith(names(values), "_")] + } + + if (!empty) { + values <- values[vapply(values, Negate(inherits), NA, "scribe_empty_value")] + } + + values } ca_set_values <- function(self, i = NULL, value) { @@ -262,14 +297,20 @@ ca_set_values <- function(self, i = NULL, value) { invisible(self) } -ca_get_args <- function(self, included = TRUE) { - if (included) { - return(self$args) +ca_get_args <- function(self, included = TRUE, super = FALSE) { + args <- self$args + + if (!included) { + nms <- sapply(args, function(arg) arg$get_name()) + ok <- match(nms, self$included, 0L) == 0L + args <- args[ok] + } + + if (super) { + args <- c(self$supers, args) } - nms <- sapply(self$args, function(arg) arg$get_name()) - ok <- match(nms, self$included, 0L) == 0L - self$args[ok] + args } ca_add_argument <- function( diff --git a/R/scribe-package.R b/R/scribe-package.R index 3fa0487..9cc47b6 100644 --- a/R/scribe-package.R +++ b/R/scribe-package.R @@ -15,8 +15,8 @@ NULL # nolint end: line_length_linter. - -op.scribe <- list( # nolint: object_name_linter. +# nolint next: object_name_linter. +op.scribe <- list( scribe.flag.no = TRUE, scribe.interactive = NULL, scribe.include = c("help", "version") diff --git a/R/super-arg.R b/R/super-arg.R new file mode 100644 index 0000000..c8e5f25 --- /dev/null +++ b/R/super-arg.R @@ -0,0 +1,48 @@ +# default super helpers. though these may be the only super helpers. the +# object is not exported so these should only be used internally. + +scribe_help_super <- function() { + scribeSuperArg$new( + aliases = "---help", + action = "flag", + default = FALSE, + n = 0, + info = "prints help information for {scribe} and quietly exits", + options = list(no = FALSE), + stop = "hard", + execute = function(self, ca) { + if (isTRUE(self$get_value())) { + cat( + "{scribe} v", format(scribe_version()), "\n", + "For more information, see https://jmbarbone.github.io/scribe/\n", + sep = "" + ) + exit() + } + } + ) +} + +scribe_version_super <- function() { + scribeSuperArg$new( + aliases = "---version", + action = "flag", + default = FALSE, + n = 0, + info = "prints the version of {scribe} and quietly exits", + options = list(no = FALSE), + stop = "hard", + execute = function(self, ca) { + if (isTRUE(self$get_value())) { + cat(format(scribe_version()), "\n", sep = "") + exit() + } + } + ) +} + +scribe_version <- function() { + package_version( + asNamespace("scribe")[[".__NAMESPACE__."]][["spec"]][["version"]] + ) +} diff --git a/R/utils.R b/R/utils.R index b53594a..258bc37 100644 --- a/R/utils.R +++ b/R/utils.R @@ -54,10 +54,10 @@ exit <- function(force = !getOption("scribe.interactive", interactive())) { invisible() } -# nolint start: object_name_linter. +# nolint next: object_name_linter. wapply <- function(x, FUN, ...) { + # nolint next: object_name_linter. FUN <- match.fun(FUN) - # nolint end: object_name_linter. fun <- function(x, ...) isTRUE(FUN(x, ...)) which(do.call(vapply, list(X = x, FUN = fun, FUN.VALUE = NA))) } diff --git a/man/command_args.Rd b/man/command_args.Rd index 1a05277..b3f406b 100644 --- a/man/command_args.Rd +++ b/man/command_args.Rd @@ -7,7 +7,8 @@ command_args( x = NULL, include = getOption("scribe.include", c("help", "version", NA_character_)), - string = NULL + string = NULL, + super = include ) } \arguments{ @@ -18,8 +19,11 @@ Otherwise the value of \code{x} is converted to a \code{character}. If \code{st not \code{NULL}, \code{\link[=scan]{scan()}} will be used to split the value into a \code{character} vector.} -\item{include}{Special default arguments to included. See \verb{$initialize()} -in \link{scribeCommandArgs} for more details.} +\item{include}{Special default arguments to included. See \verb{$initialize()} in +\link{scribeCommandArgs} for more details.} + +\item{super}{When \code{TRUE} the \link{scribeCommandArgs} object will be initialized +with standard \emph{super} arguments (e.g., \code{---help}, \code{---version})} } \value{ A \link{scribeCommandArgs} object diff --git a/man/scribeCommandArgs-class.Rd b/man/scribeCommandArgs-class.Rd index 5715f79..7901b0a 100644 --- a/man/scribeCommandArgs-class.Rd +++ b/man/scribeCommandArgs-class.Rd @@ -48,7 +48,7 @@ See also \code{\link[=command_args]{command_args()}}} \item{\code{values}}{\verb{[list]}\cr A named \code{list} of values. Empty on initialization and populated during argument resolving.} -\item{\code{args}}{\verb{[list]}\cr a List of \link{scribeArg}s} +\item{\code{args}}{\verb{[list]}\cr A List of \link{scribeArg}s} \item{\code{description}}{\verb{[character]}\cr Additional help information} @@ -65,6 +65,8 @@ and populated during argument resolving.} track parsing progress and is not meant to be accessed directly.} \item{\code{stop}}{\verb{[character]}\cr Determines parsing} + +\item{\code{supers}}{\verb{[list]}\cr A list of \code{scribeSuperArgs}s} }} \section{Methods}{ @@ -104,11 +106,12 @@ track parsing progress and is not meant to be accessed directly.} \item{\code{prefix}}{An optional prefix for the example} }} -\item{\code{get_args(included = TRUE)}}{Retrieve \code{args} +\item{\code{get_args(included = TRUE, super = FALSE)}}{Retrieve \code{args} \describe{ \item{\code{included}}{If \code{TRUE} also returns included default \link{scribeArg}s defined in \code{$initialize()}} + \item{\code{super}}{If \code{TRUE} also returns super args} }} \item{\code{get_description()}}{Retrieve \code{description}} @@ -117,11 +120,21 @@ track parsing progress and is not meant to be accessed directly.} \item{\code{get_input()}}{Retrieve \code{input}} -\item{\code{get_values()}}{Retrieve \code{values}} +\item{\code{get_values(empty = FALSE, super = FALSE, included = FALSE)}}{Retrieve \code{values} +\describe{ + \item{\code{{empty}}}{If \code{TRUE} returns empty values, too} + \item{\code{super}}{If \code{TRUE} also returns values from super args} + \item{\code{included}}{If \code{TRUE} also returns included default + \link{scribeArg}s defined in \code{$initialize()}} +}} \item{\code{help()}}{Print the help information} -\item{\code{initialize(input = "", include = c("help", "version", NA_character_))}}{Initialize the \link{scribeCommandArgs} object. The wrapper +\item{\code{initialize( + input = "", + include = c("help", "version", NA_character_), + supers = include +)}}{Initialize the \link{scribeCommandArgs} object. The wrapper \code{\link[=command_args]{command_args()}} is recommended rather than calling this method directly. @@ -130,6 +143,9 @@ track parsing progress and is not meant to be accessed directly.} to parse} \item{\code{include}}{A character vector denoting which default \link{scribeArg}s to include in \code{args}} + \item{\code{supers}}{A character vector denoting which default + \code{scribeSuperArg}s to include in \code{supers} (i.e., arguments + called with `---` prefixes)} }} \item{\code{parse()}}{Return a named \code{list} of parsed values of from each \link{scribeArg} @@ -156,9 +172,8 @@ is called prior to $parse()} \item{\code{set_input(value)}}{Set \code{input}. Note: when called, \code{resolved} is (re)set to \code{FALSE} and values need to be parsed again. - \describe{ - \item{\code{value}}{Value to set} - }} + \describe{\item{\code{value}}{Value to set}} + } \item{\code{set_values(i = TRUE, value)}}{Set \code{values} diff --git a/scribe.Rproj b/scribe.Rproj index 0a45aae..7a1f5b9 100644 --- a/scribe.Rproj +++ b/scribe.Rproj @@ -1,8 +1,8 @@ Version: 1.0 -RestoreWorkspace: Default -SaveWorkspace: Default -AlwaysSaveHistory: Default +RestoreWorkspace: No +SaveWorkspace: No +AlwaysSaveHistory: No EnableCodeIndexing: Yes UseSpacesForTab: Yes diff --git a/tests/testthat/_snaps/class-args.md b/tests/testthat/_snaps/class-args.md index 8bf2d61..dc868b2 100644 --- a/tests/testthat/_snaps/class-args.md +++ b/tests/testthat/_snaps/class-args.md @@ -12,3 +12,17 @@ Output [...] help text +--- + + Code + arg$show() + Output + Argument [foo] : 0 + +--- + + Code + arg$show() + Output + Argument [foo] R : 1 + diff --git a/tests/testthat/_snaps/class-command-args.md b/tests/testthat/_snaps/class-command-args.md index 0cc9e57..eea3eee 100644 --- a/tests/testthat/_snaps/class-command-args.md +++ b/tests/testthat/_snaps/class-command-args.md @@ -168,3 +168,41 @@ --help prints this and quietly exits --version prints the version of {scribe} and quietly exits +# snapshots - empty values + + Code + ca$get_args() + Output + [[1]] + Argument [--help] R : FALSE + + [[2]] + Argument [--version] R : FALSE + + [[3]] + Argument [--foo] R : + + [[4]] + Argument [--bar] R : zero + + [[5]] + Argument [--fizz] R : + + +# snapshots - super args + + Code + command_args("---help")$parse() + Output + {scribe} v0.3.0.9001 + For more information, see https://jmbarbone.github.io/scribe/ + named list() + +--- + + Code + command_args("---version")$parse() + Output + 0.3.0.9001 + named list() + diff --git a/tests/testthat/test-class-args.R b/tests/testthat/test-class-args.R index 710995f..afba539 100644 --- a/tests/testthat/test-class-args.R +++ b/tests/testthat/test-class-args.R @@ -56,10 +56,7 @@ test_that("help() [#16]", { exp <- c("...", "help text") expect_identical(obj, exp) - obj <- new_arg( - c("...", "dots"), - info = "more help here" - )$get_help() + obj <- new_arg(c("...", "dots"), info = "more help here")$get_help() exp <- c("...", "dots: more help here") expect_identical(obj, exp) @@ -174,4 +171,13 @@ test_that("snapshots", { expect_output(arg$help()) expect_snapshot(arg$show()) expect_snapshot(arg$help()) + + ca <- command_args(1) + arg <- new_arg("foo", default = 0) + ca$add_argument(arg) + expect_snapshot(arg$show()) + + # now resolve the argument so we have a different print + ca$resolve() + expect_snapshot(arg$show()) }) diff --git a/tests/testthat/test-class-command-args.R b/tests/testthat/test-class-command-args.R index 3769fdc..143550f 100644 --- a/tests/testthat/test-class-command-args.R +++ b/tests/testthat/test-class-command-args.R @@ -161,6 +161,46 @@ test_that("$add_argument(arg) [#45]", { expect_identical(obj, exp) }) +test_that("$get_values() [#73]", { + # #73 includes super args + ca <- command_args(1L) + ca$add_argument("foo") + ca$add_argument("bar") + obj <- ca$get_values() + exp <- list() + expect_identical(obj, exp) + + ca$resolve() + + obj <- ca$get_values() + exp <- list(foo = 1L, bar = NULL) + expect_identical(obj, exp) + obj <- ca$get_values() + + obj <- ca$get_values(included = TRUE) + exp <- list(help = FALSE, version = FALSE, foo = 1L, bar = NULL) + expect_identical(obj, exp) + + obj <- ca$get_values(super = TRUE) + exp <- list(`_help` = FALSE, `_version` = FALSE, foo = 1L, bar = NULL) + expect_identical(obj, exp) +}) + +test_that("$get_values(empty)", { + ca <- command_args("--stop") + ca$add_argument("foo") + ca$add_argument("--stop", stop = TRUE) + ca$resolve() + + obj <- ca$get_values() + exp <- list(stop = NA) + expect_identical(obj, exp) + + obj <- ca$get_values(empty = TRUE) + exp <- list(foo = scribe_empty_value(), stop = NA) + expect_identical(obj, exp) +}) + test_that("args are returned in original order [#25]", { ca <- command_args( c("-b", "one", "-c", "two", "-a", "three", "foo", "bar"), @@ -258,7 +298,7 @@ test_that("--help has early stop", { ca <- command_args("--help") ca$add_argument("-v") ca$add_argument("-f") - exp <- list(help = TRUE) + exp <- structure(list(), names = character()) expect_output(obj <- try(ca$parse())) expect_identical(obj, exp) }) @@ -332,8 +372,11 @@ test_that("versions", { ca$add_argument("--foo") ca$add_argument("--bar") ca$add_description("This does things") - expect_output((obj <- ca$parse())) - exp <- list(version = TRUE) + expect_warning( + expect_output((obj <- ca$parse())), + class = "deprecatedWarning" + ) + exp <- structure(list(), names = character()) expect_identical(obj, exp) expect_output(ca$version()) }) @@ -410,3 +453,18 @@ test_that("snapshots", { expect_snapshot(ca$show()) expect_snapshot(ca$help()) }) + +test_that("snapshots - empty values", { + ca <- command_args(string = "--bar zero") + ca$add_argument("--foo", stop = TRUE) + ca$add_argument("--bar", action = "list", default = character(), stop = TRUE) + ca$add_argument("--fizz", action = "list", default = character()) + ca$resolve() + ca$get_args() + expect_snapshot(ca$get_args()) +}) + +test_that("snapshots - super args", { + expect_snapshot(command_args("---help")$parse()) + expect_snapshot(command_args("---version")$parse()) +}) diff --git a/tests/testthat/test-class-super-args.R b/tests/testthat/test-class-super-args.R new file mode 100644 index 0000000..0c8457f --- /dev/null +++ b/tests/testthat/test-class-super-args.R @@ -0,0 +1,14 @@ +withr::local_options(list(scribe.interactive = TRUE)) + +test_that("---version, ---help", { + expect_output(command_args("---version")$parse()) + expect_output(command_args("---help")$parse()) +}) + +test_that("errors", { + expect_error( + scribeSuperArg$new("foo"), + regexp = "super args aliases must start with ---", + fixed = TRUE + ) +})