An R package to help debug Shiny apps during the process itself.
install.packages("ShinyTester")
(dev version devtools::install_github("mexindian/ShinyTester")
)
TL;WR VERSION: Shiny is awesome but a bit daunting and easy to make mistakes in. These functions should help make it a bit less daunting.
I came back to Shiny after a hiatus of a few years and it was much more challenging than I feel comfortable admitting. I was comitting bonehead moves like writing something
instead of output$something
, confusing where to put Output
commands vs Render
commands, etc. I would eventually find my mistake, curse myself for my terrible memory and move on with a crumpled ego. Then I had the realization that maybe if I was a beginner, I wouldn't even know what I was doing wrong.
At the same time I kind of thought to myself as I was developing a Shiny App "I really should draw a hirearchy for this crap... I can't keep it straight!" What I find especially complex about Shiny is that the functions that are fed in, for example, reading a reactive
block into a dataframe, is that they are not fed in through parameters as in a normal function, they are fed into the script in the body itself... which makes it harder to check that every block has all the stuff it needs.
These two thoughts culminated in two functions that analyze the code itself (I guess we should then call these metafunctions):
ShinyDummyCheck()
- to check how items are created in server.R
and then how they are called in ui.R
with some fairly naive checks put on, and ShinyHierarchy()
- to create an ad hoc hirearchy of the structure of the Shiny Apps - ie - what inputs go to what reactives, what reactives go to other reactives, and what then gets pushed back out to the UI as an output. It is my hope that both of these combined minimize the intrinsic boneheadedness in us all. This is really quite alpha though... please do check the Caveats!
ShinyDummyCheck
:ShinyTester::ShinyDummyCheck("https://raw.githubusercontent.com/mexindian/ShinyServer/master/LineSelector")
Provides this table:
Item | SrvCall | isOutput | VisualCall | Status |
---|---|---|---|---|
a | reactive | NA | NA | OK |
FinalDF | reactive | NA | NA | OK |
Plot3 | renderPlotly | Yes | plotlyOutput | OK |
PERC | renderText | Yes | verbatimTextOutput | OK |
fig1 | renderPlot | Yes | plotOutput | OK |
figControl | renderPlot | Yes | plotOutput | OK |
figControl | renderPlot | Yes | plotOutput | OK |
table1 | renderDataTable | Yes | dataTableOutput | OK |
Which shows that there are no errors in the Shiny app, oh except for the fact that I defined an object twice... whoops (Yeah, see that's exactly the boneheadedness I'm talkin bout). The structure of the table is as follows:
item
or output$item
ShinyHierarchy
:A simple example:
library(ShinyTester)
ShinyHierarchy("https://raw.githubusercontent.com/rstudio/shiny-examples/master/003-reactivity")
Will yield:
Which shows one of the weaknesses of the function... it assumes all Item names are unique... and will act strangely if this assumption doesn't hold (ie - caption).
A more complex example:
ShinyTester::ShinyHierarchy("https://raw.githubusercontent.com/mexindian/ShinyServer/master/LineSelector")
Yields:
And here we can start to see the structure that I'm attempting to show... there are basically three ROWS of nodes. The first one is the UI Inputs, the second row are the reactives (kinda...), and the third row are the outputs being visualized. I said the reactives are "kinda" the second row because I have introduced a small shift to each node in the middle row in order to see reactive flows into each other (if they are all in the same row, you can't really see them). The structure is made evident in a more complex case below (forgive the redacted names):
This is a very naive app, and in early stages at that... it works best with my style of programming and will probably take significant work to universalize (since we're talking about code... maybe it's impossible to fully universalize). Some other caveats:
<-
assignments, not =
or ->
assignmentsplotOutput("thingie")
works, plotOutput('thingie')
doesn't.app.R
implementation is not supported.isolate
and observe
are not supported yetFor now it only analyzes the main scripts, if you are SOURCEing files in from other places, it won't work.
input <- data.frame(Parameter1="thingie1",Parameter2="thingie2")
. Keep this commented out, but when you test, you can run through the Shiny app as if it were live.