pkgconfig/0000755000176200001440000000000013541636302012224 5ustar liggesuserspkgconfig/NAMESPACE0000644000176200001440000000022013335232351013432 0ustar liggesusers# Generated by roxygen2 (4.1.1): do not edit by hand export(get_config) export(set_config) export(set_config_in) importFrom(utils,packageName) pkgconfig/LICENSE0000644000176200001440000000006213335232351013224 0ustar liggesusersYEAR: 2014--2017 COPYRIGHT HOLDER: Gábor Csárdi pkgconfig/man/0000755000176200001440000000000013335232351012774 5ustar liggesuserspkgconfig/man/set_config.Rd0000644000176200001440000000100213335232351015374 0ustar liggesusers% Generated by roxygen2 (4.1.1): do not edit by hand % Please edit documentation in R/getset.R \name{set_config} \alias{set_config} \title{Set a configuration parameter} \usage{ set_config(...) } \arguments{ \item{...}{Parameters to set, they should be all named.} } \value{ Nothing. } \description{ Set a configuration parameter, for the package we are calling from. If called from the R prompt and not from a package, then it sets the parameter for global environment. } \seealso{ \code{\link{set_config_in}} } pkgconfig/man/pkgconfig-package.Rd0000644000176200001440000000101713335232351016622 0ustar liggesusers% Generated by roxygen2 (4.1.1): do not edit by hand % Please edit documentation in R/pkgconfig-package.R \docType{package} \name{pkgconfig-package} \alias{pkgconfig-package} \title{Persistent configuration for R packages} \description{ This package is meant to be used in other packages, and provides configuration options for them. } \details{ It is currently very minimal, and has two main functions: one for setting configuration options (\code{\link{set_config}}), and one for querying them (\code{\link{get_config}}). } pkgconfig/man/get_config.Rd0000644000176200001440000000173713335232351015377 0ustar liggesusers% Generated by roxygen2 (4.1.1): do not edit by hand % Please edit documentation in R/getset.R \name{get_config} \alias{get_config} \title{Query a configuration parameter key} \usage{ get_config(key, fallback = NULL) } \arguments{ \item{key}{The name of the parameter to query.} \item{fallback}{Fallback if the parameter id not found anywhere.} } \value{ The value of the parameter, or the fallback value if not found. } \description{ Query a configuration parameter key, and return the value set in the calling package(s). } \details{ This function is meant to be called from the package whose behavior depends on it. It searches for the given configuration key, and if it exists, it checks which package(s) it was called from and returns the configuration setting for that package. If the key is not set in any calling package, but it is set in the global environment (i.e. at the R prompt), then it returns that setting. If the key is not set anywhere, then it returns \code{NULL}. } pkgconfig/man/set_config_in.Rd0000644000176200001440000000154413335232351016075 0ustar liggesusers% Generated by roxygen2 (4.1.1): do not edit by hand % Please edit documentation in R/getset.R \name{set_config_in} \alias{set_config_in} \title{Set a configuration parameter for a package} \usage{ set_config_in(..., .in = parent.frame()) } \arguments{ \item{...}{Parameters to set, they should be all named.} \item{.in}{An environment, typically belonging to a package.} } \value{ Nothing. } \description{ This is a more flexible variant of \code{link{set_config}}, and it allows creating an custom API in the package that uses \code{pkgconfig} for its configuration. } \details{ This function is identical to \code{\link{set_config}}, but it allows supplying the environment that is used as the package the configuration is set for. This makes it possible to create an API for setting (and getting) configuration parameters. } \seealso{ \code{\link{set_config}} } pkgconfig/DESCRIPTION0000644000176200001440000000123313541636302013731 0ustar liggesusersPackage: pkgconfig Title: Private Configuration for 'R' Packages Version: 2.0.3 Author: Gábor Csárdi Maintainer: Gábor Csárdi Description: Set configuration options on a per-package basis. Options set by a given package only apply to that package, other packages are unaffected. License: MIT + file LICENSE LazyData: true Imports: utils Suggests: covr, testthat, disposables (>= 1.0.3) URL: https://github.com/r-lib/pkgconfig#readme BugReports: https://github.com/r-lib/pkgconfig/issues Encoding: UTF-8 NeedsCompilation: no Packaged: 2019-09-22 08:42:40 UTC; gaborcsardi Repository: CRAN Date/Publication: 2019-09-22 09:20:02 UTC pkgconfig/tests/0000755000176200001440000000000013541631075013370 5ustar liggesuserspkgconfig/tests/testthat/0000755000176200001440000000000013541636302015226 5ustar liggesuserspkgconfig/tests/testthat/test-globalenv.R0000644000176200001440000000117213335232567020306 0ustar liggesusers context("Global env") test_that("Global env does not bother packages", { evalq(set_config(key3 = "value"), .GlobalEnv) on.exit(try(evalq(set_config(key3 = NULL), .GlobalEnv)), add = TRUE) on.exit(try(disposables::dispose_packages(pkgs)), add = TRUE) pkgs <- disposables::make_packages( pkgA = { setter <- function() { set_config(key3 = "value2") } getter <- function() { utility123456::getter() } }, utility123456 = { getter <- function() { get_config("key3", "fallback") } } ) pkgA::setter() expect_equal(get_config("key3"), "value") expect_equal(pkgA::getter(), "value2") }) pkgconfig/tests/testthat/tests.R0000644000176200001440000000522513335232577016526 0ustar liggesusers context("Session") test_that("Session variables", { on.exit(try(disposables::dispose_packages(pkgs))) pkgs <- disposables::make_packages( pkgconfigtest = { f <- function() { set_config(foo = "bar") get_config("foo") } g <- function() { get_config("foo") } h <- function() { get_config("foobar") } } ) expect_equal(f(), "bar") expect_equal(g(), "bar") expect_null(h()) }) test_that("Composite values", { on.exit(try(disposables::dispose_packages(pkgs))) pkgs <- disposables::make_packages( pkgconfigtest = { f <- function() { set_config(foo = list(1,2,3)) get_config("foo") } g <- function() { get_config("foo") } h <- function() { get_config("foobar") } } ) expect_equal(f(), list(1,2,3)) expect_equal(g(), list(1,2,3)) expect_null(h()) }) context("Keys are private") test_that("Two packages do not interfere", { on.exit(try(disposables::dispose_packages(pkgs))) pkgs <- disposables::make_packages( pkgA = { setter <- function() { pkgconfig::set_config(key = "A") } getter <- function() { utility123456::getter() } }, pkgB = { setter <- function() { pkgconfig::set_config(key = "B") } getter <- function() { utility123456::getter() } }, utility123456 = { getter <- function() { pkgconfig::get_config("key") } } ) pkgA::setter() pkgB::setter() expect_equal(pkgA::getter(), "A") expect_equal(pkgB::getter(), "B") }) test_that("Cannot get if set by another package", { on.exit(try(disposables::dispose_packages(pkgs))) pkgs <- disposables::make_packages( pkgconfigtest1 = { getter <- function() { get_config("foo") } getter_parent <- function() { getter() } }, pkgconfigtest2 = { setter <- function() { set_config(foo = "bar") } } ) pkgconfigtest2::setter() expect_null(pkgconfigtest1::getter_parent()) }) test_that("Setting from .onLoad works fine", { on.exit(try(disposables::dispose_packages(pkgs)), add = TRUE) pkgs <- disposables::make_packages( utility123456 = { getter <- function() { pkgconfig::get_config("key", "fallback") } }, pkgA = { .onLoad <- function(lib, pkg) { pkgconfig::set_config(key = "A") } getter <- function() { utility123456::getter() } }, pkgB = { .onLoad <- function(lib, pkg) { pkgconfig::set_config(key = "B") } getter <- function() { utility123456::getter() } }, pkgC = { getter <- function() { utility123456::getter() } } ) expect_equal(pkgA::getter(), "A") expect_equal(pkgB::getter(), "B") expect_equal(pkgC::getter(), "fallback") }) pkgconfig/tests/testthat/test-errors.R0000644000176200001440000000024013335232351017633 0ustar liggesusers context("Errors") test_that("Arguments must be named", { expect_error( set_config("foo" = "bar", "foobar"), "Some parameters are not named" ) }) pkgconfig/tests/testthat/test-api.R0000644000176200001440000000175013335232554017104 0ustar liggesusers context("Creating a custom API") test_that("We can create a custom API", { on.exit(try(disposables::dispose_packages(pkgs))) pkgs <- disposables::make_packages( utility123456 = { set_opt <- function(...) { pars <- list(...) names(pars) <- paste0("utility123456::", names(pars)) do.call(pkgconfig::set_config_in, c(pars, list(.in = parent.frame()))) } get_opt <- function(key) { real_key <- paste0("utility123456::", key) pkgconfig::get_config(real_key) } }, pkgA = { setter <- function() { utility123456::set_opt(key4 = "value_A") } getter <- function() { utility123456::get_opt("key4") } }, pkgB = { setter <- function() { utility123456::set_opt(key4 = "value_B") } getter <- function() { utility123456::get_opt("key4") } } ) pkgA::setter() pkgB::setter() expect_equal(pkgA::getter(), "value_A") expect_equal(pkgB::getter(), "value_B") }) pkgconfig/tests/testthat.R0000644000176200001440000000040413541631075015351 0ustar liggesusers if (require(testthat, quietly = TRUE) && require(disposables, quietly = TRUE) && Sys.getenv("NOT_CRAN") == "true") { library(pkgconfig) test_check("pkgconfig") } else { cat("The testthat and disposables packages are required for unit tests") } pkgconfig/R/0000755000176200001440000000000013541631326012426 5ustar liggesuserspkgconfig/R/utils.R0000644000176200001440000000011413516550170013704 0ustar liggesusers `%||%` <- function(lhs, rhs) { if (!is.null(lhs)) { lhs } else { rhs } } pkgconfig/R/getset.R0000644000176200001440000000631313335232351014043 0ustar liggesusers ## This is the environment that stores all parameters config <- new.env() ## ---------------------------------------------------------------------- #' Query a configuration parameter key #' #' Query a configuration parameter key, and return the value #' set in the calling package(s). #' #' @details #' This function is meant to be called from the package whose #' behavior depends on it. It searches for the given configuration #' key, and if it exists, it checks which package(s) it was called #' from and returns the configuration setting for that package. #' #' If the key is not set in any calling package, but it is set in #' the global environment (i.e. at the R prompt), then it returns that #' setting. #' #' If the key is not set anywhere, then it returns \code{NULL}. #' #' @param key The name of the parameter to query. #' @param fallback Fallback if the parameter id not found anywhere. #' @return The value of the parameter, or the fallback value if not found. #' #' @export get_config <- function(key, fallback = NULL) { result <- get_from_session(key) if (is.null(result)) fallback else result[[1]] } get_from_session <- function(key) { value <- config[[key]] if (is.null(value)) return(NULL) pkgs <- sys.frames() pkgs <- lapply(pkgs, parent.env) pkgs <- Filter(pkgs, f = isNamespace) pkgs <- vapply(pkgs, environmentName, "") pkgs <- unique(pkgs) for (p in rev(pkgs)) { if (p %in% names(value)) return(value[p]) } if ("R_GlobalEnv" %in% names(value)) { return(value["R_GlobalEnv"]) } NULL } ## ---------------------------------------------------------------------- #' Set a configuration parameter #' #' Set a configuration parameter, for the package we are calling from. #' If called from the R prompt and not from a package, then it sets #' the parameter for global environment. #' #' @param ... Parameters to set, they should be all named. #' @return Nothing. #' #' @export #' @seealso \code{\link{set_config_in}} set_config <- function(...) { set_config_in(..., .in = parent.frame()) } check_named_args <- function(...) { nn <- names(list(...)) if (is.null(nn) || any(nn == "")) { stop("Some parameters are not named") } } #' Set a configuration parameter for a package #' #' This is a more flexible variant of \code{link{set_config}}, #' and it allows creating an custom API in the package that #' uses \code{pkgconfig} for its configuration. #' #' @details #' This function is identical to \code{\link{set_config}}, but it allows #' supplying the environment that is used as the package the configuration #' is set for. This makes it possible to create an API for setting #' (and getting) configuration parameters. #' #' @param ... Parameters to set, they should be all named. #' @param .in An environment, typically belonging to a package. #' @return Nothing. #' #' @export #' @seealso \code{\link{set_config}} #' @importFrom utils packageName set_config_in <- function(..., .in = parent.frame()) { check_named_args(...) who <- packageName(env = .in) %||% "R_GlobalEnv" set_config_session(who = who, ...) } set_config_session <- function(who, ...) { l <- list(...) for (n in names(l)) { key <- config[[n]] %||% list() key[[who]] <- l[[n]] config[[n]] <- key } } pkgconfig/R/pkgconfig-package.R0000644000176200001440000000061313541631326016111 0ustar liggesusers #' Persistent configuration for R packages #' #' This package is meant to be used in other packages, and provides #' configuration options for them. #' #' It is currently very minimal, and has two main functions: one #' for setting configuration options (\code{\link{set_config}}), and one #' for querying them (\code{\link{get_config}}). #' #' @docType package #' @name pkgconfig-package NULL pkgconfig/MD50000644000176200001440000000166613541636302012545 0ustar liggesusersc100c3a3491962722d552b713e52811d *DESCRIPTION 4b7ff7c348e978f33981b2237200878d *LICENSE cd8e0bb63bd54833fe1e01ad1a8647d6 *NAMESPACE 9f75eb47d58b82e003713579356c2af6 *R/getset.R 1531da19568f0512ff768360cb78540f *R/pkgconfig-package.R 72539c3bc185b91ce53a54f2bc8336b2 *R/utils.R 5d7a6d9138c1f41b095a2a14717707b1 *inst/NEWS.markdown ebb4499fb7c8f05aed0f8809d5d654d9 *inst/README.Rmd ebb4499fb7c8f05aed0f8809d5d654d9 *inst/README.markdown df949f83f265547f97085e9ad1891a6b *man/get_config.Rd e97c9e9fa3c680d05d3758f07448ef2b *man/pkgconfig-package.Rd 006950b03f96ba261abaeb6f225df012 *man/set_config.Rd d80ab36e7a5e1b8863b79fbbd50466b9 *man/set_config_in.Rd 73200b67627a6a24851d2765b637ac84 *tests/testthat.R fbfc42a64f2c048683101ba83d2b3a6e *tests/testthat/test-api.R da73dc4a696ec2066812ab8c467ab361 *tests/testthat/test-errors.R 44a5eba59b86b2161817bc3d1b68719d *tests/testthat/test-globalenv.R 751a831b28ca39dda21400a1127a457c *tests/testthat/tests.R pkgconfig/inst/0000755000176200001440000000000013541631357013206 5ustar liggesuserspkgconfig/inst/README.Rmd0000644000176200001440000000701213516550355014607 0ustar liggesusers # Private configuration for R packages [![Linux Build Status](https://travis-ci.org/r-lib/pkgconfig.svg?branch=master)](https://travis-ci.org/r-lib/pkgconfig) [![Windows Build status](https://ci.appveyor.com/api/projects/status/github/r-lib/pkgconfig?svg=true)](https://ci.appveyor.com/project/gaborcsardi/pkgconfig) [![](http://www.r-pkg.org/badges/version/pkgconfig)](http://www.r-pkg.org/pkg/pkgconfig) [![](http://cranlogs.r-pkg.org/badges/pkgconfig)](http://www.r-pkg.org/pkg/pkgconfig) [![Coverage Status](https://img.shields.io/codecov/c/github/r-lib/pkgconfig/master.svg)](https://codecov.io/github/r-lib/pkgconfig?branch=master) Easy way to create configuration parameters in your R package. Configuration values set in different packages are independent. Call `set_config()` to set a configuration parameter. Call `get_config()` to query it. ## Installation ```r install.packages("pkgconfig") ``` ## Typical usage > Note: this is a real example, but it is not yet implemented in > the CRAN version of the `igraph` package. The igraph package has two ways of returning a set of vertices. Before version 1.0.0, it simply returned a numeric vector. From version 1.0.0 it sets an S3 class on this vector by default, but it has an option called `return.vs.es` that can be set to `FALSE` to request the old behavior. The problem with the `return.vs.es` option is that it is global. Once set to `FALSE` (interactively or from a package), R will use that setting in all packages, which breaks packages that expect the new behavior. `pkgconfig` solves this problem, by providing configuration settings that are private to packages. Setting a configuration key from a given package will only apply to that package. ## Workflow Let's assume that two packages, `pkgA` and `pkgB`, both set the igraph option `return.vs.es`, but `pkgA` sets it to `TRUE`, and `pkgB` sets it to `FALSE`. Here is how their code will look. ### `pkgA` `pkgA` imports `set_config` from the `pkgconfig` package, and sets the `return.vs.es` option from it's `.onLoad` function: ```r .onLoad <- function(lib, pkg) { pkgconfig::set_config("igraph::return.vs.es" = TRUE) } ``` ### `pkgB` `pkgB` is similar, but it sets the option to `FALSE`: ```r .onLoad <- function(lib, pkg) { pkgconfig::set_config("igraph::return.vs.es" = FALSE) } ``` ### `igraph` The igraph package will use `get_config` to query the option, and will supply a fallback value for the cases when it is not set: ```r return_vs_es_default <- TRUE # ... igraph_func <- function() { # ... pkgconfig::get_config("igraph::return.vs.es", return_vs_es_default) # ... } ``` If `igraph_func` is called from `pkgA` (maybe through other packages), `get_config` will return `TRUE`, and if it is called from `pkgB`, `get_config` will return `FALSE`. If no package on the call stack sets the `igraph::return.vs.es` option, then its default value is used, as specified in `igraph`. ## What if `pkgA` calls `pkgB`? It might happen that both `pkgA` and `pkgB` set an option, and `pkgA` also calls functions from `pkgB`, which in turn, might call `igraph`. In this case the package that is further down the call stack wins. In other words, if the call sequence looks like this: ``` ... -> pkgA -> ... -> pkgB -> ... -> igraph ``` then `pkgB`'s value is used in `igraph`. (Assuming the last `...` does not contain a call to `pkgA` of course.) ## Feedback Please comment in the [Github issue tracker](https://github.com/r-lib/pkgconfig/issues) of the project. ## License MIT © [Gábor Csárdi](https://github.com/gaborcsardi) pkgconfig/inst/NEWS.markdown0000644000176200001440000000120313541631357015522 0ustar liggesusers # 2.0.3 No user visible changes. # 2.0.2 No user visible changes, work around a CRAN check false positive. # 2.0.1 No changes in functionality, only internal cleanup. # 2.0.0 * Can also be used from the global environment, not only from packages. * `set_config_in()` function, to allow custom APIs. This means that packages does not have to use `set_config()` and `get_config()`, but they can provide their own API. * Fix a `get_config()` bug, for composite values only the first element was returned. * Fix a bug when key was not set at all. In these cases `fallback` was ignored in `get_config()`. # 1.0.0 Initial release. pkgconfig/inst/README.markdown0000644000176200001440000000701213516550407015705 0ustar liggesusers # Private configuration for R packages [![Linux Build Status](https://travis-ci.org/r-lib/pkgconfig.svg?branch=master)](https://travis-ci.org/r-lib/pkgconfig) [![Windows Build status](https://ci.appveyor.com/api/projects/status/github/r-lib/pkgconfig?svg=true)](https://ci.appveyor.com/project/gaborcsardi/pkgconfig) [![](http://www.r-pkg.org/badges/version/pkgconfig)](http://www.r-pkg.org/pkg/pkgconfig) [![](http://cranlogs.r-pkg.org/badges/pkgconfig)](http://www.r-pkg.org/pkg/pkgconfig) [![Coverage Status](https://img.shields.io/codecov/c/github/r-lib/pkgconfig/master.svg)](https://codecov.io/github/r-lib/pkgconfig?branch=master) Easy way to create configuration parameters in your R package. Configuration values set in different packages are independent. Call `set_config()` to set a configuration parameter. Call `get_config()` to query it. ## Installation ```r install.packages("pkgconfig") ``` ## Typical usage > Note: this is a real example, but it is not yet implemented in > the CRAN version of the `igraph` package. The igraph package has two ways of returning a set of vertices. Before version 1.0.0, it simply returned a numeric vector. From version 1.0.0 it sets an S3 class on this vector by default, but it has an option called `return.vs.es` that can be set to `FALSE` to request the old behavior. The problem with the `return.vs.es` option is that it is global. Once set to `FALSE` (interactively or from a package), R will use that setting in all packages, which breaks packages that expect the new behavior. `pkgconfig` solves this problem, by providing configuration settings that are private to packages. Setting a configuration key from a given package will only apply to that package. ## Workflow Let's assume that two packages, `pkgA` and `pkgB`, both set the igraph option `return.vs.es`, but `pkgA` sets it to `TRUE`, and `pkgB` sets it to `FALSE`. Here is how their code will look. ### `pkgA` `pkgA` imports `set_config` from the `pkgconfig` package, and sets the `return.vs.es` option from it's `.onLoad` function: ```r .onLoad <- function(lib, pkg) { pkgconfig::set_config("igraph::return.vs.es" = TRUE) } ``` ### `pkgB` `pkgB` is similar, but it sets the option to `FALSE`: ```r .onLoad <- function(lib, pkg) { pkgconfig::set_config("igraph::return.vs.es" = FALSE) } ``` ### `igraph` The igraph package will use `get_config` to query the option, and will supply a fallback value for the cases when it is not set: ```r return_vs_es_default <- TRUE # ... igraph_func <- function() { # ... pkgconfig::get_config("igraph::return.vs.es", return_vs_es_default) # ... } ``` If `igraph_func` is called from `pkgA` (maybe through other packages), `get_config` will return `TRUE`, and if it is called from `pkgB`, `get_config` will return `FALSE`. If no package on the call stack sets the `igraph::return.vs.es` option, then its default value is used, as specified in `igraph`. ## What if `pkgA` calls `pkgB`? It might happen that both `pkgA` and `pkgB` set an option, and `pkgA` also calls functions from `pkgB`, which in turn, might call `igraph`. In this case the package that is further down the call stack wins. In other words, if the call sequence looks like this: ``` ... -> pkgA -> ... -> pkgB -> ... -> igraph ``` then `pkgB`'s value is used in `igraph`. (Assuming the last `...` does not contain a call to `pkgA` of course.) ## Feedback Please comment in the [Github issue tracker](https://github.com/r-lib/pkgconfig/issues) of the project. ## License MIT © [Gábor Csárdi](https://github.com/gaborcsardi)