Compare commits

...

7 Commits

Author SHA1 Message Date
Winston Chang
337f4c9c40 Fixes for LaTeX docs 2020-04-14 09:32:07 -05:00
Winston Chang
e86c0c4be4 Add shinyAppTemplate to pkgdown.yml 2020-04-13 17:58:17 -05:00
Winston Chang
44a485b07a Add informative comments 2020-04-13 15:31:24 -05:00
Winston Chang
9138adf8a1 Move 12_template to app_template dir 2020-04-13 14:37:00 -05:00
Winston Chang
e588fc5a4a Updates from code review 2020-04-13 13:14:30 -05:00
Winston Chang
19965c9eb7 Rename utils.R to sort.R 2020-04-13 11:59:24 -05:00
Winston Chang
98e390bc1b Rename 12_counter to 12_template 2020-04-13 11:56:31 -05:00
16 changed files with 145 additions and 142 deletions

View File

@@ -1,8 +1,6 @@
#' Generate a Shiny application from a template
#'
#' This function populates a directory with files for a Shiny application. They
#' are based off of the "12_counter" example which can be run with
#' `runExample()`.
#' This function populates a directory with files for a Shiny application.
#'
#' In an interactive R session, this function will, by default, prompt the user
#' which components to add to the application.
@@ -11,48 +9,49 @@
#'
#' ```
#' appdir/
#' ├── app.R
#' ├── R
#' ├── my-module.R
#' └── utils.R
#' └── tests
#' ├── server.R
#' ├── server
#' ├── test-mymodule.R
#' └── test-server.R
#' ├── shinytest.R
#' ├── shinytest
#' └── mytest.R
#' ├── testthat.R
#' └── testthat
#' ├── helper-load.R
#' └── test-utils.R
#' |- app.R
#' |- R
#' | |- my-module.R
#' | |-- sort.R
#' `-- tests
#' |- server.R
#' |- server
#' | |- test-mymodule.R
#' | `- test-server.R
#' |- shinytest.R
#' |- shinytest
#' | `- mytest.R
#' |- testthat.R
#' `-- testthat
#' |- helper-load.R
#' `- test-sort.R
#' ```
#'
#' Some notes about these files:
#' * app.R is the main application file.
#' * All files in the R/ subdirectory are automatically sourced when the
#' * `app.R` is the main application file.
#' * All files in the `R/` subdirectory are automatically sourced when the
#' application is run.
#' * The R/my-module.R file is automatically sourced when the application
#' is run. This file contains code for a [Shiny module](moduleServer()) which
#' * `R/sort.R` and `R/my-module.R` are automatically sourced when
#' the application is run. The first contains a function `lexical_sort()`,
#' and the second contains code for a [Shiny module](moduleServer()) which
#' is used in the application.
#' * The tests/ directory contains various tests for the application. You may
#' * `tests/` contains various tests for the application. You may
#' choose to use or remove any of them. They can be executed by the
#' [runTests()] function.
#' * tests/server.R is a test runner for test files in
#' tests/server/.
#' * tests/server/test-mymodule.R is a test for the module.
#' * tests/shinytest.R is a test runner for test files in the
#' tests/shinytest/ directory.
#' * tests/shinytest/mytest.R is a test that uses the
#' * `tests/server.R` is a test runner for test files in
#' `tests/server/`.
#' * `tests/server/test-mymodule.R` is a test for the module.
#' * `tests/shinytest.R` is a test runner for test files in the
#' `tests/shinytest/` directory.
#' * `tests/shinytest/mytest.R` is a test that uses the
#' [shinytest](https://rstudio.github.io/shinytest/) package to do
#' snapshot-based testing.
#' * tests/testthat.R is a test runner for test files in the
#' tests/testthat/ directory.
#' * tests/testthat/helper-load.R is a helper script that is automatically
#' loaded before running test-counter.R. (This is performed by the testthat
#' * `tests/testthat.R` is a test runner for test files in the
#' `tests/testthat/` directory.
#' * `tests/testthat/helper-load.R` is a helper script that is automatically
#' loaded before running `test-mymodule.`R. (This is performed by the testthat
#' package.)
#' * tests/testthat/test-utils.R is a set of tests that use the
#' * `tests/testthat/test-sort.R` is a set of tests that use the
#' [testthat](https://testthat.r-lib.org/) package for testing.
#'
#' @param path Path to create new shiny application template.
@@ -67,16 +66,20 @@
#' @export
shinyAppTemplate <- function(path = NULL, examples = "default")
{
if (is.null(path)) {
stop("Please provide a `path`.")
}
choices <- c(
app = "app.R : Main application file",
rdir = "R/utils.R : Helper file with R code",
rdir = "R/sort.R : Helper file with R code",
module = "R/my-module.R : Example module",
shinytest = "tests/shinytest/ : Tests using shinytest package",
testthat = "tests/testthat/ : Tests using testthat",
server = "tests/server/ : Tests of server and module code"
)
if (length(examples) == 1 && examples == "default") {
if (identical(examples, "default")) {
if (interactive()) {
examples <- "ask"
} else {
@@ -104,6 +107,8 @@ shinyAppTemplate <- function(path = NULL, examples = "default")
examples <- names(response)
}
examples <- unique(examples)
if ("all" %in% examples) {
examples <- names(choices)
}
@@ -122,53 +127,20 @@ shinyAppTemplate <- function(path = NULL, examples = "default")
# Helper to resolve paths relative to our example
example_path <- function(path) {
system.file("examples", "12_counter", path, package = "shiny")
}
# Helper to remove rdir code from a file
remove_rdir_code <- function(filename) {
txt <- readLines(filename)
txt <- txt[!grepl("# lexical_sort from R/utils.R", txt)]
txt <- sub("Lexically sorted sequence", "Sorted sequence", txt, fixed = TRUE)
txt <- sub("lexical_sort", "sort", txt, fixed = TRUE)
# Write with \n line endings on all platforms
con <- file(filename, open="wb")
writeLines(txt, con)
close(con)
}
# Helper to remove module code from a file
remove_module_code <- function(filename) {
txt <- readLines(filename)
start_lines <- grep("^ +# =+ Modules =+$", txt)
stop_lines <- grep("^ +# =+$", txt)
if (length(start_lines) != length(stop_lines)) {
stop("Start and end markers are unbalanced.")
}
if (length(start_lines) == 0) {
return()
}
drop_lines <- unlist(lapply(seq_along(start_lines), function(i) {
seq(start_lines[i], stop_lines[i])
}))
# Write with \n line endings on all platforms
con <- file(filename, open="wb")
writeLines(txt[-drop_lines], con)
close(con)
system.file("app_template", path, package = "shiny")
}
# Copy the files for a tests/ subdirectory
copy_test_dir <- function(name, with_rdir, with_module) {
tests_dir <- file.path(path, "tests")
if (!dirExists(tests_dir)) {
dir.create(tests_dir, recursive = TRUE)
}
dir.create(tests_dir, showWarnings = FALSE, recursive = TRUE)
files <- dir(example_path("tests"), recursive = TRUE)
# Note: This is not the same as using dir(pattern = "^shinytest"), since
# that will not match files inside of shinytest/.
files <- files[grepl(paste0("^", name), files)]
# Filter out files related to R/utils.R, if applicable.
# Filter out files related to R/sort.R, if applicable.
if (!with_rdir) {
files <- files[!grepl("utils", files)]
}
@@ -181,7 +153,7 @@ shinyAppTemplate <- function(path = NULL, examples = "default")
# Create any subdirectories if needed
dirs <- setdiff(unique(dirname(files)), ".")
for (dir in dirs) {
dir.create(file.path(tests_dir, dir), recursive = TRUE)
dir.create(file.path(tests_dir, dir), showWarnings = FALSE, recursive = TRUE)
}
file.copy(
@@ -216,30 +188,29 @@ shinyAppTemplate <- function(path = NULL, examples = "default")
app_file <- file.path(path, "app.R")
if ("app" %in% examples) {
if (file.exists(app_file)) {
message(app_file, " already exists")
}
file.copy(example_path("app.R"), path)
message("Not writing ", app_file, "because file already exists.")
if (!"rdir" %in% examples) {
remove_rdir_code(app_file)
}
if (!"module" %in% examples) {
remove_module_code(app_file)
} else {
writeChar(
as.character(htmlTemplate(
example_path("app.R"),
rdir = "rdir" %in% examples,
module = "module" %in% examples
)),
con = app_file,
eos = NULL
)
}
}
# R/ dir with utils and/or module
r_dir <- file.path(path, "R")
if ("rdir" %in% examples) {
if (!dirExists(r_dir)) {
dir.create(r_dir, recursive = TRUE)
}
file.copy(example_path("R/utils.R"), r_dir, recursive = TRUE)
dir.create(r_dir, showWarnings = FALSE, recursive = TRUE)
file.copy(example_path("R/sort.R"), r_dir, recursive = TRUE)
}
if ("module" %in% examples) {
if (!dirExists(r_dir)) {
dir.create(r_dir, recursive = TRUE)
}
dir.create(r_dir, showWarnings = FALSE, recursive = TRUE)
file.copy(example_path("R/my-module.R"), r_dir, recursive = TRUE)
}

View File

@@ -1,4 +1,6 @@
mymoduleUI <- function(id, label = "Counter") {
# Al uses of Shiny input/output IDs in the UI must be namespaced,
# as in ns("x").
ns <- NS(id)
tagList(
actionButton(ns("button"), label = label),
@@ -7,6 +9,8 @@ mymoduleUI <- function(id, label = "Counter") {
}
mymoduleServer <- function(id) {
# moduleServer() wraps a function to create the server component of a
# module.
moduleServer(
id,
function(input, output, session) {

View File

@@ -1,26 +1,48 @@
ui <- fluidPage(
# ======== Modules ========
{{
# These blocks of code are processed with htmlTemplate()
if (isTRUE(module)) {
' # ======== Modules ========
# mymoduleUI is defined in R/my-module.R
mymoduleUI("mymodule1", "Click counter #1"),
mymoduleUI("mymodule2", "Click counter #2"),
# =========================
'
}
}}
wellPanel(
sliderInput("size", "Data size", min = 5, max = 20, value = 10),
div("Lexically sorted sequence:"),
{{
if (isTRUE(rdir)) {
' div("Lexically sorted sequence:"),'
} else {
' div("Sorted sequence:"),'
}
}}
verbatimTextOutput("sequence")
)
)
server <- function(input, output, session) {
# ======== Modules ========
{{
if (isTRUE(module)) {
' # ======== Modules ========
# mymoduleServer is defined in R/my-module.R
mymoduleServer("mymodule1")
mymoduleServer("mymodule2")
# =========================
'
}
}}
data <- reactive({
# lexical_sort from R/utils.R
lexical_sort(seq_len(input$size))
{{
if (isTRUE(rdir)) {
' # lexical_sort from R/sort.R
lexical_sort(seq_len(input$size))'
} else {
' sort(seq_len(input$size))'
}
}}
})
output$sequence <- renderText({
paste(data(), collapse = " ")

View File

@@ -1,6 +1,7 @@
# Use testthat just for expectations
library(testthat)
# See ?testServer for more information
testServer(mymoduleServer, {
# Set initial value of a button
session$setInputs(button = 0)

View File

@@ -4,29 +4,34 @@
\alias{markdown}
\title{Insert inline Markdown}
\usage{
markdown(mds, extensions = TRUE, ...)
markdown(mds, extensions = TRUE, .noWS = NULL, ...)
}
\arguments{
\item{mds}{A character vector of Markdown source to convert to HTML. If the
vector has more than one element, resulting HTML is concatenated.}
vector has more than one element, a single-element character vector of
concatenated HTML is returned.}
\item{extensions}{Enable Github syntax extensions, defaults to \code{TRUE}.}
\item{extensions}{Enable Github syntax extensions; defaults to \code{TRUE}.}
\item{.noWS}{Character vector used to omit some of the whitespace that would
normally be written around generated HTML. Valid options include \code{before},
\code{after}, and \code{outside} (equivalent to \code{before} and \code{end}).}
\item{...}{Additional arguments to pass to \code{\link[commonmark:markdown_html]{commonmark::markdown_html()}}.
These arguments are \emph{\link[rlang:dyn-dots]{dynamic}}.}
}
\value{
an \code{html}-classed character vector of rendered HTML
a character vector marked as HTML.
}
\description{
This function accepts a character vector of
\href{https://en.wikipedia.org/wiki/Markdown}{Markdown}-syntax text and renders
it to HTML that may be included in a UI.
This function accepts
\href{https://en.wikipedia.org/wiki/Markdown}{Markdown}-syntax text and returns
HTML that may be included in Shiny UIs.
}
\details{
Prior to interpretation as Markdown, leading whitespace is trimmed from text
with \code{\link[glue:trim]{glue::trim()}}. This makes it possible to insert Markdown and for it to
be processed correctly even when the call to \code{markdown()} is indented.
Leading whitespace is trimmed from Markdown text with \code{\link[glue:trim]{glue::trim()}}.
Whitespace trimming ensures Markdown is processed correctly even when the
call to \code{markdown()} is indented within surrounding R code.
By default, \link[commonmark:extensions]{Github extensions} are enabled, but this
can be disabled by passing \code{extensions = FALSE}.

View File

@@ -52,6 +52,6 @@ below to see their documentation.
\describe{
\item{fastmap}{\code{\link[fastmap]{is.key_missing}}, \code{\link[fastmap]{key_missing}}}
\item{htmltools}{\code{\link[htmltools]{a}}, \code{\link[htmltools]{br}}, \code{\link[htmltools]{code}}, \code{\link[htmltools]{div}}, \code{\link[htmltools]{em}}, \code{\link[htmltools]{h1}}, \code{\link[htmltools]{h2}}, \code{\link[htmltools]{h3}}, \code{\link[htmltools]{h4}}, \code{\link[htmltools]{h5}}, \code{\link[htmltools]{h6}}, \code{\link[htmltools]{hr}}, \code{\link[htmltools]{HTML}}, \code{\link[htmltools]{htmlTemplate}}, \code{\link[htmltools]{img}}, \code{\link[htmltools]{includeCSS}}, \code{\link[htmltools]{includeHTML}}, \code{\link[htmltools]{includeMarkdown}}, \code{\link[htmltools]{includeScript}}, \code{\link[htmltools]{includeText}}, \code{\link[htmltools]{is.singleton}}, \code{\link[htmltools]{p}}, \code{\link[htmltools]{pre}}, \code{\link[htmltools]{singleton}}, \code{\link[htmltools]{span}}, \code{\link[htmltools]{strong}}, \code{\link[htmltools]{suppressDependencies}}, \code{\link[htmltools]{tag}}, \code{\link[htmltools]{tagAppendAttributes}}, \code{\link[htmltools]{tagAppendChild}}, \code{\link[htmltools]{tagAppendChildren}}, \code{\link[htmltools]{tagGetAttribute}}, \code{\link[htmltools]{tagHasAttribute}}, \code{\link[htmltools]{tagList}}, \code{\link[htmltools]{tags}}, \code{\link[htmltools]{tagSetChildren}}, \code{\link[htmltools]{validateCssUnit}}, \code{\link[htmltools]{withTags}}}
\item{htmltools}{\code{\link[htmltools]{HTML}}, \code{\link[htmltools]{a}}, \code{\link[htmltools]{br}}, \code{\link[htmltools]{code}}, \code{\link[htmltools]{div}}, \code{\link[htmltools]{em}}, \code{\link[htmltools]{h1}}, \code{\link[htmltools]{h2}}, \code{\link[htmltools]{h3}}, \code{\link[htmltools]{h4}}, \code{\link[htmltools]{h5}}, \code{\link[htmltools]{h6}}, \code{\link[htmltools]{hr}}, \code{\link[htmltools]{htmlTemplate}}, \code{\link[htmltools]{img}}, \code{\link[htmltools]{includeCSS}}, \code{\link[htmltools]{includeHTML}}, \code{\link[htmltools]{includeMarkdown}}, \code{\link[htmltools]{includeScript}}, \code{\link[htmltools]{includeText}}, \code{\link[htmltools]{is.singleton}}, \code{\link[htmltools]{p}}, \code{\link[htmltools]{pre}}, \code{\link[htmltools]{singleton}}, \code{\link[htmltools]{span}}, \code{\link[htmltools]{strong}}, \code{\link[htmltools]{suppressDependencies}}, \code{\link[htmltools]{tag}}, \code{\link[htmltools]{tagAppendAttributes}}, \code{\link[htmltools]{tagAppendChild}}, \code{\link[htmltools]{tagAppendChildren}}, \code{\link[htmltools]{tagGetAttribute}}, \code{\link[htmltools]{tagHasAttribute}}, \code{\link[htmltools]{tagList}}, \code{\link[htmltools]{tagSetChildren}}, \code{\link[htmltools]{tags}}, \code{\link[htmltools]{validateCssUnit}}, \code{\link[htmltools]{withTags}}}
}}

View File

@@ -18,58 +18,57 @@ directory. With "all", all template items will be added to the app
directory.}
}
\description{
This function populates a directory with files for a Shiny application. They
are based off of the "12_counter" example which can be run with
\code{runExample()}.
This function populates a directory with files for a Shiny application.
}
\details{
In an interactive R session, this function will, by default, prompt the user
which components to add to the application.
The full example application includes the following files and directories:\preformatted{appdir/
├── app.R
├── R
├── my-module.R
└── utils.R
└── tests
├── server.R
├── server
├── test-mymodule.R
└── test-server.R
├── shinytest.R
├── shinytest
└── mytest.R
├── testthat.R
└── testthat
├── helper-load.R
└── test-utils.R
|- app.R
|- R
| |- my-module.R
| |-- sort.R
`-- tests
|- server.R
|- server
| |- test-mymodule.R
| `- test-server.R
|- shinytest.R
|- shinytest
| `- mytest.R
|- testthat.R
`-- testthat
|- helper-load.R
`- test-sort.R
}
Some notes about these files:
\itemize{
\item app.R is the main application file.
\item All files in the R/ subdirectory are automatically sourced when the
\item \code{app.R} is the main application file.
\item All files in the \verb{R/} subdirectory are automatically sourced when the
application is run.
\item The R/my-module.R file is automatically sourced when the application
is run. This file contains code for a \href{moduleServer()}{Shiny module} which
\item \code{R/sort.R} and \code{R/my-module.R} are automatically sourced when
the application is run. The first contains a function \code{lexical_sort()},
and the second contains code for a \href{moduleServer()}{Shiny module} which
is used in the application.
\item The tests/ directory contains various tests for the application. You may
\item \verb{tests/} contains various tests for the application. You may
choose to use or remove any of them. They can be executed by the
\code{\link[=runTests]{runTests()}} function.
\item tests/server.R is a test runner for test files in
tests/server/.
\item tests/server/test-mymodule.R is a test for the module.
\item tests/shinytest.R is a test runner for test files in the
tests/shinytest/ directory.
\item tests/shinytest/mytest.R is a test that uses the
\item \code{tests/server.R} is a test runner for test files in
\verb{tests/server/}.
\item \code{tests/server/test-mymodule.R} is a test for the module.
\item \code{tests/shinytest.R} is a test runner for test files in the
\verb{tests/shinytest/} directory.
\item \code{tests/shinytest/mytest.R} is a test that uses the
\href{https://rstudio.github.io/shinytest/}{shinytest} package to do
snapshot-based testing.
\item tests/testthat.R is a test runner for test files in the
tests/testthat/ directory.
\item tests/testthat/helper-load.R is a helper script that is automatically
loaded before running test-counter.R. (This is performed by the testthat
\item \code{tests/testthat.R} is a test runner for test files in the
\verb{tests/testthat/} directory.
\item \code{tests/testthat/helper-load.R} is a helper script that is automatically
loaded before running \code{test-mymodule.}R. (This is performed by the testthat
package.)
\item tests/testthat/test-utils.R is a set of tests that use the
\item \code{tests/testthat/test-sort.R} is a set of tests that use the
\href{https://testthat.r-lib.org/}{testthat} package for testing.
}
}

View File

@@ -168,6 +168,7 @@ reference:
- title: Utility functions
desc: Miscellaneous utilities that may be useful to advanced users or when extending Shiny.
contents:
- shinyAppTemplate
- req
- validate
- session