├── .Rbuildignore ├── .editorconfig ├── .gitattributes ├── .github ├── .gitignore └── workflows │ ├── R-CMD-check.yaml │ └── test-coverage.yaml ├── DESCRIPTION ├── LICENSE ├── LICENSE.md ├── NAMESPACE ├── NEWS.md ├── R ├── extract.R ├── global_roclet.R ├── options.R ├── roxyglobals-package.R ├── use_roxyglobals.R └── utils.R ├── README.md ├── codecov.yml ├── cran-comments.md ├── man ├── global_roclet.Rd ├── options.Rd ├── roxyglobals-package.Rd └── use_roxyglobals.Rd └── tests ├── testthat.R └── testthat ├── config-filename ├── DESCRIPTION └── R │ ├── auto.R │ ├── empty.R │ └── manual.R ├── config-unique ├── DESCRIPTION └── R │ ├── auto.R │ ├── empty.R │ └── manual.R ├── empty └── DESCRIPTION ├── has-roclets └── DESCRIPTION ├── helper-local.R ├── test-extract.R ├── test-global_roclet.R ├── test-options.R └── test-use.R /.Rbuildignore: -------------------------------------------------------------------------------- 1 | # dot-files 2 | ^\..*$ 3 | ^codecov\.yml$ 4 | ^LICENSE\.md$ 5 | ^cran-comments\.md$ 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_style = space 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | 10 | [*.R] 11 | indent_size = 2 12 | 13 | [.travis.yml] 14 | indent_size = 2 15 | 16 | [*.md] 17 | trim_trailing_whitespace = false 18 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | 3 | man/* linguist-generated 4 | -------------------------------------------------------------------------------- /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | 9 | name: R-CMD-check 10 | 11 | jobs: 12 | R-CMD-check: 13 | runs-on: ${{ matrix.config.os }} 14 | 15 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 16 | 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | config: 21 | - {os: macos-latest, r: 'release'} 22 | - {os: windows-latest, r: 'release'} 23 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} 24 | - {os: ubuntu-latest, r: 'release'} 25 | - {os: ubuntu-latest, r: 'oldrel-1'} 26 | 27 | env: 28 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 29 | R_KEEP_PKG_SOURCE: yes 30 | 31 | steps: 32 | - uses: actions/checkout@v3 33 | 34 | - uses: r-lib/actions/setup-pandoc@v2 35 | 36 | - uses: r-lib/actions/setup-r@v2 37 | with: 38 | r-version: ${{ matrix.config.r }} 39 | http-user-agent: ${{ matrix.config.http-user-agent }} 40 | use-public-rspm: true 41 | 42 | - uses: r-lib/actions/setup-r-dependencies@v2 43 | with: 44 | extra-packages: any::rcmdcheck 45 | needs: check 46 | 47 | - uses: r-lib/actions/check-r-package@v2 48 | with: 49 | upload-snapshots: true 50 | -------------------------------------------------------------------------------- /.github/workflows/test-coverage.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | 9 | name: test-coverage 10 | 11 | jobs: 12 | test-coverage: 13 | runs-on: ubuntu-latest 14 | env: 15 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 16 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 17 | 18 | steps: 19 | - uses: actions/checkout@v3 20 | 21 | - uses: r-lib/actions/setup-r@v2 22 | with: 23 | use-public-rspm: true 24 | 25 | - uses: r-lib/actions/setup-r-dependencies@v2 26 | with: 27 | extra-packages: any::covr 28 | needs: coverage 29 | 30 | - name: Test coverage 31 | run: | 32 | covr::codecov( 33 | quiet = FALSE, 34 | clean = FALSE, 35 | install_path = file.path(Sys.getenv("RUNNER_TEMP"), "package") 36 | ) 37 | shell: Rscript {0} 38 | 39 | - name: Show testthat output 40 | if: always() 41 | run: | 42 | ## -------------------------------------------------------------------- 43 | find ${{ runner.temp }}/package -name 'testthat.Rout*' -exec cat '{}' \; || true 44 | shell: bash 45 | 46 | - name: Upload test results 47 | if: failure() 48 | uses: actions/upload-artifact@v3 49 | with: 50 | name: coverage-test-failures 51 | path: ${{ runner.temp }}/package 52 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: roxyglobals 2 | Title: 'Roxygen2' Global Variable Declarations 3 | Version: 1.0.0 4 | Authors@R: c( 5 | person("Anthony", "North", , "anthony.jl.north@gmail.com", role = c("aut", "cre", "cph")), 6 | person("Miles", "McBain", , "miles.mcbain@gmail.com", role = "ctb", 7 | comment = c(ORCID = "0000-0003-2865-2548")) 8 | ) 9 | Description: Generate utils::globalVariables() from 'roxygen2' @global and @autoglobal tags. 10 | License: MIT + file LICENSE 11 | URL: https://github.com/anthonynorth/roxyglobals 12 | BugReports: https://github.com/anthonynorth/roxyglobals/issues 13 | Imports: 14 | brio, 15 | codetools, 16 | desc, 17 | roxygen2 18 | Suggests: 19 | covr, 20 | testthat (>= 3.0.0), 21 | withr 22 | Config/testthat/edition: 3 23 | Encoding: UTF-8 24 | Roxygen: list(markdown = TRUE) 25 | RoxygenNote: 7.2.3 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2023 2 | COPYRIGHT HOLDER: Anthony North 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2023 Anthony North 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method(roclet_clean,roclet_global) 4 | S3method(roclet_output,roclet_global) 5 | S3method(roclet_process,roclet_global) 6 | S3method(roxy_tag_parse,roxy_tag_autoglobal) 7 | S3method(roxy_tag_parse,roxy_tag_global) 8 | export(global_roclet) 9 | export(options_get_filename) 10 | export(options_get_unique) 11 | export(options_set_filename) 12 | export(options_set_unique) 13 | export(use_roxyglobals) 14 | importFrom(roxygen2,roclet) 15 | importFrom(roxygen2,roclet_clean) 16 | importFrom(roxygen2,roclet_output) 17 | importFrom(roxygen2,roclet_process) 18 | importFrom(roxygen2,roxy_tag_parse) 19 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # roxyglobals 1.0.0 2 | 3 | - configurable globals filename (#10) 4 | - preserve existing roclets (#9) 5 | - (optionally) write unique globals (#6) 6 | 7 | # roxyglobals 0.2.2 8 | 9 | - @autoglobal ignore assignment expressions (#7) 10 | - @autoglobal search package imports for defined globals (#8) 11 | 12 | # roxyglobals 0.2.1 13 | 14 | - @autoglobal matching names in parent environments 15 | - indicate source for each global (#3) 16 | 17 | # roxyglobals 0.2.0 18 | 19 | - Add @autoglobal roclet (#1) 20 | -------------------------------------------------------------------------------- /R/extract.R: -------------------------------------------------------------------------------- 1 | # extract globals 2 | extract_globals <- function(fn) { 3 | env <- environment() 4 | 5 | codetools::collectUsage( 6 | fn, 7 | enterGlobal = function(type, name, expr, walker) { 8 | if (type == "function") env$last_call <- expr 9 | 10 | if (!is_defined(name, walker$globalenv) && !is_assign(env$last_call) && 11 | (type == "variable" || type == "function" && name == ":=")) { 12 | env$globals <- c(env$globals, name) 13 | } 14 | }, 15 | enterLocal = function(type, name, expr, walker) { 16 | if (type == "function") env$last_call <- expr 17 | } 18 | ) 19 | 20 | unique(env$globals) 21 | } 22 | 23 | # is expr an assignment call? 24 | is_assign <- function(expr) { 25 | is.call(expr) && as.character(expr[[1]]) %in% c("<-", "<<-", "=", "assign") 26 | } 27 | 28 | # is name defined in package env? 29 | is_defined <- function(name, env, inherit = FALSE) { 30 | exists(name, envir = env, inherits = inherit) || 31 | exists(name, envir = parent.env(env), inherits = inherit) || 32 | exists(name, envir = baseenv(), inherits = inherit) 33 | } 34 | -------------------------------------------------------------------------------- /R/global_roclet.R: -------------------------------------------------------------------------------- 1 | #' Roclet: global 2 | #' 3 | #' @description 4 | #' This roclet automates [utils::globalVariables()] declaration from @global 5 | #' and @autoglobal roxygen tags. 6 | #' 7 | #' Package authors will not typically need to invoke [global_roclet()] directly. 8 | #' Global roclet instances are created by `roxygen2` during [roxygen2::roxygenise()] 9 | #' (or [devtools::document()]). 10 | #' 11 | #' @importFrom roxygen2 roclet 12 | #' @return A [roxygen2::roclet()] instance for declaring [utils::globalVariables()] 13 | #' during [roxygen2::roxygenise()] 14 | #' @export 15 | #' 16 | #' @examples 17 | #' #' @autoglobal 18 | #' foo <- function(x) { 19 | #' # bar isn't declared -> add to utils::globalVariables() 20 | #' subset(x, bar == 4) 21 | #' } 22 | #' 23 | #' #' @global bar 24 | #' foo <- function(x) { 25 | #' # bar is explicitly defined as a global -> add to utils::globalVariables() 26 | #' subset(x, bar == 4) 27 | #' } 28 | global_roclet <- function() { 29 | roxygen2::roclet("global") 30 | } 31 | 32 | #' @importFrom roxygen2 roclet_process 33 | #' @export 34 | roclet_process.roclet_global <- function(x, blocks, env, base_path) { 35 | lines <- blocks_to_globals(blocks, options_get_unique(base_path)) 36 | c(generated_by(), "", global_variables(lines)) 37 | } 38 | 39 | #' @importFrom roxygen2 roclet_output 40 | #' @export 41 | roclet_output.roclet_global <- function(x, results, base_path, ...) { 42 | brio::write_lines(results, globals_filename(base_path)) 43 | invisible(NULL) 44 | } 45 | 46 | #' @importFrom roxygen2 roclet_clean 47 | #' @export 48 | roclet_clean.roclet_global <- function(x, base_path) { 49 | unlink(globals_filename(base_path), force = TRUE) 50 | } 51 | 52 | #' @importFrom roxygen2 roxy_tag_parse 53 | #' @export 54 | roxy_tag_parse.roxy_tag_global <- function(x) { 55 | if (x$raw == "") return(roxygen2::roxy_tag_warning(x, "requires a value")) 56 | 57 | roxygen2::tag_words(x, min = 1) 58 | } 59 | 60 | #' @importFrom roxygen2 roxy_tag_parse 61 | #' @export 62 | roxy_tag_parse.roxy_tag_autoglobal <- function(x) roxygen2::tag_toggle(x) 63 | 64 | 65 | blocks_to_globals <- function(blocks, unique) { 66 | globals <- do.call(rbind, lapply(blocks, block_to_globals)) 67 | if (length(globals) == 0) return(NULL) 68 | 69 | fmt_fn <- function(x) paste0("# <", x, ">") 70 | 71 | if (!unique) { 72 | return( 73 | paste0(quote_str(globals$global_name), ", ", fmt_fn(globals$fn_name)) 74 | ) 75 | } 76 | 77 | group_fmt <- function(x) { 78 | paste0(c( 79 | fmt_fn(x$fn_name), 80 | paste0(quote_str(x$global_name[1]), ",") 81 | )) 82 | } 83 | 84 | groups <- split(globals, globals$global_name) 85 | unlist(lapply(groups, group_fmt)) 86 | } 87 | 88 | block_to_globals <- function(block) { 89 | object <- block$object$value 90 | name <- block$object$alias %??% 91 | first(block_get_tag_values(block, c("name", "rdname"))) 92 | 93 | # @global 94 | explicit_globals <- block_get_tag_values(block, "global") 95 | 96 | # @autoglobal 97 | auto_globals <- if (roxygen2::block_has_tags(block, "autoglobal") && is.function(object)) { 98 | extract_globals(object) 99 | } 100 | 101 | globals <- unique(c(explicit_globals, auto_globals)) 102 | if (!is.null(name) && length(globals) != 0) 103 | data.frame(fn_name = rep_len(name, length(globals)), global_name = globals) 104 | } 105 | 106 | block_get_tag_values <- function(block, tags) { 107 | block_tags <- roxygen2::block_get_tags(block, tags) 108 | unlist(lapply(block_tags, function(tag) tag$val)) 109 | } 110 | 111 | generated_by <- function() { 112 | paste0("# Generated by ", utils::packageName(), ": do not edit by hand") 113 | } 114 | 115 | globals_filename <- function(base_path) { 116 | file.path(base_path, "R", options_get_filename(base_path)) 117 | } 118 | -------------------------------------------------------------------------------- /R/options.R: -------------------------------------------------------------------------------- 1 | #' Roxyglobals options 2 | #' 3 | #' @description 4 | #' Get and set roxyglobals options in DESCRIPTION file. 5 | #' 6 | #' @name options 7 | #' @inheritParams desc::desc_get_field 8 | #' @param value The new option value 9 | #' @return The option value or nothing 10 | #' @keywords internal 11 | NULL 12 | 13 | 14 | #' @export 15 | #' @examples 16 | #' options_get_unique() 17 | #' @describeIn options get unique 18 | options_get_unique <- function(file = ".") { 19 | default <- FALSE 20 | if (!in_pkg(file)) { 21 | return(default) 22 | } 23 | 24 | text <- desc::desc_get_field( 25 | options_key("unique"), 26 | default, 27 | file = file 28 | ) 29 | 30 | eval(parse(text = text)) 31 | } 32 | 33 | #' @export 34 | #' @describeIn options set unique 35 | #' @examples 36 | #' \dontrun{ 37 | #' options_set_unique(TRUE) 38 | #' } 39 | options_set_unique <- function(value, file = ".") { 40 | assert_in_pkg(file) 41 | 42 | desc::desc_set( 43 | options_key("unique"), 44 | isTRUE(value), 45 | file = file 46 | ) 47 | 48 | invisible() 49 | } 50 | 51 | #' @export 52 | #' @describeIn options get filename 53 | #' @examples 54 | #' options_get_filename() 55 | options_get_filename <- function(file = ".") { 56 | default <- "globals.R" 57 | if (!in_pkg(file)) { 58 | return(default) 59 | } 60 | 61 | desc::desc_get_field( 62 | options_key("filename"), 63 | default, 64 | file = file 65 | ) 66 | } 67 | 68 | #' @export 69 | #' @describeIn options set filename 70 | #' @examples 71 | #' \dontrun{ 72 | #' options_set_filename("roxyglobals-generated.R") 73 | #' } 74 | options_set_filename <- function(value, file = ".") { 75 | stopifnot(is_r_file(value)) 76 | assert_in_pkg(file) 77 | 78 | desc::desc_set( 79 | options_key("filename"), 80 | basename(value[1]), 81 | file = file 82 | ) 83 | 84 | invisible() 85 | } 86 | 87 | options_get_roxygen <- function(file = ".") { 88 | assert_in_pkg(file) 89 | 90 | desc_text <- desc::desc_get_field( 91 | "Roxygen", 92 | default = list(), 93 | file = file 94 | ) 95 | desc_opts <- eval(parse(text = desc_text)) 96 | all_opts <- roxygen2::load_options(file) 97 | 98 | # any options present in description + roclets 99 | keys <- unique(c(names(desc_opts), "roclets")) 100 | all_opts[keys] 101 | } 102 | 103 | options_set_roxygen <- function(options, file = ".") { 104 | assert_in_pkg(file) 105 | 106 | text <- paste_line( 107 | strwrap( 108 | deparse(options, 500L), 109 | width = 70L, 110 | exdent = 4L 111 | ) 112 | ) 113 | 114 | desc::desc_set( 115 | "Roxygen", 116 | text, 117 | file = file 118 | ) 119 | } 120 | 121 | 122 | options_key <- function(name) { 123 | paste0( 124 | c("Config", utils::packageName(), name), 125 | collapse = "/" 126 | ) 127 | } 128 | 129 | in_pkg <- function(file = ".") { 130 | tryCatch( 131 | !is.null(desc::desc(file = file)), 132 | error = function(e) FALSE 133 | ) 134 | } 135 | 136 | assert_in_pkg <- function(file) { 137 | if (!in_pkg(file)) { 138 | stop(paste0(tools::file_path_as_absolute("."), " is not inside a package.")) 139 | } 140 | 141 | invisible() 142 | } 143 | -------------------------------------------------------------------------------- /R/roxyglobals-package.R: -------------------------------------------------------------------------------- 1 | #' @keywords internal 2 | "_PACKAGE" 3 | 4 | # The following block is used by usethis to automatically manage 5 | # roxygen namespace tags. Modify with care! 6 | ## usethis namespace: start 7 | ## usethis namespace: end 8 | NULL 9 | -------------------------------------------------------------------------------- /R/use_roxyglobals.R: -------------------------------------------------------------------------------- 1 | #' Use roxyglobals 2 | #' 3 | #' Configures roxygen to use [global_roclet()], adds roxyglobals to Suggests 4 | #' @return nothing 5 | #' @export 6 | #' 7 | #' @examples 8 | #' \dontrun{ 9 | #' use_roxyglobals() 10 | #' } 11 | use_roxyglobals <- function() { 12 | assert_in_pkg(".") 13 | 14 | # add dependency 15 | desc::desc_set_dep(utils::packageName(), type = "Suggests") 16 | 17 | # current roxygen options 18 | options <- options_get_roxygen() 19 | 20 | # add roclet 21 | options$roclets <- unique(c( 22 | options$roclets, 23 | # could use a string, but this should be refactor proof 24 | paste0(utils::packageName(), "::", substitute(global_roclet)) 25 | )) 26 | 27 | # use global_roclet 28 | options_set_roxygen(options) 29 | 30 | # ensure roxyglobals options 31 | options_set_filename(options_get_filename()) 32 | options_set_unique(options_get_unique()) 33 | 34 | invisible() 35 | } 36 | -------------------------------------------------------------------------------- /R/utils.R: -------------------------------------------------------------------------------- 1 | `%??%` <- function(a, b) if (length(a) == 0) b else a 2 | first <- function(x) x[[1]] 3 | skip <- function(x, n) x[-seq_len(n)] 4 | 5 | quote_str <- function(x, q = "\"") paste0(q, x, q) 6 | paste_line <- function(...) paste0(c(...), collapse = "\n") 7 | indent <- function(..., size = 2) paste0(strrep(" ", size), c(...)) 8 | 9 | global_variables <- function(...) { 10 | c( 11 | "utils::globalVariables(c(", 12 | indent(..., deparse(NULL)), 13 | "))" 14 | ) 15 | } 16 | 17 | is_r_file <- function(filename) { 18 | ext <- toupper(tools::file_ext(trimws(filename))) 19 | ext == "R" %??% FALSE 20 | } 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # roxyglobals 2 | 3 | 4 | [![R-CMD-check](https://github.com/anthonynorth/roxyglobals/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/anthonynorth/roxyglobals/actions/workflows/R-CMD-check.yaml) 5 | [![Codecov test coverage](https://codecov.io/gh/anthonynorth/roxyglobals/branch/master/graph/badge.svg)](https://app.codecov.io/gh/anthonynorth/roxyglobals?branch=master) 6 | [![CRAN](https://www.r-pkg.org/badges/version/roxyglobals)](https://CRAN.R-project.org/package=roxyglobals) 7 | [![r-universe](https://anthonynorth.r-universe.dev/badges/roxyglobals)](https://anthonynorth.r-universe.dev/roxyglobals) 8 | [![latest](https://img.shields.io/github/r-package/v/anthonynorth/roxyglobals?label=latest&logo=r)](https://github.com/anthonynorth/roxyglobals) 9 | 10 | 11 | Generate `utils::globalVariables()` from roxygen @autoglobal and @global tags. 12 | 13 | ## Installation 14 | 15 | ```r 16 | # Install the released version from CRAN 17 | install.packages("roxyglobals") 18 | 19 | # Install the released version from r-universe 20 | install.packages("roxyglobals", repos = "https://anthonynorth.r-universe.dev") 21 | 22 | # Or the development version from GitHub: 23 | # install.packages("remotes") 24 | remotes::install_github("anthonynorth/roxyglobals") 25 | ``` 26 | 27 | ## Setup 28 | 29 | Add roxyglobals to an R package DESCRIPTION via: 30 | 31 | ```r 32 | # Add to current R package 33 | roxyglobals::use_roxyglobals() 34 | 35 | # Or add to another R package 36 | # install.packages("withr") 37 | withr::with_dir("path/to/package", roxyglobals::use_roxyglobals()) 38 | ``` 39 | 40 | ## Config 41 | 42 | By default, roxyglobals writes all discovered globals (including duplicates) to `R/globals.R`. You may configure the filename and global generation behaviour with: 43 | 44 | ```r 45 | # write globals to R/generated-globals.R 46 | roxyglobals::options_set_filename("generated-globals.R") 47 | 48 | # only emit unique globals 49 | roxyglobals::options_set_unique(TRUE) 50 | ``` 51 | 52 | ## Usage 53 | 54 | Add @autoglobal to a function roxygen comment block, example: 55 | 56 | ```r 57 | #' Summarise responses 58 | #' 59 | #' @name summarise_responses 60 | #' @param responses a data.frame of responses 61 | #' 62 | #' @autoglobal 63 | #' @export 64 | summarise_responses <- function(responses) { 65 | # station_name, station_type, end_time, start_time need to be added to 66 | # utils::globalVariables() to keep R CMD CHECK happy 67 | responses |> 68 | dplyr::group_by(station_name, station_type) |> 69 | dplyr::summarise( 70 | count_responses = dplyr::n(), 71 | total_hours = sum( 72 | as.numeric(end_time - start_time, units = "hours"), 73 | na.rm = TRUE 74 | ), 75 | .groups = "drop" 76 | ) 77 | } 78 | ``` 79 | 80 | Or @global, example: 81 | 82 | ```r 83 | #' Summarise responses 84 | #' 85 | #' @name summarise_responses 86 | #' @param responses a data.frame of responses 87 | #' 88 | #' @global station_name station_type end_time start_time 89 | #' @export 90 | summarise_responses <- function(responses) { 91 | # station_name, station_type, end_time, start_time need to be added to 92 | # utils::globalVariables() to keep R CMD CHECK happy 93 | responses |> 94 | dplyr::group_by(station_name, station_type) |> 95 | dplyr::summarise( 96 | count_responses = dplyr::n(), 97 | total_hours = sum( 98 | as.numeric(end_time - start_time, units = "hours"), 99 | na.rm = TRUE 100 | ), 101 | .groups = "drop" 102 | ) 103 | } 104 | ``` 105 | 106 | Run `devtools::document()` to generate `utils::globalVariables()` in your globals file (default `R/globals.R`). Example globals file: 107 | 108 | ```r 109 | # Generated by roxyglobals: do not edit by hand 110 | 111 | utils::globalVariables(c( 112 | "end_time", # 113 | "start_time", # 114 | "station_name", # 115 | "station_type", # 116 | NULL 117 | )) 118 | ``` 119 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: false 2 | 3 | coverage: 4 | status: 5 | project: 6 | default: 7 | target: auto 8 | threshold: 1% 9 | informational: true 10 | patch: 11 | default: 12 | target: auto 13 | threshold: 1% 14 | informational: true 15 | -------------------------------------------------------------------------------- /cran-comments.md: -------------------------------------------------------------------------------- 1 | This is a resubmission, adding short documentation to global_roclet() function. 2 | 3 | ## Test environments 4 | 5 | - GitHub Actions (macOS latest): release (4.3.1) 6 | - GitHub Actions (windows latest): release (4.3.1) 7 | - GitHub Actions (ubuntu 22.04): devel (4.4.0) 8 | - GitHub Actions (ubuntu 22.04): release (4.3.1) 9 | - GitHub Actions (ubuntu 22.04): oldrel-1 (4.2.3) 10 | - win-builder: devel (4.4.0) 11 | 12 | ## R CMD check results 13 | 0 errors | 0 warnings | 0 notes 14 | 15 | * This is a new release. 16 | -------------------------------------------------------------------------------- /man/global_roclet.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/global_roclet.R 3 | \name{global_roclet} 4 | \alias{global_roclet} 5 | \title{Roclet: global} 6 | \usage{ 7 | global_roclet() 8 | } 9 | \value{ 10 | A \code{\link[roxygen2:roclet]{roxygen2::roclet()}} instance for declaring \code{\link[utils:globalVariables]{utils::globalVariables()}} 11 | during \code{\link[roxygen2:roxygenize]{roxygen2::roxygenise()}} 12 | } 13 | \description{ 14 | This roclet automates \code{\link[utils:globalVariables]{utils::globalVariables()}} declaration from @global 15 | and @autoglobal roxygen tags. 16 | 17 | Package authors will not typically need to invoke \code{\link[=global_roclet]{global_roclet()}} directly. 18 | Global roclet instances are created by \code{roxygen2} during \code{\link[roxygen2:roxygenize]{roxygen2::roxygenise()}} 19 | (or \code{\link[devtools:document]{devtools::document()}}). 20 | } 21 | \examples{ 22 | #' @autoglobal 23 | foo <- function(x) { 24 | # bar isn't declared -> add to utils::globalVariables() 25 | subset(x, bar == 4) 26 | } 27 | 28 | #' @global bar 29 | foo <- function(x) { 30 | # bar is explicitly defined as a global -> add to utils::globalVariables() 31 | subset(x, bar == 4) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /man/options.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/options.R 3 | \name{options} 4 | \alias{options} 5 | \alias{options_get_unique} 6 | \alias{options_set_unique} 7 | \alias{options_get_filename} 8 | \alias{options_set_filename} 9 | \title{Roxyglobals options} 10 | \usage{ 11 | options_get_unique(file = ".") 12 | 13 | options_set_unique(value, file = ".") 14 | 15 | options_get_filename(file = ".") 16 | 17 | options_set_filename(value, file = ".") 18 | } 19 | \arguments{ 20 | \item{file}{DESCRIPTION file to use. By default the DESCRIPTION 21 | file of the current package (i.e. the package the working directory 22 | is part of) is used.} 23 | 24 | \item{value}{The new option value} 25 | } 26 | \value{ 27 | The option value or nothing 28 | } 29 | \description{ 30 | Get and set roxyglobals options in DESCRIPTION file. 31 | } 32 | \section{Functions}{ 33 | \itemize{ 34 | \item \code{options_get_unique()}: get unique 35 | 36 | \item \code{options_set_unique()}: set unique 37 | 38 | \item \code{options_get_filename()}: get filename 39 | 40 | \item \code{options_set_filename()}: set filename 41 | 42 | }} 43 | \examples{ 44 | options_get_unique() 45 | \dontrun{ 46 | options_set_unique(TRUE) 47 | } 48 | options_get_filename() 49 | \dontrun{ 50 | options_set_filename("roxyglobals-generated.R") 51 | } 52 | } 53 | \keyword{internal} 54 | -------------------------------------------------------------------------------- /man/roxyglobals-package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/roxyglobals-package.R 3 | \docType{package} 4 | \name{roxyglobals-package} 5 | \alias{roxyglobals} 6 | \alias{roxyglobals-package} 7 | \title{roxyglobals: 'Roxygen2' Global Variable Declarations} 8 | \description{ 9 | Generate utils::globalVariables() from 'roxygen2' @global and @autoglobal tags. 10 | } 11 | \seealso{ 12 | Useful links: 13 | \itemize{ 14 | \item \url{https://github.com/anthonynorth/roxyglobals} 15 | \item Report bugs at \url{https://github.com/anthonynorth/roxyglobals/issues} 16 | } 17 | 18 | } 19 | \author{ 20 | \strong{Maintainer}: Anthony North \email{anthony.jl.north@gmail.com} [copyright holder] 21 | 22 | Other contributors: 23 | \itemize{ 24 | \item Miles McBain \email{miles.mcbain@gmail.com} (\href{https://orcid.org/0000-0003-2865-2548}{ORCID}) [contributor] 25 | } 26 | 27 | } 28 | \keyword{internal} 29 | -------------------------------------------------------------------------------- /man/use_roxyglobals.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/use_roxyglobals.R 3 | \name{use_roxyglobals} 4 | \alias{use_roxyglobals} 5 | \title{Use roxyglobals} 6 | \usage{ 7 | use_roxyglobals() 8 | } 9 | \value{ 10 | nothing 11 | } 12 | \description{ 13 | Configures roxygen to use \code{\link[=global_roclet]{global_roclet()}}, adds roxyglobals to Suggests 14 | } 15 | \examples{ 16 | \dontrun{ 17 | use_roxyglobals() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | # This file is part of the standard setup for testthat. 2 | # It is recommended that you do not modify it. 3 | # 4 | # Where should you do additional test configuration? 5 | # Learn more about the roles of various files in: 6 | # * https://r-pkgs.org/tests.html 7 | # * https://testthat.r-lib.org/reference/test_package.html#special-files 8 | 9 | library(testthat) 10 | library(roxyglobals) 11 | 12 | test_check("roxyglobals") 13 | -------------------------------------------------------------------------------- /tests/testthat/config-filename/DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: filename 2 | Title: What the Package Does (One Line, Title Case) 3 | Version: 0.0.0.9000 4 | Authors@R: 5 | person("First", "Last", , "first.last@example.com", role = c("aut", "cre"), 6 | comment = c(ORCID = "YOUR-ORCID-ID")) 7 | Description: What the package does (one paragraph). 8 | License: `use_mit_license()`, `use_gpl3_license()` or friends to pick a 9 | license 10 | Encoding: UTF-8 11 | Roxygen: list(markdown = TRUE, roclets = c("collate", "namespace", "rd", 12 | "roxyglobals::global_roclet")) 13 | RoxygenNote: 7.2.3 14 | Suggests: 15 | roxyglobals 16 | Config/roxyglobals/unique: FALSE 17 | Config/roxyglobals/filename: generated-globals.R 18 | -------------------------------------------------------------------------------- /tests/testthat/config-filename/R/auto.R: -------------------------------------------------------------------------------- 1 | #' @autoglobal 2 | automode <- function(data) { 3 | data |> 4 | dplyr::group_by(my_key) |> 5 | dplyr::summarise(agg = sum(something)) 6 | } 7 | -------------------------------------------------------------------------------- /tests/testthat/config-filename/R/empty.R: -------------------------------------------------------------------------------- 1 | #' @title bequiet 2 | #' @name nofunction 3 | #' @global thisworks 4 | NULL 5 | -------------------------------------------------------------------------------- /tests/testthat/config-filename/R/manual.R: -------------------------------------------------------------------------------- 1 | #' @global this that 2 | #' @global thistoo 3 | manualmode <- function(data) { 4 | data |> 5 | dplyr::mutate(this = 1, that = 2) |> 6 | dplyr::arrange(thistoo) 7 | } 8 | -------------------------------------------------------------------------------- /tests/testthat/config-unique/DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: unique 2 | Title: What the Package Does (One Line, Title Case) 3 | Version: 0.0.0.9000 4 | Authors@R: 5 | person("First", "Last", , "first.last@example.com", role = c("aut", "cre"), 6 | comment = c(ORCID = "YOUR-ORCID-ID")) 7 | Description: What the package does (one paragraph). 8 | License: `use_mit_license()`, `use_gpl3_license()` or friends to pick a 9 | license 10 | Encoding: UTF-8 11 | Roxygen: list(markdown = TRUE, roclets = c("collate", "namespace", "rd", 12 | "roxyglobals::global_roclet")) 13 | RoxygenNote: 7.2.3 14 | Suggests: 15 | roxyglobals 16 | Config/roxyglobals/filename: globals.R 17 | Config/roxyglobals/unique: TRUE 18 | -------------------------------------------------------------------------------- /tests/testthat/config-unique/R/auto.R: -------------------------------------------------------------------------------- 1 | #' @autoglobal 2 | automode <- function(data) { 3 | data |> 4 | dplyr::group_by(my_key) |> 5 | dplyr::summarise(agg = sum(something)) 6 | } 7 | 8 | #' @autoglobal 9 | automode_duplicate <- function(data) { 10 | data |> 11 | dplyr::group_by(my_key) |> 12 | dplyr::summarise(agg = sum(something)) 13 | } 14 | -------------------------------------------------------------------------------- /tests/testthat/config-unique/R/empty.R: -------------------------------------------------------------------------------- 1 | #' @title bequiet 2 | #' @name nofunction 3 | #' @global thisworks 4 | NULL 5 | 6 | #' @title bequiet 7 | #' @name nofunction_duplicate 8 | #' @global thisworks 9 | NULL 10 | -------------------------------------------------------------------------------- /tests/testthat/config-unique/R/manual.R: -------------------------------------------------------------------------------- 1 | #' @global this that 2 | #' @global thistoo 3 | manualmode <- function(data) { 4 | data |> 5 | dplyr::mutate(this = 1, that = 2) |> 6 | dplyr::arrange(thistoo) 7 | } 8 | 9 | #' @global this that 10 | #' @global thistoo 11 | manualmode_duplicate <- function(data) { 12 | data |> 13 | dplyr::mutate(this = 1, that = 2) |> 14 | dplyr::arrange(thistoo) 15 | } 16 | -------------------------------------------------------------------------------- /tests/testthat/empty/DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: empty 2 | Title: What the Package Does (One Line, Title Case) 3 | Version: 0.0.0.9000 4 | Authors@R: 5 | person("First", "Last", , "first.last@example.com", role = c("aut", "cre"), 6 | comment = c(ORCID = "YOUR-ORCID-ID")) 7 | Description: What the package does (one paragraph). 8 | License: `use_mit_license()`, `use_gpl3_license()` or friends to pick a 9 | license 10 | Encoding: UTF-8 11 | RoxygenNote: 7.2.3 12 | -------------------------------------------------------------------------------- /tests/testthat/has-roclets/DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: hasroclets 2 | Title: What the Package Does (One Line, Title Case) 3 | Version: 0.0.0.9000 4 | Authors@R: 5 | person("First", "Last", , "first.last@example.com", role = c("aut", "cre"), 6 | comment = c(ORCID = "YOUR-ORCID-ID")) 7 | Description: What the package does (one paragraph). 8 | License: `use_mit_license()`, `use_gpl3_license()` or friends to pick a 9 | license 10 | Encoding: UTF-8 11 | Roxygen: list(markdown = TRUE, roclets = c("collate", "namespace", "rd", 12 | "otherpkg::other_roclet")) 13 | RoxygenNote: 7.2.3 14 | -------------------------------------------------------------------------------- /tests/testthat/helper-local.R: -------------------------------------------------------------------------------- 1 | local_package_copy <- function(pkg_dir, env = parent.frame()) { 2 | temp_dir <- withr::local_tempdir(.local_envir = env) 3 | file.copy(pkg_dir, temp_dir, recursive = TRUE) 4 | file.path(temp_dir, basename(pkg_dir)) 5 | } 6 | -------------------------------------------------------------------------------- /tests/testthat/test-extract.R: -------------------------------------------------------------------------------- 1 | test_that("extract_globals() works", { 2 | # shim dplyr 3 | mutate <- function(...) NULL 4 | 5 | fn <- function(foo) { 6 | local_fn <- function(local_param) { 7 | local_local_fn <- function(local_local_param) { 8 | my_local_local_var <- local_local_var_does_not_exist 9 | mutate(foo, new_name = local_local_param, capture_this_local_local) 10 | } 11 | 12 | my_local_var <- local_var_does_not_exist 13 | 14 | mutate(foo, capture_this_local) 15 | } 16 | 17 | mutate(foo, my_alias = capture_this * 5) 18 | } 19 | 20 | expect_identical( 21 | extract_globals(fn), 22 | c("capture_this_local_local", "capture_this_local", "capture_this") 23 | ) 24 | }) 25 | 26 | test_that("extract_globals() works with tidy rename", { 27 | # shim dplyr 28 | mutate <- function(...) NULL 29 | 30 | fn <- function(foo, name) { 31 | mutate(foo, {{ name }} := capture_this) 32 | } 33 | 34 | expect_identical( 35 | extract_globals(fn), 36 | c(":=", "capture_this") 37 | ) 38 | }) 39 | 40 | test_that("extract_globals() ignores assignments", { 41 | # shim dplyr 42 | mutate <- function(...) NULL 43 | 44 | fn <- function(foo) { 45 | my_var <- var_not_exist 46 | 47 | local_fn <- function(local_param) { 48 | my_local_var <- another_var_does_not_exist 49 | } 50 | 51 | mutate(foo, capture_this) 52 | } 53 | 54 | expect_identical( 55 | extract_globals(fn), 56 | "capture_this" 57 | ) 58 | }) 59 | 60 | test_that("extract_globals() ignores calls", { 61 | # shim dplyr 62 | mutate <- function(...) NULL 63 | 64 | fn <- function(foo) { 65 | my_var <- fn_not_exist() 66 | fn_not_exist2() 67 | 68 | local_fn <- function(local_param) { 69 | my_local_var <- fn_not_exist3() 70 | fn_not_exist4() 71 | } 72 | 73 | mutate(foo, capture_this) 74 | } 75 | 76 | expect_identical( 77 | extract_globals(fn), 78 | "capture_this" 79 | ) 80 | }) 81 | 82 | test_that("extract_globals() ignores defined globals", { 83 | # shim dplyr 84 | mutate <- function(...) NULL 85 | 86 | fn <- function(foo) { 87 | # all defined in ancestor environments 88 | mutate(foo, names, fn2, some_var) 89 | # not defined 90 | mutate(foo, capture_this) 91 | } 92 | 93 | fn2 <- function() NULL 94 | some_var <- 1 95 | 96 | expect_identical( 97 | extract_globals(fn), 98 | "capture_this" 99 | ) 100 | }) 101 | -------------------------------------------------------------------------------- /tests/testthat/test-global_roclet.R: -------------------------------------------------------------------------------- 1 | test_that("global_roclet() works", { 2 | roclet_out <- roxygen2::roc_proc_text( 3 | global_roclet(), 4 | paste_line( 5 | "#' @global foo bar", 6 | "fn1 <- function() {}", 7 | "#' @autoglobal", 8 | "fn2 <- function(x) { dplyr::select(x, foobar) }", 9 | "#' @name fn3", 10 | "#' @global foofy", 11 | "NULL" 12 | ) 13 | ) 14 | 15 | expect_identical( 16 | skip(roclet_out, 2), 17 | global_variables( 18 | "\"foo\", # ", 19 | "\"bar\", # ", 20 | "\"foobar\", # ", 21 | "\"foofy\", # " 22 | ) 23 | ) 24 | }) 25 | 26 | test_that("global_roclet() handles empty", { 27 | expect_identical( 28 | skip(roxygen2::roc_proc_text(global_roclet(), ""), 2), 29 | c( 30 | "utils::globalVariables(c(", 31 | " NULL", 32 | "))" 33 | ) 34 | ) 35 | }) 36 | 37 | test_that("global_roclet() emits generated by", { 38 | expect_identical( 39 | first(roxygen2::roc_proc_text(global_roclet(), "")), 40 | "# Generated by roxyglobals: do not edit by hand" 41 | ) 42 | }) 43 | 44 | test_that("global_roclet() writes unique globals", { 45 | pkg_path <- local_package_copy(test_path("./config-unique")) 46 | globals_file <- file.path(pkg_path, "R", "globals.R") 47 | 48 | suppressMessages(roxygen2::roxygenise(pkg_path)) 49 | 50 | expect_identical( 51 | skip(brio::read_lines(globals_file), 2), 52 | global_variables( 53 | "# ", 54 | "# ", 55 | "\"my_key\",", 56 | "# ", 57 | "# ", 58 | "\"something\",", 59 | "# ", 60 | "# ", 61 | "\"that\",", 62 | "# ", 63 | "# ", 64 | "\"this\",", 65 | "# ", 66 | "# ", 67 | "\"thistoo\",", 68 | "# ", 69 | "# ", 70 | "\"thisworks\"," 71 | ) 72 | ) 73 | }) 74 | 75 | test_that("global_roclet() writes to custom filename", { 76 | pkg_path <- local_package_copy(test_path("./config-filename")) 77 | globals_file <- file.path(pkg_path, "R", "generated-globals.R") 78 | 79 | suppressMessages(roxygen2::roxygenise(pkg_path)) 80 | 81 | expect_identical( 82 | skip(brio::read_lines(globals_file), 2), 83 | c( 84 | "utils::globalVariables(c(", 85 | " \"my_key\", # ", 86 | " \"something\", # ", 87 | " \"thisworks\", # ", 88 | " \"this\", # ", 89 | " \"that\", # ", 90 | " \"thistoo\", # ", 91 | " NULL", 92 | "))" 93 | ) 94 | ) 95 | }) 96 | 97 | test_that("global_roclet() cleans globals file", { 98 | pkg_path <- local_package_copy(test_path("./empty")) 99 | globals_file <- file.path(pkg_path, "R", "globals.R") 100 | 101 | roxygen2::roclet_clean(global_roclet(), pkg_path) 102 | expect_false( 103 | file.exists(globals_file) 104 | ) 105 | 106 | # ensure file exists 107 | suppressMessages(roxygen2::roxygenise(pkg_path, clean = TRUE)) 108 | 109 | roxygen2::roclet_clean(global_roclet(), pkg_path) 110 | expect_false( 111 | file.exists(globals_file) 112 | ) 113 | }) 114 | -------------------------------------------------------------------------------- /tests/testthat/test-options.R: -------------------------------------------------------------------------------- 1 | test_that("options_get/set_unique() works", { 2 | pkg_path <- local_package_copy(test_path("./empty")) 3 | 4 | expect_identical( 5 | options_get_unique(pkg_path), 6 | FALSE 7 | ) 8 | 9 | options_set_unique(FALSE, pkg_path) 10 | expect_identical( 11 | options_get_unique(pkg_path), 12 | FALSE 13 | ) 14 | 15 | options_set_unique(TRUE, pkg_path) 16 | expect_identical( 17 | options_get_unique(pkg_path), 18 | TRUE 19 | ) 20 | 21 | # can disable after enabling 22 | options_set_unique(FALSE, pkg_path) 23 | expect_identical( 24 | options_get_unique(pkg_path), 25 | FALSE 26 | ) 27 | }) 28 | 29 | test_that("options_get/set_filename() works", { 30 | pkg_path <- local_package_copy(test_path("./empty")) 31 | 32 | expect_identical( 33 | options_get_filename(pkg_path), 34 | "globals.R" 35 | ) 36 | 37 | options_set_filename("globals.R", pkg_path) 38 | expect_identical( 39 | options_get_filename(pkg_path), 40 | "globals.R" 41 | ) 42 | 43 | options_set_filename("foobar.R", pkg_path) 44 | expect_identical( 45 | options_get_filename(pkg_path), 46 | "foobar.R" 47 | ) 48 | }) 49 | -------------------------------------------------------------------------------- /tests/testthat/test-use.R: -------------------------------------------------------------------------------- 1 | test_that("use_roxyglobals() works", { 2 | pkg_path <- local_package_copy(test_path("./empty")) 3 | withr::local_dir(pkg_path) 4 | 5 | roxyglobals::use_roxyglobals() 6 | expect_identical( 7 | desc::desc_get_field("Roxygen"), 8 | "list(roclets = c(\"collate\", \"namespace\", \"rd\", \"roxyglobals::global_roclet\"))" 9 | ) 10 | 11 | expect_true( 12 | desc::desc_has_dep("roxyglobals", "Suggests") 13 | ) 14 | 15 | expect_identical( 16 | desc::desc_get_field("Config/roxyglobals/unique"), 17 | "FALSE" 18 | ) 19 | 20 | expect_identical( 21 | desc::desc_get_field("Config/roxyglobals/filename"), 22 | "globals.R" 23 | ) 24 | }) 25 | 26 | test_that("use_roxyglobals() preserves existing roclets", { 27 | pkg_path <- local_package_copy(test_path("./has-roclets")) 28 | withr::local_dir(pkg_path) 29 | 30 | roxyglobals::use_roxyglobals() 31 | expect_identical( 32 | desc::desc_get_field("Roxygen"), 33 | "list(markdown = TRUE, roclets = c(\"collate\", \"namespace\", \"rd\", \"otherpkg::other_roclet\", \"roxyglobals::global_roclet\"))" 34 | ) 35 | }) 36 | 37 | test_that("use_roxyglobals() preserves options_filename", { 38 | pkg_path <- local_package_copy(test_path("./config-filename")) 39 | withr::local_dir(pkg_path) 40 | 41 | roxyglobals::use_roxyglobals() 42 | 43 | expect_identical( 44 | desc::desc_get_field("Config/roxyglobals/filename"), 45 | "generated-globals.R" 46 | ) 47 | }) 48 | 49 | test_that("use_roxyglobals() preserves options_unique", { 50 | pkg_path <- local_package_copy(test_path("./config-unique")) 51 | withr::local_dir(pkg_path) 52 | 53 | roxyglobals::use_roxyglobals() 54 | 55 | expect_identical( 56 | desc::desc_get_field("Config/roxyglobals/unique"), 57 | "TRUE" 58 | ) 59 | }) 60 | --------------------------------------------------------------------------------