1 |
#' Environment Setup Environment |
|
2 |
#' |
|
3 |
#' A dedicated environment object used to store and manage path configurations |
|
4 |
#' and other setup variables for the envsetup package. This environment provides |
|
5 |
#' an isolated namespace for storing path objects that can be retrieved using |
|
6 |
#' the package's path management functions. |
|
7 |
#' |
|
8 |
#' @format An environment object created with \code{new.env()}. |
|
9 |
#' |
|
10 |
#' @details |
|
11 |
#' This environment serves as the default storage location for path objects |
|
12 |
#' when using envsetup package functions. It helps maintain clean separation |
|
13 |
#' between user workspace and package-managed paths. |
|
14 |
#' |
|
15 |
#' @examples |
|
16 |
#' # Store a path in the envsetup environment |
|
17 |
#' assign("project_root", "/path/to/project", envir = envsetup_environment) |
|
18 |
#' |
|
19 |
#' # List objects in the environment |
|
20 |
#' ls(envir = envsetup_environment) |
|
21 |
#' |
|
22 |
#' # Check if the environment exists and is an environment |
|
23 |
#' exists("envsetup_environment") |
|
24 |
#' is.environment(envsetup_environment) |
|
25 |
#' |
|
26 |
#' @seealso \code{\link{get_path}}, \code{\link[base]{new.env}} |
|
27 |
#' |
|
28 |
#' @export |
|
29 |
envsetup_environment <- new.env() |
|
30 | ||
31 |
#' Get Path Object from Environment |
|
32 |
#' |
|
33 |
#' Retrieves a path object from the specified environment using non-standard |
|
34 |
#' evaluation. The function uses `substitute()` to capture the unevaluated |
|
35 |
#' expression and `get()` to retrieve the corresponding object. |
|
36 |
#' |
|
37 |
#' @param path An unquoted name of the path object to retrieve from the environment. |
|
38 |
#' @param envir The environment to search for the path object. Defaults to the |
|
39 |
#' value of `getOption("envsetup.path.environment")`. |
|
40 |
#' |
|
41 |
#' @return The path object stored in the specified environment under the given name. |
|
42 |
#' |
|
43 |
#' @examples |
|
44 |
#' # Create a custom environment and store some paths |
|
45 |
#' path_env <- new.env() |
|
46 |
#' assign("data_dir", "/home/user/data", envir = path_env) |
|
47 |
#' assign("output_dir", "/home/user/output", envir = path_env) |
|
48 |
#' |
|
49 |
#' # Set up the option to use our custom environment |
|
50 |
#' options(envsetup.path.environment = path_env) |
|
51 |
#' |
|
52 |
#' # Retrieve paths using the function |
|
53 |
#' data_path <- get_path(data_dir) |
|
54 |
#' output_path <- get_path(output_dir) |
|
55 |
#' |
|
56 |
#' print(data_path) # "/home/user/data" |
|
57 |
#' print(output_path) # "/home/user/output" |
|
58 |
#' |
|
59 |
#' # Using with a different environment |
|
60 |
#' temp_env <- new.env() |
|
61 |
#' assign("temp_dir", "/tmp/analysis", envir = temp_env) |
|
62 |
#' temp_path <- get_path(temp_dir, envir = temp_env) |
|
63 |
#' print(temp_path) # "/tmp/analysis" |
|
64 |
#' |
|
65 |
#' @seealso \code{\link[base]{get}}, \code{\link[base]{substitute}} |
|
66 |
#' |
|
67 |
#' @export |
|
68 |
get_path <- function(path, envir = getOption("envsetup.path.environment")){ |
|
69 | 1x |
base::get(substitute(path), envir) |
70 |
} |
|
71 | ||
72 |
#' Read path |
|
73 |
#' |
|
74 |
#' Check each environment for the file and return the path to the first. |
|
75 |
#' |
|
76 |
#' The environments searched depends on the current environment. |
|
77 |
#' For example, if your workflow contains a development (dev) area and |
|
78 |
#' production area (prod), and the code is executing in the dev environment, |
|
79 |
#' we search dev and prod. If in prod, we only search prod. |
|
80 |
#' |
|
81 |
#' @param lib object containing the paths for all environments of a directory |
|
82 |
#' @param filename name of the file you would like to read |
|
83 |
#' @param full.path logical to return the path including the file name |
|
84 |
#' @param envsetup_environ name of the environment you would like to read the file from; |
|
85 |
#' default values comes from the value in the system variable ENVSETUP_ENVIRON |
|
86 |
#' which can be set by Sys.setenv(ENVSETUP_ENVIRON = "environment name") |
|
87 |
#' @param envir The environment to search for the path object. Defaults to the |
|
88 |
#' value of `getOption("envsetup.path.environment")`. |
|
89 |
#' |
|
90 |
#' @importFrom rlang quo_get_expr enquo is_string |
|
91 |
#' |
|
92 |
#' @return string containing the path of the first directory the file is found |
|
93 |
#' @export |
|
94 |
#' |
|
95 |
#' @examples |
|
96 |
#' tmpdir <- tempdir() |
|
97 |
#' |
|
98 |
#' # account for windows |
|
99 |
#' if (Sys.info()['sysname'] == "Windows") { |
|
100 |
#' tmpdir <- gsub("\\", "\\\\", tmpdir, fixed = TRUE) |
|
101 |
#' } |
|
102 |
#' |
|
103 |
#' # add config for just the data location |
|
104 |
#' hierarchy <- paste0("default: |
|
105 |
#' paths: |
|
106 |
#' data: !expr list( |
|
107 |
#' DEV = file.path('",tmpdir,"', 'demo', 'DEV', 'username', 'project1', 'data'), |
|
108 |
#' PROD = file.path('",tmpdir,"', 'demo', 'PROD', 'project1', 'data'))") |
|
109 |
#' |
|
110 |
#' # write config file to temp directory |
|
111 |
#' writeLines(hierarchy, file.path(tmpdir, "hierarchy.yml")) |
|
112 |
#' |
|
113 |
#' config <- config::get(file = file.path(tmpdir, "hierarchy.yml")) |
|
114 |
#' |
|
115 |
#' # build folder structure from config |
|
116 |
#' build_from_config(config) |
|
117 |
#' |
|
118 |
#' # setup environment based on config |
|
119 |
#' rprofile(config::get(file = file.path(tmpdir, "hierarchy.yml"))) |
|
120 |
#' |
|
121 |
#' # place data in prod data folder |
|
122 |
#' saveRDS(mtcars, file.path(tmpdir, "demo/PROD/project1/data/mtcars.rds")) |
|
123 |
#' |
|
124 |
#' # find the location of mtcars.rds |
|
125 |
#' read_path(data, "mtcars.rds") |
|
126 |
read_path <- function(lib, |
|
127 |
filename, |
|
128 |
full.path = TRUE, |
|
129 |
envsetup_environ = Sys.getenv("ENVSETUP_ENVIRON"), |
|
130 |
envir = getOption("envsetup.path.environment")) { |
|
131 | ||
132 |
# lib can be a object in a different environment |
|
133 |
# get this directly from envsetup_environment |
|
134 | 10x |
lib_arg <- quo_get_expr(enquo(lib)) |
135 | ||
136 | 10x |
if (is_string(lib_arg)) { |
137 | 1x |
stop(paste( |
138 | 1x |
"The lib argument should be an object containing the paths", |
139 | 1x |
"for all environments of a directory, not a string." |
140 | 1x |
), call. = FALSE) |
141 |
} |
|
142 | ||
143 | 9x |
read_lib <- base::get(toString(lib_arg), envir) |
144 | ||
145 | 9x |
restricted_paths <- read_lib |
146 | ||
147 | 9x |
if (length(read_lib) > 1 && envsetup_environ == "") { |
148 | 1x |
stop(paste( |
149 | 1x |
"The envsetup_environ parameter or ENVSETUP_ENVIRON environment", |
150 | 1x |
"variable must be used if hierarchical paths are set." |
151 | 1x |
), call. = FALSE) |
152 |
} |
|
153 | ||
154 | 8x |
if (envsetup_environ %in% names(read_lib)) { |
155 | 6x |
restricted_paths <- read_lib[which(names(read_lib) == envsetup_environ):length(read_lib)] |
156 | 2x |
} else if (length(read_lib) > 1) { |
157 | 1x |
warning(paste( |
158 | 1x |
"The path has named environments", |
159 | 1x |
usethis::ui_field(names(read_lib)), |
160 | 1x |
"that do not match with the envsetup_environ parameter", |
161 | 1x |
"or ENVSETUP_ENVIRON environment variable", |
162 | 1x |
usethis::ui_field(envsetup_environ) |
163 | 1x |
), call. = FALSE) |
164 |
} |
|
165 | ||
166 |
# find which paths have the object |
|
167 | 8x |
path_has_object <- |
168 | 8x |
sapply( |
169 | 8x |
unlist(restricted_paths), |
170 | 8x |
object_in_path, |
171 | 8x |
filename, |
172 | 8x |
simplify = TRUE |
173 |
) |
|
174 | ||
175 | 8x |
if (any(path_has_object) == FALSE) { |
176 | 1x |
stop(paste0(filename, " not found in ", substitute(read_lib))) |
177 |
} |
|
178 | ||
179 |
# subset and keep the first |
|
180 | 7x |
first_directory_found <- unlist(restricted_paths)[path_has_object][[1]] |
181 | ||
182 | 7x |
if (full.path == TRUE) { |
183 | 6x |
out_path <- file.path(first_directory_found, filename) |
184 |
} else { |
|
185 | 1x |
out_path <- first_directory_found |
186 |
} |
|
187 | ||
188 | 7x |
message("Read Path:", out_path, "\n") |
189 | 7x |
out_path |
190 |
} |
|
191 | ||
192 | ||
193 |
#' Retrieve a file path from an envsetup object containing paths |
|
194 |
#' |
|
195 |
#' Paths will be filtered to produce the lowest available level from a hierarchy |
|
196 |
#' of paths based on envsetup_environ |
|
197 |
#' |
|
198 |
#' @param lib Object containing the paths for all environments of a directory |
|
199 |
#' @param filename Name of the file you would like to write |
|
200 |
#' @param envsetup_environ Name of the environment to which you would like to |
|
201 |
#' write. Defaults to the ENVSETUP_ENVIRON environment variable |
|
202 |
#' @param envir The environment to search for the path object. Defaults to the |
|
203 |
#' value of `getOption("envsetup.path.environment")`. |
|
204 |
#' |
|
205 |
#' @importFrom rlang quo_get_expr enquo is_string |
|
206 |
#' |
|
207 |
#' @return path to write |
|
208 |
#' @export |
|
209 |
#' |
|
210 |
#' @examples |
|
211 |
#' tmpdir <- tempdir() |
|
212 |
#' |
|
213 |
#' # account for windows |
|
214 |
#' if (Sys.info()['sysname'] == "Windows") { |
|
215 |
#' tmpdir <- gsub("\\", "\\\\", tmpdir, fixed = TRUE) |
|
216 |
#' } |
|
217 |
#' |
|
218 |
#' # add config for just the data location |
|
219 |
#' hierarchy <- paste0("default: |
|
220 |
#' paths: |
|
221 |
#' data: !expr list( |
|
222 |
#' DEV = file.path('",tmpdir,"', 'demo', 'DEV', 'username', 'project1', 'data'), |
|
223 |
#' PROD = file.path('",tmpdir,"', 'demo', 'PROD', 'project1', 'data'))") |
|
224 |
#' |
|
225 |
#' # write config file to temp directory |
|
226 |
#' writeLines(hierarchy, file.path(tmpdir, "hierarchy.yml")) |
|
227 |
#' |
|
228 |
#' config <- config::get(file = file.path(tmpdir, "hierarchy.yml")) |
|
229 |
#' |
|
230 |
#' # build folder structure from config |
|
231 |
#' build_from_config(config) |
|
232 |
#' |
|
233 |
#' # setup environment based on config |
|
234 |
#' rprofile(config::get(file = file.path(tmpdir, "hierarchy.yml"))) |
|
235 |
#' |
|
236 |
#' # find location to write mtcars.rds |
|
237 |
#' write_path(data, "mtcars.rds") |
|
238 |
#' |
|
239 |
#' # save data in data folder using write_path |
|
240 |
#' saveRDS(mtcars, write_path(data, "mtcars.rds")) |
|
241 |
write_path <- function(lib, filename = NULL, envsetup_environ = Sys.getenv("ENVSETUP_ENVIRON"), |
|
242 |
envir = getOption("envsetup.path.environment")) { |
|
243 |
# examine lib to ensure it's not a string |
|
244 |
# if it's a string, you end up with an incorrect path |
|
245 | 6x |
lib_arg <- quo_get_expr(enquo(lib)) |
246 | ||
247 | 6x |
if (is_string(lib_arg)) { |
248 | 1x |
stop(paste( |
249 | 1x |
"The lib argument should be an object containing the paths", |
250 | 1x |
"for all environments of a directory, not a string." |
251 | 1x |
), call. = FALSE) |
252 |
} |
|
253 | ||
254 | 5x |
write_path <- base::get(toString(lib_arg), envir) |
255 | 5x |
path <- write_path |
256 | ||
257 | 5x |
if (length(write_path) > 1 && envsetup_environ == "") { |
258 | 1x |
stop(paste( |
259 | 1x |
"The envsetup_environ parameter or ENVSETUP_ENVIRON environment", |
260 | 1x |
"variable must be used if hierarchical paths are set." |
261 | 1x |
), call. = FALSE) |
262 |
} |
|
263 | ||
264 | 4x |
if (envsetup_environ %in% names(write_path)) { |
265 | 2x |
path <- path[[envsetup_environ]] |
266 | 2x |
} else if (length(write_path) > 1) { |
267 | 1x |
warning(paste( |
268 | 1x |
"The path has named environments", |
269 | 1x |
usethis::ui_field(names(lib)), |
270 | 1x |
"that do not match with the envsetup_environ parameter", |
271 | 1x |
"or ENVSETUP_ENVIRON environment variable", |
272 | 1x |
usethis::ui_field(envsetup_environ) |
273 | 1x |
), call. = FALSE) |
274 |
} |
|
275 | ||
276 | 4x |
out_path <- path |
277 | ||
278 | 4x |
if (!is.null(filename)) { |
279 | 2x |
out_path <- file.path(path, filename) |
280 |
} |
|
281 | ||
282 | 4x |
message("Write Path:", out_path, "\n") |
283 | 4x |
out_path |
284 |
} |
|
285 | ||
286 | ||
287 |
# return T/F for if the data exists in the directories |
|
288 |
object_in_path <- function(path, object) { |
|
289 | 20x |
f_path <- file.path(path, object) |
290 | 20x |
file.exists(f_path) |
291 |
} |
|
292 | ||
293 | ||
294 | ||
295 | ||
296 |
#' Build directory structure from a configuration file |
|
297 |
#' |
|
298 |
#' @param config configuration object from config::get() containing paths |
|
299 |
#' @param root root directory to build from. |
|
300 |
#' Leave as NULL if using absolute paths. Set to working directory if using relative paths. |
|
301 |
#' |
|
302 |
#' @importFrom fs dir_tree |
|
303 |
#' @importFrom usethis ui_done |
|
304 |
#' |
|
305 |
#' @return Called for its side-effects. The directories build print as a tree-like format from `fs::dir_tree()`. |
|
306 |
#' @export |
|
307 |
#' |
|
308 |
#' @examples |
|
309 |
#' tmpdir <- tempdir() |
|
310 |
#' |
|
311 |
#' hierarchy <- "default: |
|
312 |
#' paths: |
|
313 |
#' data: !expr list(DEV = '/demo/DEV/username/project1/data', |
|
314 |
#' PROD = '/demo/PROD/project1/data') |
|
315 |
#' output: !expr list(DEV = '/demo/DEV/username/project1/output', |
|
316 |
#' PROD = '/demo/PROD/project1/output') |
|
317 |
#' programs: !expr list(DEV = '/demo/DEV/username/project1/programs', |
|
318 |
#' PROD = '/demo/PROD/project1/programs') |
|
319 |
#' docs: !expr list(DEV = 'docs', |
|
320 |
#' PROD = 'docs')" |
|
321 |
#' |
|
322 |
#' writeLines(hierarchy, file.path(tmpdir, "hierarchy.yml")) |
|
323 |
#' |
|
324 |
#' config <- config::get(file = file.path(tmpdir, "hierarchy.yml")) |
|
325 |
#' |
|
326 |
#' build_from_config(config, tmpdir) |
|
327 |
build_from_config <- function(config, root = NULL) { |
|
328 | 2x |
if (!exists("paths", where = config)) { |
329 | 1x |
usethis::ui_oops("No paths are specified as part of your configuration. Update your config file to add paths.") |
330 | 1x |
return(invisible()) |
331 |
} |
|
332 | ||
333 | 1x |
if (is.null(root)) { |
334 | ! |
paths <- unlist(config$paths, use.names = FALSE) |
335 |
} else { |
|
336 | 1x |
paths <- file.path(root, unlist(config$paths, use.names = FALSE)) |
337 |
} |
|
338 | ||
339 | 1x |
walk(paths, ~ { |
340 | 8x |
if (!dir.exists(.x)) { |
341 | 7x |
dir.create(.x, recursive = TRUE) |
342 |
} |
|
343 |
}) |
|
344 | ||
345 |
# find the root of the paths provided in the config |
|
346 | 1x |
if (is.null(root)) { |
347 | ! |
base_path <- strsplit(paths[1], "")[[1]] |
348 | ||
349 | ! |
for (i in seq_along(paths)) { |
350 | ! |
compare_path <- strsplit(paths[i], "")[[1]] |
351 | ||
352 | ! |
end <- min(length(base_path), length(compare_path)) |
353 | ||
354 | ! |
tf <- base_path[1:end] == compare_path[1:end] |
355 | ||
356 | ! |
first_false <- min(which(tf == FALSE), end + 1) |
357 | ||
358 | ! |
base_path <- base_path[1:first_false - 1] |
359 |
} |
|
360 | ||
361 | ! |
root <- paste0(base_path, collapse = "") |
362 |
} |
|
363 | ||
364 | 1x |
ui_done("Directories built") |
365 | 1x |
dir_tree(root, type = "directory") |
366 |
} |
1 |
#' Initialize the R environment with envsetup |
|
2 |
#' |
|
3 |
#' @param project Character. The path to the project directory. |
|
4 |
#' @param config_path Character. The path of the config file. Defaults to NULL. |
|
5 |
#' @param create_paths Logical indicating if missing paths should be created. Defaults to NULL. |
|
6 |
#' @export |
|
7 |
#' @importFrom usethis ui_yeah ui_oops ui_info ui_done |
|
8 |
#' @importFrom config get |
|
9 |
#' @return Called for its side-effects. |
|
10 |
#' |
|
11 |
#' @examples |
|
12 |
#' tmpdir <- tempdir() |
|
13 |
#' print(tmpdir) |
|
14 |
#' |
|
15 |
#' # account for windows |
|
16 |
#' if (Sys.info()['sysname'] == "Windows") { |
|
17 |
#' tmpdir <- gsub("\\", "\\\\", tmpdir, fixed = TRUE) |
|
18 |
#' } |
|
19 |
#' |
|
20 |
#' # Create an example config file\ |
|
21 |
#' hierarchy <- paste0("default: |
|
22 |
#' paths: |
|
23 |
#' data: !expr list( |
|
24 |
#' DEV = file.path('",tmpdir,"', 'demo', 'DEV', 'username', 'project1', 'data'), |
|
25 |
#' PROD = file.path('",tmpdir,"', 'demo', 'PROD', 'project1', 'data')) |
|
26 |
#' output: !expr list( |
|
27 |
#' DEV = file.path('",tmpdir,"', 'demo', 'DEV', 'username', 'project1', 'output'), |
|
28 |
#' PROD = file.path('",tmpdir,"', 'demo', 'PROD', 'project1', 'output')) |
|
29 |
#' programs: !expr list( |
|
30 |
#' DEV = file.path('",tmpdir,"', 'demo', 'DEV', 'username', 'project1', 'programs'), |
|
31 |
#' PROD = file.path('",tmpdir,"', 'demo', 'PROD', 'project1', 'programs'))") |
|
32 |
#' |
|
33 |
#' |
|
34 |
#' writeLines(hierarchy, file.path(tmpdir, "hierarchy.yml")) |
|
35 |
#' |
|
36 |
#' init(project = tmpdir, |
|
37 |
#' config_path = file.path(tmpdir, "hierarchy.yml"), |
|
38 |
#' create_paths = TRUE) |
|
39 |
init <- function(project, config_path = NULL, create_paths = NULL) { |
|
40 | ||
41 | 5x |
create_config <- FALSE |
42 | 5x |
config_found <- FALSE |
43 | ||
44 | 5x |
if (is.null(config_path)) { |
45 | ! |
create_config <- ui_yeah("No path to an exisiting configuration file was provided. |
46 | ! |
Would you like us to create a default configuration in your project directory?", |
47 | ! |
n_no = 1 |
48 |
) |
|
49 |
} else { |
|
50 | 5x |
if (file.exists(config_path) && !dir.exists(config_path)) { |
51 | 5x |
config_found <- TRUE |
52 | ||
53 | 5x |
usethis::ui_done("Configuration file found!") |
54 | ||
55 |
# verify directories exist |
|
56 | 5x |
config <- config::get(file = config_path) |
57 | ||
58 | 5x |
if (!exists("paths", where = config)) { |
59 | ! |
ui_oops("No paths are specified as part of your configuration. Update your config file to add paths.") |
60 | ! |
return(invisible()) |
61 |
} |
|
62 | ||
63 | 5x |
paths <- unlist(config$paths, use.names = FALSE) |
64 | ||
65 | 5x |
missing_directories <- !vapply(paths, dir.exists, TRUE) |
66 | ||
67 | 5x |
if (any(missing_directories)) { |
68 | 5x |
ui_info( |
69 | 5x |
c("The following paths in your configuration do not exist:", |
70 | 5x |
paths[missing_directories]) |
71 |
) |
|
72 | ||
73 |
# if not, ask if user would like them built |
|
74 | 5x |
if (is.null(create_paths)) { |
75 | ! |
create_paths <- |
76 | ! |
usethis::ui_yeah( |
77 | ! |
"Would you like us to create your directories to match your configuration?", |
78 | ! |
n_no = 1 |
79 |
) |
|
80 |
} |
|
81 | ||
82 | 5x |
if (!create_paths) { |
83 | 5x |
ui_info("All path objects will not work since directories are missing.") |
84 |
} |
|
85 |
} |
|
86 |
} else { |
|
87 | ! |
stop(paste("No configuration file is found at", config_path), call. = FALSE) |
88 |
} |
|
89 |
} |
|
90 | ||
91 |
# if user agrees, write a configuration file to the project directory and create paths |
|
92 | 5x |
if (create_config) { |
93 | ! |
default_path <- system.file("default_envsetup.yml", package = "envsetup", mustWork = TRUE) |
94 | ||
95 | ! |
config_path <- file.path(project, "envsetup.yml") |
96 | ||
97 | ! |
file.copy(default_path, config_path, overwrite = TRUE) |
98 | ||
99 | ! |
ui_done(paste("Configuration file (envsetup.yml) has been written to", project)) |
100 | ||
101 | ! |
create_paths <- TRUE |
102 | 5x |
} else if (config_found <- FALSE) { |
103 | ! |
stop("Aborting envsetup initialization. A configuration file is needed.", call. = FALSE) |
104 |
} |
|
105 | ||
106 |
# create the .Rprofile or add envsetup to the bottom |
|
107 | 5x |
add <- sprintf( |
108 | 5x |
'\nlibrary(envsetup)\nrprofile(config::get(file = "%s"))', |
109 | 5x |
config_path |
110 |
) |
|
111 | ||
112 | 5x |
envsetup_write_rprofile( |
113 | 5x |
add = add, |
114 | 5x |
file = file.path(project, ".Rprofile") |
115 |
) |
|
116 | ||
117 | 5x |
if (create_paths) { |
118 | ! |
build_from_config( |
119 | ! |
config::get(file = config_path) |
120 |
) |
|
121 |
} |
|
122 | ||
123 | 5x |
ui_done("envsetup initialization complete") |
124 |
} |
|
125 | ||
126 |
envsetup_write_rprofile <- function(add, file) { |
|
127 | 5x |
if (!file.exists(file)) { |
128 | 3x |
writeLines(add, file) |
129 | 3x |
ui_done(paste(".Rprofile created")) |
130 | 3x |
return(TRUE) |
131 |
} |
|
132 | ||
133 | 2x |
before <- readLines(file, warn = FALSE) |
134 | ||
135 |
# if there is a call to `rprofile()` in the .Rprofile, assume setup was already done and exit |
|
136 | 2x |
if (any(grepl("rprofile\\(", before))) { |
137 | 1x |
warning("It looks like your project has already been initialized to use envsetup. |
138 | 1x |
Manually adjust your .Rprofile if you need to change the environment setup.", |
139 | 1x |
call. = FALSE |
140 |
) |
|
141 | 1x |
return(invisible()) |
142 |
} |
|
143 | ||
144 | 1x |
after <- c(before, add) |
145 | ||
146 | 1x |
writeLines(after, file) |
147 | ||
148 | 1x |
ui_done(paste(".Rprofile updated")) |
149 |
} |
1 |
#' Set the R autos |
|
2 |
#' |
|
3 |
#' Set the directory paths of any 'autos'. 'Autos' are |
|
4 |
#' directory paths that hold .R files containing R functions. These paths may be |
|
5 |
#' used when functions apply to an analysis, protocol, or even at a global |
|
6 |
#' level, but don't fit in or necessarily require a package or haven't been |
|
7 |
#' incorporated into a package. |
|
8 |
#' |
|
9 |
#' @param autos named list of character vectors |
|
10 |
#' @param envsetup_environ name of the environment you would like to read from; |
|
11 |
#' default values comes from the value in the system variable ENVSETUP_ENVIRON |
|
12 |
#' which can be set by Sys.setenv(ENVSETUP_ENVIRON = "environment name") |
|
13 |
#' @param overwrite logical indicating if sourcing of autos should overwrite an object in global if it already exists |
|
14 |
#' |
|
15 |
#' @return Called for side-effects. Directory paths of the R autos added to search path are printed. |
|
16 |
#' |
|
17 |
#' @importFrom purrr walk walk2 |
|
18 |
#' @importFrom rlang is_named |
|
19 |
#' @importFrom usethis ui_field |
|
20 |
#' @noRd |
|
21 |
set_autos <- function(autos, envsetup_environ = Sys.getenv("ENVSETUP_ENVIRON"), overwrite = TRUE) { |
|
22 | ||
23 |
# Must be named list |
|
24 | 19x |
if (!is_named(autos)) { |
25 | 1x |
stop("Paths for autos in your envsetup configuration file must be named", call. = FALSE) |
26 |
} |
|
27 | ||
28 |
# remove NULL before further processing |
|
29 |
# NULL is expected for hierarchical paths when running in an environment |
|
30 |
# after the first level of the hierarchy |
|
31 | 18x |
autos <- autos[!vapply(autos, is.null, FALSE)] |
32 | ||
33 | 18x |
for (i in seq_along(autos)) { |
34 | 45x |
cur_autos <- autos[[i]] |
35 | ||
36 | 45x |
if (length(cur_autos) > 1) { |
37 |
# Hierarchical paths must be named |
|
38 | 15x |
if (!is_named(cur_autos)) { |
39 | 1x |
stop("Hierarchical autos paths in your envsetup configuration file must be named", call. = FALSE) |
40 |
} |
|
41 | ||
42 |
# envsetup_environ must be used if using hierarchical paths |
|
43 | 14x |
if (envsetup_environ == "") { |
44 | 1x |
stop(paste( |
45 | 1x |
"The envsetup_environ parameter or ENVSETUP_ENVIRON environment", |
46 | 1x |
"variable must be used if hierarchical autos are set." |
47 | 1x |
), call. = FALSE) |
48 |
} |
|
49 |
} |
|
50 | ||
51 | 43x |
if (!is.null(names(cur_autos)) && !envsetup_environ %in% names(cur_autos) |
52 | 43x |
&& envsetup_environ != "") { |
53 | 1x |
warning(paste( |
54 | 1x |
"The", ui_field(names(autos[i])), "autos has named", |
55 | 1x |
"environments", ui_field(names(cur_autos)), |
56 | 1x |
"that do not match with the envsetup_environ parameter", |
57 | 1x |
"or ENVSETUP_ENVIRON environment variable", |
58 | 1x |
ui_field(envsetup_environ) |
59 | 1x |
), call. = FALSE) |
60 |
} |
|
61 | ||
62 | 43x |
filtered_autos <- cur_autos |
63 | ||
64 | 43x |
if (envsetup_environ %in% names(cur_autos)) { |
65 | 12x |
filtered_autos <- |
66 | 12x |
cur_autos[which(names(cur_autos) == envsetup_environ):length(cur_autos)] |
67 |
} |
|
68 | ||
69 | 43x |
autos[[i]] <- filtered_autos |
70 |
} |
|
71 | ||
72 |
# Flatten the paths to collapse the names down to a single vector |
|
73 | 16x |
flattened_paths <- unlist(autos) |
74 | ||
75 |
# Check the autos before they're set |
|
76 | 16x |
if (!(is.null(flattened_paths) || is.character(flattened_paths))) { |
77 | 1x |
stop("Paths provided for autos must be directories", call. = FALSE) |
78 |
} |
|
79 | ||
80 |
# If there are any existing autos then reset them |
|
81 | 15x |
detach_autos() |
82 | ||
83 |
# Now source everything |
|
84 | 15x |
walk2( |
85 | 15x |
flattened_paths, |
86 | 15x |
names(flattened_paths), |
87 | 15x |
~ attach_auto(.x, .y, overwrite = overwrite) |
88 |
) |
|
89 |
} |
|
90 | ||
91 |
#' Source scripts and warn of conflicts |
|
92 |
#' |
|
93 |
#' Source a script, only adding objects to global if they do not already exist |
|
94 |
#' |
|
95 |
#' @param file path to a script containing object to add to global |
|
96 |
#' @param overwrite logical indicating if sourcing should overwrite an object in global if it already exists |
|
97 |
#' |
|
98 |
#' @importFrom usethis ui_value |
|
99 |
#' |
|
100 |
#' @return Called for side-effects. Objects are added to the global environment. |
|
101 |
#' |
|
102 |
#' @noRd |
|
103 |
source_warn_conflicts <- function(file, overwrite = TRUE){ |
|
104 | ||
105 |
# create a new environment to source into |
|
106 | 179x |
new_env <- new.env() |
107 | ||
108 | 179x |
cat("Sourcing file: ", ui_value(file), "\n") |
109 | ||
110 |
# source directory into a this environment |
|
111 | 179x |
sys.source(file, |
112 | 179x |
envir = new_env) |
113 | ||
114 |
# compare objects to find unique and non-unique |
|
115 | 178x |
objects_in_new_env <- ls(new_env) |
116 | 178x |
objects_in_global <- ls(.GlobalEnv) |
117 | ||
118 | 178x |
if (overwrite == FALSE) { |
119 | 16x |
objects_to_assign <- setdiff(objects_in_new_env, objects_in_global) |
120 | 16x |
objects_to_skip_assign <- intersect(objects_in_new_env, objects_in_global) |
121 | 16x |
objects_that_are_overwritten <- NULL |
122 | 162x |
} else if (overwrite == TRUE) { |
123 | 162x |
objects_to_assign <- objects_in_new_env |
124 | 162x |
objects_to_skip_assign <- NULL |
125 | 162x |
objects_that_are_overwritten <- intersect(objects_in_new_env, objects_in_global) |
126 |
} else { |
|
127 | ! |
warning("overwrite must contain a logical") |
128 |
} |
|
129 | ||
130 | 178x |
for (obj_name in objects_to_assign) { |
131 | 229x |
assign_and_move_function(obj_name, temp_env = new_env, envir = .GlobalEnv) |
132 | 229x |
record_function_metadata(obj_name, file) |
133 |
} |
|
134 | ||
135 | 178x |
if (length(objects_to_assign) != 0) { |
136 | 174x |
cat("\n The following objects are added to .GlobalEnv:", sep = "\n") |
137 | 174x |
cat("", sep = "\n") |
138 | 174x |
cat(paste0(" ", ui_value(objects_to_assign), "\n")) |
139 |
} |
|
140 | ||
141 | ||
142 | 178x |
if (length(objects_to_skip_assign) != 0) { |
143 | 6x |
cat("\n The following objects were not added to .GlobalEnv as they already exist:", sep = "\n") |
144 | 6x |
cat("", sep = "\n") |
145 | 6x |
cat(paste0(" ", ui_value(objects_to_skip_assign), "\n")) |
146 |
} |
|
147 | ||
148 | ||
149 | 178x |
if (length(objects_that_are_overwritten) != 0) { |
150 | 51x |
cat("\n The following objects were overwritten in .GlobalEnv:", sep = "\n") |
151 | 51x |
cat("", sep = "\n") |
152 | 51x |
cat(paste0(" ", ui_value(objects_that_are_overwritten), "\n")) |
153 |
} |
|
154 | ||
155 | 178x |
cat("", sep = "\n") |
156 | ||
157 |
} |
|
158 | ||
159 | ||
160 |
assign_and_move_function <- function(obj_name, temp_env, envir){ |
|
161 | 229x |
assign(obj_name, base::get(obj_name, envir = temp_env), envir = envir) |
162 |
} |
|
163 | ||
164 | ||
165 |
record_function_metadata <- function(obj_name, file){ |
|
166 | ||
167 |
# store the metadata for the objects |
|
168 | 229x |
new_record <- data.frame( |
169 | 229x |
object_name = obj_name, |
170 | 229x |
script = file |
171 |
) |
|
172 | ||
173 | 229x |
if (exists("object_metadata", envsetup_environment)) { |
174 | 213x |
df <- merge( |
175 | 213x |
base::get("object_metadata", envsetup_environment), |
176 | 213x |
new_record, |
177 | 213x |
by = "object_name", |
178 | 213x |
all = TRUE |
179 |
) |
|
180 | ||
181 | 213x |
if (any(c("script.x", "script.y") %in% names(df))) { |
182 | 213x |
df$script <- ifelse(is.na(df$script.y), df$script.x, df$script.y) |
183 | 213x |
df$script.x <- NULL |
184 | 213x |
df$script.y <- NULL |
185 |
} |
|
186 | ||
187 | 213x |
envsetup_environment$object_metadata <- df |
188 |
} else { |
|
189 | 16x |
envsetup_environment$object_metadata <- new_record |
190 |
} |
|
191 | ||
192 |
} |
|
193 | ||
194 |
#' Source order of functions |
|
195 |
#' |
|
196 |
#' This function is used to define the sorting order of functions if |
|
197 |
#' `@include` is used to define function dependencies. |
|
198 |
#' |
|
199 |
#' @param path Directory path |
|
200 |
#' |
|
201 |
#' @importFrom utils getFromNamespace |
|
202 |
#' |
|
203 |
#' @noRd |
|
204 |
collate_func <- function(path){ |
|
205 | 48x |
r_scripts <- list.files(path, |
206 | 48x |
pattern = ".r$", |
207 | 48x |
ignore.case = TRUE, |
208 | 48x |
full.names = TRUE |
209 |
) |
|
210 | ||
211 | 48x |
generate_collate <- utils::getFromNamespace("generate_collate", "roxygen2") |
212 | ||
213 | 48x |
collated_func <- generate_collate(path) |
214 | ||
215 | 48x |
if (is.null(collated_func)) { |
216 | 39x |
r_scripts |
217 |
} else { |
|
218 | 9x |
sapply(1:length(collated_func), function(x) file.path(path, collated_func[[x]])) |
219 |
} |
|
220 | ||
221 |
} |
|
222 | ||
223 | ||
224 |
#' Attach a function directory |
|
225 |
#' |
|
226 |
#' This function is used to create an rautos path. All .R files from a given |
|
227 |
#' path are sourced. Functions are imported into from a directory and returned |
|
228 |
#' to an environment. This environment can then be used to attach on to the |
|
229 |
#' search path. This function should not be called directly. To apply autos, |
|
230 |
#' use `set_autos()`. |
|
231 |
#' |
|
232 |
#' @param path Directory path |
|
233 |
#' @param name Directory name |
|
234 |
#' @param overwrite logical indicating if sourcing of autos should overwrite an object in global if it already exists |
|
235 |
#' @noRd |
|
236 |
#' |
|
237 |
#' @return Called for side-effects. Directory paths of the R autos added to search path are printed. |
|
238 |
attach_auto <- function(path, name, overwrite = TRUE) { |
|
239 | ||
240 | 61x |
if (!(dir.exists(path) || file.exists(path))) { |
241 |
# Check if the auto actually exists |
|
242 | 1x |
warning(sprintf("An autos path specified in your envsetup configuration file does not exist: %s = %s", name, path), |
243 | 1x |
call. = FALSE) |
244 | 60x |
} else if (file.exists(path) && !dir.exists(path)) { |
245 |
# if file, source it |
|
246 | 14x |
source_warn_conflicts(path, overwrite = overwrite) |
247 |
} else { |
|
248 | 46x |
collated_r_scripts <- collate_func(path) |
249 | ||
250 | 46x |
if (!identical(collated_r_scripts, character(0))) { |
251 | 46x |
walk(collated_r_scripts, source_warn_conflicts, overwrite = overwrite) |
252 |
} else { |
|
253 | ! |
message("No files found in ", path, ". Nothing to attach.") |
254 |
} |
|
255 |
} |
|
256 |
} |
|
257 | ||
258 |
#' Detach the autos from the current session |
|
259 |
#' |
|
260 |
#' This function will remove any autos that have been set from the search path |
|
261 |
#' |
|
262 |
#' @return Called for its side-effects. |
|
263 |
#' @export |
|
264 |
#' |
|
265 |
#' @examples |
|
266 |
#' tmpdir <- tempdir() |
|
267 |
#' print(tmpdir) |
|
268 |
#' |
|
269 |
#' # account for windows |
|
270 |
#' if (Sys.info()['sysname'] == "Windows") { |
|
271 |
#' tmpdir <- gsub("\\", "\\\\", tmpdir, fixed = TRUE) |
|
272 |
#' } |
|
273 |
#' |
|
274 |
#' # Create an example config file\ |
|
275 |
#' hierarchy <- paste0("default: |
|
276 |
#' paths: |
|
277 |
#' functions: !expr list(DEV = file.path('",tmpdir,"', |
|
278 |
#' 'demo', |
|
279 |
#' 'DEV', |
|
280 |
#' 'username', |
|
281 |
#' 'project1', |
|
282 |
#' 'functions'), |
|
283 |
#' PROD = file.path('",tmpdir,"', |
|
284 |
#' 'demo', |
|
285 |
#' 'PROD', |
|
286 |
#' 'project1', |
|
287 |
#' 'functions')) |
|
288 |
#' autos: |
|
289 |
#' my_functions: !expr list(DEV = file.path('",tmpdir,"', |
|
290 |
#' 'demo', |
|
291 |
#' 'DEV', |
|
292 |
#' 'username', |
|
293 |
#' 'project1', |
|
294 |
#' 'functions'), |
|
295 |
#' PROD = file.path('",tmpdir,"', |
|
296 |
#' 'demo', |
|
297 |
#' 'PROD', |
|
298 |
#' 'project1', |
|
299 |
#' 'functions'))") |
|
300 |
#' |
|
301 |
#' # write config |
|
302 |
#' writeLines(hierarchy, file.path(tmpdir, "hierarchy.yml")) |
|
303 |
#' |
|
304 |
#' config <- config::get(file = file.path(tmpdir, "hierarchy.yml")) |
|
305 |
#' |
|
306 |
#' build_from_config(config) |
|
307 |
#' |
|
308 |
#' # write function to DEV |
|
309 |
#' writeLines("dev_function <- function() {print(environment(dev_function))}", |
|
310 |
#' file.path(tmpdir, 'demo', 'DEV', 'username', 'project1', 'functions', 'dev_function.r')) |
|
311 |
#' |
|
312 |
#' # write function to PROD |
|
313 |
#' writeLines("prod_function <- function() {print(environment(prod_function))}", |
|
314 |
#' file.path(tmpdir, 'demo', 'PROD', 'project1', 'functions', 'prod_function.r')) |
|
315 |
#' |
|
316 |
#' # setup the environment |
|
317 |
#' Sys.setenv(ENVSETUP_ENVIRON = "DEV") |
|
318 |
#' rprofile(config::get(file = file.path(tmpdir, "hierarchy.yml"))) |
|
319 |
#' |
|
320 |
#' # show dev_function() and prod_function() are available and print their location |
|
321 |
#' dev_function() |
|
322 |
#' prod_function() |
|
323 |
#' |
|
324 |
#' # remove autos from search |
|
325 |
#' detach_autos() |
|
326 |
detach_autos <- function() { |
|
327 | ||
328 | 27x |
if (exists("object_metadata", envir = envsetup_environment)){ |
329 | 15x |
rm(list = envsetup_environment$object_metadata$object_name, envir = .GlobalEnv) |
330 | 15x |
rm("object_metadata", envir = envsetup_environment) |
331 |
} |
|
332 | ||
333 |
} |
1 |
#' Function used to pass through code to the .Rprofile |
|
2 |
#' |
|
3 |
#' @param config configuration object from config::get() |
|
4 |
#' @param envir The environment to search for the path object. Defaults to the |
|
5 |
#' value of `getOption("envsetup.path.environment")`. |
|
6 |
#' @param overwrite logical indicating if sourcing of autos should overwrite an object in global if it already exists |
|
7 |
#' @importFrom envnames environment_name |
|
8 |
#' @export |
|
9 |
#' @return Called for its side effects. Directory paths and autos are added to the search path based on your config. |
|
10 |
#' |
|
11 |
#' @examples |
|
12 |
#' # temp location to store configuration files |
|
13 |
#' tmpdir <- tempdir() |
|
14 |
#' print(tmpdir) |
|
15 |
#' |
|
16 |
#' # Create an example config file |
|
17 |
#' hierarchy <- "default: |
|
18 |
#' paths: |
|
19 |
#' data: !expr list(DEV = '/demo/DEV/username/project1/data', |
|
20 |
#' PROD = '/demo/PROD/project1/data') |
|
21 |
#' output: !expr list(DEV = '/demo/DEV/username/project1/output', |
|
22 |
#' PROD = '/demo/PROD/project1/output') |
|
23 |
#' programs: !expr list(DEV = '/demo/DEV/username/project1/programs', |
|
24 |
#' PROD = '/demo/PROD/project1/programs')" |
|
25 |
#' |
|
26 |
#' writeLines(hierarchy, file.path(tmpdir, "hierarchy.yml")) |
|
27 |
#' |
|
28 |
#' rprofile(config::get(file = file.path(tmpdir, "hierarchy.yml"))) |
|
29 |
rprofile <- function(config, |
|
30 |
envir = getOption("envsetup.path.environment"), |
|
31 |
overwrite = TRUE) { |
|
32 | ||
33 |
# remove autos and pass everything else to "envsetup:paths" |
|
34 | 8x |
config_minus_autos <- config |
35 | 8x |
config_minus_autos$autos <- NULL |
36 | ||
37 | 8x |
walk2(names(config_minus_autos$paths), |
38 | 8x |
config_minus_autos$paths, |
39 | 8x |
assign, |
40 | 8x |
envir = envir) |
41 | ||
42 | 8x |
message(paste0("Assigned paths to ", environment_name(envir))) |
43 | ||
44 |
# store config with a standard name in a standard location |
|
45 | 8x |
assign("auto_stored_envsetup_config", config, envir = envir) |
46 | ||
47 |
# If autos exist, set them |
|
48 | 8x |
if (!is.null(config$autos)) { |
49 | 5x |
set_autos(config$autos, overwrite = overwrite) |
50 |
} |
|
51 |
} |
1 |
#' Validate a configuration file |
|
2 |
#' |
|
3 |
#' A helper function to help troubleshoot common problems that can occur when |
|
4 |
#' building your configuration file. |
|
5 |
#' |
|
6 |
#' @param config configuration object from config::get() |
|
7 |
#' |
|
8 |
#' @return Called for its side-effects. Prints findings from validation checks. |
|
9 |
#' @export |
|
10 |
#' |
|
11 |
#' @examples |
|
12 |
#' # temp location to store configuration files |
|
13 |
#' tmpdir <- tempdir() |
|
14 |
#' print(tmpdir) |
|
15 |
#' |
|
16 |
#' # Each path only points to one location, i.e. there is no hierarchy for a path |
|
17 |
#' no_hierarchy <- 'default: |
|
18 |
#' paths: |
|
19 |
#' data: "/demo/DEV/username/project1/data" |
|
20 |
#' output: "/demo/DEV/username/project1/output" |
|
21 |
#' programs: "/demo/DEV/username/project1/programs"' |
|
22 |
#' |
|
23 |
#' writeLines(no_hierarchy, file.path(tmpdir, "no_hierarchy.yml")) |
|
24 |
#' |
|
25 |
#' validate_config(config::get(file = file.path(tmpdir, "no_hierarchy.yml"))) |
|
26 |
#' |
|
27 |
#' # A path can point to multiple locations, i.e. there is a hierarchy |
|
28 |
#' hierarchy <- "default: |
|
29 |
#' paths: |
|
30 |
#' data: !expr list(DEV = '/demo/DEV/username/project1/data', |
|
31 |
#' PROD = '/demo/PROD/project1/data') |
|
32 |
#' output: !expr list(DEV = '/demo/DEV/username/project1/output', |
|
33 |
#' PROD = '/demo/PROD/project1/output') |
|
34 |
#' programs: !expr list(DEV = '/demo/DEV/username/project1/programs', |
|
35 |
#' PROD = '/demo/PROD/project1/programs') |
|
36 |
#' envsetup_environ: !expr Sys.setenv(ENVSETUP_ENVIRON = 'DEV'); 'DEV'" |
|
37 |
#' |
|
38 |
#' writeLines(hierarchy, file.path(tmpdir, "hierarchy.yml")) |
|
39 |
#' |
|
40 |
#' validate_config(config::get(file = file.path(tmpdir, "hierarchy.yml"))) |
|
41 |
#' |
|
42 |
#' # A hierarchy is present for paths, but they are not named |
|
43 |
#' hierarchy_no_names <- "default: |
|
44 |
#' paths: |
|
45 |
#' data: !expr list('/demo/DEV/username/project1/data', '/demo/PROD/project1/data') |
|
46 |
#' output: !expr list('/demo/DEV/username/project1/output', '/demo/PROD/project1/output') |
|
47 |
#' programs: !expr list('/demo/DEV/username/project1/programs', '/demo/PROD/project1/programs') |
|
48 |
#' envsetup_environ: !expr Sys.setenv(ENVSETUP_ENVIRON = 'DEV'); 'DEV'" |
|
49 |
#' |
|
50 |
#' |
|
51 |
#' writeLines(hierarchy_no_names, file.path(tmpdir, "hierarchy_no_names.yml")) |
|
52 |
#' |
|
53 |
#' validate_config(config::get(file = file.path(tmpdir, "hierarchy_no_names.yml"))) |
|
54 |
#' |
|
55 |
#' |
|
56 |
#' # No paths are specified |
|
57 |
#' no_paths <- "default: |
|
58 |
#' autos: |
|
59 |
#' my_functions: '/demo/PROD/project1/R'" |
|
60 |
#' |
|
61 |
#' writeLines(no_paths, file.path(tmpdir, "no_paths.yml")) |
|
62 |
#' |
|
63 |
#' validate_config(config::get(file = file.path(tmpdir, "no_paths.yml"))) |
|
64 |
validate_config <- function(config) { |
|
65 | 4x |
validate_paths(config) |
66 |
} |
|
67 | ||
68 | ||
69 |
#' Validate the paths in a configuration |
|
70 |
#' |
|
71 |
#' @param config configuration object from config::get() |
|
72 |
#' |
|
73 |
#' @importFrom usethis ui_done ui_info |
|
74 |
#' @importFrom purrr walk |
|
75 |
#' |
|
76 |
#' @noRd |
|
77 |
validate_paths <- function(config) { |
|
78 |
# does the paths element exist? |
|
79 | 4x |
if (exists("paths", where = config)) { |
80 | 3x |
ui_done("paths are specified as part of your configuration") |
81 |
} else { |
|
82 | 1x |
ui_info("no paths are specified as part of your configuration, skipping path valiation") |
83 | 1x |
return(invisible()) |
84 |
} |
|
85 | ||
86 |
# any hierarchical paths? |
|
87 | 3x |
if (is_hierarchical(config$paths)) { |
88 | 2x |
ui_done(c("hierarchal paths found for:", names(config$paths[sapply(config$paths, is.list)]))) |
89 |
} else { |
|
90 | 1x |
ui_info("no hierarchical paths found") |
91 | 1x |
return(invisible()) |
92 |
} |
|
93 | ||
94 |
# are your hierarchical paths named |
|
95 | 2x |
has_hierarchy <- sapply(config$paths, is.list) |
96 | ||
97 | 2x |
check_for_names <- names(config$paths[has_hierarchy]) |
98 | ||
99 | 2x |
has_names <- function(.x) { |
100 | 6x |
!is.null(names(config$paths[has_hierarchy][[.x]])) |
101 |
} |
|
102 | ||
103 | 2x |
name_results <- sapply(check_for_names, has_names) |
104 | ||
105 | 2x |
walk( |
106 | 2x |
names(name_results[!name_results]), |
107 | 2x |
~ usethis::ui_todo( |
108 | 2x |
paste0( |
109 | 2x |
usethis::ui_field(.), |
110 | 2x |
" has a hierarchy but they are not named. Please update your configuration to name the hierarchy for ", |
111 | 2x |
usethis::ui_field(.), "." |
112 |
) |
|
113 |
) |
|
114 |
) |
|
115 |
} |
|
116 | ||
117 |
is_hierarchical <- function(x) { |
|
118 | 3x |
tf <- sapply(x, is.list) |
119 | 3x |
any(tf) |
120 |
} |
1 |
.onLoad <- function(libname, pkgname) { |
|
2 | ! |
op <- options() |
3 | ! |
op.envsetup <- list( |
4 | ! |
envsetup.config.path = system.file("_envsetup.yml", |
5 | ! |
package = "envsetup", |
6 | ! |
mustWork = TRUE |
7 |
), |
|
8 | ! |
envsetup.rprofile.path = system.file(".Rprofile", package = "envsetup"), |
9 | ! |
envsetup.renviron.path = system.file(".Renviron", package = "envsetup"), |
10 | ! |
envsetup.path.environment = .GlobalEnv |
11 |
) |
|
12 | ||
13 | ! |
toset <- !(names(op.envsetup) %in% names(op)) |
14 | ! |
if (any(toset)) options(op.envsetup[toset]) |
15 | ||
16 | ! |
invisible() |
17 |
} |