diff --git a/.Rbuildignore b/.Rbuildignore index 5e4a333..05b421c 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -1,2 +1,5 @@ ^LICENSE\.md$ ^\.github$ +^_pkgdown\.yml$ +^docs$ +^pkgdown$ diff --git a/.github/workflows/pkgdown.yaml b/.github/workflows/pkgdown.yaml new file mode 100644 index 0000000..b7a5210 --- /dev/null +++ b/.github/workflows/pkgdown.yaml @@ -0,0 +1,43 @@ +# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples +# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help +on: + push: + branches: [main] + +name: pkgdown + +jobs: + pkgdown: + runs-on: ubuntu-latest + # Only restrict concurrency for non-PR jobs + concurrency: + group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }} + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + permissions: + contents: write + steps: + - uses: actions/checkout@v3 + + - uses: r-lib/actions/setup-pandoc@v2 + + - uses: r-lib/actions/setup-r@v2 + with: + use-public-rspm: true + + - uses: r-lib/actions/setup-r-dependencies@v2 + with: + extra-packages: any::pkgdown, local::. + needs: website + + - name: Build site + run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) + shell: Rscript {0} + + - name: Deploy to GitHub pages 🚀 + if: github.event_name != 'pull_request' + uses: JamesIves/github-pages-deploy-action@v4.4.1 + with: + clean: false + branch: gh-pages + folder: docs diff --git a/.gitignore b/.gitignore index e75435c..1e236e1 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,4 @@ po/*~ # RStudio Connect folder rsconnect/ +docs diff --git a/DESCRIPTION b/DESCRIPTION index 36d6428..c280b7b 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: tower -Title: Easy middle ware library for Shiny -Version: 0.0.0.9000 +Title: Easy Middle Ware Library for Shiny +Version: 0.1.0 Authors@R: c(person(given = "ixpantia, SRL", role = "cph", @@ -16,9 +16,9 @@ Encoding: UTF-8 Roxygen: list(markdown = TRUE) RoxygenNote: 7.2.3 Imports: - shiny, compiler Suggests: testthat (>= 3.0.0), + shiny, stringr Config/testthat/edition: 3 diff --git a/README.md b/README.md index 12119b6..605dc38 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,76 @@ +# tower + [![R-CMD-check](https://github.com/ixpantia/tower/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/ixpantia/tower/actions/workflows/R-CMD-check.yaml) + +Dead simple middleware for R Shiny. + +## Summary + +`tower` is a simple library for adding middleware to Shiny applications. +It is inspired by the [tower](https://docs.rs/tower/latest/tower/) crate for Rust. +It is designed to enable package authors and Shiny developers to extend +Shiny a little bit more than what is usually possible. + +You can use `tower` to add middlewares that forward, modify, or intercept +requests in Shiny applications. This can be useful for adding logging, authentication, +caching, or routing to your Shiny applications. + +## Installation + +You can install the development version of `tower` from GitHub with: + +``` r +# install.packages("remotes") +remotes::install_github("ixpantia/tower") +``` + +## Example + +We may want to add a new route to our Shiny application that adds a count +to a counter every time a user visits the route. We can do this with `tower` +by adding a middleware that intercepts the request and increments the counter. + +``` r +library(shiny) +library(tower) + +# Counter environment +COUNTER <- new.env() +COUNTER$counter <- 0 + +# Middleware to increment the counter +increment_counter <- function(req) { + if (req$PATH_INFO == "/increment") { + COUNTER$counter <- COUNTER$counter + 1 + return( + httpResponse( + 200, + "text/plain", + paste("Counter is now", COUNTER$counter) + ) + ) + } +} + +# A very empty Shiny app (not necessary for the demo) +ui <- fluidPage() +server <- function(input, output, session) {} + +shinyApp(ui, server) |> + create_tower() |> + add_http_layer(increment_counter) |> + build_tower() +``` + +If you run the code above and visit the route `/increment` in your browser, +you will see the counter increment every time you visit the route. + +## How it works + +Basically, `tower` adds layers to a Shiny application. A layer is a function +that takes a request and returns either a response or NULL. If a layer returns +a response, the response is sent to the client and the request is not forwarded +to the next layer. If a layer returns NULL, the request is forwarded to the next +layer. diff --git a/_pkgdown.yml b/_pkgdown.yml new file mode 100644 index 0000000..d71acfb --- /dev/null +++ b/_pkgdown.yml @@ -0,0 +1,4 @@ +url: ~ +template: + bootstrap: 5 + diff --git a/inst/examples/counter/app.R b/inst/examples/counter/app.R new file mode 100644 index 0000000..8a801df --- /dev/null +++ b/inst/examples/counter/app.R @@ -0,0 +1,29 @@ +library(shiny) +library(tower) + +# Counter environment +COUNTER <- new.env() +COUNTER$counter <- 0 + +# Middleware to increment the counter +increment_counter <- function(req) { + if (req$PATH_INFO == "/increment") { + COUNTER$counter <- COUNTER$counter + 1 + return( + httpResponse( + 200, + "text/plain", + paste("Counter is now", COUNTER$counter) + ) + ) + } +} + +# A very empty Shiny app (not necesarry for the demo) +ui <- fluidPage() +server <- function(input, output, session) {} + +shinyApp(ui, server) |> + create_tower() |> + add_http_layer(increment_counter) |> + build_tower()