Giter Club home page Giter Club logo

gargoyle's Introduction

gargoyle

Lifecycle: stable R-CMD-check

The goal of gargoyle is to provide an event-based mechanism for {shiny}.

Installation

You can install the dev version of {gargoyle} with:

remotes::install_github("ColinFay/gargoyle")

About

You’re reading the doc about version : 0.0.1

This README has been compiled on the

Sys.time()
#> [1] "2023-03-27 16:53:20 CEST"

Here are the test & coverage results :

devtools::check(quiet = TRUE)
#> ℹ Loading gargoyle
#> ── R CMD check results ───────────────────────────────────── gargoyle 0.0.1 ────
#> Duration: 10.3s
#> 
#> 0 errors ✔ | 0 warnings ✔ | 0 notes ✔
covr::package_coverage()
#> gargoyle Coverage: 56.36%
#> R/funs.R: 51.02%
#> R/logs.R: 100.00%

What the heck?

{gargoyle} is a package that provides wrappers around {shiny} to turn your app into and event-based application instead of a full reactive app. The framework is centered around a listen & trigger mechanism.

It works with classical UI, and just needs tweaking the server side of your app.

{shiny}’s default reactive behavior is very helpful when it comes to building small applications. Because, you know, the good thing about reactivity is that when something moves somewhere, it’s updated everywhere. But the bad thing about reactivity is that when something moves somewhere, it’s updated everywhere. So it does work pretty well on small apps, but can get very complicated on bigger apps, and can quickly get out of hands.

That’s where {gargoyle} comes into play: it provides an event based paradigm for building your apps, so that things happen under a control flow.

For whom?

If you’re just building small {shiny} apps, you’re probably good with {shiny} default reactive behavior. But if ever you’ve struggled with reactivity on more bigger apps, you might find {gargoyle} useful.

The trade-off

{gargoyle} will be more verbose and will demand more work upfront to make things happen. I believe this is for the best if you’re working on a big project.

Design pattern

{gargoyle} has:

  • init, listen & trigger, which allow to initiate, listen on, and trigger an event

  • on, that runs the expr when the event in triggered

gargoyle::trigger() can print messages to the console using options("gargoyle.talkative" = TRUE).

Example

library(shiny)
library(gargoyle)
options("gargoyle.talkative" = TRUE)
ui <- function(request){
  tagList(
    h4('Go'),
    actionButton("y", "y"),
    h4('Output of z$v'),
    tableOutput("evt")
  )
}

server <- function(input, output, session){

  # Initiating the flags
  init("airquality", "iris", "renderiris")

  # Creating a new env to store values, instead of
  # a reactive structure
  z <- new.env()

  observeEvent( input$y , {
    z$v <- mtcars
    # Triggering the flag
    trigger("airquality")
  })

  on("airquality", {
    # Triggering the flag
    z$v <- airquality
    trigger("iris")
  })

  on("iris", {
    # Triggering the flag
    z$v <- iris
    trigger("renderiris")
  })

  output$evt <- renderTable({
    # This part will only render when the renderiris
    # flag is triggered
    watch("renderiris")
    head(z$v)
  })

}

shinyApp(ui, server)

You can then get & clear the logs of the times the triggers were called:

get_gargoyle_logs()
clear_gargoyle_logs()

Code of Conduct

Please note that the gargoyle project is released with a Contributor Code of Conduct. By contributing to this project, you agree to abide by its terms.

gargoyle's People

Contributors

colinfay avatar ggpinto avatar gueyenono avatar ilyazar avatar statnmap avatar valexandre avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

gargoyle's Issues

Wrong flag names in example of main functions and man

Running the example of R/funs.R produces and error due to wrong (typo) trigger elements:

Listening on http://127.0.0.1:7407
Warning: Error in : [Gargoyle] Flag airquality hasn't been initiated: can't listen to it.
  43: stop
  42: stop_if
  41: on
  40: server [#29]
   3: runApp
   2: print.shiny.appobj
   1: <Anonymous>
Error : [Gargoyle] Flag airquality hasn't been initiated: can't listen to it.

Can be fixed by changing line 30 to init("airquality", "iris", "renderiris"), see #9

Warning: Error in watch: could not find function "watch"

Hi @ColinFay ,

Thank you so much for {gargoyle}. A colleague of mine and I have been implementing it a lot.

Once in a while , I get this error when I launch the app for the first time. Do you know why? We've initiated, triggered and watched (through gargoyle::on()) our events correctly but still.

Screen Shot 2023-06-23 at 3 55 51 PM

The "watch" function is not working properly

Making shiny dashboards, using golem, it was working fine, but then, in a new shiny dashboard, also using golem, now this occurs:

--- in app_server.R:
...
gargoyle::init("ggl_test")
...
--- in mod_1_server:
...
gargoyle::trigger("ggl_test")
...
--- in another mod:
...
gargoyle::on("ggl_test", {
	print("ok1") # this runs only when calling mod_1_server (expected behavior)
})
shiny::observeEvent(gargoyle::watch("ggl_test"), {
	print("ok2") # this runs when calling mod_1_server but also when the app starts (before calling mod_1_server) - not supposed to happen 
})

Bug: arg `session` not passed down from `on()` to `watch()`

When working with the tests/testthat/test-funs.R file I realized that the following code part does not work:

library(shiny)
library(gargoyle)

shiny::reactiveConsole(TRUE)
s <- shiny::MockShinySession$new()

init("pif", session = s)
#> [[1]]
#> reactiveVal: [1] "0"
test_for_on_function <- on(
  "pif",
  {
    cat("TEST fails")
  },
  session = s
)
test_for_on_function$.func()
#> Error in session$userData[[name]](): attempt to apply non-function
shiny::reactiveConsole(FALSE)

A more specific error message is goes on:

error message

Warning: Error in session$userData[[name]]: attempt to apply non-function 51: gargoyle::watch [/home/iz/Dropbox/r-pkgs/gargoyle/R/funs.R#107] 50: eval_tidy 49: eventFunc 36: observeEvent(gargoyle::watch("pif")) 35: contextFunc 34: env$runWith 27: ctx$run 26: run 7: flushCallback 6: FUN 5: lapply 4: ctx$executeFlushCallbacks 3: .getReactiveEnvironment()$flush 2: flushReact 1: cb

This is probably because inside the on() function:

on <- function(
  name,
  expr,
  session = getDefaultReactiveDomain()
  ){
  ...
  observeEvent(
      substitute(gargoyle::watch(name)),
  ...
}

the gargoyle::watch(name) does not update the session i.e. it should probably be gargoyle::watch(name, session = session).

May be fixed by #18

trigger doesn't work when watch is not imported

Hey Colin,

Thanks for this package which is really cool to deal with reactivity in shiny apps, in particular when we use module like with the golem framework.

I am developping a golem app and I started by using your function that I borrowed from your hexmake app and it works well. Now that gargoyle is on CRAN I am changing the code by importing the needed functions. I never use watch() but only trigger() and on() (and obviously init()). After removing the internal function and switch to @importFrom gargoyle trigger on init it didn't work anymore. I figured the problem out by adding watch() in the @importFrom gargoyle section even if I never use directly the watch() function

I am under the impression that I shouldn't have to add watch() because it is included in on() function, but maybe I am wrong. What do you think?

Thanks

Update gargoyle please

Hi @ColinFay

please consider an update of gargoyle with the current PR's or at least merging #18 if otherwise it takes too much time.

I need this #18 fix in an app I am working now, otherwise I'd have to use gargoyle with the app on my fix branch or consider some esoteric workarounds for the control flow (it uses a different session in the whole shiny::module(), it's complicated and it would cost quite some time to unwrap the trigger/watch mechanisms already implemented).

Also, the current PR's are rather small and straightforward, let me know if I can help ... the update would be very much welcome and I'd be super happy if you could find the time.

Using more than one 'name' argument for on()?

library(shiny)
library(gargoyle)
options("gargoyle.talkative" = TRUE)
ui <- function(request){
  tagList(
    h4('Go'),
    actionButton("y", "y"),
    actionButton("z", "z"),
    h4('Output of z$v'),
    tableOutput("evt")
  )
}

server <- function(input, output, session){
  
  # Initiating the flags
  init( "airquality", "iris", "renderiris", "a2")
  
  # Creating a new env to store values, instead of
  # a reactive structure
  z <- new.env()
  
  observeEvent( input$y , {
    z$v <- mtcars
    # Triggering the flag
    trigger("airquality")
  })
  
  observeEvent( input$y , {
    #z$v <- mtcars
    # Triggering the flag
    trigger("a2")
  })
  
  on("airquality", {
    # Triggering the flag
    z$v <- airquality
    trigger("iris")
  })
  
  on("iris", {
    # Triggering the flag
    z$v <- iris
    trigger("renderiris")
  })
  
  ## need to use both 'iris' & 'a2'
  on({"iris", "a2"},{
    print("observed!")
  })
  
  output$evt <- renderTable({
    # This part will only render when the renderiris
    # flag is triggered
    watch("renderiris")
    head(z$v) 
  })
  
}

shinyApp(ui, server)

Note: It doesn't work!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.