Skip to content

Commit

Permalink
feat: error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
JohnCoene committed Mar 9, 2024
1 parent cb9321a commit d046f29
Show file tree
Hide file tree
Showing 11 changed files with 100 additions and 7 deletions.
4 changes: 2 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: communicate
Title: Communicate Between 'Shiny' Client and Server
Version: 0.1.2.9001
Version: 0.1.3.9000
Authors@R:
c(
person(given = "John",
Expand All @@ -15,7 +15,7 @@ Description: What the package does (one paragraph).
License: GPL (>= 3)
Encoding: UTF-8
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.2.3
RoxygenNote: 7.3.0
Imports:
shiny,
jsonlite,
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

S3method(print,converters_fn)
S3method(print,defaults_fn)
S3method(print,errors_fn)
export(Character)
export(Dataframe)
export(Date)
Expand Down
1 change: 1 addition & 0 deletions R/aaa.R
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ env$prefix <- NULL
env$handlers <- list()
env$schemas <- list()
env$defaults <- list()
env$errors <- list()
40 changes: 39 additions & 1 deletion R/core.R
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,23 @@ com <- \(id, handler) {
# defaults may be reactives
check_args_match(env$handlers[[id]], ...)
env$defaults[[id]] <- list(...)
com_send(id)

on.exit(
com_send(id),
add = TRUE
)

fn <- \(handler, ...){
if(!is.function(handler))
stop("Handler must be a function")

if(length(methods::formalArgs(handler)) < 1L)
stop("Handler must have at least one argument")

env$errors[[id]] <- handler
} |>
construct_errors_fn() |>
invisible()
}

fn |>
Expand Down Expand Up @@ -82,6 +98,15 @@ construct_converters_fn <- \(fn) {
)
}

#' @rdname constructors
#' @keywords internal
construct_errors_fn <- \(fn) {
structure(
fn,
class = c("errors_fn", class(fn))
)
}

#' @export
print.defaults_fn <- \(x, ...) {
cat("Add defaults, e.g.: (x = 1)\n")
Expand All @@ -92,6 +117,11 @@ print.converters_fn <- \(x, ...) {
cat("Add converters, e.g.: (x = as_dataframe)\n")
}

#' @export
print.errors_fn <- \(x, ...) {
cat("Add error handlers, e.g.: (error_handler_fn)\n")
}

#' Communicate
#'
#' Serve communication channels.
Expand Down Expand Up @@ -124,6 +154,10 @@ com_serve <- \(session = shiny::getDefaultReactiveDomain()) {
error = results$message,
id = results$id
)

if(env$errors[[name]]){
env$errors[[name]](results)
}
}

http_response_json(results, status)
Expand Down Expand Up @@ -164,6 +198,10 @@ com_send <- \(name, session = shiny::getDefaultReactiveDomain()) {
status <- 200L
if(inherits(results, "error")) {
status <- 400L
if(length(env$errors[[name]])){
env$errors[[name]](results)
}

results <- list(
error = results$message,
id = results$id
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
Small framework to communicate between Shiny client and server via HTTP requests.
Run `communicate::example()` for a short demo.

[Site](https://communicate.opifex.org/)

</div>

## Installation
Expand Down
2 changes: 1 addition & 1 deletion docs/_coverpage.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@
[GitHub](https://github.com/devOpifex/communicate)
[Get Started](/r)

![color](#3f3f3f)
![color](#fff)
5 changes: 4 additions & 1 deletion docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="description" content="Description">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@4/lib/themes/dark.css">
<link
rel="stylesheet"
href="//cdn.jsdelivr.net/npm/docsify@4/themes/vue.css"
/>
</head>
<body>
<div id="app"></div>
Expand Down
2 changes: 2 additions & 0 deletions docs/js.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ packer::npm_install("@devopifex/communicate")
Ideally you'd want to catch errors as shown below,
where the callback fails because there is no default for `x`.

See the R Package page to see how to handle errors R-side.

```r
library(shiny)
library(communicate)
Expand Down
43 changes: 43 additions & 0 deletions docs/r.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,46 @@ server <- \(input, output, session){

shinyApp(ui, server)
```

## Error handling

Finally, you can pass an error handler.

Note that the code below purposely fails.

```r
library(shiny)
library(communicate)

add <- \(x, y){
x + y + "error" # this will cause an error
}

err <- \(error){
cat("Aaaaah, an error!\n")
print(error)
}

# more on JavaScript error handling in the JavaScript page
script <- "
$('#btn').on('click', () => {
communicate.com('add', {x: 1})
.then(res => alert(`equals: ${res}`))
.catch(error => alert('There was an error')); # catch error
})
"

ui <- fluidPage(
# import dependencies
useCommunicate(),
h1("Hello"),
tags$a("Communicate", id = "btn"),
tags$script(HTML(script))
)

server <- \(input, output, session){
com("add", add)(x = Integer, y = Numeric)(y = 1.1)(err)
}

shinyApp(ui, server)
```
3 changes: 3 additions & 0 deletions man/constructors.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions srcjs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ window.Shiny.addCustomMessageHandler(
},
);

async function com(id: string, args) {
async function com(id: string, args: any) {
if (!id) {
throw new Error("No id provided");
}
Expand Down Expand Up @@ -125,7 +125,7 @@ function makeQuery(id: string, args: any): string {
return argNames
.map((argName) => {
let arg = args[argName];
const valid = valids.find((valid) => valid.name === argName);
const valid = valids.find((valid: any) => valid.name === argName);

if (!valid) {
throw new Error(
Expand Down

0 comments on commit d046f29

Please sign in to comment.