Skip to content

Commit 0322090

Browse files
authored
Merge pull request #187 from frictionlessdata/add_class
Add `datapackage` class, update `create_package()` and `check_package()`
2 parents 6af1b3d + ed4f1e9 commit 0322090

27 files changed

Lines changed: 274 additions & 106 deletions

NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Generated by roxygen2: do not edit by hand
22

33
export(add_resource)
4+
export(check_package)
45
export(create_package)
56
export(create_schema)
67
export(get_schema)

NEWS.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@
22

33
* `add_resource()` now supports adding additional resource properties via the `...` argument.
44
* `read_resource()` now supports column selection via the `col_select` argument from `readr::read_delim()`. This can vastly improve reading speed (#123).
5+
* `create_package()` now accepts a `descriptor` argument so that a Data Package object can be created from an existing object (#184). It will always validate the created object with `create_package()`.
6+
* `check_package()` is now a public function, so it can be used by other packages (#185).
57
* `readr::problems()` is included in NAMESPACE so users don't have to load readr to inspect parsing issues. The function is mentioned in the documentation of `read_resource()` (#129).
68
* `cli::cli_abort()`, `cli::cli_warn()` and `cli::cli_inform()` are used for all errors, warnings, and messages (#163). This has several advantages:
79
* Messages use semantic colours for variables, parameters, fields, etc.
810
* Messages and warnings can be silenced with a global or local option, see [this blog post](https://ropensci.org/blog/2024/02/06/verbosity-control-packages/).
911
* Each call has an [rlang](https://cran.r-project.org/package=rlang) class, e.g. `frictionless_error_fields_without_name`, making it easier to test for specific errors.
12+
* A `package` object now has a `datapackage` class (#184), `check_package()` will warn if it is missing.
1013
* The dependencies [glue](https://cran.r-project.org/package=glue) and [assertthat](https://cran.r-project.org/package=assertthat) are removed (#163). The functionality of glue is replaced by cli, `assertthat::assert()` calls are now `if()` functions.
1114
* Adhere to the requirements of [checklist](https://github.com/inbo/checklist), so that `.zenodo.json` can be created with `checklist::update_citation()`.
1215
* Add [Pieter Huybrechts](https://orcid.org/0000-0002-6658-6062) as author. Welcome Pieter!

R/check_package.R

Lines changed: 57 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,76 @@
1-
#' Check Data Package object
1+
#' Check a Data Package object
22
#'
3-
#' Check if an object is a list describing a Data Package, i.e. it has the
4-
#' required properties `resources` and `directory`.
3+
#' Check if an object is a Data Package object with the required properties.
54
#'
6-
#' @param package List describing a Data Package.
5+
#' @inheritParams read_resource
76
#' @return `TRUE` or error.
87
#' @family check functions
9-
#' @noRd
8+
#' @export
109
check_package <- function(package) {
11-
# Check package is a list with resources (list) and directory (character)
12-
if (
13-
!is.list(package) ||
14-
!all(c("resources", "directory") %in% names(package)) ||
15-
!is.list(package$resources) ||
16-
!is.character(package$directory)
17-
) {
10+
general_message <- "{.arg package} must be a Data Package object."
11+
tip_message <- paste(
12+
"Create a valid Data Package object with {.fun read_package} or ",
13+
"{.fun create_package}."
14+
)
15+
16+
# Check package is a list
17+
if (!is.list(package)) {
18+
cli::cli_abort(
19+
c(
20+
general_message,
21+
"x" = "{.arg package} is not a list.",
22+
"i" = tip_message
23+
),
24+
class = "frictionless_error_package_invalid"
25+
)
26+
}
27+
28+
# Check package has resources (list)
29+
if (!is.list(package$resources)) {
30+
cli::cli_abort(
31+
c(
32+
general_message,
33+
"x" = "{.arg package} is missing a {.field resources} property or it is
34+
not a list.",
35+
"i" = tip_message
36+
),
37+
class = "frictionless_error_package_invalid"
38+
)
39+
}
40+
41+
# Check package has directory (character)
42+
if (!is.character(package$directory)) {
1843
cli::cli_abort(
19-
"{.arg package} must be a list describing a Data Package created with
20-
{.fun read_package} or {.fun create_package}.",
44+
c(
45+
general_message,
46+
"x" = "{.arg package} is missing a {.field directory} property or it is
47+
not a character.",
48+
"i" = tip_message
49+
),
2150
class = "frictionless_error_package_invalid"
2251
)
2352
}
2453

2554
# Check all resources (if any) have a name
2655
if (purrr::some(package$resources, ~ is.null(.x$name))) {
2756
cli::cli_abort(
28-
"All resources in {.arg package} must have a {.field name} property.",
57+
"All {.field resources} in {.arg package} must have a {.field name}
58+
property.",
2959
class = "frictionless_error_resources_without_name"
3060
)
3161
}
3262

63+
# Warn if class is missing
64+
if (!"datapackage" %in% class(package)) {
65+
cli::cli_warn(
66+
c(
67+
general_message,
68+
"x" = "{.arg package} is missing a {.val datapackage} class.",
69+
"i" = tip_message
70+
),
71+
class = "frictionless_warning_package_without_class"
72+
)
73+
}
74+
3375
return(TRUE)
3476
}

R/check_path.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#' Check path or URL
1+
#' Check a path or URL
22
#'
33
#' Check if a [path or
44
#' URL](https://specs.frictionlessdata.io/data-resource/#url-or-path) is valid

R/check_schema.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#' Check Table Schema object
1+
#' Check a Table Schema object
22
#'
33
#' Check if an object is a list describing a Table Schema and (optionally)
44
#' compare against a provided data frame.

R/create_package.R

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,47 @@
1-
#' Create an empty Data Package
1+
#' Create a Data Package
22
#'
3-
#' Initiates a list describing a [Data
4-
#' Package](https://specs.frictionlessdata.io/data-package/).
5-
#' This empty Data Package can be extended with metadata and resources (see
6-
#' [add_resource()]).
7-
#' Added resources will make the Data Package meet [Tabular Data
8-
#' Package](https://specs.frictionlessdata.io/tabular-data-package/)
9-
#' requirements, so `profile` is set to `tabular-data-package`.
3+
#' Initiates a [Data Package](https://specs.frictionlessdata.io/data-package/)
4+
#' object, either from scratch or from an existing list.
5+
#' This Data Package object is a list with a `datapackage` class and the
6+
#' following properties:
7+
#' - All properties of the original `descriptor`.
8+
#' - [`resources`](
9+
#' https://specs.frictionlessdata.io/data-package/#required-properties) (an
10+
#' empty list) if not present.
11+
#' - `directory` (set to `"."` for the current directory) if not present.
12+
#' It is used as the base path to access resources with [read_resource()].
1013
#'
11-
#' @return List describing a Data Package.
14+
#' The function will run [check_package()] on the created package to make sure
15+
#' it is valid.
16+
#'
17+
#' @param descriptor List to be made into a Data Package object.
18+
#' If `NULL`, an empty Data Package object will be created from scratch.
19+
#' @return Data Package object.
1220
#' @family create functions
1321
#' @export
1422
#' @examples
1523
#' # Create a Data Package
1624
#' package <- create_package()
1725
#' str(package)
18-
create_package <- function() {
19-
list(
20-
profile = "tabular-data-package",
21-
resources = list(),
22-
directory = "." # Current directory
23-
)
26+
create_package <- function(descriptor = NULL) {
27+
if (!is.null(descriptor) && !is.list(descriptor)) {
28+
cli::cli_abort(
29+
"{.arg descriptor} must be a list if provided.",
30+
class = "frictionless_error_descriptor_invalid"
31+
)
32+
}
33+
34+
# Add properties
35+
descriptor$resources <- replace_null(descriptor$resources, list())
36+
descriptor$directory <- replace_null(descriptor$directory, ".") # Current dir
37+
38+
# Add datapackage class
39+
if (!"datapackage" %in% class(descriptor)) {
40+
class(descriptor) <- c("datapackage", class(descriptor))
41+
}
42+
43+
# Check that created package is valid
44+
check_package(descriptor)
45+
46+
return(descriptor)
2447
}

R/read_package.R

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@
55
#' that describes the Data Package metadata and its Data Resources.
66
#'
77
#' @param file Path or URL to a `datapackage.json` file.
8-
#' @return List describing a Data Package.
9-
#' The function will add a custom property `directory` with the directory the
10-
#' descriptor was read from.
11-
#' It is used as a base path to access resources.
8+
#' @return Data Package object, see [create_package()].
129
#' @family read functions
1310
#' @export
1411
#' @examples
@@ -64,5 +61,5 @@ read_package <- function(file = "datapackage.json") {
6461
}
6562
cli::cli_inform(message, class = "frictionless_message_usage_rights")
6663

67-
descriptor
64+
create_package(descriptor)
6865
}

R/read_resource.R

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@
1010
#' Column names are taken from the provided Table Schema (`schema`), not from
1111
#' the header in the CSV file(s).
1212
#'
13-
#' @param package List describing a Data Package, created with [read_package()]
14-
#' or [create_package()].
13+
#' @param package Data Package object, created with [read_package()] or
14+
#' [create_package()].
1515
#' @param resource_name Name of the Data Resource.
1616
#' @param col_select Character vector of the columns to include in the result,
1717
#' in the order provided.
1818
#' Selecting columns can improve read speed.
19-
#' @return A [tibble()] data frame with the Data Resource's tabular data.
19+
#' @return [tibble()] data frame with the Data Resource's tabular data.
2020
#' If there are parsing problems, a warning will alert you.
2121
#' You can retrieve the full details by calling [problems()] on your data
2222
#' frame.
@@ -177,7 +177,7 @@
177177
#' `character`.
178178
#' - [any](https://specs.frictionlessdata.io/table-schema/#any) as `character`.
179179
#' - Any other value is not allowed.
180-
#' - Type is guessed when not provided.
180+
#' - Type is guessed if not provided.
181181
#' @examples
182182
#' # Read a datapackage.json file
183183
#' package <- read_package(

R/resources.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#'
33
#' Lists the names of the Data Resources included in a Data Package.
44
#'
5-
#' @param package List describing a Data Package.
5+
#' @inheritParams read_resource
66
#' @return Character vector with the Data Resource names.
77
#' @family read functions
88
#' @export

R/write_package.R

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@
1414
#' data are written to a CSV file using [readr::write_csv()], `path` points to
1515
#' location of file, `data` property is removed.
1616
#' Use `compress = TRUE` to gzip those CSV files.
17-
#' @param package List describing a Data Package, created with [read_package()]
18-
#' or [create_package()].
17+
#' @inheritParams read_resource
1918
#' @param directory Path to local directory to write files to.
2019
#' @param compress If `TRUE`, data of added resources will be gzip compressed
2120
#' before being written to disk (e.g. `deployments.csv.gz`).

0 commit comments

Comments
 (0)