[education/rkward/devel/workspace_output] /: Rework the (unreleased) R interface to workspace output.

Thomas Friedrichsmeier null at kde.org
Sat Oct 10 20:10:56 BST 2020


Git commit f8a2dc17d3b637d2830cf9fb022bb7fc4350fea2 by Thomas Friedrichsmeier.
Committed on 10/10/2020 at 19:09.
Pushed by tfry into branch 'devel/workspace_output'.

Rework the (unreleased) R interface to workspace output.

This will still require a bunch of changes in the frontend, but I think it will help
clarity there, too.

M  +1    -0    rkward/rbackend/rpackages/rkward/DESCRIPTION
M  +1    -3    rkward/rbackend/rpackages/rkward/NAMESPACE
M  +0    -88   rkward/rbackend/rpackages/rkward/R/rk.filename-functions.R
A  +107  -0    rkward/rbackend/rpackages/rkward/R/rk.output.R
M  +0    -18   rkward/rbackend/rpackages/rkward/man/rk.get.tempfile.name.Rd
M  +3    -1    scripts/roxygenize.sh

https://invent.kde.org/education/rkward/commit/f8a2dc17d3b637d2830cf9fb022bb7fc4350fea2

diff --git a/rkward/rbackend/rpackages/rkward/DESCRIPTION b/rkward/rbackend/rpackages/rkward/DESCRIPTION
index b106db1c..a3ec8f86 100755
--- a/rkward/rbackend/rpackages/rkward/DESCRIPTION
+++ b/rkward/rbackend/rpackages/rkward/DESCRIPTION
@@ -33,6 +33,7 @@ Collate:
     'rk.edit-functions.R'
     'rk.filename-functions.R'
     'rk.label-functions.R'
+    'rk.output.R'
     'rk.plugin-functions.R'
     'rk.print-functions.R'
     'rk.replace.function.R'
diff --git a/rkward/rbackend/rpackages/rkward/NAMESPACE b/rkward/rbackend/rpackages/rkward/NAMESPACE
index 8a1d79b9..51c6fab4 100644
--- a/rkward/rbackend/rpackages/rkward/NAMESPACE
+++ b/rkward/rbackend/rpackages/rkward/NAMESPACE
@@ -54,7 +54,6 @@ export(rk.call.plugin)
 export(rk.capture.output)
 export(rk.check.for.pandoc)
 export(rk.clear.plot.history)
-export(rk.create.output.dir)
 export(rk.demo)
 export(rk.describe.alternative)
 export(rk.discard.preview.data)
@@ -63,7 +62,6 @@ export(rk.edit)
 export(rk.edit.files)
 export(rk.embed.device)
 export(rk.end.capture.output)
-export(rk.export.output.dir)
 export(rk.first.plot)
 export(rk.flush.output)
 export(rk.force.append.plot)
@@ -79,7 +77,6 @@ export(rk.graph.off)
 export(rk.graph.on)
 export(rk.header)
 export(rk.home)
-export(rk.import.output.dir)
 export(rk.last.plot)
 export(rk.list)
 export(rk.list.labels)
@@ -89,6 +86,7 @@ export(rk.load.pluginmaps)
 export(rk.make.repos.string)
 export(rk.next.plot)
 export(rk.old.packages)
+export(rk.output)
 export(rk.plot.history.summary)
 export(rk.previous.plot)
 export(rk.print)
diff --git a/rkward/rbackend/rpackages/rkward/R/rk.filename-functions.R b/rkward/rbackend/rpackages/rkward/R/rk.filename-functions.R
index f519c204..8c72f880 100644
--- a/rkward/rbackend/rpackages/rkward/R/rk.filename-functions.R
+++ b/rkward/rbackend/rpackages/rkward/R/rk.filename-functions.R
@@ -268,91 +268,3 @@
 
 	rk.set.output.html.file (x, ...)
 }
-
-
-## TODO move the following functions (and some others to a dedicated file/help page)
-
-#' A Reference Class to represent an RKWard Output directory.
-#' @name RK.Output
-#' @description Reference class to represent an RKWard Output directory.
-#' \code{activate} Set this output directory as the one where output will be appended.
-#' @param index if specified: Write to a specific HTML file within the output directory, instead of the default.
-#' @returns NULL
-#' @import methods
-#' @exportClass RK.Output
-"RK.Output"<-setRefClass(Class="RK.Output",
-	fields=list(id="character"),
-	methods=list(
-		activate=function() {
-			.rk.do.call("output", c ("activate", id))
-		},
-		isEmpty=function() {
-			isTRUE(.rk.do.call("output", c ("isEmpty", id)))
-		},
-		isModified=function() {
-			isTRUE(.rk.do.call("output", c ("isModified", id)))
-		},
-		load=function(url, overwrite=FALSE, ask=FALSE) {
-			if (missing(url)) stop("No url specified")
-			.rk.do.call("output", c ("load", id, url, isTRUE(overwrite), isTRUE(ask)))
-		},
-		save=function(url, overwrite=FALSE, ask=FALSE) {
-			.rk.do.call("output", c ("save", id, url, isTRUE(overwrite), isTRUE(ask)))
-		},
-		export=function(url, overwrite=FALSE, ask=FALSE) {
-			if (missing(url)) stop("No url specified")
-			.rk.do.call("output", c ("export", id, url, isTRUE(overwrite), isTRUE(ask)))
-		},
-		clear=function(ask=TRUE) {
-			.rk.do.call("output", c ("clear", id, isTRUE(ask)))
-		},
-		close=function(discard=FALSE, ask=FALSE) {
-			.rk.do.call("output", c ("close", isTRUE(discard), isTRUE(ask)))
-		},
-		view=function(raise=TRUE) {
-			.rk.do.call("output", c ("view", isTRUE(raise)))
-		},
-		.workingDir=function() {
-			.rk.do.call("output", c ("workingDir"))
-		},
-		.targetDir=function() {
-			.rk.do.call("output", c ("targetDir"))
-		}
-	))
-
-
-# 
-# .rk.purge.target.dir (target, ask) {
-# 	if (dir.exists (target)) {
-# 		if (isTRUE (ask)) {
-# 			if (!rk.show.question (paste ("The target directory (", target, ") already exists, and proceeding will delete everyhting inside this directory. Are you sure you want to proceed?", sep = ""))) {
-# 				stop("Aborted by user")
-# 			}
-# 		}
-# 		try (target, recursive=TRUE)
-# 	}
-# 	stopifnot (dir.create (target.dir, showWarnings=FALSE, recursive=TRUE))
-# }
-
-#' @export
-#' @rdname rk.get.tempfile.name
-rk.export.output.dir <- function (source.dir=basename (rk.get.output.html.file ()), target.dir, ask=TRUE) {
-# This is not terribly complex, but we need an implementation in the frontend, anyway, so we use that.
-	.rk.do.call ("output", c ("export", source.dir, target.dir, as.character (isTRUE (ask))))
-}
-
-#' @export
-#' @rdname rk.get.tempfile.name
-rk.import.output.dir <- function (source.dir, activate="index.html", mode=c("ask", "integrate", "keep.separate"), ask=TRUE) {
-# This is not terribly complex, but we need an implementation in the frontend, anyway, so we use that.
-	.rk.do.call ("output", c ("import", source.dir, activate, as.character (isTRUE (ask))))
-	rk.get.output.html.file ()
-}
-
-#' @export
-#' @rdname rk.get.tempfile.name
-rk.create.output.dir <- function () {
-# This is not terribly complex, but we need an implementation in the frontend, anyway, so we use that.
-	.rk.do.call ("output", c ("create"))
-	rk.get.output.html.file ()
-}
diff --git a/rkward/rbackend/rpackages/rkward/R/rk.output.R b/rkward/rbackend/rpackages/rkward/R/rk.output.R
new file mode 100644
index 00000000..9825c8ba
--- /dev/null
+++ b/rkward/rbackend/rpackages/rkward/R/rk.output.R
@@ -0,0 +1,107 @@
+#' Class and functions for organizing RKWard output.
+#' @name RK.Output
+#'
+#' @description Since version 0.7.5, RKWard supports more than one piece of output. While dealing with only a single output page, there will be no need for the user to call any of
+#'              these functions, directly, as exactly one output
+#'              is always opened for writing in RKWard. However, these functions allow to manage several distinct output pages, programmatically.
+#'
+#'              The primary entry point is the function \code{rk.output}, which allows to retrieve the current output directly, or to load or create a new one. This will return
+#'              an instance of the \code{RK.Output} reference class, which has methods for subsequent manipulation. Two instances of this class may be pointing to the same
+#'              logical output file/directory (e.g. when loading the same output file, twice), in which case any operation will affect both instances the same.
+#'
+#'              Internally, outputs are managed by the RKWard frontend. The frontend will ask to save any unsaved modified output pages on exit.
+#'
+#'              At the time of this writing, output is stored in directories containing an HTML index file, and, usually, several image files, and possibly more.
+#'              However other types of output may be supported in the future, and therefore assumptions about the details of the output storage should be avoided.
+#'
+#'              \code{rk.output} can be used to create or load output files, as well as to obtain a reference to an already loaded output file. After that, use the class methods
+#'              to operate on the reference obtained.
+#'
+#' @param filename The location where an existing output directory is loaded from or saved/exported to. Note that this is usually not the same location where functions such as
+#'                 \link{rk.print} will write to (these operate on a temporary "working copy", but rather the target directory, where changes should eventually be saved
+#'                 back, to.
+#'
+#' @param create If \code{TRUE}, create a new output directory. The parameter \code{filename}, if specified, is the target save file/directory, in this case. Should this already exist,
+#'               an error will be raised. If \code{FALSE}, load or re-use an existing output directory. If the parameter \code{filename} is left \code{NULL}, \code{rk.output} will
+#'               return the currently active output.
+#'
+#' @param all If \code{TRUE}, return a list of all currently loaded output directories.
+#'
+#' @param overwrite If \code{TRUE}, RKWard will overwrite any existing output when saving, or discard any existing modifications when closing or reverting an output. If \code{FALSE}, trying
+#'                  to overwrite/discard existing files/modifications will result in an error. If \code{NULL} (the default), the frontend will ask what to do in this case.
+#'
+#' @param discard See \code{overwrite} for the meaning.
+#'
+#' @param ask Prompt the user for confirmation before potentially desctrucitve operations such as closing or reverting a modified output.
+#'
+#' @param raise Raise the output window, if it is already visble.
+#'
+#' @returns NULL
+#' @field id An internal identifier. NULL for a closed output. This should be treated as read-only, but you can use this to test whether two output handles are the same.
+#' @import methods
+#' @exportClass RK.Output
+RK.Output <- setRefClass(Class="RK.Output", fields=list(id="character"),
+	methods=list(
+	# The implementation of most of these is not terribly complex, but we need an implementation in the frontend, anyway, so we use that.
+		activate=function() {
+"Set this output as the one that rk.print and other RKWard Output functions will write to."
+			.rk.do.call("output", c ("activate", .checkId()))
+		},
+		isEmpty=function() {
+"Returns TRUE, if the output is currently empty."
+			isTRUE(.rk.do.call("output", c ("isEmpty", .checkId())))
+		},
+		isModified=function() {
+"Returns TRUE, if this output has any changes that may need saving."
+			isTRUE(.rk.do.call("output", c ("isModified", .checkId())))
+		},
+		revert=function(ask=TRUE) {
+"Revert this output to the last saved state. If no previous state is available (never saved, before), clears the output."
+			.rk.do.call("output", c ("revert", .checkId(), isTRUE(ask)))
+		},
+		save=function(filename, overwrite=NULL) {
+"Save this output, either to the last known save location (if no filename is specified) or to a new location (\"save as\")."
+			.rk.do.call("output", c ("save", .checkId(), filename, if (is.Null(overwrite)) "ask" else isTRUE(overwrite)))
+		},
+		export=function(filename, overwrite=NULL) {
+"Save this output, to the specified location, but keep it associated with the previous location (\"save a copy\")."
+			if (missing(filename)) stop("No file name specified")
+			.rk.do.call("output", c ("export", .checkId(), filename, if (is.Null(overwrite)) "ask" else isTRUE(overwrite)))
+		},
+		clear=function(ask=TRUE) {
+"Clear all content from this output. As with any function in this class, this affects the working copy, only, until you call save. Therefore, by default, the user will be prompted for confirmation
+if and only if there are unsaved changes pending."
+			.rk.do.call("output", c ("clear", .checkId(), isTRUE(ask)))
+		},
+		close=function(discard=NULL) {
+"Forget about this output file, also closing any open views. Note: Trying to call any further methods on this object will fail."
+			.rk.do.call("output", c ("close", .checkId(), isTRUE(discard), isTRUE(ask)))
+			id=NULL
+		},
+		view=function(raise=TRUE) {
+"Open this output for viewing in the frontend."
+			.rk.do.call("output", c ("view", .checkId(), isTRUE(raise)))
+		},
+		.workingDir=function() {
+"The path of the working copy of this object. Please don't use this except for automated tests. The internals may be subject to change."
+			.rk.do.call("output", c ("workingDir", .checkId()))
+		},
+		filename=function() {
+"Return the target filename for this output, i.e. the location where it will be saved, to. This will be an empty string for newly created outputs that have not been saved, yet.
+Do not write anything to the target filename, directly! This is purely for information."
+			.rk.do.call("output", c ("filename", .checkId()))
+		},
+		.checkId=function() {
+"For internal use: Throws an error, if the id parameter is NULL or too long, returns a length one character vector otherwise."
+			i <- as.character(id)
+			if (length(i) != 1) stop ("Invalid output id. Use rk.output() to obtain a valid output handle.")
+			i
+		}
+	))
+
+#' @export
+#' @rdname RK.Output
+"rk.output" <- function(filename=NULL, create=FALSE, all=FALSE) {
+	if(all && (!is.Null(filename) || create)) stop("'all' cannot be combined with 'create' or 'filename'")
+	.rk.do.call("output", c ("get", as.character(isTRUE(create)), as.character(isTRUE(all)), as.character(load)))
+}
diff --git a/rkward/rbackend/rpackages/rkward/man/rk.get.tempfile.name.Rd b/rkward/rbackend/rpackages/rkward/man/rk.get.tempfile.name.Rd
index 743d3e52..8fe937e3 100644
--- a/rkward/rbackend/rpackages/rkward/man/rk.get.tempfile.name.Rd
+++ b/rkward/rbackend/rpackages/rkward/man/rk.get.tempfile.name.Rd
@@ -8,9 +8,6 @@
 \alias{rk.tempdir}
 \alias{rk.home}
 \alias{rk.flush.output}
-\alias{rk.export.output.dir}
-\alias{rk.import.output.dir}
-\alias{rk.create.output.dir}
 \title{RKWard file names}
 \usage{
 rk.get.tempfile.name(
@@ -39,21 +36,6 @@ rk.flush.output(
   ask = TRUE,
   ...
 )
-
-rk.export.output.dir(
-  source.dir = basename(rk.get.output.html.file()),
-  target.dir,
-  ask = TRUE
-)
-
-rk.import.output.dir(
-  source.dir,
-  activate = "index.html",
-  mode = c("ask", "integrate", "keep.separate"),
-  ask = TRUE
-)
-
-rk.create.output.dir()
 }
 \arguments{
 \item{prefix}{a string, used as a filename prefix when saving images to the
diff --git a/scripts/roxygenize.sh b/scripts/roxygenize.sh
index 7366435d..a32ac0cf 100755
--- a/scripts/roxygenize.sh
+++ b/scripts/roxygenize.sh
@@ -27,10 +27,12 @@ done
 
 echo "
 	library (roxygen2)
+	library (devtools)
 	packages <- c ($PACKAGES)
 	for (package in packages) {
 		#dummy <- roxygen2:::source_package (package) # See https://github.com/klutometis/roxygen/issues/167
-		roxygenize (package, load_code=load_source)
+		document (package)
+#		roxygenize (package, load_code=load_source)
 		possibly_empty_dirs <- paste (package, c ('inst/doc', 'inst'), sep='/')
 		for (dir in possibly_empty_dirs) {
 			if (file.exists (dir)) suppressWarnings (try (file.remove (dir)))




More information about the rkward-tracker mailing list