GlobalOptions/0000755000176200001440000000000013670235703013034 5ustar liggesusersGlobalOptions/NAMESPACE0000644000176200001440000000136213666773336014273 0ustar liggesusersS3method("$", "GlobalOptionsFun") export("$.GlobalOptionsFun") S3method("$", "InternalOptionValue") export("$.InternalOptionValue") S3method("$<-", "GlobalOptionsFun") export("$<-.GlobalOptionsFun") S3method(".DollarNames", "GlobalOptionsFun") export(".DollarNames.GlobalOptionsFun") S3method("[", "GlobalOptionsFun") export("[.GlobalOptionsFun") S3method("[[", "GlobalOptionsFun") export("[[.GlobalOptionsFun") S3method("[[<-", "GlobalOptionsFun") export("[[<-.GlobalOptionsFun") S3method("names", "GlobalOptionsFun") export("names.GlobalOptionsFun") S3method("print", "GlobalOptionsFun") export("print.GlobalOptionsFun") export(".v") export("dump_opt") export("setGlobalOptions") export("set_opt") import("methods") importFrom("utils", .DollarNames) GlobalOptions/LICENSE0000644000176200001440000000005113451161570014032 0ustar liggesusersYEAR: 2019 COPYRIGHT HOLDER: Zuguang Gu GlobalOptions/man/0000755000176200001440000000000013670151477013614 5ustar liggesusersGlobalOptions/man/Subset.InternalOptionValue.Rd0000644000176200001440000000076113666773336021326 0ustar liggesusers\name{$.InternalOptionValue} \alias{$.InternalOptionValue} \alias{Subset.InternalOptionValue} \title{ Get value of other options } \description{ Get value of other options } \usage{ \method{$}{InternalOptionValue}(x, nm) } \arguments{ \item{x}{should always be \code{.v}} \item{nm}{name of the option} } \details{ \code{.v$nm} is basically a short version of \code{.v(nm)}. } \seealso{ \code{\link[=Dot.v]{.v}} } \author{ Zuguang Gu } \examples{ # There is no example NULL } GlobalOptions/man/dump_opt.Rd0000644000176200001440000000077413666773336015753 0ustar liggesusers\name{dump_opt} \alias{dump_opt} \title{ Print all fields of a single option } \description{ Print all fields of a single option } \usage{ dump_opt(opt, opt_name) } \arguments{ \item{opt}{the option object returned by \code{\link{set_opt}} or \code{\link{setGlobalOptions}}.} \item{opt_name}{a single name of the option.} } \details{ Actually this function is identical to \code{opt[opt_name]}. } \author{ z.gu@dkfz.de } \examples{ opt = set_opt(a = 1, b = "b") dump_opt(opt, "a") dump_opt(opt, "b") } GlobalOptions/man/names.GlobalOptionsFun.Rd0000644000176200001440000000057013666773336020445 0ustar liggesusers\name{names.GlobalOptionsFun} \alias{names.GlobalOptionsFun} \title{ Option names } \description{ Option names } \usage{ \method{names}{GlobalOptionsFun}(x) } \arguments{ \item{x}{the option object returned by \code{\link{set_opt}} or \code{\link{setGlobalOptions}}.} } \value{ A vector of option names } \examples{ opt = set_opt( a = 1, b = "text" ) names(opt) } GlobalOptions/man/Extract.GlobalOptionsFun.Rd0000644000176200001440000000102213666773336020745 0ustar liggesusers\name{[.GlobalOptionsFun} \alias{[.GlobalOptionsFun} \alias{Extract.GlobalOptionsFun} \title{ Get a single GlobalOption object } \description{ Get a single GlobalOption object } \usage{ \method{[}{GlobalOptionsFun}(x, nm) } \arguments{ \item{x}{the option object returned by \code{\link{set_opt}} or \code{\link{setGlobalOptions}}.} \item{nm}{a single name of the option.} } \details{ This function is only used internally. } \author{ Zuguang Gu } \examples{ opt = set_opt(a = 1, b = "b") opt["a"] opt["b"] } GlobalOptions/man/setGlobalOptions.Rd0000644000176200001440000000523613666773336017412 0ustar liggesusers\name{setGlobalOptions} \alias{setGlobalOptions} \title{ Produce a function which can get or set global options } \description{ Produce a function which can get or set global options } \usage{ setGlobalOptions(...) } \arguments{ \item{...}{specification of options, see 'details' section} } \details{ The function has a short name \code{\link{set_opt}}. The most simple way is to construct an option function (e.g. \code{opt()}) as: \preformatted{ opt = set_opt( "a" = 1, "b" = "text" ) } Then users can get or set the options by \preformatted{ opt() opt("a") opt$a opt[["a"]] opt(c("a", "b")) opt("a", "b") opt("a" = 2) opt$a = 2 opt[["a"]] = 2 opt("a" = 2, "b" = "new_text") } Options can be reset to their default values by: \preformatted{ opt(RESET = TRUE) } The value for each option can be set as a list which contains more configurations of the option: \preformatted{ opt = set_opt( "a" = list(.value = 1, .length = 1, .class = "numeric", .validate = function(x) x > 0) ) } The different fields in the list can be used to filter or validate the option values. \describe{ \item{\code{.value}}{The default value.} \item{\code{.length}}{The valid length of the option value. It can be a vector, the check will be passed if one of the length fits.} \item{\code{.class}}{The valid class of the option value. It can be a vector, the check will be passed if one of the classes fits.} \item{\code{.validate}}{Validation function. The input parameter is the option value and should return a single logical value.} \item{\code{.failed_msg}}{Once validation failed, the error message that is printed.} \item{\code{.filter}}{Filtering function. The input parameter is the option value and it should return a filtered option value.} \item{\code{.read.only}}{Logical. The option value can not be modified if it is set to \code{TRUE}.} \item{\code{.visible}}{Logical. Whether the option is visible to users.} \item{\code{.private}}{Logical. The option value can only be modified in the same namespace where the option function is created.} \item{\code{.synonymous}}{a single option name which should have been already defined ahead of current option. The option specified will be shared by current option.} \item{\code{.description}}{a short text for describing the option. The description is only used when printing the object.} } For more detailed explanation, please go to the vignette. } \author{ Zuguang Gu } \examples{ opt = set_opt( a = 1, b = "text" ) opt # for more examples, please go to the vignette } GlobalOptions/man/Subset.GlobalOptionsFun.Rd0000644000176200001440000000076213666773336020612 0ustar liggesusers\name{$.GlobalOptionsFun} \alias{$.GlobalOptionsFun} \alias{Subset.GlobalOptionsFun} \title{ Get option value by dollar symbol } \description{ Get option value by dollar symbol } \usage{ \method{$}{GlobalOptionsFun}(x, nm) } \arguments{ \item{x}{the object returned by \code{\link{set_opt}} or \code{\link{setGlobalOptions}}.} \item{nm}{a single option name.} } \details{ \code{opt$a} is same as \code{opt("a")}. } \author{ Zuguang Gu } \examples{ opt = set_opt(a = 1) opt$a } GlobalOptions/man/Assign.GlobalOptionsFun.Rd0000644000176200001440000000123113666773336020561 0ustar liggesusers\name{$<-.GlobalOptionsFun} \alias{$<-.GlobalOptionsFun} \alias{Assign.GlobalOptionsFun} \title{ Set option value by dollar symbol } \description{ Set option value by dollar symbol } \usage{ \method{$}{GlobalOptionsFun}(x, nm) <- value } \arguments{ \item{x}{the object returned by \code{\link{set_opt}} or \code{\link{setGlobalOptions}}.} \item{nm}{a single option name.} \item{value}{the value which is assigned to the option.} } \details{ \code{opt$a = 1} is same as \code{opt("a" = 1)}. Note you cannot reconfigurate the option by assigning a configuration list. } \author{ Zuguang Gu } \examples{ opt = set_opt(a = 1) opt$a = 2 opt$a } GlobalOptions/man/Dot.DollarNames.GlobalOptionsFun.Rd0000644000176200001440000000121213666773336022262 0ustar liggesusers\name{.DollarNames.GlobalOptionsFun} \alias{.DollarNames.GlobalOptionsFun} \alias{Dot.DollarNames.GlobalOptionsFun} \title{ The .DollarNames method for the GlobalOptionsFun class } \description{ The .DollarNames method for the GlobalOptionsFun class } \usage{ \method{.DollarNames}{GlobalOptionsFun}(x, pattern = "") } \arguments{ \item{x}{the object returned by \code{\link{set_opt}} or \code{\link{setGlobalOptions}}.} \item{pattern}{pattern, please ignore it.} } \details{ This makes the option object looks like a list that it allows option name completion after \code{$}. } \author{ z.gu@dkfz.de } \examples{ # There is no example NULL } GlobalOptions/man/ExtractExtractAssign.GlobalOptionsFun.Rd0000644000176200001440000000120613666773336023451 0ustar liggesusers\name{[[<-.GlobalOptionsFun} \alias{[[<-.GlobalOptionsFun} \alias{ExtractExtractAssign.GlobalOptionsFun} \title{ Set option value by subset operator } \description{ Set option value by subset operator } \usage{ \method{[[}{GlobalOptionsFun}(x, nm) <- value } \arguments{ \item{x}{the option object returned by \code{\link{set_opt}} or \code{\link{setGlobalOptions}}.} \item{nm}{a single option name.} \item{value}{the value which is assigned to the option.} } \details{ \code{opt[["a"]] = 1} is same as \code{opt("a" = 1)} or \code{opt$a = 1}. } \author{ Zuguang Gu } \examples{ opt = set_opt(a = 1) opt[["a"]] = 2 opt$a } GlobalOptions/man/Dot.v.Rd0000644000176200001440000000115713666773336015112 0ustar liggesusers\name{.v} \alias{.v} \alias{Dot.v} \title{ Get value of other options } \description{ Get value of other options } \usage{ .v(opt_name, name_is_character = NA) } \arguments{ \item{opt_name}{name of the option, can be quoted or not quoted.} \item{name_is_character}{whether \code{opt_name} is a character, only used internally.} } \details{ When setting one option, the value can be dependent on other option names. The current value of other option can be accessed by \code{v(nm)} or \code{v$nm}. } \author{ Zuguang Gu } \examples{ opt = set_opt(a = 1, b = function() .v$a*2) opt$b opt(a = 2); opt$b } GlobalOptions/man/set_opt.Rd0000644000176200001440000000062013666773336015567 0ustar liggesusers\name{set_opt} \alias{set_opt} \title{ Produce a function which can get or set global options } \description{ Produce a function which can get or set global options } \usage{ set_opt(...) } \arguments{ \item{...}{all go to \code{\link{setGlobalOptions}}} } \details{ This is just a short name for \code{\link{setGlobalOptions}}. } \author{ z.gu@dkfz.de } \examples{ # There is no example NULL } GlobalOptions/man/ExtractExtract.GlobalOptionsFun.Rd0000644000176200001440000000104213666773336022302 0ustar liggesusers\name{[[.GlobalOptionsFun} \alias{[[.GlobalOptionsFun} \alias{ExtractExtract.GlobalOptionsFun} \title{ Get option value by subset operator } \description{ Get option value by subset operator } \usage{ \method{[[}{GlobalOptionsFun}(x, nm) } \arguments{ \item{x}{the option object returned by \code{\link{set_opt}} or \code{\link{setGlobalOptions}}.} \item{nm}{a single option name.} } \details{ \code{opt[["a"]]} is same as \code{opt("a")} or \code{opt$a}. } \author{ Zuguang Gu } \examples{ opt = set_opt(a = 1) opt[["a"]] } GlobalOptions/man/print.GlobalOptionsFun.Rd0000644000176200001440000000064013666773336020474 0ustar liggesusers\name{print.GlobalOptionsFun} \alias{print.GlobalOptionsFun} \title{ Print the GlobalOptionsFun object } \description{ Print the GlobalOptionsFun object } \usage{ \method{print}{GlobalOptionsFun}(x, ...) } \arguments{ \item{x}{the option object returned by \code{\link{set_opt}} or \code{\link{setGlobalOptions}}.} \item{...}{other arguments} } \author{ z.gu@dkfz.de } \examples{ # There is no example NULL } GlobalOptions/DESCRIPTION0000644000176200001440000000122713670235703014544 0ustar liggesusersPackage: GlobalOptions Type: Package Title: Generate Functions to Get or Set Global Options Version: 0.1.2 Date: 2020-06-06 Author: Zuguang Gu Maintainer: Zuguang Gu Depends: R (>= 3.3.0), methods Imports: utils Suggests: testthat (>= 1.0.0), knitr, markdown, GetoptLong VignetteBuilder: knitr Description: It provides more configurations on the option values such as validation and filtering on the values, making options invisible or private. URL: https://github.com/jokergoo/GlobalOptions License: MIT + file LICENSE Repository: CRAN NeedsCompilation: no Packaged: 2020-06-10 12:34:07 UTC; jokergoo Date/Publication: 2020-06-10 20:00:03 UTC GlobalOptions/build/0000755000176200001440000000000013670151477014140 5ustar liggesusersGlobalOptions/build/vignette.rds0000644000176200001440000000032213670151477016474 0ustar liggesusersb```b`@YH39{N~RbAIf~^^Pn qԼԢĒTJR4eBd栩G *a4D0!)gKME% 5/$~hZ8S+`zP԰Aհe ,s\ܠL t7`~΢r=xAq$Gs=ʕXVr7GlobalOptions/tests/0000755000176200001440000000000013670151477014203 5ustar liggesusersGlobalOptions/tests/test-all.R0000644000176200001440000000010612704527430016041 0ustar liggesuserslibrary(testthat) library(GlobalOptions) test_check("GlobalOptions") GlobalOptions/tests/testthat/0000755000176200001440000000000012472454675016050 5ustar liggesusersGlobalOptions/tests/testthat/test.R0000644000176200001440000001664113546055670017155 0ustar liggesuserslibrary(testthat) library(GlobalOptions) context("Test `GlobalOptions`") "%==%" = function(x, y) { expect_that(x, is_identical_to(y)) } "%err%" = function(x, y) { expect_that(x, throws_error(y)) } opt = set_opt( a = 1, b = "text" ) test_that("get option values", { opt() %==% list(a = 1, b = "text") opt("a") %==% 1 opt[["a"]] %==% 1 opt$a %==% 1 opt("b") %==% "text" opt("c") %err% "No such option" opt(c("a", "b")) %==% list(a = 1, b = "text") opt("a", "b") %==% list(a = 1, b = "text") opt(c("a", "b", "c")) %err% "No such option" opt("a", "b", "c") %err% "No such option" }) test_that("set option values", { opt("a" = 2) opt("a") %==% 2 opt$a = 4 opt$a %==% 4 opt[["a"]] = 6 opt$a %==% 6 opt(RESET = TRUE) opt("a") %==% 1 opt("a" = 2, "b" = "str") opt("a") %==% 2 opt("b") %==% "str" opt(RESET = TRUE) op = opt() opt("a" = 2, "b" = "str") opt(op) opt("a") %==% 1 opt("b") %==% "text" opt("c" = 1) %err% "No such option" opt(1, "b" = "a") %err% "When setting options, all arguments should be named" opt(list(1, "b" = "a")) %err% "When setting options, all arguments should be named" opt("a" = 1, "c" = 1) %err% "No such option" }) test_that("testing valus are also list", { opt("a" = list(a = 1, b = 2)) expect_that(opt("a"), is_identical_to(list(a = 1, b = 2))) }) # testing if advanced setting is not mixed test_that("testing on mixed setting", { expect_that(opt <- set_opt( a = list(.value = 1, length = 1, class = "numeric") ), gives_warning("mixed")) expect_that(opt("a"), is_identical_to( list(.value = 1, length = 1, class = "numeric") )) }) # testing .length and .class opt = set_opt( a = list(.value = 1, .length = 1, .class = "numeric") ) test_that("tesing on .length and .class ", { expect_that(opt(), is_identical_to(list(a = 1))) expect_that(opt(a = 1:3), throws_error("Length of .* should be")) expect_that(opt(a = "text"), throws_error("Class of .* should be")) }) # testing read.only opt = set_opt( a = list(.value = 1, .read.only = TRUE), b = 2 ) test_that("tesing on .read.only ", { expect_that(opt(), is_identical_to(list(a = 1, b = 2))) expect_that(opt(a = 2), throws_error("is a read-only option")) expect_that(opt(READ.ONLY = TRUE), is_identical_to(list(a = 1))) expect_that(opt(READ.ONLY = FALSE), is_identical_to(list(b = 2))) }) opt = set_opt( a = list(.value = 1, .validate = function(x) x > 0, .failed_msg = "'a' should be a positive number.") ) test_that("testing on .failed_msg", { expect_that(opt(a = -1), throws_error("positive")) }) # testing .validate and .filter opt = set_opt( a = list(.value = 1, .validate = function(x) x > 0 && x < 10, .filter = function(x) c(x, x)) ) test_that("tesing on .validate and .filter ", { expect_that(opt(), is_identical_to(list(a = c(1)))) opt(a = 2) expect_that(opt(), is_identical_to(list(a = c(2, 2)))) expect_that(opt(a = 20), throws_error("Your option is invalid")) }) # test value after filter opt = set_opt( a = list(.value = 1, .length = 1, .filter = function(x) c(x, x)) ) test_that("testing on validation of filtered value", { expect_that(opt(a = 2), throws_error("Length of filtered")) }) # testing if .value is a function opt = set_opt( a = list(.value = 1), b = list(.value = 2, .class = "function"), c = list(.value = function(x) 3, .class = "numeric") ) test_that("testing if '.value' is set as a function", { #expect_that(opt(), is_identical_to(list(a = 1, b = 2, c = 3))) opt(a = function(x) 1) expect_that(opt("a"), is_identical_to(1)) opt(b = function(x) 2) expect_that(body(opt("b")), is_identical_to(2)) expect_that(opt(c = function(x) "text"), throws_error("Class of .* should be")) }) # testing if.value is a function and uses OPT opt = set_opt( a = 1, b = function() .v$a * 2 ) test_that("tesing if '.value' is a function and using other option values", { expect_that(opt("b"), is_identical_to(2)) opt(a = 2) expect_that(opt("b"), is_identical_to(4)) opt(RESET = TRUE) expect_that(opt("b"), is_identical_to(2)) }) # testing if.validate and .filter use OPT opt = set_opt( a = 1, b = list(.value = 2, .validate = function(x) { if(.v$a > 0) x > 0 else x < 0 }, .filter = function(x) { x + .v$a }) ) test_that("tesing '.validate' and '.filter' using other option values", { opt(a = 1, b = 2) expect_that(opt("b"), is_identical_to(3)) expect_that(opt(a = 1, b = -1), throws_error("Your option is invalid")) expect_that(opt(a = -1, b = 1), throws_error("Your option is invalid")) }) # test in input value is NULL opt = set_opt( a = 1 ) test_that("tesing if input value is NULL", { expect_that(opt(NULL), is_identical_to(NULL)) opt(a = NULL) expect_that(opt("a"), is_identical_to(NULL)) }) ## test if .value is invisible opt = set_opt( a = list(.value = 1, .visible = FALSE), b = 1 ) test_that("testing if '.value' is visible", { expect_that(opt(), is_identical_to(list(b = 1))) expect_that(opt("a"), is_identical_to(1)) opt(a = 2) expect_that(opt("a"), is_identical_to(2)) }) ############################################ opt = set_opt( a = 1 ) f1 = function() { opt(LOCAL = TRUE) opt(a = 2) return(opt("a")) } f1() # 2 f2 = function() { opt(LOCAL = TRUE) opt(a = 4) return(opt("a")) } f2() # 4 test_that("testing local mode", { expect_that(f1(), is_identical_to(2)) expect_that(f2(), is_identical_to(4)) expect_that(opt$a, is_identical_to(1)) opt(LOCAL = TRUE) opt(a = 4) expect_that(opt("a"), is_identical_to(4)) opt(LOCAL = FALSE) expect_that(opt("a"), is_identical_to(1)) }) opt = setGlobalOptions( a = 1 ) f1 = function() { opt(LOCAL = TRUE) opt(a = 2) return(f2()) } f2 = function() { opt("a") } f1() # 2 test_that("testing local mode 2", { expect_that(f1(), is_identical_to(2)) expect_that(opt("a"), is_identical_to(1)) }) opt = set_opt( a = 1 ) opt(LOCAL = TRUE) opt(a = 2) f1 = function() { return(opt("a")) } f1() test_that("testing local mode 3", { expect_that(f1(), is_identical_to(2)) }) opt = set_opt( a = list(.value = 1, .private = TRUE) ) require(stats) ns = getNamespace("stats") environment(opt)$options$a$`__generated_namespace__` = ns test_that("testing private", { expect_that(opt$a <- 2, throws_error("is a private option")) }) ########################################## opt = set_opt(a = NULL) opt$a = 1 opt$a = NULL test_that("testing set value to NULL", { expect_that(opt$a, is_identical_to(NULL)) }) ########################################## opt = set_opt(a = 1, b = list(".synonymous" = "a")) test_that("test .synonymous", { expect_that(opt$a, is_identical_to(opt$b)) opt(a = 2) expect_that(opt$a, is_identical_to(opt$b)) expect_that(opt$a, is_identical_to(2)) opt(b = 3) expect_that(opt$a, is_identical_to(opt$b)) expect_that(opt$a, is_identical_to(3)) expect_that(opt <- set_opt(a = 1, b = list(".synonymous" = "c"), c = 1), throws_error("has not been created yet")) }) #### test ADD opt = set_opt(a = 1) test_that("test ADD", { expect_that(opt$b <- 1, throws_error("No such option")) opt(b = 1, ADD = TRUE) expect_that(opt$b, is_identical_to(1)) opt(c = list(.value = "a", .class = "character"), ADD = TRUE) expect_that(opt$c <- 1, throws_error("should be")) opt(d = list(.value = 1, .class = "numeric"), e = list(.value = "a", .class = "character"), ADD = TRUE) expect_that(opt$d, is_identical_to(1)) expect_that(opt$e, is_identical_to("a")) }) GlobalOptions/vignettes/0000755000176200001440000000000013670151477015051 5ustar liggesusersGlobalOptions/vignettes/GlobalOptions.Rmd0000644000176200001440000003617613666773311020310 0ustar liggesusers--- title: "Generate Global Options" author: "Zuguang Gu (z.gu@dkfz.de)" date: '`r Sys.Date()`' output: html_document: fig_caption: true toc: true toc_depth: 3 toc_float: true vignette: > %\VignetteIndexEntry{Generate Global Options} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- --------------------------------------------------------------------- ```{r, echo = FALSE, message = FALSE} library(knitr) knitr::opts_chunk$set( error = FALSE, tidy = FALSE, message = FALSE, comment = NA, fig.align = "center") library(GetoptLong) ``` Global option function such as `options()` and `par()` provides a way to control global settings. Here the **GlobalOptions** package provides a more general and controlable way to generate such functions, which can: 1. validate the values (e.g. class, length and self-defined validations); 2. set read-only options; 3. set invisible options; 4. set private options which are only accessable in a certain namespace; 5. support local options and global option; 6. print options with explanations. ## General usage The most simple use is to generate an option function with default values by callling `setGlobalOptions()` or its short versoin `set_opt()`: ```{r} library(GlobalOptions) opt = set_opt( a = 1, b = "text" ) ``` The returned value `opt` is an option function which can be used to get or set options. Options in `opt` can be accessed either by specifying as arguments or by using the `$` operator. ```{r} opt() opt("a") opt$a op = opt() op opt(a = 2, b = "new text") opt() opt$b = "" opt() opt(op) opt() ``` `opt` generated by `set_opt()` contains an argument `RESET` which is used to reset the options to the default: ```{r} opt(a = 2, b = "new text") opt(RESET = TRUE) opt() ``` Simply printing `opt` gives a summary of all options. ```{r} opt ``` ## Advanced usage If option values are set as lists, more configurations can be customized. ### Simple validation There are two basic fields that are used to check the input option values: ```{r} opt = set_opt( a = list(.value = 1, .length = c(1, 3), .class = "numeric") ) ``` In above code, `.value` is the default value for the option `a`. The length of the value is controlled by `.length` and the length should be either 1 or 3. The class of the value should be `numeric`. If the input value does not fit these criterions, there will be an error. The value of `.length` or `.class` is a vector and the checking will be passed if one of the value fits user's input. ```{r error = TRUE, purl = FALSE} opt(a = 1:2) # there will be error because the length is 2 opt(a = "text") # there will be error because the input is character ``` ### Read-only options The value can be set as read-only by `.read.only` field and modifying such option will cause an error. ```{r error = TRUE, purl = FALSE} opt = set_opt( a = list(.value = 1, .read.only = TRUE) ) opt(a = 2) # there will be error because a is read-only ``` There is also a pre-defined argument `READ.ONLY` in `opt()` which controls whether to return only the read-only options or not. ```{r} opt = set_opt( a = list(.value = 1, .read.only = TRUE), b = 2 ) opt(READ.ONLY = TRUE) opt(READ.ONLY = FALSE) opt(READ.ONLY = NULL) # default, to return both ``` ### User-defined validation More customized validation of the option values can be controlled by `.validate` field. The value of `.validate` should be a function. The input of the validation function is the input option value and the function should only return a logical value. `a` should only between 0 and 10 in following example. ```{r error = TRUE, purl = FALSE} opt = set_opt( a = list(.value = 1, .validate = function(x) x > 0 && x < 10 ) ) opt(a = 20) # This will cause an error ``` `.failed_msg` is used to configure the error message once validation is failed. ```{r error = TRUE, purl = FALSE} opt = set_opt( a = list(.value = 1, .validate = function(x) x > 0 && x < 10, .failed_msg = "'a' should be in (0, 10)." ) ) opt(a = 20) # This will cause an error ``` ### Filter the option values Filtering on the option values can be controlled by `.filter` field. This is useful when the input option value is not valid but it is not necessary to throw errors. More proper way is to modify the value silently. For example, there is an option to control whether to print messages or not and it should be set to `TRUE` or `FALSE`. However, users may set some other type of values such as `NULL` or `NA`. In this case, non-`TRUE` values can be converted to logical values by `.filter`. Similar as `.validate`, the input value for filter function is the input option value, and it should return a filtered option value. ```{r} opt = set_opt( verbose = list(.value = TRUE, .filter = function(x) { if(is.null(x)) { return(FALSE) } else if(is.na(x)) { return(FALSE) } else { return(x) } }) ) opt(verbose = FALSE); opt("verbose") opt(verbose = NA); opt("verbose") opt(verbose = NULL); opt("verbose") ``` Another example is when there is an option which controls four margin values of a plot, the length of the value can either be 1, 2, or 4. With `.filter`, length can be normaliezd to 4 consistently. ```{r} opt = set_opt( margin = list(.value = c(1, 1, 1, 1), .length = c(1, 2, 4), .filter = function(x) { if(length(x) == 1) { return(rep(x, 4)) } else if(length(x) == 2) { return(rep(x, 2)) } else { return(x) } }) ) opt(margin = 2); opt("margin") opt(margin = c(2, 4)); opt("margin") ``` ### Dynamic querying the option value The input option value can be set dynamicly by setting it as a function. When the option value is set as a function and class of the option is non-function, it will be executed when querying the option. In the following example, the `prefix` option corresponds to the prefix of log messages. The returned option value is the string after the execution of the input function. ```{r} opt = set_opt( prefix = "" ) opt(prefix = function() paste("[", Sys.time(), "] ", sep = " ")) opt("prefix") # or opt$prefix Sys.sleep(2) opt("prefix") ``` If the value of the option is a real function and users don't want to execute it, just set `.class` to contain `function`, then the function will be treated as a simple value. ```{r} opt = set_opt( test_fun = list(.value = function(x1, x2) t.test(x1, x2)$p.value, .class = "function") ) opt(test_fun = function(x1, x2) cor.test(x1, x2)$p.value) opt("test_fun") # or opt$test_fun ``` ### Interaction between options The self-defined function (i.e. value function, validation function or filter function) is applied per-option independently. But sometimes we want to set one option based on values of other options. In this case, we need a function which can get other option values. `.v()` can be used to access other option values defined beforehand. `.v("a")` can also be written as `.v(a)` or `.v$a`. ```{r} opt = set_opt( a = 1, b = function() 2 * .v$a ) opt("b") # or opt$b opt(a = 2) opt("b") ``` However, you can still overwrite option `b`: ```{r} opt(a = 2, b = 3) # b was overwriiten and will not be 2*a opt() ``` `.v` can also be used in `.validate` and `.filter` fields. In the second example, sign of `b` should be as same as sign of `a`. ```{r error = TRUE, purl = FALSE} opt = set_opt( a = 1, b = list(.value = 0, .validate = function(x) { if(.v$a > 0) x > 0 else x < 0 }, .filter = function(x) { x + .v$a }, .failed_msg = "'b' should have same sign as 'a'.") ) opt(b = 1) opt("b") opt(a = 1, b = -1) # this should cause an error ``` ### Local options The option funtion also has a `LOCAL` argument which switches local mode and global mode. When `LOCAL` is set to `TRUE`, a copy of current options is generated and all queries are applied on the copy version. The local mode is turned off when `LOCAL` is explicitely specified to `FALSE`. ```{r} opt = set_opt( a = 1 ) opt(LOCAL = TRUE) opt(a = 2) opt$a opt(LOCAL = FALSE) opt$a ``` Local mode will be automatically turned off when enrivonment changes. In following example, local mode only works inside `f1()` and `f2()` functions and the local copies are independent in `f1()` and `f2()`. Note when leaving e.g. `f1()`, the copy of the option is deleted. ```{r} opt = set_opt( a = 1 ) f1 = function() { opt(LOCAL = TRUE) opt(a = 2) return(opt$a) } f1() opt$a f2 = function() { opt(LOCAL = TRUE) opt(a = 4) return(opt$a) } f2() opt$a ``` If `f1()` calls `f2()`, `f2()` will be in the same local mode as `f1()`. In other word, all children frames are in a same local mode if the parent frame is in local mode. ```{r} opt = set_opt( a = 1 ) f1 = function() { opt(LOCAL = TRUE) opt(a = 2) return(f2()) } f2 = function() { opt$a } f1() opt$a ``` ### Synonymous options It can be possible that several weeks later, developers have better names for the options. They want to use the new option names but still do not want to disable the old ones. In this case, `.synonymous` field can be set to let the new option and old option reference to a same internal option object (which means all other configuration specified for this option is ignored). The change of values of either one will also affect the companions correspondingly. ```{r} opt = set_opt( old = 1, new = list(.value = 1, .synonymous = "old") ) opt() opt$old = 2 opt() opt$new = 3 opt() ``` ### Print the object There is a `.description` field for each option which is only used when printing the summary of options. As shown before, simply entering the option object gives a summary table for all options. ```{r} opt = set_opt( a = 1, b = "b", c = list(.value = letters[1:4], .class = "character", .description = "26 letters"), d = list(.value = c(0, 0), .class = "numeric", .validate = function(x) x[1]^2 + x[2]^2 <= 1, .failed_msg = "The point should be in the unit circle", .description = "start points in the unit circle"), e = list(.value = rnorm, .class = "function", .description = "distribution to generate random numbers and a very long long long long long long long long text") ) opt ``` Use `dump_opt()` to get summary for each option. ```{r} dump_opt(opt, "a") dump_opt(opt, "d") ``` ### Add new options New options can be added after the option function is created by explicitely specifying `ADD = TRUE`: ```{r} opt = set_opt(a = 1) opt(b = 2, ADD = TRUE) opt ``` Note you cannot add new options by using `$` (or more precisely `$<-`) operator because `$` can only access options that have already been created. ```{r, error = TRUE, purl = FALSE} opt$c = 3 ``` Like using a complex configuration list when creating a new option in `set_opt()`, here you can also use configuration list with `ADD = TRUE`. ```{r, error = TRUE, purl = FALSE} opt(c = list(.value = "c", .class = "character"), ADD = TRUE) opt opt$c = 1 ``` Of course you can put more than one options in `opt()` when adding them. ## Features for package development Two additional fields may be helpful when developing packages. `.visible` controls whether options are visible to users. The invisible option can only be queried or modified by specifying its option name (just like you can only open the door with the correct unique key). This would be helpful if users want to put some secret options while do not want others to access. Is this case, they can assign names with complex strings like `.__MY_PRIVATE_KEY__.` as their secret options and afterwards they can access it with this special key. ```{r} opt = set_opt( a = list(.value = 1, .visible = FALSE), b = 2 ) opt() opt$a opt$a = 2 opt$a opt() ``` Another field `.private` controls whether the option is only private to the namespace (e.g. packages). If it is set to `TRUE`, the option can only be modified in the same namespace (or top environment) where the option function is generated. E.g, if you are writing a package named **foo** and generating an option function `foo_opt()`, by setting the option with `.private` to `TRUE`, the value for such options can only be modified inside **foo** package while it is not permitted outside **foo**. At the same time, private options become read-only options if querying outside **foo** package. In following example, we manually modify the namespace where `set_opt()` is called in `stats` package. ```{r} opt = set_opt( a = list(.value = 1, .private = TRUE) ) require(stats) ns = getNamespace("stats") environment(opt)$options$a$`__generated_namespace__` = ns ``` There will be error if trying to modify `a` which is private in `stats` namespace. ```{r error = TRUE, purl = FALSE} opt$a = 2 ``` But you can still access it. ```{r} opt$a ``` The option object generated by `set_opt()` is actually a function. It contains four arguments: `...`, `RESET`, `READ.ONLY`, `LOCAL`, `ADD`. If you want to put the option function into a package, remember to document all the four arguments: ```{r} args(opt) ``` ## Misc The order of validation when modifying an option value is `.read.only`, `.private`, `.length`, `.class`, `.validate`, `.filter`, `.length`, `.class`. Note validation on length and class of the option values will be applied again after filtering. Global options are stored in private environments. Each time when generating a option function, there will be new environments created. Thus global options will not conflict if they come from different option functions. ```{r} opt1 = set_opt( a = list(.value = 1) ) opt2 = set_opt( a = list(.value = 1) ) opt1$a = 2 opt1$a opt2$a ``` Note the option values can also be set as a list, so for the list containing configurations, names of the field is started with a dot `.` to be distinguished from the normal list. ```{r error = TRUE, purl = FALSE} opt = set_opt( list = list(a = 1, b = 2) ) opt() opt = set_opt( list = list(.value = list(a = 1, b = 2), .class = "list") ) opt() opt$list = 1 # this will cause an error ``` If you made a type of the field names when configurating the options (e.g. forgot to type the leading dot), there will be a warning and the whole configuration list is treated as a normal list for this option. ```{r} opt = set_opt( a = list(.value = 1, class = "numeric") # <- here it should be .class ) opt$a ``` The final and the most important thing is the validation by `.class`, `.length`, `.validate`, `.filter` will not be applied on default values because users who design their option functions should know whether the default values are valid or not. ```{r} opt = set_opt( a = list(.value = -1, .validate = function(x) x > 0) ) opt$a ``` ## Session info ```{r} sessionInfo() ``` GlobalOptions/vignettes/custom.css0000755000176200001440000000451613301312760017067 0ustar liggesusers#toc { position: fixed; left: 0; top: 20px; width: 200px; height: 100%; overflow:auto; padding: 0px 10px 0px 10px; } #toc_header { display: none; } #toc ul { margin: 0px; padding: 0px; } #toc ul li { list-style-type: none; } #toc ul li ul li { margin-left: 15px; list-style-type: circle; } body, td { font-family: Arial,Helvetica,sans-serif; background-color: white; font-size: 13px; max-width: 800px; margin: auto; margin-left:210px; padding: 0px 10px 0px 10px; border-left: 1px solid #EEEEEE; line-height: 150%; } tt, code, pre { font-family: 'DejaVu Sans Mono', 'Droid Sans Mono', 'Lucida Console', Consolas, Monaco, monospace; } h1 { font-size:2.2em; } h2 { font-size:1.8em; } h3 { font-size:1.4em; } h4 { font-size:1.0em; } h5 { font-size:0.9em; } h6 { font-size:0.8em; } a { text-decoration: none; } a:hover { text-decoration: underline; } a:visited { color: rgb(50%, 0%, 50%); } pre, img { max-width: 100%; } pre { overflow-x: auto; } pre code { display: block; padding: 0.5em; } code { font-size: 92%; border: 1px solid #ccc; } code[class] { background-color: #F8F8F8; } table, td, th { border: none; } blockquote { color:#666666; margin:0; padding-left: 1em; border-left: 0.5em #EEE solid; } hr { height: 0px; border-bottom: none; border-top-width: thin; border-top-style: dotted; border-top-color: #999999; } @media print { * { background: transparent !important; color: black !important; filter:none !important; -ms-filter: none !important; } body { font-size:12pt; max-width:100%; } a, a:visited { text-decoration: underline; } hr { visibility: hidden; page-break-before: always; } pre, blockquote { padding-right: 1em; page-break-inside: avoid; } tr, img { page-break-inside: avoid; } img { max-width: 100% !important; } @page :left { margin: 15mm 20mm 15mm 10mm; } @page :right { margin: 15mm 10mm 15mm 20mm; } p, h2, h3 { orphans: 3; widows: 3; } h2, h3 { page-break-after: avoid; } } GlobalOptions/NEWS0000644000176200001440000000643013664444255013545 0ustar liggesusersChanges in version 0.1.2 * `.DollarNames.GlobalOptionsFun` now only completes with public options * adjust the print method ---------------------------------------------------------------------- Changes in version 0.1.1 * do not refresh if the value is not an executable function ----------------------------------------------------------------------- Changes in version 0.1.0 * add `print.GlobalOptionsFun()` and `dump_opt()`. * add `.DollarNames.GlobalOptionsFun()` to allow auto completion after `$`. * add `.v()` and `.v$..` to access other option values when configuring current option. * add `set_opt()` to replace `setGlobalOptions()` * add `ADD` argument in the option function to add new options after the option function is created. ------------------------------------------------------------------------- Changes in version 0.0.13 * register S3 method in NAMESPACE ------------------------------------------------------------------------- Changes in version 0.0.12 * add .synonymous option --------------------------------------------------------------------------- Changes in version 0.0.11 * improved docs * opt$nm can be set to NULL now * `failed_msg` are wrapped --------------------------------------------------------------------------- Changes in version 0.0.10 * `$<-GlobalOptionsFun` are now invoked in right environment ---------------------------------------------------------------------------- Changes in version 0.0.9 ----------------------------------------------------------------------------- * support local mode Changes in version 0.0.8 ----------------------------------------------------------------------------- * assign a class for the generated functions * option value can be assigned by '$' symbol Changes in version 0.0.6 ------------------------------------------------------------------------------ * re-construct by reference class * default value for `.filter` and `.validate` are set to `NULL` * private value will not be reset if calling environment is not same as generating environment Changes in version 0.0.5 ------------------------------------------------------------------------------ * change interval options to `__generatedNamespace__` Changes in version 0.0.4 ------------------------------------------------------------------------------ * completely replace `is` with `inherits` Changes in version 0.0.3 ------------------------------------------------------------------------------ * remove dependency of `methods` package * `value_fun` is deleted after setting every option * function is attached to the value if option values is set as a dynamic function so that opt(opt(READ.ONLY = FALSE)) will be correct if there are option values set as dynamic functions. Changes in version 0.0.2 ------------------------------------------------------------------------------ * use knitr to build the vignette. * add .private and .visible fields for making options specification. Private options can only be modified if the namespace in which the options function is generated and the namespace where the options function is called is the same namespace. Invisible options will not be returned when getting the option values. Changes in version 0.0.1 ------------------------------------------------------------------------------ * the first release GlobalOptions/R/0000755000176200001440000000000012563221062013226 5ustar liggesusersGlobalOptions/R/setGlobalOptions.R0000644000176200001440000004463213666773773016704 0ustar liggesusers # == title # Produce a function which can get or set global options # # == param # -... specification of options, see 'details' section # # == detail # The function has a short name `set_opt`. # # The most simple way is to construct an option function (e.g. ``opt()``) as: # # opt = set_opt( # "a" = 1, # "b" = "text" # ) # # Then users can get or set the options by # # opt() # opt("a") # opt$a # opt[["a"]] # opt(c("a", "b")) # opt("a", "b") # opt("a" = 2) # opt$a = 2 # opt[["a"]] = 2 # opt("a" = 2, "b" = "new_text") # # Options can be reset to their default values by: # # opt(RESET = TRUE) # # The value for each option can be set as a list which contains more configurations of the option: # # opt = set_opt( # "a" = list(.value = 1, # .length = 1, # .class = "numeric", # .validate = function(x) x > 0) # ) # # The different fields in the list can be used to filter or validate the option values. # # -``.value`` The default value. # -``.length`` The valid length of the option value. It can be a vector, the check will be passed if one of the length fits. # -``.class`` The valid class of the option value. It can be a vector, the check will be passed if one of the classes fits. # -``.validate`` Validation function. The input parameter is the option value and should return a single logical value. # -``.failed_msg`` Once validation failed, the error message that is printed. # -``.filter`` Filtering function. The input parameter is the option value and it should return a filtered option value. # -``.read.only`` Logical. The option value can not be modified if it is set to ``TRUE``. # -``.visible`` Logical. Whether the option is visible to users. # -``.private`` Logical. The option value can only be modified in the same namespace where the option function is created. # -``.synonymous`` a single option name which should have been already defined ahead of current option. The option specified will be shared by current option. # -``.description`` a short text for describing the option. The description is only used when printing the object. # # For more detailed explanation, please go to the vignette. # # == author # Zuguang Gu # # == example # opt = set_opt( # a = 1, # b = "text" # ) # opt # # for more examples, please go to the vignette setGlobalOptions = function(...) { # the environment where the function is called envoking_env = parent.frame() args = list(...) if(any(is.null(names(args))) || any(names(args) == "")) { stop("You should provide named arguments.") } if("RESET" %in% names(args)) { stop("Don't use 'RESET' as the option name.") } if("READ.ONLY" %in% names(args)) { stop("Don't use 'READ.ONLY' as the option name.") } if("LOCAL" %in% names(args)) { stop("Don't use 'LOCAL' as the option name.") } if("ADD" %in% names(args)) { stop("Don't use 'ADD' as the option name.") } add_opt = function(arg, name, envoking_env, calling_ns = NULL) { if(is.list(arg)) { if(".synonymous" %in% names(arg)) { if(is.null(options[[ arg[[".synonymous"]] ]])) { stop(paste0("Option ", arg[[".synonymous"]], " has not been created yet.")) } opt = options[[ arg[[".synonymous"]] ]] return(opt) } } # if it is an advanced setting if(is.list(arg) && length(setdiff(names(arg), c(".value", ".class", ".length", ".validate", ".failed_msg", ".filter", ".read.only", ".private", ".visible", ".description"))) == 0) { default_value = arg[[".value"]] length = if(is.null(arg[[".length"]])) numeric(0) else arg[[".length"]] class = if(is.null(arg[[".class"]])) character(0) else arg[[".class"]] if(is.null(arg[[".validate"]])) { validate = function(x) TRUE } else { if(is.function(arg[[".validate"]])) { validate = arg[[".validate"]] } else { stop(paste("'.validate' field in", name, "should be a function.\n")) } } failed_msg = ifelse(is.null(arg[[".failed_msg"]]), "Your option is invalid.", arg[[".failed_msg"]][1]) if(is.null(arg[[".filter"]])) { filter = function(x) x } else { if(is.function(arg[[".filter"]])) { filter = arg[[".filter"]] } else { stop(paste("'.filter' field in", name, "should be a function.\n")) } } read.only = ifelse(is.null(arg[[".read.only"]]), FALSE, arg[[".read.only"]]) private = ifelse(is.null(arg[[".private"]]), FALSE, arg[[".private"]]) visible = ifelse(is.null(arg[[".visible"]]), TRUE, arg[[".visible"]]) description = ifelse(is.null(arg[[".description"]]), "", arg[[".description"]]) } else { if(is.list(arg) && length(intersect(names(arg), c(".value", ".class", ".length", ".validate", "failed_msg", ".filter", ".read.only", ".private", ".visible", ".synonymous", ".description"))) > 0 && length(setdiff(names(arg), c(".value", ".class", ".length", ".validate", "failed_msg", ".filter", ".read.only", ".private", ".visible", ".synonymous", ".description"))) > 0) { warning(paste("Your definition for '", name, "' is mixed. It should only contain\n.value, .class, .length, .validate, .failed_msg, .filter, .read.only, .private, .visible, .synonymous, .description. Ignore the setting and use the whole list as the default value.\n", sep = "")) } default_value = arg length = numeric(0) class = character(0) validate = function(x) TRUE failed_msg = "Your option is invalid." filter = function(x) x read.only = FALSE private = FALSE visible = TRUE description = "" } opt = GlobalOption$new( name = name, default_value = default_value, value = default_value, length = length, class = class, validate = validate, failed_msg = failed_msg, filter = filter, read.only = read.only, private = private, visible = visible, description = description, "__generated_namespace__" = topenv(envoking_env)) if(!is.null(calling_ns)) { opt$set(default_value, calling_ns) } else { opt$set(default_value, calling_ns, initialize = TRUE) } return(opt) } # format the options options = vector("list", length = length(args)) opt_names = names(args) names(options) = opt_names for(i in seq_along(args)) { options[[i]] = add_opt(args[[i]], opt_names[i], envoking_env) } local_options = NULL local_options_start_env = NULL opt_fun = function(..., RESET = FALSE, READ.ONLY = NULL, LOCAL = FALSE, ADD = FALSE) { # the environment where foo.options() is called calling_ns = topenv(parent.frame()) # top package where foo.options() is called e = environment() if(!missing(LOCAL) && !LOCAL) { local_options_start_env <<- NULL local_options <<- NULL options = options # cat("enforce to be global mode.\n") return(invisible(NULL)) } else if(LOCAL) { # check whether there is already local_options initialized if(is.null(parent.env(e)$local_options_start_env)) { local_options_start_env <<- parent.frame() # parent envir is where opt_fun is called local_options <<- lapply(options, function(opt) opt$copy()) } else if(!is.parent.frame(parent.env(e)$local_options_start_env, parent.frame())) { local_options_start_env <<- parent.frame() # parent envir is where opt_fun is called local_options <<- lapply(options, function(opt) opt$copy()) } options = local_options # cat("under local mode: ", get_env_str(local_options_start_env), "\n") return(invisible(NULL)) } else { # if local_options_start_env exists, it probably in local mode if(!is.null(parent.env(e)$local_options_start_env)) { # if calling frame is offspring environment of local_options_start_env if(identical(parent.env(e)$local_options_start_env, parent.frame())) { options = local_options # cat("in a same environment, still under local mode.\n") } else if(is.parent.frame(parent.env(e)$local_options_start_env, parent.frame())) { options = local_options # cat("in child environment, still under local mode.\n") } else { local_options_start_env <<- NULL local_options <<- NULL under_local_mode = FALSE options = options # cat("leave the local mode, now it is global mode.\n") } } else { options = options # cat("under global mode.\n") } } if(RESET) { for(i in seq_along(options)) { options[[i]]$reset(calling_ns) } return(invisible(NULL)) } args = list(...) # input value is NULL if(length(args) == 1 && is.null(names(args)) && is.null(args[[1]])) { return(NULL) } # if settings are stored in one object and send this object if(length(args) == 1 && is.list(args[[1]]) && is.null(names(args))) { args = args[[1]] } # refresh all # lapply(options[intersect(names(args), names(options))], function(opt) opt$refresh()) # getting all options if(length(args) == 0 && ADD) { return(invisible(NULL)) } if(length(args) == 0) { opts = lapply(options, function(opt) opt$get(calling_ns, read.only = READ.ONLY)) # some NULL are valid value, some NULL means do not output this option opts = opts[sapply(opts, function(opt) is.null(attr(opt, "not_available")))] return(opts) } # getting part of the options if(is.null(names(args)) && ADD) { return(invisible(NULL)) } if(is.null(names(args))) { args = unlist(args) if(length(setdiff(args, names(options)))) { stop(paste("No such option(s):", paste(setdiff(args, names(options)), collapse = ""))) } opts = lapply(options[args], function(opt) opt$get(calling_ns, read.only = READ.ONLY, enforce_visible = TRUE)) opts = opts[sapply(opts, function(opt) is.null(attr(opt, "not_available")))] if(length(args) == 1) { opts = opts[[1]] } return(opts) } # set the options name = names(args) option.names = names(options) if(any(name == "")) { stop("When setting options, all arguments should be named.") } else { # first check on copy for(i in seq_along(args)) { # if there are names which are not defined in options, create one if(sum(name[i] %in% option.names) == 0) { if(ADD) { options[[ name[i] ]] <<- add_opt(args[[ name[i] ]], name[i], envoking_env, calling_ns) } else { stop(paste("No such option: '", name[i], "'.\nIf you want to add this new option, please use your_opt_fun(", name[i], " = ..., ADD = TRUE)", sep = "")) } } else { # user's value value = args[[ name[i] ]] options[[ name[i] ]]$set(value, calling_ns) } } } return(invisible(NULL)) } class(opt_fun) = "GlobalOptionsFun" return(opt_fun) } # == title # Print the GlobalOptionsFun object # # == param # -x the option object returned by `set_opt` or `setGlobalOptions`. # -... other arguments # # == author # z.gu@dkfz.de # print.GlobalOptionsFun = function(x, ...) { lt = x() options = get("options", envir = environment(x)) options = options[names(lt)] option = names(options) value = sapply(options, function(opt) value2text(opt$real_value, width = Inf)) description = sapply(options, function(opt) opt$description) option_max_width = max(nchar(c("Option", option))) value_max_width = max(nchar(c("Value", value))) desc_max_width = max(nchar(description)) cat(" ", "Option", strrep(" ", option_max_width - 6), sep = "") cat(" ", "Value", strrep(" ", value_max_width - 5), sep = "") cat("\n") cat(" ", strrep("-", option_max_width), sep = "") cat(":", strrep("-", min( max(value_max_width + 2, desc_max_width), getOption("width") - option_max_width - 2)), sep = "") cat("\n") for(i in seq_along(option)) { cat(" ", option[i], strrep(" ", option_max_width - nchar(option[i])), sep = "") cat(" ", value[i], strrep(" ", value_max_width - nchar(value[i])), sep = "") cat("\n") if(description[i] != "") { txt = paste0("(", description[i], ")") txt = strwrap(txt, width = 0.9*getOption("width") - option_max_width + 1) txt = paste(strrep(" ", option_max_width + 2), txt, sep = "") txt = paste(txt, collapse = "\n") cat(txt, sep = "") cat("\n") } } # cat(" ", strrep("-", option_max_width), sep = "") # cat("-", strrep("-", min( max(value_max_width + 2, desc_max_width), getOption("width") - option_max_width - 2)), sep = "") # cat("\n") # cat(" Use `", opt_nm, "$opt_name` or `", opt_nm, "[['opt_name']]` to retrieve the value.\n", sep = "") # cat(" Use `", opt_nm, "$opt_name = value` or `", opt_nm, "[['opt_name']] = value` to set the value.\n", sep = "") } # == title # Get a single GlobalOption object # # == param # -x the option object returned by `set_opt` or `setGlobalOptions`. # -nm a single name of the option. # # == details # This function is only used internally. # # == author # Zuguang Gu # # == example # opt = set_opt(a = 1, b = "b") # opt["a"] # opt["b"] "[.GlobalOptionsFun" = function(x, nm) { options = get("options", envir = environment(x)) if(length(nm) > 1) { stop("The index can only be length of 1.\n") } options[[nm]] } # == title # Print all fields of a single option # # == param # -opt the option object returned by `set_opt` or `setGlobalOptions`. # -opt_name a single name of the option. # # == details # Actually this function is identical to ``opt[opt_name]``. # # == author # z.gu@dkfz.de # # == example # opt = set_opt(a = 1, b = "b") # dump_opt(opt, "a") # dump_opt(opt, "b") dump_opt = function(opt, opt_name) { if(length(opt_name) > 1) { stop("The option name can only be length of 1.\n") } opt[opt_name] } # == title # Get option value by subset operator # # == param # -x the option object returned by `set_opt` or `setGlobalOptions`. # -nm a single option name. # # == details # ``opt[["a"]]`` is same as ``opt("a")`` or ``opt$a``. # # == author # Zuguang Gu # # == example # opt = set_opt(a = 1) # opt[["a"]] "[[.GlobalOptionsFun" = function(x, nm) { if(is.numeric(nm)) { stop("The index should only be option name.\n") } if(length(nm) > 1) { stop("The index can only be length of 1.\n") } x(nm) } # == title # Set option value by subset operator # # == param # -x the option object returned by `set_opt` or `setGlobalOptions`. # -nm a single option name. # -value the value which is assigned to the option. # # == details # ``opt[["a"]] = 1`` is same as ``opt("a" = 1)`` or ``opt$a = 1``. # # == author # Zuguang Gu # # == example # opt = set_opt(a = 1) # opt[["a"]] = 2 # opt$a "[[<-.GlobalOptionsFun" = function(x, nm, value) { if(is.numeric(nm)) { stop("The index should only be option names.\n") } if(length(nm) > 1) { stop("The index can only be length of 1.\n") } lt = list(value) names(lt) = nm assign(".__temp_opt__.", x, envir = parent.frame()) do.call(".__temp_opt__.", lt, envir = parent.frame()) rm(".__temp_opt__.", envir = parent.frame()) return(x) } # == title # Option names # # == param # -x the option object returned by `set_opt` or `setGlobalOptions`. # # == value # A vector of option names # # == example # opt = set_opt( # a = 1, # b = "text" # ) # names(opt) names.GlobalOptionsFun = function(x) { names(x()) } # == title # The .DollarNames method for the GlobalOptionsFun class # # == param # -x the object returned by `set_opt` or `setGlobalOptions`. # -pattern pattern, please ignore it. # # == details # This makes the option object looks like a list that it allows # option name completion after ``$``. # # == author # z.gu@dkfz.de # .DollarNames.GlobalOptionsFun = function(x, pattern = "") { lt = x() names(lt) } # == title # Produce a function which can get or set global options # # == param # -... all go to `setGlobalOptions` # # == details # This is just a short name for `setGlobalOptions`. # # == author # z.gu@dkfz.de set_opt = function(...) {} set_opt = setGlobalOptions env2txt = function(env) { if(identical(env, emptyenv())) { return("R_EmptyEnv") } else if(identical(env, .GlobalEnv)){ return("R_GlobalEnv") } else if(isNamespace(env)) { return(getNamespaceName(env)) } else if(!is.null(attr(env, "name"))) { return(attr(env, "name")) } else { return(get_env_str(env)) } } insertEnvBefore = function(fun, e) { oe = environment(fun) # where `fun` is defined environment(fun) = e parent.env(e) = oe return(fun) } deleteEnvBefore = function(fun) { environment(fun) = parent.env(environment(fun)) return(fun) } print_env_stack = function(e, depth = Inf) { if(is.function(e)) { env = environment(e) } else { env = e } i_depth = 0 while(!identical(env, emptyenv()) && i_depth < depth) { cat(env2txt(env), "\n") env = parent.env(env) i_depth = i_depth + 1 } } is.parent.env = function(p, e) { while(1) { e = parent.env(e) if(identical(e, emptyenv())) { return(FALSE) } if(identical(p, e)) { return(TRUE) } } return(FALSE) } is.parent.frame = function(p, e) { if(identical(p, e)) { return(FALSE) } i = 1 + 1 while(!is_top_env(e)) { e = parent.frame(n = i) if(identical(p, e)) { return(TRUE) } i = i + 1 } return(FALSE) } is_top_env = function(e) { if(identical(e, .GlobalEnv)) { return(TRUE) } else if(isNamespace(e)) { return(TRUE) } else { return(FALSE) } } # with_sink is copied from testthat package with_sink = function (connection, code, ...) { sink(connection, ...) on.exit(sink()) code } get_env_str = function(env) { temp = file() with_sink(temp, print(env)) output <- paste0(readLines(temp, warn = FALSE), collapse = "\n") close(temp) return(output) } stop = function(msg) { base::stop(paste(strwrap(msg), collapse = "\n"), call. = FALSE) } warning = function(msg) { base::warning(paste(strwrap(msg), collapse = "\n"), call. = FALSE) } # == title # Get option value by dollar symbol # # == param # -x the object returned by `set_opt` or `setGlobalOptions`. # -nm a single option name. # # == details # ``opt$a`` is same as ``opt("a")``. # # == author # Zuguang Gu # # == example # opt = set_opt(a = 1) # opt$a "$.GlobalOptionsFun" = function(x, nm) { x(nm) } # == title # Set option value by dollar symbol # # == param # -x the object returned by `set_opt` or `setGlobalOptions`. # -nm a single option name. # -value the value which is assigned to the option. # # == details # ``opt$a = 1`` is same as ``opt("a" = 1)``. # # Note you cannot reconfigurate the option by assigning a configuration list. # # == author # Zuguang Gu # # == example # opt = set_opt(a = 1) # opt$a = 2 # opt$a "$<-.GlobalOptionsFun" = function(x, nm, value) { lt = list(value) names(lt) = nm assign(".__temp_opt__.", x, envir = parent.frame()) do.call(".__temp_opt__.", lt, envir = parent.frame()) rm(".__temp_opt__.", envir = parent.frame()) return(x) } GlobalOptions/R/class.R0000644000176200001440000002047313321075324014465 0ustar liggesusers NOT_AVAILABLE = NA attr(NOT_AVAILABLE, "not_available") = TRUE GlobalOption = setRefClass("GlobalOption", fields = list( name = "character", default_value = "ANY", # default_value value = "ANY", # setted value real_value = "ANY", # is value is a function to be executed, it is the returned value length = "numeric", class = "character", validate = "function", failed_msg = "character", filter = "function", read.only = "logical", private = "logical", visible = "logical", description = "character", "__generated_namespace__" = "environment"), methods = list( initialize = function(...) { obj = callSuper(...) if(length(obj$failed_msg) == 0) { obj$failed_msg = "Your option is invalid." } return(obj) }, # get current value get = function(calling_ns = parent.frame(), read.only = NULL, enforce_visible = FALSE) { # in case the value is an executable function if(inherits(.self$value, "function") && !("function" %in% .self$class)) { .self$refresh() } if(!.self$visible && !enforce_visible) { return(NOT_AVAILABLE) } if(is.null(read.only)) { return(.self$real_value) } else { if(! identical(.self$`__generated_namespace__`, calling_ns)) { if(.self$private) { return(NOT_AVAILABLE) } } if(read.only) { if(.self$read.only) { return(.self$real_value) } } else { if(!.self$read.only) { return(.self$real_value) } } } return(NOT_AVAILABLE) # a special NA which means invalid NA }, # set and refresh current value set = function(opt_value = NULL, calling_ns = parent.frame(), initialize = FALSE) { if(is.function(opt_value) && length(intersect(.self$class, "function")) == 0) { value_fun = opt_value opt_value = value_fun() } if(initialize) { # do not do checking if(exists("value_fun")) { value <<- value_fun } else { value <<- opt_value } # in case the function is executable .self$refresh() return(NULL) } # test on read only if(.self$read.only) { stop(paste("'", .self$name, "' is a read-only option.\n", sep = "")) } # test on private # in option function generation and calling are in the same namespace, then private options can be modified if( (!identical(.self$`__generated_namespace__`, calling_ns)) && .self$private) { stop(paste("'", .self$name, "' is a private option and it can only be modified inside '", env2txt(.self$`__generated_namespace__`), "' namespace while not '", env2txt(calling_ns), "'.\n", sep = "")) } # test on value length if(length(.self$length)) { if(!(length(opt_value) %in% .self$length)) { if(length(.self$length) == 1) { stop(paste("Length of '", .self$name, "' should be ", .self$length, ".", sep = "")) } else { stop(paste("Length of '", .self$name, "' should be one of ", paste(.self$length, collapse = ", "), ".\n", sep = "")) } } } # test on classes of the values if(length(.self$class)) { if(!any(sapply(.self$class, function(cl) inherits(opt_value, cl)))) { if(length(.self$class)) { stop(paste("Class of '", .self$name, "' should be '", .self$class, "'.\n", sep = "")) } else { stop(paste("Class of '", .self$name, "' should be one of '", paste(.self$class, collapse = ", "), "'.\n", sep = "")) } } } # test on validate function failed_msg_ = paste(strwrap(paste(.self$name, " didn't pass the validation. ", .self$failed_msg, "\n", sep = "")), collapse = "\n") failed_msg_ = paste0(failed_msg_, "\n") if(!.self$validate(opt_value)) stop(failed_msg_) # filter on data opt_value = .self$filter(opt_value) # check filtered value again # test on value length if(length(.self$length)) { if(!(length(opt_value) %in% .self$length)) { stop(paste("Length of filtered '", .self$name, "' should be one of ", paste(.self$length, collapse = ", "), "\n", sep = "")) } } # test on classes of the values if(length(.self$class)) { if(!any(sapply(.self$class, function(cl) inherits(opt_value, cl)))) { stop(paste("Class of filtered '", .self$name, "' should be one of '", paste(.self$class, collapse = ", "), "'.\n", sep = "")) } } # finally, all values are correct if(exists("value_fun")) { value <<- value_fun } else { value <<- opt_value } # in case the function is executable .self$refresh() }, # set to default value reset = function(calling_ns = parent.frame()) { if(identical(.self$`__generated_namespace__`, calling_ns)) { # read-only options cannot be reset if(! .self$read.only) { .self$value = .self$default_value } } else { # read-only and private options can not be reset if(! (.self$read.only || .self$private) ) { .self$value = .self$default_value } } .self$refresh() }, refresh = function() { if(inherits(.self$value, "function") && !("function" %in% .self$class)) { .self$real_value = .self$value() } else { .self$real_value = .self$value } }, copy = function (shallow = FALSE) { def <- .refClassDef value_ <- new(def) vEnv <- as.environment(value_) selfEnv <- as.environment(.self) for (field in names(def@fieldClasses)) { if (shallow) base::assign(field, base::get(field, envir = selfEnv), envir = vEnv) # get here will conflict with the `get` in this reference class else { current <- base::get(field, envir = selfEnv) if (is(current, "envRefClass")) current <- current$copy(FALSE) base::assign(field, current, envir = vEnv) } } value_ }, fields = function() { names(.refClassDef@fieldClasses) }, show = function() { fd = .self$fields() fd = setdiff(fd, "value") df = data.frame("Field" = fd, "Value" = sapply(fd, function(x) value2text(.self[[x]], x)), check.names = FALSE, stringsAsFactors = FALSE) df[[1]][ df[1] == "real_value" ] = "current_value" print(df, row.names = FALSE) } ) ) value2text = function(v, field, width = 40) { if(is.null(v)) { "NULL" } else if(is.function(v)) { "a user-defined function" } else if(length(v) == 0) { if(field %in% c("length", "class")) { "no limit" } else { paste0(class(v)[1], "(0)") } } else if(is.environment(v)) { env2txt(v) } else if(identical(v, "")) { "\"\"" } else if(is.matrix(v)) { nr = nrow(v) nc = ncol(v) paste("a matrix with", nrow(v), ifelse(nr == 1, "row", "rows,"), ncol(v), ifelse(nc == 1, "column", "columns")) } else if(is.factor(v)) { nl = nlevels(v) paste("a factor with", nlevels(v), ifelse(nl == 1, "level", "levels")) } else if(is.atomic(v)) { toString(v, width = width) } else if(is.data.frame(v)) { nr = nrow(v) nc = ncol(v) paste("a data frame with", nrow(v), ifelse(nr == 1, "row", "rows,"), ncol(v), ifelse(nc == 1, "column", "columns")) } else if(is.list(v)) { paste("a list with", length(v), "elements") } else { paste0("a (", paste(class(v), collapse = ", "), ") object") } } # == title # Get value of other options # # == param # -opt_name name of the option, can be quoted or not quoted. # -name_is_character whether ``opt_name`` is a character, only used internally. # # == details # When setting one option, the value can be dependent on other option names. # The current value of other option can be accessed by ``v(nm)`` or ``v$nm``. # # == author # Zuguang Gu # # == example # opt = set_opt(a = 1, b = function() .v$a*2) # opt$b # opt(a = 2); opt$b .v = function(opt_name, name_is_character = NA) { if(is.na(name_is_character)) { opt_name = substitute(opt_name) } else if(name_is_character) { opt_name = opt_name } n = 1 while(TRUE) { e = parent.frame(n = n) if(exists("options", envir = e, inherits = FALSE)) { break } n = n + 1 if(n > 50) { stop("Cannot find the correct environment.") } } get("options", envir = e)[[opt_name]]$real_value } class(.v) = "InternalOptionValue" # == title # Get value of other options # # == param # -x should always be ``.v`` # -nm name of the option # # == details # ``.v$nm`` is basically a short version of ``.v(nm)``. # # == seealso # `.v` # # == author # Zuguang Gu "$.InternalOptionValue" = function(x, nm) { x(nm, name_is_character = TRUE) } GlobalOptions/MD50000644000176200001440000000301713670235703013345 0ustar liggesusers0e568dd1d2194c477bf4a69ab26c7579 *DESCRIPTION 1e583b908b576c0cb9491a4431ca1ccc *LICENSE 5ea658476ee3e6a44748996a680f251b *NAMESPACE 42033116e4785d17af71bc581b3a1ec9 *NEWS 4b7e5f2263d04267a00fa495de3fdd9d *R/class.R 16ff8f206edb5ca904791bd8917c785a *R/setGlobalOptions.R 82138dde3a996062795d0602cc8ee5c4 *build/vignette.rds 4537ad5fbd9f9fb20f5afd059d9bada3 *inst/doc/GlobalOptions.R 198534bbc3867267048104b1de71860e *inst/doc/GlobalOptions.Rmd efca5bbe42a1181cd1538a0caeea9495 *inst/doc/GlobalOptions.html b1f953f8c49cb57307f4afe7b6a8aa38 *man/Assign.GlobalOptionsFun.Rd 5ecc78ce626a0627bb7300ad4350ba5d *man/Dot.DollarNames.GlobalOptionsFun.Rd 74ba56450433be2f257e0604f744908b *man/Dot.v.Rd 3f173432c5b0b0fae14b08647e77b8af *man/Extract.GlobalOptionsFun.Rd ac34c06f6fcf996f8447f078e17227c7 *man/ExtractExtract.GlobalOptionsFun.Rd 305aea59454941d6e41d0a705ff495e9 *man/ExtractExtractAssign.GlobalOptionsFun.Rd dfd3a467eccf744048e23ca7c062cb71 *man/Subset.GlobalOptionsFun.Rd 27d8efef69fdb31c3b036b45ce2beece *man/Subset.InternalOptionValue.Rd cc6a8d7122818da9307284e1e988dd8b *man/dump_opt.Rd aa404db643663de3c665bcd65ee79b1b *man/names.GlobalOptionsFun.Rd 43e8e69f962db817701f18bcfec85449 *man/print.GlobalOptionsFun.Rd 5293c70a4c6130cfe9498445f1b2798d *man/setGlobalOptions.Rd df5d9a6d03b662c2c36291f5da5f5519 *man/set_opt.Rd d5c509ca0fd08fc109f97c0f026e41ef *tests/test-all.R 8f8fcc713dc0575ea48863d4a2896d2e *tests/testthat/test.R 198534bbc3867267048104b1de71860e *vignettes/GlobalOptions.Rmd cebb488a47ec21fbf6c3684793828eee *vignettes/custom.css GlobalOptions/inst/0000755000176200001440000000000013670151477014016 5ustar liggesusersGlobalOptions/inst/doc/0000755000176200001440000000000013670151477014563 5ustar liggesusersGlobalOptions/inst/doc/GlobalOptions.R0000644000176200001440000001420413670151477017463 0ustar liggesusers## ---- echo = FALSE, message = FALSE------------------------------------------- library(knitr) knitr::opts_chunk$set( error = FALSE, tidy = FALSE, message = FALSE, comment = NA, fig.align = "center") library(GetoptLong) ## ----------------------------------------------------------------------------- library(GlobalOptions) opt = set_opt( a = 1, b = "text" ) ## ----------------------------------------------------------------------------- opt() opt("a") opt$a op = opt() op opt(a = 2, b = "new text") opt() opt$b = "" opt() opt(op) opt() ## ----------------------------------------------------------------------------- opt(a = 2, b = "new text") opt(RESET = TRUE) opt() ## ----------------------------------------------------------------------------- opt ## ----------------------------------------------------------------------------- opt = set_opt( a = list(.value = 1, .length = c(1, 3), .class = "numeric") ) ## ----------------------------------------------------------------------------- opt = set_opt( a = list(.value = 1, .read.only = TRUE), b = 2 ) opt(READ.ONLY = TRUE) opt(READ.ONLY = FALSE) opt(READ.ONLY = NULL) # default, to return both ## ----------------------------------------------------------------------------- opt = set_opt( verbose = list(.value = TRUE, .filter = function(x) { if(is.null(x)) { return(FALSE) } else if(is.na(x)) { return(FALSE) } else { return(x) } }) ) opt(verbose = FALSE); opt("verbose") opt(verbose = NA); opt("verbose") opt(verbose = NULL); opt("verbose") ## ----------------------------------------------------------------------------- opt = set_opt( margin = list(.value = c(1, 1, 1, 1), .length = c(1, 2, 4), .filter = function(x) { if(length(x) == 1) { return(rep(x, 4)) } else if(length(x) == 2) { return(rep(x, 2)) } else { return(x) } }) ) opt(margin = 2); opt("margin") opt(margin = c(2, 4)); opt("margin") ## ----------------------------------------------------------------------------- opt = set_opt( prefix = "" ) opt(prefix = function() paste("[", Sys.time(), "] ", sep = " ")) opt("prefix") # or opt$prefix Sys.sleep(2) opt("prefix") ## ----------------------------------------------------------------------------- opt = set_opt( test_fun = list(.value = function(x1, x2) t.test(x1, x2)$p.value, .class = "function") ) opt(test_fun = function(x1, x2) cor.test(x1, x2)$p.value) opt("test_fun") # or opt$test_fun ## ----------------------------------------------------------------------------- opt = set_opt( a = 1, b = function() 2 * .v$a ) opt("b") # or opt$b opt(a = 2) opt("b") ## ----------------------------------------------------------------------------- opt(a = 2, b = 3) # b was overwriiten and will not be 2*a opt() ## ----------------------------------------------------------------------------- opt = set_opt( a = 1 ) opt(LOCAL = TRUE) opt(a = 2) opt$a opt(LOCAL = FALSE) opt$a ## ----------------------------------------------------------------------------- opt = set_opt( a = 1 ) f1 = function() { opt(LOCAL = TRUE) opt(a = 2) return(opt$a) } f1() opt$a f2 = function() { opt(LOCAL = TRUE) opt(a = 4) return(opt$a) } f2() opt$a ## ----------------------------------------------------------------------------- opt = set_opt( a = 1 ) f1 = function() { opt(LOCAL = TRUE) opt(a = 2) return(f2()) } f2 = function() { opt$a } f1() opt$a ## ----------------------------------------------------------------------------- opt = set_opt( old = 1, new = list(.value = 1, .synonymous = "old") ) opt() opt$old = 2 opt() opt$new = 3 opt() ## ----------------------------------------------------------------------------- opt = set_opt( a = 1, b = "b", c = list(.value = letters[1:4], .class = "character", .description = "26 letters"), d = list(.value = c(0, 0), .class = "numeric", .validate = function(x) x[1]^2 + x[2]^2 <= 1, .failed_msg = "The point should be in the unit circle", .description = "start points in the unit circle"), e = list(.value = rnorm, .class = "function", .description = "distribution to generate random numbers and a very long long long long long long long long text") ) opt ## ----------------------------------------------------------------------------- dump_opt(opt, "a") dump_opt(opt, "d") ## ----------------------------------------------------------------------------- opt = set_opt(a = 1) opt(b = 2, ADD = TRUE) opt ## ----------------------------------------------------------------------------- opt = set_opt( a = list(.value = 1, .visible = FALSE), b = 2 ) opt() opt$a opt$a = 2 opt$a opt() ## ----------------------------------------------------------------------------- opt = set_opt( a = list(.value = 1, .private = TRUE) ) require(stats) ns = getNamespace("stats") environment(opt)$options$a$`__generated_namespace__` = ns ## ----------------------------------------------------------------------------- opt$a ## ----------------------------------------------------------------------------- args(opt) ## ----------------------------------------------------------------------------- opt1 = set_opt( a = list(.value = 1) ) opt2 = set_opt( a = list(.value = 1) ) opt1$a = 2 opt1$a opt2$a ## ----------------------------------------------------------------------------- opt = set_opt( a = list(.value = 1, class = "numeric") # <- here it should be .class ) opt$a ## ----------------------------------------------------------------------------- opt = set_opt( a = list(.value = -1, .validate = function(x) x > 0) ) opt$a ## ----------------------------------------------------------------------------- sessionInfo() GlobalOptions/inst/doc/GlobalOptions.Rmd0000644000176200001440000003617613666773311020022 0ustar liggesusers--- title: "Generate Global Options" author: "Zuguang Gu (z.gu@dkfz.de)" date: '`r Sys.Date()`' output: html_document: fig_caption: true toc: true toc_depth: 3 toc_float: true vignette: > %\VignetteIndexEntry{Generate Global Options} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- --------------------------------------------------------------------- ```{r, echo = FALSE, message = FALSE} library(knitr) knitr::opts_chunk$set( error = FALSE, tidy = FALSE, message = FALSE, comment = NA, fig.align = "center") library(GetoptLong) ``` Global option function such as `options()` and `par()` provides a way to control global settings. Here the **GlobalOptions** package provides a more general and controlable way to generate such functions, which can: 1. validate the values (e.g. class, length and self-defined validations); 2. set read-only options; 3. set invisible options; 4. set private options which are only accessable in a certain namespace; 5. support local options and global option; 6. print options with explanations. ## General usage The most simple use is to generate an option function with default values by callling `setGlobalOptions()` or its short versoin `set_opt()`: ```{r} library(GlobalOptions) opt = set_opt( a = 1, b = "text" ) ``` The returned value `opt` is an option function which can be used to get or set options. Options in `opt` can be accessed either by specifying as arguments or by using the `$` operator. ```{r} opt() opt("a") opt$a op = opt() op opt(a = 2, b = "new text") opt() opt$b = "" opt() opt(op) opt() ``` `opt` generated by `set_opt()` contains an argument `RESET` which is used to reset the options to the default: ```{r} opt(a = 2, b = "new text") opt(RESET = TRUE) opt() ``` Simply printing `opt` gives a summary of all options. ```{r} opt ``` ## Advanced usage If option values are set as lists, more configurations can be customized. ### Simple validation There are two basic fields that are used to check the input option values: ```{r} opt = set_opt( a = list(.value = 1, .length = c(1, 3), .class = "numeric") ) ``` In above code, `.value` is the default value for the option `a`. The length of the value is controlled by `.length` and the length should be either 1 or 3. The class of the value should be `numeric`. If the input value does not fit these criterions, there will be an error. The value of `.length` or `.class` is a vector and the checking will be passed if one of the value fits user's input. ```{r error = TRUE, purl = FALSE} opt(a = 1:2) # there will be error because the length is 2 opt(a = "text") # there will be error because the input is character ``` ### Read-only options The value can be set as read-only by `.read.only` field and modifying such option will cause an error. ```{r error = TRUE, purl = FALSE} opt = set_opt( a = list(.value = 1, .read.only = TRUE) ) opt(a = 2) # there will be error because a is read-only ``` There is also a pre-defined argument `READ.ONLY` in `opt()` which controls whether to return only the read-only options or not. ```{r} opt = set_opt( a = list(.value = 1, .read.only = TRUE), b = 2 ) opt(READ.ONLY = TRUE) opt(READ.ONLY = FALSE) opt(READ.ONLY = NULL) # default, to return both ``` ### User-defined validation More customized validation of the option values can be controlled by `.validate` field. The value of `.validate` should be a function. The input of the validation function is the input option value and the function should only return a logical value. `a` should only between 0 and 10 in following example. ```{r error = TRUE, purl = FALSE} opt = set_opt( a = list(.value = 1, .validate = function(x) x > 0 && x < 10 ) ) opt(a = 20) # This will cause an error ``` `.failed_msg` is used to configure the error message once validation is failed. ```{r error = TRUE, purl = FALSE} opt = set_opt( a = list(.value = 1, .validate = function(x) x > 0 && x < 10, .failed_msg = "'a' should be in (0, 10)." ) ) opt(a = 20) # This will cause an error ``` ### Filter the option values Filtering on the option values can be controlled by `.filter` field. This is useful when the input option value is not valid but it is not necessary to throw errors. More proper way is to modify the value silently. For example, there is an option to control whether to print messages or not and it should be set to `TRUE` or `FALSE`. However, users may set some other type of values such as `NULL` or `NA`. In this case, non-`TRUE` values can be converted to logical values by `.filter`. Similar as `.validate`, the input value for filter function is the input option value, and it should return a filtered option value. ```{r} opt = set_opt( verbose = list(.value = TRUE, .filter = function(x) { if(is.null(x)) { return(FALSE) } else if(is.na(x)) { return(FALSE) } else { return(x) } }) ) opt(verbose = FALSE); opt("verbose") opt(verbose = NA); opt("verbose") opt(verbose = NULL); opt("verbose") ``` Another example is when there is an option which controls four margin values of a plot, the length of the value can either be 1, 2, or 4. With `.filter`, length can be normaliezd to 4 consistently. ```{r} opt = set_opt( margin = list(.value = c(1, 1, 1, 1), .length = c(1, 2, 4), .filter = function(x) { if(length(x) == 1) { return(rep(x, 4)) } else if(length(x) == 2) { return(rep(x, 2)) } else { return(x) } }) ) opt(margin = 2); opt("margin") opt(margin = c(2, 4)); opt("margin") ``` ### Dynamic querying the option value The input option value can be set dynamicly by setting it as a function. When the option value is set as a function and class of the option is non-function, it will be executed when querying the option. In the following example, the `prefix` option corresponds to the prefix of log messages. The returned option value is the string after the execution of the input function. ```{r} opt = set_opt( prefix = "" ) opt(prefix = function() paste("[", Sys.time(), "] ", sep = " ")) opt("prefix") # or opt$prefix Sys.sleep(2) opt("prefix") ``` If the value of the option is a real function and users don't want to execute it, just set `.class` to contain `function`, then the function will be treated as a simple value. ```{r} opt = set_opt( test_fun = list(.value = function(x1, x2) t.test(x1, x2)$p.value, .class = "function") ) opt(test_fun = function(x1, x2) cor.test(x1, x2)$p.value) opt("test_fun") # or opt$test_fun ``` ### Interaction between options The self-defined function (i.e. value function, validation function or filter function) is applied per-option independently. But sometimes we want to set one option based on values of other options. In this case, we need a function which can get other option values. `.v()` can be used to access other option values defined beforehand. `.v("a")` can also be written as `.v(a)` or `.v$a`. ```{r} opt = set_opt( a = 1, b = function() 2 * .v$a ) opt("b") # or opt$b opt(a = 2) opt("b") ``` However, you can still overwrite option `b`: ```{r} opt(a = 2, b = 3) # b was overwriiten and will not be 2*a opt() ``` `.v` can also be used in `.validate` and `.filter` fields. In the second example, sign of `b` should be as same as sign of `a`. ```{r error = TRUE, purl = FALSE} opt = set_opt( a = 1, b = list(.value = 0, .validate = function(x) { if(.v$a > 0) x > 0 else x < 0 }, .filter = function(x) { x + .v$a }, .failed_msg = "'b' should have same sign as 'a'.") ) opt(b = 1) opt("b") opt(a = 1, b = -1) # this should cause an error ``` ### Local options The option funtion also has a `LOCAL` argument which switches local mode and global mode. When `LOCAL` is set to `TRUE`, a copy of current options is generated and all queries are applied on the copy version. The local mode is turned off when `LOCAL` is explicitely specified to `FALSE`. ```{r} opt = set_opt( a = 1 ) opt(LOCAL = TRUE) opt(a = 2) opt$a opt(LOCAL = FALSE) opt$a ``` Local mode will be automatically turned off when enrivonment changes. In following example, local mode only works inside `f1()` and `f2()` functions and the local copies are independent in `f1()` and `f2()`. Note when leaving e.g. `f1()`, the copy of the option is deleted. ```{r} opt = set_opt( a = 1 ) f1 = function() { opt(LOCAL = TRUE) opt(a = 2) return(opt$a) } f1() opt$a f2 = function() { opt(LOCAL = TRUE) opt(a = 4) return(opt$a) } f2() opt$a ``` If `f1()` calls `f2()`, `f2()` will be in the same local mode as `f1()`. In other word, all children frames are in a same local mode if the parent frame is in local mode. ```{r} opt = set_opt( a = 1 ) f1 = function() { opt(LOCAL = TRUE) opt(a = 2) return(f2()) } f2 = function() { opt$a } f1() opt$a ``` ### Synonymous options It can be possible that several weeks later, developers have better names for the options. They want to use the new option names but still do not want to disable the old ones. In this case, `.synonymous` field can be set to let the new option and old option reference to a same internal option object (which means all other configuration specified for this option is ignored). The change of values of either one will also affect the companions correspondingly. ```{r} opt = set_opt( old = 1, new = list(.value = 1, .synonymous = "old") ) opt() opt$old = 2 opt() opt$new = 3 opt() ``` ### Print the object There is a `.description` field for each option which is only used when printing the summary of options. As shown before, simply entering the option object gives a summary table for all options. ```{r} opt = set_opt( a = 1, b = "b", c = list(.value = letters[1:4], .class = "character", .description = "26 letters"), d = list(.value = c(0, 0), .class = "numeric", .validate = function(x) x[1]^2 + x[2]^2 <= 1, .failed_msg = "The point should be in the unit circle", .description = "start points in the unit circle"), e = list(.value = rnorm, .class = "function", .description = "distribution to generate random numbers and a very long long long long long long long long text") ) opt ``` Use `dump_opt()` to get summary for each option. ```{r} dump_opt(opt, "a") dump_opt(opt, "d") ``` ### Add new options New options can be added after the option function is created by explicitely specifying `ADD = TRUE`: ```{r} opt = set_opt(a = 1) opt(b = 2, ADD = TRUE) opt ``` Note you cannot add new options by using `$` (or more precisely `$<-`) operator because `$` can only access options that have already been created. ```{r, error = TRUE, purl = FALSE} opt$c = 3 ``` Like using a complex configuration list when creating a new option in `set_opt()`, here you can also use configuration list with `ADD = TRUE`. ```{r, error = TRUE, purl = FALSE} opt(c = list(.value = "c", .class = "character"), ADD = TRUE) opt opt$c = 1 ``` Of course you can put more than one options in `opt()` when adding them. ## Features for package development Two additional fields may be helpful when developing packages. `.visible` controls whether options are visible to users. The invisible option can only be queried or modified by specifying its option name (just like you can only open the door with the correct unique key). This would be helpful if users want to put some secret options while do not want others to access. Is this case, they can assign names with complex strings like `.__MY_PRIVATE_KEY__.` as their secret options and afterwards they can access it with this special key. ```{r} opt = set_opt( a = list(.value = 1, .visible = FALSE), b = 2 ) opt() opt$a opt$a = 2 opt$a opt() ``` Another field `.private` controls whether the option is only private to the namespace (e.g. packages). If it is set to `TRUE`, the option can only be modified in the same namespace (or top environment) where the option function is generated. E.g, if you are writing a package named **foo** and generating an option function `foo_opt()`, by setting the option with `.private` to `TRUE`, the value for such options can only be modified inside **foo** package while it is not permitted outside **foo**. At the same time, private options become read-only options if querying outside **foo** package. In following example, we manually modify the namespace where `set_opt()` is called in `stats` package. ```{r} opt = set_opt( a = list(.value = 1, .private = TRUE) ) require(stats) ns = getNamespace("stats") environment(opt)$options$a$`__generated_namespace__` = ns ``` There will be error if trying to modify `a` which is private in `stats` namespace. ```{r error = TRUE, purl = FALSE} opt$a = 2 ``` But you can still access it. ```{r} opt$a ``` The option object generated by `set_opt()` is actually a function. It contains four arguments: `...`, `RESET`, `READ.ONLY`, `LOCAL`, `ADD`. If you want to put the option function into a package, remember to document all the four arguments: ```{r} args(opt) ``` ## Misc The order of validation when modifying an option value is `.read.only`, `.private`, `.length`, `.class`, `.validate`, `.filter`, `.length`, `.class`. Note validation on length and class of the option values will be applied again after filtering. Global options are stored in private environments. Each time when generating a option function, there will be new environments created. Thus global options will not conflict if they come from different option functions. ```{r} opt1 = set_opt( a = list(.value = 1) ) opt2 = set_opt( a = list(.value = 1) ) opt1$a = 2 opt1$a opt2$a ``` Note the option values can also be set as a list, so for the list containing configurations, names of the field is started with a dot `.` to be distinguished from the normal list. ```{r error = TRUE, purl = FALSE} opt = set_opt( list = list(a = 1, b = 2) ) opt() opt = set_opt( list = list(.value = list(a = 1, b = 2), .class = "list") ) opt() opt$list = 1 # this will cause an error ``` If you made a type of the field names when configurating the options (e.g. forgot to type the leading dot), there will be a warning and the whole configuration list is treated as a normal list for this option. ```{r} opt = set_opt( a = list(.value = 1, class = "numeric") # <- here it should be .class ) opt$a ``` The final and the most important thing is the validation by `.class`, `.length`, `.validate`, `.filter` will not be applied on default values because users who design their option functions should know whether the default values are valid or not. ```{r} opt = set_opt( a = list(.value = -1, .validate = function(x) x > 0) ) opt$a ``` ## Session info ```{r} sessionInfo() ``` GlobalOptions/inst/doc/GlobalOptions.html0000644000176200001440000332271513670151477020242 0ustar liggesusers Generate Global Options

Global option function such as options() and par() provides a way to control global settings. Here the GlobalOptions package provides a more general and controlable way to generate such functions, which can:

  1. validate the values (e.g. class, length and self-defined validations);
  2. set read-only options;
  3. set invisible options;
  4. set private options which are only accessable in a certain namespace;
  5. support local options and global option;
  6. print options with explanations.

General usage

The most simple use is to generate an option function with default values by callling setGlobalOptions() or its short versoin set_opt():

library(GlobalOptions)
opt = set_opt(
    a = 1,
    b = "text"
)

The returned value opt is an option function which can be used to get or set options. Options in opt can be accessed either by specifying as arguments or by using the $ operator.

opt()
$a
[1] 1

$b
[1] "text"
opt("a")
[1] 1
opt$a
[1] 1
op = opt()
op
$a
[1] 1

$b
[1] "text"
opt(a = 2, b = "new text")
opt()
$a
[1] 2

$b
[1] "new text"
opt$b = ""
opt()
$a
[1] 2

$b
[1] ""
opt(op)
opt()
$a
[1] 1

$b
[1] "text"

opt generated by set_opt() contains an argument RESET which is used to reset the options to the default:

opt(a = 2, b = "new text")
opt(RESET = TRUE)
opt()
$a
[1] 1

$b
[1] "text"

Simply printing opt gives a summary of all options.

opt
 Option Value
 ------:-------
 a      1    
 b      text 

Advanced usage

If option values are set as lists, more configurations can be customized.

Simple validation

There are two basic fields that are used to check the input option values:

opt = set_opt(
    a = list(.value = 1,
             .length = c(1, 3),
             .class = "numeric")
)

In above code, .value is the default value for the option a. The length of the value is controlled by .length and the length should be either 1 or 3. The class of the value should be numeric. If the input value does not fit these criterions, there will be an error. The value of .length or .class is a vector and the checking will be passed if one of the value fits user’s input.

opt(a = 1:2)  # there will be error because the length is 2
Error: Length of 'a' should be one of 1, 3.
opt(a = "text")  # there will be error because the input is character
Error: Class of 'a' should be 'numeric'.

Read-only options

The value can be set as read-only by .read.only field and modifying such option will cause an error.

opt = set_opt(
    a = list(.value = 1,
             .read.only = TRUE)
)
opt(a = 2)  # there will be error because a is read-only
Error: 'a' is a read-only option.

There is also a pre-defined argument READ.ONLY in opt() which controls whether to return only the read-only options or not.

opt = set_opt(
    a = list(.value = 1,
             .read.only = TRUE),
    b = 2
)
opt(READ.ONLY = TRUE)
$a
[1] 1
opt(READ.ONLY = FALSE)
$b
[1] 2
opt(READ.ONLY = NULL)  # default, to return both
$a
[1] 1

$b
[1] 2

User-defined validation

More customized validation of the option values can be controlled by .validate field. The value of .validate should be a function. The input of the validation function is the input option value and the function should only return a logical value.

a should only between 0 and 10 in following example.

opt = set_opt(
    a = list(.value = 1,
             .validate = function(x) x > 0 && x < 10
    )
)
opt(a = 20)  # This will cause an error
Error: a didn't pass the validation. Your option is invalid.

.failed_msg is used to configure the error message once validation is failed.

opt = set_opt(
    a = list(.value = 1,
             .validate = function(x) x > 0 && x < 10,
             .failed_msg = "'a' should be in (0, 10)."
    )
)
opt(a = 20)  # This will cause an error
Error: a didn't pass the validation. 'a' should be in (0, 10).

Filter the option values

Filtering on the option values can be controlled by .filter field. This is useful when the input option value is not valid but it is not necessary to throw errors. More proper way is to modify the value silently. For example, there is an option to control whether to print messages or not and it should be set to TRUE or FALSE. However, users may set some other type of values such as NULL or NA. In this case, non-TRUE values can be converted to logical values by .filter. Similar as .validate, the input value for filter function is the input option value, and it should return a filtered option value.

opt = set_opt(
    verbose = 
        list(.value = TRUE,
             .filter = function(x) {
                 if(is.null(x)) {
                     return(FALSE)
                 } else if(is.na(x)) {
                     return(FALSE)
                 } else {
                     return(x)
                 }
              })
)
opt(verbose = FALSE); opt("verbose")
[1] FALSE
opt(verbose = NA); opt("verbose")
[1] FALSE
opt(verbose = NULL); opt("verbose")
[1] FALSE

Another example is when there is an option which controls four margin values of a plot, the length of the value can either be 1, 2, or 4. With .filter, length can be normaliezd to 4 consistently.

opt = set_opt(
    margin = 
        list(.value = c(1, 1, 1, 1),
             .length = c(1, 2, 4),
             .filter = function(x) {
                if(length(x) == 1) {
                    return(rep(x, 4))
                } else if(length(x) == 2) {
                    return(rep(x, 2))
                } else {
                    return(x)
                }
            })
)
opt(margin = 2); opt("margin")
[1] 2 2 2 2
opt(margin = c(2, 4)); opt("margin")
[1] 2 4 2 4

Dynamic querying the option value

The input option value can be set dynamicly by setting it as a function. When the option value is set as a function and class of the option is non-function, it will be executed when querying the option. In the following example, the prefix option corresponds to the prefix of log messages. The returned option value is the string after the execution of the input function.

opt = set_opt(
    prefix = ""
)
opt(prefix = function() paste("[", Sys.time(), "] ", sep = " "))
opt("prefix")  # or opt$prefix
[1] "[ 2020-06-10 14:34:04 ] "
Sys.sleep(2)
opt("prefix")
[1] "[ 2020-06-10 14:34:06 ] "

If the value of the option is a real function and users don’t want to execute it, just set .class to contain function, then the function will be treated as a simple value.

opt = set_opt(
    test_fun = list(.value = function(x1, x2) t.test(x1, x2)$p.value,
                    .class = "function")
)
opt(test_fun = function(x1, x2) cor.test(x1, x2)$p.value)
opt("test_fun")  # or opt$test_fun
function(x1, x2) cor.test(x1, x2)$p.value

Interaction between options

The self-defined function (i.e. value function, validation function or filter function) is applied per-option independently. But sometimes we want to set one option based on values of other options. In this case, we need a function which can get other option values. .v() can be used to access other option values defined beforehand. .v("a") can also be written as .v(a) or .v$a.

opt = set_opt(
    a = 1,
    b = function() 2 * .v$a
)
opt("b")  # or opt$b
[1] 2
opt(a = 2)
opt("b")
[1] 4

However, you can still overwrite option b:

opt(a = 2, b = 3) # b was overwriiten and will not be 2*a
opt()
$a
[1] 2

$b
[1] 3

.v can also be used in .validate and .filter fields. In the second example, sign of b should be as same as sign of a.

opt = set_opt(
    a = 1,
    b = list(.value = 0,
             .validate = function(x) {
                 if(.v$a > 0) x > 0
                 else x < 0
             },
             .filter = function(x) {
                 x + .v$a
             },
             .failed_msg = "'b' should have same sign as 'a'.")
)
opt(b = 1)
opt("b")
[1] 2
opt(a = 1, b = -1)  # this should cause an error
Error: b didn't pass the validation. 'b' should have same sign as 'a'.

Local options

The option funtion also has a LOCAL argument which switches local mode and global mode. When LOCAL is set to TRUE, a copy of current options is generated and all queries are applied on the copy version. The local mode is turned off when LOCAL is explicitely specified to FALSE.

opt = set_opt(
    a = 1
)

opt(LOCAL = TRUE)
opt(a = 2)
opt$a
[1] 2
opt(LOCAL = FALSE)
opt$a
[1] 1

Local mode will be automatically turned off when enrivonment changes. In following example, local mode only works inside f1() and f2() functions and the local copies are independent in f1() and f2(). Note when leaving e.g. f1(), the copy of the option is deleted.

opt = set_opt(
    a = 1
)

f1 = function() {
    opt(LOCAL = TRUE)
    opt(a = 2)
    return(opt$a)
}
f1()
[1] 2
opt$a
[1] 1
f2 = function() {
    opt(LOCAL = TRUE)
    opt(a = 4)
    return(opt$a)
}
f2()
[1] 4
opt$a
[1] 1

If f1() calls f2(), f2() will be in the same local mode as f1(). In other word, all children frames are in a same local mode if the parent frame is in local mode.

opt = set_opt(
    a = 1
)

f1 = function() {
    opt(LOCAL = TRUE)
    opt(a = 2)
    return(f2())
}

f2 = function() {
    opt$a
}

f1()
[1] 2
opt$a
[1] 1

Synonymous options

It can be possible that several weeks later, developers have better names for the options. They want to use the new option names but still do not want to disable the old ones. In this case, .synonymous field can be set to let the new option and old option reference to a same internal option object (which means all other configuration specified for this option is ignored). The change of values of either one will also affect the companions correspondingly.

opt = set_opt(
    old = 1,
    new = list(.value = 1,
               .synonymous = "old")
)
opt()
$old
[1] 1

$new
[1] 1
opt$old = 2
opt()
$old
[1] 2

$new
[1] 2
opt$new = 3
opt()
$old
[1] 3

$new
[1] 3

Add new options

New options can be added after the option function is created by explicitely specifying ADD = TRUE:

opt = set_opt(a = 1)
opt(b = 2, ADD = TRUE)
opt
 Option Value
 ------:-------
 a      1    
 b      2    

Note you cannot add new options by using $ (or more precisely $<-) operator because $ can only access options that have already been created.

opt$c = 3
Error: No such option: 'c'. If you want to add this new option, please use
your_opt_fun(c = ..., ADD = TRUE)

Like using a complex configuration list when creating a new option in set_opt(), here you can also use configuration list with ADD = TRUE.

opt(c = list(.value = "c", 
             .class = "character"), 
    ADD = TRUE)
opt
 Option Value
 ------:-------
 a      1    
 b      2    
 c      c    
opt$c = 1
Error: Class of 'c' should be 'character'.

Of course you can put more than one options in opt() when adding them.

Features for package development

Two additional fields may be helpful when developing packages. .visible controls whether options are visible to users. The invisible option can only be queried or modified by specifying its option name (just like you can only open the door with the correct unique key). This would be helpful if users want to put some secret options while do not want others to access. Is this case, they can assign names with complex strings like .__MY_PRIVATE_KEY__. as their secret options and afterwards they can access it with this special key.

opt = set_opt(
    a = list(.value = 1,
             .visible = FALSE),
    b = 2
)
opt()
$b
[1] 2
opt$a
[1] 1
opt$a = 2
opt$a
[1] 2
opt()
$b
[1] 2

Another field .private controls whether the option is only private to the namespace (e.g. packages). If it is set to TRUE, the option can only be modified in the same namespace (or top environment) where the option function is generated. E.g, if you are writing a package named foo and generating an option function foo_opt(), by setting the option with .private to TRUE, the value for such options can only be modified inside foo package while it is not permitted outside foo. At the same time, private options become read-only options if querying outside foo package.

In following example, we manually modify the namespace where set_opt() is called in stats package.

opt = set_opt(
    a = list(.value = 1,
             .private = TRUE)
)
require(stats)
ns = getNamespace("stats")
environment(opt)$options$a$`__generated_namespace__` = ns

There will be error if trying to modify a which is private in stats namespace.

opt$a = 2
Error: 'a' is a private option and it can only be modified inside 'stats'
namespace while not 'R_GlobalEnv'.

But you can still access it.

opt$a
[1] 1

The option object generated by set_opt() is actually a function. It contains four arguments: ..., RESET, READ.ONLY, LOCAL, ADD. If you want to put the option function into a package, remember to document all the four arguments:

args(opt)
function (..., RESET = FALSE, READ.ONLY = NULL, LOCAL = FALSE, 
    ADD = FALSE) 
NULL

Misc

The order of validation when modifying an option value is .read.only, .private, .length, .class, .validate, .filter, .length, .class. Note validation on length and class of the option values will be applied again after filtering.

Global options are stored in private environments. Each time when generating a option function, there will be new environments created. Thus global options will not conflict if they come from different option functions.

opt1 = set_opt(
    a = list(.value = 1)
)
opt2 = set_opt(
    a = list(.value = 1)
)
opt1$a = 2
opt1$a
[1] 2
opt2$a
[1] 1

Note the option values can also be set as a list, so for the list containing configurations, names of the field is started with a dot . to be distinguished from the normal list.

opt = set_opt(
    list = list(a = 1,
                b = 2)
)
opt()
$list
$list$a
[1] 1

$list$b
[1] 2
opt = set_opt(
    list = list(.value = list(a = 1, b = 2),
                .class = "list")
)
opt()
$list
$list$a
[1] 1

$list$b
[1] 2
opt$list = 1  # this will cause an error
Error: Class of 'list' should be 'list'.

If you made a type of the field names when configurating the options (e.g. forgot to type the leading dot), there will be a warning and the whole configuration list is treated as a normal list for this option.

opt = set_opt(
  a = list(.value = 1,
           class = "numeric")  # <- here it should be .class
)
Warning: Your definition for 'a' is mixed. It should only contain .value,
.class, .length, .validate, .failed_msg, .filter, .read.only, .private,
.visible, .synonymous, .description. Ignore the setting and use the
whole list as the default value.
opt$a
$.value
[1] 1

$class
[1] "numeric"

The final and the most important thing is the validation by .class, .length, .validate, .filter will not be applied on default values because users who design their option functions should know whether the default values are valid or not.

opt = set_opt(
    a = list(.value = -1,
             .validate = function(x) x > 0)
)
opt$a
[1] -1

Session info

sessionInfo()
R version 4.0.0 (2020-04-24)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Catalina 10.15.4

Matrix products: default
BLAS:   /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRblas.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRlapack.dylib

locale:
[1] C/en_GB.UTF-8/en_GB.UTF-8/C/en_GB.UTF-8/en_GB.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] GlobalOptions_0.1.2 GetoptLong_1.0.0    knitr_1.28         

loaded via a namespace (and not attached):
 [1] Rcpp_1.0.4.6    digest_0.6.25   crayon_1.3.4    magrittr_1.5   
 [5] evaluate_0.14   rlang_0.4.6     stringi_1.4.6   rmarkdown_2.1  
 [9] rjson_0.2.20    tools_4.0.0     stringr_1.4.0   xfun_0.14      
[13] yaml_2.2.1      compiler_4.0.0  htmltools_0.4.0