From 2935041135b49e5355740c03437001e5e42caad8 Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Thu, 22 Oct 2026 15:59:02 -0400 Subject: [PATCH] feat(R): Add reactlog_read() to read back reactlog_write() output Fixes #71 --- NAMESPACE | 1 + NEWS.md | 2 ++ R/showReactLog.R | 21 +++++++++++++++++++++ man/reactlog_read.Rd | 18 ++++++++++++++++++ man/reactlog_write.Rd | 10 ++++++++++ tests/testthat/test-render.R | 16 ++++++++++++++++ 6 files changed, 68 insertions(+) create mode 100644 man/reactlog_read.Rd diff --git a/NAMESPACE b/NAMESPACE index c4f8636..8615497 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -4,6 +4,7 @@ export(reactlog_disable) export(reactlog_enable) export(reactlog_module_server) export(reactlog_module_ui) +export(reactlog_read) export(reactlog_render) export(reactlog_show) export(reactlog_write) diff --git a/NEWS.md b/NEWS.md index 8996ec4..7f4e66b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,6 +4,8 @@ - The graph legend now shows that a solid line is a dependency, a dashed line is an `isolate()`d dependency, and a dotted line is an inactive dependency (one that existed earlier but is not active at the current step) (#76) +- New `reactlog_read()` reads a file written by `reactlog_write()` back into a log that `reactlog_show()` can display (#71) + - Migrated project license from GPL-3 to the MIT License. - Added a `?` button to the reactlog header. Hover over it or click it to see every keyboard shortcut, with key names for your operating system. The navigation button tooltips now also use your operating system's key names. The vignette has the same list (#73) diff --git a/R/showReactLog.R b/R/showReactLog.R index 80650cd..94f9ddb 100644 --- a/R/showReactLog.R +++ b/R/showReactLog.R @@ -76,7 +76,14 @@ reactlog_show <- function(log = shiny::reactlog(), time = TRUE, ...) { #' json representation is return. #' @param session_token Session token identifier to be used when subsetting the #' complete reactlog +#' @seealso [reactlog_read()] to read the file back in. #' @export +#' @examples +#' \dontrun{ +#' reactlog_write(shiny::reactlog(), "reactlog.json") +#' log <- reactlog_read("reactlog.json") +#' reactlog_show(log) +#' } reactlog_write <- function(log, file = stdout(), session_token = NULL) { if (!is.null(session_token)) { log <- Filter( @@ -103,6 +110,20 @@ reactlog_write <- function(log, file = stdout(), session_token = NULL) { } } +#' Read reactlog +#' +#' Read a reactlog written by [reactlog_write()], so it can be passed to +#' [reactlog_show()] or [reactlog_render()]. +#' +#' @param file JSON file written by [reactlog_write()]. +#' @return The reactlog: a list of log entries with a `version` attribute. +#' @export +reactlog_read <- function(file) { + log <- jsonlite::read_json(file, simplifyVector = FALSE) + # ponytail: the JSON has no version field; "1" is the only version so far + attr(log, "version") <- "1" + log +} write_utf8 <- function(x, file, ...) { writeLines(enc2utf8(x), file, ..., useBytes = TRUE) diff --git a/man/reactlog_read.Rd b/man/reactlog_read.Rd new file mode 100644 index 0000000..0bd0c76 --- /dev/null +++ b/man/reactlog_read.Rd @@ -0,0 +1,18 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/showReactLog.R +\name{reactlog_read} +\alias{reactlog_read} +\title{Read reactlog} +\usage{ +reactlog_read(file) +} +\arguments{ +\item{file}{JSON file written by \code{\link[=reactlog_write]{reactlog_write()}}.} +} +\value{ +The reactlog: a list of log entries with a \code{version} attribute. +} +\description{ +Read a reactlog written by \code{\link[=reactlog_write]{reactlog_write()}}, so it can be passed to +\code{\link[=reactlog_show]{reactlog_show()}} or \code{\link[=reactlog_render]{reactlog_render()}}. +} diff --git a/man/reactlog_write.Rd b/man/reactlog_write.Rd index 31fa8fc..50c760d 100644 --- a/man/reactlog_write.Rd +++ b/man/reactlog_write.Rd @@ -20,3 +20,13 @@ Write the reactlog to a file. If a session token is provided, all reactive interactions will be subsetted to either the global session or the session provided. } +\examples{ +\dontrun{ +reactlog_write(shiny::reactlog(), "reactlog.json") +log <- reactlog_read("reactlog.json") +reactlog_show(log) +} +} +\seealso{ +\code{\link[=reactlog_read]{reactlog_read()}} to read the file back in. +} diff --git a/tests/testthat/test-render.R b/tests/testthat/test-render.R index a0fc4e0..5236f0e 100644 --- a/tests/testthat/test-render.R +++ b/tests/testthat/test-render.R @@ -32,3 +32,19 @@ test_that("reactlog_show() defaults to shiny::reactlog()", { reactlog_show() expect_true(called) }) + +test_that("reactlog_read() reads back what reactlog_write() wrote", { + log <- list(list( + action = "define", reactId = "r1", label = "a\\b\"c\\", + type = "observable", session = NULL, time = 1 + )) + attr(log, "version") <- "1" + file <- tempfile(fileext = ".json") + on.exit(unlink(file)) + reactlog_write(log, file) + + log2 <- reactlog_read(file) + expect_identical(attr(log2, "version"), "1") + expect_identical(reactlog_write(log2, NULL), reactlog_write(log, NULL)) + expect_no_error(reactlog_render(log2)) +})