23  leprechaun


  • leprechaun has many of the same features found in the golem framework, but without the added dependencies (think golem à la carte)

  • Helper functions for creating modules, app.R files, JavaScript, CSS, SCSS, HTML, etc.

  • leprechaun apps also come ‘pre-packaged’ with UI, server, and standalone app functions

  • Additional features and functionality are added with a variety of use_* functions ‘bundle’ resources in the inst/ folder:

    • use_sass(), use_config(), use_packer() (with use_js_utils()), etc.

This chapter walks through building a version of the moviesApp with the leprechaun framework. The resulting app-package (lap) is in the 23_leprechaun branch.

install.packages("leprechaun")
library(leprechaun)

After checking out the 23_leprechaun branch, be sure to load, document, and install the application.



Ctrl/Cmd + Shift + L / D / B

23.1 lap (a leprechaun app-package)

lap exports the movies data and the standalone app function, run().

library(lap)
lap::run()

Launch app with the shinypak package:

launch('23_leprechaun')
(a) lap movies app
Figure 23.1: After loading, documenting, and installing lap, launch the movies with run()

I’ve created the shinypak R package In an effort to make each section accessible and easy to follow:

Install shinypak using pak (or remotes):

# install.packages('pak')
pak::pak('mjfrigaard/shinypak')

Review the chapters in each section:

library(shinypak)
list_apps(regex = 'leprechaun')
## # A tibble: 1 × 2
##   branch        last_updated       
##   <chr>         <dttm>             
## 1 23_leprechaun 2024-01-18 10:59:53

Launch the app:

launch(app = "23_leprechaun")

Download the app:

get_app(app = "23_leprechaun")

23.2 Set up

Creating a new leprechaun app-package can be done with usethis::create_package()1 (which we covered in Packages). After your app-package is created, leprechaun::scaffold() builds the core app files:2

Code files: the code files in new leprechaun apps include the following:

  • _disable_autoload.R: Disables shiny::loadSupport() (covered in Launch)

  • assets.R: includes serveAssets() for serving JavaScript files, remove_modules() and get_modules() for adding/removing modules, and the collapse_files() helper function for collapsing files.3

  • input-handlers.R: leprechaun_handler_df() and leprechaun_handler_list() are helper functions for including data and lists “from Websocket”.4

  • leprechaun-utils.R: includes a make_send_message() function for “send custom messages to the front-end” with the “namespace carried along”. Read below for demonstration of how this is used.5

  • run.R: the standalone app function for your leprechaun app (covered in Launch).

  • server.R & ui.R are applications primary UI and server functions.

    • ui: contains the application UI and the assets() function (which calls serveAssets() covered above).6
    • server.R: creates send_message from make_send_message(session) (covered below).7
  • zzz.R is a utility function file that holds the .onLoad() function for adding external resources (using methods covered in External files).8

R
├── _disable_autoload.R
├── assets.R
├── input-handlers.R
├── leprechaun-utils.R
├── run.R
├── server.R
├── ui.R
└── zzz.R

1 directory, 8 files
1
Disables shiny::loadSupport()
2
Includes functions for serving JavaScript files, adding/removing modules, and collapsing files.
3
Utility functions for handling lists and data.frames
4
Contains the make_send_message() function for ‘send[ing] custom messages to the front-end’
5
Standalone app function
6
App primary server function
7
App primary ui function
8
Includes wrapper for adding external files
  • Adding dependencies: shiny, bslib, htmltools and pkgload should be added to the DESCRIPTION with usethis::use_package() (covered in the Dependencies chapter).
Imports: 
    bslib,
    htmltools,
    shiny
Suggests: 
    pkgload

The lap folder structure should look familiar if you’ve been following along with the previous chapters. The standard R package files (DESCRIPTION, NAMESPACE, and .Rproj) are accompanied by a .leprechaun lock file.

lap/
├── .leprechaun
├── DESCRIPTION
├── NAMESPACE
└── moviesApp.Rproj

The initial call to leprechaun::scaffold() creates the following folders in the inst/ folder (recall that inst/ contents are available when the package is installed).

inst/
├── assets
├── dev
├── img
└── run
    └── app.R

5 directories, 1 file

inst/run/app.R contains a call to leprechaun::build(), pkgload::load_all(), then a call to the standalone app function, run().9

23.3 Development

Code files: new code files in leprechaun apps can be created with usethis::use_r() or with a helper function:

  • Create modules with add_module(). Modules created with add_module("name") will have:10
    • A module file with a module_name prefix (R/module_name.R)
    • A module UI function: nameUI()
    • A module server function: name_server()

  • Create an app.R file with add_app_file(). This includes a call to pkgload::load_all() and run().11

  • App files: R/run.R contains functions for running the app.12

  • Add the modules to R/ui.R and R/server.R.

    - R/server.R includes a call to send_message by default.13

    - R/ui.R holds the ui() and assets() functions.14

  • Utility function: R/utils_scatter_plot.R holds the scatter_plot() utility function.15

  • Adding files: The R/zzz.R file contains the .onLoad() function, which wraps system.file('img', package = 'lap') and addResourcePath() for including external resources.

R
├── module_plot_display.R
├── module_var_input.R
├── run.R
├── server.R
├── ui.R
└── utils_scatter_plot.R

1 directory, 6 files

23.3.1 Data files

  • Including data files: the movies.RData data was moved into inst/extdata, then loaded into data/ with the script created with usethis::use_data_raw('movies') (similar to methods covered in the Data chapter):
data-raw/
└── movies.R

1 directory, 1 file
└── extdata
      └── movies.RData
1 directory, 1 file
data
└── movies.rda

1 directory, 1 file

23.3.2 Adding features

The leprechaun workflow involves a combination of use_ functions that are combined with leprechaun::build() to add various functionality to your application. I’ll demonstrate three options in the sections below:

23.3.2.1 Use packer

I’ll demo using the make_send_message() from the JavaScript example on the package website:

  • Run packer::scaffold_leprechaun()

  • Run leprechaun::use_packer()

  • Run leprechaun::build()

This creates inst/dev/packer.R and inst/assets/index.js.

inst
├── assets
   └── index.js
├── dev
   └── packer.R
├── extdata
   └── movies.RData
├── img
└── run
    └── app.R

Assign the output from make_send_message() to send_message() in R/server.R, then pass the msgId and text of the message:

server <- function(input, output, session){
    ## New code -->
  send_message <- make_send_message(session)
  
    send_message("show-packer",
                  text = "this message is from your R/server.R file")
    
   selected_vars <- var_input_server("vars")

   plot_display_server("plot", var_inputs = selected_vars)
   ## New code <--

}
1
Create send_message()
2
Use send_message() to send message the UI.

After loading, documenting, and installing your app-package:



Ctrl/Cmd + Shift + L / D / B

Run the application:

lap::run()
(a) send_message() in lap::run()
Figure 23.2: Adding the make_send_message() functionality to R/server.R

23.3.2.2 Add images

Adding images is simplified with the .onLoad() function, which I’ll demonstrate by adding the leprechaun.jpg image file to the UI function.

  • Place the leprechaun.jpg file in inst/img/

  • Add the img/ path to the code to UI:
tags$img(
  src = "img/leprechaun.jpg", 
  height = "25%", 
  width = "25%")
  • run devtools::load_all(), devtools::document(), and devtools::install(), then run the application with run():



Ctrl/Cmd + Shift + L / D / B

lap::run()
(a) leprechaun.jpg in R/ui.R
Figure 23.3: Adding images to inst/img/

23.3.2.3 Use Sass

To add Sass styling, I can use leprechaun’s use_sass() function

  • Run leprechaun::use_sass()

  • a scss/ folder will be created that contains _core.scss and main.scss
scss
├── _core.scss
└── main.scss

1 directory, 2 files
  • The original _core.scss file is below
html{
    .error {
        color: red
    }
}
  • Change the color: from red to green (#38B44A) using $accent: #38B44A;
$accent: #38B44A;

html{
    h1 {
        color: $accent;
    }
}
leprechaun::build()
 Running packer.R
 Bundled       
 Running sass.R
  • Once again, run devtools::load_all(), devtools::document(), and devtools::install(), then run the application with run():



Ctrl/Cmd + Shift + L / D / B

lap::run()
Figure 23.4: Running lap with new Sass

23.3.2.4 New root folders

It’s important to note that adding features to your leprechaun app-package will add non-standard directories to the root folder:

├── node_modules/ <- too many sub-folders to list here!
├── package-lock.json
├── package.json
├── scss/
   ├── _core.scss
   └── main.scss
├── srcjs/
   ├── config
   ├── index.js
   └── modules
├── webpack.common.js
├── webpack.dev.js
└── webpack.prod.js

These folders are necessary for adding JavaScript/SCSS functionality, so don’t remove them (just make sure they’re added to the .Rbuildignore/.gitignore)

23.4 Tests

leprechaun doesn’t any specific support for testing (like the golem framework), but we can create tests using any combination of testthat, testServer(), and shinytest2.

23.5 lap dependencies

It’s also worth noting that using the leprechaun framework doesn’t add itself as a dependency:

# in the 23_leprechaun branch of moviesApp
pak::local_deps_explain(deps = 'leprechaun', root = ".")
x leprechaun  

Recap

RECAP  


leprechaun apps are built using the same methods as app-packages (devtools and usethis), and are intended to be a ‘leaner and smaller’ version of golem.

‘it generates code and does not make itself a dependency of the application you build; this means applications are leaner, and smaller’

leprechaun is similar to golem in that it ‘bundles’ various app-package development functions into helper/utility functions. For example, the .onLoad() function saves some time (at the cost of making your app code less clear and explicit).

leprechaun also relies on external packages like packer to integrate and bundle external code files, so becoming more familiar with these packages will extend what you can build with leprechaun.

Please open an issue on GitHub


  1. Remember to provide a list of fields to usethis::create_package() for the DESCRIPTION file (or edit this manually).↩︎

  2. leprechaun::scaffold() has arguments for ui (fluidPage or navbarPage) bs_version (bootstrap version) and overwrite (if you need to start over).↩︎

  3. Link to R/assets.R file.↩︎

  4. Link to R/input-handlers.R file.↩︎

  5. Link to R/leprechaun-utils.R file.↩︎

  6. Link to R/ui.R file.↩︎

  7. Link to R/server.R file.↩︎

  8. Link to R/zzz.R file.↩︎

  9. Link to inst/run/app.R file.↩︎

  10. leprechaun modules do not have the same naming conventions as golem (or that I’ve recommended throughout this book).↩︎

  11. pkgload::load_all() has reset set to TRUE and helpers set to FALSE.↩︎

  12. R/run.R includes functions for running the production (run()) and development (run_dev()) version of the application.↩︎

  13. make_send_message() is in R/leprechaun-utils.R.↩︎

  14. assets() loads the resources called in the R/assets.R file with the serveAssets() function.↩︎

  15. The same scatter_plot() function from moviesApp (i.e, imports .data from rlang)↩︎

  16. srcjs/, package-lock.json, package.json, webpack.common.js, webpack.dev.js and webpack.prod.js are added to the .Rbuildignore (and node_modules/ is added to the .gitignore).↩︎

  17. The scss folder in the root directory is used by leprechaun::build() to create inst/dev/sass.R.↩︎