Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

shiny.autoreload in {golem} #263

Open
ColinFay opened this issue Nov 1, 2019 · 34 comments
Open

shiny.autoreload in {golem} #263

ColinFay opened this issue Nov 1, 2019 · 34 comments
Labels
bug Something isn't working

Comments

@ColinFay
Copy link
Member

ColinFay commented Nov 1, 2019

As of right now, options(shiny.autoreload = TRUE) doesn't work with golem

@ColinFay
Copy link
Member Author

ColinFay commented Nov 1, 2019

Related SO question by @xari https://stackoverflow.com/questions/58648662/autoreload-in-golem-apps-shiny-option-doesnt-work

@ColinFay
Copy link
Member Author

ColinFay commented Nov 1, 2019

Ok so as far as I can tell it's because this behavior is triggered by shiny:::initAutoReloadMonitor, which is triggered when running a "standard app" in a directory with runApp, which is not triggered by the current implementation of golem.

For example, if I create a new golem, delete the R/ folder, create a shiny app in a folder called R and do :

> options(shiny.autoreload = TRUE)
> runApp(appDir = 'R')

The option works as expected.

And :

> trace("runApp", where = asNamespace("shiny"))
> trace("initAutoReloadMonitor", where = asNamespace("shiny"))
Tracing function "initAutoReloadMonitor" in package
"namespace:shiny"
[1] "initAutoReloadMonitor"

> runApp(appDir = 'R')
trace: runApp(appDir = "R")
Tracing initAutoReloadMonitor(appDir) on entry 

So runApp on directory works.

Then back in a golem app:

> options(shiny.autoreload = TRUE)
> trace("runApp", where = asNamespace("shiny"))
> trace("initAutoReloadMonitor", where = asNamespace("shiny"))

Then launch the run_dev,

> # Run the application
> lpppa::run_app()
Tracing runApp(x) on entry 
Loading required package: shiny

Listening on http://127.0.0.1:5164

I have the trace of runApp, but no initAutoReloadMonitor. So apparently the runApp called from golem (natively) doesn't launch the reloadMonitor.

Not sure how to handle that so far 🤔

@ColinFay ColinFay added the bug Something isn't working label Nov 27, 2019
@mik3y64
Copy link

mik3y64 commented Nov 29, 2019

I am having this issue and it's slowing down my development workflow. I did some test too (by intentionally not using golem to find out the root cause) and I suspect it's the issue with Shiny modules? Golem app uses modules and it gets affected too?

Filed similar issues at shiny but without any help. autoreload doesn't load with Shiny modules #2711

In my package (it's golem but doesn't matter because I am running the app without any golem configurations), R/ dir have three files

  • R/app.R
  • R/shiny_run.R
  • R/my_module.R

autoreload works without module

# app.R
library(shiny)

ui <- fluidPage(
  selectInput("sample_input", "changeLabel", choices = letters[1:3])
)
server <- function(input, output, session) {
  NULL
}
shinyApp(ui = ui, server = server)
# shiny_run.R
options(shiny.autoreload = TRUE)
shiny::runApp()

Editing the "changeLabel" in selectInput() does the change the label.

autoreload doesn't work with module

# my_module.R
moduleUI <- function(id) { 
  ns <- NS(id)
  selectInput(ns("sample_input"), "changeLabel", choices = letters[1:3])
}
moduleServer <- function(input, output, session) {
  NULL
}
# app.R
library(shiny)
source("my_module.R")

ui <- fluidPage(
  moduleUI('test')
)
server <- function(input, output, session) {
  callModule(moduleServer, 'test')
}
shinyApp(ui = ui, server = server)
# shiny_run.R
options(shiny.autoreload = TRUE)
shiny::runApp()

Editing the "changeLabel" in selectInput() does refresh the Shiny application but "changeLabel" does not change at all.

@julianstanley
Copy link

julianstanley commented May 24, 2020

I'm just getting started with this, but I've been using a relatively hacky solution for this issue. Not sure if it's helpful to others, but I'll throw it here in case:


So, each time you change bits of your shiny application, you're supposed to run run_dev.R, right?

Instead of getting auto.reload to work, I just use bash to watch for changes in the R/ directory and call run_dev.R each time I see changes. Of course, it takes a bit to run run_dev.R, but I still find it handy.

Step 1: Set the port

I set that in run_dev.R:

# run_dev.R
[...]

# Run the application
options(shiny.port = 11616) # Added this line
run_app()

I just use port 11616 for fun, since it spells "app" (A=1, P=16).

Step 2: Create utils/watch_app.sh and utils/run_app.sh

Then I create a ./utils folder in my shiny directory and put two files there: watch_app.sh and run_app.sh.

watch_app.sh just checks for changes in the "R" directory. If it sees any changes, it: (1) kills all processes on port 11616, and (2) runs the run_dev.R script.

#!/bin/bash -eu
find ../R | entr sh -c 'lsof -i:11616 | awk '"'"'NR!=1 {print $2}'"'"' | xargs kill ; (cd .. && Rscript ./dev/run_dev.R &)'

run_app.sh (1) makes sure that all processes on port 11616 are killed when watch_app.sh is killed, and (2) runs watch_app.sh:

#!/usr/bin/env bash
trap watch_app.sh kill $( lsof -i:11616 -t)
bash watch_app.sh

Typical workflow

(1) Open up RStudio and my project
(2) Open up a terminal, navigate to [project-root]/utils, run run_app.sh, minimize terminal
(3) Open up a browser, navigate to localhost:11616
(4) Edit things in RStudio and refresh the browser when I want to see changes.

It's definitely a lot slower than Shiny's autorefresh, but it seems to work ok.


Sources: using entr | killing by port | nesting single quotes hack.

@gueyenono
Copy link

gueyenono commented Aug 22, 2020

My solution to this issue makes use of a very small Javascript script, which automatically reloads the Shiny app (which is in fact a web page) after a desired length of time. I realize that many Shiny users are not comfortable with using Javascript and I, myself, am learning as of the time of writing this. So, I will write my solution in the form of a detailed step by step guide:

1- Create a Javascript file

The {golem} package makes the first step very trivial. Open the dev/02_dev.R file and look for the function golem::add_js_file(). Execute it by passing your desired file name to it as a character string (e.g. golem::add_js_file("script")). Note that you do not need to specify any extension to the file name. After running this code, the file script.js will be created in the inst/app/www/ folder. Look for it and open it (it does not matter whether you open it in RStudio or not).

2- Add the JavaScript code

Copy the code below, paste it in script.js and save it:

  // Function for reloading the page
  reloadPage = () => {
    window.location.reload()
  }
  setTimeout(reloadPage, 1000) // The page is reloaded every second (1000 milliseconds), adjust it to your needs

Do not forget to change the reload interval, which is 1000 milliseconds above (i.e. one second), if you want to.

3- Run your application

Run your application as usual by running the code in dev/run_dev.R.

Warning

Keep in mind that this hack is mostly useful for building your UI and that you will not be able to efficiently test your app while the script is running because it will automatically reload. For this reason options(shiny.autoreload = TRUE) would still be a more efficient way in your app development process.

In order to stop the script from running, you can just comment it out by using // at the beginning of each line of code:

  // Function for reloading the page
  // reloadPage = () => {
  //    window.location.reload()
  // }
  // setTimeout(reloadPage, 1000) // The page is reloaded every second (1000 milliseconds), adjust it to your needs

Source: this solution was adapted from this Stackoverflow thread.

@gorgitko
Copy link

gorgitko commented Oct 30, 2020

Hello everyone!

I have also came to this problem and it's slowing down my productivity. The solution proposed by @julianstanley is quite similar to the Shiny's native autoreload, but I have actually realized that I like more something similar to the classic devtools shortcut Ctrl+Shift+L (calls load_all()). So I have made a simple reload Bash script and utilized the shrtcts package, which can assign any R command to keyboard shortcut (within RStudio). Now I can reload my Shiny app with Ctrl+Shift+R and watch its output in RStudio terminal. The steps are simple:

  1. Put this script in <your golem package>/utils/reload_app.sh:
#!/usr/bin/env bash
cd "$(dirname "$0")"

echo "Running $0"

APP_NAME=$(basename $(realpath ../))

app_pid=${APP_NAME}.pid

if [[ -f $app_pid ]]; then
  printf "$APP_NAME is running, killing ... "
  pkill -F $app_pid
  printf "Done."
fi

printf "\nStarting $APP_NAME ... "
nohup Rscript ../dev/run_dev.R > $APP_NAME.log 2>&1 &
echo $! > $app_pid
printf "Done.\n"

I don't like killing processes through filtering their PID, so here, PID is saved to file and later used for killing.

  1. Install shrtcts
  2. Put this into ~/.Rprofile:
reload_shiny_app <- function() {
  system2("bash", paste0(golem::get_golem_wd(), "/utils/reload_app.sh"))

  cat("Waiting for Shiny app to start...\n")
  for (i in 1:1000) {
    is_app_running <- system2("curl", c("-IL", "http://localhost:8080"), stdout = NULL, stderr = NULL)

    ##-- exit code 0 means everything is fine :)
    if (!is_app_running) {
      rstudioapi::viewer("http://localhost:8080/")
      break
    }

    Sys.sleep(0.1)
  }
}

The app port is hardcoded here, so change it according to your settings. Make sure you have curl installed, as it is used to check whether the app is running.

  1. Put this into ~/.shrtcts.R:
#' Reload current Shiny application.
#'
#' @description <app package>/utils/reload_app.sh must be present.
#'
#' @interactive
#' @shortcut Ctrl+Shift+R
globalenv()$reload_shiny_app

I don't understand how shrtcts is parsing the shortcut command, but if you directly use in shortcut definition what's in the reload_shiny_app(), then reload_app.sh is executed twice. But maybe just something weird is happening on my side, so feel free to try it without the intermediate reload_shiny_app() 😉

  1. In RStudio you can now press Ctrl+Shift+R (or whatever you define in ~/.shrtcts.R) and your app will be started. Then open terminal, navigate to utils directory and run tail -f <your app name>.log to watch app's output. Anytime you press Ctrl+Shift+R, your app will be reloaded and shown in RStudio viewer pane.

I think this sort of shortcut could be added to golem's RStudio addin.

@ColinFay
Copy link
Member Author

ColinFay commented Feb 4, 2021

Ok folks, so to sum up the issue is that the autoreload only works when calling runApp() on a directory, and by default {golem} apps don't do this runApp().

Here is the workaround to make this work:

  • Step 1: create an app.R deployment file with golem::add_rstudioconnect_file().

  • Step 2: change your dev/run_dev.R script as such :

# Set options here
options(golem.app.prod = FALSE) # TRUE = production mode, FALSE = development mode

# Detach all loaded packages and clean your environment
golem::detach_all_attached()
# rm(list=ls(all.names = TRUE))

# Document and reload your package
golem::document_and_reload()

options(shiny.autoreload = TRUE)
# Run the application
shiny::runApp(appDir = here::here())

Enjoy 🎉

@xari
Copy link

xari commented Feb 4, 2021

Ok folks, so to sum up the issue is that the autoreload only works when calling runApp() on a directory, and by default {golem} apps don't do this runApp().

Here is the workaround to make this work:

* Step 1: create an app.R deployment file with `golem::add_rstudioconnect_file()`.

* Step 2: change your dev/run_dev.R script as such :
# Set options here
options(golem.app.prod = FALSE) # TRUE = production mode, FALSE = development mode

# Detach all loaded packages and clean your environment
golem::detach_all_attached()
# rm(list=ls(all.names = TRUE))

# Document and reload your package
golem::document_and_reload()

options(shiny.autoreload = TRUE)
# Run the application
shiny::runApp()

Enjoy 🎉

Thanks for the follow-up, @ColinFay .

When my colleague and I originally encountered this issue, we came to the conclusion that our development workflow must have been fundamentally different from yours. At the time, we would make amendments to our business logic code, and then re-start our app to confirm that everything worked as intended. This involved a lot of waiting for the app to re-load, etc.

The conclusion that we came to is that you probably follow more of a test-driven-development workflow, where you run smaller pieces of your business logic without the need to start-up the entire app. Working this way means that the auto-reload feature is generally less necessary, as you only really need to start -up the app in development when testing UI features.

Chapter 10 of your book gets into TDD somewhat, but it could also be interesting to see a screencast that addresses this topic of "efficient development workflow".

@dwhdai
Copy link

dwhdai commented Feb 4, 2021

Ok folks, so to sum up the issue is that the autoreload only works when calling runApp() on a directory, and by default {golem} apps don't do this runApp().

Here is the workaround to make this work:

  • Step 1: create an app.R deployment file with golem::add_rstudioconnect_file().
  • Step 2: change your dev/run_dev.R script as such :
# Set options here
options(golem.app.prod = FALSE) # TRUE = production mode, FALSE = development mode

# Detach all loaded packages and clean your environment
golem::detach_all_attached()
# rm(list=ls(all.names = TRUE))

# Document and reload your package
golem::document_and_reload()

options(shiny.autoreload = TRUE)
# Run the application
shiny::runApp()

Enjoy 🎉

Hmm, I just tried this and it didn't work for me. My workflow was:

  1. Create app.R and make changes to dev/run_dev.R as Colin suggested
  2. Click "Run App" in dev/run_dev.R.
  3. Make changes in either app_ui.R or a UI module; neither change registered after refreshing the browser.

Attaching my session info below

image

@nvelden
Copy link

nvelden commented Mar 18, 2021

I followed the same steps and it also doesn't work for me.

@ColinFay
Copy link
Member Author

ColinFay commented Apr 2, 2021

@dwhdai this refreshes only if you save the file inside R (not the browser), and also, there is an issue with {shiny} itself rstudio/shiny#2711 :)

I feel like this should be implemented at {golem} level so that we have this under control.

For example in golem::run_dev() 🤔

@rsimonmd
Copy link

rsimonmd commented Jun 7, 2021

EDIT: Unintentionally wrote this in French at first ...

Hi @ColinFay,

The aformentionned solution works (I merely added appDir = here::here() in order to make it work with rstudio jobs while leaving run_dev.R inside the /dev folder, but its not what matters), but by "works" I mean "the page refreshes"

However, while the page refreshes, the UI changes do not appear. Unless I make a change to app.R, in which case the app in correctly refreshed and changes to modules taken into account. Of course, right after I wrote this I found the same kind of comment in the {shiny} repo ...

In any case, while I do not have a solution to contribute, knowing how to do this is good enough to make it work, I just add/remove a character in app.R and I can dev the app while watching my changes

So, thank you for writing a solution, and for what you guys do with {golem} :-)

@ColinFay
Copy link
Member Author

Note to self : here is a prototype of implementing an auto.reload in {golem}:

loops <- new.env()

run_dev_later <- function(){
  hash <- digest::digest(
    file.info(
      list.files(
        path = c("R/", "inst"),
        recursive = TRUE, 
        full.names = TRUE
      )
    )[, "mtime"]
  )
  proc <- processx::process$new(
    stderr = "|",
    stdout = "|",
    "Rscript", c(
      "-e", 
      "golem::run_dev()"
    )
  )
  Sys.sleep(5)
  
  run_dev_now <- function(){
    
    new_hash <- digest::digest(
      file.info(
        list.files(
          path = c("R/", "inst"),
          recursive = TRUE, 
          full.names = TRUE
        )
      )[, "mtime"]
    )
    if (new_hash != hash){
      cli::cat_rule("Relaunching the app")
      hash <<- new_hash
      proc$interrupt()
      proc <<- processx::process$new(
        "Rscript", c(
          "-e", 
          "golem::run_dev()"
        )
      )
      Sys.sleep(5)
    }
    later::later(
      run_dev_now, 
      1, 
      loop = loops$run_dev
    )
  }
  loops$run_dev <- later::create_loop()
  run_dev_now()
}

run_dev_later()

# When done
later::destroy_loop(
  loops$run_dev
)

@ColinFay
Copy link
Member Author

ColinFay commented Jul 28, 2021

Don't forget to put the stderr and stdout in a file.

logs <- tempfile()
proc <- processx::process$new(
  stderr = logs,
  stdout = logs,
  "Rscript", c(
    "-e", 
    "golem::run_dev()"
  )
)
Sys.sleep(5)
readLines(logs)
proc$kill()

The same terminal as R would be the best :

proc <- processx::process$new(
  stderr = "",
  stdout = "",
  "Rscript", c(
    "-e", 
    "golem::run_dev()"
  )
)
Sys.sleep(5)
proc$kill()

Cervangirard pushed a commit that referenced this issue Jul 29, 2021
After 2 years, as much expected as Kamelott, here is finally the feature for autoreload :)

Close #263 #303
@GitHunter0
Copy link

Can't wait for version 0.4.0 with this autoreload feature, it will make the app development process way quicker and a lot more pleasurable

@Cervangirard
Copy link
Member

Can't wait for version 0.4.0 with this autoreload feature, it will make the app development process way quicker and a lot more pleasurable

Hi,

It is implemented in the autorelaod branch.
You can test it by installing this branch with the following command:

remotes::install_github("thinkr-open/golem", "autorelaod")

And inside your golem application golem::run_dev(autoreaload = TRUE)

@vreederene-90
Copy link

Ive tested the solutions with a new golem project to nullify any of my own tinkering.
After installing the branch and running golem::run_dev(autoreload = TRUE), then editing a file, saving it and refreshing the browser, I dont see the changes I've made (e.g., I added a h1 tag on the main ui file). The other fix you suggested a while back (golem::add_rstudioconnect_file()), doesnt work for me either. I can see that the page refreshes, but I dont see the UI being updated. I've tried to source the modules' file in the main app_server.R as well, but that didnt work either.
I've tried combinations of these solutions as well (e.g., run_dev(autoreload = TRUE)) with the rstudioconnect version of dev/run-dev.R), but cannot seem to get it to work.

Im using Windows 19043.1288, R 4.1.1 and shiny 1.7.1.

@GitHunter0
Copy link

GitHunter0 commented Oct 28, 2021

Thank you, @Cervangirard. I'm having a strange issue, when I run golem::run_dev(autoreaload = TRUE), I receive this error message:

Error in golem::run_dev(autoreaload = TRUE) : 
  unused argument (autoreaload = TRUE)

It is like the parameter autoreaload does not exist but when I inspect run_dev(), it is there.

I removed and reinstalled Golem several times using remotes::install_github("thinkr-open/golem", "autorelaod") but error persists.

sessionInfo()
#> R version 4.1.1 (2021-08-10)
#> Platform: x86_64-w64-mingw32/x64 (64-bit)
#> Running under: Windows 10 x64 (build 19043)
#> 
#> Matrix products: default
#> 
#> locale:
#> [1] LC_COLLATE=English_United States.1252 
#> [2] LC_CTYPE=English_United States.1252   
#> [3] LC_MONETARY=English_United States.1252
#> [4] LC_NUMERIC=C                          
#> [5] LC_TIME=English_United States.1252    
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> loaded via a namespace (and not attached):
#>  [1] digest_0.6.28   withr_2.4.2     magrittr_2.0.1  reprex_2.0.1   
#>  [5] evaluate_0.14   highr_0.9       stringi_1.7.5   rlang_0.4.11   
#>  [9] cli_3.0.1       rstudioapi_0.13 fs_1.5.0        rmarkdown_2.10 
#> [13] tools_4.1.1     stringr_1.4.0   glue_1.4.2      xfun_0.27      
#> [17] yaml_2.2.1      fastmap_1.1.0   compiler_4.1.1  htmltools_0.5.2
#> [21] knitr_1.36

**Golem 0.3.1.9002**

Created on 2021-10-28 by the reprex package (v2.0.1)

@rsimonmd
Copy link

rsimonmd commented Nov 2, 2021

Thank you, @Cervangirard. I'm having a strange issue, when I run golem::run_dev(autoreaload = TRUE), I receive this error message:

Error in golem::run_dev(autoreaload = TRUE) : 
  unused argument (autoreaload = TRUE)

It is like the parameter autoreaload does not exist but when I inspect run_dev(), it is there.

I removed and reinstalled Golem several times using remotes::install_github("thinkr-open/golem", "autorelaod") but error persists.

sessionInfo()
#> R version 4.1.1 (2021-08-10)
#> Platform: x86_64-w64-mingw32/x64 (64-bit)
#> Running under: Windows 10 x64 (build 19043)
#> 
#> Matrix products: default
#> 
#> locale:
#> [1] LC_COLLATE=English_United States.1252 
#> [2] LC_CTYPE=English_United States.1252   
#> [3] LC_MONETARY=English_United States.1252
#> [4] LC_NUMERIC=C                          
#> [5] LC_TIME=English_United States.1252    
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> loaded via a namespace (and not attached):
#>  [1] digest_0.6.28   withr_2.4.2     magrittr_2.0.1  reprex_2.0.1   
#>  [5] evaluate_0.14   highr_0.9       stringi_1.7.5   rlang_0.4.11   
#>  [9] cli_3.0.1       rstudioapi_0.13 fs_1.5.0        rmarkdown_2.10 
#> [13] tools_4.1.1     stringr_1.4.0   glue_1.4.2      xfun_0.27      
#> [17] yaml_2.2.1      fastmap_1.1.0   compiler_4.1.1  htmltools_0.5.2
#> [21] knitr_1.36

**Golem 0.3.1.9002**

Created on 2021-10-28 by the reprex package (v2.0.1)

Hello,

This is most likely becquse you wrote "autoreaload" instead of "autoreload"

Best,

Raphael

@GitHunter0
Copy link

Haha simplest solution ever (I had copied and pasted, then the typo happened). Thank you, @rsimonmd !

However, the command is not working for me either, I'm having the same issue as @vreederene-90 :

After installing the branch and running golem::run_dev(autoreload = TRUE), then editing a file, saving it and refreshing the browser, I dont see the changes I've made (e.g., I added a h1 tag on the main ui file).

@GitHunter0
Copy link

@Cervangirard , I believe there is a misspelling in the name of the branch autorelaod , shouldn't it be named autoreload ?

@toufikswar
Copy link

Hello @ColinFay,

Many thanks for all the work you are doing on Golem, great package, I am fan.

Regarding this autoreload bug in golem, has it been addressed?

Thanks!!

@gacolitti
Copy link

The new autoreload option does not seem to respect different modals for viewing the app. For example, it does not use the RStudio window viewer when this line is run:

options(shiny.launch.browser = .rs.invokeShinyWindowViewer)

Moreover, autoreload always opens in a new external browser tab on reload. This is different from the normal Shiny autoreload which will refresh the preexisting browser tab.

This feature would definitely be nice to have with some refinement.

@vreederene-90
Copy link

Ive written a small package that includes two workarounds to this problem: autoreload.fix.

@toufikswar
Copy link

Ive written a small package that includes two workarounds to this problem: autoreload.fix.

Hello @vreederene-90,

Thanks for the effort.

I am trying your package. So basically I have my golem project, I added the option(shiny.autoreload = TRUE) as stated in your documentation. And added the short (F5) to RStudio for refreshing. It didn't work.

I also tried to run the command autoreload.fix::autorefresh() but didn't work for me.

Anything I might be doing wrong?

@vreederene-90
Copy link

Anything I might be doing wrong?

Please verify you have written options instead of option, and that this command is executed before you boot your app. You have to press the shortcut while the focus of your PC is on Rstudio, not the browser. Also be sure your working directory is set to your project root. If you still have any problems, let me know.

@toufikswar
Copy link

toufikswar commented Apr 12, 2022

Anything I might be doing wrong?

Please verify you have written options instead of option, and that this command is executed before you boot your app. You have to press the shortcut while the focus of your PC is on Rstudio, not the browser. Also be sure your working directory is set to your project root. If you still have any problems, let me know.

@vreederene-90 I tried the following, with no success unfortunately.

  1. I added options(shiny.autoreload = TRUE) in my run_dev.R (options with an "s".) I executed the command beforehand in my R console. When running getwd() in my R Console I get correct the path (my Shiny golem project).

  2. I created a shortcut for Refresh Shiny that I have tied to F5

  3. I executed my Shiny app from my Terminal using R -e "autoreload.fix::autorefresh()" (making sure I am in the working dir of my project)

What happens is that the application doesn't load. The browser opens a tab with 127.0.0.1:3070 but a white empty page is shown.
I tried to get the focus back to RStudio and press fn + F5 (I am on mac), but in vain.

Any ideas ?

Thanks for your help

@toufikswar
Copy link

toufikswar commented May 17, 2022

We spent some time with @vreederene-90 to check his solution autoreload.fix (thanks again Rene) and it works pretty well !

@toscm
Copy link

toscm commented Jul 19, 2022

Hi everyone, not sure if this is still relevant , but I encountered the same problem yesterday and I found the following fix to work as well: patch the function shiny::cachedFuncWithFile to not only monitor changes of app.R / ui.R / server.R, but also changes of ".*\\.(r|html?|js|css|png|jpe?g|gif)$" (as is done by shiny::initAutoReloadMonitor):

cached_func_with_file <- function(dir,
                                  file,
                                  func,
                                  case.sensitive = FALSE) {
  dir <- normalizePath(dir, mustWork = TRUE)
  value <- NULL
  filePattern <- getOption(
    "shiny.autoreload.pattern",
    ".*\\.(r|html?|js|css|png|jpe?g|gif)$"
  )
  last_mtimes <- NULL
  function(...) {
    file.path.func <- if (case.sensitive) file.path else file.path.ci
    fname <- file.path.func(dir, file)
    files <- list.files(dir, filePattern, recursive = TRUE, ignore.case = TRUE)
    files <- sort_c(files)
    mtimes <- file.info(files)$mtime
    names(mtimes) <- files
    if (!identical(last_mtimes, mtimes)) {
      value <<- func(fname, ...)
      last_mtimes <<- mtimes
    }
    value
  }
}

shiny_env <- environment(shiny:::cachedFuncWithFile)
unlockBinding("cachedFuncWithFile", shiny_env)
body(shiny_env$cachedFuncWithFile) <- body(cached_func_with_file)
lockBinding("cachedFuncWithFile", shiny_env)
options(shiny.autoreload = TRUE)

After executing this code you can use shiny::runApp as usual and it will automatically resource all relevant files on modification (in addition to restarting the active sessions). This code can e.g. be put in a wrapper function runAppPatched or even at your local .Rprofile.

Here you can see the the relevant code part of shiny that calls cached_func_with_file :

image

@ncullen93
Copy link

ncullen93 commented Aug 9, 2022

@toscm that didn't work for me, unfortunately. I ran your code in the Rstudio console then launched the app using golem::run_dev() but no changes to app_ui.R were picked up. Maybe I did it wrong.

It's definitely still a relevant issue though.. makes it hard to use golem on a day-to-day basis.

@b-rodrigues
Copy link

Hello all

I've tried some of the proposed solutions, unfortunately they don't work for me. Is there something else we could try in the meantime?

Cheers

@tomjemmett
Copy link

I’ve had something that I’ve been using locally for a while that sort of works for my use case. I’ve packaged this up into a package and would welcome some comments! The way I’m using this currently is to use this:

watchr::watch_files_and_start_task(
  \() {
    try({
      app <- golem::run_dev()
      print(app)
    })
  },
  \() fs::dir_ls(path = c("R"), recurse = TRUE, glob = "*.R"),
  "inst/golem-config.yml",
  "DESCRIPTION"
)

It launches the golem app in the background using callr, but one thing it doesn’t do is automatically reconnect the shiny app, but you can do that with session$allowReconnect.

I also explicitly choose a port to run the shiny app on, this method doesn’t automatically fire up a web browser for me, so it’s easier for me to pick a port and stick with it:

@toufikswar
Copy link

Hello @tomjemmett
I am trying to figure out how to use your solution.

I downloaded {watcher} and I am running the watchr::watch_files_and_start_task() in a separate job.

In my main console, i am launching my Shiny app with run_dev.R

Is that the correct way of using your package?

Many thanks,

@tomjemmett
Copy link

tomjemmett commented Apr 20, 2023

you wouldn't need to launch the app with run_dev as the job will be running it for you. It won't automatically load a web browser though, so you do have to call this manually. So I tend to configure my R/run_app.R script to launch on a fixed port (say 8081):

run_app <- function(onStart = NULL, # nolint
                    options = list(port = 8081),
                    enableBookmarking = NULL, # nolint
                    uiPattern = "/", # nolint
                    ...) {
...
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests