diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index a3ac6182..e96d304e 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -23,6 +23,8 @@ jobs: - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} - {os: ubuntu-latest, r: 'release'} - {os: ubuntu-latest, r: 'oldrel-1'} + # And minimum supported version in DESCRIPTION + - {os: ubuntu-latest, r: '3.6.0'} env: GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} diff --git a/DESCRIPTION b/DESCRIPTION index 36fdedba..d9cc2890 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -32,7 +32,7 @@ URL: https://github.com/frictionlessdata/frictionless-r, https://docs.ropensci.org/frictionless/ BugReports: https://github.com/frictionlessdata/frictionless-r/issues Depends: - R (>= 3.5.0) + R (>= 3.6.0) Imports: cli, dplyr, diff --git a/NEWS.md b/NEWS.md index cc5efd8f..f6bc4516 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,6 @@ # frictionless (development version) +* **frictionless now relies on R version 3.6.0 or higher**. Originally it stated version 3.5.0 or higher, but this was not tested and likely not true (#238). * `read_package()` now returns a warning rather than an error when a `datapackage.json` contains no resources. This allows use to create the JSON and then add resources with frictionless (#265). * `example_package()` now has a `version` parameter, allowing to load the example Data Package following the Data Package [v1](https://specs.frictionlessdata.io/) or [v2](https://datapackage.org/) specification (#249). diff --git a/R/add_resource.R b/R/add_resource.R index 7c3655ce..50a7c730 100644 --- a/R/add_resource.R +++ b/R/add_resource.R @@ -156,13 +156,13 @@ add_resource <- function(package, resource_name, data, schema = NULL, check_schema(schema, df) # Check ellipsis - if (...length() != length(...names())) { + if (length(list(...)) != length(get_dot_names(...))) { cli::cli_abort( "All arguments in {.arg ...} must be named.", class = "frictionless_error_argument_unnamed" ) } - properties <- ...names() + properties <- get_dot_names(...) reserved_properties <- c( "name", "path", "profile", "format", "mediatype", "encoding", "dialect" ) # data and schema are also reserved, but are named arguments diff --git a/R/read_resource.R b/R/read_resource.R index cad9c302..a440bb4e 100644 --- a/R/read_resource.R +++ b/R/read_resource.R @@ -55,7 +55,11 @@ read_resource <- function(package, resource_name, col_select = NULL) { # Read data from data } else if (resource$read_from == "data") { - df <- dplyr::as_tibble(do.call(rbind.data.frame, resource$data)) + df <- do.call( + function(...) rbind.data.frame(..., stringsAsFactors = FALSE), + resource$data + ) + df <- dplyr::as_tibble(df) # Read data from path(s) } else if (resource$read_from == "path" || resource$read_from == "url") { diff --git a/R/utils.R b/R/utils.R index 381ee3e9..d2718eb5 100644 --- a/R/utils.R +++ b/R/utils.R @@ -76,3 +76,17 @@ read_descriptor <- function(x, directory = NULL, safe = FALSE) { jsonlite::fromJSON(x, simplifyDataFrame = FALSE, simplifyVector = TRUE) } } + +#' Get names of arguments passed to ellipsis +#' +#' Replicates the base function [...names()] available in R >= 4.0.0. +#' +#' @param ... objects, possibly named +#' @return A character vector of the names of the ... arguments +#' @noRd +get_dot_names <- function(...) { + # Get all the names from ... + dot_names <- names(list(...)) + # Return the names that are not an empty string (no name set) + return(dot_names[dot_names != ""]) +} diff --git a/tests/testthat/test-utils.R b/tests/testthat/test-utils.R index c238bb01..0a905592 100644 --- a/tests/testthat/test-utils.R +++ b/tests/testthat/test-utils.R @@ -58,3 +58,19 @@ test_that("is_url() tests whether path is URL", { expect_false(is_url("http:/example.com")) expect_true(is_url("ftps://example.com")) # Not a correct protocol }) + +test_that("get_dot_names() returns the names passed via ...", { + test_fn <- function(...) { + get_dot_names(...) + } + expect_identical(test_fn(a = "1", b = "2"), c("a", "b")) +}) + +test_that("get_dot_names() does not return empty strings for unnamed args passed + to ellipsis", { + test_fn <- function(...) { + get_dot_names(...) + } + expect_identical(test_fn(a = "1", "2", b = "3", c = "4"), c("a", "b", "c")) + expect_length(test_fn(a = "1", "2", b = "3", c = "4"), 3) +})