From 90a8b0b682131bdf7bff92fb5a4a7d8c673f36bf Mon Sep 17 00:00:00 2001 From: GidonFrischkorn Date: Fri, 23 Feb 2024 09:06:44 +0100 Subject: [PATCH 1/6] Load default options & add startup message --- R/zzz.R | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 R/zzz.R diff --git a/R/zzz.R b/R/zzz.R new file mode 100644 index 00000000..c4a057a2 --- /dev/null +++ b/R/zzz.R @@ -0,0 +1,38 @@ +## set default options for afex_options: +.onLoad <- function(libname, pkgname) { + options <- options() + options_bmm <- list( + bmm.default_priors = TRUE + ) + + options_to_set <- !(names(options_bmm) %in% names(options)) + if (any(options_to_set)) options(options_bmm[options_to_set]) + + invisible() +} + +.onAttach <- function(libname, pkgname) { + # test if local installation is behing CRAN + cran_pkgs <- available.packages() + cran_version <- cran_pkgs[which(cran_pkgs[,"Package"] == "bmm"),"Version"] + local_version <- packageVersion("bmm") + behind_cran <- cran_version > local_version + + startUpMsg <- c( + paste0("Loading bmm (version: ",local_version,"). ", + "A short introduction to package is available by calling help(\"bmm\"). More detailed + articles on how to fit different models are available via vignettes(\"bmm\").") + ) + + if (interactive()) { + if (length(behind_cran) > 0 && behind_cran) { + msg <- "A newer version of bmm is available on CRAN." + packageStartupMessage(msg, "\nWould you like to install it?") + if (menu(c("Yes", "No")) == 1) { + update.packages("vistributions") + } + } else { + packageStartupMessage(paste(strwrap(startUpMsg), collapse = "\n")) + } + } +} From 688454abfd67f6edca9cbb54fd64ce8b901cb243 Mon Sep 17 00:00:00 2001 From: GidonFrischkorn Date: Fri, 23 Feb 2024 09:29:57 +0100 Subject: [PATCH 2/6] Update DESCRIPTION --- DESCRIPTION | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index ee6fea73..a41215ea 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -4,11 +4,13 @@ Version: 0.3.9.9000 Authors@R: c( person("Vencislav", "Popov", , "vencislav.popov@gmail.com", role = c("aut", "cre", "cph")), person("Gidon", "Frischkorn", , "gidon.frischkorn@psychologie.uzh.ch", role = c("aut", "cph"))) -Description: Wrapper functions and custom distributions that make it easier to estimate common - measurement models for using the 'brms' package. Currently implemented - are the two-parameter mixture model by Zhang and Luck (2008),the three- - parameter mixture model by Bays et al (2009), and the Interference Measurement Model - (Oberauer et al., 2017). +Description: Implementations of computational measurement models using the 'brms' package. + Currently implemented models can be listed using supported_models(). The package also + provides functions to extract model informations such as priors, or the generated + STAN code. For all implemented models there are also density and random generation + functions to easily explore model predictions and evaluate parameter recovery. + Finally, helper functions aid in pre- and post-processing data for efficient communication + of results. License: GPL (>= 3) Encoding: UTF-8 Roxygen: list(markdown = TRUE) From 303139fbcb4b4bb8b36cf085f5f7ab8784e8227c Mon Sep 17 00:00:00 2001 From: GidonFrischkorn Date: Fri, 23 Feb 2024 09:37:25 +0100 Subject: [PATCH 3/6] remove .onLoad options --- DESCRIPTION | 5 ++++- R/zzz.R | 8 -------- man/bmm-package.Rd | 2 +- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index a41215ea..0e27507b 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -11,7 +11,7 @@ Description: Implementations of computational measurement models using the 'brms functions to easily explore model predictions and evaluate parameter recovery. Finally, helper functions aid in pre- and post-processing data for efficient communication of results. -License: GPL (>= 3) +License: GPL-2 Encoding: UTF-8 Roxygen: list(markdown = TRUE) RoxygenNote: 7.3.1 @@ -43,6 +43,9 @@ Imports: methods URL: https://github.com/venpopov/bmm, https://venpopov.github.io/bmm/ BugReports: https://github.com/venpopov/bmm/issues +Additional_repositories: + https://mc-stan.org/r-packages/ + https://paul-buerkner.github.io/brms/ VignetteBuilder: knitr Depends: R (>= 2.10), diff --git a/R/zzz.R b/R/zzz.R index c4a057a2..43960b0b 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -1,14 +1,6 @@ ## set default options for afex_options: .onLoad <- function(libname, pkgname) { - options <- options() - options_bmm <- list( - bmm.default_priors = TRUE - ) - - options_to_set <- !(names(options_bmm) %in% names(options)) - if (any(options_to_set)) options(options_bmm[options_to_set]) - invisible() } .onAttach <- function(libname, pkgname) { diff --git a/man/bmm-package.Rd b/man/bmm-package.Rd index 18169409..6f13b7e3 100644 --- a/man/bmm-package.Rd +++ b/man/bmm-package.Rd @@ -6,7 +6,7 @@ \alias{bmm-package} \title{bmm: Easy and Accesible Bayesian Measurement Models using 'brms'} \description{ -Wrapper functions and custom distributions that make it easier to estimate common measurement models for using the 'brms' package. Currently implemented are the two-parameter mixture model by Zhang and Luck (2008),the three- parameter mixture model by Bays et al (2009), and the Interference Measurement Model (Oberauer et al., 2017). +Implementations of computational measurement models using the 'brms' package. Currently implemented models can be listed using supported_models(). The package also provides functions to extract model informations such as priors, or the generated STAN code. For all implemented models there are also density and random generation functions to easily explore model predictions and evaluate parameter recovery. Finally, helper functions aid in pre- and post-processing data for efficient communication of results. } \seealso{ Useful links: From 50bf7a45e22ce381bc2c9e6ea8385a77ecca8610 Mon Sep 17 00:00:00 2001 From: GidonFrischkorn Date: Tue, 27 Feb 2024 09:17:38 +0100 Subject: [PATCH 4/6] Initiate pkgStartUp Message --- R/zzz.R | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/R/zzz.R b/R/zzz.R index 43960b0b..538f88b4 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -4,27 +4,34 @@ } .onAttach <- function(libname, pkgname) { - # test if local installation is behing CRAN - cran_pkgs <- available.packages() + # test if local installation is behind CRAN + cran_pkgs <- utils::available.packages(repos = "http://cran.us.r-project.org") cran_version <- cran_pkgs[which(cran_pkgs[,"Package"] == "bmm"),"Version"] - local_version <- packageVersion("bmm") + local_version <- utils::packageVersion("bmm") behind_cran <- cran_version > local_version startUpMsg <- c( - paste0("Loading bmm (version: ",local_version,"). ", - "A short introduction to package is available by calling help(\"bmm\"). More detailed - articles on how to fit different models are available via vignettes(\"bmm\").") + paste0("A short introduction to package is available by calling help(\"bmm\"). \n", + "More detailed articles on how to fit different models are available via vignettes(\"bmm\").") ) + banner <- " _ +| |_ _____ _____ +| . | | | +|___|_|_|_|_|_|_| +" + + versionMsg <- paste0("Loading bmm (version: ",local_version,").\n") + if (interactive()) { if (length(behind_cran) > 0 && behind_cran) { msg <- "A newer version of bmm is available on CRAN." packageStartupMessage(msg, "\nWould you like to install it?") - if (menu(c("Yes", "No")) == 1) { - update.packages("vistributions") + if (utils::menu(c("Yes", "No")) == 1) { + utils::update.packages("bmm") } } else { - packageStartupMessage(paste(strwrap(startUpMsg), collapse = "\n")) + packageStartupMessage(banner, versionMsg, startUpMsg) } } } From 1acfb57a85d6dfb9f63dd2edf6e0ef1aa200562f Mon Sep 17 00:00:00 2001 From: GidonFrischkorn Date: Tue, 27 Feb 2024 09:21:23 +0100 Subject: [PATCH 5/6] CleanUp formatting --- R/zzz.R | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/R/zzz.R b/R/zzz.R index 538f88b4..8b117c91 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -10,11 +10,7 @@ local_version <- utils::packageVersion("bmm") behind_cran <- cran_version > local_version - startUpMsg <- c( - paste0("A short introduction to package is available by calling help(\"bmm\"). \n", - "More detailed articles on how to fit different models are available via vignettes(\"bmm\").") - ) - + # add banner of package banner <- " _ | |_ _____ _____ | . | | | @@ -23,6 +19,11 @@ versionMsg <- paste0("Loading bmm (version: ",local_version,").\n") + startUpMsg <- c( + paste0("A short introduction to package is available by calling help(\"bmm\"). \n", + "More detailed articles on how to fit different models are available via vignettes(\"bmm\").") + ) + if (interactive()) { if (length(behind_cran) > 0 && behind_cran) { msg <- "A newer version of bmm is available on CRAN." From cecc64bf18ec0be80a104d3a4a7154d589f537b6 Mon Sep 17 00:00:00 2001 From: Ven Popov Date: Tue, 27 Feb 2024 12:46:15 +0100 Subject: [PATCH 6/6] new function bmm_options and use it onLoad - bmm_options() allows to view or set all bmm global options - it is called onLoad to set defaults and display them in the package load message - add tryCatch2 to be able to handle messages and display them when desired --- DESCRIPTION | 2 +- NAMESPACE | 1 + NEWS.md | 2 + R/fit_model.R | 14 ++++-- R/utils.R | 96 ++++++++++++++++++++++++++++++++++++- R/zzz.R | 10 ++-- _pkgdown.yml | 1 + man/bmm_options.Rd | 44 +++++++++++++++++ man/fit_model.Rd | 22 ++++----- tests/testthat/test-utils.R | 11 +++++ 10 files changed, 181 insertions(+), 22 deletions(-) create mode 100644 man/bmm_options.Rd diff --git a/DESCRIPTION b/DESCRIPTION index 7a28a08d..8a73499c 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: bmm Title: Easy and Accesible Bayesian Measurement Models using 'brms' -Version: 0.3.9.9000 +Version: 0.3.10.9000 Authors@R: c( person("Vencislav", "Popov", , "vencislav.popov@gmail.com", role = c("aut", "cre", "cph")), person("Gidon", "Frischkorn", , "gidon.frischkorn@psychologie.uzh.ch", role = c("aut", "cph")), diff --git a/NAMESPACE b/NAMESPACE index 813efbf9..9e8546cd 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -42,6 +42,7 @@ export(IMMbsc) export(IMMfull) export(bmf) export(bmf2bf) +export(bmm_options) export(bmmformula) export(c_bessel2sqrtexp) export(c_sqrtexp2bessel) diff --git a/NEWS.md b/NEWS.md index 0c916742..54f722bc 100644 --- a/NEWS.md +++ b/NEWS.md @@ -8,6 +8,8 @@ * add informed default priors for all models. You can always use the `get_model_prior()` function to see the default priors for a model * add a new function `set_default_prior` for developers, which allows them to more easily set default priors on new models regardless of the user-specified formula * you can now specify variables for models via regular expressions rather than character vectors (#102) +* you can now view and set all bmm global options via `bmm_options()`. See `?bmm_options` for more information +* add a startup message upon loading the package ### Bug fixes * fix a bug in the mixture3p and IMM models which caused an error when intercept was not supressed and set size was used as predictor diff --git a/R/fit_model.R b/R/fit_model.R index 01cadfc2..12edc0c8 100644 --- a/R/fit_model.R +++ b/R/fit_model.R @@ -26,10 +26,10 @@ #' fitting, but you can provide prior constraints to model parameters #' @param sort_data Logical. If TRUE, the data will be sorted by the predictor #' variables for faster sampling. If FALSE, the data will not be sorted, but -#' sampling will be slower. If NULL (the default), `fit_model()` will check if +#' sampling will be slower. If "check" (the default), `fit_model()` will check if #' the data is sorted, and ask you via a console prompt if it should be #' sorted. You can set the default value for this option using global -#' `options(bmm.sort_data = TRUE/FALSE)` +#' `options(bmm.sort_data = TRUE/FALSE/"check)`)` or via `bmm_options(sort_data)` #' @param silent Verbosity level between 0 and 2. If 1 (the default), most of the #' informational messages of compiler and sampler are suppressed. If 2, even #' more messages are suppressed. The actual sampling progress is still @@ -74,9 +74,13 @@ #' backend='cmdstanr') #' } #' -fit_model <- function(formula, data, model, parallel = FALSE, chains = 4, - prior = NULL, sort_data = getOption('bmm.sort_data', NULL), - silent = getOption('bmm.silent', 1), ...) { +fit_model <- function(formula, data, model, + prior = NULL, + chains = 4, + parallel = getOption('bmm.parallel', FALSE), + sort_data = getOption('bmm.sort_data', "check"), + silent = getOption('bmm.silent', 1), + ...) { # warning for using old version dots <- list(...) if ("model_type" %in% names(dots)) { diff --git a/R/utils.R b/R/utils.R index 3223d7a6..10381974 100644 --- a/R/utils.R +++ b/R/utils.R @@ -256,7 +256,7 @@ stop_quietly <- function() { # data is ordered by the predictor variables. This function checks if the data is # ordered by the predictors, and if not, it suggests to the user to sort the data order_data_query <- function(model, data, formula) { - sort_data <- getOption("bmm.sort_data", NULL) + sort_data <- getOption("bmm.sort_data", "check") dpars <- names(formula) predictors <- rhs_vars(formula) predictors <- predictors[not_in(predictors, dpars)] @@ -382,3 +382,97 @@ identical.formula <- function(x, y, ...) { res <- waldo::compare(x, y, ignore_formula_env = TRUE) length(res) == 0 } + + +#' View or change global bmm options +#' @param sort_data logical. If TRUE, the data will be sorted by the predictors. If +#' FALSE, the data will not be sorted, but sampling will be slower. If "check" (the +#' default), `fit_model()` will check if the data is sorted, and ask you via a +#' console prompt if it should be sorted. +#' @param parallel logical. If TRUE, chains will be run in parallel. If FALSE, chains will +#' be run sequentially. You can also set these value for each model separately via +#' the argument `parallel` in `fit_model()`. +#' @param default_priors logical. If TRUE (default), the default bmm priors will be used. If +#' FALSE, only the basic `brms` priors will be used. +#' @param silent numeric. Verbosity level between 0 and 2. If 1 (the default), most of the +#' informational messages of compiler and sampler are suppressed. If 2, even +#' more messages are suppressed. The actual sampling progress is still printed. +#' @param reset_options logical. If TRUE, the options will be reset to their default values +#' @details The `bmm_options` function is used to view or change the current bmm +#' options. If no arguments are provided, the function will return the current +#' options. If arguments are provided, the function will change the options and +#' return the old options invisibly. If you provide only some of the arguments, +#' the other options will not be changed. The options are stored in the global options +#' list and will be used by `fit_model()` and other functions in the `bmm` package. +#' Each of these options can also be set manually using the built-in `options()` function, +#' by setting the `bmm.sort_data`, `bmm.default_priors`, and `bmm.silent` options. +#' @return A message with the current bmm options and their values, and invisibly +#' returns the old options for use with on.exit() and friends. +#' @export +bmm_options <- function(sort_data, parallel, default_priors, silent, reset_options = FALSE) { + opts <- ls() + if (!missing(sort_data) && sort_data != "check" && !is.logical(sort_data)) { + stop2("sort_data must be one of TRUE, FALSE, or 'check'") + } + if (!missing(parallel) && !is.logical(parallel)) { + stop2("parallel must be one of TRUE or FALSE") + } + if (!missing(default_priors) && !is.logical(default_priors)) { + stop2("default_priors must be a TRUE or FALSE") + } + if (!missing(silent) && (!is.numeric(silent) || silent < 0 || silent > 2)) { + stop2("silent must be one of 0, 1, or 2") + } + + # set default options if function is called for the first time or if reset_options is TRUE + if (reset_options) { + options(bmm.sort_data = "check", + bmm.parallel = FALSE, + bmm.default_priors = TRUE, + bmm.silent = 1) + } + + # change options if arguments are provided. get argument name and loop over non-missing arguments + op <- list() + non_missing_args <- names(match.call())[-1] + non_missing_args <- non_missing_args[!non_missing_args %in% "reset_options"] + for (i in non_missing_args) { + op[[paste0('bmm.',i)]] <- get(i) + } + + old_op <- options(op) + message2("\nCurrent bmm options:\n", + crayon::green(paste0(" sort_data = ", getOption("bmm.sort_data"),"", + "\n parallel = ", getOption("bmm.parallel"), + "\n default_priors = ", getOption("bmm.default_priors"), + "\n silent = ", getOption("bmm.silent"), "\n")), + "For more information on these options or how to change them, see help(bmm_options).\n") + invisible(old_op) +} + +# an improved version of tryCatch that captures messages as well +# modified version of https://github.com/cran/admisc/blob/master/R/tryCatchWEM.R +tryCatch2 <- function(expr, capture = FALSE) { + toreturn <- list() + output <- withVisible(withCallingHandlers( + tryCatch(expr, error = function(e) { + toreturn$error <<- e$message + NULL + }), + warning = function(w) { + toreturn$warning <<- c(toreturn$warning, w$message) + invokeRestart("muffleWarning") + }, + message = function(m) { + toreturn$message <<- paste(toreturn$message, m$message, sep = "") + invokeRestart("muffleMessage") + } + )) + if (capture && output$visible && !is.null(output$value)) { + toreturn$output <- utils::capture.output(output$value) + toreturn$value <- output$value + } + if (length(toreturn) > 0) { + return(toreturn) + } +} diff --git a/R/zzz.R b/R/zzz.R index 8b117c91..f08d4baa 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -1,6 +1,5 @@ -## set default options for afex_options: .onLoad <- function(libname, pkgname) { - + suppressMessages(bmm_options(reset_options = TRUE)) } .onAttach <- function(libname, pkgname) { @@ -21,9 +20,12 @@ startUpMsg <- c( paste0("A short introduction to package is available by calling help(\"bmm\"). \n", - "More detailed articles on how to fit different models are available via vignettes(\"bmm\").") + "More detailed articles on how to fit different models are available via vignettes(\"bmm\").\n", + "You can view the list of currently available models by calling supported_models().\n") ) + optionsMsg <- tryCatch2(bmm_options())$message + if (interactive()) { if (length(behind_cran) > 0 && behind_cran) { msg <- "A newer version of bmm is available on CRAN." @@ -32,7 +34,7 @@ utils::update.packages("bmm") } } else { - packageStartupMessage(banner, versionMsg, startUpMsg) + packageStartupMessage(banner, versionMsg, startUpMsg, optionsMsg) } } } diff --git a/_pkgdown.yml b/_pkgdown.yml index e6118884..aa837eb8 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -30,6 +30,7 @@ reference: - contents: - "bmm" - "supported_models" + - "bmm_options" - title: "Fitting models" desc: "Main functions for model fitting" - contents: diff --git a/man/bmm_options.Rd b/man/bmm_options.Rd new file mode 100644 index 00000000..e49370da --- /dev/null +++ b/man/bmm_options.Rd @@ -0,0 +1,44 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{bmm_options} +\alias{bmm_options} +\title{View or change global bmm options} +\usage{ +bmm_options(sort_data, parallel, default_priors, silent, reset_options = FALSE) +} +\arguments{ +\item{sort_data}{logical. If TRUE, the data will be sorted by the predictors. If +FALSE, the data will not be sorted, but sampling will be slower. If "check" (the +default), \code{fit_model()} will check if the data is sorted, and ask you via a +console prompt if it should be sorted.} + +\item{parallel}{logical. If TRUE, chains will be run in parallel. If FALSE, chains will +be run sequentially. You can also set these value for each model separately via +the argument \code{parallel} in \code{fit_model()}.} + +\item{default_priors}{logical. If TRUE (default), the default bmm priors will be used. If +FALSE, only the basic \code{brms} priors will be used.} + +\item{silent}{numeric. Verbosity level between 0 and 2. If 1 (the default), most of the +informational messages of compiler and sampler are suppressed. If 2, even +more messages are suppressed. The actual sampling progress is still printed.} + +\item{reset_options}{logical. If TRUE, the options will be reset to their default values} +} +\value{ +A message with the current bmm options and their values, and invisibly +returns the old options for use with on.exit() and friends. +} +\description{ +View or change global bmm options +} +\details{ +The \code{bmm_options} function is used to view or change the current bmm +options. If no arguments are provided, the function will return the current +options. If arguments are provided, the function will change the options and +return the old options invisibly. If you provide only some of the arguments, +the other options will not be changed. The options are stored in the global options +list and will be used by \code{fit_model()} and other functions in the \code{bmm} package. +Each of these options can also be set manually using the built-in \code{options()} function, +by setting the \code{bmm.sort_data}, \code{bmm.default_priors}, and \code{bmm.silent} options. +} diff --git a/man/fit_model.Rd b/man/fit_model.Rd index 671f9b4a..b02b99e6 100644 --- a/man/fit_model.Rd +++ b/man/fit_model.Rd @@ -8,10 +8,10 @@ fit_model( formula, data, model, - parallel = FALSE, - chains = 4, prior = NULL, - sort_data = getOption("bmm.sort_data", NULL), + chains = 4, + parallel = getOption("bmm.parallel", FALSE), + sort_data = getOption("bmm.sort_data", "check"), silent = getOption("bmm.silent", 1), ... ) @@ -30,23 +30,23 @@ number of required arguments which need to be specified within the function call. Call \code{\link[=supported_models]{supported_models()}} to see the list of supported models and their required arguments} -\item{parallel}{Logical; If TRUE, the number of cores on your machine will be -detected and brms will fit max(chains, cores) number of chains (specified -by the \code{chain} argument) in parallel using the parallel package} - -\item{chains}{Numeric. Number of Markov chains (defaults to 4)} - \item{prior}{One or more \code{brmsprior} objects created by \code{\link[brms:set_prior]{brms::set_prior()}} or related functions and combined using the c method or the + operator. See also \code{\link[=get_model_prior]{get_model_prior()}} for more help. Not necessary for the default model fitting, but you can provide prior constraints to model parameters} +\item{chains}{Numeric. Number of Markov chains (defaults to 4)} + +\item{parallel}{Logical; If TRUE, the number of cores on your machine will be +detected and brms will fit max(chains, cores) number of chains (specified +by the \code{chain} argument) in parallel using the parallel package} + \item{sort_data}{Logical. If TRUE, the data will be sorted by the predictor variables for faster sampling. If FALSE, the data will not be sorted, but -sampling will be slower. If NULL (the default), \code{fit_model()} will check if +sampling will be slower. If "check" (the default), \code{fit_model()} will check if the data is sorted, and ask you via a console prompt if it should be sorted. You can set the default value for this option using global -\code{options(bmm.sort_data = TRUE/FALSE)}} +\verb{options(bmm.sort_data = TRUE/FALSE/"check)})\verb{or via}bmm_options(sort_data)`} \item{silent}{Verbosity level between 0 and 2. If 1 (the default), most of the informational messages of compiler and sampler are suppressed. If 2, even diff --git a/tests/testthat/test-utils.R b/tests/testthat/test-utils.R index 527df258..79537a35 100644 --- a/tests/testthat/test-utils.R +++ b/tests/testthat/test-utils.R @@ -41,3 +41,14 @@ test_that("get_variables works", { expect_equal(get_variables('a|b', c('a', 'b', 'c'), regex = FALSE), 'a|b') expect_error(get_variables('d', c('a', 'b', 'c'), regex = TRUE)) }) + +test_that("bmm_options works", { + withr::defer(suppressMessages(bmm_options())) + expect_message(bmm_options(), "Current bmm options") + expect_message(bmm_options(sort_data = TRUE), "sort_data = TRUE") + expect_equal(getOption('bmm.sort_data'), TRUE) + op <- suppressMessages(bmm_options(sort_data = FALSE)) + expect_equal(getOption('bmm.sort_data'), FALSE) + options(op) + expect_equal(getOption('bmm.sort_data'), TRUE) +})