Title: | A Framework for Enterprise Shiny Applications |
Version: | 1.11.0 |
Description: | A framework that supports creating and extending enterprise Shiny applications using best practices. |
URL: | https://appsilon.github.io/rhino/, https://github.com/Appsilon/rhino |
BugReports: | https://github.com/Appsilon/rhino/issues |
License: | LGPL-3 |
Encoding: | UTF-8 |
RoxygenNote: | 7.3.2 |
Depends: | R (≥ 2.10) |
Imports: | box (≥ 1.1.3), box.linters (≥ 0.10.5), box.lsp, callr, cli, config, fs, glue, lintr (≥ 3.0.0), logger, purrr, renv, rstudioapi, sass, shiny, styler, testthat (≥ 3.0.0), utils, withr, yaml |
Suggests: | covr, knitr, lifecycle, mockery, rcmdcheck, rex, rlang, rmarkdown, shiny.react, spelling |
LazyData: | true |
Config/testthat/edition: | 3 |
Config/testthat/parallel: | true |
Language: | en-US |
NeedsCompilation: | no |
Packaged: | 2025-04-02 07:15:43 UTC; kuba |
Author: | Kamil Żyła [aut, cre], Jakub Nowicki [aut], Leszek Siemiński [aut], Marek Rogala [aut], Recle Vibal [aut], Tymoteusz Makowski [aut], Rodrigo Basa [aut], Eduardo Almeida [ctb], Appsilon Sp. z o.o. [cph] |
Maintainer: | Kamil Żyła <opensource+kamil@appsilon.com> |
Repository: | CRAN |
Date/Publication: | 2025-04-02 07:30:02 UTC |
Destructure a named list into individual variables
Description
The destructuring operator %<-%
allows you to extract multiple named values from a list
into individual variables in a single assignment. This provides a convenient way to
unpack list elements by name.
While it works with any named list, it was primarily designed to improve the ergonomics of working with Shiny modules that return multiple reactive values. Instead of manually assigning each reactive value from a module's return list, you can destructure them all at once.
Usage
lhs %<-% rhs
Arguments
lhs |
A call to |
rhs |
A named list containing the values to assign |
Value
Invisibly returns the right-hand side list
Examples
# Basic destructuring
data <- list(x = 1, y = 2, z = 3)
c(x, y) %<-% data
x # 1
y # 2
# Works with unsorted names
result <- list(last = "Smith", first = "John")
c(first, last) %<-% result
# Shiny module example
if (interactive()) {
module_server <- function(id) {
shiny::moduleServer(id, function(input, output, session) {
list(
value = shiny::reactive(input$num),
text = shiny::reactive(input$txt)
)
})
}
# Clean extraction of reactive values
c(value, text) %<-% module_server("my_module")
}
# Can be used with pipe operations
# Note: The piped expression must be wrapped in brackets
## Not run:
c(value) %<-% (
123 |>
list(value = _)
)
## End(Not run)
Rhino application
Description
The entrypoint for a Rhino application.
Your app.R
should contain nothing but a call to rhino::app()
.
Usage
app()
Details
This function is a wrapper around shiny::shinyApp()
.
It reads rhino.yml
and performs some configuration steps (logger, static files, box modules).
You can run a Rhino application in typical fashion using shiny::runApp()
.
Rhino will load the app/main.R
file as a box module (box::use(app/main)
).
It should export two functions which take a single id
argument -
the ui
and server
of your top-level Shiny module.
Value
An object representing the app (can be passed to shiny::runApp()
).
Legacy entrypoint
It is possible to specify a different way to load your application
using the legacy_entrypoint
option in rhino.yml
:
-
app_dir
: Rhino will run the app usingshiny::shinyAppDir("app")
. -
source
: Rhino willsource("app/main.R")
. This file should define the top-levelui
andserver
objects to be passed toshinyApp()
. -
box_top_level
: Rhino will loadapp/main.R
as a box module (as it does by default), but the exportedui
andserver
objects will be considered as top-level.
The legacy_entrypoint
setting is useful when migrating an existing Shiny application to Rhino.
It is recommended to transform your application step by step:
With
app_dir
you should be able to run your application right away (just put the files in theapp
directory).With
source
setting your application structure must be brought closer to Rhino, but you can still uselibrary()
andsource()
functions.With
box_top_level
you can be confident that the whole app is properly modularized, as box modules can only load other box modules (library()
andsource()
won't work).The last step is to remove the
legacy_entrypoint
setting completely. Compared tobox_top_level
you'll need to make your top-levelui
andserver
into a Shiny module (functions taking a singleid
argument).
Examples
## Not run:
# Your `app.R` should contain nothing but this single call:
rhino::app()
## End(Not run)
Watch and automatically run R tests
Description
Watches R files in the app
directory and tests/testthat
directory for changes.
When code files in app
change, all tests are rerun. When test files change,
only the changed test file is rerun.
Usage
auto_test_r(reporter = NULL, filter = NULL, hash = TRUE)
Arguments
reporter |
|
filter |
filter passed to |
hash |
Logical. Whether to use file hashing to detect changes. Default is TRUE. If FALSE, file modification times are used instead. |
Value
None. This function is called for side effects.
Examples
if (interactive()) {
# Watch files and automatically run tests when changes are detected
auto_test_r()
}
Build JavaScript
Description
Builds the app/js/index.js
file into app/static/js/app.min.js
.
The code is transformed and bundled
using Babel and webpack,
so the latest JavaScript features can be used
(including ECMAScript 2015 aka ES6 and newer standards).
Requires Node.js to be available on the system.
Usage
build_js(watch = FALSE)
Arguments
watch |
Keep the process running and rebuilding JS whenever source files change. |
Details
Functions/objects defined in the global scope do not automatically become window
properties,
so the following JS code:
function sayHello() { alert('Hello!'); }
won't work as expected if used in R like this:
tags$button("Hello!", onclick = 'sayHello()');
Instead you should explicitly export functions:
export function sayHello() { alert('Hello!'); }
and access them via the global App
object:
tags$button("Hello!", onclick = "App.sayHello()")
Value
None. This function is called for side effects.
Examples
if (interactive()) {
# Build the `app/js/index.js` file into `app/static/js/app.min.js`.
build_js()
}
Build Sass
Description
Builds the app/styles/main.scss
file into app/static/css/app.min.css
.
Usage
build_sass(watch = FALSE)
Arguments
watch |
Keep the process running and rebuilding Sass whenever source files change.
Only supported for |
Details
The build method can be configured using the sass
option in rhino.yml
:
-
node
: Use Dart Sass (requires Node.js to be available on the system). -
r
: Use the{sass}
R package.
It is recommended to use Dart Sass which is the primary,
actively developed implementation of Sass.
On systems without Node.js you can use the {sass}
R package as a fallback.
It is not advised however, as it uses the deprecated
LibSass implementation.
Value
None. This function is called for side effects.
Examples
if (interactive()) {
# Build the `app/styles/main.scss` file into `app/static/css/app.min.css`.
build_sass()
}
Manage dependencies
Description
Install, remove or update the R package dependencies of your Rhino project.
Usage
pkg_install(packages)
pkg_remove(packages)
Arguments
packages |
Character vector of package names. |
Details
Use pkg_install()
to install or update a package to the latest version.
Use pkg_remove()
to remove a package.
These functions will install or remove packages from the local {renv}
library,
and update the dependencies.R
and renv.lock
files accordingly, all in one step.
The underlying {renv}
functions can still be called directly for advanced use cases.
See the Explanation: Renv configuration
to learn about the details of the setup used by Rhino.
Value
None. This functions are called for side effects.
Examples
## Not run:
# Install dplyr
rhino::pkg_install("dplyr")
# Update shiny to the latest version
rhino::pkg_install("shiny")
# Install a specific version of shiny
rhino::pkg_install("shiny@1.6.0")
# Install shiny.i18n package from GitHub
rhino::pkg_install("Appsilon/shiny.i18n")
# Install Biobase package from Bioconductor
rhino::pkg_install("bioc::Biobase")
# Install shiny from local source
rhino::pkg_install("~/path/to/shiny")
# Remove dplyr
rhino::pkg_remove("dplyr")
## End(Not run)
Development mode
Description
Run application in development mode with automatic rebuilding and reloading.
Usage
devmode(
build_sass = TRUE,
build_js = TRUE,
run_r_unit_tests = TRUE,
auto_test_r_args = list(reporter = NULL, filter = NULL, hash = TRUE),
...
)
Arguments
build_sass |
Boolean. Rebuild Sass automatically in the background? |
build_js |
Boolean. Rebuild JavaScript automatically in the background? |
run_r_unit_tests |
Boolean. Run R unit tests automatically in the background? |
auto_test_r_args |
List. Additional arguments passed to |
... |
Additional arguments passed to |
Details
This function will launch the Shiny app in
development mode
(as if options(shiny.devmode = TRUE)
was set).
The app will be automatically reloaded whenever the sources change.
Additionally, Rhino will automatically rebuild JavaScript and Sass in the background
and run R unit tests with the auto_test_r()
function.
Please note that this feature requires Node.js.
Value
None. This function is called for side effects.
Print diagnostics
Description
Prints information which can be useful for diagnosing issues with Rhino.
Usage
diagnostics()
Value
None. This function is called for side effects.
Examples
if (interactive()) {
# Print diagnostic information.
diagnostics()
}
Format JavaScript
Description
Runs prettier on JavaScript files in app/js
directory.
Requires Node.js installed.
Usage
format_js(fix = TRUE)
Arguments
fix |
If |
Details
You can prevent prettier from formatting a given chunk of your code by adding a special comment:
// prettier-ignore
Read more about ignoring code.
Value
None. This function is called for side effects.
Format R
Description
Uses the {styler}
and {box.linters}
packages to automatically format R sources. As with
styler
, carefully examine the results after running this function.
Usage
format_r(paths, exclude_files = NULL, ...)
Arguments
paths |
Character vector of files and directories to format. |
exclude_files |
Character vector with regular expressions of files that should be excluded from styling. |
... |
Optional arguments to pass to |
Details
The code is formatted according to the styler::tidyverse_style
guide with one adjustment:
spacing around math operators is not modified to avoid conflicts with box::use()
statements.
If available, box::use()
calls are reformatted by styling functions provided by
{box.linters}
. These include:
Separating
box::use()
calls for packages and local modulesAlphabetically sorting packages, modules, and functions.
Adding trailing commas
box.linters::style_*
functions require the treesitter
and treesitter.r
packages. These, in
turn, require R >= 4.3.0. format_r()
will continue to operate without these but will not
perform box::use()
call styling.
For more information on box::use()
call styling please refer to the {box.linters}
styling
functions
documentation.
Value
None. This function is called for side effects.
Examples
if (interactive()) {
# Format a single file.
format_r("app/main.R")
# Format all files in a directory.
format_r("app/view")
}
Format Sass
Description
Runs prettier on Sass (.scss) files in app/styles
directory.
Requires Node.js installed.
Usage
format_sass(fix = TRUE)
Arguments
fix |
If |
Details
You can prevent prettier from formatting a given chunk of your code by adding a special comment:
// prettier-ignore
Read more about ignoring code.
Value
None. This function is called for side effects.
Create Rhino application
Description
Generates the file structure of a Rhino application. Can be used to start a fresh project or to migrate an existing Shiny application created without Rhino.
Usage
init(
dir = ".",
github_actions_ci = TRUE,
rhino_version = "rhino",
force = FALSE
)
Arguments
dir |
Name of the directory to create application in. |
github_actions_ci |
Should the GitHub Actions CI be added? |
rhino_version |
When using an existing |
force |
Boolean; force initialization? By default, Rhino will refuse to initialize a project in the home directory. |
Details
The recommended steps for migrating an existing Shiny application to Rhino:
Put all app files in the
app
directory, so that it can be run withshiny::shinyAppDir("app")
(assuming all dependencies are installed).If you have a list of dependencies in form of
library()
calls, put them in thedependencies.R
file. If this file does not exist, Rhino will generate it based onrenv::dependencies("app")
.If your project uses
{renv}
, putrenv.lock
andrenv
directory in the project root. Rhino will try to only add the necessary dependencies to your lockfile.Run
rhino::init()
in the project root.
Value
None. This function is called for side effects.
Lint JavaScript
Description
Runs ESLint on the JavaScript sources in the app/js
directory.
Requires Node.js to be available on the system.
Usage
lint_js(fix = FALSE)
Arguments
fix |
Automatically fix problems. |
Details
If your JS code uses global objects defined by other JS libraries or R packages,
you'll need to let the linter know or it will complain about undefined objects.
For example, the {leaflet}
package defines a global object L
.
To access it without raising linter errors, add /* global L */
comment in your JS code.
You don't need to define Shiny
and $
as these global variables are defined by default.
If you find a particular ESLint error inapplicable to your code, you can disable a specific rule for the next line of code with a comment like:
// eslint-disable-next-line no-restricted-syntax
See the ESLint documentation for full details.
Value
None. This function is called for side effects.
Examples
if (interactive()) {
# Lint the JavaScript sources in the `app/js` directory.
lint_js()
}
Lint R
Description
Uses the {lintr}
package to check all R sources in the app
and tests/testthat
directories
for style errors.
Usage
lint_r(paths = NULL)
Arguments
paths |
Character vector of directories and files to lint.
When |
Details
The linter rules can be adjusted
in the .lintr
file.
You can set the maximum number of accepted style errors
with the legacy_max_lint_r_errors
option in rhino.yml
.
This can be useful when inheriting legacy code with multiple styling issues.
The box.linters::namespaced_function_calls()
linter requires the {treesitter}
and
{treesitter.r}
packages. These require R >= 4.3.0. lint_r()
will continue to run and skip
namespaced_function_calls()
if its dependencies are not available.
Value
None. This function is called for side effects.
Lint Sass
Description
Runs Stylelint on the Sass sources in the app/styles
directory.
Requires Node.js to be available on the system.
Usage
lint_sass(fix = FALSE)
Arguments
fix |
Automatically fix problems. |
Value
None. This function is called for side effects.
Examples
if (interactive()) {
# Lint the Sass sources in the `app/styles` directory.
lint_sass()
}
Logging functions
Description
Convenient way to log messages at a desired severity level.
Usage
log
Format
An object of class list
of length 7.
Details
The log
object is a list of logging functions, in order of decreasing severity:
-
fatal
-
error
-
warn
-
success
-
info
-
debug
-
trace
Rhino configures logging based on settings read from the config.yml
file
in the root of your project:
-
rhino_log_level
: The minimum severity of messages to be logged. -
rhino_log_file
: The file to save logs to. IfNA
, standard error stream will be used.
The default config.yml
file uses !expr Sys.getenv()
so that log level and file can also be configured
by setting the RHINO_LOG_LEVEL
and RHINO_LOG_FILE
environment variables.
The functions re-exported by the log
object are aliases for {logger}
functions.
You can also import the package and use it directly to utilize its full capabilities.
Examples
## Not run:
box::use(rhino[log])
# Messages can be formatted using glue syntax.
name <- "Rhino"
log$warn("Hello {name}!")
log$info("{1:3} + {1:3} = {2 * (1:3)}")
## End(Not run)
React components
Description
Declare the React components defined in your app.
Usage
react_component(name)
Arguments
name |
The name of the component. |
Details
There are three steps to add a React component to your Rhino application:
Define the component using JSX and register it with
Rhino.registerReactComponents()
.Declare the component in R with
rhino::react_component()
.Use the component in your application.
Please refer to the Tutorial: Use React in Rhino to learn about the details.
Value
A function representing the component.
Examples
# Declare the component.
TextBox <- react_component("TextBox")
# Use the component.
ui <- TextBox("Hello!", font_size = 20)
Population of rhinos
Description
A dataset containing population of 5 species of rhinos.
Usage
rhinos
Format
A data frame with 58 rows and 3 variables:
- Year
year
- Population
rhinos population
- Species
rhinos species
Source
Run Cypress end-to-end tests
Description
Uses Cypress to run end-to-end tests
defined in the tests/cypress
directory.
Requires Node.js to be available on the system.
Usage
test_e2e(interactive = FALSE)
Arguments
interactive |
Should Cypress be run in the interactive mode? |
Details
Check out: Tutorial: Write end-to-end tests with Cypress to learn how to write end-to-end tests for your Rhino app.
If you want to write end-to-end tests with {shinytest2}
, see our
How-to: Use shinytest2
guide.
Value
None. This function is called for side effects.
Examples
if (interactive()) {
# Run the end-to-end tests in the `tests/cypress` directory.
test_e2e()
}
Run R unit tests
Description
Uses the {testhat}
package to run all unit tests in tests/testthat
directory.
Usage
test_r(...)
Arguments
... |
Additional arguments passed to |
Value
None. This function is called for side effects.
Examples
if (interactive()) {
# Run all unit tests in the `tests/testthat` directory.
test_r()
}