diff --git a/.nojekyll b/.nojekyll index 073ac90..8261627 100644 --- a/.nojekyll +++ b/.nojekyll @@ -1 +1 @@ -3c4d1cbb \ No newline at end of file +92bdda7b \ No newline at end of file diff --git a/app_packages.html b/app_packages.html index fc3c5ff..6eadcd5 100644 --- a/app_packages.html +++ b/app_packages.html @@ -262,6 +262,12 @@ GitHub + + diff --git a/code_tools.html b/code_tools.html index d2886fb..2101044 100644 --- a/code_tools.html +++ b/code_tools.html @@ -262,6 +262,12 @@ GitHub + + diff --git a/create.html b/create.html new file mode 100644 index 0000000..a49dd67 --- /dev/null +++ b/create.html @@ -0,0 +1,855 @@ + + + + + + + +Shiny App-Packages - Appendix: Creating packages in Posit Workbench + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+ + + +
+

Appendix: Creating packages in Posit Workbench

+
+ + + +
+ + + + +
+ + +

“Can the .Rproj file convert an R project into an R package?”

+

The technical answer is no, because the .Rproj only activates the Build pane in the IDE. The tools in the Build pane are directly connected to the devtools package (hence the PackageUseDevtools: Yes in the .Rproj file).

+

For example, if the mandatory fields are added to the DESCRIPTION, but the .Rproj file doesn’t have any of the package configuration fields, then the Build pane will not be displayed:

+
+

Build pane tools Mandatory DESCRIPTION fields with package configuration in .Rproj file

+
Figure 1: ?(caption)
+
+

Conversely, changing the Project build tools to Package will add the package development fields in the .Rproj file without having the mandatory fields in the DESCRIPTION will trigger the Build pane:

+
+
+
+

+
(a) DESCRIPTION
+
+
Figure 2: Unchanged DESCRIPTION file after changing projApp.Rproj
+
+
+

However, if we install devtools and try to load the code in the R/ folder with Build > Load All, we will get the following error:

+
+
install.packages("devtools")
+library(devtools)
+devtools::load_all()
+
+
+
+

+
(a) Build pane tools
+
+
Figure 3: Attempt to load_all() code in R/ folder
+
+

The image above demonstrates why setting the Project build tools in your Project Options is insufficient to convert a project into a package. The directory must include a DESCRIPTION file containing the mandatory fields and an .Rproj file with the package configuration fields listed above to be a functional R package.

+

Read more about DESCRIPTION file fields in R Packages, 2ed

+
+
+
+ +
+
+RStudio project files +
+
+
+
+
+
+

You can read more about the benefits of using RStudio projects to configuring project-level options here.

+
+
+
+
+

Terminate

+

To get the IDE to re-read the .Rproj file, you can terminate the session (Session > Terminate R… > Yes)

+
+
+
+
+

+
(a) Session > Terminate
+
+
+
+
+

+
(b) Click Yes
+
+
+
+

Figure 4: Terminate your R session

+
+
+

Creating app-packages: scenarios

+

The callout blocks below represent scenarios I’ve encountered requiring an app-package:

+

Scenario #1

+
+
+
+ +
+
+Scenario #1: Create a new shiny app-package +
+
+
+
+
+
+

You’re in the ‘brainstorming’ stages of app development, and no code (or very little code) has been written. Maybe you’ve investigated using a framework (like golem or rhino) but can’t decide if their features will fit your needs.

+
+
+
+
+

In this case, you’re in the perfect position to create a new package with usethis::create_package() or devtools::create(). If you’re using Posit Workbench, do File > New Project > New Directory > R Package.

+

Scenario #2

+
+
+
+ +
+
+Scenario #2: Convert a working app project into an app-package +
+
+
+
+
+
+

The app is currently deployed and is being accessed by users, but the underlying code lives in the ‘root’ folder (i.e., an app.R or ui.R and server.R, global.R, helper.R, and/or other .R files, data, static resources, etc.). The application works, but you’d like to convert it to a package structure without breaking it.

+
+
+
+
+

This is a circumstance where I’d manually create the DESCRIPTION file (or use usethis::use_description()) and set the package-level build tools (Tools > Project options > Build Tools > Project build tools). Then I’d begin organizing the shiny app files into a package structure (i.e., move .R files into the R/ folder, data files into the data/ folder, etc.)

+

Scenario #3

+
+
+
+ +
+
+Scenario #3: Convert a legacy app project into a shiny app-package +
+
+
+
+
+
+

The app was written some time ago, used superseded or deprecated functions (i.e., callModule()), and needs updating. Updating the apps might include adding data or static resources, writing utility functions and modules, etc.

+

The critical distinction is that this application is not working and requires updates.

+
+
+
+
+

Scenario #4

+
+
+
+ +
+
+Scenario #4: Convert your ‘dev’ app project into an app-package +
+
+
+
+
+
+

You’re currently developing an app project, and you read somewhere it should be an app-package; however, you can’t find clear instructions on how to accomplish this incrementally.

+

And maybe you’re a minimalist (like me) who likes to have complete control over every aspect of the development process.

+
+
+
+
+ + +
+
+ + + + \ No newline at end of file diff --git a/css.html b/css.html index f5d18cb..339a155 100644 --- a/css.html +++ b/css.html @@ -262,6 +262,12 @@ GitHub + + diff --git a/data.html b/data.html index 92af797..0c09119 100644 --- a/data.html +++ b/data.html @@ -93,7 +93,7 @@ } - +
@@ -373,32 +380,11 @@

Data files

We’ve documented the functions in pkgApp and successfully managed the dependencies with the NAMESPACE and DESCRIPTION files. In this chapter, we’re going to cover how make the sure movie.RData file becomes part of pkgApp, and other locations for data files in app-packages.

App-package data

There are three folders used to store data in R packages: data/, data-raw/, and inst/extdata/. The folder you’ll use will depend on the format, accessibility, and intended purpose of the data file in your app-package.

-
-
-
- -
-
-GitHub [pkgApp]:[04-data] -
-
-
-
-
-
-

This shiny app project can be found in the 04-data branch of the pkgApp repo.

-

This branch was created from the 03-dependencies branch using:

-
git checkout -B 04-data
-git push -u origin 04-data
-
-
-
-
-

data/

+

The data/ folder

You can read more about the data folder in the ‘Data in packages’ section of Writing R Extenstions and the ‘Data’ chapter of R Packages, 2ed.

The primary location for app-package data is the data/ folder. Data objects in the data/ folder are available in the package namespace when it’s installed and loaded, and can be accessed with the package::data syntax. See the example below:

-
library(dplyr)
+
 
tibble [19,066 × 13] (S3: tbl_df/tbl/data.frame)
@@ -427,47 +413,47 @@ 

Data files

  1. Move the movies.RData file into a newly created the data/ folder:

    -
    pkgApp/
    -
    -  └──data/
    -      └── movies.RData
    +
    pkgApp/
    +
    +  └──data/
    +      └── movies.RData
  2. Include LazyData: true in the DESCRIPTION file (I’ve added it above Imports:):

    -
    Package: pkgApp
    -Version: 0.0.0.9000
    -Type: Package
    -Title: movies app
    -Description: A movies data shiny application.
    -Author: John Smith [aut, cre]
    -Maintainer: John Smith <John.Smith@email.io>
    -License: GPL-3
    -RoxygenNote: 7.2.3
    -Encoding: UTF-8
    -Roxygen: list(markdown = TRUE)
    -LazyData: true
    -Imports:
    -  shiny,
    -  ggplot2,
    -  rlang,
    -  stringr,
    -  tools
    +
    Package: pkgApp
    +Version: 0.0.0.9000
    +Type: Package
    +Title: movies app
    +Description: A movies data shiny application.
    +Author: John Smith [aut, cre]
    +Maintainer: John Smith <John.Smith@email.io>
    +License: GPL-3
    +RoxygenNote: 7.2.3
    +Encoding: UTF-8
    +Roxygen: list(markdown = TRUE)
    +LazyData: true
    +Imports:
    +  shiny,
    +  ggplot2,
    +  rlang,
    +  stringr,
    +  tools
  3. Load, document, and install.

Ctrl/Cmd + Shift + L

-
ℹ Loading pkgApp
+
ℹ Loading pkgApp

Ctrl/Cmd + Shift + D

-
==> devtools::document(roclets = c('rd', 'collate', 'namespace'))
-
-ℹ Updating pkgApp documentation
-ℹ Loading pkgApp
-Documentation completed
+
==> devtools::document(roclets = c('rd', 'collate', 'namespace'))
+
+ℹ Updating pkgApp documentation
+ℹ Loading pkgApp
+Documentation completed

Ctrl/Cmd + Shift + B

In the Build pane, you’ll notice a few new ** data lines of output after adding data:

-
** data
-*** moving datasets to lazyload DB
-** byte-compile and prepare package for lazy loading
+
** data
+*** moving datasets to lazyload DB
+** byte-compile and prepare package for lazy loading

We can check to see if movies has been included in pkgApp using the package::data syntax:

@@ -476,9 +462,23 @@

Data files

Figure 1: movies is now part of pkgApp
-

If you’d prefer to store data using the .rda format, the usethis package has the use_data() function that will automatically store an object in data/ (this function will also add LazyData: true to the DESCRIPTION)

+

usethis::use_data()

+

If you’d prefer to store data using the .rda format, the usethis package has the use_data() function that will automatically store an object in data/ in the .rda format.

+

To use usethis::use_data(), we can load the movies data into the global environment with load("movies.RData"), then run usethis::use_data(movies):

+
+
usethis::use_data(movies)
+
+
✔ Setting active project to '/path/to/pkgApp'
+✔ Adding 'R' to Depends field in DESCRIPTION
+✔ Creating 'data/'
+✔ Saving 'movies' to 'data/movies.rda'
+• Document your data (see 'https://r-pkgs.org/data.html')
+

The Depends: field is added to the DESCRIPTION file with an R version (this ensures the data files will be loaded)

+
Depends: 
+    R (>= 2.10)
+

(this function will also add LazyData: true to the DESCRIPTION)

- -
+

If you happened to download, install and load the monthApp example from Mastering Shiny, you may have noticed the NAMESPACE was empty, but the data was exported from the package:

@@ -504,11 +504,11 @@

Data files

Documenting data can be tedious, but it’s worth the effort if you’ll be sharing your application with collaborators. There are multiple ways to store the documentation for datasets. For example, we could create a data.R file in the R/ folder.

-
fs::file_create("R/data.R")
+
fs::file_create("R/data.R")

In data.R, we provide a @title, @description, and @details for the data (with or without the tags), followed by @format:

-
#' @title IMDB movies data 
+
#' @title IMDB movies data 
 #'
 #' @description
 #' Movie review data. Note: these data come from the [Building Web Applications with shiny course](https://rstudio-education.github.io/shiny-course/). 
@@ -521,7 +521,7 @@ 

Data files

@format

The text following @format is a one-sentence description of the data (with it’s dimensions).

-
#' @title IMDB movies data 
+
#' @title IMDB movies data 
 #'
 #' @description
 #' Movie review data. Note: these data come from the [Building Web Applications with shiny course](https://rstudio-education.github.io/shiny-course/). 
@@ -534,14 +534,14 @@ 

Data files

\describe & \item

Each variable (column) in the data is documented with a combination of \describe and \item (pay close attention to the curly brackets):

-
#' \describe{
+
#' \describe{
 #'  \item{variable}{description}
 #' }

After closing the curly brackets in \describe, place the name of the data in quotes ("movies") on the following line.

Below is the documentation for the first five columns in the movies dataset:

-
#' @title IMDB movies data 
+
#' @title IMDB movies data 
 #'
 #' @description
 #' Movie review data. Note: these data come from the [Building Web Applications with shiny course](https://rstudio-education.github.io/shiny-course/). 
@@ -562,16 +562,16 @@ 

Data files

If we load and document pkgApp, we can see a preview of the help file:

Ctrl/Cmd + Shift + L

-
ℹ Loading pkgApp
+
ℹ Loading pkgApp

Ctrl/Cmd + Shift + D

-
==> devtools::document(roclets = c('rd', 'collate', 'namespace'))
-
-ℹ Updating pkgApp documentation
-ℹ Loading pkgApp
-Writing movies.Rd
-Documentation completed
+
==> devtools::document(roclets = c('rd', 'collate', 'namespace'))
+
+ℹ Updating pkgApp documentation
+ℹ Loading pkgApp
+Writing movies.Rd
+Documentation completed
-
?movies
+
?movies
@@ -582,7 +582,7 @@

Data files

I’ve provided documentation for the full movies dataset below.

-
show/hide full movies data documenation
#' @title IMDB movies data 
+
show/hide full movies data documenation
#' @title IMDB movies data 
 #'
 #' @description
 #' Movie review data. Note: these data come from the [Building Web Applications with shiny course](https://rstudio-education.github.io/shiny-course/). 
@@ -635,7 +635,7 @@ 

Data files

After documenting the movies data in data.R, we’ll remove the call to load() in the mod_scatter_display_server() function and replace it with a direct call to the dataset:

-
mod_scatter_display_server <- function(id, var_inputs) {
+
mod_scatter_display_server <- function(id, var_inputs) {
   shiny::moduleServer(id, function(input, output, session) {
 
     inputs <- shiny::reactive({
@@ -684,20 +684,20 @@ 

Data files

dplyr example

To illustrate other options for data documentation, we’ll use the dplyr package. dplyr stores its data in the data/ folder:

-
data/
-├── band_instruments.rda
-├── band_instruments2.rda
-├── band_members.rda
-├── starwars.rda
-└── storms.rda
+
data/
+├── band_instruments.rda
+├── band_instruments2.rda
+├── band_members.rda
+├── starwars.rda
+└── storms.rda

The documentation for the datasets in dplyr are stored in R/ using a data- prefix:

-
R/
-├── data-bands.R
-├── data-starwars.R
-└── data-storms.R
+
R/
+├── data-bands.R
+├── data-starwars.R
+└── data-storms.R

The three band_ datasets have documented in a single file, data-bands.R:

-
show/hide documentation for dplyr::band_ datasets
# from the dplyr github repo: 
+
show/hide documentation for dplyr::band_ datasets
# from the dplyr github repo: 
 # https://github.com/tidyverse/dplyr/blob/main/R/data-bands.R
 # 
 #' Band membership
@@ -730,7 +730,7 @@ 

Data files

In the example above, note that two of the datasets (band_instruments and band_instruments2) have the @format set to NULL, and define the help search name with @rdname. The @examples tag can be used to view the dataset when users click ‘Run Examples

Either method will–what’s important is that each dataset in your package has documentation.

-
+
@@ -739,10 +739,10 @@

Data files

-
+

Documenting data requires the following roxygen2 structure:

-
#' 
+
#' 
 #' @title single-sentence describing [data]
 #' 
 #' @description
@@ -759,10 +759,10 @@ 

Data files

-

data-raw/

+

The data-raw/ folder

The data-raw folder is not an official directory in the standard R package structure, but it’s a common location for any data processing or cleaning scripts, and the raw data file for datasets stored in data/.

- -
+

The code used to produce the movies dataset in the data/ directory might* come from this GitHub repo. If so, the data-raw folder is where the data processing and preparation scritps would be stored (along with a copy of the data in .csv format) before saving a copy in the data/ folder.

@@ -783,97 +783,81 @@

Data files

dplyr example

If we look at the data in the dplyr package again, we can see the data-raw/ folder contains a combination of .R and .csv files:

-
data-raw/
-├── band_members.R
-├── starwars.R
-├── starwars.csv
-└── storms.R
-
-1 directory, 4 files
+
data-raw/
+├── band_members.R
+├── starwars.R
+├── starwars.csv
+└── storms.R
+
+1 directory, 4 files

In this example, the starwars.R script downloads & prepares starwars, then saves a .csv copy of the data in data-raw.

Read more about the data-raw folder in R Packages, 2ed

-

inst/extdata/

+

The inst/extdata/ folder

The extdata folder (inside inst/) can be used for external datasets in other file formats (.csv, .tsv, .txt, .xlsx, etc).

The data files in inst/extdata/ aren’t directly loadable using the package::data syntax or the data() function like with the data/ directory. These files can be imported using the file path accessor function, system.file().

For example, if we create the inst/extdata/ and save a copy of movies as a .fst file:

-
-
fst package v0.9.8
+
fst package v0.9.8
-
fs::dir_create("inst/extdata/")
+
fs::dir_create("inst/extdata/")
 fst::write_fst(
   x = movies, 
   path = "inst/extdata/movies.fst", 
   compress = 75)
-
fstcore package v0.9.14
-(OpenMP was not detected, using single threaded mode)
+
fstcore package v0.9.14
+(OpenMP was not detected, using single threaded mode)

Then load, document, and install pkgApp:

-

Ctrl/Cmd + Shift + L

-

Ctrl/Cmd + Shift + D

-

Ctrl/Cmd + Shift + B

+

Ctrl/Cmd + Shift + L / D / B

We can import movies.fst using system.file() to create a path to the file:

-
tibble::as_tibble(
+
tibble::as_tibble(
   fst::read_fst(path = 
       system.file("extdata/", "movies.fst", package = "pkgApp")
     )
   )
-
# A tibble: 651 × 34
-   title       title_type genre runtime mpaa_rating studio thtr_rel_date      
-   <chr>       <fct>      <fct>   <dbl> <fct>       <fct>  <dttm>             
- 1 Filly Brown Feature F… Drama      80 R           Indom… 2013-04-18 21:00:00
- 2 The Dish    Feature F… Drama     101 PG-13       Warne… 2001-03-13 21:00:00
- 3 Waiting fo… Feature F… Come…      84 R           Sony … 1996-08-20 21:00:00
- 4 The Age of… Feature F… Drama     139 PG          Colum… 1993-09-30 21:00:00
- 5 Malevolence Feature F… Horr…      90 R           Ancho… 2004-09-09 21:00:00
- 6 Old Partner Documenta… Docu…      78 Unrated     Shcal… 2009-01-14 21:00:00
- 7 Lady Jane   Feature F… Drama     142 PG-13       Param… 1985-12-31 21:00:00
- 8 Mad Dog Ti… Feature F… Drama      93 R           MGM/U… 1996-11-07 21:00:00
- 9 Beauty Is … Documenta… Docu…      88 Unrated     Indep… 2012-09-06 21:00:00
-10 The Snowto… Feature F… Drama     119 Unrated     IFC F… 2012-03-01 21:00:00
-# ℹ 641 more rows
-# ℹ 27 more variables: thtr_rel_year <dbl>, thtr_rel_month <dbl>,
-#   thtr_rel_day <dbl>, dvd_rel_date <dttm>, dvd_rel_year <dbl>,
-#   dvd_rel_month <dbl>, dvd_rel_day <dbl>, imdb_rating <dbl>,
-#   imdb_num_votes <int>, critics_rating <fct>, critics_score <dbl>,
-#   audience_rating <fct>, audience_score <dbl>, best_pic_nom <fct>,
-#   best_pic_win <fct>, best_actor_win <fct>, best_actress_win <fct>, …
-# ℹ Use `print(n = ...)` to see more rows
+
# A tibble: 651 × 34
+   title  title_type genre runtime mpaa_rating studio thtr_rel_date      
+   <chr>  <fct>      <fct>   <dbl> <fct>       <fct>  <dttm>             
+ 1 Filly… Feature F… Drama      80 R           Indom… 2013-04-18 21:00:00
+ 2 The D… Feature F… Drama     101 PG-13       Warne… 2001-03-13 21:00:00
+ 3 Waiti… Feature F… Come…      84 R           Sony … 1996-08-20 21:00:00
+ 4 The A… Feature F… Drama     139 PG          Colum… 1993-09-30 21:00:00
+ 5 Malev… Feature F… Horr…      90 R           Ancho… 2004-09-09 21:00:00
+ 6 Old P… Documenta… Docu…      78 Unrated     Shcal… 2009-01-14 21:00:00
+ 7 Lady … Feature F… Drama     142 PG-13       Param… 1985-12-31 21:00:00
+ 8 Mad D… Feature F… Drama      93 R           MGM/U… 1996-11-07 21:00:00
+ 9 Beaut… Documenta… Docu…      88 Unrated     Indep… 2012-09-06 21:00:00
+10 The S… Feature F… Drama     119 Unrated     IFC F… 2012-03-01 21:00:00
+# ℹ 641 more rows
+# ℹ 27 more variables: thtr_rel_year <dbl>, thtr_rel_month <dbl>,
+#   thtr_rel_day <dbl>, dvd_rel_date <dttm>, dvd_rel_year <dbl>,
+#   dvd_rel_month <dbl>, dvd_rel_day <dbl>, imdb_rating <dbl>,
+#   imdb_num_votes <int>, critics_rating <fct>, critics_score <dbl>,
+#   audience_rating <fct>, audience_score <dbl>, best_pic_nom <fct>,
+#   best_pic_win <fct>, best_actor_win <fct>, best_actress_win <fct>, …
+# ℹ Use `print(n = ...)` to see more rows

We’ll cover inst/ and system.file() in more detail in the next chapter.

-
-
-
- -
-
-GitHub [pkgApp]:[04-data] -
-
-
-
-
-
-

The code for this section was pushed to the [04-data] branch of the [pkgApp] repo.

-
git add .
-git commit -m 'git message'
-git push 
-
-
-
+ +
+
+

+
New Git Branch
-

Recap

+

The code for this section is in the [04_data] branch of the [pkgApp] repo.

+

Recap

It’s common for shiny apps to require data, so knowing how to store and access these files in your app-package will make it easier to load and reproducible in other environments. Here are a few other things to consider when including data in your app-package:

-
+
@@ -882,7 +866,7 @@

Data files

-
+
    @@ -892,7 +876,7 @@

    Data files

  • Format: data files in data/ must be either .rda or .RData format.

  • Documentation: document the data/ files in either a single R/data.R file or individual .R files. Documentation should include the following roxygen2 format:

    -
    #' 
    +
    #' 
     #' @title 
     #' 
     #' @description
    diff --git a/debugging.html b/debugging.html
    index bd073bd..6b5917f 100644
    --- a/debugging.html
    +++ b/debugging.html
    @@ -262,6 +262,12 @@
       
      GitHub
       
    +
  • +
diff --git a/dependencies.html b/dependencies.html index a77e007..75c596b 100644 --- a/dependencies.html +++ b/dependencies.html @@ -296,6 +296,12 @@ GitHub
+ + @@ -627,7 +633,28 @@

Dependencies

  • Call add-on package functions using the package::function() syntax, and

  • Include them in the NAMESPACE by using either @importFrom or @import

  • -

    Explicit namespacing let’s users know which functions are from your package and which functions are from external packages. Listing external functions and packages ensures their loaded when users install and load your package.

    +

    Explicit namespacing let’s users know which functions are from your package and which functions are from external packages.

    +
    + +
    +
    +
    +

    Think of add-on packages and their functions as families you can invite to a party. @import will invite the entire family, and @importFrom will only invite specific family members (this is helpful if it’s a huge family and you don’t have unlimited food and drinks).

    +

    When you’re at the party, it’s clearer to refer to the guests by their first and last name, i.e., ‘This is my friend, Beth Johnson, she’s an amazing breakdancer!’ which avoids confusing them with another guest (maybe ‘Beth Smith,’ who has two left feet).

    +

    This metaphor should help explain why we use the package::function() syntax when referring to functions from add-on packages: our package can quickly become a massive party with many guests, and we don’t want anyone confused about who’s capable of performing what.

    +
    +
    +
    +
    +

    Importing external functions and packages ensures they’re loaded when users install and load your package.

    @importFrom or @import

    @@ -755,11 +782,12 @@

    Dependencies

    • R/movies_app.R

      +
        +
      • +movies_app() is the standalone app function, so we’ll export this function and @import shiny here
      • +
      -
      show/hide R/movies_app.R roxygen2
      # movies_app() is the standalone app function, so we'll export 
      -# this function and @import shiny here:
      -# ==============================================================
      -#'
      +
      show/hide R/movies_app.R roxygen2
      #'
       #' @export
       #' 
       #' @import shiny
      @@ -768,48 +796,49 @@ 

      Dependencies

    • -

      R/movies_ui.R

      +

      R/movies_ui.R & R/movies_server.R

      +
        +
      • +movies_ui() and movies_server() both use only shiny functions, so they don’t need an @import or @importFrom tags (but both are exported):
      • +
      -
      show/hide R/movies_ui.R & R/movies_server.R roxygen2
      # movies_ui() and movies_server() both use only shiny functions, 
      -# so they don't need any @import or @importFrom tags (but both 
      -# are exported):
      -# ==============================================================
      -#'
      +
      show/hide R/movies_ui.R & R/movies_server.R roxygen2
      #'
       #' @export
      -#' 
      -#'
      +#'
    • R/mod_var_input.R

      +
        +
      • +mod_var_input_ui() and mod_var_input_server() only use shiny functions, so no need for @import or @importFrom tags (however, we’ll export both module functions).
      • +
      -
      show/hide R/mod_var_input.R roxygen2
      # mod_var_input_ui() and mod_var_input_server() only use shiny 
      -# functions, so no need for @import or @importFrom tags (however,
      -# this module is exported):
      -# 
      -# ==============================================================
      -#'
      +
      show/hide R/mod_var_input.R roxygen2
      #'
       #' @export
      -#' 
      -#'
      +#'
    • R/mod_scatter_display.R

      +
        +
      • +mod_scatter_display_ui() also only contains shiny functions (no @import or @importFrom) so this function only gets an @export tag
      • +
      -
      show/hide R/mod_scatter_display.R roxygen2
      # mod_scatter_display_ui() also only contains shiny functions 
      -# (no @import or @importFrom) so this function only gets an 
      -# @export tag:
      -# ==============================================================
      -#' 
      +
      show/hide R/mod_scatter_display.R roxygen2
      #' 
       #' @export
      -#' 
      -# mod_scatter_display_server() uses functions from tools, 
      -# ggplot2 and stringr (all are imported with @importFrom tags). 
      -# This function is also exported:
      -# ==============================================================
      +#' 
      +
      +
      +
        +
      • +mod_scatter_display_server() uses functions from tools, ggplot2 and stringr (all are imported with @importFrom tags). This function is also exported:
      • +
      +
      +
      show/hide R/mod_scatter_display.R roxygen2
      #' 
       #' @export
       #' 
       #' @importFrom tools toTitleCase
      @@ -821,26 +850,26 @@ 

      Dependencies

    The updated DESCRIPTION file is below:

    -
    Package: pkgApp
    -Version: 0.0.0.9000
    -Type: Package
    -Title: movies app
    -Description: A movies data shiny application.
    -Author: John Smith [aut, cre]
    -Maintainer: John Smith <John.Smith@email.io>
    -License: GPL-3
    -DisplayMode: Showcase
    -RoxygenNote: 7.2.3
    -Encoding: UTF-8
    -Roxygen: list(markdown = TRUE)
    -Imports:
    -  shiny,
    -  ggplot2,
    -  rlang,
    -  stringr,
    -  tools
    -<remember to keep an empty final line>
    -

    Ctrl/Cmd + Shift + L, D, B

    +
    Package: pkgApp
    +Version: 0.0.0.9000
    +Type: Package
    +Title: movies app
    +Description: A movies data shiny application.
    +Author: John Smith [aut, cre]
    +Maintainer: John Smith <John.Smith@email.io>
    +License: GPL-3
    +DisplayMode: Showcase
    +RoxygenNote: 7.2.3
    +Encoding: UTF-8
    +Roxygen: list(markdown = TRUE)
    +Imports:
    +  shiny,
    +  ggplot2,
    +  rlang,
    +  stringr,
    +  tools
    +<remember to keep an empty final line>
    +

    Ctrl/Cmd + Shift + L / D / B

    After pkgApp installs, I’ll check the namespace by using pkgApp:: in the Console:

    @@ -853,7 +882,7 @@

    Dependencies

    As a final step, we want to remove any calls to library() in pkgApp (these packages are now handled in the NAMESPACE and DESCRIPTION files).

    app.R should now contain a single call to library(pkgApp) and then the movies_app() function:

    -
    # pkgs <- c("shiny", "shinythemes", "stringr", "ggplot2", "rlang")
    +
    # pkgs <- c("shiny", "shinythemes", "stringr", "ggplot2", "rlang")
     # install.packages(pkgs, quiet = TRUE)
     
     # load pacakge ----------------------------------
    @@ -875,10 +904,10 @@ 

    Dependencies

    New Git Branch
    -

    The code for the next section is in the [03_dependencies] branch of the [pkgApp] repo.

    +

    The code for this section is in the [03_dependencies] branch of the [pkgApp] repo.

    Recap

    -
    +
    @@ -887,7 +916,7 @@

    Dependencies

    -
    +

    This chapter covered:

    diff --git a/devtools.html b/devtools.html index 777b44d..0a998f0 100644 --- a/devtools.html +++ b/devtools.html @@ -296,6 +296,12 @@ GitHub
    + + @@ -354,13 +360,13 @@

    devtools<

    -

    The previous chapter covered how to create new or convert existing shiny app projects into app-packages with usethis::create_package(). We also manually transformed a shiny project into an app-package by adding specific fields to the DESCRIPTION file.

    -

    Regardless of the method we use to start our app-package, if we want it to be ‘functional’, we need to install the devtools package.

    +

    The previous chapter covered what files are needed to create R packages and app-packages, how to create new or convert existing shiny app projects into app-packages with usethis::create_package().

    +

    After setting up the DESCRIPTION file and configuring the package Build Tools, we’ll need to test it’s ‘functionality’ with the devtools package.

    -

    By ‘functional’, I mean that the app-package manages dependencies and has documentation in addition to storing code and data.

    +

    By ‘functionality’, I mean the app-package can call the devtools functions for loading the code in R/, creating documentation, and successfully installing the package from the source files.

    Let’s get started!

    Developing with devtools

    diff --git a/frameworks.html b/frameworks.html index b9144e2..b4b85ed 100644 --- a/frameworks.html +++ b/frameworks.html @@ -262,6 +262,12 @@ GitHub
    + + diff --git a/github.html b/github.html index e727a35..9b6a204 100644 --- a/github.html +++ b/github.html @@ -61,6 +61,7 @@ + @@ -295,6 +296,12 @@ GitHub
    + + @@ -864,6 +871,9 @@

    GitHub

    diff --git a/glossary.html b/glossary.html index 1b7cd46..6acbda7 100644 --- a/glossary.html +++ b/glossary.html @@ -18,26 +18,7 @@ margin: 0 0.8em 0.2em -1em; /* quarto-specific, see https://github.com/quarto-dev/quarto-cli/issues/4556 */ vertical-align: middle; } -/* CSS for citations */ -div.csl-bib-body { } -div.csl-entry { - clear: both; -} -.hanging-indent div.csl-entry { - margin-left:2em; - text-indent:-2em; -} -div.csl-left-margin { - min-width:2em; - float:left; -} -div.csl-right-inline { - margin-left:2em; - padding-left:1em; -} -div.csl-indent { - margin-left: 2em; -} + @@ -281,6 +262,12 @@ GitHub
    + + @@ -289,7 +276,28 @@
    @@ -324,14 +332,23 @@

    Glossary of terms

    - +

    Shiny apps

    +

    Utility/helper functions

    +

    External resources

    +

    Module

    +

    Standalone app function

    +

    Packages

    +

    Project

    +

    Any directory of R executable files with an RStudio/Posit workbench project file (.Rproj).

    +

    Package

    +

    A directory of functions, documentation, and/or data can be installed and loaded into an R session. Packages include the necessary dependency management (NAMESPACE) and metadata files (DESCRIPTION) and has access to the package development tools in RStudio/Posit Workbench.

    +

    App-package

    +

    An app-package is a package containing a shiny application. App-packages have all of the functionality of a standard R package, but also contain the files and folders required to successfully develop, run, and deploy a shiny application.

    +

    devtools

    end glossary.qmd

    - -
    - +
    +
    -Warning! +Caution!
    @@ -354,62 +360,41 @@

    The inst/ folder plays a special role in R packages, and can be incredibly helpful for app-packages. In the previous chapter we covered including external datasets in the inst/extdata/ folder and accessing these files with system.file(). In this chapter we’ll cover how to add external resources (i.e., the files stored in the www/ folder of your shiny app project) to your shiny app-packages.

    -
    -
    -
    - -
    -
    -GitHub [pkgApp]:[05-inst] -
    -
    -
    -
    -
    -
    -

    This shiny app project can be found in the 05-inst branch of the pkgApp repo.

    -

    This branch was created from the 04-data branch using:

    -
    git checkout -B 05-inst
    -git push -u origin 05-inst
    -
    -
    -
    -

    pkgApp

    The current structure of pkgApp is below:

    -
    pkgApp/
    -├── DESCRIPTION
    -├── NAMESPACE
    -├── R/
    -│   ├── data.R
    -│   ├── mod_scatter_display.R
    -│   ├── mod_var_input.R
    -│   ├── movies_app.R
    -│   ├── movies_server.R
    -│   ├── movies_ui.R
    -│   └── scatter_plot.R
    -├── README.md
    -├── app.R
    -├── data/
    -│   └── movies.RData
    -├── inst/
    -│   └── extdata/
    -│       └── movies.fst
    -├── man/
    -│   ├── mod_scatter_display_server.Rd
    -│   ├── mod_scatter_display_ui.Rd
    -│   ├── mod_var_input_server.Rd
    -│   ├── mod_var_input_ui.Rd
    -│   ├── movies.Rd
    -│   ├── movies_app.Rd
    -│   ├── movies_server.Rd
    -│   ├── movies_ui.Rd
    -│   └── scatter_plot.Rd
    -├── pkgApp.Rproj
    -└── www/
    -    └── shiny.png
    -
    -7 directories, 23 files
    +
    pkgApp/
    +  ├── DESCRIPTION
    +  ├── NAMESPACE
    +  ├── R/
    +  │   ├── data.R
    +  │   ├── mod_scatter_display.R
    +  │   ├── mod_var_input.R
    +  │   ├── movies_app.R
    +  │   ├── movies_server.R
    +  │   ├── movies_ui.R
    +  │   └── scatter_plot.R
    +  ├── README.md
    +  ├── app.R
    +  ├── data/
    +  │   └── movies.RData
    +  ├── inst/
    +  │   └── extdata
    +  │       └── movies.fst
    +  ├── man/
    +  │   ├── mod_scatter_display_server.Rd
    +  │   ├── mod_scatter_display_ui.Rd
    +  │   ├── mod_var_input_server.Rd
    +  │   ├── mod_var_input_ui.Rd
    +  │   ├── movies.Rd
    +  │   ├── movies_app.Rd
    +  │   ├── movies_server.Rd
    +  │   ├── movies_ui.Rd
    +  │   └── scatter_plot.Rd
    +  ├── pkgApp.Rproj
    +  └── www/
    +      └── shiny.png
    +
    +7 directories, 24 files

    When we run movies_app(), we see the following:

    @@ -424,50 +409,50 @@

    What’s in the ‘installed’ package?

    We’ve been running load_all(), document(), and install() on pkgApp, but we haven’t looked at the contents of the installed package. Reviewing the installed package will help us understand what the system.file() function is doing, and how we can use the inst/ folder to include external resources to our app-package.

    When we run devtools::install(), the output tells us where the package is being installed:

    -
    ==> R CMD INSTALL --preclean --no-multiarch --with-keep.source pkgApp
    -
    -* installing to library ‘/path/to/install/Library/R/x86_64/4.2/library’
    +
    ==> R CMD INSTALL --preclean --no-multiarch --with-keep.source pkgApp
    +
    +* installing to library ‘/path/to/install/Library/R/x86_64/4.2/library’

    If we add the pkgApp/ directory to the end of the path above, we can view the contents of the installed package (I’m using the tree command in the Terminal, but the fs::dir_tree() function will also print a folder tree):

    -
    /path/to/install/Library/R/x86_64/4.2/library/pkgApp/
    -├── DESCRIPTION
    -├── INDEX
    -├── Meta/
    -│   ├── Rd.rds
    -│   ├── data.rds
    -│   ├── features.rds
    -│   ├── hsearch.rds
    -│   ├── links.rds
    -│   ├── nsInfo.rds
    -│   └── package.rds
    -├── NAMESPACE
    -├── R/
    -│   ├── pkgApp
    -│   ├── pkgApp.rdb
    -│   └── pkgApp.rdx
    -├── data/
    -│   ├── Rdata.rdb
    -│   ├── Rdata.rds
    -│   └── Rdata.rdx
    -├── extdata/
    -│   └── movies.fst
    -├── help/
    -│   ├── AnIndex
    -│   ├── aliases.rds
    -│   ├── paths.rds
    -│   ├── pkgApp.rdb
    -│   └── pkgApp.rdx
    -└── html/
    -    ├── 00Index.html
    -    └── R.css
    -
    -7 directories, 24 files
    +
    /path/to/install/Library/R/x86_64/4.2/library/pkgApp/
    +├── DESCRIPTION
    +├── INDEX
    +├── Meta/
    +│   ├── Rd.rds
    +│   ├── data.rds
    +│   ├── features.rds
    +│   ├── hsearch.rds
    +│   ├── links.rds
    +│   ├── nsInfo.rds
    +│   └── package.rds
    +├── NAMESPACE
    +├── R/
    +│   ├── pkgApp
    +│   ├── pkgApp.rdb
    +│   └── pkgApp.rdx
    +├── data/
    +│   ├── Rdata.rdb
    +│   ├── Rdata.rds
    +│   └── Rdata.rdx
    +├── extdata/
    +│   └── movies.fst
    +├── help/
    +│   ├── AnIndex
    +│   ├── aliases.rds
    +│   ├── paths.rds
    +│   ├── pkgApp.rdb
    +│   └── pkgApp.rdx
    +└── html/
    +    ├── 00Index.html
    +    └── R.css
    +
    +7 directories, 24 files

    The installed version of pkgApp has many of the same files as the ‘source’ version we’ve been writing (i.e., the NAMESPACE and DESCRIPTION). It also might surprise you to see that many of the source package files aren’t included in the installed version (.R, .Rd files. etc.). The key takeaway here is that the inst/ subfolders and files are available in the installed version unchanged (with the inst/ folder omitted.).

    Hopefully viewing this folder structure helps demystify what happens when a package is installed. Below is the official documentation on what happens to the inst/ folder (and it’s subfolders) when a package is installed:

    The contents of the inst/ subdirectory will be copied recursively to the installation directory. Subdirectories of inst/ should not interfere with those used by R (currently, R/, data/, demo/, exec/, libs/, man/, help/, html/ and Meta/, and earlier versions used latex/, R-ex/).” - Writing R extensions, Package subdirectories

    - -
    +

    You can explore the structure of other installed packages to see how they work ‘under the hood’ to gain insight into how they use the inst/ folder.

    @@ -506,7 +491,7 @@

  • These files are used in readr::readr_example() (in R/example.R):

    -
    #' Get path to readr example
    +
    #' Get path to readr example
     #'
     #' readr comes bundled with a number of sample files in its `inst/extdata`
     #' directory. This function make them easy to access
    @@ -530,70 +515,69 @@ 

    How system.file() works

    -

    system.file() gives us access to the package files on installation (i.e., the files we see at the path above).

    -

    In the previous chapter, we used system.file() to access the movies.fst file in inst/extdata/:

    +

    system.file() gives us access to the package files on installation (i.e., the files we see at the path above). In the previous chapter, we used system.file() to access the movies.fst file in inst/extdata/:

    -
    dplyr::glimpse(  
    +
    dplyr::glimpse(  
       fst::read_fst(path = 
           system.file("extdata/", "movies.fst", package = "pkgApp")
         )
       )
    -
    show/hide glimpse() of movies.fst
    fstcore package v0.9.14
    -(OpenMP was not detected, using single threaded mode)
    -Rows: 651
    -Columns: 34
    -$ title            <chr> "Filly Brown", "The Dish", "Waiting for Guffman", "The Age of …
    -$ title_type       <fct> Feature Film, Feature Film, Feature Film, Feature Film, Featur…
    -$ genre            <fct> Drama, Drama, Comedy, Drama, Horror, Documentary, Drama, Drama…
    -$ runtime          <dbl> 80, 101, 84, 139, 90, 78, 142, 93, 88, 119, 127, 108, 110, 100…
    -$ mpaa_rating      <fct> R, PG-13, R, PG, R, Unrated, PG-13, R, Unrated, Unrated, PG, P…
    -$ studio           <fct> Indomina Media Inc., Warner Bros. Pictures, Sony Pictures Clas…
    -$ thtr_rel_date    <dttm> 2013-04-18 21:00:00, 2001-03-13 21:00:00, 1996-08-20 21:00:00…
    -$ thtr_rel_year    <dbl> 2013, 2001, 1996, 1993, 2004, 2009, 1986, 1996, 2012, 2012, 19…
    -$ thtr_rel_month   <dbl> 4, 3, 8, 10, 9, 1, 1, 11, 9, 3, 6, 12, 1, 9, 6, 8, 3, 7, 4, 9,…
    -$ thtr_rel_day     <dbl> 19, 14, 21, 1, 10, 15, 1, 8, 7, 2, 19, 18, 4, 23, 20, 27, 1, 1…
    -$ dvd_rel_date     <dttm> 2013-07-29 21:00:00, 2001-08-27 21:00:00, 2001-08-20 21:00:00…
    -$ dvd_rel_year     <dbl> 2013, 2001, 2001, 2001, 2005, 2010, 2003, 2004, 2013, 2012, 20…
    -$ dvd_rel_month    <dbl> 7, 8, 8, 11, 4, 4, 2, 3, 1, 8, 5, 9, 7, 2, 3, 12, 8, 1, 6, 1, …
    -$ dvd_rel_day      <dbl> 30, 28, 21, 6, 19, 20, 18, 2, 21, 14, 1, 23, 9, 13, 22, 21, 19…
    -$ imdb_rating      <dbl> 5.5, 7.3, 7.6, 7.2, 5.1, 7.8, 7.2, 5.5, 7.5, 6.6, 6.8, 6.0, 7.…
    -$ imdb_num_votes   <int> 899, 12285, 22381, 35096, 2386, 333, 5016, 2272, 880, 12496, 7…
    -$ critics_rating   <fct> Rotten, Certified Fresh, Certified Fresh, Certified Fresh, Rot…
    -$ critics_score    <dbl> 45, 96, 91, 80, 33, 91, 57, 17, 90, 83, 89, 67, 80, 25, 15, 78…
    -$ audience_rating  <fct> Upright, Upright, Upright, Upright, Spilled, Upright, Upright,…
    -$ audience_score   <dbl> 73, 81, 91, 76, 27, 86, 76, 47, 89, 66, 75, 46, 89, 53, 36, 64…
    -$ best_pic_nom     <fct> no, no, no, no, no, no, no, no, no, no, no, no, no, no, no, no…
    -$ best_pic_win     <fct> no, no, no, no, no, no, no, no, no, no, no, no, no, no, no, no…
    -$ best_actor_win   <fct> no, no, no, yes, no, no, no, yes, no, no, yes, no, yes, no, no…
    -$ best_actress_win <fct> no, no, no, no, no, no, no, no, no, no, no, no, yes, no, no, y…
    -$ best_dir_win     <fct> no, no, no, yes, no, no, no, no, no, no, no, no, no, no, no, y…
    -$ top200_box       <fct> no, no, no, no, no, no, no, no, no, no, yes, no, no, no, no, n…
    -$ director         <chr> "Michael D. Olmos", "Rob Sitch", "Christopher Guest", "Martin …
    -$ actor1           <chr> "Gina Rodriguez", "Sam Neill", "Christopher Guest", "Daniel Da…
    -$ actor2           <chr> "Jenni Rivera", "Kevin Harrington", "Catherine O'Hara", "Miche…
    -$ actor3           <chr> "Lou Diamond Phillips", "Patrick Warburton", "Parker Posey", "…
    -$ actor4           <chr> "Emilio Rivera", "Tom Long", "Eugene Levy", "Richard E. Grant"…
    -$ actor5           <chr> "Joseph Julian Soria", "Genevieve Mooy", "Bob Balaban", "Alec …
    -$ imdb_url         <chr> "http://www.imdb.com/title/tt1869425/", "http://www.imdb.com/t…
    -$ rt_url           <chr> "//www.rottentomatoes.com/m/filly_brown_2012/", "//www.rottent…
    +
    show/hide glimpse() of movies.fst
    fstcore package v0.9.14
    +(OpenMP was not detected, using single threaded mode)
    +Rows: 651
    +Columns: 34
    +$ title            <chr> "Filly Brown", "The Dish", "Waiting for…
    +$ title_type       <fct> Feature Film, Feature Film, Feature Fil…
    +$ genre            <fct> Drama, Drama, Comedy, Drama, Horror, Do…
    +$ runtime          <dbl> 80, 101, 84, 139, 90, 78, 142, 93, 88, …
    +$ mpaa_rating      <fct> R, PG-13, R, PG, R, Unrated, PG-13, R, …
    +$ studio           <fct> Indomina Media Inc., Warner Bros. Pictu…
    +$ thtr_rel_date    <dttm> 2013-04-18 21:00:00, 2001-03-13 21:00:…
    +$ thtr_rel_year    <dbl> 2013, 2001, 1996, 1993, 2004, 2009, 198…
    +$ thtr_rel_month   <dbl> 4, 3, 8, 10, 9, 1, 1, 11, 9, 3, 6, 12, …
    +$ thtr_rel_day     <dbl> 19, 14, 21, 1, 10, 15, 1, 8, 7, 2, 19, …
    +$ dvd_rel_date     <dttm> 2013-07-29 21:00:00, 2001-08-27 21:00:…
    +$ dvd_rel_year     <dbl> 2013, 2001, 2001, 2001, 2005, 2010, 200…
    +$ dvd_rel_month    <dbl> 7, 8, 8, 11, 4, 4, 2, 3, 1, 8, 5, 9, 7,…
    +$ dvd_rel_day      <dbl> 30, 28, 21, 6, 19, 20, 18, 2, 21, 14, 1…
    +$ imdb_rating      <dbl> 5.5, 7.3, 7.6, 7.2, 5.1, 7.8, 7.2, 5.5,…
    +$ imdb_num_votes   <int> 899, 12285, 22381, 35096, 2386, 333, 50…
    +$ critics_rating   <fct> Rotten, Certified Fresh, Certified Fres…
    +$ critics_score    <dbl> 45, 96, 91, 80, 33, 91, 57, 17, 90, 83,…
    +$ audience_rating  <fct> Upright, Upright, Upright, Upright, Spi…
    +$ audience_score   <dbl> 73, 81, 91, 76, 27, 86, 76, 47, 89, 66,…
    +$ best_pic_nom     <fct> no, no, no, no, no, no, no, no, no, no,…
    +$ best_pic_win     <fct> no, no, no, no, no, no, no, no, no, no,…
    +$ best_actor_win   <fct> no, no, no, yes, no, no, no, yes, no, n…
    +$ best_actress_win <fct> no, no, no, no, no, no, no, no, no, no,…
    +$ best_dir_win     <fct> no, no, no, yes, no, no, no, no, no, no…
    +$ top200_box       <fct> no, no, no, no, no, no, no, no, no, no,…
    +$ director         <chr> "Michael D. Olmos", "Rob Sitch", "Chris…
    +$ actor1           <chr> "Gina Rodriguez", "Sam Neill", "Christo…
    +$ actor2           <chr> "Jenni Rivera", "Kevin Harrington", "Ca…
    +$ actor3           <chr> "Lou Diamond Phillips", "Patrick Warbur…
    +$ actor4           <chr> "Emilio Rivera", "Tom Long", "Eugene Le…
    +$ actor5           <chr> "Joseph Julian Soria", "Genevieve Mooy"
    +$ imdb_url         <chr> "http://www.imdb.com/title/tt1869425/",…
    +$ rt_url           <chr> "//www.rottentomatoes.com/m/filly_brown…

    To include the contents of www/ in our app-package, we need to use isnt/ folder, system.file(), and the shiny::addResourcePath() function.

  • addResourcePath()

    shiny’s addResourcePath() function will add a “directory of static resources to Shiny’s web server.” In pkgApp, want to add the www directory that includes the shiny.png file (currently in our root (".") folder).

    If we want to continue using the www/ folder name, we can simply move www and it’s contents into inst/:

    -
    inst/
    -├── extdata
    -│   └── movies.fst
    -└── www/
    -    └── shiny.png
    -
    -3 directories, 2 files
    +
    inst/
    +├── extdata
    +│   └── movies.fst
    +└── www/
    +    └── shiny.png
    +
    +3 directories, 2 files

    In R/movies_ui.R function, we’ll include the addResourcePath() at the top of the tagList() and reference the image in img() using only the subfolder in the path:

    -
    movies_ui <- function() {
    +
    movies_ui <- function() {
       shiny::addResourcePath('www', system.file('www', package = 'pkgApp'))
       shiny::tagList(
         shiny::fluidPage(
    @@ -621,6 +605,11 @@ 

    }

    After loading, documenting, and installing, the application now includes the image file.

    +

    Ctrl/Cmd + Shift + L / D / B

    +
    +
    library(pkgApp)
    +movies_app()
    +
    @@ -631,30 +620,16 @@

    You can read more about adding external resources in the documentation for addResourcePath(). We’ll also cover adding CSS and JavaScript files in later chapters.

    -
    -
    -
    - -
    -
    -GitHub [pkgApp]:[05-inst] -
    -
    -
    -
    -
    -
    -

    The code for this section was pushed to the [05-inst] branch of the [pkgApp] repo.

    -
    git add .
    -git commit -m 'git message'
    -git push 
    -
    -
    -
    + +
    +
    +

    +
    New Git Branch
    -

    Recap

    +

    The code for this section is in the [05_inst] branch of the [pkgApp] repo.

    +

    Recap

    -
    +
    @@ -663,7 +638,7 @@

    -
    +
      @@ -676,7 +651,7 @@

      inst/ └── www/ └── shiny.png -
      # add path
      +
      # add path
       addResourcePath('www', system.file('www', package='pkgApp'))
       # use path
       shiny::img(src = 'www/shiny.png')
      diff --git a/intro.html b/intro.html index 00449f1..c6445ce 100644 --- a/intro.html +++ b/intro.html @@ -262,6 +262,12 @@ GitHub
      + +

    diff --git a/js.html b/js.html index c4daa8f..e4753ff 100644 --- a/js.html +++ b/js.html @@ -262,6 +262,12 @@ GitHub
    + + diff --git a/leprechaun.html b/leprechaun.html index f4d69a9..576aa0a 100644 --- a/leprechaun.html +++ b/leprechaun.html @@ -296,6 +296,12 @@ GitHub
    + + diff --git a/packages.html b/packages.html index e3e2f3c..59ccdb0 100644 --- a/packages.html +++ b/packages.html @@ -296,6 +296,12 @@ GitHub
    + + @@ -307,31 +313,37 @@
    -

    This chapter dives into the makings of R packages. I’ll cover how R packages differ from R projects and what’s required to convert a shiny project into an R package.

    -

    The previous chapter covered a few practices to adopt during shiny development that improves the extensibility of your app to an R package (these are summarized in the callout box below).

    +

    This chapter dives into the makings of R packages. We’ll start by covering the differences between R projects, R packages, and app-packages. Then we’ll dive into what’s required to convert a R project into an R package. The last section in this chapter will cover the usethis::create_package() function, which can be used to create new shiny app-packages and convert existing shiny projects into app-packages.

    +

    Projects, packages, and app-packages

    +

    Before we start, we should establish some ‘operational definitions’ of what I mean by the terms ‘project’, ‘package’, and ‘app-package’.

    + +
    +

    I’ll be using color to differentiate shiny app projects, R packages, and app-packages.

    +
      +
    • project: I consider a ‘project’ to be any directory of files requiring R (the binary downloaded from CRAN, i.e., ‘R 4.3.1 “Beagle Scouts” released on …’) to execute with an RStudio/Posit workbench project file (.Rproj).

    • +
    • package: I’ll be using the term ‘package’ to describe a directory of functions, documentation, and/or data can be installed and loaded into an R session. A package includes the necessary dependency management (NAMESPACE) and metadata files (DESCRIPTION) and has access to the package development tools in RStudio/Posit Workbench.

    • +
    • app-package: an app-package (which you have probably already guessed) is a package containing a shiny application. App-packages have all of the functionality of a standard R package, but also contain the files and folders required to successfully develop, run, and deploy a shiny application.

    • +
    +
    +
    +
    + +
    +
    +R Packages & Posit Workbench +
    +
    +
    +
    +
    +
    +

    Posit Workbench (formerly RStudio) is a popular integrated development environment (IDE) that streamlines many of the R package development tasks. I’ve purposely connected Posit Workbench–specifically, the package development tools provided in the Build pane and devtools–to the definitions above for package and app-package above.

    +

    However, it’s not necessary to use Posit Workbench (or .Rproj files) to develop R packages. There are alternative package development tools and processes available outside of Posit Workbench, and many developers use these setups.

    +

    Package development outside Posit Workbench would look almost identical to development in the IDE:

    +
      +
    1. Create the package structure (R/ for .R scripts, man/ for documentation, data/ for datasets, etc.)

    2. +
    3. Add DESCRIPTION and NAMESPACE files, etc.

    4. +
    5. Ensure the package can be installed and loaded into an R session.

    6. +
    +

    It’s also possible to use many of the development workflow functions we’ll cover here outside of the IDE (roxygen2::roxygenize(), devtools::check(), devtools::install(), etc.).

    +
    +
    +
    +
    +

    The previous chapter covered a few practices to adopt during shiny development that improves the extensibility of your app project to an app-package (these are summarized in the callout box below).

    - -
    +
      @@ -433,21 +481,17 @@

      Packages

    -

    For more information on the topics in this chapter, you should read Writing R Extensions (the official documentation for creating R packages) and R Packages, 2ed.

    -

    -App-packages: motivation

    -

    There are multiple reasons for deciding to develop an app-package: your app project might have to be in a package structure due to specific organizational needs or practices around deploying applications; or maybe you’ve been convinced it’s the best practice for improving the shareability/reproducibility of your app project.

    - -
    -

    I’ll be using color to differentiate shiny app projects from app-packages.

    -

    Regardless of the reason, you’ve likely found yourself in one of two circumstances:

    +

    +App-packages: motivation

    +

    There are multiple reasons for deciding to develop an app-package: your app project might have to be in a package structure due to specific organizational needs or practices around deploying applications; or maybe you’ve been convinced it’s the best practice for improving the shareability/reproducibility of your app project.

    +

    Regardless of the reason, you’ve likely found yourself in one of two circumstances:

      -
    1. You want to develop a new shiny app, but want it structured as an app-package.

    2. -
    3. You’ve already developed a shiny app project, but now you need to convert it to an app-package

    4. +
    5. You want to develop a new shiny app, but want it structured as an app-package.

    6. +
    7. You’ve already developed a shiny app project, but now you need to convert it to an app-package

    -

    This chapter will cover what’s required in an app-package so you’ll be able to handle both scenarios above.

    -

    What makes an R package?

    -

    Below is a folder tree with some of the typical files and folders found in R packages:

    +

    R package vs. shiny project +

    +

    Below is a folder tree with some of the typical files and folders found in R packages:

    <R package>/
         ├── DESCRIPTION
    @@ -469,8 +513,8 @@ 

    Packages

    └── renv/
    -

    The reason R packages can be easily shared, reused, and reproduced is because they all have a familiar structure, and each folder and file plays an essential role in extending R’s capabilities.

    -

    At first glance, the contents above can seem daunting when compared to the folders and files that currently exist in your app project (like projApp below):

    +

    The reason R packages can be easily shared, reused, and reproduced is because they all have a familiar structure, and each folder and file plays an essential role in extending R’s capabilities.

    +

    At first glance, the contents above can seem daunting when compared to the folders and files that currently exist in your app project (like projApp below):

    projApp/
     ├── DESCRIPTION
    @@ -488,28 +532,20 @@ 

    Packages

    4 directories, 9 files
    -

    Fortunately, app-packages don’t require all the files and folders displayed above to gain the functionality and benefits of an R package.

    +

    Fortunately, app-packages don’t require all the files and folders displayed above to gain the functionality and benefits of an R package.

    +

    This chapter will cover what’s required in all R packages, so you’ll be able to handle both creating new app-packages and converting existing shiny projects into app-packages

    +

    What makes an R package?

    If you’ve read R packages, 2ed or Mastering Shiny, you might recall encountering one (or both) of the following quotes,

    ‘Every package must have a DESCRIPTION. In fact, it’s the defining feature of a package (RStudio and devtools consider any directory containing DESCRIPTION to be a package)’ - R Packages, 2ed (Chapter 9, DESCRIPTION)

    ‘all a project needs to be a package is a directory of R/ files and a DESCRIPTION file.’ - Mastering Shiny (Chapter 20, Packages)

    -

    If you’ve been following along with the code, these quotes should confuse you. The projApp shiny project has a DESCRIPTION file and an R/ directory, but projApp is not a functioning package.

    -

    A functioning package should have (at minimum) the following:

    -

    The Build pane

    -

    Functioning R packages have access to the Build pane, which allows us to quickly iterate between loading, installing, and testing. But if projApp.Rproj is opened, only the following panes are visible in the IDE:

    -
    -
    -
    -

    -
    (a) projApp IDE
    -
    -
    Figure 1: Project IDE panes
    -
    -
    -

    +

    If you’ve been following along with the code, these quotes should confuse you. The projApp shiny project has a DESCRIPTION file and an R/ directory, but projApp is not a functioning package.

    +

    As we defined above, a functioning package can be installed and loaded into an R session, and has access to the Build pane in the IDE.

    +

    devtools functions

    -

    In order to use the options in the Build pane, we have to be able to run the devtools functions. However, when we install and load devtools, then try to load the code in the R/ folder with load_all(), we see the following error:

    +

    devtools simulates installing and loading with load_all() (we’ll cover this function extensively in devtools chapter),

    +

    But when we attempt to load the code in the R/ folder with load_all(), we see the following error:

    install.packages("devtools")
     library(devtools)
    @@ -520,19 +556,29 @@ 

    Packages

    (a) Load All Error
    -
    Figure 2: devtools is looking for the Package field in the DESCRIPTION file
    +
    Figure 1: devtools is looking for the Package field in the DESCRIPTION file
    +
    +

    The Build pane

    +

    When opened, functioning packages have access to the Build pane, which allows us to quickly iterate between loading, installing, and testing. But when projApp is opened, the Build pane is not displayed in the IDE:

    +
    +
    +
    +

    +
    (a) projApp IDE
    +
    +
    Figure 2: Project IDE panes
    -

    So, in order to be a functioning package, projApp must have 1) access to the Build pane in the IDE, 2) the ability to run the devtools development functions (starting with the function we’ll use the most during package development, load_all())

    -

    What really makes an R package?

    -

    As we’ve just learned, the presence of the DESCRIPTION file and an R/ folder are not sufficient to turn your app project into a app-package (i.e., with a Build pane and ‘functioning’ devtools workflow).

    -

    For practical purposes, I’ll rewrite the minimum requirements for creating a ‘functional’ R package:

    +
    +

    As we’ve just learned, the presence of the DESCRIPTION file and an R/ folder are not sufficient to turn your app project into a package (i.e., or an app-package).

    +

    What really makes an R package?

    +

    For our purposes, I’ll rewrite the minimum requirements for creating a ‘functional’ R package:

    -

    A project needs a DESCRIPTION file with specific fields, a directory of R/ files, and a properly configured .Rproj file to be a package.” - Me

    +

    A project needs a DESCRIPTION file with specific fields, a directory of R/ files, and a properly configured .Rproj file to be a package.” - Me

    -

    Let’s see how each of these requirements work together to create a app-package.

    +

    Let’s see how each of these requirements work together to convert the contents of projApp from a project into a package.

    DESCRIPTION fields

    -

    The official R documentation lists the following required fields for the DESCRIPTION in R packages:

    +

    The official R documentation lists the following required fields for the DESCRIPTION in R packages:

    The ‘Package’, ‘Version’, ‘License’, ‘Description’, ‘Title’, ‘Author’, and ‘Maintainer’ fields are mandatory

    @@ -554,7 +600,7 @@

    Packages

    License: GPL-3 *leave empty final line*
    - -
    +

    Note that the Author and Maintainer fields require additional information beyond first and last name (i.e., John Smith). These can be created with the utils::person() function:

    @@ -594,12 +640,13 @@

    Packages

    Figure 3: Where is the Build pane?
    -

    Project options

    +

    +Project options

    i.e., the .Rproj file

    .Rproj files are plain text files with various settings for the IDE. The reason we were able to run devtools::load_all() above without the presence of the Build pane is because the IDE displays the Build pane after reading the fields in projApp.Rproj file.

    The quickest way to access the fields the .Rproj file is under Tools > Project options.

    -

    Default project options

    -

    Tools > Project options provide access to the project-level options. For example, fields 2 - 4 are available under General, fields 5 - 8 affect the Code options, and the final two fields deal with Sweave.

    +

    Default project options

    +

    Tools > Project options provide access to the project-level options. For example, fields 2 - 4 are available under General, fields 5 - 8 affect the Code options, and the final two fields deal with Sweave.

    @@ -607,7 +654,7 @@

    Packages

    Figure 4: Field settings from projApp.Rproj file
    -

    These are the default settings that were created when we selected the Shiny App project from the New Project Wizard in the last chapter.

    +

    These are the default settings that were created when we selected the Shiny App project from the New Project Wizard in the last chapter.

    Build tools

    If you’ve read the What makes an RStudio Project? section of R Packages (2 ed), you’ll notice a few differences between each .Rproj file (I’ve placed the files side-by-side so you can compare them below):

    @@ -617,7 +664,7 @@

    Packages

    Figure 5: Comparison of projApp.Rproj file and .Rproj file in R Packages, 2ed
    -

    I’ve circled the fields in the R Packages, 2ed .Rproj file that illustrate it’s configured to work with a package. Note that in projApp, the Project build tools are initially set to (None) under Build Tools:

    +

    I’ve circled the fields in the R Packages, 2ed .Rproj file that illustrate it’s configured to work with a package. Note that in projApp, the Project build tools are initially set to (None) under Build Tools:

    @@ -625,7 +672,7 @@

    Packages

    Figure 6: Build tool settings in projApp.Rproj file
    -

    This setting should help explain the absence of any package development fields in the projApp.Rproj when we compared it to the version in R Packages, 2ed (i.e., BuildType, PackageUseDevtools, PackageInstallArgs, and PackageRoxygenize).

    +

    This setting should help explain the absence of any package development fields in the projApp.Rproj when we compared it to the version in R Packages, 2ed (i.e., BuildType, PackageUseDevtools, PackageInstallArgs, and PackageRoxygenize).

    Changing the Project build tools option to Package will set the default Build Tools options:

    @@ -643,13 +690,13 @@

    Packages

    Figure 8: Default build tool settings in projApp.Rproj file
      -
    • BuildType: Package tells the IDE projApp is a package and triggers the Build pane.

    • +
    • BuildType: Package tells the IDE projApp is a package and triggers the Build pane.

    • PackageUseDevtools: Yes links the options in the Build pane to the devtools package.

    • The PackageInstallArgs are complicated, but I’ve included some information about them in the callout block below (and you can read more in the official R documentation)

    • The fourth option (PackageRoxygenize) is available under Generate documentation with Roxygen > Use roxygen to generate

        -
      • These options affect the documentation in your app-package. To match the example from R Packages, 2ed above, make sure Rd files, Collate field, and NAMESPACE file are selected and click OK +
      • These options affect the documentation in your package. To match the example from R Packages, 2ed above, make sure Rd files, Collate field, and NAMESPACE file are selected and click OK
    • @@ -690,7 +737,7 @@

      Packages

      PackageRoxygenize: rd,collate,namespace
      - -
      +
        @@ -711,7 +758,7 @@

        Packages

    Build pane

    -

    When the new session starts, the new project-level options will activate the Build pane in the IDE.

    +

    When the new session starts, the new project-level options will activate the Build pane in the IDE.

    @@ -721,7 +768,7 @@

    Packages

    Figure 10: Build pane triggered from project-level settings
    -

    When the IDE reboots, I can see the Build pane has been added, and I can check the app-package functionality by loading the code with Build > Load All

    +

    When the IDE reboots, I can see the Build pane has been added, and I can check the package functionality by loading the code with Build > Load All

    @@ -731,8 +778,9 @@

    Packages

    I should see the following in the Console:

    ℹ Loading projApp
    -

    There you have it–projApp is a functional app-package!

    -

    An app-package

    +

    There you have it–projApp is a functional package!

    +

    Functional R packages +

    @@ -742,21 +790,26 @@

    Packages

    Figure 12: Fully functional shiny app-package
    -

    The DESCRIPTION file contains the seven mandatory fields (Package, Version, License, Description, Title, Author, and Maintainer), which make it possible to run the necessary devtools functions.

    -

    The .Rproj file contains the three package configuration fields (BuildType: Package, PackageUseDevtools: Yes, and PackageInstallArgs: --no-multiarch --with-keep.source), which makes the Build pane accessible and functional.

    -

    A quicker way: create_package() +

    A functional R package:

    +
      +
    1. The DESCRIPTION file contains the seven mandatory fields (Package, Version, License, Description, Title, Author, and Maintainer), which make it possible to run the necessary devtools functions.

    2. +
    3. The .Rproj file contains the three package configuration fields (BuildType: Package, PackageUseDevtools: Yes, and PackageInstallArgs: --no-multiarch --with-keep.source), which makes the Build pane accessible and functional.

    4. +
    +

    The items above will create a functional R package, but these are only the first steps (we haven’t developed anything yet!) on our way to an app-package.

    +

    Think of the two items above as two-part process: the DESCRIPTION fields are required by the official R documentation (and hence, devtools), and the .Rproj fields are required by the IDE trigger the Build pane. For more information on the topics in this chapter, you should read Writing R Extensions (the official documentation for creating R packages) and R Packages, 2ed.

    +

    Creating packages with create_package()

    -

    The Posit documentation lists only two ways to create packages,

    +

    The Posit documentation lists the following ways to create R packages,

    1. Call usethis::create_package().

    2. In RStudio, do File > New Project > New Directory > R Package. This ultimately calls usethis::create_package(), so really there’s just one way.

    -

    While this information isn’t incorrect, we did just demonstrate it’s possible to create a package without the usethis::create_package() function. Still, if you’re looking for a way to quickly create or convert your app into an app-package, using create_package() is a great option.

    -

    Below are suggestions for using create_package() to create and convert app-packages.

    +

    While this information isn’t incorrect, we did just demonstrate it’s possible to create a package without the usethis::create_package() function. Still, if you’re looking for a way to quickly create or convert your app project into an app-package, using create_package() is a great option.

    +

    This function is covered in the ‘Fundamental development workflows’ chapter of R Packages, 2ed. I’ve provided some suggestions below for using create_package():

    New app-packages

    -

    If you want to create a new shiny app-package, but haven’t written any code, you can create one with the create_package() function from the usethis package.

    +

    If you want to create a new shiny app-package, but haven’t written any code, you can create it with the create_package() function from the usethis package.

    First install devtools:

    install.packages("devtools")
    @@ -764,7 +817,7 @@ 

    Packages

    devtools automatically loads usethis

    Loading required package: usethis
    -

    Assuming your current working directory is where you want your new app-package, call usethis::create_package() with the path argument set to getwd()

    +

    Assuming your current working directory is where you want your new app-package, call usethis::create_package() with the path argument set to getwd()

    usethis::create_package(path = getwd())
    @@ -804,16 +857,18 @@

    Packages

    ├── NAMESPACE ├── R/ └── projApp.Rproj
    -

    You’re now free to develop newApp. Store any .R files in the R/ folder, edit the DESCRIPTION file with details about the application, and read up on package development in R Packages.

    +

    You’re now free to develop newApp. Store and document any .R files in the R/ folder, edit the DESCRIPTION file with details about the application, read through R Packages and Mastering Shiny, and add the shiny code to complete your app-package

    Converting existing app

    -

    If you already have a shiny app project that needs to be converted into an app-package (like the app files stored in the 03_projApp branch), you can also use create_package() in your root folder, but I recommend using the following arguments:

    +

    If you already have a shiny app project that needs to be converted into an app-package (like the app files stored in the 03_projApp branch), you can also use create_package(path = getwd()) in your root folder, but I recommend using the following arguments:

    DESCRIPTION arguments

      -
    • path: set this with getwd() to avoid warnings about nested projects.

    • -
    • fields: these arguments are passed to the fields argument of usethis::use_description() (we’ve covered the importance of the DESCRIPTION file in the next section).

    • -

      All fields should be passed in a list() as field = 'value' pairs.

      +

      fields: these are arguments passed to usethis::use_description(). If fields is empty, a boilerplate DESCRIPTION file is created (similar to this one).

      +
        +
      • These boilerplate fields in the DESCRIPTION work, but I’ve found some of the fields are unnecessary (i.e., I’ve never needed Authors@R: or ORCID) and inevitably require revision, so I’d prefer to handle this during the creation process (and remove the risk of forgetting to change it later).

      • +
      • All fields should be passed in a list() as field = 'value' pairs.

      • +
      usethis::use_description(
           list(Package = 'projApp',
      @@ -849,13 +904,13 @@ 

      Packages

    • Two additional arguments from use_description() are passed to create_package():

        -
      • check_name: verifies your app-package name is valid for CRAN, so we can set this to FALSE (unless you’re planning on submitting the package to CRAN)

      • -
      • roxygen2: is set to TRUE by default. This adds the necessary roxygen2 fields which I won’t cover here because we’ll cover documentation in-depth a future chapter.

      • +
      • check_name: verifies your app-package name is valid for CRAN, so we can set this to FALSE (unless you’re planning on submitting to CRAN)

      • +
      • roxygen2: is TRUE by default and adds the fields required to use roxygen2 (which I won’t cover here because we’ll cover documentation in-depth a future chapter).

    - -
    +
    -

    When converting your existing shiny app project into am app-package with usethis::create_package(), don’t use '.' in the path argument:

    +

    When converting your existing shiny app project into am app-package with usethis::create_package(), don’t use '.' in the path argument:

    usethis::create_package('.')
    -

    This will return the following warning about creating nested projects, and ask if you want to create the project anyway:

    -
    New project 'projApp' is nested inside an existing project './', which is 
    -rarely a good idea. If this is unexpected, the here package has a function,
    -`here::dr_here()` that reveals why './' is regarded as a project.
    +

    This will return the following warning about creating nested projects, and ask if you want to proceed anyway:

    +
    New project 'projApp' is nested inside an existing project
    +'./', which is rarely a good idea. If this is unexpected, 
    +the here package has a function,
    +`here::dr_here()` that reveals why './' is regarded as a
    +project.
       
     Do you want to create anyway?
     
     1: Yes
     2: No way
     3: Not now
    -

    We can avoid this warning altogether by passing the getwd() to the path argument, so I recommend cancelling the project creation:

    +

    We can avoid this warning altogether by passing the getwd() to the path argument, so I recommend cancelling the project creation:

    Selection: 2
     Error: Cancelling project creation.
    @@ -888,8 +945,8 @@

    Packages

    IDE arguments

      -
    • rstudio: adds the necessary package development fields in the .Rproj file (leave as TRUE)

    • -
    • open: set to FALSE (we don’t need RStudio to open in a new session)

    • +
    • rstudio: adds the necessary Build Tools fields in the .Rproj file (leave as TRUE)

    • +
    • open: can be set to FALSE because we don’t need RStudio/Posit Workbench to open in a new session

    usethis::create_package(
    @@ -917,7 +974,7 @@ 

    Packages

    After running usethis::create_package() with the arguments above, the IDE will present us with a few prompts to confirm:

    Overwrite pre-existing file 'DESCRIPTION'?
    Overwrite pre-existing file 'projApp.Rproj'?
    -

    The final package structure is below:

    +

    The app-package structure is below:

    ├── DESCRIPTION
     ├── NAMESPACE
     ├── R
    @@ -932,7 +989,7 @@ 

    Packages

    └── shiny.png 3 directories, 10 files
    -

    The DESCRIPTION file (shown below) has a few additional fields we didn’t include when we converted projApp above (Encoding, Roxygen, and RoxygenNote), but we will cover these in the upcoming chapters.

    +

    The DESCRIPTION file (shown below) has a few additional fields (Encoding, Roxygen, and RoxygenNote) we didn’t include when we converted projApp above, but we will cover these in the upcoming chapters.

    Package: projApp
     Title: movies app
     Version: 0.0.0.9000
    @@ -951,9 +1008,9 @@ 

    Packages

    The code for this section was pushed to the [06_create-package] branch of the [projApp] repo.

    Recap

    -

    This chapter has covered the importance of the DESCRIPTION file in R packages, project and package fields in .Rproj files, and the usethis::create_package() function.

    +

    This chapter has covered the mandatory fields in the DESCRIPTION file and the package configuration fields in .Rproj. We also covered creating and converting projects using the usethis::create_package() function.

    -
    +
    @@ -962,22 +1019,23 @@

    Packages

    -
    +
    • -

      The DESCRIPTION file ultimately controls whether you have a functional shiny app-package

      +

      The DESCRIPTION file ultimately controls whether you have a functional shiny package

        -
      • The mandatory fields for an R package are: Package, Version, License, Description, Title, Author, and Maintainer +
      • The mandatory fields are: Package, Version, License, Description, Title, Author, and Maintainer
    • -
    • usethis::create_package() can be used to create a new shiny app-package and to convert an existing shiny project into a shiny app-package

    • +
    • usethis::create_package() can be used to create a new package and to convert an existing shiny project into a shiny app-package

    • -

      The IDE reads RStudio project (.Rproj) files and determines R session settings at the project-level (i.e., working directory, workspace, history, code formatting, etc.)

      +

      The IDE reads RStudio (.Rproj) files and determines R session settings at the project-level (i.e., working directory, workspace, history, code formatting, etc.)

        -
      • Package development settings can be accessed via Project Options > Build Tools +
      • +Package development settings can be accessed via Project Options > Build Tools
    • diff --git a/rhino.html b/rhino.html index b2928f4..13a05f5 100644 --- a/rhino.html +++ b/rhino.html @@ -296,6 +296,12 @@ GitHub
    + + diff --git a/roxygen2.html b/roxygen2.html index ea1c353..70347fe 100644 --- a/roxygen2.html +++ b/roxygen2.html @@ -296,6 +296,12 @@ GitHub
    + + diff --git a/run.html b/run.html index f3e2c7c..7ec7ff6 100644 --- a/run.html +++ b/run.html @@ -296,6 +296,12 @@ GitHub
    + + diff --git a/search.json b/search.json index 3b34f79..275359d 100644 --- a/search.json +++ b/search.json @@ -32,7 +32,7 @@ "href": "index.html#what-i-assume-about-you", "title": "Shiny App-Packages", "section": "What I assume about you", - "text": "What I assume about you\nIf you’re reading this, I assume you’re comfortable with R, RStudio, Shiny, and the tidyverse. Maybe you haven’t built a ton of applications, but you understand reactivity, and you’re comfortable with the core Shiny concepts (i.e., the UI, server, *_Input(), *_Output(), and render_* functions, etc.)." + "text": "What I assume about you\nIf you’re reading this, I assume you’re comfortable with R, Posit Workbench, Shiny, and the tidyverse. Maybe you haven’t built a ton of applications, but you understand reactivity, and you’re comfortable with the core Shiny concepts (i.e., the UI, server, *_Input(), *_Output(), and render_* functions, etc.)." }, { "objectID": "index.html#other-resources", @@ -81,7 +81,7 @@ "href": "shiny.html#new-shiny-app-projects", "title": "Shiny apps", "section": "New shiny app projects", - "text": "New shiny app projects\nIf you’re creating a new application using the New Project Wizard, you’ll see the following:\n\n\n\n(a) New shiny app\n\nFigure 1: New shiny app project\n\nAfter selecting the location of your shiny app project, you can pick a name and decide whether you want to use Git or renv.\n\n\n\n(a) Shiny app info\n\nFigure 2: New shiny app project in a Git repository\n\nAfter clicking Create Project, a new RStudio session will open with your project files." + "text": "New shiny app projects\nIf you’re creating a new application using the New Project Wizard, you’ll see the following:\n\n\n\n(a) New shiny app\n\nFigure 1: New shiny app project\n\nAfter selecting the location of your shiny app project, you can pick a name and decide whether you want to use Git or renv.\n\n\n\n(a) Shiny app info\n\nFigure 2: New shiny app project in a Git repository\n\nAfter clicking Create Project, a new session will open with your project files." }, { "objectID": "shiny.html#shiny-app-project-contents", @@ -132,47 +132,54 @@ "section": "Recap", "text": "Recap\nThis chapter has covered some of the differences between developing shiny apps and regular R programming, creating new shiny projects in Posit Workbench, and some practices to adopt that can make the transition to app-packages a little easier. The code used in this chapter are stored in the projApp repository.\n\n\n\n\n\n\nRecap\n\n\n\n\n\n\n\nPlacing any utility or helper files in an R/ folder removes the need to call source() in app.R\nImages, CSS, JavaScript, and other static resources can be stored in www/ and shiny will serve these files when the application is run.\nREADME.md files can can be used to document application description, purpose, requirements, etc.\nDESCRIPTION files provide metadata and includes fields that affect application deployment (i.e., DisplayMode: Showcase)\nConverting the application code into functions (modules and standalone app functions) creates a ‘division of labor’ for each component, which makes it easier to think about and work on them independently.\nFinally, if you’re using renv, run renv::status() and renv::snapshot() to manage dependencies\n\n\n\n\n\nIn the next chapter, I’ll cover what makes a package a package, and some do’s and don’ts when converting a developed shiny application into an R package.\nend shiny.qmd" }, + { + "objectID": "packages.html#projects-packages-and-app-packages", + "href": "packages.html#projects-packages-and-app-packages", + "title": "Packages", + "section": "Projects, packages, and app-packages", + "text": "Projects, packages, and app-packages\nBefore we start, we should establish some ‘operational definitions’ of what I mean by the terms ‘project’, ‘package’, and ‘app-package’.\n\n\nI’ll be using color to differentiate shiny app projects, R packages, and app-packages.\n\nproject: I consider a ‘project’ to be any directory of files requiring R (the binary downloaded from CRAN, i.e., ‘R 4.3.1 “Beagle Scouts” released on …’) to execute with an RStudio/Posit workbench project file (.Rproj).\npackage: I’ll be using the term ‘package’ to describe a directory of functions, documentation, and/or data can be installed and loaded into an R session. A package includes the necessary dependency management (NAMESPACE) and metadata files (DESCRIPTION) and has access to the package development tools in RStudio/Posit Workbench.\napp-package: an app-package (which you have probably already guessed) is a package containing a shiny application. App-packages have all of the functionality of a standard R package, but also contain the files and folders required to successfully develop, run, and deploy a shiny application.\n\n\n\n\n\n\n\nR Packages & Posit Workbench\n\n\n\n\n\n\nPosit Workbench (formerly RStudio) is a popular integrated development environment (IDE) that streamlines many of the R package development tasks. I’ve purposely connected Posit Workbench–specifically, the package development tools provided in the Build pane and devtools–to the definitions above for package and app-package above.\nHowever, it’s not necessary to use Posit Workbench (or .Rproj files) to develop R packages. There are alternative package development tools and processes available outside of Posit Workbench, and many developers use these setups.\nPackage development outside Posit Workbench would look almost identical to development in the IDE:\n\nCreate the package structure (R/ for .R scripts, man/ for documentation, data/ for datasets, etc.)\nAdd DESCRIPTION and NAMESPACE files, etc.\nEnsure the package can be installed and loaded into an R session.\n\nIt’s also possible to use many of the development workflow functions we’ll cover here outside of the IDE (roxygen2::roxygenize(), devtools::check(), devtools::install(), etc.).\n\n\n\n\nThe previous chapter covered a few practices to adopt during shiny development that improves the extensibility of your app project to an app-package (these are summarized in the callout box below).\n\n\n\n\n\n\nShiny ‘pre-package’ practices\n\n\n\n\n\n\n\n\nFolders\n\nwww: can be used to store static resources (images, CSS files, JavaScript)\n\nR/: any .R files in the R/ folder will automatically be loaded with your app when it’s run.\n\nConverting the code in app.R into separate functions (i.e., modules and standalone app functions) also allows you to develop and debug the code independently.\n\n\n\n\n\nFiles\n\n\nDESCRIPTION files store metadata and include fields for application deployment (i.e., DisplayMode: Showcase)\n\n\nREADME.md files can document application descriptions, purposes, requirements, etc.\n\n\n\nDependencies\n\n\nrenv can be used to help manage app dependencies.\n\nRun renv::status() & renv::snapshot() to record dependencies in the lock file." + }, { "objectID": "packages.html#app-packages-motivation", "href": "packages.html#app-packages-motivation", "title": "Packages", "section": "\nApp-packages: motivation", - "text": "App-packages: motivation\nThere are multiple reasons for deciding to develop an app-package: your app project might have to be in a package structure due to specific organizational needs or practices around deploying applications; or maybe you’ve been convinced it’s the best practice for improving the shareability/reproducibility of your app project.\n\n\nI’ll be using color to differentiate shiny app projects from app-packages.\nRegardless of the reason, you’ve likely found yourself in one of two circumstances:\n\nYou want to develop a new shiny app, but want it structured as an app-package.\nYou’ve already developed a shiny app project, but now you need to convert it to an app-package\n\nThis chapter will cover what’s required in an app-package so you’ll be able to handle both scenarios above." + "text": "App-packages: motivation\nThere are multiple reasons for deciding to develop an app-package: your app project might have to be in a package structure due to specific organizational needs or practices around deploying applications; or maybe you’ve been convinced it’s the best practice for improving the shareability/reproducibility of your app project.\nRegardless of the reason, you’ve likely found yourself in one of two circumstances:\n\nYou want to develop a new shiny app, but want it structured as an app-package.\nYou’ve already developed a shiny app project, but now you need to convert it to an app-package\n\nR package vs. shiny project\n\nBelow is a folder tree with some of the typical files and folders found in R packages:\n\n<R package>/\n ├── DESCRIPTION\n ├── <R package>.Rproj\n ├── LICENSE \n ├── LICENSE.md \n ├── NAMESPACE \n ├── NEWS.md\n ├── README.Rmd\n ├── README.md\n ├── renv.lock\n ├── R/\n ├── man/\n ├── tests/\n ├── data/ \n ├── data-raw/ \n ├── vignettes/ \n ├── inst/ \n └── renv/\n \n\nThe reason R packages can be easily shared, reused, and reproduced is because they all have a familiar structure, and each folder and file plays an essential role in extending R’s capabilities.\nAt first glance, the contents above can seem daunting when compared to the folders and files that currently exist in your app project (like projApp below):\n\nprojApp/\n├── DESCRIPTION\n├── R/\n│ ├── mod_scatter_display.R\n│ ├── mod_var_input.R\n│ └── utils.R\n├── README.md\n├── app.R\n├── movies.RData\n├── projApp.Rproj\n├── rsconnect/\n└── www/\n └── shiny.png\n\n4 directories, 9 files\n\nFortunately, app-packages don’t require all the files and folders displayed above to gain the functionality and benefits of an R package.\nThis chapter will cover what’s required in all R packages, so you’ll be able to handle both creating new app-packages and converting existing shiny projects into app-packages" }, { "objectID": "packages.html#what-makes-an-r-package", "href": "packages.html#what-makes-an-r-package", "title": "Packages", "section": "What makes an R package?", - "text": "What makes an R package?\nBelow is a folder tree with some of the typical files and folders found in R packages:\n\n<R package>/\n ├── DESCRIPTION\n ├── <R package>.Rproj\n ├── LICENSE \n ├── LICENSE.md \n ├── NAMESPACE \n ├── NEWS.md\n ├── README.Rmd\n ├── README.md\n ├── renv.lock\n ├── R/\n ├── man/\n ├── tests/\n ├── data/ \n ├── data-raw/ \n ├── vignettes/ \n ├── inst/ \n └── renv/\n \n\nThe reason R packages can be easily shared, reused, and reproduced is because they all have a familiar structure, and each folder and file plays an essential role in extending R’s capabilities.\nAt first glance, the contents above can seem daunting when compared to the folders and files that currently exist in your app project (like projApp below):\n\nprojApp/\n├── DESCRIPTION\n├── R/\n│ ├── mod_scatter_display.R\n│ ├── mod_var_input.R\n│ └── utils.R\n├── README.md\n├── app.R\n├── movies.RData\n├── projApp.Rproj\n├── rsconnect/\n└── www/\n └── shiny.png\n\n4 directories, 9 files\n\nFortunately, app-packages don’t require all the files and folders displayed above to gain the functionality and benefits of an R package.\nIf you’ve read R packages, 2ed or Mastering Shiny, you might recall encountering one (or both) of the following quotes,\n\n‘Every package must have a DESCRIPTION. In fact, it’s the defining feature of a package (RStudio and devtools consider any directory containing DESCRIPTION to be a package)’ - R Packages, 2ed (Chapter 9, DESCRIPTION)\n‘all a project needs to be a package is a directory of R/ files and a DESCRIPTION file.’ - Mastering Shiny (Chapter 20, Packages)\n\nIf you’ve been following along with the code, these quotes should confuse you. The projApp shiny project has a DESCRIPTION file and an R/ directory, but projApp is not a functioning package.\nA functioning package should have (at minimum) the following:\nThe Build pane\nFunctioning R packages have access to the Build pane, which allows us to quickly iterate between loading, installing, and testing. But if projApp.Rproj is opened, only the following panes are visible in the IDE:\n\n\n\n\n(a) projApp IDE\n\nFigure 1: Project IDE panes\n\n\n\ndevtools functions\nIn order to use the options in the Build pane, we have to be able to run the devtools functions. However, when we install and load devtools, then try to load the code in the R/ folder with load_all(), we see the following error:\n\ninstall.packages(\"devtools\")\nlibrary(devtools)\ndevtools::load_all()\n\n\n\n\n(a) Load All Error\n\nFigure 2: devtools is looking for the Package field in the DESCRIPTION file\n\nSo, in order to be a functioning package, projApp must have 1) access to the Build pane in the IDE, 2) the ability to run the devtools development functions (starting with the function we’ll use the most during package development, load_all())" + "text": "What makes an R package?\nIf you’ve read R packages, 2ed or Mastering Shiny, you might recall encountering one (or both) of the following quotes,\n\n‘Every package must have a DESCRIPTION. In fact, it’s the defining feature of a package (RStudio and devtools consider any directory containing DESCRIPTION to be a package)’ - R Packages, 2ed (Chapter 9, DESCRIPTION)\n‘all a project needs to be a package is a directory of R/ files and a DESCRIPTION file.’ - Mastering Shiny (Chapter 20, Packages)\n\nIf you’ve been following along with the code, these quotes should confuse you. The projApp shiny project has a DESCRIPTION file and an R/ directory, but projApp is not a functioning package.\nAs we defined above, a functioning package can be installed and loaded into an R session, and has access to the Build pane in the IDE.\n\ndevtools functions\ndevtools simulates installing and loading with load_all() (we’ll cover this function extensively in devtools chapter),\nBut when we attempt to load the code in the R/ folder with load_all(), we see the following error:\n\ninstall.packages(\"devtools\")\nlibrary(devtools)\ndevtools::load_all()\n\n\n\n\n(a) Load All Error\n\nFigure 1: devtools is looking for the Package field in the DESCRIPTION file\n\nThe Build pane\nWhen opened, functioning packages have access to the Build pane, which allows us to quickly iterate between loading, installing, and testing. But when projApp is opened, the Build pane is not displayed in the IDE:\n\n\n\n\n(a) projApp IDE\n\nFigure 2: Project IDE panes\n\n\nAs we’ve just learned, the presence of the DESCRIPTION file and an R/ folder are not sufficient to turn your app project into a package (i.e., or an app-package)." }, { "objectID": "packages.html#what-really-makes-an-r-package", "href": "packages.html#what-really-makes-an-r-package", "title": "Packages", "section": "What really makes an R package?", - "text": "What really makes an R package?\nAs we’ve just learned, the presence of the DESCRIPTION file and an R/ folder are not sufficient to turn your app project into a app-package (i.e., with a Build pane and ‘functioning’ devtools workflow).\nFor practical purposes, I’ll rewrite the minimum requirements for creating a ‘functional’ R package:\n\n“A project needs a DESCRIPTION file with specific fields, a directory of R/ files, and a properly configured .Rproj file to be a package.” - Me\n\nLet’s see how each of these requirements work together to create a app-package.\n\nDESCRIPTION fields\nThe official R documentation lists the following required fields for the DESCRIPTION in R packages:\n\nThe ‘Package’, ‘Version’, ‘License’, ‘Description’, ‘Title’, ‘Author’, and ‘Maintainer’ fields are mandatory\n\nBelow is an example DESCRIPTION file for projApp with the mandatory fields:\n\n\n\n\nNew Git Branch\n\nThe code for this section was pushed to the [04_description] branch of the [projApp] repo.\nPackage: projApp\nVersion: 0.0.0.9000\nType: Package\nTitle: movies app\nDescription: A movies data shiny application.\nAuthor: John Smith [aut, cre]\nMaintainer: John Smith <John.Smith@email.io>\nLicense: GPL-3\n*leave empty final line*\n\n\n\n\n\n\nAuthor and Maintainer fields\n\n\n\n\n\nNote that the Author and Maintainer fields require additional information beyond first and last name (i.e., John Smith). These can be created with the utils::person() function:\n\nshow/hide person() output# Author\nutils::person(\n given = \"John\", \n family = \"Smith\", \n role = c(\"aut\", \"cre\"))\n## [1] \"John Smith [aut, cre]\"\n# Maintainer\nutils::person(\n given = \"John\", \n family = \"Smith\", \n email = \"John.Smith@email.io\")\n## [1] \"John Smith <John.Smith@email.io>\"\n\n\n\n\n\nAfter adding the mandatory fields to the DESCRIPTION file in projApp, load_all() runs without the previous error, but the IDE still doesn’t display the Build pane:\n\n\n\n\n(a) projApp IDE\n\nFigure 3: Where is the Build pane?\n\n\nProject options\ni.e., the .Rproj file\n.Rproj files are plain text files with various settings for the IDE. The reason we were able to run devtools::load_all() above without the presence of the Build pane is because the IDE displays the Build pane after reading the fields in projApp.Rproj file.\nThe quickest way to access the fields the .Rproj file is under Tools > Project options.\nDefault project options\nTools > Project options provide access to the project-level options. For example, fields 2 - 4 are available under General, fields 5 - 8 affect the Code options, and the final two fields deal with Sweave.\n\n\n\n(a) projApp.Rproj fields\n\nFigure 4: Field settings from projApp.Rproj file\n\nThese are the default settings that were created when we selected the Shiny App project from the New Project Wizard in the last chapter.\nBuild tools\nIf you’ve read the What makes an RStudio Project? section of R Packages (2 ed), you’ll notice a few differences between each .Rproj file (I’ve placed the files side-by-side so you can compare them below):\n\n\n\n(a) .Rproj files\n\nFigure 5: Comparison of projApp.Rproj file and .Rproj file in R Packages, 2ed\n\nI’ve circled the fields in the R Packages, 2ed .Rproj file that illustrate it’s configured to work with a package. Note that in projApp, the Project build tools are initially set to (None) under Build Tools:\n\n\n\n(a) projApp.Rproj build tools\n\nFigure 6: Build tool settings in projApp.Rproj file\n\nThis setting should help explain the absence of any package development fields in the projApp.Rproj when we compared it to the version in R Packages, 2ed (i.e., BuildType, PackageUseDevtools, PackageInstallArgs, and PackageRoxygenize).\nChanging the Project build tools option to Package will set the default Build Tools options:\n\n\n\n(a) Default package build tools\n\nFigure 7: Default build tool settings\n\nThe links between the Build Tools options and fields in projApp.Rproj are in the figure below:\n\n\n\n(a) projApp.Rproj build tool fields\n\nFigure 8: Default build tool settings in projApp.Rproj file\n\n\nBuildType: Package tells the IDE projApp is a package and triggers the Build pane.\nPackageUseDevtools: Yes links the options in the Build pane to the devtools package.\nThe PackageInstallArgs are complicated, but I’ve included some information about them in the callout block below (and you can read more in the official R documentation)\n\nThe fourth option (PackageRoxygenize) is available under Generate documentation with Roxygen > Use roxygen to generate\n\nThese options affect the documentation in your app-package. To match the example from R Packages, 2ed above, make sure Rd files, Collate field, and NAMESPACE file are selected and click OK\n\n\n\n\n\n\n\n(a) PackageRoxygenize in .Rproj file\n\nFigure 9: roxygen2 build settings\n\nAfter clicking OK, the IDE will automatically reboot, and the additional fields will be added to the bottom of the projApp.Rproj:\n\n\n\n\nNew Git Branch\n\nThe code for this section was pushed to the [05_rproj] branch of the [projApp] repo.\n\nVersion: 1.0\n\nRestoreWorkspace: Default\nSaveWorkspace: Default\nAlwaysSaveHistory: Default\n\nEnableCodeIndexing: Yes\nUseSpacesForTab: Yes\nNumSpacesForTab: 2\nEncoding: UTF-8\n\nRnwWeave: Sweave\nLaTeX: XeLaTeX\n\nBuildType: Package\nPackageUseDevtools: Yes\nPackageInstallArgs: --no-multiarch --with-keep.source\nPackageRoxygenize: rd,collate,namespace\n\n\n\n\n\n\n\nWhat is --no-multiarch --with-keep.source?\n\n\n\n\n\n\n\n--no-multiarch: refers to the option for the package installer to only compile the package for the architecture of the current R session. By default, R tries to compile packages for 32-bit and 64-bit architectures if running in a 64-bit R session on Windows. This flag can help avoid problems if a package can only be compiled on one architecture. Read more here.\n--with-keep.source: In R, when a function is created, its body can be stored in two ways: 1) as a parsed but unevaluated expression and 2) as a character string containing the function’s source code. By default, only the parsed expression is kept. If –with-keep.source is specified, R will also keep the original source code as a character string, which can be helpful for debugging and for tools that analyze or modify source code. Read more here.\n\n\n\n\n\nBuild pane\nWhen the new session starts, the new project-level options will activate the Build pane in the IDE.\n\n\n\n\n(a) Build pane in IDE\n\nFigure 10: Build pane triggered from project-level settings\n\n\nWhen the IDE reboots, I can see the Build pane has been added, and I can check the app-package functionality by loading the code with Build > Load All\n\n\n\n(a) Load the code in the R/ folder\n\nFigure 11: Identical to running devtools::load_all()\n\nI should see the following in the Console:\nℹ Loading projApp\nThere you have it–projApp is a functional app-package!" + "text": "What really makes an R package?\nFor our purposes, I’ll rewrite the minimum requirements for creating a ‘functional’ R package:\n\n“A project needs a DESCRIPTION file with specific fields, a directory of R/ files, and a properly configured .Rproj file to be a package.” - Me\n\nLet’s see how each of these requirements work together to convert the contents of projApp from a project into a package.\n\nDESCRIPTION fields\nThe official R documentation lists the following required fields for the DESCRIPTION in R packages:\n\nThe ‘Package’, ‘Version’, ‘License’, ‘Description’, ‘Title’, ‘Author’, and ‘Maintainer’ fields are mandatory\n\nBelow is an example DESCRIPTION file for projApp with the mandatory fields:\n\n\n\n\nNew Git Branch\n\nThe code for this section was pushed to the [04_description] branch of the [projApp] repo.\nPackage: projApp\nVersion: 0.0.0.9000\nType: Package\nTitle: movies app\nDescription: A movies data shiny application.\nAuthor: John Smith [aut, cre]\nMaintainer: John Smith <John.Smith@email.io>\nLicense: GPL-3\n*leave empty final line*\n\n\n\n\n\n\nAuthor and Maintainer fields\n\n\n\n\n\nNote that the Author and Maintainer fields require additional information beyond first and last name (i.e., John Smith). These can be created with the utils::person() function:\n\nshow/hide person() output# Author\nutils::person(\n given = \"John\", \n family = \"Smith\", \n role = c(\"aut\", \"cre\"))\n## [1] \"John Smith [aut, cre]\"\n# Maintainer\nutils::person(\n given = \"John\", \n family = \"Smith\", \n email = \"John.Smith@email.io\")\n## [1] \"John Smith <John.Smith@email.io>\"\n\n\n\n\n\nAfter adding the mandatory fields to the DESCRIPTION file in projApp, load_all() runs without the previous error, but the IDE still doesn’t display the Build pane:\n\n\n\n\n(a) projApp IDE\n\nFigure 3: Where is the Build pane?\n\n\n\nProject options\ni.e., the .Rproj file\n.Rproj files are plain text files with various settings for the IDE. The reason we were able to run devtools::load_all() above without the presence of the Build pane is because the IDE displays the Build pane after reading the fields in projApp.Rproj file.\nThe quickest way to access the fields the .Rproj file is under Tools > Project options.\nDefault project options\nTools > Project options provide access to the project-level options. For example, fields 2 - 4 are available under General, fields 5 - 8 affect the Code options, and the final two fields deal with Sweave.\n\n\n\n(a) projApp.Rproj fields\n\nFigure 4: Field settings from projApp.Rproj file\n\nThese are the default settings that were created when we selected the Shiny App project from the New Project Wizard in the last chapter.\nBuild tools\nIf you’ve read the What makes an RStudio Project? section of R Packages (2 ed), you’ll notice a few differences between each .Rproj file (I’ve placed the files side-by-side so you can compare them below):\n\n\n\n(a) .Rproj files\n\nFigure 5: Comparison of projApp.Rproj file and .Rproj file in R Packages, 2ed\n\nI’ve circled the fields in the R Packages, 2ed .Rproj file that illustrate it’s configured to work with a package. Note that in projApp, the Project build tools are initially set to (None) under Build Tools:\n\n\n\n(a) projApp.Rproj build tools\n\nFigure 6: Build tool settings in projApp.Rproj file\n\nThis setting should help explain the absence of any package development fields in the projApp.Rproj when we compared it to the version in R Packages, 2ed (i.e., BuildType, PackageUseDevtools, PackageInstallArgs, and PackageRoxygenize).\nChanging the Project build tools option to Package will set the default Build Tools options:\n\n\n\n(a) Default package build tools\n\nFigure 7: Default build tool settings\n\nThe links between the Build Tools options and fields in projApp.Rproj are in the figure below:\n\n\n\n(a) projApp.Rproj build tool fields\n\nFigure 8: Default build tool settings in projApp.Rproj file\n\n\nBuildType: Package tells the IDE projApp is a package and triggers the Build pane.\nPackageUseDevtools: Yes links the options in the Build pane to the devtools package.\nThe PackageInstallArgs are complicated, but I’ve included some information about them in the callout block below (and you can read more in the official R documentation)\n\nThe fourth option (PackageRoxygenize) is available under Generate documentation with Roxygen > Use roxygen to generate\n\nThese options affect the documentation in your package. To match the example from R Packages, 2ed above, make sure Rd files, Collate field, and NAMESPACE file are selected and click OK\n\n\n\n\n\n\n\n(a) PackageRoxygenize in .Rproj file\n\nFigure 9: roxygen2 build settings\n\nAfter clicking OK, the IDE will automatically reboot, and the additional fields will be added to the bottom of the projApp.Rproj:\n\n\n\n\nNew Git Branch\n\nThe code for this section was pushed to the [05_rproj] branch of the [projApp] repo.\n\nVersion: 1.0\n\nRestoreWorkspace: Default\nSaveWorkspace: Default\nAlwaysSaveHistory: Default\n\nEnableCodeIndexing: Yes\nUseSpacesForTab: Yes\nNumSpacesForTab: 2\nEncoding: UTF-8\n\nRnwWeave: Sweave\nLaTeX: XeLaTeX\n\nBuildType: Package\nPackageUseDevtools: Yes\nPackageInstallArgs: --no-multiarch --with-keep.source\nPackageRoxygenize: rd,collate,namespace\n\n\n\n\n\n\n\nWhat is --no-multiarch --with-keep.source?\n\n\n\n\n\n\n\n--no-multiarch: refers to the option for the package installer to only compile the package for the architecture of the current R session. By default, R tries to compile packages for 32-bit and 64-bit architectures if running in a 64-bit R session on Windows. This flag can help avoid problems if a package can only be compiled on one architecture. Read more here.\n--with-keep.source: In R, when a function is created, its body can be stored in two ways: 1) as a parsed but unevaluated expression and 2) as a character string containing the function’s source code. By default, only the parsed expression is kept. If –with-keep.source is specified, R will also keep the original source code as a character string, which can be helpful for debugging and for tools that analyze or modify source code. Read more here.\n\n\n\n\n\nBuild pane\nWhen the new session starts, the new project-level options will activate the Build pane in the IDE.\n\n\n\n\n(a) Build pane in IDE\n\nFigure 10: Build pane triggered from project-level settings\n\n\nWhen the IDE reboots, I can see the Build pane has been added, and I can check the package functionality by loading the code with Build > Load All\n\n\n\n(a) Load the code in the R/ folder\n\nFigure 11: Identical to running devtools::load_all()\n\nI should see the following in the Console:\nℹ Loading projApp\nThere you have it–projApp is a functional package!" }, { - "objectID": "packages.html#an-app-package", - "href": "packages.html#an-app-package", + "objectID": "packages.html#functional-r-packages", + "href": "packages.html#functional-r-packages", "title": "Packages", - "section": "An app-package", - "text": "An app-package\n\n\n\n\n(a) Shiny app-package (with DESCRIPTION and Build pane)\n\nFigure 12: Fully functional shiny app-package\n\n\nThe DESCRIPTION file contains the seven mandatory fields (Package, Version, License, Description, Title, Author, and Maintainer), which make it possible to run the necessary devtools functions.\nThe .Rproj file contains the three package configuration fields (BuildType: Package, PackageUseDevtools: Yes, and PackageInstallArgs: --no-multiarch --with-keep.source), which makes the Build pane accessible and functional." + "section": "Functional R packages\n", + "text": "Functional R packages\n\n\n\n\n\n(a) Shiny app-package (with DESCRIPTION and Build pane)\n\nFigure 12: Fully functional shiny app-package\n\n\nA functional R package:\n\nThe DESCRIPTION file contains the seven mandatory fields (Package, Version, License, Description, Title, Author, and Maintainer), which make it possible to run the necessary devtools functions.\nThe .Rproj file contains the three package configuration fields (BuildType: Package, PackageUseDevtools: Yes, and PackageInstallArgs: --no-multiarch --with-keep.source), which makes the Build pane accessible and functional.\n\nThe items above will create a functional R package, but these are only the first steps (we haven’t developed anything yet!) on our way to an app-package.\nThink of the two items above as two-part process: the DESCRIPTION fields are required by the official R documentation (and hence, devtools), and the .Rproj fields are required by the IDE trigger the Build pane. For more information on the topics in this chapter, you should read Writing R Extensions (the official documentation for creating R packages) and R Packages, 2ed." }, { - "objectID": "packages.html#a-quicker-way-create_package", - "href": "packages.html#a-quicker-way-create_package", + "objectID": "packages.html#creating-packages-with-create_package", + "href": "packages.html#creating-packages-with-create_package", "title": "Packages", - "section": "A quicker way: create_package()\n", - "text": "A quicker way: create_package()\n\nThe Posit documentation lists only two ways to create packages,\n\n\nCall usethis::create_package().\nIn RStudio, do File > New Project > New Directory > R Package. This ultimately calls usethis::create_package(), so really there’s just one way.\n\n\nWhile this information isn’t incorrect, we did just demonstrate it’s possible to create a package without the usethis::create_package() function. Still, if you’re looking for a way to quickly create or convert your app into an app-package, using create_package() is a great option.\nBelow are suggestions for using create_package() to create and convert app-packages.\nNew app-packages\nIf you want to create a new shiny app-package, but haven’t written any code, you can create one with the create_package() function from the usethis package.\nFirst install devtools:\n\ninstall.packages(\"devtools\")\nlibrary(devtools)\n\ndevtools automatically loads usethis\nLoading required package: usethis\nAssuming your current working directory is where you want your new app-package, call usethis::create_package() with the path argument set to getwd()\n\nusethis::create_package(path = getwd())\n\nThis launches a series of actions:\n\n\nFirst, the active project is set to whatever was given to the path argument.\n✔ Setting active project to 'path/to/newApp'\n\n\nThe R/ folder and DESCRIPTION/ file are created:\n✔ Creating 'R/'\n✔ Writing 'DESCRIPTION'\n\n\nThe NAMESPACE and .Rproj files are created:\n✔ Writing 'NAMESPACE'\n✔ Writing 'newApp.Rproj'\n\n\nThe .Rproj is added to the .Rbuildignore file, the .Rproj.user folder is added to the .gitignore and .Rbuildignore files.\n✔ Adding '^newApp\\\\.Rproj$' to '.Rbuildignore'\n✔ Adding '.Rproj.user' to '.gitignore'\n✔ Adding '^\\\\.Rproj\\\\.user$' to '.Rbuildignore'\n\n\nA new session is opened from the new .Rproj file:\n✔ Opening 'path/to/newApp/' in new RStudio session\n\n\nWhen the new session opens, newApp has the following contents:\nnewApp/\n ├── .Rbuildignore\n ├── .Rproj.user/\n ├── .gitignore\n ├── DESCRIPTION\n ├── NAMESPACE\n ├── R/\n └── projApp.Rproj\nYou’re now free to develop newApp. Store any .R files in the R/ folder, edit the DESCRIPTION file with details about the application, and read up on package development in R Packages.\nConverting existing app\nIf you already have a shiny app project that needs to be converted into an app-package (like the app files stored in the 03_projApp branch), you can also use create_package() in your root folder, but I recommend using the following arguments:\n\nDESCRIPTION arguments\n\npath: set this with getwd() to avoid warnings about nested projects.\nfields: these arguments are passed to the fields argument of usethis::use_description() (we’ve covered the importance of the DESCRIPTION file in the next section).\n\nAll fields should be passed in a list() as field = 'value' pairs.\n\nusethis::use_description(\n list(Package = 'projApp',\n Version = '0.0.0.9000',\n Title = 'movies app',\n Description = 'A movie-review shiny application.',\n \"Authors@R\" = NULL,\n Author = utils::person(\n given = \"John\", \n family = \"Smith\", \n email = \"John.Smith@email.io\", \n role = c(\"aut\", \"cre\")),\n Maintainer = utils::person(\n given = \"John\", \n family = \"Smith\",\n email = \"John.Smith@email.io\"),\n License = \"GPL-3\"))\n\n\n\nA few of the fields require specially formatted values from (see the utils::person() examples below).\n\nutils::person(\"John\", \"Smith\", \n email = \"John.Smith@email.io\", \n role = c(\"aut\", \"cre\"))\n\n[1] \"John Smith <John.Smith@email.io> [aut, cre]\"\n\n\n\n\n\n\nTwo additional arguments from use_description() are passed to create_package():\n\ncheck_name: verifies your app-package name is valid for CRAN, so we can set this to FALSE (unless you’re planning on submitting the package to CRAN)\nroxygen2: is set to TRUE by default. This adds the necessary roxygen2 fields which I won’t cover here because we’ll cover documentation in-depth a future chapter.\n\n\n\n\n\n\n\n\n\nWARNING: Don’t use usethis::create_package('.')!\n\n\n\n\n\n\nWhen converting your existing shiny app project into am app-package with usethis::create_package(), don’t use '.' in the path argument:\nusethis::create_package('.')\nThis will return the following warning about creating nested projects, and ask if you want to create the project anyway:\nNew project 'projApp' is nested inside an existing project './', which is \nrarely a good idea. If this is unexpected, the here package has a function,\n`here::dr_here()` that reveals why './' is regarded as a project.\n \nDo you want to create anyway?\n\n1: Yes\n2: No way\n3: Not now\nWe can avoid this warning altogether by passing the getwd() to the path argument, so I recommend cancelling the project creation:\nSelection: 2\nError: Cancelling project creation.\n\n\n\n\nIDE arguments\n\nrstudio: adds the necessary package development fields in the .Rproj file (leave as TRUE)\nopen: set to FALSE (we don’t need RStudio to open in a new session)\n\n\nusethis::create_package(\n path = getwd(),\n fields = list(Package = 'projApp',\n Version = '0.0.0.9000',\n Title = 'movies app',\n Description = 'A movie-review shiny application.',\n \"Authors@R\" = NULL,\n Author = utils::person(\n given = \"John\", \n family = \"Smith\", \n email = \"John.Smith@email.io\", \n role = c(\"aut\", \"cre\")),\n Maintainer = utils::person(\n given = \"John\", \n family = \"Smith\",\n email = \"John.Smith@email.io\"),\n License = \"GPL-3\"),\n roxygen = TRUE,\n check_name = FALSE, \n rstudio = TRUE,\n open = FALSE)\n\nAfter running usethis::create_package() with the arguments above, the IDE will present us with a few prompts to confirm:\nOverwrite pre-existing file 'DESCRIPTION'?\nOverwrite pre-existing file 'projApp.Rproj'?\nThe final package structure is below:\n├── DESCRIPTION\n├── NAMESPACE\n├── R\n│ ├── mod_scatter_display.R\n│ ├── mod_var_input.R\n│ └── utils.R\n├── README.md\n├── app.R\n├── movies.RData\n├── projApp.Rproj\n└── www\n └── shiny.png\n\n3 directories, 10 files\nThe DESCRIPTION file (shown below) has a few additional fields we didn’t include when we converted projApp above (Encoding, Roxygen, and RoxygenNote), but we will cover these in the upcoming chapters.\nPackage: projApp\nTitle: movies app\nVersion: 0.0.0.9000\nAuthor: John Smith <John.Smith@email.io> [aut, cre]\nMaintainer: John Smith <John.Smith@email.io>\nDescription: A movie-review shiny application.\nLicense: GPL-3\nEncoding: UTF-8\nRoxygen: list(markdown = TRUE)\nRoxygenNote: 7.2.3\n\n\n\n\nNew Git Branch\n\nThe code for this section was pushed to the [06_create-package] branch of the [projApp] repo." + "section": "Creating packages with create_package()\n", + "text": "Creating packages with create_package()\n\nThe Posit documentation lists the following ways to create R packages,\n\n\nCall usethis::create_package().\nIn RStudio, do File > New Project > New Directory > R Package. This ultimately calls usethis::create_package(), so really there’s just one way.\n\n\nWhile this information isn’t incorrect, we did just demonstrate it’s possible to create a package without the usethis::create_package() function. Still, if you’re looking for a way to quickly create or convert your app project into an app-package, using create_package() is a great option.\nThis function is covered in the ‘Fundamental development workflows’ chapter of R Packages, 2ed. I’ve provided some suggestions below for using create_package():\nNew app-packages\nIf you want to create a new shiny app-package, but haven’t written any code, you can create it with the create_package() function from the usethis package.\nFirst install devtools:\n\ninstall.packages(\"devtools\")\nlibrary(devtools)\n\ndevtools automatically loads usethis\nLoading required package: usethis\nAssuming your current working directory is where you want your new app-package, call usethis::create_package() with the path argument set to getwd()\n\nusethis::create_package(path = getwd())\n\nThis launches a series of actions:\n\n\nFirst, the active project is set to whatever was given to the path argument.\n✔ Setting active project to 'path/to/newApp'\n\n\nThe R/ folder and DESCRIPTION/ file are created:\n✔ Creating 'R/'\n✔ Writing 'DESCRIPTION'\n\n\nThe NAMESPACE and .Rproj files are created:\n✔ Writing 'NAMESPACE'\n✔ Writing 'newApp.Rproj'\n\n\nThe .Rproj is added to the .Rbuildignore file, the .Rproj.user folder is added to the .gitignore and .Rbuildignore files.\n✔ Adding '^newApp\\\\.Rproj$' to '.Rbuildignore'\n✔ Adding '.Rproj.user' to '.gitignore'\n✔ Adding '^\\\\.Rproj\\\\.user$' to '.Rbuildignore'\n\n\nA new session is opened from the new .Rproj file:\n✔ Opening 'path/to/newApp/' in new RStudio session\n\n\nWhen the new session opens, newApp has the following contents:\nnewApp/\n ├── .Rbuildignore\n ├── .Rproj.user/\n ├── .gitignore\n ├── DESCRIPTION\n ├── NAMESPACE\n ├── R/\n └── projApp.Rproj\nYou’re now free to develop newApp. Store and document any .R files in the R/ folder, edit the DESCRIPTION file with details about the application, read through R Packages and Mastering Shiny, and add the shiny code to complete your app-package\nConverting existing app\nIf you already have a shiny app project that needs to be converted into an app-package (like the app files stored in the 03_projApp branch), you can also use create_package(path = getwd()) in your root folder, but I recommend using the following arguments:\n\nDESCRIPTION arguments\n\n\nfields: these are arguments passed to usethis::use_description(). If fields is empty, a boilerplate DESCRIPTION file is created (similar to this one).\n\nThese boilerplate fields in the DESCRIPTION work, but I’ve found some of the fields are unnecessary (i.e., I’ve never needed Authors@R: or ORCID) and inevitably require revision, so I’d prefer to handle this during the creation process (and remove the risk of forgetting to change it later).\nAll fields should be passed in a list() as field = 'value' pairs.\n\n\nusethis::use_description(\n list(Package = 'projApp',\n Version = '0.0.0.9000',\n Title = 'movies app',\n Description = 'A movie-review shiny application.',\n \"Authors@R\" = NULL,\n Author = utils::person(\n given = \"John\", \n family = \"Smith\", \n email = \"John.Smith@email.io\", \n role = c(\"aut\", \"cre\")),\n Maintainer = utils::person(\n given = \"John\", \n family = \"Smith\",\n email = \"John.Smith@email.io\"),\n License = \"GPL-3\"))\n\n\n\nA few of the fields require specially formatted values from (see the utils::person() examples below).\n\nutils::person(\"John\", \"Smith\", \n email = \"John.Smith@email.io\", \n role = c(\"aut\", \"cre\"))\n\n[1] \"John Smith <John.Smith@email.io> [aut, cre]\"\n\n\n\n\n\n\nTwo additional arguments from use_description() are passed to create_package():\n\ncheck_name: verifies your app-package name is valid for CRAN, so we can set this to FALSE (unless you’re planning on submitting to CRAN)\nroxygen2: is TRUE by default and adds the fields required to use roxygen2 (which I won’t cover here because we’ll cover documentation in-depth a future chapter).\n\n\n\n\n\n\n\n\n\nWARNING: Don’t use usethis::create_package('.')!\n\n\n\n\n\n\nWhen converting your existing shiny app project into am app-package with usethis::create_package(), don’t use '.' in the path argument:\nusethis::create_package('.')\nThis will return the following warning about creating nested projects, and ask if you want to proceed anyway:\nNew project 'projApp' is nested inside an existing project\n'./', which is rarely a good idea. If this is unexpected, \nthe here package has a function,\n`here::dr_here()` that reveals why './' is regarded as a\nproject.\n \nDo you want to create anyway?\n\n1: Yes\n2: No way\n3: Not now\nWe can avoid this warning altogether by passing the getwd() to the path argument, so I recommend cancelling the project creation:\nSelection: 2\nError: Cancelling project creation.\n\n\n\n\nIDE arguments\n\nrstudio: adds the necessary Build Tools fields in the .Rproj file (leave as TRUE)\nopen: can be set to FALSE because we don’t need RStudio/Posit Workbench to open in a new session\n\n\nusethis::create_package(\n path = getwd(),\n fields = list(Package = 'projApp',\n Version = '0.0.0.9000',\n Title = 'movies app',\n Description = 'A movie-review shiny application.',\n \"Authors@R\" = NULL,\n Author = utils::person(\n given = \"John\", \n family = \"Smith\", \n email = \"John.Smith@email.io\", \n role = c(\"aut\", \"cre\")),\n Maintainer = utils::person(\n given = \"John\", \n family = \"Smith\",\n email = \"John.Smith@email.io\"),\n License = \"GPL-3\"),\n roxygen = TRUE,\n check_name = FALSE, \n rstudio = TRUE,\n open = FALSE)\n\nAfter running usethis::create_package() with the arguments above, the IDE will present us with a few prompts to confirm:\nOverwrite pre-existing file 'DESCRIPTION'?\nOverwrite pre-existing file 'projApp.Rproj'?\nThe app-package structure is below:\n├── DESCRIPTION\n├── NAMESPACE\n├── R\n│ ├── mod_scatter_display.R\n│ ├── mod_var_input.R\n│ └── utils.R\n├── README.md\n├── app.R\n├── movies.RData\n├── projApp.Rproj\n└── www\n └── shiny.png\n\n3 directories, 10 files\nThe DESCRIPTION file (shown below) has a few additional fields (Encoding, Roxygen, and RoxygenNote) we didn’t include when we converted projApp above, but we will cover these in the upcoming chapters.\nPackage: projApp\nTitle: movies app\nVersion: 0.0.0.9000\nAuthor: John Smith <John.Smith@email.io> [aut, cre]\nMaintainer: John Smith <John.Smith@email.io>\nDescription: A movie-review shiny application.\nLicense: GPL-3\nEncoding: UTF-8\nRoxygen: list(markdown = TRUE)\nRoxygenNote: 7.2.3\n\n\n\n\nNew Git Branch\n\nThe code for this section was pushed to the [06_create-package] branch of the [projApp] repo." }, { "objectID": "packages.html#recap", "href": "packages.html#recap", "title": "Packages", "section": "Recap", - "text": "Recap\nThis chapter has covered the importance of the DESCRIPTION file in R packages, project and package fields in .Rproj files, and the usethis::create_package() function.\n\n\n\n\n\n\nRecap: Packages!\n\n\n\n\n\n\n\n\nThe DESCRIPTION file ultimately controls whether you have a functional shiny app-package\n\nThe mandatory fields for an R package are: Package, Version, License, Description, Title, Author, and Maintainer\n\n\n\nusethis::create_package() can be used to create a new shiny app-package and to convert an existing shiny project into a shiny app-package\n\nThe IDE reads RStudio project (.Rproj) files and determines R session settings at the project-level (i.e., working directory, workspace, history, code formatting, etc.)\n\nPackage development settings can be accessed via Project Options > Build Tools\n\n\n\n\n\n\n\n\nIn the next chapter, I’ll cover how you can Install, Document, Test, and Check your package with a single click!\nend packages.qmd" + "text": "Recap\nThis chapter has covered the mandatory fields in the DESCRIPTION file and the package configuration fields in .Rproj. We also covered creating and converting projects using the usethis::create_package() function.\n\n\n\n\n\n\nRecap: Packages!\n\n\n\n\n\n\n\n\nThe DESCRIPTION file ultimately controls whether you have a functional shiny package\n\nThe mandatory fields are: Package, Version, License, Description, Title, Author, and Maintainer\n\n\n\nusethis::create_package() can be used to create a new package and to convert an existing shiny project into a shiny app-package\n\nThe IDE reads RStudio (.Rproj) files and determines R session settings at the project-level (i.e., working directory, workspace, history, code formatting, etc.)\n\n\nPackage development settings can be accessed via Project Options > Build Tools\n\n\n\n\n\n\n\n\nIn the next chapter, I’ll cover how you can Install, Document, Test, and Check your package with a single click!\nend packages.qmd" }, { "objectID": "devtools.html#developing-with-devtools", @@ -256,7 +263,7 @@ "href": "dependencies.html#roxygen2-imports", "title": "Dependencies", "section": "\nroxygen2 imports", - "text": "roxygen2 imports\nNow that we’ve covered which functions we want accessible to users of pkgApp, we need to address the functions pkgApp imports. If we continue using scatter_plot() as an example, we see it contains functions from two add-on packages (ggplot2 and rlang):\n\nshow/hide scatter_plot()scatter_plot <- function(df, x_var, y_var, col_var, alpha_var, size_var) {\n ggplot2::ggplot(data = df,\n ggplot2::aes(x = .data[[x_var]],\n y = .data[[y_var]],\n color = .data[[col_var]])) +\n ggplot2::geom_point(alpha = alpha_var, size = size_var)\n\n}\n\n\nTwo components help to ensure both ggplot2 and rlang functions become part of pkgApp:\n\nCall add-on package functions using the package::function() syntax, and\nInclude them in the NAMESPACE by using either @importFrom or @import\n\nExplicit namespacing let’s users know which functions are from your package and which functions are from external packages. Listing external functions and packages ensures their loaded when users install and load your package.\n\n@importFrom or @import\n\nI’ve included both tags below, but you should use @importFrom far more than @import (with the exception being @import shiny). Read more here:\n\n\n@importFrom: import a function from an add-on package into your package NAMESPACE.\n\n#' @importFrom package function\n\n\n\n@import: import all functions from an add-on package into your NAMESPACE.\n\n#' @import package\n\n\n\nBelow is an example of @importFrom in R/scatter_plot.R:\n\n#' \n#' @importFrom ggplot2 ggplot aes geom_point\n#' @importFrom rlang .data\n#' \n\nTo record these changes in the NAMESPACE, I’ll load() and document():\n\nCtrl/Cmd + Shift + L\n\nℹ Loading pkgApp\n\nCtrl/Cmd + Shift + D\n\nNote that the contents of man/utils.Rd have now been written to man/scatter_plot.Rd:\n==> devtools::document(roclets = c('rd', 'collate', 'namespace'))\n\nℹ Updating pkgApp documentation\nℹ Loading pkgApp\nWriting NAMESPACE\nWriting scatter_plot.Rd\nWriting NAMESPACE\nDocumentation completed\nYou may have noticed the document() resulted in the NAMESPACE being written twice (one for imports, one for exports).\n\nCtrl/Cmd + Shift + B\n\nAfter installing pkgApp, I can use pkgApp:: in the Console and see the scatter_plot() function help file in the tab completion:\n\n\n\n(a) pkgApp::scatter_plot()\n\nFigure 2: Tab completion for scatter_plot()\n\nExporting scatter_plot() also means users can run the examples in the scatter_plot() help file,\n\n\n\n(a) scatter_plot() examples\n\nFigure 3: Running examples in ?scatter_plot" + "text": "roxygen2 imports\nNow that we’ve covered which functions we want accessible to users of pkgApp, we need to address the functions pkgApp imports. If we continue using scatter_plot() as an example, we see it contains functions from two add-on packages (ggplot2 and rlang):\n\nshow/hide scatter_plot()scatter_plot <- function(df, x_var, y_var, col_var, alpha_var, size_var) {\n ggplot2::ggplot(data = df,\n ggplot2::aes(x = .data[[x_var]],\n y = .data[[y_var]],\n color = .data[[col_var]])) +\n ggplot2::geom_point(alpha = alpha_var, size = size_var)\n\n}\n\n\nTwo components help to ensure both ggplot2 and rlang functions become part of pkgApp:\n\nCall add-on package functions using the package::function() syntax, and\nInclude them in the NAMESPACE by using either @importFrom or @import\n\nExplicit namespacing let’s users know which functions are from your package and which functions are from external packages.\n\n\n\n\n\n\nWhy use the package::function() syntax?\n\n\n\n\n\n\nThink of add-on packages and their functions as families you can invite to a party. @import will invite the entire family, and @importFrom will only invite specific family members (this is helpful if it’s a huge family and you don’t have unlimited food and drinks).\nWhen you’re at the party, it’s clearer to refer to the guests by their first and last name, i.e., ‘This is my friend, Beth Johnson, she’s an amazing breakdancer!’ which avoids confusing them with another guest (maybe ‘Beth Smith,’ who has two left feet).\nThis metaphor should help explain why we use the package::function() syntax when referring to functions from add-on packages: our package can quickly become a massive party with many guests, and we don’t want anyone confused about who’s capable of performing what.\n\n\n\n\nImporting external functions and packages ensures they’re loaded when users install and load your package.\n\n@importFrom or @import\n\nI’ve included both tags below, but you should use @importFrom far more than @import (with the exception being @import shiny). Read more here:\n\n\n@importFrom: import a function from an add-on package into your package NAMESPACE.\n\n#' @importFrom package function\n\n\n\n@import: import all functions from an add-on package into your NAMESPACE.\n\n#' @import package\n\n\n\nBelow is an example of @importFrom in R/scatter_plot.R:\n\n#' \n#' @importFrom ggplot2 ggplot aes geom_point\n#' @importFrom rlang .data\n#' \n\nTo record these changes in the NAMESPACE, I’ll load() and document():\n\nCtrl/Cmd + Shift + L\n\nℹ Loading pkgApp\n\nCtrl/Cmd + Shift + D\n\nNote that the contents of man/utils.Rd have now been written to man/scatter_plot.Rd:\n==> devtools::document(roclets = c('rd', 'collate', 'namespace'))\n\nℹ Updating pkgApp documentation\nℹ Loading pkgApp\nWriting NAMESPACE\nWriting scatter_plot.Rd\nWriting NAMESPACE\nDocumentation completed\nYou may have noticed the document() resulted in the NAMESPACE being written twice (one for imports, one for exports).\n\nCtrl/Cmd + Shift + B\n\nAfter installing pkgApp, I can use pkgApp:: in the Console and see the scatter_plot() function help file in the tab completion:\n\n\n\n(a) pkgApp::scatter_plot()\n\nFigure 2: Tab completion for scatter_plot()\n\nExporting scatter_plot() also means users can run the examples in the scatter_plot() help file,\n\n\n\n(a) scatter_plot() examples\n\nFigure 3: Running examples in ?scatter_plot" }, { "objectID": "dependencies.html#namespace-directives", @@ -277,14 +284,14 @@ "href": "dependencies.html#namespace-in-pkgapp", "title": "Dependencies", "section": "\nNAMESPACE in pkgApp\n", - "text": "NAMESPACE in pkgApp\n\nI’ve added @export to each function in pkgApp and used explicit namespacing (::) with @importFrom to import the functions from the add-on packages.\n\n\nR/movies_app.R\n\nshow/hide R/movies_app.R roxygen2# movies_app() is the standalone app function, so we'll export \n# this function and @import shiny here:\n# ==============================================================\n#'\n#' @export\n#' \n#' @import shiny\n#'\n\n\n\n\nR/movies_ui.R\n\nshow/hide R/movies_ui.R & R/movies_server.R roxygen2# movies_ui() and movies_server() both use only shiny functions, \n# so they don't need any @import or @importFrom tags (but both \n# are exported):\n# ==============================================================\n#'\n#' @export\n#' \n#'\n\n\n\n\nR/mod_var_input.R\n\nshow/hide R/mod_var_input.R roxygen2# mod_var_input_ui() and mod_var_input_server() only use shiny \n# functions, so no need for @import or @importFrom tags (however,\n# this module is exported):\n# \n# ==============================================================\n#'\n#' @export\n#' \n#'\n\n\n\n\nR/mod_scatter_display.R\n\nshow/hide R/mod_scatter_display.R roxygen2# mod_scatter_display_ui() also only contains shiny functions \n# (no @import or @importFrom) so this function only gets an \n# @export tag:\n# ==============================================================\n#' \n#' @export\n#' \n# mod_scatter_display_server() uses functions from tools, \n# ggplot2 and stringr (all are imported with @importFrom tags). \n# This function is also exported:\n# ==============================================================\n#' @export\n#' \n#' @importFrom tools toTitleCase\n#' @importFrom stringr str_replace_all\n#' @importFrom ggplot2 labs theme_minimal theme\n#' \n\n\n\n\nThe updated DESCRIPTION file is below:\nPackage: pkgApp\nVersion: 0.0.0.9000\nType: Package\nTitle: movies app\nDescription: A movies data shiny application.\nAuthor: John Smith [aut, cre]\nMaintainer: John Smith <John.Smith@email.io>\nLicense: GPL-3\nDisplayMode: Showcase\nRoxygenNote: 7.2.3\nEncoding: UTF-8\nRoxygen: list(markdown = TRUE)\nImports:\n shiny,\n ggplot2,\n rlang,\n stringr,\n tools\n<remember to keep an empty final line>\nCtrl/Cmd + Shift + L, D, B\nAfter pkgApp installs, I’ll check the namespace by using pkgApp:: in the Console:\n\n\n\n(a) pkgApp:: namespace\n\nFigure 6: Exported functions from pkgApp" + "text": "NAMESPACE in pkgApp\n\nI’ve added @export to each function in pkgApp and used explicit namespacing (::) with @importFrom to import the functions from the add-on packages.\n\n\nR/movies_app.R\n\n\nmovies_app() is the standalone app function, so we’ll export this function and @import shiny here\n\n\nshow/hide R/movies_app.R roxygen2#'\n#' @export\n#' \n#' @import shiny\n#'\n\n\n\n\nR/movies_ui.R & R/movies_server.R\n\n\nmovies_ui() and movies_server() both use only shiny functions, so they don’t need an @import or @importFrom tags (but both are exported):\n\n\nshow/hide R/movies_ui.R & R/movies_server.R roxygen2#'\n#' @export\n#' \n\n\n\n\nR/mod_var_input.R\n\n\nmod_var_input_ui() and mod_var_input_server() only use shiny functions, so no need for @import or @importFrom tags (however, we’ll export both module functions).\n\n\nshow/hide R/mod_var_input.R roxygen2#'\n#' @export\n#' \n\n\n\n\nR/mod_scatter_display.R\n\n\nmod_scatter_display_ui() also only contains shiny functions (no @import or @importFrom) so this function only gets an @export tag\n\n\nshow/hide R/mod_scatter_display.R roxygen2#' \n#' @export\n#' \n\n\n\n\nmod_scatter_display_server() uses functions from tools, ggplot2 and stringr (all are imported with @importFrom tags). This function is also exported:\n\n\nshow/hide R/mod_scatter_display.R roxygen2#' \n#' @export\n#' \n#' @importFrom tools toTitleCase\n#' @importFrom stringr str_replace_all\n#' @importFrom ggplot2 labs theme_minimal theme\n#' \n\n\n\n\nThe updated DESCRIPTION file is below:\nPackage: pkgApp\nVersion: 0.0.0.9000\nType: Package\nTitle: movies app\nDescription: A movies data shiny application.\nAuthor: John Smith [aut, cre]\nMaintainer: John Smith <John.Smith@email.io>\nLicense: GPL-3\nDisplayMode: Showcase\nRoxygenNote: 7.2.3\nEncoding: UTF-8\nRoxygen: list(markdown = TRUE)\nImports:\n shiny,\n ggplot2,\n rlang,\n stringr,\n tools\n<remember to keep an empty final line>\nCtrl/Cmd + Shift + L / D / B\nAfter pkgApp installs, I’ll check the namespace by using pkgApp:: in the Console:\n\n\n\n(a) pkgApp:: namespace\n\nFigure 6: Exported functions from pkgApp" }, { "objectID": "dependencies.html#app.r", "href": "dependencies.html#app.r", "title": "Dependencies", "section": "app.R", - "text": "app.R\nAs a final step, we want to remove any calls to library() in pkgApp (these packages are now handled in the NAMESPACE and DESCRIPTION files).\napp.R should now contain a single call to library(pkgApp) and then the movies_app() function:\n\n# pkgs <- c(\"shiny\", \"shinythemes\", \"stringr\", \"ggplot2\", \"rlang\")\n# install.packages(pkgs, quiet = TRUE)\n\n# load pacakge ----------------------------------\nlibrary(pkgApp)\n\n# movies_app ------------------------------------\nmovies_app()\n\n\n\n\n(a) pkgApp::movies_app()\n\nFigure 7: Exported functions from pkgApp\n\n\n\n\n\nNew Git Branch\n\nThe code for the next section is in the [03_dependencies] branch of the [pkgApp] repo." + "text": "app.R\nAs a final step, we want to remove any calls to library() in pkgApp (these packages are now handled in the NAMESPACE and DESCRIPTION files).\napp.R should now contain a single call to library(pkgApp) and then the movies_app() function:\n\n# pkgs <- c(\"shiny\", \"shinythemes\", \"stringr\", \"ggplot2\", \"rlang\")\n# install.packages(pkgs, quiet = TRUE)\n\n# load pacakge ----------------------------------\nlibrary(pkgApp)\n\n# movies_app ------------------------------------\nmovies_app()\n\n\n\n\n(a) pkgApp::movies_app()\n\nFigure 7: Exported functions from pkgApp\n\n\n\n\n\nNew Git Branch\n\nThe code for this section is in the [03_dependencies] branch of the [pkgApp] repo." }, { "objectID": "dependencies.html#recap", @@ -298,21 +305,21 @@ "href": "data.html#app-package-data", "title": "Data files", "section": "App-package data", - "text": "App-package data\nThere are three folders used to store data in R packages: data/, data-raw/, and inst/extdata/. The folder you’ll use will depend on the format, accessibility, and intended purpose of the data file in your app-package.\n\n\n\n\n\n\nGitHub [pkgApp]:[04-data]\n\n\n\n\n\n\nThis shiny app project can be found in the 04-data branch of the pkgApp repo.\nThis branch was created from the 03-dependencies branch using:\ngit checkout -B 04-data\ngit push -u origin 04-data" + "text": "App-package data\nThere are three folders used to store data in R packages: data/, data-raw/, and inst/extdata/. The folder you’ll use will depend on the format, accessibility, and intended purpose of the data file in your app-package." }, { - "objectID": "data.html#data", - "href": "data.html#data", + "objectID": "data.html#the-data-folder", + "href": "data.html#the-data-folder", "title": "Data files", - "section": "data/", - "text": "data/\nYou can read more about the data folder in the ‘Data in packages’ section of Writing R Extenstions and the ‘Data’ chapter of R Packages, 2ed.\nThe primary location for app-package data is the data/ folder. Data objects in the data/ folder are available in the package namespace when it’s installed and loaded, and can be accessed with the package::data syntax. See the example below:\n\nlibrary(dplyr)\nstr(dplyr::storms)\n\ntibble [19,066 × 13] (S3: tbl_df/tbl/data.frame)\n $ name : chr [1:19066] \"Amy\" \"Amy\" \"Amy\" \"Amy\" ...\n $ year : num [1:19066] 1975 1975 1975 1975 1975 ...\n $ month : num [1:19066] 6 6 6 6 6 6 6 6 6 6 ...\n $ day : int [1:19066] 27 27 27 27 28 28 28 28 29 29 ...\n $ hour : num [1:19066] 0 6 12 18 0 6 12 18 0 6 ...\n $ lat : num [1:19066] 27.5 28.5 29.5 30.5 31.5 32.4 33.3 34 34.4 34 ...\n $ long : num [1:19066] -79 -79 -79 -79 -78.8 -78.7 -78 -77 -75.8 -74.8 ...\n $ status : Factor w/ 9 levels \"disturbance\",..: 7 7 7 7 7 7 7 7 8 8 ...\n $ category : num [1:19066] NA NA NA NA NA NA NA NA NA NA ...\n $ wind : int [1:19066] 25 25 25 25 25 25 25 30 35 40 ...\n $ pressure : int [1:19066] 1013 1013 1013 1013 1012 1012 1011 1006 1004 1002 ...\n $ tropicalstorm_force_diameter: int [1:19066] NA NA NA NA NA NA NA NA NA NA ...\n $ hurricane_force_diameter : int [1:19066] NA NA NA NA NA NA NA NA NA NA ...\n\n\nLazyData: true\nFiles in data/ should be in the .rda or .RData format. Data files become part of a package when they’re added to the data/ folder and LazyData: true is added to the DESCRIPTION file.\n\nLazyData: true: the data is only loaded into memory if it is explicitly accessed by the user or a function in the package. Until then, only the dataset names is loaded. This practice user-friendly and is the default for most R packages.\nLazyData: false (or omitted): accessing a data file from the package requires explicitly loading it using the data() function.\n\nBelow are the steps for adding movies to pkgApp:\n\n\nMove the movies.RData file into a newly created the data/ folder:\npkgApp/\n │\n └──data/\n └── movies.RData\n\n\nInclude LazyData: true in the DESCRIPTION file (I’ve added it above Imports:):\nPackage: pkgApp\nVersion: 0.0.0.9000\nType: Package\nTitle: movies app\nDescription: A movies data shiny application.\nAuthor: John Smith [aut, cre]\nMaintainer: John Smith <John.Smith@email.io>\nLicense: GPL-3\nRoxygenNote: 7.2.3\nEncoding: UTF-8\nRoxygen: list(markdown = TRUE)\nLazyData: true\nImports:\n shiny,\n ggplot2,\n rlang,\n stringr,\n tools\n\nLoad, document, and install.\n\nCtrl/Cmd + Shift + L\nℹ Loading pkgApp\nCtrl/Cmd + Shift + D\n==> devtools::document(roclets = c('rd', 'collate', 'namespace'))\n\nℹ Updating pkgApp documentation\nℹ Loading pkgApp\nDocumentation completed\nCtrl/Cmd + Shift + B\nIn the Build pane, you’ll notice a few new ** data lines of output after adding data:\n** data\n*** moving datasets to lazyload DB\n** byte-compile and prepare package for lazy loading\nWe can check to see if movies has been included in pkgApp using the package::data syntax:\n\n\n\n(a) roxygen2\n\nFigure 1: movies is now part of pkgApp\n\nIf you’d prefer to store data using the .rda format, the usethis package has the use_data() function that will automatically store an object in data/ (this function will also add LazyData: true to the DESCRIPTION)\n\n\n\n\n\n\nMastering Shiny data example\n\n\n\n\n\n\nIf you happened to download, install and load the monthApp example from Mastering Shiny, you may have noticed the NAMESPACE was empty, but the data was exported from the package:\n\n\nmonthApp exports\n\nData files in data/ don’t require roxygen2 tags to be included in a package namespace.\n\n\n\n\nDocumenting data/\n\nDocumenting data can be tedious, but it’s worth the effort if you’ll be sharing your application with collaborators. There are multiple ways to store the documentation for datasets. For example, we could create a data.R file in the R/ folder.\n\nfs::file_create(\"R/data.R\")\n\nIn data.R, we provide a @title, @description, and @details for the data (with or without the tags), followed by @format:\n\n#' @title IMDB movies data \n#'\n#' @description\n#' Movie review data. Note: these data come from the [Building Web Applications with shiny course](https://rstudio-education.github.io/shiny-course/). \n#' \n#' @details\n#' Read more about acquiring these data in the ['Web Scraping and programming' section of Data science in a box](https://datasciencebox.org/02-exploring-data#web-scraping-and-programming) \n#'\n#' @format\n\n@format\nThe text following @format is a one-sentence description of the data (with it’s dimensions).\n\n#' @title IMDB movies data \n#'\n#' @description\n#' Movie review data. Note: these data come from the [Building Web Applications with shiny course](https://rstudio-education.github.io/shiny-course/). \n#' \n#' @details\n#' Read more about acquiring these data in the ['Web Scraping and programming' section of Data science in a box](https://datasciencebox.org/02-exploring-data#web-scraping-and-programming) \n#' \n#' @format A data frame with [] rows and [] variables:\n\n\\describe & \\item\nEach variable (column) in the data is documented with a combination of \\describe and \\item (pay close attention to the curly brackets):\n\n#' \\describe{\n#' \\item{variable}{description}\n#' }\n\nAfter closing the curly brackets in \\describe, place the name of the data in quotes (\"movies\") on the following line.\nBelow is the documentation for the first five columns in the movies dataset:\n\n#' @title IMDB movies data \n#'\n#' @description\n#' Movie review data. Note: these data come from the [Building Web Applications with shiny course](https://rstudio-education.github.io/shiny-course/). \n#' \n#' @details\n#' Read more about acquiring these data in the ['Web Scraping and programming' section of Data science in a box](https://datasciencebox.org/02-exploring-data#web-scraping-and-programming) \n#'\n#' @format A data frame with 651 rows and 34 variables:\n#' \\describe{\n#' \\item{title}{movie title}\n#' \\item{title_type}{type, fct (Documentary, Feature Film, TV Movie)}\n#' \\item{genre}{movie genre, fct (Action & Adventure, Animation, etc.}\n#' \\item{runtime}{movie length in minutes, num, avg = 106, sd = 19.4}\n#' \\item{mpaa_rating}{movie rating, fct (G, NC-17, PG, PG-13, R, Unrated)}\n#' }\n#'\n\"movies\"\n\nIf we load and document pkgApp, we can see a preview of the help file:\nCtrl/Cmd + Shift + L\nℹ Loading pkgApp\nCtrl/Cmd + Shift + D\n==> devtools::document(roclets = c('rd', 'collate', 'namespace'))\n\nℹ Updating pkgApp documentation\nℹ Loading pkgApp\nWriting movies.Rd\nDocumentation completed\n\n?movies\n\n\n\n\n(a) The movies help file\n\nFigure 2: Documentation for the movies dataset\n\nI’ve provided documentation for the full movies dataset below.\n\nshow/hide full movies data documenation#' @title IMDB movies data \n#'\n#' @description\n#' Movie review data. Note: these data come from the [Building Web Applications with shiny course](https://rstudio-education.github.io/shiny-course/). \n#' \n#' @details\n#' Read more about acquiring these data in the ['Web Scraping and programming' section of Data science in a box](https://datasciencebox.org/02-exploring-data#web-scraping-and-programming) \n#'\n#' @format A data frame with 651 rows and 34 variables:\n#' \\describe{\n#' \\item{title}{movie title}\n#' \\item{title_type}{type, fct (Documentary, Feature Film, TV Movie)}\n#' \\item{genre}{movie genre, fct (Action & Adventure, Animation, etc.}\n#' \\item{runtime}{movie length in minutes, num, avg = 106, sd = 19.4}\n#' \\item{mpaa_rating}{movie rating, fct (G, NC-17, PG, PG-13, R, Unrated)}\n#' \\item{studio}{name of studio, chr}\n#' \\item{thtr_rel_date}{Theatre release date, POSIXct, min = 1970-05-19 21:00:00, max = 2014-12-24 21:00:00}\n#' \\item{thtr_rel_year}{Theatre release year, num, min = 1970, max = 2014}\n#' \\item{thtr_rel_month}{Theatre release month, num, min = 1, max =12}\n#' \\item{thtr_rel_day}{Theatre release day, num, min = 1, max =31}\n#' \\item{dvd_rel_date}{DVD release date, POSIXct, min = 1991-03-27 21:00:00, max = 2015-03-02 21:00:00}\n#' \\item{dvd_rel_year}{DVD release year, num, min = 1991, max = 2015}\n#' \\item{dvd_rel_month}{DVD release month, num, min = 1, max = 12}\n#' \\item{dvd_rel_day}{DVD release day, num, min = 1, max = 31}\n#' \\item{imdb_rating}{Internet movie database rating, avg = 6.49, sd = 1.08}\n#' \\item{imdb_num_votes}{Internet movie database votes, avg = 57533, sd = 112124}\n#' \\item{critics_rating}{Rotten tomatoes rating, fct (Certified Fresh, Fresh, Rotten)}\n#' \\item{critics_score}{Rotten tomatoes score, avg = 57.7, sd = 28.4}\n#' \\item{audience_rating}{Audience rating, fct (Spilled, Upright)}\n#' \\item{audience_score}{Audience score, avg = 62.4, sd = 20.2}\n#' \\item{best_pic_nom}{Best picture nomination, fct (no, yes)}\n#' \\item{best_pic_win}{Best picture win, fct (no, yes)}\n#' \\item{best_actor_win}{Best actor win, fct (no, yes)}\n#' \\item{best_actress_win}{Best actress win, fct (no, yes)}\n#' \\item{best_dir_win}{Best director win, fct (no, yes)}\n#' \\item{top200_box}{Top 20 box-office, fct (no, yes)}\n#' \\item{director}{Name of director, chr}\n#' \\item{actor1}{Name of leading actor, chr}\n#' \\item{actor2}{Name of supporting actor, chr}\n#' \\item{actor3}{Name of #3 actor, chr}\n#' \\item{actor4}{Name of #4 actor, chr}\n#' \\item{actor5}{Name of #5 actor, chr}\n#' \\item{imdb_url}{IMDB URL}\n#' \\item{rt_url}{Rotten tomatoes URL}\n#' }\n#'\n\"movies\"\n\n\nRemove load()\n\nAfter documenting the movies data in data.R, we’ll remove the call to load() in the mod_scatter_display_server() function and replace it with a direct call to the dataset:\n\nmod_scatter_display_server <- function(id, var_inputs) {\n shiny::moduleServer(id, function(input, output, session) {\n\n inputs <- shiny::reactive({\n plot_title <- tools::toTitleCase(var_inputs$plot_title())\n list(\n x = var_inputs$x(),\n y = var_inputs$y(),\n z = var_inputs$z(),\n alpha = var_inputs$alpha(),\n size = var_inputs$size(),\n plot_title = plot_title\n )\n })\n output$scatterplot <- shiny::renderPlot({\n plot <- scatter_plot(\n # data -----------------------------------------------------\n df = movies,\n x_var = inputs()$x,\n y_var = inputs()$y,\n col_var = inputs()$z,\n alpha_var = inputs()$alpha,\n size_var = inputs()$size\n )\n plot +\n ggplot2::labs(\n title = inputs()$plot_title,\n x = stringr::str_replace_all(tools::toTitleCase(inputs()$x), \"_\", \" \"),\n y = stringr::str_replace_all(tools::toTitleCase(inputs()$y), \"_\", \" \")\n ) +\n ggplot2::theme_minimal() +\n ggplot2::theme(legend.position = \"bottom\")\n })\n })\n}\n\nAfter loading, documenting, and installing the package, we see the following application:\n\n\n\n\n(a) movies_app() with movies data file\n\nFigure 3: pkgApp::movies in movies_app()\n\n\n\ndplyr example\nTo illustrate other options for data documentation, we’ll use the dplyr package. dplyr stores its data in the data/ folder:\ndata/\n├── band_instruments.rda\n├── band_instruments2.rda\n├── band_members.rda\n├── starwars.rda\n└── storms.rda\nThe documentation for the datasets in dplyr are stored in R/ using a data- prefix:\nR/\n├── data-bands.R\n├── data-starwars.R\n└── data-storms.R\nThe three band_ datasets have documented in a single file, data-bands.R:\n\nshow/hide documentation for dplyr::band_ datasets# from the dplyr github repo: \n# https://github.com/tidyverse/dplyr/blob/main/R/data-bands.R\n# \n#' Band membership\n#'\n#' These data sets describe band members of the Beatles and Rolling Stones. They\n#' are toy data sets that can be displayed in their entirety on a slide (e.g. to\n#' demonstrate a join).\n#'\n#' `band_instruments` and `band_instruments2` contain the same data but use\n#' different column names for the first column of the data set.\n#' `band_instruments` uses `name`, which matches the name of the key column of\n#' `band_members`; `band_instruments2` uses `artist`, which does not.\n#'\n#' @format Each is a tibble with two variables and three observations\n#' @examples\n#' band_members\n#' band_instruments\n#' band_instruments2\n\"band_members\"\n\n#' @rdname band_members\n#' @format NULL\n\"band_instruments\"\n\n#' @rdname band_members\n#' @format NULL\n\"band_instruments2\"\n\n\nIn the example above, note that two of the datasets (band_instruments and band_instruments2) have the @format set to NULL, and define the help search name with @rdname. The @examples tag can be used to view the dataset when users click ‘Run Examples’\nEither method will–what’s important is that each dataset in your package has documentation.\n\n\n\n\n\n\nDocumenting data in data/\n\n\n\n\n\nDocumenting data requires the following roxygen2 structure:\n#' \n#' @title single-sentence describing [data]\n#' \n#' @description\n#' Single-paragraph describing [data]\n#' \n#' @format [data] number of rows and columns:\n#' \\describe{\n#' \\item{variable}{description}\n#' \\item{variable}{description}\n#' }\n#'\n\"[data]\"\nReplace [data] with the name of your dataset.\n\n\n\ndata-raw/\nThe data-raw folder is not an official directory in the standard R package structure, but it’s a common location for any data processing or cleaning scripts, and the raw data file for datasets stored in data/.\n\n\n\n\n\n\nScripts for creating movies data\n\n\n\n\n\n\nThe code used to produce the movies dataset in the data/ directory might* come from this GitHub repo. If so, the data-raw folder is where the data processing and preparation scritps would be stored (along with a copy of the data in .csv format) before saving a copy in the data/ folder.\n*I say ‘might’ because it’s not clear if the movies.RData is the output from these .R files (although many of the column names match).\n\n\n\n\n\ndplyr example\nIf we look at the data in the dplyr package again, we can see the data-raw/ folder contains a combination of .R and .csv files:\ndata-raw/\n├── band_members.R\n├── starwars.R\n├── starwars.csv\n└── storms.R\n\n1 directory, 4 files\nIn this example, the starwars.R script downloads & prepares starwars, then saves a .csv copy of the data in data-raw.\nRead more about the data-raw folder in R Packages, 2ed" + "section": "The data/ folder", + "text": "The data/ folder\nYou can read more about the data folder in the ‘Data in packages’ section of Writing R Extenstions and the ‘Data’ chapter of R Packages, 2ed.\nThe primary location for app-package data is the data/ folder. Data objects in the data/ folder are available in the package namespace when it’s installed and loaded, and can be accessed with the package::data syntax. See the example below:\n\nlibrary(dplyr)\nstr(dplyr::storms)\n\ntibble [19,066 × 13] (S3: tbl_df/tbl/data.frame)\n $ name : chr [1:19066] \"Amy\" \"Amy\" \"Amy\" \"Amy\" ...\n $ year : num [1:19066] 1975 1975 1975 1975 1975 ...\n $ month : num [1:19066] 6 6 6 6 6 6 6 6 6 6 ...\n $ day : int [1:19066] 27 27 27 27 28 28 28 28 29 29 ...\n $ hour : num [1:19066] 0 6 12 18 0 6 12 18 0 6 ...\n $ lat : num [1:19066] 27.5 28.5 29.5 30.5 31.5 32.4 33.3 34 34.4 34 ...\n $ long : num [1:19066] -79 -79 -79 -79 -78.8 -78.7 -78 -77 -75.8 -74.8 ...\n $ status : Factor w/ 9 levels \"disturbance\",..: 7 7 7 7 7 7 7 7 8 8 ...\n $ category : num [1:19066] NA NA NA NA NA NA NA NA NA NA ...\n $ wind : int [1:19066] 25 25 25 25 25 25 25 30 35 40 ...\n $ pressure : int [1:19066] 1013 1013 1013 1013 1012 1012 1011 1006 1004 1002 ...\n $ tropicalstorm_force_diameter: int [1:19066] NA NA NA NA NA NA NA NA NA NA ...\n $ hurricane_force_diameter : int [1:19066] NA NA NA NA NA NA NA NA NA NA ...\n\n\nLazyData: true\nFiles in data/ should be in the .rda or .RData format. Data files become part of a package when they’re added to the data/ folder and LazyData: true is added to the DESCRIPTION file.\n\nLazyData: true: the data is only loaded into memory if it is explicitly accessed by the user or a function in the package. Until then, only the dataset names is loaded. This practice user-friendly and is the default for most R packages.\nLazyData: false (or omitted): accessing a data file from the package requires explicitly loading it using the data() function.\n\nBelow are the steps for adding movies to pkgApp:\n\n\nMove the movies.RData file into a newly created the data/ folder:\npkgApp/\n │\n └──data/\n └── movies.RData\n\n\nInclude LazyData: true in the DESCRIPTION file (I’ve added it above Imports:):\nPackage: pkgApp\nVersion: 0.0.0.9000\nType: Package\nTitle: movies app\nDescription: A movies data shiny application.\nAuthor: John Smith [aut, cre]\nMaintainer: John Smith <John.Smith@email.io>\nLicense: GPL-3\nRoxygenNote: 7.2.3\nEncoding: UTF-8\nRoxygen: list(markdown = TRUE)\nLazyData: true\nImports:\n shiny,\n ggplot2,\n rlang,\n stringr,\n tools\n\nLoad, document, and install.\n\nCtrl/Cmd + Shift + L\nℹ Loading pkgApp\nCtrl/Cmd + Shift + D\n==> devtools::document(roclets = c('rd', 'collate', 'namespace'))\n\nℹ Updating pkgApp documentation\nℹ Loading pkgApp\nDocumentation completed\nCtrl/Cmd + Shift + B\nIn the Build pane, you’ll notice a few new ** data lines of output after adding data:\n** data\n*** moving datasets to lazyload DB\n** byte-compile and prepare package for lazy loading\nWe can check to see if movies has been included in pkgApp using the package::data syntax:\n\n\n\n(a) roxygen2\n\nFigure 1: movies is now part of pkgApp\n\nusethis::use_data()\nIf you’d prefer to store data using the .rda format, the usethis package has the use_data() function that will automatically store an object in data/ in the .rda format.\nTo use usethis::use_data(), we can load the movies data into the global environment with load(\"movies.RData\"), then run usethis::use_data(movies):\n\nusethis::use_data(movies)\n\n✔ Setting active project to '/path/to/pkgApp'\n✔ Adding 'R' to Depends field in DESCRIPTION\n✔ Creating 'data/'\n✔ Saving 'movies' to 'data/movies.rda'\n• Document your data (see 'https://r-pkgs.org/data.html')\nThe Depends: field is added to the DESCRIPTION file with an R version (this ensures the data files will be loaded)\nDepends: \n R (>= 2.10)\n(this function will also add LazyData: true to the DESCRIPTION)\n\n\n\n\n\n\nMastering Shiny data example\n\n\n\n\n\n\nIf you happened to download, install and load the monthApp example from Mastering Shiny, you may have noticed the NAMESPACE was empty, but the data was exported from the package:\n\n\nmonthApp exports\n\nData files in data/ don’t require roxygen2 tags to be included in a package namespace.\n\n\n\n\nDocumenting data/\n\nDocumenting data can be tedious, but it’s worth the effort if you’ll be sharing your application with collaborators. There are multiple ways to store the documentation for datasets. For example, we could create a data.R file in the R/ folder.\n\nfs::file_create(\"R/data.R\")\n\nIn data.R, we provide a @title, @description, and @details for the data (with or without the tags), followed by @format:\n\n#' @title IMDB movies data \n#'\n#' @description\n#' Movie review data. Note: these data come from the [Building Web Applications with shiny course](https://rstudio-education.github.io/shiny-course/). \n#' \n#' @details\n#' Read more about acquiring these data in the ['Web Scraping and programming' section of Data science in a box](https://datasciencebox.org/02-exploring-data#web-scraping-and-programming) \n#'\n#' @format\n\n@format\nThe text following @format is a one-sentence description of the data (with it’s dimensions).\n\n#' @title IMDB movies data \n#'\n#' @description\n#' Movie review data. Note: these data come from the [Building Web Applications with shiny course](https://rstudio-education.github.io/shiny-course/). \n#' \n#' @details\n#' Read more about acquiring these data in the ['Web Scraping and programming' section of Data science in a box](https://datasciencebox.org/02-exploring-data#web-scraping-and-programming) \n#' \n#' @format A data frame with [] rows and [] variables:\n\n\\describe & \\item\nEach variable (column) in the data is documented with a combination of \\describe and \\item (pay close attention to the curly brackets):\n\n#' \\describe{\n#' \\item{variable}{description}\n#' }\n\nAfter closing the curly brackets in \\describe, place the name of the data in quotes (\"movies\") on the following line.\nBelow is the documentation for the first five columns in the movies dataset:\n\n#' @title IMDB movies data \n#'\n#' @description\n#' Movie review data. Note: these data come from the [Building Web Applications with shiny course](https://rstudio-education.github.io/shiny-course/). \n#' \n#' @details\n#' Read more about acquiring these data in the ['Web Scraping and programming' section of Data science in a box](https://datasciencebox.org/02-exploring-data#web-scraping-and-programming) \n#'\n#' @format A data frame with 651 rows and 34 variables:\n#' \\describe{\n#' \\item{title}{movie title}\n#' \\item{title_type}{type, fct (Documentary, Feature Film, TV Movie)}\n#' \\item{genre}{movie genre, fct (Action & Adventure, Animation, etc.}\n#' \\item{runtime}{movie length in minutes, num, avg = 106, sd = 19.4}\n#' \\item{mpaa_rating}{movie rating, fct (G, NC-17, PG, PG-13, R, Unrated)}\n#' }\n#'\n\"movies\"\n\nIf we load and document pkgApp, we can see a preview of the help file:\nCtrl/Cmd + Shift + L\nℹ Loading pkgApp\nCtrl/Cmd + Shift + D\n==> devtools::document(roclets = c('rd', 'collate', 'namespace'))\n\nℹ Updating pkgApp documentation\nℹ Loading pkgApp\nWriting movies.Rd\nDocumentation completed\n\n?movies\n\n\n\n\n(a) The movies help file\n\nFigure 2: Documentation for the movies dataset\n\nI’ve provided documentation for the full movies dataset below.\n\nshow/hide full movies data documenation#' @title IMDB movies data \n#'\n#' @description\n#' Movie review data. Note: these data come from the [Building Web Applications with shiny course](https://rstudio-education.github.io/shiny-course/). \n#' \n#' @details\n#' Read more about acquiring these data in the ['Web Scraping and programming' section of Data science in a box](https://datasciencebox.org/02-exploring-data#web-scraping-and-programming) \n#'\n#' @format A data frame with 651 rows and 34 variables:\n#' \\describe{\n#' \\item{title}{movie title}\n#' \\item{title_type}{type, fct (Documentary, Feature Film, TV Movie)}\n#' \\item{genre}{movie genre, fct (Action & Adventure, Animation, etc.}\n#' \\item{runtime}{movie length in minutes, num, avg = 106, sd = 19.4}\n#' \\item{mpaa_rating}{movie rating, fct (G, NC-17, PG, PG-13, R, Unrated)}\n#' \\item{studio}{name of studio, chr}\n#' \\item{thtr_rel_date}{Theatre release date, POSIXct, min = 1970-05-19 21:00:00, max = 2014-12-24 21:00:00}\n#' \\item{thtr_rel_year}{Theatre release year, num, min = 1970, max = 2014}\n#' \\item{thtr_rel_month}{Theatre release month, num, min = 1, max =12}\n#' \\item{thtr_rel_day}{Theatre release day, num, min = 1, max =31}\n#' \\item{dvd_rel_date}{DVD release date, POSIXct, min = 1991-03-27 21:00:00, max = 2015-03-02 21:00:00}\n#' \\item{dvd_rel_year}{DVD release year, num, min = 1991, max = 2015}\n#' \\item{dvd_rel_month}{DVD release month, num, min = 1, max = 12}\n#' \\item{dvd_rel_day}{DVD release day, num, min = 1, max = 31}\n#' \\item{imdb_rating}{Internet movie database rating, avg = 6.49, sd = 1.08}\n#' \\item{imdb_num_votes}{Internet movie database votes, avg = 57533, sd = 112124}\n#' \\item{critics_rating}{Rotten tomatoes rating, fct (Certified Fresh, Fresh, Rotten)}\n#' \\item{critics_score}{Rotten tomatoes score, avg = 57.7, sd = 28.4}\n#' \\item{audience_rating}{Audience rating, fct (Spilled, Upright)}\n#' \\item{audience_score}{Audience score, avg = 62.4, sd = 20.2}\n#' \\item{best_pic_nom}{Best picture nomination, fct (no, yes)}\n#' \\item{best_pic_win}{Best picture win, fct (no, yes)}\n#' \\item{best_actor_win}{Best actor win, fct (no, yes)}\n#' \\item{best_actress_win}{Best actress win, fct (no, yes)}\n#' \\item{best_dir_win}{Best director win, fct (no, yes)}\n#' \\item{top200_box}{Top 20 box-office, fct (no, yes)}\n#' \\item{director}{Name of director, chr}\n#' \\item{actor1}{Name of leading actor, chr}\n#' \\item{actor2}{Name of supporting actor, chr}\n#' \\item{actor3}{Name of #3 actor, chr}\n#' \\item{actor4}{Name of #4 actor, chr}\n#' \\item{actor5}{Name of #5 actor, chr}\n#' \\item{imdb_url}{IMDB URL}\n#' \\item{rt_url}{Rotten tomatoes URL}\n#' }\n#'\n\"movies\"\n\n\nRemove load()\n\nAfter documenting the movies data in data.R, we’ll remove the call to load() in the mod_scatter_display_server() function and replace it with a direct call to the dataset:\n\nmod_scatter_display_server <- function(id, var_inputs) {\n shiny::moduleServer(id, function(input, output, session) {\n\n inputs <- shiny::reactive({\n plot_title <- tools::toTitleCase(var_inputs$plot_title())\n list(\n x = var_inputs$x(),\n y = var_inputs$y(),\n z = var_inputs$z(),\n alpha = var_inputs$alpha(),\n size = var_inputs$size(),\n plot_title = plot_title\n )\n })\n output$scatterplot <- shiny::renderPlot({\n plot <- scatter_plot(\n # data -----------------------------------------------------\n df = movies,\n x_var = inputs()$x,\n y_var = inputs()$y,\n col_var = inputs()$z,\n alpha_var = inputs()$alpha,\n size_var = inputs()$size\n )\n plot +\n ggplot2::labs(\n title = inputs()$plot_title,\n x = stringr::str_replace_all(tools::toTitleCase(inputs()$x), \"_\", \" \"),\n y = stringr::str_replace_all(tools::toTitleCase(inputs()$y), \"_\", \" \")\n ) +\n ggplot2::theme_minimal() +\n ggplot2::theme(legend.position = \"bottom\")\n })\n })\n}\n\nAfter loading, documenting, and installing the package, we see the following application:\n\n\n\n\n(a) movies_app() with movies data file\n\nFigure 3: pkgApp::movies in movies_app()\n\n\n\ndplyr example\nTo illustrate other options for data documentation, we’ll use the dplyr package. dplyr stores its data in the data/ folder:\ndata/\n├── band_instruments.rda\n├── band_instruments2.rda\n├── band_members.rda\n├── starwars.rda\n└── storms.rda\nThe documentation for the datasets in dplyr are stored in R/ using a data- prefix:\nR/\n├── data-bands.R\n├── data-starwars.R\n└── data-storms.R\nThe three band_ datasets have documented in a single file, data-bands.R:\n\nshow/hide documentation for dplyr::band_ datasets# from the dplyr github repo: \n# https://github.com/tidyverse/dplyr/blob/main/R/data-bands.R\n# \n#' Band membership\n#'\n#' These data sets describe band members of the Beatles and Rolling Stones. They\n#' are toy data sets that can be displayed in their entirety on a slide (e.g. to\n#' demonstrate a join).\n#'\n#' `band_instruments` and `band_instruments2` contain the same data but use\n#' different column names for the first column of the data set.\n#' `band_instruments` uses `name`, which matches the name of the key column of\n#' `band_members`; `band_instruments2` uses `artist`, which does not.\n#'\n#' @format Each is a tibble with two variables and three observations\n#' @examples\n#' band_members\n#' band_instruments\n#' band_instruments2\n\"band_members\"\n\n#' @rdname band_members\n#' @format NULL\n\"band_instruments\"\n\n#' @rdname band_members\n#' @format NULL\n\"band_instruments2\"\n\n\nIn the example above, note that two of the datasets (band_instruments and band_instruments2) have the @format set to NULL, and define the help search name with @rdname. The @examples tag can be used to view the dataset when users click ‘Run Examples’\nEither method will–what’s important is that each dataset in your package has documentation.\n\n\n\n\n\n\nDocumenting data in data/\n\n\n\n\n\nDocumenting data requires the following roxygen2 structure:\n#' \n#' @title single-sentence describing [data]\n#' \n#' @description\n#' Single-paragraph describing [data]\n#' \n#' @format [data] number of rows and columns:\n#' \\describe{\n#' \\item{variable}{description}\n#' \\item{variable}{description}\n#' }\n#'\n\"[data]\"\nReplace [data] with the name of your dataset.\n\n\n\nThe data-raw/ folder\nThe data-raw folder is not an official directory in the standard R package structure, but it’s a common location for any data processing or cleaning scripts, and the raw data file for datasets stored in data/.\n\n\n\n\n\n\nScripts for creating movies data\n\n\n\n\n\n\nThe code used to produce the movies dataset in the data/ directory might* come from this GitHub repo. If so, the data-raw folder is where the data processing and preparation scritps would be stored (along with a copy of the data in .csv format) before saving a copy in the data/ folder.\n*I say ‘might’ because it’s not clear if the movies.RData is the output from these .R files (although many of the column names match).\n\n\n\n\n\ndplyr example\nIf we look at the data in the dplyr package again, we can see the data-raw/ folder contains a combination of .R and .csv files:\ndata-raw/\n├── band_members.R\n├── starwars.R\n├── starwars.csv\n└── storms.R\n\n1 directory, 4 files\nIn this example, the starwars.R script downloads & prepares starwars, then saves a .csv copy of the data in data-raw.\nRead more about the data-raw folder in R Packages, 2ed" }, { - "objectID": "data.html#instextdata", - "href": "data.html#instextdata", + "objectID": "data.html#the-instextdata-folder", + "href": "data.html#the-instextdata-folder", "title": "Data files", - "section": "inst/extdata/", - "text": "inst/extdata/\nThe extdata folder (inside inst/) can be used for external datasets in other file formats (.csv, .tsv, .txt, .xlsx, etc).\nThe data files in inst/extdata/ aren’t directly loadable using the package::data syntax or the data() function like with the data/ directory. These files can be imported using the file path accessor function, system.file().\nFor example, if we create the inst/extdata/ and save a copy of movies as a .fst file:\n\nlibrary(fs)\nlibrary(tibble)\nlibrary(fst)\n\n\nfst package v0.9.8\n\n\nfs::dir_create(\"inst/extdata/\")\nfst::write_fst(\n x = movies, \n path = \"inst/extdata/movies.fst\", \n compress = 75)\n\n\nfstcore package v0.9.14\n(OpenMP was not detected, using single threaded mode)\n\nThen load, document, and install pkgApp:\nCtrl/Cmd + Shift + L\nCtrl/Cmd + Shift + D\nCtrl/Cmd + Shift + B\nWe can import movies.fst using system.file() to create a path to the file:\n\ntibble::as_tibble(\n fst::read_fst(path = \n system.file(\"extdata/\", \"movies.fst\", package = \"pkgApp\")\n )\n )\n\n# A tibble: 651 × 34\n title title_type genre runtime mpaa_rating studio thtr_rel_date \n <chr> <fct> <fct> <dbl> <fct> <fct> <dttm> \n 1 Filly Brown Feature F… Drama 80 R Indom… 2013-04-18 21:00:00\n 2 The Dish Feature F… Drama 101 PG-13 Warne… 2001-03-13 21:00:00\n 3 Waiting fo… Feature F… Come… 84 R Sony … 1996-08-20 21:00:00\n 4 The Age of… Feature F… Drama 139 PG Colum… 1993-09-30 21:00:00\n 5 Malevolence Feature F… Horr… 90 R Ancho… 2004-09-09 21:00:00\n 6 Old Partner Documenta… Docu… 78 Unrated Shcal… 2009-01-14 21:00:00\n 7 Lady Jane Feature F… Drama 142 PG-13 Param… 1985-12-31 21:00:00\n 8 Mad Dog Ti… Feature F… Drama 93 R MGM/U… 1996-11-07 21:00:00\n 9 Beauty Is … Documenta… Docu… 88 Unrated Indep… 2012-09-06 21:00:00\n10 The Snowto… Feature F… Drama 119 Unrated IFC F… 2012-03-01 21:00:00\n# ℹ 641 more rows\n# ℹ 27 more variables: thtr_rel_year <dbl>, thtr_rel_month <dbl>,\n# thtr_rel_day <dbl>, dvd_rel_date <dttm>, dvd_rel_year <dbl>,\n# dvd_rel_month <dbl>, dvd_rel_day <dbl>, imdb_rating <dbl>,\n# imdb_num_votes <int>, critics_rating <fct>, critics_score <dbl>,\n# audience_rating <fct>, audience_score <dbl>, best_pic_nom <fct>,\n# best_pic_win <fct>, best_actor_win <fct>, best_actress_win <fct>, …\n# ℹ Use `print(n = ...)` to see more rows\nWe’ll cover inst/ and system.file() in more detail in the next chapter.\n\n\n\n\n\n\nGitHub [pkgApp]:[04-data]\n\n\n\n\n\n\nThe code for this section was pushed to the [04-data] branch of the [pkgApp] repo.\ngit add .\ngit commit -m 'git message'\ngit push" + "section": "The inst/extdata/ folder", + "text": "The inst/extdata/ folder\nThe extdata folder (inside inst/) can be used for external datasets in other file formats (.csv, .tsv, .txt, .xlsx, etc).\nThe data files in inst/extdata/ aren’t directly loadable using the package::data syntax or the data() function like with the data/ directory. These files can be imported using the file path accessor function, system.file().\nFor example, if we create the inst/extdata/ and save a copy of movies as a .fst file:\n\nlibrary(fs)\nlibrary(tibble)\nlibrary(fst)\n\n\nfst package v0.9.8\n\n\nfs::dir_create(\"inst/extdata/\")\nfst::write_fst(\n x = movies, \n path = \"inst/extdata/movies.fst\", \n compress = 75)\n\n\nfstcore package v0.9.14\n(OpenMP was not detected, using single threaded mode)\n\nThen load, document, and install pkgApp:\nCtrl/Cmd + Shift + L / D / B\nWe can import movies.fst using system.file() to create a path to the file:\n\ntibble::as_tibble(\n fst::read_fst(path = \n system.file(\"extdata/\", \"movies.fst\", package = \"pkgApp\")\n )\n )\n\n# A tibble: 651 × 34\n title title_type genre runtime mpaa_rating studio thtr_rel_date \n <chr> <fct> <fct> <dbl> <fct> <fct> <dttm> \n 1 Filly… Feature F… Drama 80 R Indom… 2013-04-18 21:00:00\n 2 The D… Feature F… Drama 101 PG-13 Warne… 2001-03-13 21:00:00\n 3 Waiti… Feature F… Come… 84 R Sony … 1996-08-20 21:00:00\n 4 The A… Feature F… Drama 139 PG Colum… 1993-09-30 21:00:00\n 5 Malev… Feature F… Horr… 90 R Ancho… 2004-09-09 21:00:00\n 6 Old P… Documenta… Docu… 78 Unrated Shcal… 2009-01-14 21:00:00\n 7 Lady … Feature F… Drama 142 PG-13 Param… 1985-12-31 21:00:00\n 8 Mad D… Feature F… Drama 93 R MGM/U… 1996-11-07 21:00:00\n 9 Beaut… Documenta… Docu… 88 Unrated Indep… 2012-09-06 21:00:00\n10 The S… Feature F… Drama 119 Unrated IFC F… 2012-03-01 21:00:00\n# ℹ 641 more rows\n# ℹ 27 more variables: thtr_rel_year <dbl>, thtr_rel_month <dbl>,\n# thtr_rel_day <dbl>, dvd_rel_date <dttm>, dvd_rel_year <dbl>,\n# dvd_rel_month <dbl>, dvd_rel_day <dbl>, imdb_rating <dbl>,\n# imdb_num_votes <int>, critics_rating <fct>, critics_score <dbl>,\n# audience_rating <fct>, audience_score <dbl>, best_pic_nom <fct>,\n# best_pic_win <fct>, best_actor_win <fct>, best_actress_win <fct>, …\n# ℹ Use `print(n = ...)` to see more rows\nWe’ll cover inst/ and system.file() in more detail in the next chapter.\n\n\n\n\nNew Git Branch\n\nThe code for this section is in the [04_data] branch of the [pkgApp] repo." }, { "objectID": "data.html#recap", @@ -326,21 +333,21 @@ "href": "inst_www.html#pkgapp", "title": "\ninst/ & www/\n", "section": "pkgApp", - "text": "pkgApp\nThe current structure of pkgApp is below:\npkgApp/\n├── DESCRIPTION\n├── NAMESPACE\n├── R/\n│ ├── data.R\n│ ├── mod_scatter_display.R\n│ ├── mod_var_input.R\n│ ├── movies_app.R\n│ ├── movies_server.R\n│ ├── movies_ui.R\n│ └── scatter_plot.R\n├── README.md\n├── app.R\n├── data/\n│ └── movies.RData\n├── inst/\n│ └── extdata/\n│ └── movies.fst\n├── man/\n│ ├── mod_scatter_display_server.Rd\n│ ├── mod_scatter_display_ui.Rd\n│ ├── mod_var_input_server.Rd\n│ ├── mod_var_input_ui.Rd\n│ ├── movies.Rd\n│ ├── movies_app.Rd\n│ ├── movies_server.Rd\n│ ├── movies_ui.Rd\n│ └── scatter_plot.Rd\n├── pkgApp.Rproj\n└── www/\n └── shiny.png\n\n7 directories, 23 files\nWhen we run movies_app(), we see the following:\n\n\n\n\n(a) movies_app() without logo\n\nFigure 1: www not accessible in pkgApp\n\n\nThe shiny.png logo in www/ is not being loaded into the UI when the application is launched. Before we dive into adding the contents of www/ into our app-package, we’ll take a step back and investigate what happens when pkgApp is installed." + "text": "pkgApp\nThe current structure of pkgApp is below:\npkgApp/\n ├── DESCRIPTION\n ├── NAMESPACE\n ├── R/\n │ ├── data.R\n │ ├── mod_scatter_display.R\n │ ├── mod_var_input.R\n │ ├── movies_app.R\n │ ├── movies_server.R\n │ ├── movies_ui.R\n │ └── scatter_plot.R\n ├── README.md\n ├── app.R\n ├── data/\n │ └── movies.RData\n ├── inst/\n │ └── extdata\n │ └── movies.fst\n ├── man/\n │ ├── mod_scatter_display_server.Rd\n │ ├── mod_scatter_display_ui.Rd\n │ ├── mod_var_input_server.Rd\n │ ├── mod_var_input_ui.Rd\n │ ├── movies.Rd\n │ ├── movies_app.Rd\n │ ├── movies_server.Rd\n │ ├── movies_ui.Rd\n │ └── scatter_plot.Rd\n ├── pkgApp.Rproj\n └── www/\n └── shiny.png\n\n7 directories, 24 files\nWhen we run movies_app(), we see the following:\n\n\n\n\n(a) movies_app() without logo\n\nFigure 1: www not accessible in pkgApp\n\n\nThe shiny.png logo in www/ is not being loaded into the UI when the application is launched. Before we dive into adding the contents of www/ into our app-package, we’ll take a step back and investigate what happens when pkgApp is installed." }, { "objectID": "inst_www.html#whats-in-the-installed-package", "href": "inst_www.html#whats-in-the-installed-package", "title": "\ninst/ & www/\n", "section": "What’s in the ‘installed’ package?", - "text": "What’s in the ‘installed’ package?\nWe’ve been running load_all(), document(), and install() on pkgApp, but we haven’t looked at the contents of the installed package. Reviewing the installed package will help us understand what the system.file() function is doing, and how we can use the inst/ folder to include external resources to our app-package.\nWhen we run devtools::install(), the output tells us where the package is being installed:\n==> R CMD INSTALL --preclean --no-multiarch --with-keep.source pkgApp\n\n* installing to library ‘/path/to/install/Library/R/x86_64/4.2/library’\nIf we add the pkgApp/ directory to the end of the path above, we can view the contents of the installed package (I’m using the tree command in the Terminal, but the fs::dir_tree() function will also print a folder tree):\n/path/to/install/Library/R/x86_64/4.2/library/pkgApp/\n├── DESCRIPTION\n├── INDEX\n├── Meta/\n│ ├── Rd.rds\n│ ├── data.rds\n│ ├── features.rds\n│ ├── hsearch.rds\n│ ├── links.rds\n│ ├── nsInfo.rds\n│ └── package.rds\n├── NAMESPACE\n├── R/\n│ ├── pkgApp\n│ ├── pkgApp.rdb\n│ └── pkgApp.rdx\n├── data/\n│ ├── Rdata.rdb\n│ ├── Rdata.rds\n│ └── Rdata.rdx\n├── extdata/\n│ └── movies.fst\n├── help/\n│ ├── AnIndex\n│ ├── aliases.rds\n│ ├── paths.rds\n│ ├── pkgApp.rdb\n│ └── pkgApp.rdx\n└── html/\n ├── 00Index.html\n └── R.css\n\n7 directories, 24 files\nThe installed version of pkgApp has many of the same files as the ‘source’ version we’ve been writing (i.e., the NAMESPACE and DESCRIPTION). It also might surprise you to see that many of the source package files aren’t included in the installed version (.R, .Rd files. etc.). The key takeaway here is that the inst/ subfolders and files are available in the installed version unchanged (with the inst/ folder omitted.).\nHopefully viewing this folder structure helps demystify what happens when a package is installed. Below is the official documentation on what happens to the inst/ folder (and it’s subfolders) when a package is installed:\n\n“The contents of the inst/ subdirectory will be copied recursively to the installation directory. Subdirectories of inst/ should not interfere with those used by R (currently, R/, data/, demo/, exec/, libs/, man/, help/, html/ and Meta/, and earlier versions used latex/, R-ex/).” - Writing R extensions, Package subdirectories\n\n\n\n\n\n\n\nOther installed package structures\n\n\n\n\n\n\nYou can explore the structure of other installed packages to see how they work ‘under the hood’ to gain insight into how they use the inst/ folder.\n\n\nFor example, the inst/extdata/ folder in the readr package holds a variety of datasets:\n/path/to/install/Library/R/x86_64/4.2/library/readr/\n\nextdata/\n ├── challenge.csv\n ├── chickens.csv\n ├── epa78.txt\n ├── example.log\n ├── fwf-sample.txt\n ├── massey-rating.txt\n ├── mini-gapminder-africa.csv\n ├── mini-gapminder-americas.csv\n ├── mini-gapminder-asia.csv\n ├── mini-gapminder-europe.csv\n ├── mini-gapminder-oceania.csv\n ├── mtcars.csv\n ├── mtcars.csv.bz2\n ├── mtcars.csv.zip\n └── whitespace-sample.txt\n\n1 directory, 15 files\n\n\nThese files are used in readr::readr_example() (in R/example.R):\n#' Get path to readr example\n#'\n#' readr comes bundled with a number of sample files in its `inst/extdata`\n#' directory. This function make them easy to access\n#'\n#' @param file Name of file. If `NULL`, the example files will be listed.\n#' @export\n#' @examples\n#' readr_example()\n#' readr_example('challenge.csv')\nreadr_example <- function(file = NULL) {\n if (is.null(file)) {\n dir(system.file('extdata', package = 'readr'))\n } else {\n system.file('extdata', file, package = 'readr', mustWork = TRUE)\n }\n}\n\n\n\n\n\n\nHow system.file() works\nsystem.file() gives us access to the package files on installation (i.e., the files we see at the path above).\nIn the previous chapter, we used system.file() to access the movies.fst file in inst/extdata/:\n\ndplyr::glimpse( \n fst::read_fst(path = \n system.file(\"extdata/\", \"movies.fst\", package = \"pkgApp\")\n )\n )\n\n\nshow/hide glimpse() of movies.fstfstcore package v0.9.14\n(OpenMP was not detected, using single threaded mode)\nRows: 651\nColumns: 34\n$ title <chr> \"Filly Brown\", \"The Dish\", \"Waiting for Guffman\", \"The Age of …\n$ title_type <fct> Feature Film, Feature Film, Feature Film, Feature Film, Featur…\n$ genre <fct> Drama, Drama, Comedy, Drama, Horror, Documentary, Drama, Drama…\n$ runtime <dbl> 80, 101, 84, 139, 90, 78, 142, 93, 88, 119, 127, 108, 110, 100…\n$ mpaa_rating <fct> R, PG-13, R, PG, R, Unrated, PG-13, R, Unrated, Unrated, PG, P…\n$ studio <fct> Indomina Media Inc., Warner Bros. Pictures, Sony Pictures Clas…\n$ thtr_rel_date <dttm> 2013-04-18 21:00:00, 2001-03-13 21:00:00, 1996-08-20 21:00:00…\n$ thtr_rel_year <dbl> 2013, 2001, 1996, 1993, 2004, 2009, 1986, 1996, 2012, 2012, 19…\n$ thtr_rel_month <dbl> 4, 3, 8, 10, 9, 1, 1, 11, 9, 3, 6, 12, 1, 9, 6, 8, 3, 7, 4, 9,…\n$ thtr_rel_day <dbl> 19, 14, 21, 1, 10, 15, 1, 8, 7, 2, 19, 18, 4, 23, 20, 27, 1, 1…\n$ dvd_rel_date <dttm> 2013-07-29 21:00:00, 2001-08-27 21:00:00, 2001-08-20 21:00:00…\n$ dvd_rel_year <dbl> 2013, 2001, 2001, 2001, 2005, 2010, 2003, 2004, 2013, 2012, 20…\n$ dvd_rel_month <dbl> 7, 8, 8, 11, 4, 4, 2, 3, 1, 8, 5, 9, 7, 2, 3, 12, 8, 1, 6, 1, …\n$ dvd_rel_day <dbl> 30, 28, 21, 6, 19, 20, 18, 2, 21, 14, 1, 23, 9, 13, 22, 21, 19…\n$ imdb_rating <dbl> 5.5, 7.3, 7.6, 7.2, 5.1, 7.8, 7.2, 5.5, 7.5, 6.6, 6.8, 6.0, 7.…\n$ imdb_num_votes <int> 899, 12285, 22381, 35096, 2386, 333, 5016, 2272, 880, 12496, 7…\n$ critics_rating <fct> Rotten, Certified Fresh, Certified Fresh, Certified Fresh, Rot…\n$ critics_score <dbl> 45, 96, 91, 80, 33, 91, 57, 17, 90, 83, 89, 67, 80, 25, 15, 78…\n$ audience_rating <fct> Upright, Upright, Upright, Upright, Spilled, Upright, Upright,…\n$ audience_score <dbl> 73, 81, 91, 76, 27, 86, 76, 47, 89, 66, 75, 46, 89, 53, 36, 64…\n$ best_pic_nom <fct> no, no, no, no, no, no, no, no, no, no, no, no, no, no, no, no…\n$ best_pic_win <fct> no, no, no, no, no, no, no, no, no, no, no, no, no, no, no, no…\n$ best_actor_win <fct> no, no, no, yes, no, no, no, yes, no, no, yes, no, yes, no, no…\n$ best_actress_win <fct> no, no, no, no, no, no, no, no, no, no, no, no, yes, no, no, y…\n$ best_dir_win <fct> no, no, no, yes, no, no, no, no, no, no, no, no, no, no, no, y…\n$ top200_box <fct> no, no, no, no, no, no, no, no, no, no, yes, no, no, no, no, n…\n$ director <chr> \"Michael D. Olmos\", \"Rob Sitch\", \"Christopher Guest\", \"Martin …\n$ actor1 <chr> \"Gina Rodriguez\", \"Sam Neill\", \"Christopher Guest\", \"Daniel Da…\n$ actor2 <chr> \"Jenni Rivera\", \"Kevin Harrington\", \"Catherine O'Hara\", \"Miche…\n$ actor3 <chr> \"Lou Diamond Phillips\", \"Patrick Warburton\", \"Parker Posey\", \"…\n$ actor4 <chr> \"Emilio Rivera\", \"Tom Long\", \"Eugene Levy\", \"Richard E. Grant\"…\n$ actor5 <chr> \"Joseph Julian Soria\", \"Genevieve Mooy\", \"Bob Balaban\", \"Alec …\n$ imdb_url <chr> \"http://www.imdb.com/title/tt1869425/\", \"http://www.imdb.com/t…\n$ rt_url <chr> \"//www.rottentomatoes.com/m/filly_brown_2012/\", \"//www.rottent…\n\n\nTo include the contents of www/ in our app-package, we need to use isnt/ folder, system.file(), and the shiny::addResourcePath() function." + "text": "What’s in the ‘installed’ package?\nWe’ve been running load_all(), document(), and install() on pkgApp, but we haven’t looked at the contents of the installed package. Reviewing the installed package will help us understand what the system.file() function is doing, and how we can use the inst/ folder to include external resources to our app-package.\nWhen we run devtools::install(), the output tells us where the package is being installed:\n==> R CMD INSTALL --preclean --no-multiarch --with-keep.source pkgApp\n\n* installing to library ‘/path/to/install/Library/R/x86_64/4.2/library’\nIf we add the pkgApp/ directory to the end of the path above, we can view the contents of the installed package (I’m using the tree command in the Terminal, but the fs::dir_tree() function will also print a folder tree):\n/path/to/install/Library/R/x86_64/4.2/library/pkgApp/\n├── DESCRIPTION\n├── INDEX\n├── Meta/\n│ ├── Rd.rds\n│ ├── data.rds\n│ ├── features.rds\n│ ├── hsearch.rds\n│ ├── links.rds\n│ ├── nsInfo.rds\n│ └── package.rds\n├── NAMESPACE\n├── R/\n│ ├── pkgApp\n│ ├── pkgApp.rdb\n│ └── pkgApp.rdx\n├── data/\n│ ├── Rdata.rdb\n│ ├── Rdata.rds\n│ └── Rdata.rdx\n├── extdata/\n│ └── movies.fst\n├── help/\n│ ├── AnIndex\n│ ├── aliases.rds\n│ ├── paths.rds\n│ ├── pkgApp.rdb\n│ └── pkgApp.rdx\n└── html/\n ├── 00Index.html\n └── R.css\n\n7 directories, 24 files\nThe installed version of pkgApp has many of the same files as the ‘source’ version we’ve been writing (i.e., the NAMESPACE and DESCRIPTION). It also might surprise you to see that many of the source package files aren’t included in the installed version (.R, .Rd files. etc.). The key takeaway here is that the inst/ subfolders and files are available in the installed version unchanged (with the inst/ folder omitted.).\nHopefully viewing this folder structure helps demystify what happens when a package is installed. Below is the official documentation on what happens to the inst/ folder (and it’s subfolders) when a package is installed:\n\n“The contents of the inst/ subdirectory will be copied recursively to the installation directory. Subdirectories of inst/ should not interfere with those used by R (currently, R/, data/, demo/, exec/, libs/, man/, help/, html/ and Meta/, and earlier versions used latex/, R-ex/).” - Writing R extensions, Package subdirectories\n\n\n\n\n\n\n\nOther installed package structures\n\n\n\n\n\n\nYou can explore the structure of other installed packages to see how they work ‘under the hood’ to gain insight into how they use the inst/ folder.\n\n\nFor example, the inst/extdata/ folder in the readr package holds a variety of datasets:\n/path/to/install/Library/R/x86_64/4.2/library/readr/\n\nextdata/\n ├── challenge.csv\n ├── chickens.csv\n ├── epa78.txt\n ├── example.log\n ├── fwf-sample.txt\n ├── massey-rating.txt\n ├── mini-gapminder-africa.csv\n ├── mini-gapminder-americas.csv\n ├── mini-gapminder-asia.csv\n ├── mini-gapminder-europe.csv\n ├── mini-gapminder-oceania.csv\n ├── mtcars.csv\n ├── mtcars.csv.bz2\n ├── mtcars.csv.zip\n └── whitespace-sample.txt\n\n1 directory, 15 files\n\n\nThese files are used in readr::readr_example() (in R/example.R):\n#' Get path to readr example\n#'\n#' readr comes bundled with a number of sample files in its `inst/extdata`\n#' directory. This function make them easy to access\n#'\n#' @param file Name of file. If `NULL`, the example files will be listed.\n#' @export\n#' @examples\n#' readr_example()\n#' readr_example('challenge.csv')\nreadr_example <- function(file = NULL) {\n if (is.null(file)) {\n dir(system.file('extdata', package = 'readr'))\n } else {\n system.file('extdata', file, package = 'readr', mustWork = TRUE)\n }\n}\n\n\n\n\n\n\nHow system.file() works\nsystem.file() gives us access to the package files on installation (i.e., the files we see at the path above). In the previous chapter, we used system.file() to access the movies.fst file in inst/extdata/:\n\ndplyr::glimpse( \n fst::read_fst(path = \n system.file(\"extdata/\", \"movies.fst\", package = \"pkgApp\")\n )\n )\n\n\nshow/hide glimpse() of movies.fstfstcore package v0.9.14\n(OpenMP was not detected, using single threaded mode)\nRows: 651\nColumns: 34\n$ title <chr> \"Filly Brown\", \"The Dish\", \"Waiting for…\n$ title_type <fct> Feature Film, Feature Film, Feature Fil…\n$ genre <fct> Drama, Drama, Comedy, Drama, Horror, Do…\n$ runtime <dbl> 80, 101, 84, 139, 90, 78, 142, 93, 88, …\n$ mpaa_rating <fct> R, PG-13, R, PG, R, Unrated, PG-13, R, …\n$ studio <fct> Indomina Media Inc., Warner Bros. Pictu…\n$ thtr_rel_date <dttm> 2013-04-18 21:00:00, 2001-03-13 21:00:…\n$ thtr_rel_year <dbl> 2013, 2001, 1996, 1993, 2004, 2009, 198…\n$ thtr_rel_month <dbl> 4, 3, 8, 10, 9, 1, 1, 11, 9, 3, 6, 12, …\n$ thtr_rel_day <dbl> 19, 14, 21, 1, 10, 15, 1, 8, 7, 2, 19, …\n$ dvd_rel_date <dttm> 2013-07-29 21:00:00, 2001-08-27 21:00:…\n$ dvd_rel_year <dbl> 2013, 2001, 2001, 2001, 2005, 2010, 200…\n$ dvd_rel_month <dbl> 7, 8, 8, 11, 4, 4, 2, 3, 1, 8, 5, 9, 7,…\n$ dvd_rel_day <dbl> 30, 28, 21, 6, 19, 20, 18, 2, 21, 14, 1…\n$ imdb_rating <dbl> 5.5, 7.3, 7.6, 7.2, 5.1, 7.8, 7.2, 5.5,…\n$ imdb_num_votes <int> 899, 12285, 22381, 35096, 2386, 333, 50…\n$ critics_rating <fct> Rotten, Certified Fresh, Certified Fres…\n$ critics_score <dbl> 45, 96, 91, 80, 33, 91, 57, 17, 90, 83,…\n$ audience_rating <fct> Upright, Upright, Upright, Upright, Spi…\n$ audience_score <dbl> 73, 81, 91, 76, 27, 86, 76, 47, 89, 66,…\n$ best_pic_nom <fct> no, no, no, no, no, no, no, no, no, no,…\n$ best_pic_win <fct> no, no, no, no, no, no, no, no, no, no,…\n$ best_actor_win <fct> no, no, no, yes, no, no, no, yes, no, n…\n$ best_actress_win <fct> no, no, no, no, no, no, no, no, no, no,…\n$ best_dir_win <fct> no, no, no, yes, no, no, no, no, no, no…\n$ top200_box <fct> no, no, no, no, no, no, no, no, no, no,…\n$ director <chr> \"Michael D. Olmos\", \"Rob Sitch\", \"Chris…\n$ actor1 <chr> \"Gina Rodriguez\", \"Sam Neill\", \"Christo…\n$ actor2 <chr> \"Jenni Rivera\", \"Kevin Harrington\", \"Ca…\n$ actor3 <chr> \"Lou Diamond Phillips\", \"Patrick Warbur…\n$ actor4 <chr> \"Emilio Rivera\", \"Tom Long\", \"Eugene Le…\n$ actor5 <chr> \"Joseph Julian Soria\", \"Genevieve Mooy\"…\n$ imdb_url <chr> \"http://www.imdb.com/title/tt1869425/\",…\n$ rt_url <chr> \"//www.rottentomatoes.com/m/filly_brown…\n\n\nTo include the contents of www/ in our app-package, we need to use isnt/ folder, system.file(), and the shiny::addResourcePath() function." }, { "objectID": "inst_www.html#addresourcepath", "href": "inst_www.html#addresourcepath", "title": "\ninst/ & www/\n", "section": "addResourcePath()", - "text": "addResourcePath()\nshiny’s addResourcePath() function will add a “directory of static resources to Shiny’s web server.” In pkgApp, want to add the www directory that includes the shiny.png file (currently in our root (\".\") folder).\nIf we want to continue using the www/ folder name, we can simply move www and it’s contents into inst/:\ninst/\n├── extdata\n│ └── movies.fst\n└── www/\n └── shiny.png\n\n3 directories, 2 files\nIn R/movies_ui.R function, we’ll include the addResourcePath() at the top of the tagList() and reference the image in img() using only the subfolder in the path:\n\nmovies_ui <- function() {\n shiny::addResourcePath('www', system.file('www', package = 'pkgApp'))\n shiny::tagList(\n shiny::fluidPage(\n shiny::titlePanel(\n shiny::div(\n shiny::img(\n src = \"www/shiny.png\",\n height = 60,\n width = 55,\n style = \"margin:10px 10px\"\n ), \n \"Movies Reviews\"\n )\n ),\n shiny::sidebarLayout(\n shiny::sidebarPanel(\n mod_var_input_ui(\"vars\")\n ),\n shiny::mainPanel(\n mod_scatter_display_ui(\"plot\")\n )\n )\n )\n )\n} \n\nAfter loading, documenting, and installing, the application now includes the image file.\n\n\n\n\n(a) movies_app() with logo\n\nFigure 2: inst/www accessible with addResourcePath()\n\n\nYou can read more about adding external resources in the documentation for addResourcePath(). We’ll also cover adding CSS and JavaScript files in later chapters.\n\n\n\n\n\n\nGitHub [pkgApp]:[05-inst]\n\n\n\n\n\n\nThe code for this section was pushed to the [05-inst] branch of the [pkgApp] repo.\ngit add .\ngit commit -m 'git message'\ngit push" + "text": "addResourcePath()\nshiny’s addResourcePath() function will add a “directory of static resources to Shiny’s web server.” In pkgApp, want to add the www directory that includes the shiny.png file (currently in our root (\".\") folder).\nIf we want to continue using the www/ folder name, we can simply move www and it’s contents into inst/:\ninst/\n├── extdata\n│ └── movies.fst\n└── www/\n └── shiny.png\n\n3 directories, 2 files\nIn R/movies_ui.R function, we’ll include the addResourcePath() at the top of the tagList() and reference the image in img() using only the subfolder in the path:\n\nmovies_ui <- function() {\n shiny::addResourcePath('www', system.file('www', package = 'pkgApp'))\n shiny::tagList(\n shiny::fluidPage(\n shiny::titlePanel(\n shiny::div(\n shiny::img(\n src = \"www/shiny.png\",\n height = 60,\n width = 55,\n style = \"margin:10px 10px\"\n ), \n \"Movies Reviews\"\n )\n ),\n shiny::sidebarLayout(\n shiny::sidebarPanel(\n mod_var_input_ui(\"vars\")\n ),\n shiny::mainPanel(\n mod_scatter_display_ui(\"plot\")\n )\n )\n )\n )\n} \n\nAfter loading, documenting, and installing, the application now includes the image file.\nCtrl/Cmd + Shift + L / D / B\n\nlibrary(pkgApp)\nmovies_app()\n\n\n\n\n\n(a) movies_app() with logo\n\nFigure 2: inst/www accessible with addResourcePath()\n\n\nYou can read more about adding external resources in the documentation for addResourcePath(). We’ll also cover adding CSS and JavaScript files in later chapters.\n\n\n\n\nNew Git Branch\n\nThe code for this section is in the [05_inst] branch of the [pkgApp] repo." }, { "objectID": "inst_www.html#recap", @@ -354,14 +361,14 @@ "href": "tests.html#running-test", "title": "Tests", "section": "Running test()\n", - "text": "Running test()\n\n\n\n\n\n\n\nGitHub [pkgApp]:[06-tests]\n\n\n\n\n\n\nThis shiny app project can be found in the 06-tests branch of the pkgApp repo.\nThis branch was created from the 05-inst branch using:\ngit checkout -B 06-tests\ngit push -u origin 06-tests\n\n\n\n\nThe fourth devtools habit we want to adopt is regularly writing and running tests. We can test our package using the Build pane or the keyboard shortcut:\n\n\nCtrl/Cmd + Shift + T\n\n\n\n(a) Test you app-package\n\nFigure 1: devtools::test() (run all tests in your tests/ folder)\n\nWhen we initally run devtools::test() in pkgApp, we see the following:\n==> devtools::test()\n\nℹ No testing infrastructure found.\n• Setup testing with `usethis::use_testthat()`.\nThis shouldn’t be surprising, because we haven’t written any tests for pkgApp yet!. The error is informative, though, because it tells us pkgApp doesn’t have the testing infrastructure set up. In R packages using\nIn the next section we’ll cover setting up the test suite in your app-package.\n\n\n\n\n\n\nMastering Shiny monthApp tests\n\n\n\n\n\n\nIf you downloaded, loaded and installed monthApp example from Mastering Shiny, then clicked on Test in the Build pane, you also saw the following:\n\n\nTesting monthApp app-package" + "text": "Running test()\n\nThe fourth devtools habit to adopt is regularly writing and running tests. If you’re using Posit Workbench and have devtools installed, you can test our package using the Build pane or the keyboard shortcut:\n\n\nCtrl/Cmd + Shift + T\n\n\n\n(a) Test you app-package\n\nFigure 1: devtools::test() (run all tests in your tests/ folder)\n\nWhen we initally run devtools::test() in pkgApp, we see the following:\n==> devtools::test()\n\nℹ No testing infrastructure found.\n• Setup testing with `usethis::use_testthat()`.\nThis shouldn’t be surprising, because we haven’t written any tests for pkgApp yet!. The error is informative, though, because it tells us pkgApp doesn’t have the testing infrastructure set up. In R packages using\nIn the next section we’ll cover setting up the test suite in your app-package.\n\n\n\n\n\n\nMastering Shiny monthApp tests\n\n\n\n\n\n\nIf you downloaded, loaded and installed monthApp example from Mastering Shiny, then clicked on Test in the Build pane, you also saw the following:\n\n\nTesting monthApp app-package" }, { "objectID": "tests.html#your-app-package-test-suite", "href": "tests.html#your-app-package-test-suite", "title": "Tests", "section": "Your app-package test suite", - "text": "Your app-package test suite\nIf you adopt test-driven development, you’ll develop tests before writing any utility functions, modules, or standalone app function. However, if you’re a mere mortal like the rest of us, you’ll typically develop your tests and functions in tandem.\nRegardless of the testing strategy, we’ll follow the advice in the output above and set up the testing infrastructure with the testthat package:\nuse_testthat()\nThe ‘infrastructure’ created by running usethis::use_testthat() is detailed below:\n\nusethis::use_testthat()\n\n\n\nSet active project to current working directory:\n✔ Setting active project to '/Users/mjfrigaard/projects/pkgs/apps/pkgApp'\n\n\nIn the DESCRIPTION file, add the Suggests field and include testthat (>= 3.0.0) and the testthat edition (Config/testthat/edition: 3)\n✔ Adding 'testthat' to Suggests field in DESCRIPTION\n✔ Adding '3' to Config/testthat/edition\n\n\nA new tests/ folder is created, with a testthat/ subfolder:\n✔ Creating 'tests/testthat/'\n\n\nThe testthat.R file is created (sometimes referred to as the test ‘runner’ because it runs all your tests).\n✔ Writing 'tests/testthat.R'\n\n\nFinally, we’re given some advice on the next step for creating our first test:\n• Call `use_test()` to initialize a basic test file and open it for editing.\nOur new tests/ folder structure is below:\ntests/\n ├── testthat\n └── testthat.R\n\n2 directories, 1 file" + "text": "Your app-package test suite\nIf you adopt test-driven development, you’ll develop tests before writing any utility functions, modules, or standalone app function. However, if you’re a mere mortal like the rest of us, you’ll typically develop your tests and functions in tandem.\nRegardless of the testing strategy, we’ll follow the advice in the output above and set up the testing infrastructure with the testthat package:\nuse_testthat()\nThe ‘infrastructure’ created by running usethis::use_testthat() is detailed below:\n\nusethis::use_testthat()\n\n\n\nSet active project to current working directory:\n✔ Setting active project to '/path/to/pkgApp'\n\n\nIn the DESCRIPTION file, add the Suggests field and include testthat (>= 3.0.0) and the testthat edition (Config/testthat/edition: 3)\n✔ Adding 'testthat' to Suggests field in DESCRIPTION\n✔ Adding '3' to Config/testthat/edition\n\n\nA new tests/ folder is created, with a testthat/ subfolder:\n✔ Creating 'tests/testthat/'\n\n\nThe testthat.R file is created (sometimes referred to as the test ‘runner’ because it runs all your tests).\n✔ Writing 'tests/testthat.R'\n\n\nFinally, we’re given some advice on the next step for creating our first test:\n• Call `use_test()` to initialize a basic test file and open it for editing.\nOur new tests/ folder structure is below:\ntests/\n ├── testthat\n └── testthat.R\n\n2 directories, 1 file" }, { "objectID": "tests.html#unit-tests", @@ -382,7 +389,7 @@ "href": "tests.html#end-to-end-tests", "title": "Tests", "section": "End-to-end tests", - "text": "End-to-end tests\nshinytest2\nend testing.qmd" + "text": "End-to-end tests\nshinytest2\n\n\n\n\nNew Git Branch\n\nThe code for this section is in the [06_tests] branch of the [pkgApp] repo.\nend testing.qmd" }, { "objectID": "run.html#app-package-contents", @@ -462,11 +469,25 @@ "text": "sinew\nThe sinew package also warrants mentioning because it can help ensure you’re namespacing functions from add-on packages.\nend code_tools.qmd" }, { - "objectID": "glossary.html", - "href": "glossary.html", + "objectID": "glossary.html#shiny-apps", + "href": "glossary.html#shiny-apps", "title": "Glossary of terms", - "section": "", - "text": "ALERT!\n\n\n\n\n\n\nThis section is currently under development. Thank you for your patience.\n\n\n\n\n\n\n\nend glossary.qmd" + "section": "Shiny apps", + "text": "Shiny apps\nUtility/helper functions\nExternal resources\nModule\nStandalone app function" + }, + { + "objectID": "glossary.html#packages", + "href": "glossary.html#packages", + "title": "Glossary of terms", + "section": "Packages", + "text": "Packages\nProject\nAny directory of R executable files with an RStudio/Posit workbench project file (.Rproj).\nPackage\nA directory of functions, documentation, and/or data can be installed and loaded into an R session. Packages include the necessary dependency management (NAMESPACE) and metadata files (DESCRIPTION) and has access to the package development tools in RStudio/Posit Workbench.\nApp-package\nAn app-package is a package containing a shiny application. App-packages have all of the functionality of a standard R package, but also contain the files and folders required to successfully develop, run, and deploy a shiny application." + }, + { + "objectID": "glossary.html#devtools", + "href": "glossary.html#devtools", + "title": "Glossary of terms", + "section": "devtools", + "text": "devtools\nend glossary.qmd" }, { "objectID": "github.html#projapp", @@ -474,5 +495,12 @@ "title": "GitHub", "section": "projApp", "text": "projApp\nThe projApp repo is a shiny application built outside of a package structure. It was initially created using the New Project Wizard (with a Git repo initialized).\nIf you’re new to GitHub, the steps below will walk you through setting up Git and GitHub from RStudio using the Git pane. The initial files in the project are below:\nprojApp/\n ├── app.R\n └── projApp.Rproj\n\n1 directory, 2 files\nAfter creating the project, head over to GitHub and create am empty repo. You’ll see the following options:\n\n\n\n\n(a) New repository on GitHub\n\nFigure 1: Empty GitHub repository options\n\n\nWe’re interested in the second option, “…push an existing repository from the command line”, but we’re going to use Posit Workbench’s Git pane.\nCommit\nCommit these initial changes to the repo using the Commit icon in the Git pane\n\n\n\n(a) Commit changes\n\n\n\n(b) First commit\n\nFigure 2: Click on Commit to open the Git window. Stage all the files, add a commit message, and click Commit\n\nAdd a commit message and click Commit. This is the equivalent of entering the following commands in the Terminal:\ngit commit -m 'first commit'\n\n\n\n\n\n\nCommit changes in your repository with a specific message\n\n\n\n\n\n\ngit commit -m 'first commit'\n\ngit commit: This command captures a snapshot of the changes in your project’s tracked files and directories. By committing, you’re saving the current state of those files in the Git repository.\n-m: This flag indicates that a commit message will be provided directly from the command line.\n'first commit': This is the commit message associated with this commit. Commit messages are useful for documenting the changes you’ve made, making it easier for others (and your future self) to understand the evolution and purpose of changes in the project.\n\nWhen you run git commit -m 'first commit', you are committing your staged changes (i.e., changes you’ve previously added to the staging area using git add) with the message ‘first commit’.\nThis message is then stored in the Git history, allowing anyone who looks at the commit logs to see a brief description of what was done in that particular commit.\n\n\n\n\nReview the output from the commit.\n\n\n\n(a) First commit output\n\nFigure 3: The .gitignore, app.R, and projApp.Rproj files have been committed to main\n\nThis shows the contents of projApp are part of the main branch. Now we need to make sure the local branch has a remote (on GitHub at https://github.com/<username>/projApp.git).\n\n\n\n\n\n\nRename the currently checked-out branch to main\n\n\n\n\n\n\nThe command git branch -M main is used to rename the currently checked-out branch to main.\nHere’s a breakdown of the command:\n\ngit branch: This command without any arguments would list all the local branches in the current repository. But, with certain options (like -M), you can perform other branch-related operations.\n-M: This option stands for ‘move/rename’ and forcibly renames the branch. If a branch named main already exists, it will be overwritten because of the forceful nature of the -M option. If you want to avoid accidentally overwriting an existing branch, you could use -m (lowercase) instead. The lowercase -m will rename only if the target name doesn’t already exist.\nmain: This is the new name for the currently checked-out branch.\n\nThe command became more common after the industry started transitioning from using master to main as the default branch name for new repositories. Running git branch -M main is a way to rename the master branch to main in existing repositories.\n\n\n\n\nAdding remotes\nClick the New Branch icon in the Git pane and create a new main branch. Then click on Add Remote… and name the remote origin.\n\n\n\n(a) Add branch and remote name\n\n\n\n(b) Add remote URL\n\nFigure 4: Create new main branch to track origin\n\nThe Remote URL is the link from the Quick Setup above. After clicking Add and Create, you’ll be asked to checkout or overwrite the existing main branch. In this case, we can select Overwrite (because we’re already on the main branch).\n\n\n\n\n\n\nAdd a new remote to your Git repository\n\n\n\n\n\n\ngit remote add origin https://github.com/<username>/<repo>.git\n\ngit remote: used to manage and set remotes (‘remote repositories’) for your project\nadd: specifies that you want to add a new remote.\norigin: a convention widely used in the Git community is to name the primary remote repository origin.\nhttps://github.com/<username>/<repo>.git: This is the URL to the Git repository (hosted on GitHub). Replace <username> with the GitHub username of the repository owner and <repo> with the name of the repository.\n\nSo, when you run this command, you’re telling Git: ‘I want to add a new remote called origin, and the URL for this remote is https://github.com/<username>/<repo>.git'.’\nAfter executing this command, you can then push to and pull from the repository using this remote by referring to its name (origin). For instance, git push origin master would push your local master branch to the master branch on the origin remote.\n\n\n\n\n\n\n\n(a) Overwrite main\n\n\n\n(b) branch main set up to track origin/main\n\nFigure 5: main will now track the remote (origin)\n\n\n\n\n\n\n\nPush a local branch (main) to a remote (origin), and set local branch to track remote branch\n\n\n\n\n\n\ngit push -u origin main\n\ngit push: used to push commits from your local repository to a remote repository.\norigin: name of the remote repository you want to push to. When you clone a repo or add a remote using git remote add, it’s common to name the main remote origin (though it could technically be any name).\nmain: name of the branch you’re pushing to the remote repository.\n-u or --set-upstream: When this option is used, it sets a tracking relationship between the local and upstream remote branches. This means that in the future, using git pull or git push doesn’t require specifying the remote or branch (Git will know you’re referring to the origin/main branch).\n\nIn essence, git push -u origin main is saying: ‘Push my ’main’ branch to the ‘origin’ remote, and also set my local ‘main’ branch to track the ‘main’ branch on ‘origin’.\nThe common workflow for setting up Git from the command line is below:\n# make changes \ngit add .\ngit commit 'first commit'\n# set remote on GitHub\ngit remote add origin https://github.com/<username>/<repo>.git\n# rename the current branch to main\ngit branch -M main\n# push and set upstream to origin (remote)\ngit push -u origin main" + }, + { + "objectID": "create.html#creating-app-packages-scenarios", + "href": "create.html#creating-app-packages-scenarios", + "title": "Appendix: Creating packages in Posit Workbench", + "section": "Creating app-packages: scenarios", + "text": "Creating app-packages: scenarios\nThe callout blocks below represent scenarios I’ve encountered requiring an app-package:\nScenario #1\n\n\n\n\n\n\nScenario #1: Create a new shiny app-package\n\n\n\n\n\n\nYou’re in the ‘brainstorming’ stages of app development, and no code (or very little code) has been written. Maybe you’ve investigated using a framework (like golem or rhino) but can’t decide if their features will fit your needs.\n\n\n\n\nIn this case, you’re in the perfect position to create a new package with usethis::create_package() or devtools::create(). If you’re using Posit Workbench, do File > New Project > New Directory > R Package.\nScenario #2\n\n\n\n\n\n\nScenario #2: Convert a working app project into an app-package\n\n\n\n\n\n\nThe app is currently deployed and is being accessed by users, but the underlying code lives in the ‘root’ folder (i.e., an app.R or ui.R and server.R, global.R, helper.R, and/or other .R files, data, static resources, etc.). The application works, but you’d like to convert it to a package structure without breaking it.\n\n\n\n\nThis is a circumstance where I’d manually create the DESCRIPTION file (or use usethis::use_description()) and set the package-level build tools (Tools > Project options > Build Tools > Project build tools). Then I’d begin organizing the shiny app files into a package structure (i.e., move .R files into the R/ folder, data files into the data/ folder, etc.)\nScenario #3\n\n\n\n\n\n\nScenario #3: Convert a legacy app project into a shiny app-package\n\n\n\n\n\n\nThe app was written some time ago, used superseded or deprecated functions (i.e., callModule()), and needs updating. Updating the apps might include adding data or static resources, writing utility functions and modules, etc.\nThe critical distinction is that this application is not working and requires updates.\n\n\n\n\nScenario #4\n\n\n\n\n\n\nScenario #4: Convert your ‘dev’ app project into an app-package\n\n\n\n\n\n\nYou’re currently developing an app project, and you read somewhere it should be an app-package; however, you can’t find clear instructions on how to accomplish this incrementally.\nAnd maybe you’re a minimalist (like me) who likes to have complete control over every aspect of the development process." } ] \ No newline at end of file diff --git a/shiny.html b/shiny.html index 1f4f7f4..3dd3d1e 100644 --- a/shiny.html +++ b/shiny.html @@ -296,6 +296,12 @@ GitHub
    + + @@ -434,7 +440,7 @@

    Shiny apps

    Figure 2: New shiny app project in a Git repository
    -

    After clicking Create Project, a new RStudio session will open with your project files.

    +

    After clicking Create Project, a new session will open with your project files.

    Shiny app project contents

    Note that the only items in the new shiny app project are app.R and the projApp.Rproj file.

    projApp/
    diff --git a/sitemap.xml b/sitemap.xml
    index d1f68e1..be8d8c0 100644
    --- a/sitemap.xml
    +++ b/sitemap.xml
    @@ -2,94 +2,98 @@
     
       
         https://mjfrigaard.github.io/shinyap/index.html
    -    2023-08-18T04:15:20.708Z
    +    2023-08-18T21:16:41.974Z
       
       
         https://mjfrigaard.github.io/shinyap/intro.html
    -    2023-08-18T04:15:20.717Z
    +    2023-08-18T21:16:41.984Z
       
       
         https://mjfrigaard.github.io/shinyap/shiny.html
    -    2023-08-18T04:15:20.798Z
    +    2023-08-18T21:16:42.083Z
       
       
         https://mjfrigaard.github.io/shinyap/packages.html
    -    2023-08-18T04:15:20.842Z
    +    2023-08-18T21:16:42.140Z
       
       
         https://mjfrigaard.github.io/shinyap/devtools.html
    -    2023-08-18T04:15:20.870Z
    +    2023-08-18T21:16:42.180Z
       
       
         https://mjfrigaard.github.io/shinyap/app_packages.html
    -    2023-08-18T04:15:20.887Z
    +    2023-08-18T21:16:42.193Z
       
       
         https://mjfrigaard.github.io/shinyap/roxygen2.html
    -    2023-08-18T04:15:20.937Z
    +    2023-08-18T21:16:42.270Z
       
       
         https://mjfrigaard.github.io/shinyap/dependencies.html
    -    2023-08-18T04:15:20.972Z
    +    2023-08-18T21:16:42.307Z
       
       
         https://mjfrigaard.github.io/shinyap/data.html
    -    2023-08-18T04:15:21.015Z
    +    2023-08-18T21:16:42.355Z
       
       
         https://mjfrigaard.github.io/shinyap/inst_www.html
    -    2023-08-18T04:15:21.045Z
    +    2023-08-18T21:16:42.392Z
       
       
         https://mjfrigaard.github.io/shinyap/tests.html
    -    2023-08-18T04:15:21.062Z
    +    2023-08-18T21:16:42.408Z
       
       
         https://mjfrigaard.github.io/shinyap/run.html
    -    2023-08-18T04:15:21.071Z
    +    2023-08-18T21:16:42.419Z
       
       
         https://mjfrigaard.github.io/shinyap/frameworks.html
    -    2023-08-18T04:15:21.078Z
    +    2023-08-18T21:16:42.429Z
       
       
         https://mjfrigaard.github.io/shinyap/golem.html
    -    2023-08-18T04:15:21.089Z
    +    2023-08-18T21:16:42.437Z
       
       
         https://mjfrigaard.github.io/shinyap/leprechaun.html
    -    2023-08-18T04:15:21.096Z
    +    2023-08-18T21:16:42.445Z
       
       
         https://mjfrigaard.github.io/shinyap/rhino.html
    -    2023-08-18T04:15:21.104Z
    +    2023-08-18T21:16:42.453Z
       
       
         https://mjfrigaard.github.io/shinyap/special_topics.html
    -    2023-08-18T04:15:21.111Z
    +    2023-08-18T21:16:42.460Z
       
       
         https://mjfrigaard.github.io/shinyap/debugging.html
    -    2023-08-18T04:15:21.118Z
    +    2023-08-18T21:16:42.468Z
       
       
         https://mjfrigaard.github.io/shinyap/css.html
    -    2023-08-18T04:15:21.125Z
    +    2023-08-18T21:16:42.476Z
       
       
         https://mjfrigaard.github.io/shinyap/js.html
    -    2023-08-18T04:15:21.132Z
    +    2023-08-18T21:16:42.483Z
       
       
         https://mjfrigaard.github.io/shinyap/code_tools.html
    -    2023-08-18T04:15:21.144Z
    +    2023-08-18T21:16:42.495Z
       
       
         https://mjfrigaard.github.io/shinyap/glossary.html
    -    2023-08-18T04:15:21.151Z
    +    2023-08-18T21:16:42.504Z
       
       
         https://mjfrigaard.github.io/shinyap/github.html
    -    2023-08-18T04:15:21.165Z
    +    2023-08-18T21:16:42.519Z
    +  
    +  
    +    https://mjfrigaard.github.io/shinyap/create.html
    +    2023-08-18T21:16:42.533Z
       
     
    diff --git a/special_topics.html b/special_topics.html
    index cec098c..6698acf 100644
    --- a/special_topics.html
    +++ b/special_topics.html
    @@ -262,6 +262,12 @@
       
      GitHub
       
    + + diff --git a/tests.html b/tests.html index 8d98bf5..fa8ec8d 100644 --- a/tests.html +++ b/tests.html @@ -296,6 +296,12 @@ GitHub + + @@ -375,15 +381,15 @@

    Tests

    -

    Writing tests for your app-package poses some unique challenges. As we covered in the first chapter, shiny applications are reactive, so some of the conventional testing techniques and methods for regular R packages don’t directly apply. Fortunately, the infrastructure for storing and running tests in your app-package is identical to a standard R package.

    -

    This chapter will cover three layers of testing (unit testing, integration/module testing, and end-to-end testing), a summary of the recommendations for testing shiny apps, and what you should focus on testing in your shiny app-package.

    -
    +

    Writing tests for your app-package poses some unique challenges. As we covered in the first chapter, shiny applications are reactive, so some of the conventional testing techniques and methods for regular R packages don’t directly apply. Fortunately, the infrastructure for storing and running tests in your app-package is identical to a standard R package.

    +

    This chapter will cover three layers of testing (unit testing, integration/module testing, and end-to-end testing), a summary of the recommendations for testing shiny apps, and where you should focus your tests.

    +
    -Disclaimer +Testing disclaimer
    @@ -407,28 +413,7 @@

    Tests

    (If you’re using devtools, you won’t have to worry about installing testthat and covr)

    Running test()

    -
    -
    -
    - -
    -
    -GitHub [pkgApp]:[06-tests] -
    -
    -
    -
    -
    -
    -

    This shiny app project can be found in the 06-tests branch of the pkgApp repo.

    -

    This branch was created from the 05-inst branch using:

    -
    git checkout -B 06-tests
    -git push -u origin 06-tests
    -
    -
    -
    -
    -

    The fourth devtools habit we want to adopt is regularly writing and running tests. We can test our package using the Build pane or the keyboard shortcut:

    +

    The fourth devtools habit to adopt is regularly writing and running tests. If you’re using Posit Workbench and have devtools installed, you can test our package using the Build pane or the keyboard shortcut:

    Ctrl/Cmd + Shift + T

    @@ -440,14 +425,14 @@

    Tests

    Figure 1: devtools::test() (run all tests in your tests/ folder)

    When we initally run devtools::test() in pkgApp, we see the following:

    -
    ==> devtools::test()
    -
    -ℹ No testing infrastructure found.
    -• Setup testing with `usethis::use_testthat()`.
    +
    ==> devtools::test()
    +
    +ℹ No testing infrastructure found.
    +• Setup testing with `usethis::use_testthat()`.

    This shouldn’t be surprising, because we haven’t written any tests for pkgApp yet!. The error is informative, though, because it tells us pkgApp doesn’t have the testing infrastructure set up. In R packages using

    In the next section we’ll cover setting up the test suite in your app-package.

    - -
    +

    If you downloaded, loaded and installed monthApp example from Mastering Shiny, then clicked on Test in the Build pane, you also saw the following:

    @@ -474,48 +459,48 @@

    Tests

    use_testthat()

    The ‘infrastructure’ created by running usethis::use_testthat() is detailed below:

    -
    usethis::use_testthat()
    +
    usethis::use_testthat()
    • Set active project to current working directory:

      -
      ✔ Setting active project to '/Users/mjfrigaard/projects/pkgs/apps/pkgApp'
      +
      ✔ Setting active project to '/path/to/pkgApp'
    • In the DESCRIPTION file, add the Suggests field and include testthat (>= 3.0.0) and the testthat edition (Config/testthat/edition: 3)

      -
      ✔ Adding 'testthat' to Suggests field in DESCRIPTION
      -✔ Adding '3' to Config/testthat/edition
      +
      ✔ Adding 'testthat' to Suggests field in DESCRIPTION
      +✔ Adding '3' to Config/testthat/edition
    • A new tests/ folder is created, with a testthat/ subfolder:

      -
      ✔ Creating 'tests/testthat/'
      +
      ✔ Creating 'tests/testthat/'
    • The testthat.R file is created (sometimes referred to as the test ‘runner’ because it runs all your tests).

      -
      ✔ Writing 'tests/testthat.R'
      +
      ✔ Writing 'tests/testthat.R'

    Finally, we’re given some advice on the next step for creating our first test:

    -
    • Call `use_test()` to initialize a basic test file and open it for editing.
    +
    • Call `use_test()` to initialize a basic test file and open it for editing.

    Our new tests/ folder structure is below:

    -
    tests/
    -  ├── testthat
    -  └── testthat.R
    -
    -2 directories, 1 file
    +
    tests/
    +  ├── testthat
    +  └── testthat.R
    +
    +2 directories, 1 file

    Unit tests

    If I’m writing write a unit test for the scatter_plot() function in R/scatter_plot.R, I’ll create test file with usethis::use_test("scatter_plot").

    New tests with use_test()

    -
    usethis::use_test("scatter_plot")
    +
    usethis::use_test("scatter_plot")

    The IDE will automatically open the new test file:

    -
    ✔ Writing 'tests/testthat/test-scatter_plot.R'
    -• Modify 'tests/testthat/test-scatter_plot.R'
    +
    ✔ Writing 'tests/testthat/test-scatter_plot.R'
    +• Modify 'tests/testthat/test-scatter_plot.R'

    The new file contains boilerplate test below (I’ve added the testthat namespace and arguments):

    -
    testthat::test_that(desc = "multiplication works", code = {
    +
    testthat::test_that(desc = "multiplication works", code = {
       testthat::expect_equal(object = 2 * 2, expected = 4)
     })
    @@ -528,9 +513,16 @@

    Tests

    Module tests involve both UI and server functions, I consider these integration tests (i.e., we’re testing how a module’s functions ‘integrate’ with each other, or with other modules).

    testServer()

    testServer(args = list())

    -

    End-to-end tests

    -

    shinytest2

    -

    end testing.qmd

    +

    End-to-end tests

    +

    shinytest2

    + +
    +
    +

    +
    New Git Branch
    +
    +

    The code for this section is in the [06_tests] branch of the [pkgApp] repo.

    +

    end testing.qmd