Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .github/workflows/r-check-version.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
on:
pull_request:
branches: [main, master]

name: version checks

jobs:
check-r-version:
runs-on: ubuntu-latest
env:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v3

- uses: r-lib/actions/setup-r@v2
with:
use-public-rspm: true

- uses: r-lib/actions/setup-r-dependencies@v2
with:
extra-packages: any::gh, any::fuj

- uses: jmbarbone/actions/r-check-version@main
with:
ignore-dev-version: true
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: scribe
Title: Command Argument Parsing
Version: 0.2.0.9000
Version: 0.2.0.9001
Authors@R:
person(
given = "Jordan Mark",
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

export(command_args)
export(new_arg)
export(scribe_convert)
export(value_convert)
exportClasses(scribeArg)
exportClasses(scribeCommandArgs)
Expand Down
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# scribe (development version)

- `convert` is no longer ignored when set in `scribeArg` [#70](https://github.com/jmbarbone/scribe/issues/70)
- `$convert` field now defaults to the newly exported `scribe_convert()` helper. This selects one of three conversions: 1) default (see next bullet), 2) string evaluation, and 3) no conversion.
- default conversions use `value_convert()`, which internally uses `utils::type.convert()` (and some additional steps for dates. Be aware that `type.convert("1", as.is = TRUE)` will return integers, and a decimal should be included if a numeric is desired (e.g., `type.convert("1.", as.is = TRUE)`
- `flag` action now accepts `NA` as a default [#67](https://github.com/jmbarbone/scribe/issues/67)

# scribe 0.2.0
Expand Down
23 changes: 15 additions & 8 deletions R/arg.R
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ new_arg <- function(
aliases = "",
action = arg_actions(),
default = NULL,
convert = default_convert,
convert = scribe_convert(),
n = NA_integer_,
info = NULL,
options = list(),
Expand Down Expand Up @@ -91,7 +91,7 @@ arg_initialize <- function( # nolint: cyclocomp_linter.
aliases = "",
action = arg_actions(),
default = NULL,
convert = default_convert,
convert = scribe_convert(),
n = NA_integer_,
info = NA_character_,
options = list(),
Expand Down Expand Up @@ -237,7 +237,7 @@ arg_initialize <- function( # nolint: cyclocomp_linter.

self$field("aliases", aliases)
self$field("action", action)
self$field("convert", convert)
self$field("convert", scribe_convert(convert))
self$field("options", options)
self$field("info", as.character(info))
self$field("n", as.integer(n))
Expand Down Expand Up @@ -372,7 +372,7 @@ arg_is_resolved <- function(self) {

# internal ----------------------------------------------------------------

arg_parse_value <- function(self, ca) {
arg_parse_value <- function(self, ca) { # nolint: cyclocomp_linter.
default <-
if (is_arg(self$default)) {
self$default$get_value()
Expand Down Expand Up @@ -443,26 +443,33 @@ arg_parse_value <- function(self, ca) {

if (self$positional && is.na(value)) {
value <- self$get_default()
} else {
ca_remove_working(ca, m)
}

ca_remove_working(ca, m)
},
flag = {
value <- !grepl("^--?no-", ca_get_working(ca)[m + off])
ca_remove_working(ca, m)
}
)

value <- value_convert(value, to = default %||% self$convert)
ca$field("stop", structure(self$stop, arg = self))
ca$field("stop", structure(self$stop, arg = self))
}

if (self$action == "flag") {
invisible() # do nothing
} else if (identical(self$convert, value_convert)) {
value <- self$convert(value, to = default %||% default_convert)
} else {
value <- self$convert(value)
}

self$field("value", value)
self$field("resolved", TRUE)
value
}


# helpers -----------------------------------------------------------------

is_arg <- function(x) {
Expand Down
2 changes: 1 addition & 1 deletion R/class-args.R
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ scribeArg$methods(
aliases = "",
action = arg_actions(),
default = NULL,
convert = default_convert,
convert = scribe_convert(),
n = NA_integer_,
info = NA_character_,
options = list(),
Expand Down
2 changes: 1 addition & 1 deletion R/class-command-args.R
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ scribeCommandArgs$methods(
...,
action = arg_actions(),
options = NULL,
convert = default_convert,
convert = scribe_convert(),
default = NULL,
n = NA_integer_,
info = NULL,
Expand Down
2 changes: 1 addition & 1 deletion R/command-args.R
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ ca_add_argument <- function(
...,
n = NA_integer_,
action = NULL,
convert = default_convert,
convert = scribe_convert(),
options = NULL,
default = NULL,
info = NULL,
Expand Down
34 changes: 33 additions & 1 deletion R/convert.R
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
#' @examples
#' str(value_convert("2023-03-05", as.Date))
#' value_convert("a", factor(letters))
#' @returns A parsed value from `x`
#' @returns
#' * [value_convert()]: A parsed value from `x`
#' @export
value_convert <- function(x, to = default_convert) {
if (!is.character(x) || is.null(to)) {
Expand All @@ -43,6 +44,37 @@ value_convert <- function(x, to = default_convert) {
x
}

#' @rdname value_convert
#' @export
#' @param method The conversion method:
#' * `TRUE` or `"default"`: uses [value_convert()]
#' * `"evaluate"` executes the string as an expression
#' * `FALSE` or `NA` does nothing
#' * When passed a `function`, simply returns the function
#' @returns
#' * [scribe_convert()]: A function that takes a argument `x` and converts it
scribe_convert <- function(method = c("default", "evaluate", "none")) {
if (is.function(method)) {
return(method)
}

if (is.null(method) || isFALSE(method) || isTRUE(is.na(method))) {
method <- "none"
}

if (isTRUE(method)) {
method <- "default"
}

method <- match.arg(method)
switch(
method,
none = identity,
default = value_convert,
evaluate = function(x, ...) eval(str2expression(x), baseenv())
)
}

default_convert <- function(x) {
if (!length(x)) {
return(x)
Expand Down
2 changes: 1 addition & 1 deletion man/new_arg.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion man/scribeArg-class.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion man/scribeCommandArgs-class.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 18 additions & 1 deletion man/value_convert.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions tests/testthat/test-class-command-args.R
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,16 @@ test_that("'stop' args [#60]", {
expect_identical(new_arg(stop = FALSE)$stop, "none")
})

test_that("'convert' isn't ignored [#70]", {
ca <- command_args("1")
ca$add_argument("foo", convert = function(...) stop("success"))
expect_error(ca$parse(), "success")

ca <- command_args("1")
ca$add_argument("foo", convert = function(...) stop("success"), default = 1)
expect_error(ca$parse(), "success")
})

test_that("snapshots", {
ca <- command_args(string = "foo bar --fizz")
ca$add_description("this does a thing")
Expand Down
64 changes: 64 additions & 0 deletions tests/testthat/test-convert.R
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,70 @@ test_that("is_bool_like(), as_bool()", {
expect_true(as_bool(TRUE))
})

test_that("scribe_convert()", {
expect_identical(scribe_convert(scribe_convert()), value_convert)
expect_identical(scribe_convert(TRUE), value_convert)
expect_identical(scribe_convert("default"), value_convert)
expect_identical(scribe_convert(NA), identity)
expect_identical(scribe_convert(FALSE), identity)
expect_identical(scribe_convert(NULL), identity)

expect_type(scribe_convert("eval"), "closure")
expect_type(scribe_convert(function(x) x), "closure")
})

test_that("command_arg() default conversions", {
withr::local_options(scribe.include = NA)
ca <- command_args()
ca$add_argument("foo", convert = TRUE)

obj <- ca$set_input("1")$parse()
exp <- list(foo = 1L)
expect_identical(obj, exp)

obj <- ca$set_input("1.")$parse()
exp <- list(foo = 1.0)
expect_identical(obj, exp)

obj <- ca$set_input("2020-01-01")$parse()
exp <- list(foo = as.POSIXct("2020-01-01"))
expect_identical(obj, exp)
})

test_that("command_arg() no conversion", {
withr::local_options(scribe.include = NA)
ca <- command_args(100)
ca$add_argument("foo", convert = FALSE)
obj <- ca$parse()
exp <- list(foo = "100")
expect_identical(obj, exp)
})

test_that("command_arg() evaluate", {
withr::local_options(scribe.include = NA)
ca <- command_args("data.frame(a = 1)")
ca$add_argument("foo", convert = "evaluate")
obj <- ca$parse()
exp <- list(foo = data.frame(a = 1))
expect_identical(obj, exp)
})

test_that("command_arg() custom", {
withr::local_options(scribe.include = NA)

foo <- function(x) {
x <- strsplit(x, "|", fixed = TRUE)[[1]]
vapply(x, as.double, NA_real_, USE.NAMES = FALSE)
}

ca <- command_args()
ca$add_argument("foo", convert = foo)
ca$set_input("1|2|3")
obj <- ca$parse()
exp <- list(foo = c(1, 2, 3))
expect_identical(obj, exp)
})

test_that("default_convert(character()) [#5]", {
expect_identical(default_convert(character()), character())
})