Title: | Run Code Only Once |
---|---|
Description: | Allows code to be run only once on a given computer, using lockfiles. Typical use cases include startup messages shown only when a package is loaded for the very first time. |
Authors: | David Hugh-Jones [aut, cre] |
Maintainer: | David Hugh-Jones <[email protected]> |
License: | MIT + file LICENSE |
Version: | 0.2.0 |
Built: | 2024-08-15 02:27:00 UTC |
Source: | https://github.com/hughjonesd/onetime |
The onetime package works by storing lockfiles in
rappdirs::user_config_dir()
. It won't do so unless permission has been
granted. Before using onetime
functions, package authors should call
check_ok_to_store(ask = TRUE)
in an interactive session, in functions which
are called directly from the command line.
check_ok_to_store( ask = FALSE, message = "The onetime package requests to store files in '%s'.", confirm_prompt = "Is this OK? [Yn] ", confirm_answers = c("Y", "y", "Yes", "yes", "YES"), default_answer = "Y" )
check_ok_to_store( ask = FALSE, message = "The onetime package requests to store files in '%s'.", confirm_prompt = "Is this OK? [Yn] ", confirm_answers = c("Y", "y", "Yes", "yes", "YES"), default_answer = "Y" )
ask |
|
message |
Message to display to the user. |
confirm_prompt |
Character string. Question to prompt the user to hide the message in future. |
confirm_answers |
Character vector. Answers which will cause the message to be hidden in future. |
default_answer |
Character string. Default answer if user simply presses return. |
If your package is not used interactively, a workaround is to call
set_ok_to_store()
. This grants permission and prints an informative
message. Package owners should only call this if they cannot ask
explicitly.
onetime_message_confirm()
is an exception: by default it doesn't require
global permission to store files, since the user accepting "Don't show this
again" is considered sufficient.
ask = TRUE
asks the user, if he or she has not already given permission,
and if the session is interactive()
.
Remaining parameters are passed to onetime_message_confirm()
in this case,
and ignored otherwise. A "%s"
in message
will be replaced by the
onetime storage directory.
TRUE
if:
We already have permission;
ask
is TRUE
, we are in an interactive session and the user
gives us permission;
options("onetime.dir")
is set to a non-NULL
value.
Otherwise FALSE
.
check_ok_to_store()
check_ok_to_store()
Onetime allows package authors to run code only once (ever) for a given
user. It does so by writing a file, typically to a folder in the user's
configuration directory as given by rappdirs::user_config_dir()
. The
user can set an alternative filepath using options("onetime.dir")
.
Core functions include:
onetime_do()
runs arbitrary code only once.
onetime_warning()
and friends print a warning or message only once.
onetime_message_confirm()
prints a message and asks
"Show this message again?"
onetime_rlang_warn()
and onetime_rlang_inform()
print messages using
functions from the rlang package.
onetime_only()
returns a function that runs only once.
check_ok_to_store()
and set_ok_to_store()
check for or grant
permission to store lockfiles on the user's computer.
It is package authors' responsibility to check for permission
to store lockfiles. This may have been done already by another package if
onetime was already installed. You can ask permission interactively on
the command line by calling check_ok_to_store()
with ask = TRUE
.
For more information, see vignette("onetime")
.
library(onetime) ids <- paste0("onetime-readme-", 1:3) for (i in 1:5) { onetime_do(cat("This command will only be run once.\n"), id = ids[1]) onetime_warning("This warning will only be shown once.", id = ids[2]) onetime_message("This message will only be shown once.", id = ids[3]) }
## This command will only be run once. ## Warning: This warning will only be shown once. ## This message will only be shown once.
Maintainer: David Hugh-Jones [email protected]
Useful links:
Report bugs at https://github.com/hughjonesd/onetime/issues
Check if a onetime call has already been made
onetime_been_done( id = deprecate_calling_package(), path = default_lockfile_dir(), expiry = NULL )
onetime_been_done( id = deprecate_calling_package(), path = default_lockfile_dir(), expiry = NULL )
id |
Unique ID string. If this is unset, the name of the calling
package will be used. Since onetime 0.2.0, not setting |
path |
Directory to store lockfiles. The default uses a unique
directory corresponding to the calling package, beneath
|
expiry |
|
TRUE
if the call has been recorded (within the
expiry
time, if given).
oo <- options(onetime.dir = tempdir(check = TRUE)) id <- sample(10000L, 1) onetime_been_done(id = id) onetime_message("Creating an ID", id = id) onetime_been_done(id = id) onetime_reset(id = id) options(oo)
oo <- options(onetime.dir = tempdir(check = TRUE)) id <- sample(10000L, 1) onetime_been_done(id = id) onetime_message("Creating an ID", id = id) onetime_been_done(id = id) onetime_reset(id = id) options(oo)
By default lockfiles are stored beneath the onetime base directory,
in a directory named after the calling package. You can use a different
subdirectory by setting path = onetime_dir("dirname")
in calls to
onetime functions.
onetime_dir(dir)
onetime_dir(dir)
dir |
String. Name of a single directory. |
onetime_dir()
does not autocreate the directory (but it will get created
during the call to onetime_do()
).
The path.
onetime_dir("my-folder") oo <- options(onetime.dir = tempdir(check = TRUE)) onetime_dir("my-folder") options(oo)
onetime_dir("my-folder") oo <- options(onetime.dir = tempdir(check = TRUE)) onetime_dir("my-folder") options(oo)
When first called, onetime_do()
evaluates an expression. It then creates a
lockfile recording a unique ID which will prevent the expression being run
on subsequent calls.
onetime_do( expr, id = deprecate_calling_package(), path = default_lockfile_dir(), expiry = NULL, default = NULL, without_permission = c("warn", "run", "stop", "pass", "ask") )
onetime_do( expr, id = deprecate_calling_package(), path = default_lockfile_dir(), expiry = NULL, default = NULL, without_permission = c("warn", "run", "stop", "pass", "ask") )
expr |
The code to evaluate. An R statement or |
id |
Unique ID string. If this is unset, the name of the calling
package will be used. Since onetime 0.2.0, not setting |
path |
Directory to store lockfiles. The default uses a unique
directory corresponding to the calling package, beneath
|
expiry |
|
default |
Value to return if |
without_permission |
Character string. What to do if the user hasn't
given permission to store files? |
onetime_do()
is the engine used by other onetime functions.
Calls are identified by id
. If you use the same value of id
across
different calls to onetime functions, only the first call will get made.
The default path
, where lockfiles are stored, is in a per-package directory
beneath rappdirs::user_config_dir()
. To use a different subdirectory within
the onetime base directory, set path = onetime_dir("dirname")
.
End users can also set options(onetime.dir)
to change the base directory.
Package authors should only set this option locally within package functions,
if at all.
If the call gives an error, the lockfile is still written.
expiry
is backward-looking. That is, expiry
is used at check time to see
if the lockfile was written after Sys.time() - expiry
. It is not used when
the lockfile is created. So, you should set expiry
to the same value
whenever you call onetime_do()
. See the example.
onetime_do()
invisibly returns the value of expr
,
or default
if expr
was not run because it had been run already.
oo <- options(onetime.dir = tempdir(check = TRUE)) id <- sample(10000L, 1L) for (n in 1:3) { onetime_do(print("printed once"), id = id) } # expiry is "backward-looking": id2 <- sample(10000L, 1L) expiry <- as.difftime(1, units = "secs") onetime_do(print("Expires quickly, right?"), id = id2, expiry = expiry) Sys.sleep(2) onetime_do(print("This won't be shown..."), id = id2) onetime_do(print("... but this will"), id = id2, expiry = expiry) onetime_reset(id = id) onetime_reset(id = id2) options(oo)
oo <- options(onetime.dir = tempdir(check = TRUE)) id <- sample(10000L, 1L) for (n in 1:3) { onetime_do(print("printed once"), id = id) } # expiry is "backward-looking": id2 <- sample(10000L, 1L) expiry <- as.difftime(1, units = "secs") onetime_do(print("Expires quickly, right?"), id = id2, expiry = expiry) Sys.sleep(2) onetime_do(print("This won't be shown..."), id = id2) onetime_do(print("... but this will"), id = id2, expiry = expiry) onetime_reset(id = id) onetime_reset(id = id2) options(oo)
This manually marks an action as done.
onetime_mark_as_done( id = deprecate_calling_package(), path = default_lockfile_dir() )
onetime_mark_as_done( id = deprecate_calling_package(), path = default_lockfile_dir() )
id |
Unique ID string. If this is unset, the name of the calling
package will be used. Since onetime 0.2.0, not setting |
path |
Directory to store lockfiles. The default uses a unique
directory corresponding to the calling package, beneath
|
Note that no expiry
parameter is available, because expiry
is
backward-looking. See onetime_do()
for more information.
Marking an action done requires permission to store files on the user's computer, just like other onetime actions.
Invisible TRUE
if the action represented
by id
had not been done before, and has now been explicitly marked as done.
Invisible FALSE
if it was already marked as done (and still is).
oo <- options(onetime.dir = tempdir(check = TRUE)) id <- sample(10000L, 1) onetime_mark_as_done(id = id) onetime_message("Won't be shown", id = id) onetime_reset(id = id) options(oo)
oo <- options(onetime.dir = tempdir(check = TRUE)) id <- sample(10000L, 1) onetime_mark_as_done(id = id) onetime_message("Won't be shown", id = id) onetime_reset(id = id) options(oo)
This uses readline()
to ask the user if the message should
be shown again in future.
onetime_message_confirm( ..., id = deprecate_calling_package(), path = default_lockfile_dir(), expiry = NULL, confirm_prompt = "Show this message again? [yN] ", confirm_answers = c("N", "n", "No", "no"), default_answer = "N", require_permission = FALSE, without_permission = "warn", noninteractive = paste0("To hide this message in future, run:\n", " onetime::onetime_mark_as_done(id = \"", id, "\")"), message = .Deprecated() )
onetime_message_confirm( ..., id = deprecate_calling_package(), path = default_lockfile_dir(), expiry = NULL, confirm_prompt = "Show this message again? [yN] ", confirm_answers = c("N", "n", "No", "no"), default_answer = "N", require_permission = FALSE, without_permission = "warn", noninteractive = paste0("To hide this message in future, run:\n", " onetime::onetime_mark_as_done(id = \"", id, "\")"), message = .Deprecated() )
... |
Passed to |
id |
Unique ID string. If this is unset, the name of the calling
package will be used. Since onetime 0.2.0, not setting |
path |
Directory to store lockfiles. The default uses a unique
directory corresponding to the calling package, beneath
|
expiry |
|
confirm_prompt |
Character string. Question to prompt the user to hide the message in future. |
confirm_answers |
Character vector. Answers which will cause the message to be hidden in future. |
default_answer |
Character string. Default answer if user simply presses return. |
require_permission |
Logical. Ask permission to store files on the user's
computer, if this hasn't been granted? Setting this to |
without_permission |
Character string. What to do if the user hasn't
given permission to store files? |
noninteractive |
String. Additional message to send in non-interactive
sessions. Set to |
message |
Deprecated. Use unnamed arguments |
By default, the message will be hidden if the user answers "n", "No", or "N", or just presses return to the prompt question.
Unlike other onetime
functions, onetime_message_confirm()
doesn't by
default require permission to store files on the user's computer. The
assumption is that saying "Don't show this message again" counts as
granting permission (just for this one message). You can ask for broader
permission by setting require_permission = TRUE
and
without_permission = "ask"
.
NULL
if the message was not shown (shown already or non-interactive
session and noninteractive
was NULL
).
TRUE
if the user confirmed, i.e. chose to hide the message.
FALSE
if the message was shown but the user did not confirm (did not
choose to hide the message, or non-interactive session and noninteractive
was not NULL
).
Results are returned invisibly.
Note that by default, TRUE
is returned when the user answers "no" to
"Show this message again?" and FALSE
is returned when the user answers
"yes".
oo <- options(onetime.dir = tempdir(check = TRUE)) id <- sample(10000L, 1L) onetime_message_confirm("A message to show one or more times", id = id) onetime_reset(id = id) options(oo)
oo <- options(onetime.dir = tempdir(check = TRUE)) id <- sample(10000L, 1L) onetime_message_confirm("A message to show one or more times", id = id) onetime_reset(id = id) options(oo)
This takes a function and returns the same function wrapped by onetime_do()
.
Use it for code which should run only once, but which may be called from
multiple locations. This frees you from having to use the same id
multiple
times.
onetime_only( .f, id = deprecate_calling_package(), path = default_lockfile_dir(), default = NULL, without_permission = "warn" )
onetime_only( .f, id = deprecate_calling_package(), path = default_lockfile_dir(), default = NULL, without_permission = "warn" )
.f |
A function |
id |
Unique ID string. If this is unset, the name of the calling
package will be used. Since onetime 0.2.0, not setting |
path |
Directory to store lockfiles. The default uses a unique
directory corresponding to the calling package, beneath
|
default |
Value to return from |
without_permission |
Character string. What to do if the user hasn't
given permission to store files? |
A wrapped function. The function itself returns the result of .f
,
or default
if the inner function was not called.
oo <- options(onetime.dir = tempdir(check = TRUE)) id <- sample(10000L, 1) sample_once <- onetime_only(sample, id = id) sample_once(1:10) sample_once(1:10) onetime_reset(id) options(oo)
oo <- options(onetime.dir = tempdir(check = TRUE)) id <- sample(10000L, 1) sample_once <- onetime_only(sample, id = id) sample_once(1:10) sample_once(1:10) onetime_reset(id) options(oo)
Reset a onetime call by ID
onetime_reset(id = deprecate_calling_package(), path = default_lockfile_dir())
onetime_reset(id = deprecate_calling_package(), path = default_lockfile_dir())
id |
Unique ID string. If this is unset, the name of the calling
package will be used. Since onetime 0.2.0, not setting |
path |
Directory to store lockfiles. The default uses a unique
directory corresponding to the calling package, beneath
|
The result of file.remove()
, invisibly.
oo <- options(onetime.dir = tempdir(check = TRUE)) id <- sample(10000L, 1) onetime_message("will be shown", id = id) onetime_message("won't be shown", id = id) onetime_reset(id = id) onetime_message("will be shown", id = id) onetime_reset(id = id) options(oo)
oo <- options(onetime.dir = tempdir(check = TRUE)) id <- sample(10000L, 1) onetime_message("will be shown", id = id) onetime_message("won't be shown", id = id) onetime_reset(id = id) onetime_message("will be shown", id = id) onetime_reset(id = id) options(oo)
These functions use onetime_do()
to print a warning or message just
once.
onetime_warning( ..., id = deprecate_calling_package(), path = default_lockfile_dir(), expiry = NULL, without_permission = "warn" ) onetime_message( ..., id = deprecate_calling_package(), path = default_lockfile_dir(), expiry = NULL, without_permission = "warn" ) onetime_startup_message( ..., id = deprecate_calling_package(), path = default_lockfile_dir(), expiry = NULL, without_permission = "warn" )
onetime_warning( ..., id = deprecate_calling_package(), path = default_lockfile_dir(), expiry = NULL, without_permission = "warn" ) onetime_message( ..., id = deprecate_calling_package(), path = default_lockfile_dir(), expiry = NULL, without_permission = "warn" ) onetime_startup_message( ..., id = deprecate_calling_package(), path = default_lockfile_dir(), expiry = NULL, without_permission = "warn" )
... |
Passed to |
id |
Unique ID string. If this is unset, the name of the calling
package will be used. Since onetime 0.2.0, not setting |
path |
Directory to store lockfiles. The default uses a unique
directory corresponding to the calling package, beneath
|
expiry |
|
without_permission |
Character string. What to do if the user hasn't
given permission to store files? |
Invisible TRUE
if the message/warning was shown, invisible
FALSE
otherwise.
oo <- options(onetime.dir = tempdir(check = TRUE)) id <- sample(10000L, 1) for (n in 1:3) { onetime_warning("will be shown once", id = id) } onetime_reset(id = id) options(oo)
oo <- options(onetime.dir = tempdir(check = TRUE)) id <- sample(10000L, 1) for (n in 1:3) { onetime_warning("will be shown once", id = id) } onetime_reset(id = id) options(oo)
rlang
functionsIf you use these you will need to add "rlang"
to your package dependencies.
onetime_rlang_warn( ..., id = deprecate_calling_package(), path = default_lockfile_dir(), expiry = NULL, without_permission = "warn" ) onetime_rlang_inform( ..., id = deprecate_calling_package(), path = default_lockfile_dir(), expiry = NULL, without_permission = "warn" )
onetime_rlang_warn( ..., id = deprecate_calling_package(), path = default_lockfile_dir(), expiry = NULL, without_permission = "warn" ) onetime_rlang_inform( ..., id = deprecate_calling_package(), path = default_lockfile_dir(), expiry = NULL, without_permission = "warn" )
... |
Passed to |
id |
Unique ID string. If this is unset, the name of the calling
package will be used. Since onetime 0.2.0, not setting |
path |
Directory to store lockfiles. The default uses a unique
directory corresponding to the calling package, beneath
|
expiry |
|
without_permission |
Character string. What to do if the user hasn't
given permission to store files? |
Invisibly: TRUE
if the message/warning was shown, FALSE
otherwise.
oo <- options(onetime.dir = tempdir(check = TRUE)) id <- sample(10000L, 1) for (n in 1:3) { onetime_rlang_warn(c("rlang-style warning", i = "Extra info"), id = id) } onetime_reset(id = id) options(oo)
oo <- options(onetime.dir = tempdir(check = TRUE)) id <- sample(10000L, 1) for (n in 1:3) { onetime_rlang_warn(c("rlang-style warning", i = "Extra info"), id = id) } onetime_reset(id = id) options(oo)
End users may use this from the command line. Package authors should only
call it if they cannot ask for permission interactively using
check_ok_to_store(ask = TRUE)
.
set_ok_to_store(ok = TRUE)
set_ok_to_store(ok = TRUE)
ok |
|
Invisible NULL
.
## Not run: set_ok_to_store() ## End(Not run)
## Not run: set_ok_to_store() ## End(Not run)