Skip to content

Commit 4cc1298

Browse files
ColinFaypachadotdevclaude
authored
implement non-interactive flow for create_if_needed() (#1230)
* implement non-interactive flow for create_if_needed() * Simplify create_if_needed() logic for #1154 - Remove warn_if_exists parameter (not needed for basic behavior) - Simplify overwrite parameter (only for future extensibility) - If file/dir exists and overwrite=FALSE: return TRUE, do nothing - If doesn't exist and interactive: ask user for permission - If doesn't exist and non-interactive: create silently with message() - Update tests to cover all three paths This matches the intended behavior: non-interactive mode creates files instead of failing, but still respects existing files. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com> * chore: version bump & news update --------- Co-authored-by: Mauricio Vargas Sepulveda <mavargas11@uc.cl> Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
1 parent 31d3fea commit 4cc1298

5 files changed

Lines changed: 121 additions & 116 deletions

File tree

DESCRIPTION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Package: golem
22
Title: A Framework for Robust Shiny Applications
3-
Version: 0.5.2.9000
3+
Version: 0.5.1.9015
44
Authors@R: c(
55
person("Colin", "Fay", , "contact@colinfay.me", role = c("cre", "aut"),
66
comment = c(ORCID = "0000-0001-7343-1846")),

NEWS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131

3232
- Renamed a function in 02_dev.R (add_any_file => add_empty_file)
3333

34+
- The `create_if_needed()` function has been fixed to work in non interactive mode (#1154, @pachadotdev)
35+
3436
## Internal changes
3537

3638
- `{golem}` now embarks a `claude.md` file and a series of skills

R/utils.R

Lines changed: 38 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -17,73 +17,67 @@ create_if_needed <- function(
1717
"file",
1818
"directory"
1919
),
20-
content = NULL
20+
content = NULL,
21+
overwrite = FALSE
2122
) {
2223
type <- match.arg(
2324
type
2425
)
2526

2627
# Check if file or dir already exist
2728
if (type == "file") {
28-
dont_exist <- Negate(
29-
fs_file_exists
30-
)(
31-
path
32-
)
29+
already_exists <- fs_file_exists(path)
3330
} else if (type == "directory") {
34-
dont_exist <- Negate(
35-
fs_dir_exists
36-
)(
37-
path
38-
)
31+
already_exists <- fs_dir_exists(path)
32+
}
33+
34+
# If it already exists and overwrite is FALSE, do nothing
35+
if (already_exists && !overwrite) {
36+
return(TRUE)
3937
}
40-
# If it doesn't exist, ask if we are allowed to create it
41-
if (dont_exist) {
38+
39+
# File doesn't exist (or we're overwriting) - need to create it
40+
if (!already_exists) {
4241
if (rlang_is_interactive()) {
42+
# In interactive mode, ask user for permission
4343
ask <- ask_golem_creation_file(
4444
path,
4545
type
4646
)
47-
# Return early if the user doesn't allow
4847
if (!ask) {
49-
return(
50-
FALSE
51-
)
52-
}
53-
# Create the file
54-
if (type == "file") {
55-
fs_file_create(
56-
path
57-
)
58-
write(
59-
content,
60-
path,
61-
append = TRUE
62-
)
63-
} else if (type == "directory") {
64-
fs_dir_create(
65-
path,
66-
recurse = TRUE
67-
)
48+
return(FALSE)
6849
}
6950
} else {
70-
# We don't create the file if we are not in
71-
# interactive mode
72-
stop(
51+
# In non-interactive mode, inform user of creation
52+
message(
7353
sprintf(
74-
"The %s %s doesn't exist.",
75-
basename(
76-
path
77-
),
78-
type
54+
"Creating %s %s",
55+
type,
56+
path
7957
)
8058
)
8159
}
8260
}
61+
62+
# Create the file or directory
63+
if (type == "file") {
64+
fs_file_create(path)
65+
if (!is.null(content)) {
66+
write(
67+
content,
68+
path,
69+
append = !overwrite
70+
)
71+
}
72+
} else if (type == "directory") {
73+
fs_dir_create(
74+
path,
75+
recurse = TRUE
76+
)
77+
}
78+
8379
# TRUE means that file exists (either created or already there)
84-
return(
85-
TRUE
86-
)
80+
return(TRUE)
8781
}
8882

8983
ask_golem_creation_file <- function(

README.md

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
[![Lifecycle:
66
stable](https://img.shields.io/badge/lifecycle-stable-brightgreen.svg)](https://lifecycle.r-lib.org/articles/stages.html)[![R-CMD-check](https://github.com/ThinkR-open/golem/workflows/R-CMD-check/badge.svg)](https://github.com/ThinkR-open/golem/actions)
77
[![Coverage
8-
status](https://codecov.io/gh/ThinkR-open/golem/branch/master/graph/badge.svg)](https://app.codecov.io/github/ThinkR-open/golem/tree/master)[![CRAN
8+
status](https://codecov.io/gh/ThinkR-open/golem/branch/fix/create-if-needed/graph/badge.svg)](https://app.codecov.io/github/ThinkR-open/golem/tree/fix/create-if-needed)[![CRAN
99
status](https://www.r-pkg.org/badges/version/golem)](https://cran.r-project.org/package=golem)
1010

1111
<!-- badges: end -->
1212

1313
# {golem} <img src="https://raw.githubusercontent.com/ThinkR-open/golem/master/inst/rstudio/templates/project/golem.png" align="right" width="120"/>
1414

15-
> You’re reading the doc about version: 0.5.1.9011. Note that `{golem}`
15+
> You’re reading the doc about version: 0.5.1.9015. Note that `{golem}`
1616
> follows the [semantic versioning](https://semver.org/) scheme.
1717
1818
Production-grade `{shiny}` applications, from creation to deployment.
@@ -82,16 +82,16 @@ This `README` has been compiled on the
8282

8383
``` r
8484
Sys.time()
85-
#> [1] "2025-05-30 15:24:30 CEST"
85+
#> [1] "2026-04-13 10:36:56 CEST"
8686
```
8787

8888
Here are the test & coverage results:
8989

9090
``` r
9191
devtools::check(quiet = TRUE)
9292
#> ℹ Loading golem
93-
#> ── R CMD check results ─────────────────────────────────── golem 0.5.1.9011 ────
94-
#> Duration: 52.7s
93+
#> ── R CMD check results ─────────────────────────────────── golem 0.5.1.9015 ────
94+
#> Duration: 45.2s
9595
#>
9696
#> ❯ checking for future file timestamps ... NOTE
9797
#> unable to verify current time
@@ -102,47 +102,47 @@ devtools::check(quiet = TRUE)
102102
``` r
103103
Sys.setenv("NOT_CRAN" = TRUE)
104104
covr::package_coverage()
105-
#> golem Coverage: 88.56%
105+
#> golem Coverage: 87.99%
106106
#> R/boostrap_base.R: 0.00%
107107
#> R/bootstrap_attachment.R: 0.00%
108108
#> R/bootstrap_pkgload.R: 0.00%
109109
#> R/bootstrap_roxygen2.R: 0.00%
110110
#> R/bootstrap_rstudio_api.R: 0.00%
111111
#> R/bootstrap_testthat.R: 0.00%
112112
#> R/bootstrap_dockerfiler.R: 23.33%
113-
#> R/test_helpers.R: 47.37%
114-
#> R/bootstrap_desc.R: 55.56%
115-
#> R/cli_msg.R: 75.64%
116-
#> R/modules_fn.R: 76.44%
117-
#> R/install_dev_deps.R: 78.26%
118-
#> R/addins.R: 78.65%
119-
#> R/add_r_files.R: 78.79%
120-
#> R/config.R: 80.29%
121-
#> R/add_rstudio_files.R: 80.82%
113+
#> R/test_helpers.R: 45.06%
114+
#> R/bootstrap_desc.R: 50.00%
115+
#> R/addins.R: 76.00%
116+
#> R/add_r_files.R: 78.81%
117+
#> R/cli_msg.R: 80.36%
118+
#> R/modules_fn.R: 80.62%
119+
#> R/install_dev_deps.R: 80.70%
120+
#> R/add_rstudio_files.R: 81.36%
121+
#> R/add_dockerfiles_renv.R: 81.40%
122+
#> R/reload.R: 82.83%
122123
#> R/bootstrap_usethis.R: 85.45%
123-
#> R/disable_autoload.R: 88.00%
124-
#> R/reload.R: 88.64%
125-
#> R/sanity_check.R: 88.64%
126-
#> R/add_dockerfiles.R: 89.61%
127-
#> R/add_dockerfiles_renv.R: 89.68%
128-
#> R/make_dev.R: 90.00%
129-
#> R/use_favicon.R: 90.82%
130-
#> R/js.R: 93.75%
131-
#> R/use_files_internal.R: 93.75%
132-
#> R/use_recommended.R: 94.34%
133-
#> R/add_files.R: 95.15%
134-
#> R/create_golem.R: 95.86%
135-
#> R/boostrap_cli.R: 96.43%
136-
#> R/run_dev.R: 96.77%
137-
#> R/desc.R: 96.84%
138-
#> R/use_utils.R: 98.04%
139-
#> R/utils.R: 99.32%
124+
#> R/js.R: 86.21%
125+
#> R/add_dockerfiles.R: 86.73%
126+
#> R/sanity_check.R: 91.86%
127+
#> R/disable_autoload.R: 91.89%
128+
#> R/create_golem.R: 92.67%
129+
#> R/boostrap_cli.R: 92.68%
130+
#> R/add_files.R: 93.75%
131+
#> R/use_favicon.R: 93.84%
132+
#> R/use_files_internal.R: 95.83%
133+
#> R/make_dev.R: 96.43%
134+
#> R/utils.R: 96.93%
135+
#> R/use_recommended.R: 96.94%
136+
#> R/desc.R: 97.71%
137+
#> R/run_dev.R: 98.18%
138+
#> R/use_utils.R: 98.87%
140139
#> R/add_resource_path.R: 100.00%
141140
#> R/boostrap_crayon.R: 100.00%
142141
#> R/boostrap_fs.R: 100.00%
143142
#> R/browser_button.R: 100.00%
144143
#> R/bundle_resources.R: 100.00%
145144
#> R/cats.R: 100.00%
145+
#> R/config.R: 100.00%
146146
#> R/enable_roxygenize.R: 100.00%
147147
#> R/get_sysreqs.R: 100.00%
148148
#> R/globals.R: 100.00%

tests/testthat/test-utils.R

Lines changed: 48 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -15,81 +15,90 @@ test_that("rlang_is_interactive() works", {
1515
})
1616

1717
test_that("create_if_needed creates a file if required", {
18-
expect_error(
18+
# Test: non-interactive mode creates file silently
19+
temp_file <- tempfile()
20+
expect_true(
1921
testthat::with_mocked_bindings(
2022
rlang_is_interactive = function() {
21-
return(
22-
FALSE
23-
)
23+
return(FALSE)
2424
},
2525
code = {
26-
create_if_needed(
27-
tempfile()
28-
)
26+
create_if_needed(temp_file)
2927
}
3028
)
3129
)
30+
expect_true(file.exists(temp_file))
31+
unlink(temp_file)
32+
33+
# Test: interactive mode, user says no
3234
expect_false(
3335
testthat::with_mocked_bindings(
3436
rlang_is_interactive = function() {
35-
return(
36-
TRUE
37-
)
37+
return(TRUE)
3838
},
39-
ask_golem_creation_file = function(
40-
path,
41-
type
42-
) {
43-
return(
44-
FALSE
45-
)
39+
ask_golem_creation_file = function(path, type) {
40+
return(FALSE)
4641
},
4742
code = {
48-
create_if_needed(
49-
tempfile()
50-
)
43+
create_if_needed(tempfile())
5144
}
5245
)
5346
)
47+
48+
# Test: interactive mode, user says yes to file
49+
temp_file <- tempfile()
5450
expect_true(
5551
testthat::with_mocked_bindings(
5652
rlang_is_interactive = function() {
57-
return(
58-
TRUE
59-
)
53+
return(TRUE)
6054
},
6155
ask_golem_creation_file = function(path, type) {
62-
return(
63-
TRUE
64-
)
56+
return(TRUE)
6557
},
6658
code = {
67-
create_if_needed(
68-
tempfile()
69-
)
59+
create_if_needed(temp_file)
7060
}
7161
)
7262
)
63+
expect_true(file.exists(temp_file))
64+
unlink(temp_file)
65+
66+
# Test: interactive mode, user says yes to directory
67+
temp_dir <- tempfile()
7368
expect_true(
7469
testthat::with_mocked_bindings(
7570
rlang_is_interactive = function() {
76-
return(
77-
TRUE
78-
)
71+
return(TRUE)
7972
},
8073
ask_golem_creation_file = function(path, type) {
81-
return(
82-
TRUE
83-
)
74+
return(TRUE)
8475
},
8576
code = {
86-
create_if_needed(
87-
tempfile(),
88-
type = "dir"
89-
)
77+
create_if_needed(temp_dir, type = "directory")
78+
}
79+
)
80+
)
81+
expect_true(dir.exists(temp_dir))
82+
unlink(temp_dir, recursive = TRUE)
83+
84+
# Test: file already exists, should return TRUE without asking
85+
temp_file <- tempfile()
86+
file.create(temp_file)
87+
expect_true(
88+
testthat::with_mocked_bindings(
89+
rlang_is_interactive = function() {
90+
return(TRUE)
91+
},
92+
ask_golem_creation_file = function(path, type) {
93+
# This should NOT be called when file exists
94+
stop("ask_golem_creation_file should not be called")
95+
},
96+
code = {
97+
create_if_needed(temp_file)
9098
}
9199
)
92100
)
101+
unlink(temp_file)
93102
})
94103

95104
test_that("ask_golem_creation_file works", {

0 commit comments

Comments
 (0)