Skip to content

Commit f9f20aa

Browse files
Sebastiaan VandewoudeSebastiaan Vandewoude
authored andcommitted
initial commit
0 parents commit f9f20aa

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+2705
-0
lines changed

.Rbuildignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
^.*\.Rproj$
2+
^\.Rproj\.user$
3+
^LICENSE.md

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.Rproj.user
2+
.Rhistory
3+
.RData
4+
.Ruserdata

DESCRIPTION

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
Package: reactflush
2+
Type: Package
3+
Title: Shiny Code Reactivity Visualizer
4+
Version: 0.1.0
5+
Authors@R: c(person("Rawat", "Vimal", email = "vimal.rawat@basf.com", role = c("aut")),
6+
person("Vandewoude", "Sebastiaan", email = "sebastiaan.vandewoude@basf.com", role = c("aut","cre")),
7+
person("Kawalia", "Amit", email = "amit.kawalia@basf.com", role = c("ctb")),
8+
person("BASF SE", role = "cph"))
9+
Description: Compute and display all shiny reactive inputs and output in a network visualization by analyzing your code.
10+
Depends:
11+
R (>= 3.0.2)
12+
Imports:
13+
assertthat,
14+
DiagrammeR,
15+
dplyr,
16+
fs,
17+
glue,
18+
magrittr,
19+
purrr,
20+
readr,
21+
rlang,
22+
rstudioapi,
23+
stringr,
24+
tibble,
25+
tidyr
26+
Suggests:
27+
shiny (>= 1.2.0.9001),
28+
testthat
29+
License: GPL-3
30+
Encoding: UTF-8
31+
LazyData: true
32+
RoxygenNote: 6.1.1
33+
URL: https://github.com/svdwoude/reactflush
34+
BugReports: https://github.com/svdwoude/reactflush/issues

LICENSE.md

Lines changed: 595 additions & 0 deletions
Large diffs are not rendered by default.

NAMESPACE

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Generated by roxygen2: do not edit by hand
2+
3+
export(convert_to_mermaid_connection)
4+
export(convert_to_mermaid_node)
5+
export(parse_shiny_app)
6+
export(parse_shiny_server)
7+
export(plot_mermaid)
8+
export(reactflush)
9+
export(reactflush_markdown)
10+
export(render_mermaid)
11+
import(DiagrammeR)
12+
import(dplyr)
13+
import(fs)
14+
import(purrr)
15+
import(readr)
16+
import(stringr)
17+
import(tibble)
18+
importFrom(magrittr,"%>%")
19+
importFrom(rlang,.data)
20+
importFrom(stats,setNames)

R/annotate.R

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
#' annotate all inputs/reactives for each code block
2+
#'
3+
#' @description identify and annotate all inputs/reactives referenced in code block.
4+
#' Take isolated inputs/reactives into account
5+
#'
6+
#' @param blocks \code{data.frame} all code blocks in server.R code
7+
#' @param inpts \code{vector} of all inputs/reactives
8+
#' @param type \code{string} which type to annotate: inputs or reactives
9+
#'
10+
#' @importFrom rlang .data
11+
#' @import dplyr purrr
12+
annotate_type <- function(blocks, inpts, type = c("inputs","reactives")){
13+
type <- rlang::arg_match(type)
14+
15+
blocks <- blocks %>%
16+
mutate(
17+
type_expr = map(expr, list_matches, matches = inpts),
18+
type_opts = map(options, list_matches, matches = inpts),
19+
type_islt = map(isolate, list_matches, matches = inpts),
20+
type_deps = pmap(
21+
list(type, type_expr, type_opts),
22+
determine_invalidators
23+
)
24+
) %>%
25+
rename(
26+
!! sep_(type, "expr") := .data$type_expr,
27+
!! sep_(type, "opts") := .data$type_opts,
28+
!! sep_(type, "islt") := .data$type_islt,
29+
!! sep_(type, "deps") := .data$type_deps
30+
)
31+
32+
return(blocks)
33+
}
34+
35+
36+
#' annotate all side effects for each code block
37+
#'
38+
#' @description identify and annotate all side effects triggered in code block.
39+
#' Take both reactiveVal() and values$reactiveValues into account
40+
#'
41+
#' @param blocks \code{data.frame} all code blocks in server.R code
42+
#' @param reactives \code{vector} of all reactives, both reactiveVal() and values$reactiveValues
43+
#'
44+
#' @importFrom rlang .data
45+
#' @import dplyr purrr
46+
annotate_side_effects <- function(blocks, reactives) {
47+
# extract the name of the reactiveValues
48+
reactive_value <- blocks %>%
49+
filter(.data$type == "reactiveValues") %>%
50+
pull(.data$name)
51+
52+
# match code with reactive side effects
53+
blocks <- blocks %>%
54+
mutate(
55+
side_effects_deps = map(body, list_side_effects, matches = reactives, reactive_value = reactive_value)
56+
)
57+
58+
return(blocks)
59+
}
60+
61+
62+
#' annotate all input defentions in code blocks WIP
63+
#'
64+
#' @description identify and annotate all defenitions within code block. WIP
65+
annotate_definitions <- function() {
66+
#(?:Input|Box)\([^)]*['"]timepoint_analysis['"]
67+
}
68+
69+
70+
#' list all occuring matches in code block
71+
#'
72+
#' @description list all occuring matches in code block
73+
#'
74+
#' @param code \code{string} code of code block
75+
#' @param matches \code{vector} of all inputs/reactives
76+
#'
77+
#' @importFrom magrittr "%>%"
78+
#' @import dplyr stringr
79+
list_matches <- function(code, matches) {
80+
code %>%
81+
str_extract(pattern = fixed(matches)) %>%
82+
enframe() %>%
83+
filter(!is.na(value)) %>%
84+
pull(value) %>%
85+
str_replace("\\(\\)","") %>%
86+
str_trim()
87+
}
88+
89+
90+
#' list all occuring side effects in code block
91+
#'
92+
#' @description list all occuring matches in code block
93+
#'
94+
#' @param code \code{string} code of code block
95+
#' @param matches \code{vector} of all reactives, both reactiveVal() and values$reactiveValues
96+
#' @param reactive_value \code{string} name of reactiveValues() object, defautls to "values"
97+
#' @param regex_assgn \code{string} regex to detect assignments
98+
#' @param regex_braces \code{string} regex to detect assignment in braces
99+
#'
100+
#' @importFrom magrittr "%>%"
101+
#' @import dplyr tibble stringr
102+
list_side_effects <- function(code, matches, reactive_value = "values", regex_assgn = regex_pattern()$assignment, regex_braces = regex_pattern()$braces_start) {
103+
# if there are no reactives, we will not find side effects
104+
if(identical(matches, character(0))) {
105+
return(character())
106+
}
107+
108+
no_r_name <- "[^a-zA-Z0-9._]"
109+
# seperate reactiveValues values$<name> from reactiveVals <name>(..)
110+
# also remove last two () characters from reactiveVals
111+
reactive_vals <- matches[!str_detect(matches, paste0(reactive_value, "\\$"))] %>% str_sub(end = -3)
112+
reactive_values <- matches[str_detect(matches, paste0(reactive_value, "\\$"))] %>% str_replace(fixed("$"), fixed("\\$"))
113+
114+
# concate <name> with assignemnt pattern: (...
115+
# add to no_r_name part to avoid selected_values(...) being matched as values(...)
116+
regex_reactiveVals_assgn <- paste0(no_r_name, "(", reactive_vals, ")", regex_braces)
117+
118+
matches <- code %>%
119+
str_extract(pattern = regex_reactiveVals_assgn) %>%
120+
enframe() %>%
121+
filter(!is.na(value))
122+
123+
if(nrow(matches) == 0) {
124+
matches <- character()
125+
} else {
126+
matches <- matches %>%
127+
mutate(value = str_split(value, "\\(", n = 2, simplify = TRUE)[,1]) %>%
128+
pull(value)
129+
}
130+
131+
# if no reactiveValues are detected, skip this step
132+
if(!identical(reactive_value, character(0))) {
133+
# concate values$<name> with assignemnt pattern: values$<name> <- or =
134+
regex_reactiveValues_assgn <- paste0("(",reactive_values, ")", regex_assgn)
135+
136+
matches <- code %>%
137+
str_match(pattern = regex_reactiveValues_assgn) %>%
138+
.[,2] %>%
139+
enframe() %>%
140+
filter(!is.na(value)) %>%
141+
pull(value) %>%
142+
c(matches)
143+
144+
}
145+
146+
return(matches)
147+
}
148+
149+
150+
#' determine reactive invalidators
151+
#'
152+
#' @description list all reactive invalidators in code block, depending on type and where
153+
#' within the code the inputs/reactives are used
154+
#'
155+
#' @param type \code{string} type of code block (reactive, observeEvent, ...)
156+
#' @param expr \code{vector} inputs/reactives referenced in main expression
157+
#' @param opts \code{vector} inputs/reactives referenced in options accompanying expression
158+
#'
159+
#' @importFrom magrittr "%>%"
160+
#' @import stringr
161+
determine_invalidators <- function(type, expr, opts) {
162+
# depending on the type the block is invalidated either expr, opts or both or none
163+
if(str_detect(type, fixed("event", ignore_case=TRUE))) {
164+
# observeEvent & eventReactive only are only invalidated by options, not
165+
# their expression
166+
return(opts)
167+
} else if(type == "function"){
168+
# functions do not get invalidated
169+
return(character())
170+
} else {
171+
# reactive, observe & renders are invalidated by both expr and opts
172+
return(c(expr, opts) %>% unique())
173+
}
174+
}

0 commit comments

Comments
 (0)