{"id":14066460,"url":"https://github.com/dgkf/devutils","last_synced_at":"2025-07-31T18:32:24.629Z","repository":{"id":129307777,"uuid":"422014592","full_name":"dgkf/devutils","owner":"dgkf","description":"Utilities for common package development patterns","archived":false,"fork":false,"pushed_at":"2022-12-17T16:05:15.000Z","size":40,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-02-05T03:48:31.017Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"R","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dgkf.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2021-10-28T00:30:48.000Z","updated_at":"2023-05-22T22:59:07.000Z","dependencies_parsed_at":"2023-03-24T14:31:38.893Z","dependency_job_id":null,"html_url":"https://github.com/dgkf/devutils","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dgkf%2Fdevutils","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dgkf%2Fdevutils/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dgkf%2Fdevutils/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dgkf%2Fdevutils/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dgkf","download_url":"https://codeload.github.com/dgkf/devutils/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246266271,"owners_count":20749754,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-13T07:05:06.660Z","updated_at":"2025-03-30T01:37:56.046Z","avatar_url":"https://github.com/dgkf.png","language":"R","funding_links":[],"categories":["R"],"sub_categories":[],"readme":"### 🚧 This package is being split into [`options`](https://github.com/dgkf/options) and [`suggests`](https://github.com/dgkf/suggests) 🚧\n\n# `devutils`\n\nUtilities for common package development patterns\n\n##### options\n\n- Managing internal package options, defaulting to global options or environment\n  variables\n- Provides helpers for parsing environment variable strings into more convenient\n  R objects.\n- Automated package option documentation\n- Provides helpers for creating a stub function with parameters corresponding to\n  each option, such that parameter definitions can be easily inherited. Helpful\n  for globally configurable behaviors that also are used to set frequent\n  parameter defaults, such as `quiet` or `verbose`. \n\n##### suggests\n\n- Transparent class for suggested packages\n- Provide fallback functions for when a suggested package isn't available\n\n##### common infix operators\n\n- `%||%` \"if-not-null-else\" and `%?%` \"if-not-error-else\" infix operators\n\n## Examples\n\n### Defining options\n\nOptions are maintained in a hidden namespace variable called `.devutils`. By\ncalling the `define_pkgoption(s)` functions, you are updating that environment\nto include definitions for your options, storing their defaults, descriptions,\nand related option and environment variable names.\n\n`define_pkgoptions` provides a convenient shorthand for defining many options\nquickly, whereas `define_pkgoption` is a bit more rigid about its inputs, but\ngives you more flexibility to customize the option behavior.\n\n```r\ndefine_pkgoptions(\n  \"This is an example of how a package author would document their internally\",\n  \"used options. This option could make the package default to executing\",\n  \"quietly.\"\n  quiet = TRUE\n\n  \"Multiple options can be defined, providing default values if a global\",\n  \"option or environment variable isn't set.\"\n  second_example = FALSE\n\n  \"Default values are lazily evaluated, so you are free to use package\",\n  \"functions without worrying about build-time evaluation order\"\n  lazy_example = fn_not_defined_until_later()\n)\n\ndefine_pkgoption(\n  \"concrete_example\",\n  desc = paste0(\n    \"Or, if you prefer a more concrete constructor you can define each option \",\n    \"explicitly.\"),\n  option_name = \"mypackage_concrete\", # define custom option names\n  envvar_name = \"MYPACKAGE_CONCRETE\", # and custom environment variable names\n  envvar_fn = pkgoption_fn_is_true()  # and use helpers to handle envvar parsing\n)\n```\n\nOnce these are defined, you can use `pkgoption()` within your package to\nretrieve values, implicitly sourcing values from global options, environment\nvariables or the provided default value.\n\n### Creating option Rd documentation\n\nAs long as the options have been created as shown above, documenting your\noptions is as easy as adding this small roxygen stub within your package.\n\n```r\n#' @eval devutils::roxygenize_pkgoptions()\nNULL\n```\n\nProduces `?mypackage_options`\n\n```\nmypackage Options\n\nDescription:\n\n     Internally used, package-specific options. All options will\n     prioritize R options() values, and fall back to environment\n     variables if undefined. If neither the option nor the environment\n     variable is set, a default value is used.\n\nOptions:\n\n     quiet\n          This is an example of how a package author would document their\n          internally used options. This option could make the package default to\n          executing quietly.\n\n          default:\n\n              TRUE\n\n          option: mypackage.quiet\n\n          envvar: R_MYPACKAGE_QUIET (raw)\n...\n```\n\n### Reusing option documentation for parameters\n\nIn some cases, options are frequently used as default values for function\nparameters. In these cases, you might find that you're repeating yourself trying\nto document all the environment settings that might affect both the parameter\nand option. Instead, you can create a function stub from which you can inherit\nparameters in order to reuse the same option definitions as parameter\ndefinitions.\n\n```r\n#' @eval devutils::roxygenize_pkgoption_params()\nmypackage_option_params \u003c- NULL\n\n#' Example Function\n#'\n#' @inheritParams mypackage_option_params\n#'\ncount_to_three \u003c- function(quiet = pkgoption(\"quiet\")) {\n  for (i in 1:3) if (!quiet) cat(i, \"\\n\")\n}\n```\n\n### Throw error when suggested package is unavailable\n\nBy default, suggested packages are loaded as a new object. Functions from the\nsuggested package are accessed by `$` or `[[`.\n\n```r\n#' @import devutils\nmissingpackage \u003c- suggested(\"missingpackage\")\n\n#' @export\ncount_to_three \u003c- function() {\n  missingpackage$do_thing()\n}\n```\n\n```sh\nR\u003e library(mypackage)\nR\u003e count_to_three()\nError: This feature is unavailable because package 'missingpackage' is not installed.\n```\n\n\u003e It can be inconvenient to manage the overhead of remembering which packages\n\u003e are suggested (using `$` and imported `::`). You can call\n\u003e `permit_mutation(\"all\")` to give `devutils` permission to modify your package\n\u003e namespace, masking `::` and `:::` with versions that allow for suggested\n\u003e packages to be automatically handled.  \n\u003e\n\u003e Masking base operators, especially for something as foundational as package\n\u003e namespace interaction could be error-prone. For now this behavior is only for\n\u003e testing purposes.\n\n### Define a fallback behavior when a suggested package is unavailable\n\nSometimes, a suggested package is convenient because it produces nicer output\nthan something you care to write yourself, but you can still hack something\nsufficient together when the suggested package isn't available. In this\nscenario, you can provide a fallback to that function. Errors will still be\nraised if other functions are used, but the fallback will be used when only that\nfunction is needed.\n\n```r\n#' @import devutils\nmissingpackage \u003c- suggested(\"missingpackage\")\n\nsuggested_fallback(\n  missingpackage$do_thing,\n  function() for(i in 1:3) cat(i, \"\\n\")\n)\n\n#' @export\ncount_to_three \u003c- function() {\n  missingpackage$do_thing()\n  missingpackage$do_other_thing()\n}\n```\n\n```sh\nR\u003e library(mypackage)\nR\u003e count_to_three()\n1\n2\n3\nError: This feature is unavailable because package 'missingpackage' is not installed.\n```\n\n### A drop-in replacement for dependencies (experimental)\n\nIf you're feeling adventurous, you can give `devutils` added privileges to mask\nsome base functions in your package namespace, allowing you to flag a package as\nsuggested while directly reuseing existing namespaced package objects (accessed via\n`::` or `:::`).\n\nThis features is very experimental, and mucking with the base namespace is not\nsomething that should be done carelessly. For now, this feature is only\nrecommended for testing.\n\n```r\n#' @import devutils\ndevutils::permit_mutation(\"all\")\n\nsuggested(\"utils\")  # modified in-place; now `utils` is a suggested package object\n\n#' @export\ncount_to_three \u003c- function() {\n  for (i in utils::head(1:5, 3L)) cat(i, \"\\n\")\n}\n```\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdgkf%2Fdevutils","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdgkf%2Fdevutils","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdgkf%2Fdevutils/lists"}