Skip to content

Commit

Permalink
feat: Combine guess and to_config
Browse files Browse the repository at this point in the history
Tags: feat, test, doc

Why?

- Need to migrate package to the new config file management

What?

- Allow `df_to_config()` to run on the output of `check_not_registered_files()`

Issue #24
  • Loading branch information
statnmap committed Aug 1, 2022
1 parent 15a701a commit e232a6d
Showing 1 changed file with 130 additions and 66 deletions.
196 changes: 130 additions & 66 deletions dev/flat_clean_fusen_files.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,31 @@ library(testthat)
pkgload::load_all(export_all = FALSE)
```

This vignette shows tools that are used internally to {fusen} to keep track of all files created during the `inflate()` process.
They are exported for users who start using {fusen} >= 0.5, in a package built without {fusen}, or with earlier versions of {fusen}.

The recommended process for a migration is:

- Run `check_not_registered_files()` on your package
- Open the "dev/config_not_registered.csv" file
+ The csv contains all existing "R/", "tests/" and "vignettes/" files
+ If you used earlier version of {fusen}, the csv may list the flat file of origin
- Clean or modify the csv file
+ Let `origin = "keep"` for files to keep, that are not coming from a flat file
- Run `df_to_config()`


## Protect existing R, tests and vignettes files

`df_to_config()` allows to add your own list of files that you want to `keep` in your package, despite not beeing created with a flat file.
This is important if you started to develop without {fusen}, and start using a flat file from now on, so that {fusen} does not delete your existing files.

```{r function-df_to_config, filename="clean_fusen_files"}
#' Add a tibble of files and types to the 'fusen' config file
#' @param df_files A dataframe with 'type' and 'path' columns
#' @param flat_file_path
#' @param df_files A dataframe with 'type' and 'path' columns
#' or a csv file path as issued from `[check_not_registered_files()]`
#' or nothing (and it will take the csv file in "dev/")
#' @param flat_file_path Character. Usually `"keep"` or the name of the origin flat file, although inflating the flat file should have the same result.
#'
#' @importFrom stats setNames
#'
Expand All @@ -35,14 +51,29 @@ This is important if you started to develop without {fusen}, and start using a f
#' - Change path to config file with `options(fusen.config_file = "dev/config_fusen.yaml")`
#'
df_to_config <- function(df_files, flat_file_path = "keep") {
# Verify df_files
if (!is.data.frame(df_files)) {
stop("df_files should be a dataframe with 'type' and 'path' columns.")
config_file <- getOption("fusen.config_file", default = "dev/config_fusen.yaml")
# User entry verifications
if (missing(df_files)) {
df_files <- file.path(dirname(config_file), 'config_not_registered.csv')
}
if (!is.data.frame(df_files) && file.exists(df_files)) {
df_files <- read.csv(df_files)
} else if (!is.data.frame(df_files) && !file.exists(df_files)) {
stop("'", df_files, "' does not exist. You can run `check_not_registered_files()` before.")
}
# hen if is.data.frame(df_files)
if (!all(c("type", "path") %in% names(df_files))) {
stop("df_files should contains two columns named: 'type' and 'path'")
}
if (!"origin" %in% names(df_files)) {
df_files[["origin"]] <- flat_file_path
}
if (!all(grepl("^R$|^r$|^test$|^tests$|^vignette$|^vignettes$", df_files[["type"]]))) {
stop("Only types 'R', 'test' or 'vignette' are allowed")
}
Expand All @@ -51,84 +82,92 @@ df_to_config <- function(df_files, flat_file_path = "keep") {
stop(paste("Some files in df_files do not exist: ",
paste(
paste0(df_files[["type"]][!all_exists], ": ",
df_files[["path"]][!all_exists]),
df_files[["path"]][!all_exists]),
collapse = ", ")))
}
if (is.null(getOption("fusen.config_file"))) {
config_file <- "dev/config_fusen.yaml"
} else {
config_file <- getOption("fusen.config_file")
}
# TODO Separate function for a unique df
# TODO Run it on multiple 'flat_file_path', if there is column 'origin'
# If there is no 'origin', then fill it with "keep"
# TODO verify same file is not registered under two 'origin'
# Warn the user otherwise
# for each origin as flat_file_path
# browser()
if (file.exists(config_file)) {
complete_yaml <- yaml::read_yaml(config_file)
all_keep_before <- complete_yaml[[basename(flat_file_path)]]
if (!is.null(complete_yaml)) {
cli::cli_alert_info("This files group already existed, it has been overwritten.")
complete_yaml[[basename(flat_file_path)]] <- NULL
}
} else {
complete_yaml <- list()
all_keep_before <- NULL
}
each_flat_file_path <- unique(df_files[["origin"]])
all_groups_list <- lapply(each_flat_file_path, function(x) update_one_group_yaml(df_files, complete_yaml, x)) %>%
setNames(basename(each_flat_file_path))
all_modified <- names(complete_yaml)[names(complete_yaml) %in% names(all_groups_list)]
if (!is.null(all_modified)) {
cli::cli_alert_info(
paste0("Some files group already existed and were overwritten: ",
paste(all_modified, collapse = ", ")))
complete_yaml[all_modified] <- NULL
}
# Combine with complete_yaml
complete_yaml <- c(complete_yaml, all_groups_list)
complete_yaml <- complete_yaml[sort(names(complete_yaml))]
yaml::write_yaml(complete_yaml, file = config_file)
return(config_file)
}
#' Extract name of file along with type to inform the user
#' @param list_of_files A named list of files
#' @noRd
files_list_to_vector <- function(list_of_files) {
lapply(seq_along(list_of_files), function(x) {
if (length(list_of_files[[x]]) != 0) {
paste(names(list_of_files[x]), list_of_files[[x]], sep = ": ")
}
}) %>% unlist()
}
#' Update one group in the complete yaml as list
#' @param complete_yaml The list as output of config_yaml file
#' @param flat_file_path The group to update
#' @noRd
update_one_group_yaml <- function(df_files, complete_yaml, flat_file_path) {
all_keep_before <- complete_yaml[[basename(flat_file_path)]]
this_group_list <- list(
flat = list(
path = flat_file_path,
R = c(df_files$path[grepl("^R$|^r$", df_files$type)]),
tests = c(df_files$path[grepl("^test$|^tests$", df_files$type)]),
vignettes = c(df_files$path[grepl("^vignette$|^vignettes$", df_files$type)])
)
) %>% setNames(basename(flat_file_path))
# All these will be deleted
files_list_to_vector <- function(list_of_files) {
lapply(seq_along(list_of_files), function(x) {
if (length(list_of_files[[x]]) != 0) {
paste(names(list_of_files[x]), list_of_files[[x]], sep = ": ")
}
}) %>% unlist()
}
# Those removed
those_removed <- setdiff(
all_keep_before,
this_group_list[[1]]
this_group_list
)
those_removed_vec <- files_list_to_vector(those_removed)
those_added <- setdiff(
this_group_list[[1]],
this_group_list,
all_keep_before
)
those_added_vec <- files_list_to_vector(those_added)
# Combine with complete_yaml
complete_yaml <- c(complete_yaml, this_group_list)
complete_yaml <- complete_yaml[sort(names(complete_yaml))]
yaml::write_yaml(complete_yaml, file = config_file)
# rstudioapi::navigateToFile(config_file)
if (!is.null(those_removed_vec) || length(those_removed_vec) != 0) {
silent <- lapply(paste(those_removed_vec, "was removed from the config file"), cli::cli_alert_warning)
}
if (!is.null(those_added_vec) || length(those_added_vec) != 0) {
silent <- lapply(paste(those_added_vec, "was added to the config file"), cli::cli_alert_success)
}
return(config_file)
return(this_group_list)
}
```

```{r examples-df_to_config, eval=FALSE}
Expand Down Expand Up @@ -220,7 +259,9 @@ file.create(file.path(dir_tmp, c("tata.R", "toto.R", "test-tata.R", "tata_vignet
withr::with_options(list(fusen.config_file = config_file_path), {
# debugonce(df_to_config)
expect_message(config_file <- df_to_config(all_files)) # "keep" is default
expect_message(
config_file <- df_to_config(all_files),
regexp = "Some files group already existed and were overwritten: keep") # "keep" is default
})
# rstudioapi::navigateToFile(config_file)
Expand All @@ -234,19 +275,19 @@ However, this also requires to register all existing files if you started your p
`check_not_registered_files()` shows files that are not already registered in the yaml config file. The output is consistent with what is needed for `df_to_config()` to register them if wanted.

```{r function-check_not_registered_files, filename="clean_fusen_files"}
#' Show files that are not already registered in the yaml config file
#' Show in a table files that are not already registered in the yaml config file
#'
#' If user start their package without 'fusen' or with version < 0.4, they need to create the config file, with already existing functions.
#'
#' @param path Path to package to check for not registered files
#' @param guess Logical. Guess if the file was inflated by a specific flat file
#' @param to_csv Logical. Whether to store along the config file, the outputs in a csv for the user to clean it manually
#' @return
#' @return Path to csv file if `to_csv` is TRUE. `dput()` of the dataframe otherwise.
#'
#' @export
check_not_registered_files <- function(path = ".", guess = TRUE, to_csv = TRUE) {
path <- normalizePath(path)
all_r <- list.files(file.path(path, "R"), pattern = "[.]R$|[.]r$", full.names = TRUE)
all_test <- list.files(file.path(path, "tests", "testthat"), pattern = "[.]R$|[.]r$", full.names = TRUE)
Expand Down Expand Up @@ -276,23 +317,28 @@ check_not_registered_files <- function(path = ".", guess = TRUE, to_csv = TRUE)
# TODO Are they in the config file already ?
# Read config file, and remove those already there
# All files without path, are changed for "keep"
config_file <- getOption("fusen.config_file", default = "dev/config_fusen.yaml")
csv_file <- file.path(
dirname(config_file),
'config_not_registered.csv')
if (file.exists(config_file)) {
config_list <- yaml::read_yaml(config_file)
}
# Save for manual modification
if (isTRUE(to_csv)) {
if (is.null(getOption("fusen.config_file"))) {
csv_file <- "dev/config_not_registered.csv"
} else {
csv_file <- file.path(
dirname(getOption("fusen.config_file")),
'config_not_registered.csv')
}
write.csv(res, csv_file)
write.csv(res, csv_file, row.names = FALSE)
cli::cli_alert_info(paste(
"Wrote not registered files in:", csv_file,
"Wrote not registered files in: ", csv_file,
"\nKeep only those necessary and run `df_to_config()` on the csv file."))
return(csv_file)
} else {
# return a `dput()` to allow to add to `df_to_config()`
# dput()
return(dput(res))
}
# return a `dput()` to allow to add to `df_to_config()`
# dput()
return(dput(res))
}
#' Guess flat file of origin of a script
Expand Down Expand Up @@ -328,12 +374,11 @@ dev_file <- suppressMessages(add_flat_template(pkg = dummypackage, overwrite = T
flat_file <- dev_file[grepl("flat_", dev_file)]
usethis::with_project(dummypackage, {
browser()
test_that("check_not_registered_files returns message if empty", {
expect_true(inherits(check_not_registered_files, "function"))
debugonce(check_not_registered_files)
# debugonce(check_not_registered_files)
expect_message(check_not_registered_files(), "There are no files in the package")
})
Expand All @@ -352,8 +397,27 @@ usethis::with_project(dummypackage, {
guessed_path <- guess_flat_origin(file.path(dummypackage, "dev", "0-dev_history.Rmd"))
expect_equal(guessed_path, "No path or dont exists")
expect_message(out <- check_not_registered_files(),
regexp = "Wrote not registered files in: dev/config_not_registered.csv")
content_csv <- read.csv(out)
expect_true(all(names(content_csv) %in% c("type", "path", "origin")))
expect_equal(content_csv[["type"]], c("R", "R", "test", "test", "vignette"))
# Include it in df_to_config()
out_config <- df_to_config(df_files = out)
out_config_content <- yaml::read_yaml(out_config)
expect_true(names(out_config_content) == "flat_full.Rmd")
expect_equal(names(out_config_content[["flat_full.Rmd"]]),
c("path", "R", "tests", "vignettes"))
# rstudioapi::navigateToFile(out_config)
browser()
# TODO - Test add a R file manually and include in "keep" after `check_not_registered_files()`
debugonce(check_not_registered_files)
out <- check_not_registered_files()
})
Expand Down

0 comments on commit e232a6d

Please sign in to comment.