Repository: pharmaR/riskmetric Branch: master Commit: fd07e38635dd Files: 232 Total size: 318.4 KB Directory structure: gitextract_cv8kk3_s/ ├── .Rbuildignore ├── .github/ │ ├── .gitignore │ └── workflows/ │ ├── R-CMD-check.yaml │ ├── pkgdown.yaml │ └── test-coverage.yaml ├── .gitignore ├── DESCRIPTION ├── LICENSE ├── LICENSE.md ├── NAMESPACE ├── NEWS.md ├── R/ │ ├── assess_covr_coverage.R │ ├── assess_dependencies.R │ ├── assess_downloads.R │ ├── assess_export_help.R │ ├── assess_exported_namespace.R │ ├── assess_has_bug_reports_url.R │ ├── assess_has_examples.R │ ├── assess_has_maintainer.R │ ├── assess_has_news.R │ ├── assess_has_source_control.R │ ├── assess_has_vignettes.R │ ├── assess_has_website.R │ ├── assess_last_30_bugs_status.R │ ├── assess_license.R │ ├── assess_news_current.R │ ├── assess_r_cmd_check.R │ ├── assess_remote_checks.R │ ├── assess_reverse_dependencies.R │ ├── assess_size_codebase.R │ ├── dev_hint.R │ ├── dev_tips.R │ ├── metric_score.R │ ├── options.R │ ├── pkg_assess.R │ ├── pkg_cohort.R │ ├── pkg_metric.R │ ├── pkg_metric_condition.R │ ├── pkg_metric_error.R │ ├── pkg_metric_na.R │ ├── pkg_metric_todo.R │ ├── pkg_ref_cache.R │ ├── pkg_ref_cache_NEWS_urls.R │ ├── pkg_ref_cache_archive_release_date.R │ ├── pkg_ref_cache_behaviors.R │ ├── pkg_ref_cache_bug_reports.R │ ├── pkg_ref_cache_bug_reports_host.R │ ├── pkg_ref_cache_bug_reports_url.R │ ├── pkg_ref_cache_covr_coverage.R │ ├── pkg_ref_cache_description.R │ ├── pkg_ref_cache_downloads.R │ ├── pkg_ref_cache_examples.R │ ├── pkg_ref_cache_expr_coverage.R │ ├── pkg_ref_cache_help.R │ ├── pkg_ref_cache_help_aliases.R │ ├── pkg_ref_cache_license.R │ ├── pkg_ref_cache_maintainer.R │ ├── pkg_ref_cache_news.R │ ├── pkg_ref_cache_r_cmd_check.R │ ├── pkg_ref_cache_release_date.R │ ├── pkg_ref_cache_remote_checks.R │ ├── pkg_ref_cache_repo_base_url.R │ ├── pkg_ref_cache_source_control_url.R │ ├── pkg_ref_cache_tarball_url.R │ ├── pkg_ref_cache_vignettes.R │ ├── pkg_ref_cache_web_html.R │ ├── pkg_ref_cache_web_url.R │ ├── pkg_ref_cache_website_urls.R │ ├── pkg_ref_class.R │ ├── pkg_ref_class_coersion.R │ ├── pkg_ref_class_extract.R │ ├── pkg_ref_class_format.R │ ├── pkg_ref_class_names.R │ ├── pkg_score.R │ ├── riskmetric-package.R │ ├── summarize_scores.R │ ├── utils.R │ ├── utils_memoised.R │ ├── vctrs_list_of_pkg_metric.R │ ├── vctrs_list_of_pkg_ref.R │ └── zzz.R ├── README.md ├── _pkgdown.yml ├── cran-comments.md ├── man/ │ ├── all_assessments.Rd │ ├── allow_mutation.Rd │ ├── as_pkg_metric.Rd │ ├── as_pkg_metric_condition.Rd │ ├── as_pkg_metric_error.Rd │ ├── as_pkg_metric_na.Rd │ ├── as_pkg_metric_todo.Rd │ ├── assess_covr_coverage.Rd │ ├── assess_dependencies.Rd │ ├── assess_downloads_1yr.Rd │ ├── assess_export_help.Rd │ ├── assess_exported_namespace.Rd │ ├── assess_has_bug_reports_url.Rd │ ├── assess_has_examples.Rd │ ├── assess_has_maintainer.Rd │ ├── assess_has_news.Rd │ ├── assess_has_source_control.Rd │ ├── assess_has_vignettes.Rd │ ├── assess_has_website.Rd │ ├── assess_last_30_bugs_status.Rd │ ├── assess_license.Rd │ ├── assess_news_current.Rd │ ├── assess_r_cmd_check.Rd │ ├── assess_remote_checks.Rd │ ├── assess_reverse_dependencies.Rd │ ├── assess_size_codebase.Rd │ ├── assessment_error_as_warning.Rd │ ├── assessment_error_empty.Rd │ ├── assessment_error_throw.Rd │ ├── available_pkg_ref_fields.Rd │ ├── bare_env.Rd │ ├── bug_report_metadata.Rd │ ├── cache_behaviors.Rd │ ├── capture_expr_output.Rd │ ├── dec_mutations_count.Rd │ ├── determine_pkg_source.Rd │ ├── dot-tools.Rd │ ├── examples_from_dir.Rd │ ├── examples_from_pkg.Rd │ ├── filter_rd_db.Rd │ ├── firstS3method.Rd │ ├── format_assessment_message.Rd │ ├── get_assessment_columns.Rd │ ├── get_assessments.Rd │ ├── get_package_dependencies.Rd │ ├── get_pkg_ref_classes.Rd │ ├── if_not_null_else.Rd │ ├── inc_mutations_count.Rd │ ├── is_url_subpath_of.Rd │ ├── memoise_bioc_mirrors.Rd │ ├── memoise_cran_mirrors.Rd │ ├── metric_score.Rd │ ├── metric_score.pkg_metric_covr_coverage.Rd │ ├── metric_score.pkg_metric_dependencies.Rd │ ├── metric_score.pkg_metric_downloads_1yr.Rd │ ├── metric_score.pkg_metric_export_help.Rd │ ├── metric_score.pkg_metric_exported_namespace.Rd │ ├── metric_score.pkg_metric_has_bug_reports_url.Rd │ ├── metric_score.pkg_metric_has_examples.Rd │ ├── metric_score.pkg_metric_has_maintainer.Rd │ ├── metric_score.pkg_metric_has_news.Rd │ ├── metric_score.pkg_metric_has_source_control.Rd │ ├── metric_score.pkg_metric_has_vignettes.Rd │ ├── metric_score.pkg_metric_has_website.Rd │ ├── metric_score.pkg_metric_last_30_bugs_status.Rd │ ├── metric_score.pkg_metric_license.Rd │ ├── metric_score.pkg_metric_news_current.Rd │ ├── metric_score.pkg_metric_r_cmd_check.Rd │ ├── metric_score.pkg_metric_remote_checks.Rd │ ├── metric_score.pkg_metric_reverse_dependencies.Rd │ ├── metric_score.pkg_metric_size_codebase.Rd │ ├── news_from_dir.Rd │ ├── parse_dcf_dependencies.Rd │ ├── pkg_assess.Rd │ ├── pkg_metric.Rd │ ├── pkg_metric_eval.Rd │ ├── pkg_ref.Rd │ ├── pkg_ref_cache.bug_reports_host.default.Rd │ ├── pkg_ref_cache.bug_reports_url.pkg_source.Rd │ ├── pkg_ref_cache.covr_coverage.pkg_source.Rd │ ├── pkg_ref_cache.expression_coverage.pkg_source.Rd │ ├── pkg_ref_cache.help.pkg_install.Rd │ ├── pkg_ref_cache.help.pkg_source.Rd │ ├── pkg_ref_cache.news.pkg_remote.Rd │ ├── pkg_ref_class_hierarchy.Rd │ ├── pkg_ref_mutability_error.Rd │ ├── pkg_score.Rd │ ├── print.with_eval_recording.Rd │ ├── remove_base_packages.Rd │ ├── require_cache_behaviors.Rd │ ├── riskmetric.Rd │ ├── riskmetric_metadata_caching.Rd │ ├── roxygen_assess_family.Rd │ ├── roxygen_assess_family_catalog.Rd │ ├── roxygen_cache_behaviors.Rd │ ├── roxygen_score_family.Rd │ ├── score_error_NA.Rd │ ├── score_error_default.Rd │ ├── score_error_zero.Rd │ ├── sub-sub-.pkg_ref.Rd │ ├── summarize_scores.Rd │ ├── suppressMatchingConditions.Rd │ ├── use_assessments_column_names.Rd │ ├── verify_pkg_source.Rd │ ├── vignettes_from_dir.Rd │ ├── vignettes_from_html.Rd │ ├── with.pkg_ref.Rd │ └── with_unclassed_to.Rd └── tests/ ├── testthat/ │ ├── setup_mock_web_requests.R │ ├── setup_test_packages.R │ ├── teardown_mock_web_requests.R │ ├── teardown_test_packages.R │ ├── test_assess.R │ ├── test_assess_dependencies.R │ ├── test_assess_export_help.R │ ├── test_assess_has_bug_reports_url.R │ ├── test_assess_has_examples.R │ ├── test_assess_has_news.R │ ├── test_assess_last_30_bugs_status.R │ ├── test_assess_news_current.R │ ├── test_metric_score_labels.R │ ├── test_metric_score_range.R │ ├── test_packages/ │ │ ├── pkgsourcebad/ │ │ │ ├── DESCRIPTION │ │ │ ├── NAMESPACE │ │ │ ├── R/ │ │ │ │ └── hello_world_test.R │ │ │ └── man/ │ │ │ └── hello_world_test.Rd │ │ ├── pkgsourcebad2/ │ │ │ └── DESCRIPTION │ │ ├── pkgsourcegood/ │ │ │ ├── DESCRIPTION │ │ │ ├── NAMESPACE │ │ │ ├── NEWS.md │ │ │ ├── R/ │ │ │ │ └── hello_world_test.R │ │ │ └── man/ │ │ │ └── hello_world_test.Rd │ │ └── secondLib/ │ │ └── secondLibPkg/ │ │ ├── DESCRIPTION │ │ ├── NAMESPACE │ │ ├── NEWS.md │ │ ├── R/ │ │ │ └── hello_world_test.R │ │ └── man/ │ │ └── hello_world_test.Rd │ ├── test_pkg_ref.R │ ├── test_snapshots.R │ └── test_webmocks/ │ ├── data/ │ │ ├── cran_mirrors.csv │ │ ├── cran_news.html │ │ ├── cran_package.html │ │ ├── cran_package_archive.html │ │ ├── cran_package_checks.html │ │ ├── cran_packages.csv │ │ └── github_repo_issues_api_response.json │ └── rebuild_webmocks.R └── testthat.R ================================================ FILE CONTENTS ================================================ ================================================ FILE: .Rbuildignore ================================================ ^codecov\.yml$ ^appveyor\.yml$ ^\.travis\.yml$ ^\.github ^.*\.Rproj$ ^\.Rproj\.user$ ^LICENSE\.md$ ^_pkgdown\.yml$ ^docs$ ^pkgdown$ ^cran-comments\.md$ ^\.github$ ================================================ FILE: .github/.gitignore ================================================ *.html ================================================ FILE: .github/workflows/R-CMD-check.yaml ================================================ # Workflow derived from https://github.com/r-lib/actions/tree/master/examples # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help # # NOTE: This workflow is overkill for most R packages and # check-standard.yaml is likely a better choice. # usethis::use_github_action("check-standard") will install it. on: push: branches: [main, master] pull_request: branches: [main, master] name: R-CMD-check jobs: R-CMD-check: runs-on: ${{ matrix.config.os }} name: ${{ matrix.config.os }} (${{ matrix.config.r }}) strategy: fail-fast: false matrix: config: - {os: macOS-latest, r: 'release'} - {os: windows-latest, r: 'release'} # Use 3.6 to trigger usage of RTools35 - {os: windows-latest, r: '3.6'} # Use older ubuntu to maximise backward compatibility - {os: ubuntu-22.04, r: 'devel', http-user-agent: 'release'} - {os: ubuntu-22.04, r: 'release'} - {os: ubuntu-22.04, r: 'oldrel-1'} - {os: ubuntu-22.04, r: 'oldrel-2'} - {os: ubuntu-22.04, r: 'oldrel-3'} env: GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} R_KEEP_PKG_SOURCE: yes _R_CHECK_RD_VALIDATE_RD2HTML_: false steps: - uses: actions/checkout@v2 - uses: r-lib/actions/setup-pandoc@v2 - uses: r-lib/actions/setup-r@v2 with: r-version: ${{ matrix.config.r }} http-user-agent: ${{ matrix.config.http-user-agent }} use-public-rspm: true - uses: r-lib/actions/setup-r-dependencies@v2 with: extra-packages: rcmdcheck - uses: r-lib/actions/setup-tinytex@v2 - uses: r-lib/actions/check-r-package@v2 with: args: 'c("--as-cran", "--no-manual")' - name: Show testthat output if: always() run: find check -name 'testthat.Rout*' -exec cat '{}' \; || true shell: bash - name: Upload check results if: failure() uses: actions/upload-artifact@main with: name: ${{ runner.os }}-r${{ matrix.config.r }}-results path: check ================================================ FILE: .github/workflows/pkgdown.yaml ================================================ # Workflow derived from https://github.com/r-lib/actions/tree/master/examples # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help on: push: branches: [main, master] tags: ['*'] name: pkgdown jobs: pkgdown: runs-on: ubuntu-latest env: GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v2 - uses: r-lib/actions/setup-pandoc@v2 - uses: r-lib/actions/setup-r@v2 with: use-public-rspm: true - uses: r-lib/actions/setup-r-dependencies@v2 with: extra-packages: any::pkgdown, local::. needs: website - name: Deploy package run: | git config --local user.name "$GITHUB_ACTOR" git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" Rscript -e 'pkgdown::deploy_to_branch(new_process = FALSE)' ================================================ FILE: .github/workflows/test-coverage.yaml ================================================ # Workflow derived from https://github.com/r-lib/actions/tree/master/examples # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help on: push: branches: [main, master] pull_request: branches: [main, master] name: test-coverage jobs: test-coverage: runs-on: ubuntu-latest env: GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v2 - uses: r-lib/actions/setup-r@v2 with: use-public-rspm: true - uses: r-lib/actions/setup-r-dependencies@v2 with: extra-packages: any::covr - name: Test coverage run: covr::codecov() shell: Rscript {0} ================================================ FILE: .gitignore ================================================ .DS_Store vignettes !vignettes/*.Rmd inst/doc docs .Rproj.user .Rhistory .RData .Ruserdata .html riskmetric.Rproj ================================================ FILE: DESCRIPTION ================================================ Package: riskmetric Type: Package Title: Risk Metrics to Evaluating R Packages Description: Facilities for assessing R packages against a number of metrics to help quantify their robustness. Version: 0.2.7 Authors@R: c( person("R Validation Hub", role = c("aut"), email = "psi.aims.r.validation@gmail.com"), person("Doug", "Kelkhoff", role = c("aut"), email = "doug.kelkhoff@gmail.com"), person("Marly", "Gotti", role = c("aut")), person("Eli", "Miller", role = c("cre", "aut"), email = "eli.miller@atorusresearch.com"), person("Kevin", "K", role = c("aut")), person("Yilong", "Zhang", role = c("aut")), person("Eric", "Milliman", role = c("aut")), person("Juliane", "Manitz", role = c("aut")), person("Mark", "Padgham", role = c("ctb")), person("PSI special interest group Application and Implementation of Methodologies in Statistics", role = c("cph"))) URL: https://pharmar.github.io/riskmetric/, https://github.com/pharmaR/riskmetric BugReports: https://github.com/pharmaR/riskmetric/issues License: MIT + file LICENSE Encoding: UTF-8 Imports: backports, utils, tools, xml2, httr, curl, urltools, memoise, BiocManager, cranlogs, covr, vctrs, pillar, tibble, pkgload, devtools Suggests: dplyr, jsonlite, knitr, magrittr, pkgbuild, rmarkdown, testthat, webmockr, withr RoxygenNote: 7.3.3 VignetteBuilder: knitr Config/testthat/edition: 3 ================================================ FILE: LICENSE ================================================ YEAR: 2019 COPYRIGHT HOLDER: PSI special interest group Application and Implementation of Methodologies in Statistics ================================================ FILE: LICENSE.md ================================================ pkg# MIT License Copyright (c) 2019 PSI special interest group Application and Implementation of Methodologies in Statistics Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: NAMESPACE ================================================ # Generated by roxygen2: do not edit by hand S3method("$",pkg_ref) S3method("$<-",pkg_ref) S3method("[",pkg_ref) S3method("[<-",pkg_ref) S3method("[[",pkg_ref) S3method("[[<-",pkg_ref) S3method(.DollarNames,pkg_ref) S3method(as_pkg_metric,default) S3method(as_pkg_metric,expr_output) S3method(as_pkg_ref,character) S3method(as_pkg_ref,default) S3method(as_pkg_ref,pkg_ref) S3method(as_tibble,list_of_pkg_ref) S3method(as_tibble,pkg_ref) S3method(assess_covr_coverage,default) S3method(assess_covr_coverage,pkg_source) S3method(assess_dependencies,default) S3method(assess_dependencies,pkg_bioc_remote) S3method(assess_dependencies,pkg_cran_remote) S3method(assess_dependencies,pkg_install) S3method(assess_dependencies,pkg_source) S3method(assess_downloads_1yr,pkg_ref) S3method(assess_export_help,pkg_install) S3method(assess_export_help,pkg_remote) S3method(assess_export_help,pkg_source) S3method(assess_exported_namespace,default) S3method(assess_exported_namespace,pkg_install) S3method(assess_exported_namespace,pkg_source) S3method(assess_has_bug_reports_url,default) S3method(assess_has_examples,pkg_ref) S3method(assess_has_news,pkg_ref) S3method(assess_has_vignettes,pkg_ref) S3method(assess_news_current,pkg_ref) S3method(assess_news_current,pkg_remote) S3method(assess_r_cmd_check,default) S3method(assess_r_cmd_check,pkg_bioc_remote) S3method(assess_r_cmd_check,pkg_cran_remote) S3method(assess_r_cmd_check,pkg_source) S3method(assess_remote_checks,default) S3method(assess_remote_checks,pkg_bioc_remote) S3method(assess_remote_checks,pkg_cran_remote) S3method(assess_reverse_dependencies,default) S3method(assess_size_codebase,default) S3method(assess_size_codebase,pkg_install) S3method(assess_size_codebase,pkg_source) S3method(format,pkg_metric) S3method(format,pkg_metric_error) S3method(format,pkg_missing) S3method(format,pkg_ref) S3method(metric_score,default) S3method(metric_score,pkg_metric_covr_coverage) S3method(metric_score,pkg_metric_dependencies) S3method(metric_score,pkg_metric_downloads_1yr) S3method(metric_score,pkg_metric_export_help) S3method(metric_score,pkg_metric_exported_namespace) S3method(metric_score,pkg_metric_has_bug_reports_url) S3method(metric_score,pkg_metric_has_examples) S3method(metric_score,pkg_metric_has_maintainer) S3method(metric_score,pkg_metric_has_news) S3method(metric_score,pkg_metric_has_source_control) S3method(metric_score,pkg_metric_has_vignettes) S3method(metric_score,pkg_metric_has_website) S3method(metric_score,pkg_metric_last_30_bugs_status) S3method(metric_score,pkg_metric_license) S3method(metric_score,pkg_metric_news_current) S3method(metric_score,pkg_metric_r_cmd_check) S3method(metric_score,pkg_metric_remote_checks) S3method(metric_score,pkg_metric_reverse_dependencies) S3method(metric_score,pkg_metric_size_codebase) S3method(names,pkg_ref) S3method(pillar_shaft,list_of_pkg_metric) S3method(pillar_shaft,list_of_pkg_ref) S3method(pillar_shaft,pkg_metric_error) S3method(pkg_assess,list_of_pkg_ref) S3method(pkg_assess,pkg_ref) S3method(pkg_assess,tbl_df) S3method(pkg_score,list_of_pkg_metric) S3method(pkg_score,tbl_df) S3method(print,pkg_ref) S3method(print,with_eval_recording) S3method(summarize_scores,data.frame) S3method(summarize_scores,list) S3method(vec_cast.character,list_of_pkg_ref) S3method(vec_cast.double,list_of_pkg_metric) S3method(vec_ptype_abbr,pkg_metric) S3method(vec_ptype_abbr,pkg_ref) S3method(with,pkg_ref) export(all_assessments) export(as_pkg_metric) export(as_pkg_ref) export(assess_covr_coverage) export(assess_dependencies) export(assess_downloads_1yr) export(assess_export_help) export(assess_exported_namespace) export(assess_has_bug_reports_url) export(assess_has_examples) export(assess_has_maintainer) export(assess_has_news) export(assess_has_source_control) export(assess_has_vignettes) export(assess_has_website) export(assess_last_30_bugs_status) export(assess_license) export(assess_news_current) export(assess_r_cmd_check) export(assess_remote_checks) export(assess_reverse_dependencies) export(assess_size_codebase) export(assessment_error_as_warning) export(assessment_error_empty) export(assessment_error_throw) export(get_assessments) export(metric_score) export(pkg_assess) export(pkg_metric) export(pkg_ref) export(pkg_score) export(score_error_NA) export(score_error_default) export(score_error_zero) export(summarize_scores) import(tools) importFrom(BiocManager,available) importFrom(BiocManager,repositories) importFrom(backports,import) importFrom(covr,coverage_to_list) importFrom(covr,package_coverage) importFrom(covr,tally_coverage) importFrom(cranlogs,cran_downloads) importFrom(curl,nslookup) importFrom(devtools,check) importFrom(devtools,revdep) importFrom(httr,GET) importFrom(httr,content) importFrom(memoise,memoise) importFrom(pillar,new_pillar_shaft_simple) importFrom(pillar,pillar_shaft) importFrom(pillar,style_na) importFrom(pkgload,parse_ns_file) importFrom(tibble,as_tibble) importFrom(tibble,tibble) importFrom(tools,Rd_db) importFrom(tools,file_path_sans_ext) importFrom(tools,parseLatex) importFrom(tools,testInstalledPackage) importFrom(urltools,domain) importFrom(urltools,url_encode) importFrom(utils,.DollarNames) importFrom(utils,.S3methods) importFrom(utils,available.packages) importFrom(utils,capture.output) importFrom(utils,getS3method) importFrom(utils,head) importFrom(utils,packageDescription) importFrom(utils,packageName) importFrom(utils,packageVersion) importFrom(utils,tail) importFrom(vctrs,new_list_of) importFrom(vctrs,new_vctr) importFrom(vctrs,vec_cast.character) importFrom(vctrs,vec_cast.double) importFrom(vctrs,vec_ptype_abbr) importFrom(xml2,xml_attr) importFrom(xml2,xml_attrs) importFrom(xml2,xml_find_all) importFrom(xml2,xml_text) ================================================ FILE: NEWS.md ================================================ # riskmetric (development version) # riskmetric 0.2.6 - Update to address new failing tests responding to `devtools` v2.4.7 changes. # riskmetric 0.2.5 - Update use of `vctrs` to accommdate changes to acceptable `ptype` parameters in `vctrs` v0.7.1 (#394) - Fix bug with assessing source packages that have non-R files in the R directory ([#362](https://github.com/pharmaR/riskmetric/issues/362)) # riskmetric 0.2.4 - Fix CRAN errors. # riskmetric 0.2.3 - Fix issue with CRAN package documentation flags. (#311) # riskmetric 0.2.2 - Fix bug with reporting number of downloads. # riskmetric 0.2.1 - Updates for S3 Method consistancy for `vec_cast` and `pillar_shift` per CRAN comments. # riskmetric 0.2.0 - We now have a Hex Logo! #233. Thanks to @AARON-CLARK. - Number of download assessment can now take a specified number of days. #258. Thanks to @parmsam-pfizer. - A new assessment was added for determining the size of the codebase. #66. Thanks to @shengwei66. - Fixed an issue of some scores returning negative numbers instead of values between [0,1]. Thanks to @emilliman5. - A new assessment was added for the presens of a bug report URL for the package. Thanks to @kimjj93. - A new assessment was added to score the dependency footprint of a package. Thanks to @emilliman5. # riskmetric 0.1.2 - Hotfix release to correct testing suite such that tests are less continent on assumptions of locally installed packages, addressing build issues on CRAN builders. (#223, @elimillera) # riskmetric 0.1.1 - Fixing a bug with subclassing of `pkg_ref` objects using the new concrete constructors. (#208, @dgkf) # riskmetric 0.1.0 - Initial version. - Added a `NEWS.md` file to track changes to the package. ================================================ FILE: R/assess_covr_coverage.R ================================================ #' Assess a package code coverage using the `covr` package #' #' @eval roxygen_assess_family( #' "covr_coverage", #' paste0("a list containing fields 'filecoverage' and 'totalcoverage' ", #' "containing a named numeric vector of file unit test coverage and a ", #' "singular numeric value representing overall test coverage ", #' "respectively."), #' dontrun = TRUE #' ) #' #' @export assess_covr_coverage <- function(x, ...) { UseMethod("assess_covr_coverage") } attributes(assess_covr_coverage)$column_name <- "covr_coverage" attributes(assess_covr_coverage)$label <- "Package unit test coverage" #' @export assess_covr_coverage.default <- function(x, ...) { as_pkg_metric_na(pkg_metric(class = "pkg_metric_covr_coverage")) } #' @importFrom covr coverage_to_list #' @export assess_covr_coverage.pkg_source <- function(x, ...) { pkg_metric_eval(class = "pkg_metric_covr_coverage", { covr::coverage_to_list(x$covr_coverage) }) } #' Score a package for unit test coverage #' #' Returns the overall test coverage from a covr coverage report #' #' @eval roxygen_score_family("covr_coverage", dontrun = TRUE) #' @return A \code{numeric} #' #' @export metric_score.pkg_metric_covr_coverage <- function(x, ...) { x$totalcoverage / 100 } attributes(metric_score.pkg_metric_covr_coverage)$label <- "The fraction of lines of code which are covered by a unit test." ================================================ FILE: R/assess_dependencies.R ================================================ #' Assessment of dependency footprint for a specific package #' #' Only Depends, Imports and LinkingTo dependencies are assessed because #' they are required #' #' @details The more packages a package relies on the more chances for errors exist. #' #' @eval roxygen_assess_family( #' "dependencies", #' "a dataframe of package names and they type of dependency the package being assess has to them") #' #' #' @export assess_dependencies <- function(x, ...){ UseMethod("assess_dependencies") } attributes(assess_dependencies)$column_name <- "dependencies" attributes(assess_dependencies)$label <- "Package dependency footprint" #' @export assess_dependencies.default <- function(x, ...){ as_pkg_metric_na(pkg_metric(class = "pkg_metric_dependencies")) } #' @export assess_dependencies.pkg_source <- function(x, ...){ pkg_metric_eval(class = "pkg_metric_dependencies", { parse_dcf_dependencies(x$path) }) } #' @export assess_dependencies.pkg_install <- function(x, ...){ pkg_metric_eval(class = "pkg_metric_dependencies", { parse_dcf_dependencies(x$path) }) } #' @export assess_dependencies.pkg_cran_remote <- function(x, ...){ #Attempt to find CRAN URL by matching all urls returned by getOptions("repos") to memoise_cran_mirrors table repos <- getOption("repos")[which(getOption("repos") %in% memoise_cran_mirrors()$URL)] if(length(repos)==0){ repos <- grep("[\\.|//]cran\\.", getOption("repos"), ignore.case = T, value = T) } if(length(repos)==0){ repos <- getOption("repos")[["CRAN"]] } if(length(repos)==0){ as_pkg_metric_error(error = 'Could not determine which CRAN mirror you are using.') } else{ pkg_metric_eval(class = "pkg_metric_dependencies", { get_package_dependencies(x$name, repo = repos[1]) ##Will use the first CRAN mirror found in the users environment }) } } #' @importFrom BiocManager repositories #' @export assess_dependencies.pkg_bioc_remote <- function(x, ...){ pkg_metric_eval(class = "pkg_metric_dependencies", { get_package_dependencies(x$name, BiocManager::repositories()[1]) }) } #' Score a package for dependencies #' #' Calculates a regularized score based on the number of dependencies a package has. #' Convert the number of dependencies \code{NROW(x)} into a validation #' score [0,1] \deqn{ 1 - 1 / (1 + exp(-0.5 * (NROW(x) + 4))) } #' #' The scoring function is the classic logistic curve \deqn{ / (1 + exp(-k(x-x[0])) } #' \eqn{x = NROW(x)}, sigmoid midpoint is 5 reverse dependencies, ie. \eqn{x[0] = 4}, #' and logistic growth rate of \eqn{k = 0.5}. #' #' \deqn{ 1 - 1 / (1 + exp(NROW(x)-4)) } #' #' @eval roxygen_score_family("dependencies") #' @return numeric value between \code{0} (high number of dependencies) and #' \code{1} (low number of dependencies) #' #' @export metric_score.pkg_metric_dependencies <- function(x, ...) { 1 - 1/(1 + exp(-0.5 * (NROW(x) - 4))) } attributes(metric_score.pkg_metric_dependencies)$label <- "The number of package dependencies" #Helper functions to get extract dependencies #' Gets available packages from necessary repository and filters for #' package of interest #' #' @param name package name #' @param repo package repository (e.g. CRAN or Bioconductor) #' #' @return Returns a data frame with two columns 1) Package names, 2) type of dependency (LinkingTo, Imports, Depends) #' @keywords internal #' get_package_dependencies <- function(name, repo){ ap <- available.packages(repos = repo) deps <- ap[rownames(ap)==name, c("LinkingTo","Imports","Depends")] deps <- deps[!is.na(deps)] deps <- lapply(strsplit(deps, ","), trimws) deps <- data.frame(package=unlist(deps), type=rep(names(deps), sapply(deps, length)), stringsAsFactors = FALSE, row.names = NULL) deps <- remove_base_packages(deps) return(deps) } #' Parse DCF of description file #' #' @param path pkg_ref path #' @keywords internal #' parse_dcf_dependencies <- function(path){ dcf <- read.dcf(file.path(path, "DESCRIPTION"), all=TRUE) dcf <- dcf[colnames(dcf) %in% c("LinkingTo","Imports", "Depends")] dcf <- sapply(dcf, strsplit, split=",") dcf <- lapply(dcf, trimws) deps <- data.frame(package=unlist(dcf), type=rep(names(dcf), sapply(dcf, length)), stringsAsFactors = FALSE, row.names = NULL) deps <- remove_base_packages(deps) return(deps) } #' Helper function to remove base and recommended packages #' #' @param df Data frame of dependencies of a package. #' @keywords internal #' remove_base_packages <- function(df){ inst <- memoise_available_packages() inst_priority <- inst[,"Priority"] inst_is_base_rec <- !is.na(inst_priority) & inst_priority %in% c("base", "recommended") base_rec_pkgs <- inst[inst_is_base_rec, "Package"] deps <- df[!grepl("^R\\s\\(.+\\)", df$package) | df$package %in% base_rec_pkgs, ] ##Remove "R" dependencies as well as base and recomended return(deps) } ================================================ FILE: R/assess_downloads.R ================================================ #' Assess a package for the number of downloads in the past year #' #' @details The more times a package has been downloaded the more extensive the user testing and the greater chance there is of someone finding a bug and logging it. #' #' @eval roxygen_assess_family( #' "downloads_1yr", #' "a numeric value between [0,1] indicating the volume of downloads", #' dontrun = TRUE #' ) #' #' @export assess_downloads_1yr <- function(x, ...){ UseMethod("assess_downloads_1yr") } # assign a friendly name for assess column attr(assess_downloads_1yr, "column_name") <- "downloads_1yr" attr(assess_downloads_1yr, "label") <- "number of downloads in the past year" #' @export assess_downloads_1yr.pkg_ref <- function(x, ...) { pkg_metric_eval(class = "pkg_metric_downloads_1yr", { sum(x$downloads$count) }) } #' Defining an Assessment Scoring Function #' #' Score a package for the number of downloads in the past year regularized #' Convert the number of downloads \code{x} in the past year into a validation #' score [0,1] \deqn{ 1 - 150,000 / (x + 150,000) } #' #' The scoring function is a simplification of the classic logistic curve \deqn{ #' 1 / (1 + exp(-k(x-x[0])) } with a log scale for the number of downloads #' \eqn{x = log(x)}, sigmoid midpoint is 1000 downloads, ie. \eqn{x[0] = #' log(1,000)}, and logistic growth rate of \eqn{k = 0.5}. #' #' \deqn{ 1 - 1 / (1 + exp(log(x)-log(1.5e5))) = 1 - 150,000 / (x + 150,000) } #' #' @eval roxygen_score_family("downloads_1yr") #' @return numeric value between \code{0} (low) and \code{1} (high download #' volume) converting the number of downloads. #' #' @export metric_score.pkg_metric_downloads_1yr <- function(x, ...) { # simplification from logistic: 1 - 1 / (1 + exp(log(x)-log(1.5e5))) 1 - 1.5 / (x / 1e5 + 1.5) } attributes(metric_score.pkg_metric_downloads_1yr)$label <- paste0( "A logistic rating of the number of package downloads in the past year. ", "For more details, see ?riskmetric::metric_score.pkg_metric_downloads_1yr") ================================================ FILE: R/assess_export_help.R ================================================ #' Assess a package for availability of documentation for exported values #' #' @eval roxygen_assess_family( #' "export_help", #' "a logical vector indicating existence of documentation for each namespace export") #' #' @export assess_export_help <- function(x, ...) { UseMethod("assess_export_help") } attributes(assess_export_help)$column_name <- "export_help" attributes(assess_export_help)$label <- "exported objects have documentation" #' @export assess_export_help.pkg_remote <- function(x, ...) { as_pkg_metric_na( pkg_metric(class = "pkg_metric_export_help"), message = "Cannot scrape exported documentation from a remote package webpage") } #' @importFrom pkgload parse_ns_file #' @export assess_export_help.pkg_source <- function(x, ...) { pkg_metric_eval(class = "pkg_metric_export_help", { # Read NAMESPACE lines <- readLines(paste0(x$path, "/NAMESPACE"), warn = FALSE) # Extract explicit exports export_lines <- grep("^export\\(|^exportMethod\\(", lines, value = TRUE) exports <- gsub(".*\\(([^)]+)\\).*", "\\1", export_lines) exports <- unlist(strsplit(exports, ",\\s*")) # Extract exportPattern regexes pattern_lines <- grep("^exportPattern\\(", lines, value = TRUE) patterns <- gsub(".*\\(\"([^\"]+)\"\\).*", "\\1", pattern_lines) # Match exportPattern regexes against available help aliases pattern_exports <- unlist(lapply(patterns, function(pat) { grep(pat, names(x$help_aliases), value = TRUE) })) # Combine all exports and remove duplicates all_exports <- unique(c(exports, pattern_exports)) # Check which exports have help aliases out <- all_exports %in% names(x$help_aliases) names(out) <- all_exports out }) } #' @export assess_export_help.pkg_install <- function(x, ...) { pkg_metric_eval(class = "pkg_metric_export_help", { # ignore S3-dispatched methods exports <- getNamespaceExports(x$name) out <- exports %in% names(x$help_aliases) names(out) <- exports out }) } #' Score a package for availability of documentation for exported values #' #' Coerce a logical vector indicating availability of export documentation #' #' @eval roxygen_score_family("export_help") #' @return \code{1} if any NEWS files are found, otherwise \code{0} #' #' @export metric_score.pkg_metric_export_help <- function(x, ...) { sum(x, na.rm = TRUE) / length(x) } attributes(metric_score.pkg_metric_export_help)$label <- "The fraction of exported objects that are documented." #' #' Provide development hints for improving exported value documentation #' #' @inheritParams dev_tips #' dev_tips.pkg_metric_export_help <- function(x, ...) { #' x_sorted <- x[order(names(x))] #' x_sorted <- sort(x_sorted, decreasing = TRUE) #' #' max_nchar <- max(nchar(names(x_sorted))) #' row_n <- (getOption("width") / (max_nchar + 2)) %/% 1 #' max_nchar <- (getOption("width") / row_n) %/% 1 #' #' text <- paste0( #' ifelse(x_sorted, #' dev_hint_crayon_success("\u2713"), # check mark #' dev_hint_crayon_failure("\u2718")), # x mark #' names(x_sorted), #' strrep(" ", max_nchar - nchar(names(x_sorted)))) #' #' dev_hint( #' title = "Documenting Exported Objects", #' text = list( #' dev_hint_section("Not all exported objects have help documentation."), #' dev_hint_section(paste0( #' "Consider adding [.Rd help files](https://cran.r-project.org/doc/manuals/R-exts.html#Writing-R-documentation-files) ", #' "or use [roxygen2](https://cran.r-project.org/web/packages/roxygen2/vignettes/roxygen2.html) ", #' "to document your functions alongside the source code.")), #' dev_hint_section(paste0(text, #' ifelse(seq_along(text) %% row_n == 0, "\n", ""), #' collapse = ""), #' list(wrap = FALSE)))) #' } ================================================ FILE: R/assess_exported_namespace.R ================================================ #' Assess a package's results from running R CMD check #' #' @eval roxygen_assess_family( #' "exported_namespace", #' "List of functions and objects exported by a package, excluding S3methods", #' ) #' #' @importFrom pkgload parse_ns_file #' @export assess_exported_namespace <- function(x, ...) { UseMethod("assess_exported_namespace") } attributes(assess_exported_namespace)$column_name <- "exported_namespace" attributes(assess_exported_namespace)$label <- "Objects exported by package" #' @export assess_exported_namespace.default <- function(x, ...) { as_pkg_metric_na( pkg_metric(class = "pkg_metric_export_help"), message = sprintf("Cannot export namespace from a %s", x$source)) } #' @export assess_exported_namespace.pkg_install <- function(x, ...) { pkg_metric_eval(class = "pkg_metric_exported_namespace", { # ignore S3-dispatched methods return(getNamespaceExports(x$name)) }) } #' @export assess_exported_namespace.pkg_source <- function(x, ...) { pkg_metric_eval(class = "pkg_metric_exported_namespace", { # ignore S3-dispatched methods return(pkgload::parse_ns_file(x$path)$exports) }) } #' Score a package for the number of exported objects #' #' Score a package for the number of exported objects it has; regularized #' Convert the number of exported objects \code{length(x)} into a validation #' score [0,1] \deqn{ 1 / (1 + exp(-0.5 * (sqrt(length(x)) + sqrt(5)))) } #' #' The scoring function is the classic logistic curve \deqn{ #' 1 / (1 + exp(-k(x-x[0])) } with a square root scale for the number of exported objects #' \eqn{x = sqrt(length(x))}, sigmoid midpoint is 25 exported objects, ie. \eqn{x[0] = #' sqrt(5)}, and logistic growth rate of \eqn{k = 0.25}. #' #' \deqn{ 1 / (1 + exp(-0.25 * sqrt(length(x))-sqrt(25))) } #' #' @eval roxygen_score_family("exported_namespace") #' @return numeric value between \code{0} (high number of exported objects) and #' \code{1} (low number of exported objects) #' #' @export metric_score.pkg_metric_exported_namespace <- function(x, ...) { 1 - 1 / (1 + exp(-0.25 * (sqrt(length(x)) - sqrt(25)))) } attributes(metric_score.pkg_metric_exported_namespace)$label <- "The number of exported objects in a package" ================================================ FILE: R/assess_has_bug_reports_url.R ================================================ #' Assess a package for the presence of a url field where bugs can be reported. #' #' @eval roxygen_assess_family( #' "has_bug_reports_url", #' "a character value containing the BugReports field contents") #' #' @export assess_has_bug_reports_url <- function(x, ...) { UseMethod("assess_has_bug_reports_url") } # assign a friendly name for assess column attr(assess_has_bug_reports_url,"column_name") <- "has_bug_reports_url" attr(assess_has_bug_reports_url,"label") <- "presence of a bug reports url in repository" #' @export assess_has_bug_reports_url.default <- function(x, ...) { pkg_metric(class = "pkg_metric_has_bug_reports_url", { length(x$bug_reports_url) }) } #' Score a package for the presence of a bug report url #' #' @eval roxygen_score_family("has_bug_reports_url") #' #' @return A logical value indicating whether the package has a BugReports field #' filled in #' #' @export metric_score.pkg_metric_has_bug_reports_url <- function(x, ...) { as.numeric(x > 0) } attributes(metric_score.pkg_metric_has_bug_reports_url)$label <- "A binary indicator of whether a package links to a location to file bug reports." ================================================ FILE: R/assess_has_examples.R ================================================ #' Assess a package for the presence of example or usage fields in function documentation #' #' @eval roxygen_assess_family( #' "has_examples", #' "an integer value indicating the proportion of discovered files with examples") #' #' @export assess_has_examples <- function(x, ...) { UseMethod("assess_has_examples") } # assign a friendly name for examples column attributes(assess_has_examples)$column_name <- "has_examples" attributes(assess_has_examples)$label <- "proportion of discovered function files with examples" #' @export assess_has_examples.pkg_ref <- function(x, ...) { pkg_metric_eval(class = "pkg_metric_has_examples",{ x$examples }) } #' Score a package for the presence of a example or usage fields #' #' Coerce a logical vector indicating availability of example or usage documentation #' #' @eval roxygen_score_family("has_examples") #' @return \code{1} if any example or usage fields are found, otherwise \code{0} #' #' @export metric_score.pkg_metric_has_examples <- function(x, ...) { if (length(x) > 0) { sum(x, na.rm = TRUE) / length(x) } else { NA } } attributes(metric_score.pkg_metric_has_examples)$label <- "A proportion of R documentation files with example or usage fields" ================================================ FILE: R/assess_has_maintainer.R ================================================ #' Assess a package for an associated maintainer #' #' @eval roxygen_assess_family( #' "has_maintainer", #' "a character vector of maintainers associated with the package") #' #' @export assess_has_maintainer <- function(x, ...) { pkg_metric_eval(class = "pkg_metric_has_maintainer", { as.character(x$maintainer) }) } attributes(assess_has_maintainer)$column_name <- "has_maintainer" attributes(assess_has_maintainer)$label <- "a vector of associated maintainers" #' Score a package for inclusion of an associated maintainer #' #' Coerce a list of maintainers into a numeric value indicating whether the #' number of listed maintainers is greater than 0. #' #' @eval roxygen_score_family("has_maintainer") #' @return \code{1} if any maintainer is provided, otherwise \code{0} #' #' @export metric_score.pkg_metric_has_maintainer <- function(x, ...) { as.numeric(length(x) > 0) } attributes(metric_score.pkg_metric_has_maintainer)$label <- "A binary indicator of whether a package has a maintainer." ================================================ FILE: R/assess_has_news.R ================================================ #' Assess a package for the presence of a NEWS file #' #' @eval roxygen_assess_family( #' "has_news", #' "an integer value indicating the number of discovered NEWS files") #' #' @export assess_has_news <- function(x, ...) { UseMethod("assess_has_news") } # assign a friendly name for assess column attributes(assess_has_news)$column_name <- "has_news" attributes(assess_has_news)$label <- "number of discovered NEWS files" #' @export assess_has_news.pkg_ref <- function(x, ...) { pkg_metric(class = "pkg_metric_has_news", { length(x$news) }) } #' Score a package for the presence of a NEWS file #' #' Coerce the number of news files to binary indication of valid NEWS files #' #' @eval roxygen_score_family("has_news") #' @return \code{1} if any NEWS files are found, otherwise \code{0} #' #' @export metric_score.pkg_metric_has_news <- function(x, ...) { as.numeric(x > 0) } attributes(metric_score.pkg_metric_has_news)$label <- "A binary indicator of whether a package has an associated NEWS file." ================================================ FILE: R/assess_has_source_control.R ================================================ #' Assess a package for an associated source control url #' #' @eval roxygen_assess_family( #' "has_source_control", #' "a character vector of source control urls associated with the package") #' #' @export assess_has_source_control <- function(x, ...) { pkg_metric_eval(class = "pkg_metric_has_source_control", { x$source_control_url }) } attributes(assess_has_source_control)$column_name <- "has_source_control" attributes(assess_has_source_control)$label <- "a vector of associated source control urls" #' Score a package for inclusion of an associated source control url #' #' Coerce a list of source control urls into a numeric value indicating whether #' the number of listed urls is greater than 0. #' #' @eval roxygen_score_family("has_source_control") #' @return \code{1} if any source control url is provided, otherwise \code{0} #' #' @export metric_score.pkg_metric_has_source_control <- function(x, ...) { as.numeric(length(x) > 0) } attributes(metric_score.pkg_metric_has_source_control)$label <- paste0( "A binary indicator of whether the package has an associated ", "version-controled repository.") ================================================ FILE: R/assess_has_vignettes.R ================================================ #' Assess a package for the presence of Vignettes files #' #' @eval roxygen_assess_family( #' "has_vignettes", #' "an integer value indicating the number of discovered vignettes files") #' #' @export assess_has_vignettes <- function(x, ...) { UseMethod("assess_has_vignettes") } # assign a friendly name for assess column attributes(assess_has_vignettes)$column_name <- "has_vignettes" attributes(assess_has_vignettes)$label <- "number of discovered vignettes files" #' @export assess_has_vignettes.pkg_ref <- function(x, ...) { pkg_metric_eval(class = "pkg_metric_has_vignettes", { length(x$vignettes) }) } #' Score a package for the presence of a Vignettes file #' #' Coerce the number of vignettes files to binary indication of valid Vignettes #' #' @eval roxygen_score_family("has_vignettes") #' @return \code{1} if any Vignettes files are found, otherwise \code{0} #' #' @export metric_score.pkg_metric_has_vignettes <- function(x, ...) { as.numeric(x > 0) } attributes(metric_score.pkg_metric_has_vignettes)$label <- "A binary indicator of whether the package has any vignettes." ================================================ FILE: R/assess_has_website.R ================================================ #' Assess a package for an associated website url #' #' @eval roxygen_assess_family( #' "has_website", #' "a character vector of website urls associated with the package") #' #' @export assess_has_website <- function(x, ...) { pkg_metric_eval(class = "pkg_metric_has_website", { x$website_urls }) } attributes(assess_has_website)$column_name <- "has_website" attributes(assess_has_website)$label <- "a vector of associated website urls" #' Score a package for inclusion of an associated website url #' #' Coerce a list of website urls into a numeric value indicating whether the #' number of listed urls is greater than 0. #' #' @eval roxygen_score_family("has_website") #' @return \code{1} if any website url is provided, otherwise \code{0} #' #' @export metric_score.pkg_metric_has_website <- function(x, ...) { as.numeric(length(x) > 0) } attributes(metric_score.pkg_metric_has_website)$label <- "A binary indicator of whether the package has an acompanying website." ================================================ FILE: R/assess_last_30_bugs_status.R ================================================ #' Assess how many recent BugReports have been closed #' #' @eval roxygen_assess_family( #' "last_30_bugs_status", #' "a logical vector indicating whether a recent BugReport was closed", #' dontrun = TRUE) #' #' @export assess_last_30_bugs_status <- function(x, ...) { pkg_metric_eval(class = "pkg_metric_last_30_bugs_status", { bug_reports_status(x) }) } attributes(assess_last_30_bugs_status)$column_name <- "bugs_status" attributes(assess_last_30_bugs_status)$label <- "vector indicating whether BugReports status is closed" bug_reports_status <- function(x, ...) { UseMethod("bug_reports_status", x$bug_reports) } bug_reports_status.github_bug_report <- function(x, ...) { vapply(x$bug_reports, "[[", character(1L), "state") == "closed" } bug_reports_status.gitlab_bug_report <- function(x, ...) { vapply(x$bug_reports, "[[", character(1L), "state") == "closed" } #' Score a package for number of recently opened BugReports that are now closed #' #' @eval roxygen_score_family("last_30_bugs_status", dontrun = TRUE) #' @return a fractional value indicating percentage of last 30 bug reports that #' are now closed #' #' @export metric_score.pkg_metric_last_30_bugs_status <- function(x, ...) { mean(x, na.rm = TRUE) } attributes(metric_score.pkg_metric_last_30_bugs_status)$label <- "The fraction of the last 30 bugs which have already been closed." ================================================ FILE: R/assess_license.R ================================================ #' Assess a package for an acceptable license #' #' @eval roxygen_assess_family( #' "license", #' "a string indicating the license under which the package is released") #' #' @export assess_license <- function(x, ...) { pkg_metric_eval(class = "pkg_metric_license", { x$license }) } attributes(assess_license)$column_name <- "license" attributes(assess_license)$label <- "software is released with an acceptable license" #' Score a package for acceptable license #' #' Maps a license string to a score #' #' @eval roxygen_score_family("license") #' #' @return score of metric license #' @export metric_score.pkg_metric_license <- function(x, ...) { # defering scoring of licenses until we have a bit more consensus or guidance NA_real_ } attributes(metric_score.pkg_metric_license)$label <- "A binary indicator of whether the package ships with an acceptable license." ================================================ FILE: R/assess_news_current.R ================================================ #' Assess a package for an up-to-date NEWS file #' #' @eval roxygen_assess_family( #' "news_current", #' "a logical vector indicating whether each discovered NEWS file is up-to-date") #' #' @export assess_news_current <- function(x, ...) { UseMethod("assess_news_current") } attributes(assess_news_current)$column_name <- "news_current" attributes(assess_news_current)$label <- "NEWS file contains entry for current version number" #' @export assess_news_current.pkg_ref <- function(x, ...) { pkg_metric(class = "pkg_metric_news_current", { grepl(search_version_string(x$version), x$news) }) } #' @export assess_news_current.pkg_remote <- function(x, ...) { pkg_metric_eval(class = "pkg_metric_news_current", { html_nodes <- lapply(x$news, xml2::xml_find_all, sprintf("//text()[contains(., '%s')]", search_version_string(x$version))) vapply(html_nodes, function(i) length(i) > 0, logical(1L)) }) } #" filter trailing 0 minor versions (e.g. 0.1.0 => "0.1") search_version_string <- function(ver) { gsub("(\\.0)+$", "", as.character(ver)) } #' Score a package for NEWS files updated to current version #' #' Coerce a logical vector of discovered up-to-date NEWS to a metric score #' #' @eval roxygen_score_family("news_current") #' @return \code{1} if any NEWS files are up-to-date, otherwise \code{0} #' #' @export metric_score.pkg_metric_news_current <- function(x, ...) { as.numeric(length(x) && all(x)) } attributes(metric_score.pkg_metric_news_current)$label <- paste0( "A binary indicator of whether the associated NEWS file has an entry for ", "the current version of the package.") ================================================ FILE: R/assess_r_cmd_check.R ================================================ #' Assess a package's results from running R CMD check #' #' @eval roxygen_assess_family( #' "r_cmd_check", #' "Tally of errors, warnings and notes from running R CMD check locally", #' dontrun = TRUE) #' @export assess_r_cmd_check <- function(x, ...) { UseMethod("assess_r_cmd_check") } attributes(assess_r_cmd_check)$column_name <- "r_cmd_check" attributes(assess_r_cmd_check)$label <- "Package check results" #' @export assess_r_cmd_check.default <- function(x, ...) { as_pkg_metric_na(pkg_metric(class = "pkg_metric_r_cmd_check", message = "Source code not available to run R CMD check on.")) } #' @export assess_r_cmd_check.pkg_source <- function(x, ...) { pkg_metric_eval(class = "pkg_metric_r_cmd_check", { sapply(x$r_cmd_check[c("notes","errors","warnings")], length) }) } #' @export assess_r_cmd_check.pkg_cran_remote <- function(x, ...) { as_pkg_metric_todo(pkg_metric(class = "pkg_metric_r_cmd_check", message = "Assessment of R CMD check on remote pkg refs is not yet implemented but will be in the future")) } #' @export assess_r_cmd_check.pkg_bioc_remote <- function(x, ...) { as_pkg_metric_todo(pkg_metric(class = "pkg_metric_r_cmd_check", "Assessment of R CMD check on remote pkg refs is not yet implemented but will be in the future")) } #' Score a package based on R CMD check results run locally #' #' The scoring function is the weighted sum of notes (0.1), errors (1) and warnings (0.25), with a maximum score of 1 (no errors, notes or warnings) #' and a minimum score of 0. #' Essentially, the metric will allow up to 10 notes, 1 error or 4 warnings before returning the lowest score of 0 #' @eval roxygen_score_family("r_cmd_check", dontrun = TRUE) #' @return A weighted sum of errors and warnings of all tests preformed #' #' @export metric_score.pkg_metric_r_cmd_check <- function(x, ...) { 1 - min(c(sum(x*c(0.1, 1, 0.25)), 1)) } attributes(metric_score.pkg_metric_r_cmd_check)$label <- "A weighted sum of errors/warnings/notes from R CMD Check" ================================================ FILE: R/assess_remote_checks.R ================================================ #' Assess package checks from CRAN/Bioc or R CMD check #' #' @eval roxygen_assess_family( #' "remote_checks", #' "Tally of R CMD check results run on differnt OS flavors by BioC or CRAN", #' dontrun = TRUE) #' #' @export assess_remote_checks <- function(x, ...) { UseMethod("assess_remote_checks") } attr(assess_remote_checks, "column_name") <- "remote_checks" attributes(assess_remote_checks)$label <- "Number of OS flavors that passed/warned/errored on R CMD check" #' @export assess_remote_checks.default <- function(x, ...) { as_pkg_metric_na(pkg_metric(class="pkg_metric_remote_checks", message="Package is not a CRAN or BioC reference so there are no CRAN/BioC checks to assess")) } #' @export assess_remote_checks.pkg_cran_remote <- function(x, ...) { pkg_metric_eval(class = "pkg_metric_remote_checks",{ table(factor(x$remote_checks[["Status"]], levels = c("OK","WARN","ERROR", "NOTE", "FAIL"))) }) } #' @export assess_remote_checks.pkg_bioc_remote <- function(x, ...) { pkg_metric_eval(class = "pkg_metric_remote_checks", { table(factor(x$remote_checks[["CHECK"]], levels=c("OK","WARNINGS","ERROR","TIMEOUT"))) }) } #' Score a package based on R CMD check results run by BioC or CRAN #' #' The scoring function is the number of OS flavors that passed with OK or NOTES + 0.5*the number of OS's that produced WARNINGS divided by the number of OS's checked #' @eval roxygen_score_family("remote_checks", dontrun = TRUE) #' @return a fractional value indicating percentage OS flavors that did not produce an error or warning from R CMD check #' #' @export metric_score.pkg_metric_remote_checks <- function(x, ...) { unname((x["OK"] + (x[grepl("NOTE", names(x))] *0.75) + (x[grepl("WARN", names(x))] *0.5))/sum(x)) } attributes(metric_score.pkg_metric_remote_checks)$label <- "Weighted sum of OS flavor R CMD check results" ================================================ FILE: R/assess_reverse_dependencies.R ================================================ #' Generate list of Reverse Dependencies for a package #' #' @details The more packages that depend on a package the more chance #' for errors/bugs to be found #' #' @eval roxygen_assess_family( #' "reverse_dependencies", #' "A character vector of reverse dependencies") #' #' @export assess_reverse_dependencies <- function(x, ...){ UseMethod("assess_reverse_dependencies") } #' @importFrom devtools revdep #' @export assess_reverse_dependencies.default <- function(x, ...){ pkg_metric_eval(class = "pkg_metric_reverse_dependencies", devtools::revdep(x$name, bioconductor = TRUE) ) } attr(assess_reverse_dependencies, "column_name") <- "reverse_dependencies" attr(assess_reverse_dependencies, "label") <- "List of reverse dependencies a package has" #' Scoring method for number of reverse dependencies a package has #' #' Score a package for the number of reverse dependencies it has; regularized #' Convert the number of reverse dependencies \code{length(x)} into a validation #' score [0,1] \deqn{ 1 / (1 + exp(-0.5 * (sqrt(length(x)) + sqrt(5)))) } #' #' The scoring function is the classic logistic curve \deqn{ #' 1 / (1 + exp(-k(x-x[0])) } with a square root scale for the number of reverse dependencies #' \eqn{x = sqrt(length(x))}, sigmoid midpoint is 5 reverse dependencies, ie. \eqn{x[0] = #' sqrt(5)}, and logistic growth rate of \eqn{k = 0.5}. #' #' \deqn{ 1 / (1 + -0.5 * exp(sqrt(length(x)) - sqrt(5))) } #' @eval roxygen_score_family("reverse_dependencies", dontrun = TRUE) #' @return numeric value between \code{1} (high number of reverse dependencies) and #' \code{0} (low number of reverse dependencies) #' #' @export metric_score.pkg_metric_reverse_dependencies <- function(x,...){ 1 / (1 + exp(-0.5 * (sqrt(length(x)) - sqrt(5)))) } attributes(metric_score.pkg_metric_reverse_dependencies)$label <- "The (log10) number of packages that depend on this package." ================================================ FILE: R/assess_size_codebase.R ================================================ #' Assess a package for size of code base #' #' @eval roxygen_assess_family( #' "size_codebase", #' "a numeric value for number of lines of code base for a package") #' #' @export assess_size_codebase <- function(x, ...) { UseMethod("assess_size_codebase") } attributes(assess_size_codebase)$column_name <- "size_codebase" attributes(assess_size_codebase)$label <- "number of lines of code base" #' @export assess_size_codebase.default <- function(x, ...) { as_pkg_metric_na( pkg_metric(class = "pkg_metric_size_codebase"), message = sprintf("Cannot compute the number of lines of code from a %s", x$source)) } #' @export assess_size_codebase.pkg_install <- function(x, ...) { pkg_metric_eval(class = "pkg_metric_size_codebase", { # create character vector of exported function exports <- getNamespaceExports(x$name) # add package to the search path attachNamespace(x$name) # count number of lines for exported function through the mget function nloc <- capture.output(mget(exports, envir = as.environment(paste0("package:", x$name)))) # sum the number of lines and extract the 4 extra lines (function name, bytecode, environment, blank line) length(nloc) - 4*length(exports) }) } #' @export assess_size_codebase.pkg_source <- function(x, ...) { pkg_metric_eval(class = "pkg_metric_size_codebase", { # create character vector of function files files <- list.files( path = file.path(x$path, "R"), pattern = "\\.R$", full.names = TRUE, ignore.case = TRUE ) # define the function for counting code base count_lines <- function(x){ # read the lines of code into a character vector code_base <- readLines(x) # count all the lines n_tot <- length(code_base) # count lines for roxygen headers starting with # n_head <- length(grep("^#+", code_base)) # count the comment lines with leading spaces n_comment <- length(grep("^\\s+#+", code_base)) # count the line breaks or only white space lines n_break <- length(grep("^\\s*$", code_base)) # compute the line of code base n_tot - (n_head + n_comment + n_break) } # count number of lines for all functions nloc <- sapply(files, count_lines) # sum the number of lines sum(nloc) }) } #' Score a package for number of lines of code #' #' Scores packages based on its codebase size, as determined by number of lines of code. #' #' @eval roxygen_score_family("size_codebase") #' #' @return numeric value between \code{0} (for large codebase) and \code{1} (for small codebase) #' @export metric_score.pkg_metric_size_codebase <- function(x, ...) { 1.5 / (x / 1e2 + 1.5) } attributes(metric_score.pkg_metric_size_codebase)$label <- "A logistic rating of the number of lines of code in a package." ================================================ FILE: R/dev_hint.R ================================================ #' dev_hint <- function(title, text, format_args = list()) { #' if (is.character(text)) text <- list(dev_hint_section(text)) #' data <- list( #' title = title, #' text = text, #' format_args = format_args) #' structure(data, class = c("dev_hint", class(data))) #' } #' #' #' #' dev_hint_section <- function(text, format_args = list(wrap = TRUE)) { #' structure(text, #' format_args = format_args, #' class = c("dev_hint_section", class(text))) #' } #' #' #' #' dev_hint_crayon_title_style <- crayon::make_style(rgb(0.1, 0.2, 0.6)) #' dev_hint_crayon_title <- function(...) #' crayon::bold(dev_hint_crayon_title_style(...)) #' #' dev_hint_crayon_link <- crayon::make_style(rgb(0.1, 0.6, 0.4)) #' dev_hint_crayon_success <- crayon::make_style(rgb(0.15, 0.7, 0.15)) #' dev_hint_crayon_failure <- crayon::make_style(rgb(0.8, 0.2, 0.2)) #' #' #' #' #' @importFrom utils modifyList #' #' @export #' print.dev_hint <- function(x, ...) { #' dots <- utils::modifyList(x$format_args, list(...)) #' cat(do.call(format, c(list(x), dots))) #' } #' #' #' #' #' @export #' format.dev_hint <- function(x, use_crayon = TRUE, wrap = FALSE, ...) { #' text_refs <- lapply(x$text, strip_dev_hint_section_links) #' text <- lapply(text_refs, "[[", "text") #' refs <- unlist(lapply(text_refs, "[[", "refs")) #' #' refs <- paste0( #' dev_hint_crayon_link("[", seq_along(refs), "] ", refs, sep = ""), #' collapse = "\n") #' #' text <- append(text, list(dev_hint_section(refs, list(wrap = FALSE)))) #' text <- lapply(text, function(i) { #' do.call(format, append(list(i), attr(i, "format_args"))) #' }) #' #' paste0(dev_hint_crayon_title(x$title), "\n", paste(text, collapse = "\n\n")) #' } #' #' #' strip_dev_hint_section_links <- function(x, use_crayon = TRUE, ref_index_start = 1) { #' # find markdown links in text #' md_link_re <- "(?\\[(?[^]]+)\\]\\((?[^)]+)\\))" #' md_link <- gregexpr(md_link_re, x, perl = TRUE)[[1]] #' md_link_start <- attr(md_link, "capture.start") #' md_link_end <- md_link_start + attr(md_link, "capture.length") - 1 #' #' # match named regex groups #' if (all(md_link == -1)) { #' refs <- matrix(ncol = ncol(attr(md_link, "capture.start")))[c(),] #' } else { #' refs <- matrix(substring(x, #' first = md_link_start, #' last = md_link_end), #' ncol = ncol(attr(md_link, "capture.start"))) #' } #' colnames(refs) <- attr(md_link, "capture.names") #' #' # break text on md links #' text <- substring(x, #' first = c(0, md_link_start[,"link"]), #' last = c(md_link_start[,"link"] - 1, 1e6L)) #' #' # remove md links #' text[-1] <- substring(text[-1], #' first = attr(md_link, "capture.length")[,"link"] + 1, #' last = 1e6L) #' #' # add back in link text #' text[-1] <- paste0(refs[,"text"], #' if (nrow(refs)) #' dev_hint_crayon_link("[", ref_index_start + seq_along(refs[,"text"]) - 1, "]", sep = ""), #' text[-1]) #' text <- paste(text, collapse = "") #' #' # rejoin with original attributes #' attributes(text) <- attributes(x) #' #' # return both modified text, as well as scraped references #' list( #' text = text, #' refs = refs[,"url"]) #' } #' #' #' format.dev_hint_section <- function(x, use_crayon = TRUE, wrap = TRUE) { #' if (wrap) #' paste0(fansi::strwrap_ctl(x, indent = 2, exdent = 2), collapse = "\n") #' else #' paste0(" ", strsplit(x, "\n")[[1]], collapse = "\n") #' } ================================================ FILE: R/dev_tips.R ================================================ #' #' Provide tips for improving a metric #' #' #' #' @param x a pkg_metric object #' #' @param ... additional arguments unused #' #' #' #' @export #' dev_tips <- function(x, ...) { #' UseMethod("dev_tips") #' } ================================================ FILE: R/metric_score.R ================================================ #' Score a package metric #' #' Convert a package metric into a numeric value between 0 to 1 #' #' @param x A \code{pkg_metric_*} class object to score #' @param ... Additional arguments unused #' #' @return score of a package risk metric #' @export #' metric_score <- function(x, ...) { if (inherits(x, "pkg_metric_condition")) return(metric_score_condition(x, ...)) UseMethod("metric_score") } #' @export metric_score.default <- function(x, ...) { if (!inherits(x, "pkg_metric")) { warning(sprintf(paste0( "Don't know how to score object of class %s. score is only intended ", "to be used with objects inheriting class 'pkg_metric', ", "returning default score of 0."), paste0('"', class(x), '"', collapse = ", "))) } else { warning(sprintf(paste0( "no available scoring algorithm for metric of class %s, ", "returning default score of 0."), paste0('"', class(x)[1], '"'))) } 0L } metric_score_condition <- function(x, ...) { UseMethod("metric_score_condition") } metric_score_condition.pkg_metric_error <- function(x, ..., error_handler = score_error_default) { error_handler(x, ...) } metric_score_condition.pkg_metric_na <- function(x, ...) { structure(NA_real_, class = c("pkg_score_na", "numeric")) } metric_score_condition.pkg_metric_error <- function(x, ...) { structure(NA_real_, class = c("pkg_score_error", "numeric")) } metric_score_condition.pkg_metric_todo <- function(x, ...) { structure(NA_real_, class = c("pkg_score_todo", "numeric")) } #' Default score error handling, emitting a warning and returning 0 #' #' @inheritParams metric_score #' @return a value of package score #' @export score_error_default <- metric_score.default #' Score error handler to silently return 0 #' #' @inheritParams metric_score #' #' @return a value of package score #' @export score_error_zero <- function(...) 0 #' Score error handler to silently return NA #' #' @inheritParams metric_score #' @return a value of package score #' @export score_error_NA <- function(...) NA_real_ ================================================ FILE: R/options.R ================================================ riskmetric.options <- list( gitlab_api_host = "https://gitlab.com/api/v4", github_api_host = "https://api.github.com" ) ================================================ FILE: R/pkg_assess.R ================================================ #' Helper for creating a roxygen header from template for assess_* functions #' #' @param name the name of the assessment, assuming naming conventions are #' followed #' @param return_type an optional added commentary about the return type of the #' assessment function #' @param dontrun logical indicating whether examples should be wrapped in #' a dontrun block. This is particularly useful for assessments which may #' require an internet connection. #' #' @return roxygen section template for assess family functions #' #' @examples #' \dontrun{ #' #' @eval roxygen_assess_family( #' #' "has_news", #' #' "an integer value indicating the number of discovered NEWS files") #' } #' #' @keywords internal roxygen_assess_family <- function(name, return_type = "an atomic assessment result", dontrun = TRUE) { assess_func <- sprintf("assess_%s", name) score_func <- sprintf("metric_score.pkg_metric_%s", name) example_template <- if (dontrun) { "@examples \n\\dontrun{\nassess_%s(pkg_ref(\"%s\"))\n}" } else { "@examples assess_%s(pkg_ref(\"%s\"))" } c("@param x a \\code{pkg_ref} package reference object", "@param ... additional arguments passed on to S3 methods, rarely used", sprintf("@return a \\code{pkg_metric} containing %s", return_type), sprintf("@seealso \\code{\\link{%s}}", score_func), sprintf(example_template, name, packageName())) } #' Helper for creating a roxygen itemized list for assess_* functions #' #' @return roxygen section template for assess family function catalog #' #' @examples #' \dontrun{ #' #' @eval assess_family_catalog_roxygen() #' } #' @keywords internal roxygen_assess_family_catalog <- function() { assessments <- all_assessments() info <- lapply(assessments, attr, "label") missing_label <- vapply(info, is.null, logical(1L)) info[missing_label] <- names(info)[missing_label] c("@section Assessment function catalog:", "\\describe{", sprintf('\\item{\\code{\\link{%s}}}{%s}', names(info), info), "}") } #' A default list of assessments to perform for each package #' #' @return a list of assess_* functions exported from riskmetric #' #' @importFrom utils packageName #' @export all_assessments <- function() { fs <- grep("^assess_[^.]*$", getNamespaceExports(utils::packageName()), value = TRUE) Map(getExportedValue, fs, ns = list(utils::packageName())) } #' Get a specific set of assess_* functions for pkg_assess #' #' @param fxn_string vector of assess functions #' @return a list of specific assess_* functions exported from riskmetric #' #' @importFrom utils packageName #' @export get_assessments <- function(fxn_string=""){ Map(getExportedValue, fxn_string, ns = list(utils::packageName())) } #' Helper for retrieving a list of columns which contain pkg_metric objects #' #' @param tbl a \code{\link[tibble]{tibble}} to select columns among #' #' @return a logical vector of \code{pkg_metric} column indices #' @keywords internal get_assessment_columns <- function(tbl) { vapply(tbl, inherits, logical(1L), "list_of_pkg_metric") } #' reassign assignment list names with column_name attribute if available #' #' @param x list of columns for which to consider friendly column name #' attributes #' #' @return a vector of friendly column names if available #' @keywords internal use_assessments_column_names <- function(x) { column_names <- lapply(x, attr, "column_name") colname_null <- vapply(column_names, is.null, logical(1L)) names(x)[!colname_null] <- column_names[!colname_null] names(x)[!nchar(names(x))] <- paste0("unnamed", seq_along(x[!nchar(names(x))])) x } #' Apply assess_* family of functions to a package reference #' #' By default, use all \code{assess_*} funtions in the \code{riskmetric} #' namespace and produce a \code{\link[tibble]{tibble}} with one column per #' assessment applied. #' #' @param x A single \code{\link{pkg_ref}} object or #' \code{\link[tibble]{tibble}} of package references to assess #' @param assessments A list of assessment functions to apply to each package #' reference. By default, a list of all exported assess_* functions from the #' riskmetric package. #' @param ... additional arguments unused #' @param error_handler A function, which accepts a single parameter expecting #' the raised error, which will be called if any errors occur when attempting #' to apply an assessment function. #' #' @return Either a \code{list_of_pkg_metric} object when a single #' \code{pkg_ref} object is passed as \code{x}, or a #' \code{\link[tibble]{tibble}} of metrics when a \code{list_of_pkg_ref} or #' \code{tibble} is passed as \code{x}. When a \code{\link[tibble]{tibble}} #' is returned, it has one row per package reference and a new column per #' assessment function, with cells of that column as package metric objects #' returned when the assessment was called with the associated pacakge #' reference. #' #' @eval roxygen_assess_family_catalog() #' #' #' @importFrom tibble as_tibble #' @importFrom vctrs new_list_of #' @export pkg_assess <- function(x, assessments = all_assessments(), ..., error_handler = assessment_error_empty) { UseMethod("pkg_assess") } #' @export pkg_assess.pkg_ref <- function(x, assessments = all_assessments(), ..., error_handler = assessment_error_empty) { assessments <- use_assessments_column_names(assessments) xout <- list() for (i in seq_along(assessments)) { assessment_f <- assessments[[i]] assessment_name <- names(assessments)[[i]] xout[[assessment_name]] <- tryCatch({ assessment_f(x) }, error = function(e) { error_handler(e, x$name, assessment_name) }) attributes(xout[[assessment_name]])$label <- attributes(assessment_f)$label } vctrs::new_list_of(xout, structure(logical(), class = "pkg_metric"), class = "list_of_pkg_metric") } #' @export pkg_assess.list_of_pkg_ref <- function(x, assessments = all_assessments(), ..., error_handler = assessment_error_empty) { pkg_assess(tibble::as_tibble(x), assessments = assessments, error_handler = error_handler) } #' @export pkg_assess.tbl_df <- function(x, assessments = all_assessments(), ..., error_handler = assessment_error_empty) { assessments <- use_assessments_column_names(assessments) for (i in seq_along(assessments)) { assessment_f <- assessments[[i]] assessment_name <- names(assessments)[[i]] x[[assessment_name]] <- lapply(x$pkg_ref, function(pkg_ref) { tryCatch({ assessment_f(pkg_ref) }, error = function(e) { error_handler(e, pkg_ref$name, assessment_name) }) }) x[[assessment_name]] <- vctrs::new_list_of(x[[assessment_name]], structure(logical(), class = "pkg_metric"), class = "list_of_pkg_metric") attributes(x[[assessment_name]])$label <- attributes(assessment_f)$label } x } ================================================ FILE: R/pkg_cohort.R ================================================ #' @family pkg_ref pkg_cohort <- function() { structure( list(), class = "pkg_cohort" ) } ================================================ FILE: R/pkg_metric.R ================================================ #' A helper for structuring assessment return objects for dispatch with the #' score function #' #' @param x data to store as a \code{pkg_metric} #' @param ... additional attributes to bind to the \code{pkg_metric} object #' @param class a subclass to differentiate the \code{pkg_metric} object #' #' @return a \code{pkg_metric} object #' #' @export pkg_metric <- function(x = NA, ..., class = c()) { if (is.null(x)) x <- list() structure(x, ..., class = c(class, "pkg_metric", class(x))) } #' Convert an object to a \code{pkg_metric} #' #' @inheritParams pkg_metric #' @return a \code{pkg_metric} object #' @export as_pkg_metric <- function(x, class = c()) { UseMethod("as_pkg_metric") } #' @export as_pkg_metric.default <- function(x, class = c()) { pkg_metric(x, class = class) } #' @export as_pkg_metric.expr_output <- function(x, class = c()) { x_metric <- pkg_metric(x, class = class) if (is_error(x)) x_metric <- as_pkg_metric_error(x_metric) x_metric } #' Evaluate a metric #' #' Evalute code relevant to a metric, capturing the evaluated code as well as #' any messages, warnings or errors that are thrown in the process. #' #' @param expr An expression to evaluate in order to calculate a #' \code{pkg_metric} #' @param env An environment in which \code{expr} is to be evaluated #' @inheritParams pkg_metric #' #' @return a \code{pkg_metric} object containing the result of \code{expr} #' @keywords internal pkg_metric_eval <- function(expr, ..., class = c(), env = parent.frame()) { out <- capture_expr_output(substitute(expr), env = env, quoted = TRUE) out_metric <- as_pkg_metric(out, class = class) if (inherits(out, "error")) out_metric <- as_pkg_metric_error(out_metric) out_metric } #' @importFrom vctrs vec_ptype_abbr #' @method vec_ptype_abbr pkg_metric #' @export vec_ptype_abbr.pkg_metric <- function(x, ...) { "pkg_metric" } #' @export format.pkg_metric_error <- function(x, ...) { class_str <- gsub("^pkg_metric_", "", class(x)[[1]]) pillar::style_na(paste0("<", class_str, ">")) } #' @export format.pkg_metric <- function(x, ...) { class_str <- gsub("^pkg_metric_", "", class(x)[[1]]) data_str <- with_unclassed_to(x, "pkg_metric", pillar::pillar_shaft(x)) paste0(capture.output(data_str), collapse = "") } ================================================ FILE: R/pkg_metric_condition.R ================================================ #' A pkg_metric subclass for general metric evaluation conditions #' #' @param x an object to wrap in a \code{pkg_metric_condition} class #' @param ... additional arguments added as attributes to object \code{x} #' @param subclass an optional subclass of \code{pkg_metric_condition} to #' include #' #' @return an object after wrap \code{pkg_metric_condition} class. #' @keywords internal as_pkg_metric_condition <- function(x, ..., subclass = c()) { dots <- list(...) if (length(names(dots)) != length(dots)) stop("All ellipsis arguments must be named") dots <- dots[setdiff(names(dots), "class")] attributes(x)[names(dots)] <- dots class(x) <- c(subclass, "pkg_metric_condition", class(x)) x } ================================================ FILE: R/pkg_metric_error.R ================================================ #' A subclass wrapping an error with an additional parent class #' #' @param error an error condition object to capture #' #' @return an error condition object after wrap \code{pkg_metric_error} class. #' @keywords internal as_pkg_metric_error <- function(error) { as_pkg_metric_condition(error, subclass = "pkg_metric_error") } #' Error handler for assessments with safe fallback #' #' @inheritParams format_assessment_message #' @param ... additional arguments unused #' #' @return a pkg_metric object of pkg_metric_error subclass #' #' @family assessment error handlers #' #' @export assessment_error_empty <- function(e, ...) { as_pkg_metric_error(pkg_metric(e)) } #' Error handler for assessments to throw error immediately #' #' @inheritParams format_assessment_message #' @return the error encountered during assessment #' #' @family assessment error handlers #' #' @export assessment_error_throw <- function(e, name, assessment) { stop(format_assessment_message(e, name, assessment)) } #' Error handler for assessments to deescalate errors to warnings #' #' @inheritParams format_assessment_message #' @inherit assessment_error_empty return #' #' @family assessment error handlers #' #' @export assessment_error_as_warning <- function(e, name, assessment) { warning(format_assessment_message(e, name, assessment), call. = FALSE) assessment_error_empty(e) } #' Assessment console printing formatter #' #' make the errors and warnings consistent with meaningful indication of what #' triggered the error, including the name of the package whose reference #' triggered the error while running which asesessment. #' #' @param e an error raised during a package reference assessment #' @param name the name of the package whose package reference assessment raised #' the error #' @param assessment the name of the assessment function which raised the error #' @return a character string of formatted text to communicate the error #' #' @importFrom utils capture.output #' @keywords internal format_assessment_message <- function(e, name, assessment) { out <- "In " if (!missing(name)) out <- paste0(out, "package '", name, "' ") if (!missing(assessment)) out <- paste0(out, "while assessing '", assessment, "' ") paste0(out, "`", paste(utils::capture.output(e$call), collapse = " "), "` : \n", e$message) } #' @importFrom pillar pillar_shaft new_pillar_shaft_simple style_na #' @method pillar_shaft pkg_metric_error #' @export pillar_shaft.pkg_metric_error <- function(x, ...) { pillar::new_pillar_shaft_simple(pillar::style_na(paste0( "<", gsub("pkg_metric_", "", class(x)[[1]]), ">"))) } ================================================ FILE: R/pkg_metric_na.R ================================================ #' A pkg_metric subclass for when metrics are explicitly not applicable #' #' @param x a \code{pkg_metric} object to wrap in a \code{pkg_metric_na} #' subclass #' @param message an optional message explaining why a metric is not applicable. #' @return a \code{pkg_metric} object after wrap in a \code{pkg_metric_na} #' @keywords internal as_pkg_metric_na <- function(x, message = NULL) { as_pkg_metric_condition(x, message = message, subclass = "pkg_metric_na") } ================================================ FILE: R/pkg_metric_todo.R ================================================ #' A pkg_metric subclass for when pkg_metrics have not yet been implemented #' #' @param x a \code{pkg_metric} object to wrap in a \code{pkg_metric_todo} #' subclass #' @param message an optional message directing users and potential contributors #' toward any ongoing work or first steps toward development. #' @return a \code{pkg_metric} object after wrap in a \code{pkg_metric_todo} #' @keywords internal as_pkg_metric_todo <- function(x, message = NULL) { as_pkg_metric_condition(x, message = message, subclass = "pkg_metric_todo") } ================================================ FILE: R/pkg_ref_cache.R ================================================ #' S3 generic to calculate a `pkg_ref` field #' #' Reactively retrieve and cache `pkg_ref` metadata #' #' @section Caching Details: #' \subsection{\code{pkg_ref} class fields}{ #' The \code{pkg_ref} class structures an environment with special handling #' for indexing into the \code{pkg_ref} class using the \code{$} or \code{[[} #' operators. For all intents and purposes, the \code{pkg_ref} class is works #' conceptually similar to a lazy, immutable \code{list}, and uses the #' \code{pkg_ref_cache} function internally to lazily retrieve package #' reference fields. #' } #' \subsection{Lazy metadata caching}{ #' Laziness in a \code{pkg_ref} object refers to the delayed evaluation of the #' contents of its fields. Since some metadata is time or computationally #' intensive to retrieve, and unnessary for some assessments, we want to avoid #' that retrieval until it is needed. #' #' The first time that a field is accessed within a \code{pkg_ref} object #' \code{x}, a corresponding \code{pkg_ref_cache} S3 generic is called. For #' example, when \code{x$description} is first accessed, the \code{pkg_ref} #' object uses the function \code{pkg_ref_cache.description} to attempt to #' retrieve the contents of the corresponding \code{DESCRIPTION} file. #' #' Often, the way that this data is collected might be different depending on #' the subclass of the \code{pkg_ref}. In the case of the \code{description} #' metadata, a reference to a local install might be able to read in a local #' file directly, whereas a reference to a remote source of metadata might #' require first downloading the file. For this reason, many #' \code{pkg_ref_cache.*} functions are themselves S3 generics that dispatch #' on the class of the \code{pkg_ref} object, allowing for divergent behaviors #' for different source of package metadata. #' } #' \subsection{\code{pkg_ref} field immutability}{ #' Once a field has been calculated, its value is immutable. This behavior was #' chosen because of the long time frame over which package metadata changes, #' rendering it unnecessary to continually reevaluate fields each time they #' are accesssed. #' #' This means that within an assessment, a given field for a package will only #' ever be calculated once and preserved for downstream use. #' } #' #' @return a \code{pkg_ref} field #' @examples #' \dontrun{ #' # implementing a new field called "first_letter" that is consistently derived #' # across all pkg_ref objects: #' #' pkg_ref_cache.first_letter <- function(x, name, ...) { #' substring(x$name, 1, 1) #' } #' #' x <- pkg_ref("riskmetric") #' x$first_letter #' #' #' #' # implementing a new field called "subclass_enum" that dispatches on #' # the subclass of the pkg_ref object: #' #' pkg_ref_cache.subclass_enum <- function(x, name, ...) { #' UseMethod("pkg_ref_cache.subclass_enum") #' } #' #' pkg_ref_cache.subclass_enum.pkg_ref <- function(x, name, ...) { #' 0 #' } #' #' pkg_ref_cache.subclass_enum.pkg_install <- function(x, name, ...) { #' 1 #' } #' #' x$subclass_enum #' } #' #' @rdname riskmetric_metadata_caching #' @name pkg_ref_cache NULL #' A helper function for retrieving a list of available fields, identified based #' on implementation of a pkg_ref_cache method for a given class. #' #' @param x a package reference object #' @return a list of available fields implemented with a pkg_ref_cache method #' #' @importFrom utils .S3methods #' @keywords internal available_pkg_ref_fields <- function(x) { fs <- c(names(getNamespace(packageName())), utils::.S3methods("pkg_ref_cache")) # build data.frame by function name and S3 dispatch (to 2 levels of dispatch) fs_df <- as.data.frame(t(vapply(strsplit(fs, "\\."), `[`, character(3L), 1:3))) names(fs_df) <- c("func", "field", "class") fs_df <- fs_df[fs_df$func == "pkg_ref_cache",] fs_df <- Filter(function(method) { # filter for functions that implement an S3 for a subclass or don't have # any dispatched functions (all dispatched classes are NA). any(method$class %in% c(class(x), "default")) || all(is.na(method$class)) }, Filter(nrow, split(fs_df, fs_df$field))) fs <- names(fs_df) fs <- fs[order(fs)] fs } #' @param x a package reference object #' @param name the name of the field that needs to be cached #' @param ... additional arguments used for computing cached values #' @param .class a class name to use for S3 dispatch, defaulting to the name as #' a character value #' #' @return a value to assign to the new field in the package reference object #' environment #' #' @family package reference cache #' #' @rdname riskmetric_metadata_caching #' @keyworks internal #' @noRd pkg_ref_cache <- function(x, name, ..., .class = as.character(name)) { UseMethod("pkg_ref_cache", structure(list(), class = .class)) } ================================================ FILE: R/pkg_ref_cache_NEWS_urls.R ================================================ #' Cache appropriate urls for NEWS files #' #' @inheritParams pkg_ref_cache #' @family package reference cache #' @return a \code{pkg_ref} object #' @keywords internal #' @noRd pkg_ref_cache.news_urls <- function(x, name, ...) { UseMethod("pkg_ref_cache.news_urls") } #' @importFrom xml2 xml_attrs pkg_ref_cache.news_urls.pkg_cran_remote <- function(x, name, ...) { # scrape CRAN html for NEWS links news_links <- xml2::xml_find_all(x$web_html, xpath = '//a[.="NEWS"]') # add NEWS link url metadata to package environment sprintf("%s/%s", x$web_url, vapply(xml2::xml_attrs(news_links), "[", character(1L), "href")) } #' @importFrom xml2 xml_attrs pkg_ref_cache.news_urls.pkg_bioc_remote <- function(x, name, ...) { # scrape Bioconductor package webpage for NEWS links relative_path <- sprintf("../news/%s/NEWS", x$name) news_link_xpath <- sprintf('//a[@href="%s"]', relative_path) news_links <- xml2::xml_find_all(x$web_html, xpath = news_link_xpath) # add NEWS link url metadata to package environment xml2::url_absolute( vapply(xml2::xml_attrs(news_links), "[", character(1L), "href"), x$web_url) } ================================================ FILE: R/pkg_ref_cache_archive_release_date.R ================================================ #' Cache a List of Archived Package Release Date from a Package Reference #' #' @inheritParams pkg_ref_cache #' @family package reference cache #' @return a \code{pkg_ref} object #' @keywords internal #' @noRd pkg_ref_cache.archive_release_dates <- function(x, name, ...) { UseMethod("pkg_ref_cache.archive_release_dates") } pkg_ref_cache.archive_release_dates.pkg_cran_remote <- function(x, name, ...) { url <- sprintf("%s/src/contrib/Archive/%s", x$repo_base_url, x$name) html <- httr::content(httr::GET(url)) node <- xml2::xml_find_first(html, "//pre") text <- unlist(strsplit(xml2::xml_text(node), "\n")) db <- do.call(rbind, strsplit(text[-1], "\\s+")) version <- gsub(paste0(x$name, "_(.*)\\.tar\\.gz"), "\\1", db[,2]) date <- db[,3] cbind(name = x$name, version, date) } ================================================ FILE: R/pkg_ref_cache_behaviors.R ================================================ #' List of available caching behaviors with metadata, including default and #' annotations for building documentation #' #' @return a list contain cache behaviros information #' @keywords internal cache_behaviors <- list( "per_package_request" = list( default = function() interactive(), annotation = paste0( "requires a web request for each package individually, which can be time ", "intensive and an irresponsible use of generously hosted public R ", "package repositories. It is recommended that this behavior is disabled ", "for metric assessments of large numbers of packages or assessments ", "triggered via automated scripts. Automatically disabled by default for ", "non-interactive use.")) ) #' Document both declare_cache_behavior parameters and options list #' #' @param fmt format of cache behavior entries #' @param name_fmt special formating for name (first) component #' @param annotation_fmt special formating for annotation (second) component #' @param wrap_fmt a wrapper for the entirety of the roxygen entries #' @param collapse passed to paste #' @return a string #' @keywords internal roxygen_cache_behaviors <- function(fmt = "%s: %s", name_fmt = "%s", annotation_fmt = "%s", wrap_fmt = "%s", collapse = "\n") { cache_behavior_names <- sprintf(name_fmt, names(cache_behaviors)) cache_behavior_annotations <- sprintf(annotation_fmt, vapply(cache_behaviors, "[[", character(1L), "annotation")) sprintf(wrap_fmt, paste( sprintf(fmt, cache_behavior_names, cache_behavior_annotations), collapse = collapse)) } #' Stop if a function requires disabled behaviors #' #' @param behaviors a character vector of behavior flags to assert as #' requirements for metadata caching. values must have an entry found in #' riskmetric:::cache_behaviors list #' #' @return a boolean value #' @keywords internal require_cache_behaviors <- function(behaviors) { stopifnot(all(behaviors %in% names(cache_behaviors))) opt_names <- paste0(packageName(), ".", behaviors) names(opt_names) <- opt_names behaviors_disabled <- Filter(isFALSE, lapply(opt_names, getOption)) if (length(behaviors_disabled)) { e <- simpleError(message = paste( "package metadata caching requires behaviors disabled by option(s)", paste0('"', names(behaviors_disabled), '"', collapse = ", "))) class(e) <- c("riskmetric_disabled_behavior_error", class(e)) stop(e) } } ================================================ FILE: R/pkg_ref_cache_bug_reports.R ================================================ #' Retrieve a list of BugReports metadata #' #' @inheritParams pkg_ref_cache #' @family package reference cache #' @return a \code{pkg_ref} object #' @keywords internal #' @noRd pkg_ref_cache.bug_reports <- function(x, ...) { UseMethod("pkg_ref_cache.bug_reports") } pkg_ref_cache.bug_reports.default <- function(x, ...) { scrape_bug_reports(x, ...) } #' Helper for structuring bug reports #' #' @param bug_reports_data data to represent a bug report history - generally a #' return object from making a request to a repository's issues API #' @param x a \code{pkg_ref} object where a \code{bug_reports_host} field can be #' found #' @return a \code{bug_reports_host} field #' @keywords internal bug_report_metadata <- function(bug_reports_data, x) { structure(bug_reports_data, class = c( paste0(x$bug_reports_host, "_bug_report"), "bug_report", class(bug_reports_data))) } # Helper for scraping bug reports depending on url host name scrape_bug_reports <- function(x, ...) { disp_class <- x$bug_reports_host %||% "NULL" UseMethod("scrape_bug_reports", structure(list(), class = disp_class)) } scrape_bug_reports.default <- function(x, ...) { if (is.null(x$bug_reports_host) || length(x$bug_reports_host) == 0L) stop("package DESCRIPTION does not have a BugReports field") else stop(sprintf( "scraping bug reports fromm BugReports host '%s' not implemented", x$bug_reports_host)) } #' @importFrom httr GET content #' @keywords internal scrape_bug_reports.github <- function(x, ...) { owner_repo_issues <- gsub( ".*github[^/]*/([^/]+/[^/]+).*", "\\1", x$bug_reports_url) resp <- httr::GET(sprintf( "%s/repos/%s/issues?state=all&per_page=%s", getOption("riskmetric.github_api_host"), owner_repo_issues, 30)) out <- httr::content(resp, as = "parsed") bug_report_metadata(out, x) } #' @importFrom httr GET content #' @importFrom urltools url_encode #' @keywords internal scrape_bug_reports.gitlab <- function(x, ...) { owner_repo_issues <- gsub(".*gitlab[^/]*/(.*)", "\\1", x$bug_reports_url) owner_repo <- gsub("(.*)/issues", "\\1", owner_repo_issues) resp <- httr::GET(sprintf( "%s/projects/%s/issues?per_page=%s", getOption("riskmetric.gitlab_api_host"), url_encode(owner_repo), 30)) out <- httr::content(resp, as = "parsed") bug_report_metadata(out, x) } ================================================ FILE: R/pkg_ref_cache_bug_reports_host.R ================================================ #' Get the host name of a BugReports url #' #' @inheritParams pkg_ref_cache #' @family package reference cache #' @return a \code{pkg_ref} object #' @keywords internal #' @noRd pkg_ref_cache.bug_reports_host <- function(x, ...) { UseMethod("pkg_ref_cache.bug_reports_host") } #' Get the host name of a BugReports url #' #' @importFrom urltools domain #' @return a \code{pkg_ref} object #' @keywords internal pkg_ref_cache.bug_reports_host.default <- function(x, ...) { if (is.null(x$bug_reports_url)) return(NULL) sapply(strsplit(domain(x$bug_reports_url), "\\."), function(dm) dm[length(dm)-1]) } ================================================ FILE: R/pkg_ref_cache_bug_reports_url.R ================================================ #' Get the BugReports url #' #' @inheritParams pkg_ref_cache #' @family package reference cache #' @return a \code{pkg_ref} object #' @keywords internal #' @noRd pkg_ref_cache.bug_reports_url <- function(x, ...) { UseMethod("pkg_ref_cache.bug_reports_url") } #' Get the BugReports url #' #' @importFrom utils packageDescription #' @return a \code{pkg_ref} object #' @keywords internal pkg_ref_cache.bug_reports_url.pkg_source <- function(x, ...) { # fake a library location given the package source code path where the # description can be found, revert on exit when no longer needed packageDescription(x$name, dirname(x$path))$BugReports } #' @importFrom utils packageDescription #' @keywords internal pkg_ref_cache.bug_reports_url.pkg_install <- function(x, ...) { packageDescription(x$name)$BugReports } #' @importFrom xml2 xml_find_all xml_attr #' @keywords internal pkg_ref_cache.bug_reports_url.pkg_cran_remote <- function(x, ...) { # scrape CRAN package webpage for BugReports links bug_reports_xpath <- "//td[.='BugReports:']/following::td[1]/a" bug_reports_link <- xml_find_all(x$web_html, xpath = bug_reports_xpath) xml_attr(bug_reports_link, "href") } #' @importFrom xml2 xml_find_all xml_attr #' @keywords internal pkg_ref_cache.bug_reports_url.pkg_bioc_remote <- function(x, ...) { # scrape CRAN package webpage for BugReports links bug_reports_xpath <- "//td[.='BugReports']/following::td[1]/a" bug_reports_link <- xml_find_all(x$web_html, xpath = bug_reports_xpath) xml_attr(bug_reports_link, "href") } ================================================ FILE: R/pkg_ref_cache_covr_coverage.R ================================================ #' Retrieve output of covr::package_coverage #' #' @inheritParams pkg_ref_cache #' @family package reference cache #' @return a \code{pkg_ref} object #' @keywords internal #' @noRd pkg_ref_cache.covr_coverage <- function(x, ...) { UseMethod("pkg_ref_cache.covr_coverage") } #' Retrieve output of covr::package_coverage #' #' @importFrom tools testInstalledPackage #' @importFrom covr package_coverage #' @return a \code{pkg_ref} object #' @keywords internal pkg_ref_cache.covr_coverage.pkg_source <- function(x, ...) { # use custom 'code' to avoid triggering errors upon test failure. # practically identical to covr::package_coverage with the exclusion of # `if (result != 0L) show_failures(out_dir)` expr <- bquote(tools::testInstalledPackage(.(x$name), types = 'tests')) cnsl <- capture_expr_output({ res <- covr::package_coverage( path = x$path, type = "none", code = deparse(expr)) }) res } ================================================ FILE: R/pkg_ref_cache_description.R ================================================ #' Cache the DESCRIPTION file contents for a package reference #' #' #' @inheritParams pkg_ref_cache #' @family package reference cache #' @return a \code{pkg_ref} object #' @keywords internal #' @noRd pkg_ref_cache.description <- function(x, name, ...) { UseMethod("pkg_ref_cache.description") } pkg_ref_cache.description.pkg_install <- function(x, name, ...) { read.dcf(file.path(x$path, "DESCRIPTION")) } pkg_ref_cache.description.pkg_source <- function(x, name, ...) { read.dcf(file.path(x$path, "DESCRIPTION")) } ================================================ FILE: R/pkg_ref_cache_downloads.R ================================================ #' Cache a list of available help files as LaTeX objects #' @param n Number of days to look back with default value of 365 days #' #' @inheritParams pkg_ref_cache #' @family package reference cache #' #' @importFrom cranlogs cran_downloads #' @return a \code{pkg_ref} object #' @keywords internal #' @noRd pkg_ref_cache.downloads <- function(x, ..., n=365) { cran_downloads(x$name, from=Sys.Date()-n, to=Sys.Date()) } ================================================ FILE: R/pkg_ref_cache_examples.R ================================================ #' Cache the examples available for exported objects for a package reference #' #' @inheritParams pkg_ref_cache #' @family package reference cache #' @return a \code{pkg_ref} object #' @keywords internal #' @noRd pkg_ref_cache.examples <- function(x, name, ...) { UseMethod("pkg_ref_cache.examples") } pkg_ref_cache.examples.pkg_install <- function(x, name, ...) { examples_from_pkg(x$name) } pkg_ref_cache.examples.pkg_source <- function(x, name, ...) { examples_from_dir(x$path, x$name) } #' Filter a simple database of Rd objects in a package for files with example fields #' #' @param rddb a simple database of Rd object obtained via tools::Rd_db #' #' @return a vector of Rd file names that have example fields #' @keywords internal filter_rd_db <- function(rddb) { n <- names(rddb) examples <- lapply(n, function(i) { rd <- rddb[[i]] a <- gsub("\\}", "", gsub("\\\\(examples|example|usage)\\{", "", rd[grep("^\\\\(examples|example|usage)", rd)] ) ) man_name <- i man_name <- rep(man_name, length(a)) names(man_name) <- a return(man_name) }) # !duplicated because unique removes names e <- unlist(examples)[!duplicated(unlist(examples))] e } #' Build logical vector for Rd objects with example or usage fields discovered in a given package #' #' @param pkg a package name expected to contain exported objects #' #' @return a numeric proportion of documentation files with examples #' @keywords internal examples_from_pkg <- function(pkg) { f <- tools::Rd_db(package = pkg) # omit whole package rd f <- f[!names(f) %in% c(paste0(pkg, "-package.Rd"), paste0(pkg,".Rd"))] rd_all <- names(f) e <- filter_rd_db(f) rd_all %in% e } #' Build logical vector for Rd objects with example or usage fields discovered in a given directory #' #' @param path a package directory path expected to contain exported objects #' #' @return a numeric proportion of documentation files with examples #' @keywords internal examples_from_dir <- function(path, pkg) { f <- tools::Rd_db(dir = path) # omit whole package rd f <- f[!names(f) %in% c(paste0(pkg, "-package.Rd"), paste0(pkg,".Rd"))] rd_all <- names(f) e <- filter_rd_db(f) rd_all %in% e } ================================================ FILE: R/pkg_ref_cache_expr_coverage.R ================================================ #' Retrieve output of covr::package_coverage, tallied by expression #' #' @inheritParams pkg_ref_cache #' @family package reference cache #' @return a \code{pkg_ref} object #' @keywords internal #' @noRd pkg_ref_cache.expression_coverage <- function(x, ...) { UseMethod("pkg_ref_cache.expr_coverage") } #' Retrieve output of covr::package_coverage, tallied by expression #' #' @importFrom covr tally_coverage #' @return a \code{pkg_ref} object #' @keywords internal pkg_ref_cache.expression_coverage.pkg_source <- function(x, ...) { covr::tally_coverage(x$covr_coverage, by = "expression") } ================================================ FILE: R/pkg_ref_cache_help.R ================================================ #' Cache a list of available help files as LaTeX objects #' #' @inheritParams pkg_ref_cache #' @family package reference cache #' @return a \code{pkg_ref} object #' @keywords internal #' @noRd pkg_ref_cache.help <- function(x, name, ...) { UseMethod("pkg_ref_cache.help") } #' Cache a list of available help files as LaTeX objects #' #' @importFrom tools Rd_db parseLatex #' @return a \code{pkg_ref} object #' @keywords internal pkg_ref_cache.help.pkg_install <- function(x, name, ...) { tools::Rd_db(package = x$name) } #' Cache a list of available help files as LaTeX objects #' #' @importFrom tools Rd_db parseLatex #' @keywords internal pkg_ref_cache.help.pkg_source <- function(x, name, ...) { tools::Rd_db(dir = x$path) } ================================================ FILE: R/pkg_ref_cache_help_aliases.R ================================================ #' Cache a character vector mapping exported values to documentation filenames #' #' @inheritParams pkg_ref_cache #' @family package reference cache #' @return a \code{pkg_ref} object #' @keywords internal #' @noRd pkg_ref_cache.help_aliases <- function(x, name, ...) { UseMethod("pkg_ref_cache.help_aliases") } pkg_ref_cache.help_aliases.pkg_install <- function(x, name, ...) { readRDS(file.path(x$path, "help", "aliases.rds")) } pkg_ref_cache.help_aliases.pkg_source <- function (x, name, ...) { f <- list.files(file.path(x$path, "man"), full.names = TRUE) f <- f[grep("\\.Rd$", f)] aliases <- lapply(f, function(i) { rd <- readLines(i) a <- gsub("\\}", "", gsub("\\\\alias\\{", "", rd [grep("^\\\\alias", rd)])) man_name <- strsplit (strsplit (i, "\\/man\\/") [[1]] [2], "\\.Rd") [[1]] man_name <- rep (man_name, length (a)) names (man_name) <- a return (man_name) }) # !duplicated because unique removes names unlist(aliases) } ================================================ FILE: R/pkg_ref_cache_license.R ================================================ #' Get the package license #' #' @inheritParams pkg_ref_cache #' @family package reference cache #' @return a \code{pkg_ref} object #' @keywords internal #' @noRd pkg_ref_cache.license <- function(x, ...) { UseMethod("pkg_ref_cache.license") } pkg_ref_cache.license.default <- function(x, ...) { if ("License" %in% colnames(x$description)) unname(x$description[,"License"]) else NA_character_ } #' @importFrom xml2 xml_find_all xml_text #' @keywords internal pkg_ref_cache.license.pkg_cran_remote <- function(x, ...) { license_xpath <- "//td[.='License:']/following::td[1]" license_nodes <- xml_find_all(x$web_html, xpath = license_xpath) xml_text(license_nodes) } #' @importFrom xml2 xml_find_all xml_text #' @keywords internal pkg_ref_cache.license.pkg_bioc_remote <- function(x, ...) { license_xpath <- "//td[.='License']/following::td[1]" license_nodes <- xml_find_all(x$web_html, xpath = license_xpath) xml_text(license_nodes) } ================================================ FILE: R/pkg_ref_cache_maintainer.R ================================================ #' Cache package's Maintainer #' #' @inheritParams pkg_ref_cache #' @family package reference cache #' @return a \code{pkg_ref} object #' @keywords internal #' @noRd pkg_ref_cache.maintainer <- function(x, name, ...) { UseMethod("pkg_ref_cache.maintainer") } pkg_ref_cache.maintainer.pkg_remote <- function(x, name, ...) { maintainer_xpath <- "//td[.='Maintainer:']/following::td[1]" maintainer <- xml2::xml_text(xml2::xml_find_all(x$web_html, maintainer_xpath)) maintainer } pkg_ref_cache.maintainer.pkg_install <- function(x, name, ...) { if ("Maintainer" %in% colnames(x$description)) return(x$description[,"Maintainer"]) a <- if ("Author" %in% colnames(x$description)) x$description[,"Author"] else NA a_r <- if ("Authors@R" %in% colnames(x$description)) x$description[,"Authors@R"] else NA if (!is.na(a_r)) { a_r_exp <- parse(text = a_r) if (all(all.names(a_r_exp, unique = TRUE) %in% c("c", "person"))) { return(grep("cre", eval(a_r_exp), value = TRUE)) } } else if (!is.na(a)) { return(trimws(strsplit(a, ","))[[1]]) } NA } pkg_ref_cache.maintainer.pkg_source <- pkg_ref_cache.maintainer.pkg_install ================================================ FILE: R/pkg_ref_cache_news.R ================================================ #' Cache a list of NEWS files from a package reference #' #' @inheritParams pkg_ref_cache #' @family package reference cache #' @return a \code{pkg_ref} object #' @keywords internal #' @noRd pkg_ref_cache.news <- function(x, name, ...) { UseMethod("pkg_ref_cache.news") } #' Cache a list of NEWS files from a package reference #' #' @importFrom httr content GET #' @return a \code{pkg_ref} object #' @keywords internal pkg_ref_cache.news.pkg_remote <- function(x, name, ...) { # default encoding messages suppressed suppressMatchingConditions( lapply(x$news_urls, function(news_url) { response <- httr::GET(news_url) httr::content( response, type = response$headers$`content-type` %||% "text/html") }), messages = "default") } pkg_ref_cache.news.pkg_install <- function(x, name, ...) { news_from_dir(system.file(package = x$name)) } pkg_ref_cache.news.pkg_source <- function(x, name, ...) { news_from_dir(x$path) } #' Build a list of NEWS files discovered within a given directory #' #' @param path a package directory path expected to contain NEWS files #' #' @return a list of parsed NEWS files #' @keywords internal news_from_dir <- function(path) { # accommodate news.Rd, news.md, etc files <- list.files(path, pattern = "^NEWS($|\\.)", full.names = TRUE) if (!length(files)) return(list()) content <- rep(list(NULL), length(files)) names(content) <- files valid <- vector(length(files), mode = "logical") # attempt to parse all news.* files for (i in seq_along(files)) { f <- files[[i]] ext <- tools::file_ext(f) tryCatch({ if (tolower(tools::file_ext(f)) == "rd") { content[[i]] <- .tools()$.news_reader_default(f) } else if (tolower(ext) == "md" || nchar(ext) == 0L) { # NOTE: should we do validation of markdown format? content[[i]] <- readLines(f, warn = FALSE) } valid[[i]] <- TRUE }, error = function(e) { valid[[i]] <- FALSE }) } # NOTE: should we test whether news file is up-to-date with latest version? content[valid] } ================================================ FILE: R/pkg_ref_cache_r_cmd_check.R ================================================ #' Run R CMD check and capture the results #' #' @inheritParams pkg_ref_cache #' @family package reference cache #' @return a \code{pkg_ref} object #' @keywords internal #' @noRd pkg_ref_cache.r_cmd_check <- function(x, ...) { UseMethod("pkg_ref_cache.r_cmd_check") } pkg_ref_cache.r_cmd_check.default <- function (x, ...) { return(NA) } #' Run R CMD check and capture the results #' #' @inheritParams pkg_ref_cache #' @importFrom devtools check #' @return a \code{pkg_ref} object #' @noRd pkg_ref_cache.r_cmd_check.pkg_source <- function(x, ...){ check_results <- devtools::check(x$path, quiet=TRUE) return(check_results) } ================================================ FILE: R/pkg_ref_cache_release_date.R ================================================ #' Cache a List of Package Release Date from a Package Reference #' #' @inheritParams pkg_ref_cache #' @family package reference cache #' @return a \code{pkg_ref} object #' @keywords internal #' @noRd pkg_ref_cache.release_date <- function(x, name, ...) { UseMethod("pkg_ref_cache.release_date") } pkg_ref_cache.release_date.pkg_remote <- function(x, name, ...) { release_xpath <- "//td[.='Published:']/following::td[1]" date <- xml2::xml_text(xml2::xml_find_all(x$web_html, release_xpath)) date } pkg_ref_cache.release_date.pkg_install <- function(x, name, ...) { if (!"Date" %in% colnames(x$description)) return(NA) x$description[, "Date"] } pkg_ref_cache.release_date.pkg_source <- pkg_ref_cache.release_date.pkg_install ================================================ FILE: R/pkg_ref_cache_remote_checks.R ================================================ #' Retrieve a CRAN or Bioc checks or run R CMD check #' #' @inheritParams pkg_ref_cache #' @family package reference cache #' @return a \code{pkg_ref} object #' @keywords internal #' @noRd pkg_ref_cache.remote_checks <- function (x, ...) { UseMethod("pkg_ref_cache.remote_checks") } pkg_ref_cache.remote_checks.default <- function (x, ...) { return(NA) } #' @importFrom httr content GET #' @importFrom xml2 xml_find_all xml_text pkg_ref_cache.remote_checks.pkg_cran_remote <- function(x, ...) { webURL <- sprintf("%s/web/checks/check_results_%s.html", x$repo_base_url, x$name) page <- httr::content(httr::GET(webURL)) tables <- xml2::xml_find_all(page, ".//table") table_cran <- xml2::xml_find_all(tables[[1]], "//tr") fields <- lapply(table_cran, xml2::xml_find_all, ".//td|.//th") fields <- lapply(fields, xml2::xml_text, trim = TRUE) rst <- as.data.frame(do.call(rbind, fields[-1])) colnames(rst) <- fields[[1]] return(rst) } #' @importFrom httr content GET #' @importFrom xml2 xml_find_all xml_text pkg_ref_cache.remote_checks.pkg_bioc_remote <- function(x, ...) { webURL <- sprintf("%s/%s", x$repo_base_url, x$name) # TODO: # refine x$repo_base_url for BioConductor packages so that we don't need to do # nasty substitutions like this webURL <- sub("packages/release/bioc[^/]*", "checkResults/release/bioc-LATEST", webURL) page <- httr::content(httr::GET(webURL)) tables <- xml2::xml_find_all(page, ".//table") rows <- xml2::xml_find_all(tables[[3]], "//tr") rows <- rows[grepl("odd", xml2::xml_attr(rows, "class"))] fields <- lapply(rows, xml2::xml_find_all, ".//td|.//th") fields <- lapply(fields, function(x) x[grepl("node|status", xml2::xml_attr(x, "class"))]) text <- lapply(fields, xml2::xml_text, trim = TRUE) rst <- as.data.frame(do.call(rbind, text)) colnames(rst) <- sapply(xml2::xml_find_all(rows[[1]], ".//td"), xml2::xml_text, trim = TRUE)[-1] return(rst) } ================================================ FILE: R/pkg_ref_cache_repo_base_url.R ================================================ #' Cache value of a package's source repo's URL #' #' @inheritParams pkg_ref_cache #' @family package reference cache #' @return a \code{pkg_ref} object #' @keywords internal #' @noRd pkg_ref_cache.repo_base_url <- function(x, name, ...) { UseMethod("pkg_ref_cache.repo_base_url") } pkg_ref_cache.repo_base_url.pkg_remote <- function(x, name, ...) { gsub("/src/contrib$", "", x$repo) } ================================================ FILE: R/pkg_ref_cache_source_control_url.R ================================================ #' Cache package's Source Control URL #' #' @inheritParams pkg_ref_cache #' @family package reference cache #' @return a \code{pkg_ref} object #' @keywords internal #' @noRd pkg_ref_cache.source_control_url <- function(x, name, ...) { grep( "(github\\.com|bitbucket\\.org|gitlab\\.com)", x$website_urls, value = TRUE) } ================================================ FILE: R/pkg_ref_cache_tarball_url.R ================================================ #' Cache value of a package's source tarball URL #' #' @inheritParams pkg_ref_cache #' @family package reference cache #' @return a \code{pkg_ref} object #' @keywords internal #' @noRd pkg_ref_cache.tarball_url <- function(x, name, ...) { UseMethod("pkg_ref_cache.tarball_url") } pkg_ref_cache.tarball_url.pkg_remote <- function(x, name, ...) { sprintf("%s/%s_%s.tar.gz", x$repo, x$name, x$version) } ================================================ FILE: R/pkg_ref_cache_vignettes.R ================================================ #' Cache a List of Vignettes Files from a Package Reference #' #' @inheritParams pkg_ref_cache #' @family package reference cache #' @return a \code{pkg_ref} object #' @keywords internal #' @noRd pkg_ref_cache.vignettes <- function(x, name, ...) { UseMethod("pkg_ref_cache.vignettes") } pkg_ref_cache.vignettes.pkg_remote <- function(x, name, ...) { vignettes_from_html(x) } pkg_ref_cache.vignettes.pkg_install <- function(x, name, ...) { vignettes_from_dir(system.file(package = x$name)) } pkg_ref_cache.vignettes.pkg_source <- function(x, name, ...) { vignettes_from_dir(x$path) } #' Build a List of Vignettes Files Discovered Within a Given Directory #' #' @param path a package directory path expected to contain Vignettes files #' #' @return a vector of parsed Vignettes files #' @keywords internal vignettes_from_dir <- function(path) { folder <- c(source = "/vignettes", bundle = "/inst/doc", binary = "/doc") files <- unlist(lapply(paste0(path, folder), list.files, full.names = TRUE)) if (!length(files)) return(data.frame()) file_path = unique(tools::file_path_sans_ext(files)) filename = basename(file_path) names(file_path) <- filename file_path[tolower(filename) != tolower("index")] } #' Build a List of Vignettes Files Discovered Within a Package Website #' #' @param x a \code{pkg_ref} object #' #' @return a vector of Vignettes files #' #' @importFrom xml2 xml_attrs #' @importFrom tools file_path_sans_ext #' @keywords internal vignettes_from_html <- function(x) { nodes <- xml2::xml_find_all(x$web_html, xpath = '//a[contains(@href,"vignettes")]') if (!length(nodes)) return(c()) file_path <- unlist(xml2::xml_attrs(nodes, "href")) filename <- tools::file_path_sans_ext(basename(file_path)) file_path <- sprintf("%s/%s", x$web_url, file_path) names(file_path) <- filename file_path } ================================================ FILE: R/pkg_ref_cache_web_html.R ================================================ #' Cache package's remote display page HTML #' #' @inheritParams pkg_ref_cache #' @family package reference cache #' @return a \code{pkg_ref} object #' @keywords internal #' @noRd pkg_ref_cache.web_html <- function(x, name, ...) { UseMethod("pkg_ref_cache.web_html") } #' @importFrom httr content GET pkg_ref_cache.web_html.pkg_remote <- function(x, name, ...) { # suppress messages when httr assumes a default content parameters suppressMatchingConditions( httr::content(httr::GET(x$web_url)), messages = "default") } ================================================ FILE: R/pkg_ref_cache_web_url.R ================================================ #' Cache package's remote web URL #' #' @inheritParams pkg_ref_cache #' @family package reference cache #' @return a \code{pkg_ref} object #' @keywords internal #' @noRd pkg_ref_cache.web_url <- function(x, name, ...) { UseMethod("pkg_ref_cache.web_url") } pkg_ref_cache.web_url.pkg_cran_remote <- function(x, name, ...) { sprintf("%s/web/packages/%s", x$repo_base_url, x$name) } pkg_ref_cache.web_url.pkg_bioc_remote <- function(x, name, ...) { sprintf("%s/html/%s.html", x$repo_base_url, x$name) } ================================================ FILE: R/pkg_ref_cache_website_urls.R ================================================ #' Cache package's Website URL #' #' @inheritParams pkg_ref_cache #' @family package reference cache #' @return a \code{pkg_ref} object #' @keywords internal #' @noRd pkg_ref_cache.website_urls <- function(x, name, ...) { UseMethod("pkg_ref_cache.website_urls") } pkg_ref_cache.website_urls.pkg_remote <- function(x, name, ...) { url_xpath <- "//td[.='URL:']/following::td[1]/a" url <- xml2::xml_text(xml2::xml_find_all(x$web_html, url_xpath)) if(length(url) == 0) return(character(0L)) url } pkg_ref_cache.website_urls.default <- function(x, name, ...) { if (!"URL" %in% colnames(x$description)) return(character(0L)) trimws(strsplit(x$description[,"URL"], ",")[[1]]) } ================================================ FILE: R/pkg_ref_class.R ================================================ #' Create a package reference #' #' Create a package reference from package name or filepath, producing an object #' in which package metadata will be collected as risk assessments are #' performed. Depending on where the package was found - whether it is found as #' source code, in a local library or from a remote host - an S3 subclass is #' given to allow for source-specific collection of metadata. See 'Details' for #' a breakdown of subclasses. Different sources can be specified by passing a #' subclass as an arguemnt named 'source', see details. #' #' Package reference objects are used to collect metadata pertaining to a given #' package. As data is needed for assessing a package's risk, this metadata #' populates fields within the package reference object. #' #' The \code{pkg_ref} S3 subclasses are used extensively for divergent metadata #' collection behaviors dependent on where the package was discovered. Because #' of this, there is a rich hierarchy of subclasses to articulate the different #' ways package information can be found. #' #' A source argument can be passed using the `source` argument. This will #' override the logic that riskmetric does when determining a package source. #' This can be useful when you are scoring the most recent version present on a #' repository, or testing a specific library. #' #' \describe{ #' \item{\strong{\code{pkg_ref}}}{ A default class for general metadata #' collection.} #' \item{\strong{\code{pkg_source}}}{ A reference to a source code #' directory.} #' \item{\strong{\code{pkg_install}}}{ A reference to a package installation #' location in a package library. A specific library can be passed by #' passing the path to the library as the parameter `lib.loc`} #' \item{\strong{\code{pkg_remote}}}{ A reference to package metadata on a #' remote server. #' \describe{ #' \item{\strong{\code{pkg_cran_remote}}}{ A reference to package #' information pulled from the CRAN repository.} #' \item{\strong{\code{pkg_bioc_remote}}}{ A reference to package #' information pulled from the Bioconductor repository.} #' \item{\strong{\code{pkg_git_remote}}}{ A reference to a package source #' code git repository. (not yet implemented)} #' } #' } #' } #' #' @section Package Cohorts: #' #' *Experimental!* #' Package cohorts are structures to determine the risk of a set of packages. #' `pkg_library()` can be called to create a object containing the pkg_ref #' objects of all packages in a system library. #' #' #' @rdname pkg_ref #' @export #' #' @examples #' \dontrun{ #' # riskmetric will check for installed packages by default #' ref_1 <- pkg_ref("utils") #' ref_1$source # returns 'pkg_install' #' #' # lib.loc can be used to specify a library for pkg_install #' ref_3 <- pkg_ref("utils", source = "pkg_install", lib.loc = .libPaths()[1]) #' #' # You can also override this behavior with a source argument #' ref_2 <- pkg_ref("utils", source = "pkg_cran_remote") #' ref_2$source # returns 'pkg_cran_remote' #' } pkg_ref <- function(x, ...) { if (missing(x)) { return(structure(logical(0L), class = "pkg_ref")) } as_pkg_ref(x, ...) } #' @importFrom vctrs new_vctr #' @keywords internal new_pkg_ref <- function(name, version = NA_character_, source, ...) { dots <- list(...) if (length(dots) && is.null(names(dots)) || any(names(dots) == "")) { stop("pkg_ref ellipses arguments must be named") } source <- match.arg( source, c( "pkg_git_remote", "pkg_bioc_remote", "pkg_cran_remote", "pkg_remote", "pkg_install", "pkg_source", "pkg_missing" ), several.ok = TRUE ) source <- get_pkg_ref_classes(source) pkg_data <- as.environment(append( list( name = name, version = version, source = source[[1L]] ), dots )) structure( pkg_data, class = c(source, class(pkg_data)) ) } #' The `pkg_ref` subclass hierarchy, used for pkg_ref object creation with a #' specified subclass #' pkg_ref_class_hierarchy <- list( "pkg_ref" = list( "pkg_missing", "pkg_source", "pkg_install", "pkg_remote" = list( "pkg_cran_remote", "pkg_bioc_remote", "pkg_git_remote" ) ) ) #' Walk the pkg_ref class hierarchy to match a single subclass to a class path #' #' @param x (`character(1L)`) A subclass, among those known in pkg_ref subclasses #' @param classes (`list`) A class hierarchy, described using a named list. #' Defaults to `pkg_ref_class_hierarchy`. #' #' @return A `character(n)` class path from `pkg_ref` down to the specified #' subclass, or `FALSE` if no path is found. #' @keywords internal #' get_pkg_ref_classes <- function(x, classes = pkg_ref_class_hierarchy) { if (x %in% names(classes) || x %in% classes) { return(x) } if (!is.list(classes)) { return(FALSE) } for (i in seq_along(classes)) { subclasses <- get_pkg_ref_classes(x, classes[[i]]) if (is.character(subclasses)) return(c(subclasses, names(classes[i]))) } FALSE } #' @rdname pkg_ref #' #' @param lib.loc The path to the R library directory of the installed package. pkg_install <- function(x, lib.loc = NULL) { if (verify_pkg_source(x, "pkg_install") == "pkg_missing") { return(pkg_missing(x)) } path <- find.package(x, lib.loc = lib.loc) version <- utils::packageVersion(x, lib.loc = dirname(path)) new_pkg_ref(x, version = version, path = path, source = "pkg_install") } #' @rdname pkg_ref pkg_source <- function(x) { desc <- read.dcf(file.path(x, "DESCRIPTION")) name <- unname(desc[, "Package"]) new_pkg_ref( name, version = desc[, "Version"][[1]], path = normalizePath(x), source = "pkg_source" ) } #' @rdname pkg_ref #' #' @param repos URL of CRAN repository to pull package metadata. pkg_cran <- function( x, repos = getOption("repos", "https://cran.rstudio.com") ) { ap <- memoise_available_packages(repos = repos) info <- ap[ap[, "Package"] == x, , drop = FALSE] new_pkg_ref( x, version = info[, "Version"], repo = info[, "Repository"], source = c("pkg_cran_remote") ) } #' @rdname pkg_ref pkg_bioc <- function(x) { bp <- memoise_bioc_available() info <- bp[bp[, "Package"] == x, , drop = FALSE] new_pkg_ref( x, version = info[, "Version"], repo = "https://bioconductor.org/packages/release/bioc", source = c("pkg_bioc_remote") ) } #' @rdname pkg_ref pkg_missing <- function(x) { new_pkg_ref(x, source = c("pkg_missing")) } #' @rdname pkg_ref pkg_library <- function(lib.loc) { # Create pkg_cohort object cohort <- pkg_cohort() for (pkg in list.files(lib.loc, recursive = FALSE, full.names = FALSE)) { cohort[[length(cohort) + 1]] <- pkg_install(pkg, lib.loc = lib.loc) } cohort } #' Convert into a package object #' #' @param x A singular \code{character} value, \code{character vector} or #' \code{list} of \code{character} values of package names or source code #' directory paths. #' @param ... Additional arguments passed to methods. #' #' @return When a single value is provided, a single \code{pkg_ref} object is #' returned, possibly with a subclass based on where the package was found. If #' a \code{vector} or \code{list} is provided, a \code{list_of_pkg_ref} object #' constructed with \code{\link[vctrs]{list_of}} is returned, which can be #' considered analogous to a \code{list}. See 'Details' for further #' information about \code{pkg_ref} subclasses. #' #' @rdname pkg_ref #' #' @importFrom vctrs new_list_of #' @export as_pkg_ref <- function(x, ...) { if ((is.list(x) || is.atomic(x)) && length(x) > 1) { dots <- list(...) # iterate over the list of packages and add sources and versions pkg_ref_list <- list() for (i in seq_along(x)) { if (!is.null(dots$source)) { source <- ifelse(length(dots$source) > 1, dots$source[i], dots$source) } else { source <- NULL } pkg_ref_list[[i]] <- as_pkg_ref(x[[i]], source = source) } return(vctrs::new_list_of( pkg_ref_list, ptype = list(), class = "list_of_pkg_ref" )) } else { UseMethod("as_pkg_ref") } } #' @export as_pkg_ref.default <- function(x, ...) { stop(sprintf( "Don't know how to convert object class '%s' to class 'pkg_ref'", paste(class(x), collapse = ", ") )) } #' @export as_pkg_ref.pkg_ref <- function(x, ...) { x } #' @importFrom utils available.packages packageVersion #' @export as_pkg_ref.character <- function( x, repos = getOption("repos", "https://cran.rstudio.com"), source = NULL, lib.loc = NULL, ... ) { dots <- list(...) pkg_source_ <- ifelse( is.null(source), determine_pkg_source(x, source, repos), verify_pkg_source(x, source, repos) ) stopifnot( pkg_source_ %in% c( "pkg_install", "pkg_source", "pkg_cran_remote", "pkg_bioc_remote", "pkg_missing" ) ) switch( pkg_source_, pkg_install = pkg_install(x, lib.loc = lib.loc), pkg_source = pkg_source(x), pkg_cran_remote = pkg_cran(x, repos = repos), pkg_bioc_remote = pkg_bioc(x), pkg_missing = pkg_missing(x) ) } #' Determine the intended source of a new package #' #' @param x Package name or path to package #' @param source type of source passed in `pkg_ref` #' @return one of c('pkg_source', 'pkg_install', 'pkg_cran_remote', #' 'pkg_bioc_remote', 'pkg_missing') #' @keywords internal determine_pkg_source <- function(x, source, repos) { if (dir.exists(x) && file.exists(file.path(x, "DESCRIPTION"))) { "pkg_source" # non-source package } else if (grepl("^[[:alpha:]][[:alnum:].]*[[:alnum:]]$", x)) { if (length(find.package(x, quiet = TRUE)) != 0) { return("pkg_install") # if its not installed, pull the package to check it } else { ap <- memoise_available_packages(repos = repos) info <- ap[ap[, "Package"] == x, , drop = FALSE] p <- new_pkg_ref( x, version = info[, "Version"], repo = info[, "Repository"], source = c("pkg_remote") ) } if (is_available_cran(x, repos, p)) { "pkg_cran_remote" } else if (is_available_bioc(x, p)) { "pkg_bioc_remote" } else { "pkg_missing" } } else { stop(sprintf("can't interpret character '%s' as a package reference", x)) } } #' Verify a pkg_source when one is manually specified by the user #' @return a string of package source #' @keywords internal verify_pkg_source <- function(x, source, repos) { switch( source, pkg_install = "pkg_install", pkg_source = { # check source pakcage is present if source is "pkg_source" if (source == "pkg_source" && !dir.exists(x)) { warning(paste0(c( "Package source: `", x, "` does not exist, source is now 'pkg_missing'" ))) return("pkg_missing") } }, pkg_cran_remote = { ap <- memoise_available_packages(repos = repos) info <- ap[ap[, "Package"] == x, , drop = FALSE] p <- new_pkg_ref( x, version = info[, "Version"], repo = info[, "Repository"], source = c("pkg_remote") ) if (!is_available_cran(x, repos, p)) { warning(paste0(c( "Package: `", x, "` not found on CRAN, source is now 'pkg_missing'" ))) return("pkg_missing") } }, pkg_bioc_remote = { ap <- memoise_available_packages(repos = repos) info <- ap[ap[, "Package"] == x, , drop = FALSE] p <- new_pkg_ref( x, version = info[, "Version"], repo = info[, "Repository"], source = c("pkg_remote") ) if (!is_available_bioc(x, p)) { warning(paste0(c( "Package: `", x, "` not found on bioconductor, source is now 'pkg_missing'" ))) return("pkg_missing") } }, source ) source } ================================================ FILE: R/pkg_ref_class_coersion.R ================================================ #' @importFrom tibble as_tibble #' @method as_tibble pkg_ref #' @export as_tibble.pkg_ref <- function(x, ...) { as_tibble(vctrs::new_list_of(list(x), ptype = list(), class = "list_of_pkg_ref")) } #' @importFrom tibble tibble #' @method as_tibble list_of_pkg_ref #' @export as_tibble.list_of_pkg_ref <- function(x, ...) { package_names <- vapply(x, "[[", character(1L), "name") versions <- vapply(x, function(xi) as.character(xi$version), character(1L)) tibble::tibble( package = package_names, version = versions, pkg_ref = x) } ================================================ FILE: R/pkg_ref_class_extract.R ================================================ #' @export `$.pkg_ref` <- function(x, name) { `[[`(x, as.character(name)) } #' @export `$<-.pkg_ref` <- function(x, name, value) { `[[<-`(x, as.character(name), value = value) } #' Lazily instantiated, immutable metadata access #' #' If errors are thrown upon instantiation, they are saved and rethrown any time #' the value is attempted to be accessed. These then propegate through #' assessment and scoring functions to affect any downstream metrics. #' #' @param x pkg_ref object to extract metadata from #' @param name name of metadata field to extract #' @param ... additional arguments used to extract from internal environment #' #' @return a pkg_ref object #' @export #' @keywords internal `[[.pkg_ref` <- function(x, name, ...) { if (!name %in% bare_env(x, names(x))) { allow_mutation(x, { pkg_ref_cache(x, name) ret <- tryCatch(pkg_ref_cache(x, name), error = function(e) e) x[[name]] <- ret if (inherits(ret, "error")) stop(ret) ret }) } else { bare_env(x, { ret <- x[[name, ...]] if (inherits(ret, "error")) stop(ret) ret }) } } #' @export `[[<-.pkg_ref` <- function(x, name, value) { if (is.null(attr(x, "allowed_mutations"))) stop(pkg_ref_mutability_error(name)) bare_env(x, x[[name]] <- value) } #' @export `[.pkg_ref` <- function(x, names, ...) { lapply(names, function(n, ...) x[[n, ...]], ...) } #' @export `[<-.pkg_ref` <- function(x, names, value) { invisible(Map(function(name, value) { `[[<-`(x, name = name, value = value) }, names, value)) } #' evaluate an expression with a pkg_ref object reclassed as a bare environment #' object, used to sidestep pkg_ref assignment guardrails #' #' @param x a \code{pkg_ref} object #' @param expr an expression to evaluate, avoiding \code{pkg_ref} extraction #' handlers #' @param envir an environment in which the expression is to be evaluated #' #' @return the result of \code{expr} #' @keywords internal bare_env <- function(x, expr, envir = parent.frame()) { old_class <- class(x) class(x) <- "environment" on.exit(class(x) <- old_class) eval(expr, envir = envir) } #' pretty printing for a pkg_ref mutability error caused by trying to do #' assignment within the pkg_ref without permission #' #' @param name name of field for which mutation was attempted #' @return a \code{simplError} with subclasses \code{pkg_ref_mutability_error}, #' \code{pkg_ref_error} #' @keywords internal pkg_ref_mutability_error <- function(name) { message <- list(paste0( "Assignment to a pkg_ref environment can only be done in a ", "pkg_ref_cache call.")) if (!missing(name)) message <- append(message, list(paste0( "Extend the pkg_ref class by implementing function '", "pkg_ref_cache.", name, "'"))) e <- simpleError(message = paste(message, collapse = " ")) class(e) <- c("pkg_ref_mutability_error", "pkg_ref_error", class(e)) e } #' a wrapper to assert that a pkg_ref has been permitted to do an additional #' mutation, used to handle recursive initialization of cached fields #' #' @param x a \code{pkg_ref} object #' @param expr an expression to evaluate, and possible do a mutation within #' @param envir an environment in which the expression is to be evaluated #' #' @return the result of \code{expr} #' @keywords internal allow_mutation <- function(x, expr, envir = parent.frame()) { inc_mutations_count(x) on.exit(dec_mutations_count(x)) expr <- substitute(expr) eval(expr, envir = envir) } #' increment the number of allowed mutations #' #' @param x pkg_ref object to increment mutation counter for #' @return a pkg_ref object #' @keywords internal inc_mutations_count <- function(x) { if (is.null(attr(x, "allowed_mutations"))) attr(x, "allowed_mutations") <- 0 attr(x, "allowed_mutations") <- attr(x, "allowed_mutations") + 1 } #' decrement the number of allowed mutations #' #' @param x pkg_ref object to decrement mutation counter for #' @return pkg_ref object #' @keywords internal dec_mutations_count <- function(x) { attr(x, "allowed_mutations") <- attr(x, "allowed_mutations") - 1 if (attr(x, "allowed_mutations") <= 0) attr(x, "allowed_mutations") <- NULL } ================================================ FILE: R/pkg_ref_class_format.R ================================================ #' @importFrom utils head capture.output #' @export print.pkg_ref <- function(x, ...) { xx <- as.list(x) ns <- names(xx) ns_unused <- setdiff(available_pkg_ref_fields(x), ns) indent <- 2 width <- 0.95 * getOption("width") xs <- vapply(xx, function(xi) { truncated <- FALSE if (length(xi) > 5) truncated <- TRUE if (inherits(xi, "riskmetric_disabled_behavior_error")) return(paste0(strrep(" ", indent), "<", xi$message, ">")) x_str <- utils::capture.output(head(xi)) x_str <- gsub("\\s+$", "", x_str) x_str <- gsub(sprintf("(.{%0.f})", width - indent), "\\1\n", x_str) x_str <- unlist(strsplit(x_str, "\n")) if (length(x_str) > 5) truncated <- TRUE if (truncated) x_str <- c(head(x_str), "") paste0(strrep(" ", indent), x_str, collapse = "\n") }, character(1L)) cat( "<", paste(class(x)[1:which("pkg_ref" == class(x))], collapse = ", "), "> ", x$name, " v", as.character(x$version), "\n", if (length(ns)) paste0("$", ns, "\n", xs, collapse = "\n"), if (length(ns)) "\n", if (length(ns_unused)) paste0("$", ns_unused, "...", collapse = "\n"), if (length(ns_unused)) "\n", sep = "") invisible(x) } #' @importFrom vctrs vec_ptype_abbr #' @method vec_ptype_abbr pkg_ref #' @export vec_ptype_abbr.pkg_ref <- function(x, ...) { "pkg_ref" } #' @importFrom vctrs vec_cast.character #' @method vec_cast.character list_of_pkg_ref #' @export vec_cast.character.list_of_pkg_ref <- function(x, to, ...) { vapply(x, "[[", character(1L), "name") } #' @export format.pkg_ref <- function(x, ...) { class_str <- gsub("^pkg_", "", class(x)[[1]]) paste0(x$name, pillar::style_subtle(paste0("<", class_str, ">"))) } #' @export format.pkg_missing <- function(x, ...) { class_str <- gsub("^pkg_", "", class(x)[[1]]) pillar::style_na(paste0(x$name, "<", class_str, ">")) } ================================================ FILE: R/pkg_ref_class_names.R ================================================ #' @importFrom utils .DollarNames #' @export `.DollarNames.pkg_ref` <- function(x, pattern) { names(x) } #' @export names.pkg_ref <- function(x, ...) { c(unname(available_pkg_ref_fields(x)), bare_env(x, names(x))) } ================================================ FILE: R/pkg_score.R ================================================ #' Score a package assessment, collapsing results into a single numeric #' #' pkg_score() calculates the risk involved with using a package. Risk ranges #' from 0 (low-risk) to 1 (high-risk). #' #' @param x A \code{pkg_metric} object, whose subclass is used to choose the #' appropriate scoring method for the atomic metric metadata. Optionally, a #' \code{\link[tibble]{tibble}} can be provided, in which cases all #' \code{pkg_metric} values will be scored. #' @param ... Additional arguments passed to \code{summarize_scores} when an #' object of class \code{tbl_df} is provided, unused otherwise. #' @param error_handler Specify a function to be called if the class can't be #' identified. Most commonly this occurs for \code{pkg_metric} objects of #' subclass \code{pkg_metric_error}, which is produced when an error is #' encountered when calculating an associated assessment. #' #' @return A numeric value if a single \code{pkg_metric} is provided, or a #' \code{\link[tibble]{tibble}} with \code{pkg_metric} objects scored and #' returned as numeric values when a \code{\link[tibble]{tibble}} is provided. #' #' @examples #' \dontrun{ #' #' # scoring a single assessment #' metric_score(assess_has_news(pkg_ref("riskmetric"))) #' #' # scoring many assessments as a tibble #' library(dplyr) #' pkg_score(pkg_assess(as_tibble(pkg_ref(c("riskmetric", "riskmetric"))))) #' #' } #' #' @seealso score_error_default score_error_zero score_error_NA #' #' @export pkg_score <- function(x, ..., error_handler = score_error_default) { UseMethod("pkg_score") } #' @export pkg_score.tbl_df <- function(x, ..., error_handler = score_error_default) { assessment_columns <- get_assessment_columns(x) for (coln in which(assessment_columns)) { metric_score_s3_fun <- firstS3method("metric_score", class(x[[coln]][[1]])) x[[coln]] <- vapply(x[[coln]], metric_score, numeric(1L), error_handler = error_handler) attr(x[[coln]], "label") <- attr(metric_score_s3_fun, "label") class(x[[coln]]) <- c("pkg_score", class(x[[coln]])) } ignore_cols <- c("package", "version", "pkg_ref") x[["pkg_score"]] <- summarize_scores(x[, !names(x) %in% ignore_cols], ...) # reorder columns so that metadata columns come first pkg_cols <- intersect(names(x), c("package", "version", "pkg_ref", "pkg_score")) x <- x[, c(pkg_cols, setdiff(names(x), pkg_cols))] x } #' @export pkg_score.list_of_pkg_metric <- function(x, ..., error_handler = score_error_default) { lapply(x, function(xi) { s <- metric_score(xi, error_handler = error_handler) metric_score_s3_fun <- firstS3method("metric_score", class(xi)) attr(s, "label") <- attr(metric_score_s3_fun, "label") class(s) <- c("pkg_score", class(s)) s }) } #' Helper for creating a roxygen header from template for score.* functions #' #' @param name the name of the scoring function, assuming naming conventions are #' followed #' @param dontrun logical indicating whether examples should be wrapped in #' a dontrun block. This is particularly useful for assessments which may #' require an internet connection. #' #' @return roxygen section template for score family functions #' #' @examples #' \dontrun{ #' #' @eval roxygen_score_family("has_news") #' } #' @keywords internal roxygen_score_family <- function(name, dontrun = TRUE) { assess_func <- sprintf("assess_%s", name) score_func <- sprintf("metric_score.pkg_metric_%s", name) example_template <- if (dontrun) { "@examples \n\\dontrun{metric_score(%s(pkg_ref(\"%s\")))\n}" } else { "@examples metric_score(%s(pkg_ref(\"%s\")))" } if (!assess_func %in% getNamespaceExports(utils::packageName())) warning(sprintf(paste0("Error when generating documentation for %s. ", "Associated assessment function `%s` was not found in the `riskmetric` ", "package. Please provide one to complete documentation."), name, assess_func)) if (!score_func %in% getNamespaceExports(utils::packageName())) warning(sprintf(paste0("Error when generating documentation for %s. ", "Associated scoring function `%s` was not found in the `riskmetric` ", "package. Please provide one to complete documentation."), name, score_func)) c(sprintf("@param x a \\code{pkg_metric_%s} packge metric object", name), "@param ... additional arguments unused", sprintf(example_template, assess_func, packageName())) } ================================================ FILE: R/riskmetric-package.R ================================================ #' riskmetric #' #' Facilities for assessing R packages against a number of metrics to help #' quantify their robustness. #' #' @import tools #' #' @docType package #' @name riskmetric #' @keywords internal "_PACKAGE" # make check() happy . <- NULL ================================================ FILE: R/summarize_scores.R ================================================ #' Summarize a default set of assessments into a single risk score #' #' This function serves as an example for how a risk score might be derived. #' Assuming all assessments provided by \code{riskmetric} are available in a #' dataset, this function can be used to calculate a vector of risks. #' #' @param data a \code{\link[tibble]{tibble}} of scored assessments whose column #' names match those provided by riskmetric's \code{\link{pkg_assess}} function. #' @param weights an optional vector of non-negative weights to be assigned to #' each assessment. #' #' @return a numeric vector of risk scores #' #' @examples #' \dontrun{ #' library(dplyr) #' summarize_scores(pkg_score(pkg_assess(as_tibble(pkg_ref("riskmetric"))))) #' #' library(dplyr) #' pkg_ref("riskmetric") %>% #' pkg_assess() %>% #' pkg_score() %>% #' summarize_scores() #' } #' #' @export summarize_scores <- function(data, weights = NULL) { UseMethod("summarize_scores") } #' @export summarize_scores.data.frame <- function(data, weights = NULL) { if (missing(weights)) weights <- add_default_weights(data) # perform checks and standardize weights weights <- standardize_weights(data, weights) # calculate 'quality' and subtract from 1 to get 'risk' qual <- colSums(apply(data[names(weights)], 1L, `*`, weights), na.rm = TRUE) risk <- 1 - qual risk } #' @export summarize_scores.list <- function(data, weights = NULL) { if (missing(weights)) weights <- add_default_weights(data) # perform checks and standardize weights weights <- standardize_weights(data, weights) 1 - sum(as.numeric(data[names(weights)]) * weights, na.rm = TRUE) } # Set the default weight of each metric to 1. add_default_weights <- function(data) { # ignore columns that are not of class 'pkg_score' ignore_cols <- c("package", "version", "pkg_ref", "pkg_score") metrics <- names(data)[!(names(data) %in% ignore_cols)] # assign a weight of 1 to each metric weights <- rep(1, length(metrics)) names(weights) <- metrics weights } # Check that the provided weights are numeric and non-negative. check_weights <- function(weights) { if (!is.numeric(weights)) stop("The weights must be a numeric vector.") if (!all(weights >= 0)) stop("The weights must contain non-negative values only.") } # Check weights values and standardize them. standardize_weights <- function(data, weights) { # check that the weights vector is numeric and non-negative check_weights(weights) # re-weight for fields that are in the dataset weights <- weights[which(names(weights) %in% names(data))] # standardize weights from 0 to 1 weights <- weights / sum(weights, na.rm = TRUE) } ================================================ FILE: R/utils.R ================================================ #' If not NULL else #' #' @param lhs Left-hand side #' @param rhs Right-hand side #' #' A shorthand for a common comparison #' #' @name if_not_null_else #' @return an object same as \code{lhs} or \code{rhs} #' @keywords internal `%||%` <- function(lhs, rhs) if (!length(lhs) || is.null(lhs)) rhs else lhs #' Accessor for tools namespace #' #' used internally for #' - tools:::.news_reader_default #' @return tools namespace #' @keywords internal .tools <- memoise::memoise(function() { getNamespace("tools") }) #' check if a url originates from a list of repo urls #' #' @param url a url which may stem from one of the provided base urls #' @param urls vector of base urls #' #' @return logical vector indicating which base urls have a sub url of #' \code{url} #' @keywords internal is_url_subpath_of <- function(url, urls) { grepl(paste0("(", paste0(gsub("/$", "", urls), collapse = "|"), ")"), url) } #' Evaluate an expression after first removing a range of S3 classes #' #' @param x a structured S3-classed object #' @param .class the class to unclass the object to #' @param expr an expression to evaluate, avoiding parent classs dispatch #' @param envir an environment in which the expression is to be evaluated #' #' @return the result of \code{expr} #' @keywords internal with_unclassed_to <- function(x, .class = 1:length(class(x)), expr, envir = parent.frame()) { x_expr <- substitute(x) orig_class <- class(x) if (is.character(.class)) .class = 1:which(class(x) == .class) eval(bquote(class(.(x_expr)) <- class(.(x_expr))[-.(.class)]), envir = envir) out <- eval(expr, envir = envir) eval(bquote(class(.(x_expr)) <- .(orig_class)), envir = envir) out } #' Find the S3 method that will be evaluated when an S3 generic is called by #' an object of class \code{classes} #' #' @inheritParams utils::getS3method #' @param classes a character vector of classes used to search for the #' appropriate S3 method #' #' @importFrom utils getS3method #' @return a S3 method #' @keywords internal firstS3method <- function(f, classes, envir = parent.frame()) { s3methods <- lapply( classes, utils::getS3method, f = f, envir = envir, optional = TRUE) # [1][[1]] hacky way of getting first elem while coercing empty list to NULL Filter(Negate(is.null), s3methods)[1][[1]] } #' Capture side effects issued by an evaluated expression #' #' All messaging condition side effects are captured in the order that they #' are issued. #' #' @param expr an expression to evaluate, capturing output events as they #' are issued #' @param env the environment in which \code{expr} should be evaluated, #' defaulting to the calling environment. #' @param quoted whether \code{expr} is a quoted object and should be evaluated #' as is, or whether the expression should be captured from the function call. #' Defaults to \code{FALSE}, capturing the passed expression. #' @inheritParams base::sink #' #' #' @importFrom utils head tail #' @return an with_eval_recording object #' @keywords internal capture_expr_output <- function(expr, split = FALSE, env = parent.frame(), quoted = FALSE) { expr_quote <- substitute(expr) log_file <- tempfile("riskmetric_sink_", fileext = ".txt") log_file_con <- file(log_file, "wb") on.exit(try(close(log_file_con), silent = TRUE)) cnds_seek <- numeric() cnds_err_traceback <- NULL cnds <- list() # messages + warnings + misc conditions append_cnd <- function(cnd, envir) { cnd_seek <- seek(log_file_con) assign("cnds_seek", append(cnds_seek, cnd_seek), envir = envir) assign("cnds", append(cnds, list(cnd)), envir = envir) } n_calls <- length(sys.calls()) fn_env <- environment() sink(log_file_con, split = split) res <- withVisible(tryCatch(withCallingHandlers( if (!quoted) eval(expr_quote, env) else eval(expr, env), condition = function(cnd) { if (inherits(cnd, "message") || inherits(cnd, "warning")) { calls <- utils::head(utils::tail(sys.calls(), -(8L + n_calls)), -5L) cnd$call <- if (length(calls) > 1) calls[[length(calls) - 1]] else NULL append_cnd(cnd, fn_env) invokeRestart(computeRestarts()[[1]]) } else if (inherits(cnd, "error")) { # trim call stack back to just the scope of the evaluated expression calls <- utils::head(utils::tail(sys.calls(), -(8L + n_calls)), -2L) cnd$call <- if (length(calls) > 1) calls[[length(calls) - 1]] else NULL append_cnd(cnd, fn_env) assign("cnds_err_traceback", rev(calls), envir = fn_env) } else { append_cnd(cnd, fn_env) } }), error = function(e) { e })) # read as raw so that we can keep carriage return and console-overwrites sink(NULL) close(log_file_con) log_text <- rawToChar(readBin(log_file, "raw", file.size(log_file))) log_text_line_nchars <- nchar(strsplit(gsub("\r", "\n", log_text), "\n")[[1]]) # NOTE: Windows might use two newline characters "\r\n"? log_newlines <- cumsum(log_text_line_nchars + 1L) # rejoin into singular string to split at newlines, as well as any condition # emission points log_cuts <- sort(unique(c(log_newlines, cnds_seek))) log_cuts <- log_cuts[log_cuts < nchar(log_text)] log_text <- substring(log_text, c(1, log_cuts + 1L), c(log_cuts, nchar(log_text))) log_chars <- cumsum(nchar(log_text)) # find where to insert emitted conditions among output cnd_i <- findInterval(cnds_seek, log_chars) cnds_new_index <- cnd_i + seq_along(cnd_i) # inject conditions throughout console output as they were emitted outputs <- rep(list(NULL), length(log_text) + length(cnds_new_index)) if (length(cnds_new_index) > 0L) { outputs[cnds_new_index] <- cnds outputs[-cnds_new_index] <- log_text } else { outputs <- log_text } any_output_error <- any(vapply(outputs, inherits, logical(1L), "error")) structure( res$value, .recording = list( expr = if (!quoted) expr_quote else expr, attributes = attributes(res$value), visible = res$visible, traceback = cnds_err_traceback, output = outputs[nzchar(outputs)]), class = c("with_eval_recording", class(res$value))) } is_error <- function(expr_output) { any(vapply(attr(expr_output, "output"), inherits, logical(1L), "error")) } #' Handle pretty printing of expression output #' #' @param x expr_output to print #' @param playback a \code{logical} indicating whether evaluation output #' should be played back (\code{FALSE}), or whether the result value should #' be printed as is (\code{TRUE}, the default) #' @param cr a \code{logical} indicating whether carriage returns should be #' printed, possibly overwriting characters in the output. #' @param ... additional arguments unused #' @param sleep an \code{numeric} indicating a time to sleep between printing #' each line to console. This can be helpful if the original output overwrites #' valuable information in the log that is eventually overwritten and you #' would like to watch it play out as it was formatted. #' #' @export #' @return a print message #' @keywords internal print.with_eval_recording <- function(x, playback = FALSE, cr = TRUE, ..., sleep = 0) { # extract expr execution recording rec <- attr(x, ".recording") # extract value val <- x attributes(val) <- rec$attributes if (!playback) return(print(val)) if (rec$expr[[1]] == "{") { x_call_str <- vapply( rec$expr[-1], function(xi) paste0(deparse(xi), collapse = "\n"), character(1L)) } else { x_call_str <- capture.output(rec$expr) } x_call_str[1] <- paste(">", x_call_str[1]) x_call_str[-1] <- paste("+", x_call_str[-1]) str_call <- paste(x_call_str, collapse = "\n") str_traceback <- paste( sprintf( "%s %s", "#", capture.output(traceback(rec$traceback))), collapse = "\n") cat(str_call, "\n", sep = "") for (i in rec$output) { if (inherits(i, "message")) { message(i$message, appendLF = FALSE) } else if (inherits(i, "warning")) { message(gsub("^simple", "", .makeMessage(i)), appendLF = FALSE) } else if (inherits(i, "error")) { message(sprintf("Error%s: %s\n", if (!is.null(i$call)) sprintf(" in %s", format(i$call)) else "", i$message), appendLF = FALSE) } else if (inherits(i, "condition")) { message(.makeMessage(i)) } else if (cr) { cat(i) } else if (nzchar(gsub("\r", "", i))) { cat(gsub("\r", "\n", i)) } if (sleep > 0L) Sys.sleep(sleep) } if (!is.null(rec$traceback) && length(rec$traceback)) cat(str_traceback, "\n", sep = "") else if (rec$visible) val } #' Suppress messages and warnings based on one or more regex matches #' #' @param expr An expression to evaluate #' @param ... Named parameters, where the name indicates the class of conditions #' to capture and the value is a vector of regular expressions that, when #' matched against the respective condition message, should suppress that #' condition. #' @param .opts A named list of arguments to pass to \code{grepl} #' @param .envir The environment in which \code{expr} is to be evaluated #' @return a message printed on console #' @keywords internal suppressMatchingConditions <- function(expr, ..., .opts = list(), .envir = parent.frame()) { optioned_grepl <- function(pattern, x) do.call(grepl, append(list(pattern = pattern, x = x), .opts)) generate_cond_handler <- function(cond_regexes) { function(cond) { if (any(sapply(cond_regexes, optioned_grepl, conditionMessage(cond)))) invokeRestart(computeRestarts()[[1]]) } } do.call(withCallingHandlers, append(list(expr), lapply(list(...), generate_cond_handler))) } #' Evaluate an expression in the context of a pkg_ref #' #' \code{pkg_ref} objects are environments and can be passed to \code{with} #' in much the same way. This specialized function makes sure that any fields #' within the \code{pkg_ref} have been appropriately evaluated before trying #' to execute the expression. #' #' @inheritParams base::with #' @return the value of the evaluated expr. #' @export #' @keywords internal with.pkg_ref <- function(data, expr, ...) { expr <- substitute(expr) for (n in intersect(names(data), all.names(expr))) data[[n]] eval(expr, as.list(data), enclos = parent.frame()) } is_available_cran <- function(x, repos, p) { x %in% memoise_available_packages(repos = repos)[,"Package"] || (!is.null(memoise_cran_mirrors()) && # isTRUE added to catch any issues where the cran mirror isn't available isTRUE(is_url_subpath_of( p$repo_base_url, c(memoise_cran_mirrors()$URL, "https://cran.rstudio.com/")))) } is_available_bioc <- function(x, p){ x %in% memoise_bioc_available()[,"Package"] || (!is.null(memoise_bioc_mirrors()) && isTRUE(is_url_subpath_of(p$repo_base_url, memoise_bioc_mirrors()$URL))) } ================================================ FILE: R/utils_memoised.R ================================================ #' Fetch CRAN Mirrors Info #' #' @param all default \code{TRUE}, passed to \code{\link{utils}[getCRANmirrors]} #' @param ... additional arguments passed to \code{\link{utils}[getCRANmirrors]} #' @param .local an optional local directory to source the CRAN package index #' from, defaulting to \code{getOption("riskmetric.tests")}, used #' for isolating repository requests during testing. #' #' @importFrom curl nslookup #' @importFrom memoise memoise #' @return a data frame with mirror information #' @keywords internal memoise_cran_mirrors <- memoise::memoise({ # add parameter such that memoised results rerun if internet availability changes # NOTE: might need to implement actual caching to avoid inconsistent behavior # when run with spotty internet function(all = TRUE, ..., .local = getOption("riskmetric.tests")) { if (!is.null(.local)) { return(read.csv( file.path(.local, "test_webmocks", "data", "cran_mirrors.csv"), stringsAsFactors = FALSE)) } tryCatch({ utils::getCRANmirrors(all = all, ...) }, error = function(e) { NULL }) } }) #' @importFrom BiocManager available #' @importFrom memoise memoise #' @keywords internal memoise_bioc_available <- memoise::memoise({ function() { con <- url("https://bioconductor.org/packages/release/bioc/src/contrib/PACKAGES") on.exit(close(con)) as.data.frame(read.dcf(con), stringsAsFactors = FALSE) } }) #' Fetch BioC Mirrors Info #' #' taken from utils::chooseBioCmirror #' #' @importFrom curl nslookup #' @importFrom memoise memoise #' @return a data frame with mirror information #' @keywords internal memoise_bioc_mirrors <- memoise::memoise({ # add parameter such that memoised results rerun if internet availability changes # NOTE: might need to implement actual caching to avoid inconsistent behavior # when run with spotty internet function() { tryCatch({ read.csv("https://bioconductor.org/BioC_mirrors.csv") }, error = function(e) { NULL }) } }) #' @importFrom memoise memoise memoise_available_packages <- memoise::memoise({ function(..., repos = getOption("repos"), .local = getOption("riskmetric.tests")) { if (!is.null(.local)) { db <- read.csv( file.path(.local, "test_webmocks", "data", "cran_packages.csv"), stringsAsFactors = FALSE) db[, "Repository"] <- contrib.url(repos, getOption("pkgType")) return(db) } else if (is.null(repos)) { return(utils::available.packages(NULL)) } else if ("@CRAN@" %in% repos) { repos[repos == "@CRAN@"] <- "https://cloud.r-project.org" } utils::available.packages(repos = repos, ...) } }) ================================================ FILE: R/vctrs_list_of_pkg_metric.R ================================================ #' @importFrom pillar pillar_shaft #' @method pillar_shaft list_of_pkg_metric #' @export pillar_shaft.list_of_pkg_metric <- function(x, ...) { ucx <- lapply(x, unclass) p <- pillar::pillar_shaft(ucx) is_error <- vapply(x, inherits, logical(1L), "pkg_metric_error") p[[1]][is_error] <- vapply(x[is_error], function(xi) { pillar::pillar_shaft(xi)[[1]] }, character(1L)) is_atomic_l1 <- vapply(ucx, function(xi) is.atomic(xi) && length(xi) == 1, logical(1L)) p[[1]][is_atomic_l1] <- ucx[is_atomic_l1] attr(p, "width") <- max(attr(p, "width"), nchar(ucx[is_atomic_l1])) p } #' @importFrom vctrs vec_cast.double #' @method vec_cast.double list_of_pkg_metric #' @export vec_cast.double.list_of_pkg_metric <- function(x, to, ...) { out <- vector("numeric", length(x)) is_error <- vapply(x, inherits, logical(1L), "pkg_metric_error") out[is_error] <- NA_real_ out[!is_error] <- vapply(x, unclass, numeric(1L)) out } ================================================ FILE: R/vctrs_list_of_pkg_ref.R ================================================ #' @importFrom pillar pillar_shaft new_pillar_shaft_simple #' @method pillar_shaft list_of_pkg_ref #' @export pillar_shaft.list_of_pkg_ref <- function(x, ...) { out <- vapply(x, format, character(1L)) pillar::new_pillar_shaft_simple(out, align = "left") } ================================================ FILE: R/zzz.R ================================================ #' @importFrom backports import #' @keywords internal .onLoad <- function(libname, pkgname) { backports::import(pkgname, "isFALSE") # set default cache behaviors opts <- Filter(Negate(is.null), Map(function(i) i$default(), cache_behaviors)) names(opts) <- sprintf("riskmetric.%s", names(opts)) opts <- opts[!names(opts) %in% names(options())] do.call(options, as.list(opts)) # set default options opts <- riskmetric.options names(opts) <- sprintf("riskmetric.%s", names(opts)) opts <- opts[!names(opts) %in% names(options())] do.call(options, as.list(opts)) # if non-interactive, cache package sources on load if (!interactive()) { memoise_available_packages() } } ================================================ FILE: README.md ================================================ # riskmetric [![Lifecycle](.github/assets/lifecycle-superseded.svg)](https://github.com/pharmaR/val.meter) [![R build status](https://github.com/pharmaR/riskmetric/workflows/R-CMD-check/badge.svg)](https://github.com/pharmaR/riskmetric/actions?workflow=R-CMD-check) [![Coverage status](https://codecov.io/gh/pharmaR/riskmetric/branch/master/graph/badge.svg)](https://app.codecov.io/gh/pharmaR/riskmetric?branch=master) `riskmetric` is a collection of risk metrics to evaluate the quality of R packages. > [!IMPORTANT] > > Lifecycle: **Maintenance Only** > > The _R Validation Hub_ is shifting development efforts to our new flagship > assessment tool, [`{val.meter}`](https://github.com/pharmaR/val.meter). While > development is not ending for `{riskmetric}`, it has shifted to > maintenance-only. Please continue to file bug reports for erroneous behaviors, > but for feature requests and discussion on approach, please head over to > `{val.meter}`. > ## Background The risk of using an R package is evaluated based on a number of metrics meant to evaluate development best practices, code documentation, community engagement and development sustainability. We hope to provide a framework to quantify risk by assessing these metrics. This package serves as a starting point for exploring the heterogeneity of code quality, and begin a broader conversation about the validation of R packages. Primarily, this effort aims to provide some context for validation within regulated industries. We separate three steps in the workflow to assess the risk of an R package using `riskmetric`: 1. **Finding a source for package information (installed package or CRAN/git source)** `pkg_ref()` 1. **Assessing the package under validation criteria** `pkg_assess()` 1. **Scoring assessment criteria** `pkg_score()` The results will be assembled in a dataset of validation criteria containing an overall risk score for each package as shown in the example below. ## Installation You can install `riskmetric` from CRAN with: ```r install.packages("riskmetric") ``` Or from GitHub using `devtools` with: ```r devtools::install_github("pharmaR/riskmetric") ``` ## Example Scrape metadata locally or remotely, then assess that metadata and score it to estimate risk. For each package, derive a composite measure of risk, or a collection of individual scores which can be easily used to generate validation reports. ```r library(dplyr) library(riskmetric) pkg_ref(c("riskmetric", "utils", "tools")) %>% pkg_assess() %>% pkg_score() ``` ## The `{riskassessment}` application `riskassessment` is a full-fledged R package containing a shiny front-end that augments the utility of `riskmetric`. The application's goal is to provide a central hub for an organization to review and assess the risk of R packages, providing handy tools and guide rails along the way. The app uses a local database to store & display: * all `riskmetric` metrics, including package risk scores over time * organization-wide metric weighting, plus rules to automate org decisions (whether to endorse/ prohibit the pkg) * package-level user dialogue on the perceived risk, to facilitate communication & notes To learn more about `riskassessment`, please browse the [user guide](https://pharmar.github.io/riskassessment/) or consider taking the [demo app](https://rinpharma.shinyapps.io/risk_assessment) for a spin. ## Get Involved We have a bi-weekly sprint meeting for developers to discuss the progress. * Contact `eric.milliman@biogen.com` to be added to the meeting. * [Project Planning Meeting Structure](https://github.com/pharmaR/riskmetric/issues/57) * [Milestone](https://github.com/pharmaR/riskmetric/milestones) `riskmetric` is centrally a community project. Comfort with a quantification of risk comes via consensus, and for that this project is dependent on close community engagement. There are plenty of ways to help: - Share the package - File [issues](https://github.com/pharmaR/riskmetric/issues) when you encounter bugs - Weigh in on proposed metrics, or [suggest a new one](https://github.com/pharmaR/riskmetric/issues/new?labels=Metric%20Proposal) - Help us devise the best way to summarize risk into a single score - Help us keep documentation up to date - Contribute code to tackle the metric backlog ================================================ FILE: _pkgdown.yml ================================================ template: bootstrap: 5 reference: - title: "Package Reference" desc: "Create Package Reference for Each Package Risk Metric and Cache Metadata" contents: - starts_with("pkg_ref") - title: "Package Assessment" desc: "Assess Package Metadata against a risk criterion" contents: - starts_with("pkg_assess") - all_assessments - get_assessments - starts_with("assess") - title: "Package Risk Score" desc: "Provide Risk Score based on Risk Metrics" contents: - pkg_score - starts_with("pkg_metric") - starts_with("metric_score") - title: "Package Risk Summary" desc: "Summarizing across metric scores" contents: - summarize_scores - title: "Metric Error Handler" desc: "Error handler functions" contents: - starts_with("assessment_error") - starts_with("score_error") - title: "Utilities" desc: "Utility functions" contents: - as_pkg_metric - as_pkg_ref ================================================ FILE: cran-comments.md ================================================ ## riskmetric 0.2.5 Resolved issues with CRAN checks. Tested on Ubuntu Jammy, GitHub Action, and RHub. ## R CMD check results 0 errors | 0 warnings | 0 notes ================================================ FILE: man/all_assessments.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_assess.R \name{all_assessments} \alias{all_assessments} \title{A default list of assessments to perform for each package} \usage{ all_assessments() } \value{ a list of assess_* functions exported from riskmetric } \description{ A default list of assessments to perform for each package } ================================================ FILE: man/allow_mutation.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_ref_class_extract.R \name{allow_mutation} \alias{allow_mutation} \title{a wrapper to assert that a pkg_ref has been permitted to do an additional mutation, used to handle recursive initialization of cached fields} \usage{ allow_mutation(x, expr, envir = parent.frame()) } \arguments{ \item{x}{a \code{pkg_ref} object} \item{expr}{an expression to evaluate, and possible do a mutation within} \item{envir}{an environment in which the expression is to be evaluated} } \value{ the result of \code{expr} } \description{ a wrapper to assert that a pkg_ref has been permitted to do an additional mutation, used to handle recursive initialization of cached fields } \keyword{internal} ================================================ FILE: man/as_pkg_metric.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_metric.R \name{as_pkg_metric} \alias{as_pkg_metric} \title{Convert an object to a \code{pkg_metric}} \usage{ as_pkg_metric(x, class = c()) } \arguments{ \item{x}{data to store as a \code{pkg_metric}} \item{class}{a subclass to differentiate the \code{pkg_metric} object} } \value{ a \code{pkg_metric} object } \description{ Convert an object to a \code{pkg_metric} } ================================================ FILE: man/as_pkg_metric_condition.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_metric_condition.R \name{as_pkg_metric_condition} \alias{as_pkg_metric_condition} \title{A pkg_metric subclass for general metric evaluation conditions} \usage{ as_pkg_metric_condition(x, ..., subclass = c()) } \arguments{ \item{x}{an object to wrap in a \code{pkg_metric_condition} class} \item{...}{additional arguments added as attributes to object \code{x}} \item{subclass}{an optional subclass of \code{pkg_metric_condition} to include} } \value{ an object after wrap \code{pkg_metric_condition} class. } \description{ A pkg_metric subclass for general metric evaluation conditions } \keyword{internal} ================================================ FILE: man/as_pkg_metric_error.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_metric_error.R \name{as_pkg_metric_error} \alias{as_pkg_metric_error} \title{A subclass wrapping an error with an additional parent class} \usage{ as_pkg_metric_error(error) } \arguments{ \item{error}{an error condition object to capture} } \value{ an error condition object after wrap \code{pkg_metric_error} class. } \description{ A subclass wrapping an error with an additional parent class } \keyword{internal} ================================================ FILE: man/as_pkg_metric_na.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_metric_na.R \name{as_pkg_metric_na} \alias{as_pkg_metric_na} \title{A pkg_metric subclass for when metrics are explicitly not applicable} \usage{ as_pkg_metric_na(x, message = NULL) } \arguments{ \item{x}{a \code{pkg_metric} object to wrap in a \code{pkg_metric_na} subclass} \item{message}{an optional message explaining why a metric is not applicable.} } \value{ a \code{pkg_metric} object after wrap in a \code{pkg_metric_na} } \description{ A pkg_metric subclass for when metrics are explicitly not applicable } \keyword{internal} ================================================ FILE: man/as_pkg_metric_todo.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_metric_todo.R \name{as_pkg_metric_todo} \alias{as_pkg_metric_todo} \title{A pkg_metric subclass for when pkg_metrics have not yet been implemented} \usage{ as_pkg_metric_todo(x, message = NULL) } \arguments{ \item{x}{a \code{pkg_metric} object to wrap in a \code{pkg_metric_todo} subclass} \item{message}{an optional message directing users and potential contributors toward any ongoing work or first steps toward development.} } \value{ a \code{pkg_metric} object after wrap in a \code{pkg_metric_todo} } \description{ A pkg_metric subclass for when pkg_metrics have not yet been implemented } \keyword{internal} ================================================ FILE: man/assess_covr_coverage.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_covr_coverage.R \name{assess_covr_coverage} \alias{assess_covr_coverage} \title{Assess a package code coverage using the `covr` package} \usage{ assess_covr_coverage(x, ...) } \arguments{ \item{x}{a \code{pkg_ref} package reference object} \item{...}{additional arguments passed on to S3 methods, rarely used} } \value{ a \code{pkg_metric} containing a list containing fields 'filecoverage' and 'totalcoverage' containing a named numeric vector of file unit test coverage and a singular numeric value representing overall test coverage respectively. } \description{ Assess a package code coverage using the `covr` package } \examples{ \dontrun{ assess_covr_coverage(pkg_ref("riskmetric")) } } \seealso{ \code{\link{metric_score.pkg_metric_covr_coverage}} } ================================================ FILE: man/assess_dependencies.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_dependencies.R \name{assess_dependencies} \alias{assess_dependencies} \title{Assessment of dependency footprint for a specific package} \usage{ assess_dependencies(x, ...) } \arguments{ \item{x}{a \code{pkg_ref} package reference object} \item{...}{additional arguments passed on to S3 methods, rarely used} } \value{ a \code{pkg_metric} containing a dataframe of package names and they type of dependency the package being assess has to them } \description{ Only Depends, Imports and LinkingTo dependencies are assessed because they are required } \details{ The more packages a package relies on the more chances for errors exist. } \examples{ \dontrun{ assess_dependencies(pkg_ref("riskmetric")) } } \seealso{ \code{\link{metric_score.pkg_metric_dependencies}} } ================================================ FILE: man/assess_downloads_1yr.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_downloads.R \name{assess_downloads_1yr} \alias{assess_downloads_1yr} \title{Assess a package for the number of downloads in the past year} \usage{ assess_downloads_1yr(x, ...) } \arguments{ \item{x}{a \code{pkg_ref} package reference object} \item{...}{additional arguments passed on to S3 methods, rarely used} } \value{ a \code{pkg_metric} containing a numeric value between [0,1] indicating the volume of downloads } \description{ Assess a package for the number of downloads in the past year } \details{ The more times a package has been downloaded the more extensive the user testing and the greater chance there is of someone finding a bug and logging it. } \examples{ \dontrun{ assess_downloads_1yr(pkg_ref("riskmetric")) } } \seealso{ \code{\link{metric_score.pkg_metric_downloads_1yr}} } ================================================ FILE: man/assess_export_help.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_export_help.R \name{assess_export_help} \alias{assess_export_help} \title{Assess a package for availability of documentation for exported values} \usage{ assess_export_help(x, ...) } \arguments{ \item{x}{a \code{pkg_ref} package reference object} \item{...}{additional arguments passed on to S3 methods, rarely used} } \value{ a \code{pkg_metric} containing a logical vector indicating existence of documentation for each namespace export } \description{ Assess a package for availability of documentation for exported values } \examples{ \dontrun{ assess_export_help(pkg_ref("riskmetric")) } } \seealso{ \code{\link{metric_score.pkg_metric_export_help}} } ================================================ FILE: man/assess_exported_namespace.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_exported_namespace.R \name{assess_exported_namespace} \alias{assess_exported_namespace} \title{Assess a package's results from running R CMD check} \usage{ assess_exported_namespace(x, ...) } \arguments{ \item{x}{a \code{pkg_ref} package reference object} \item{...}{additional arguments passed on to S3 methods, rarely used} } \value{ a \code{pkg_metric} containing List of functions and objects exported by a package, excluding S3methods } \description{ Assess a package's results from running R CMD check } \examples{ \dontrun{ assess_exported_namespace(pkg_ref("riskmetric")) } } \seealso{ \code{\link{metric_score.pkg_metric_exported_namespace}} } ================================================ FILE: man/assess_has_bug_reports_url.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_has_bug_reports_url.R \name{assess_has_bug_reports_url} \alias{assess_has_bug_reports_url} \title{Assess a package for the presence of a url field where bugs can be reported.} \usage{ assess_has_bug_reports_url(x, ...) } \arguments{ \item{x}{a \code{pkg_ref} package reference object} \item{...}{additional arguments passed on to S3 methods, rarely used} } \value{ a \code{pkg_metric} containing a character value containing the BugReports field contents } \description{ Assess a package for the presence of a url field where bugs can be reported. } \examples{ \dontrun{ assess_has_bug_reports_url(pkg_ref("riskmetric")) } } \seealso{ \code{\link{metric_score.pkg_metric_has_bug_reports_url}} } ================================================ FILE: man/assess_has_examples.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_has_examples.R \name{assess_has_examples} \alias{assess_has_examples} \title{Assess a package for the presence of example or usage fields in function documentation} \usage{ assess_has_examples(x, ...) } \arguments{ \item{x}{a \code{pkg_ref} package reference object} \item{...}{additional arguments passed on to S3 methods, rarely used} } \value{ a \code{pkg_metric} containing an integer value indicating the proportion of discovered files with examples } \description{ Assess a package for the presence of example or usage fields in function documentation } \examples{ \dontrun{ assess_has_examples(pkg_ref("riskmetric")) } } \seealso{ \code{\link{metric_score.pkg_metric_has_examples}} } ================================================ FILE: man/assess_has_maintainer.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_has_maintainer.R \name{assess_has_maintainer} \alias{assess_has_maintainer} \title{Assess a package for an associated maintainer} \usage{ assess_has_maintainer(x, ...) } \arguments{ \item{x}{a \code{pkg_ref} package reference object} \item{...}{additional arguments passed on to S3 methods, rarely used} } \value{ a \code{pkg_metric} containing a character vector of maintainers associated with the package } \description{ Assess a package for an associated maintainer } \examples{ \dontrun{ assess_has_maintainer(pkg_ref("riskmetric")) } } \seealso{ \code{\link{metric_score.pkg_metric_has_maintainer}} } ================================================ FILE: man/assess_has_news.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_has_news.R \name{assess_has_news} \alias{assess_has_news} \title{Assess a package for the presence of a NEWS file} \usage{ assess_has_news(x, ...) } \arguments{ \item{x}{a \code{pkg_ref} package reference object} \item{...}{additional arguments passed on to S3 methods, rarely used} } \value{ a \code{pkg_metric} containing an integer value indicating the number of discovered NEWS files } \description{ Assess a package for the presence of a NEWS file } \examples{ \dontrun{ assess_has_news(pkg_ref("riskmetric")) } } \seealso{ \code{\link{metric_score.pkg_metric_has_news}} } ================================================ FILE: man/assess_has_source_control.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_has_source_control.R \name{assess_has_source_control} \alias{assess_has_source_control} \title{Assess a package for an associated source control url} \usage{ assess_has_source_control(x, ...) } \arguments{ \item{x}{a \code{pkg_ref} package reference object} \item{...}{additional arguments passed on to S3 methods, rarely used} } \value{ a \code{pkg_metric} containing a character vector of source control urls associated with the package } \description{ Assess a package for an associated source control url } \examples{ \dontrun{ assess_has_source_control(pkg_ref("riskmetric")) } } \seealso{ \code{\link{metric_score.pkg_metric_has_source_control}} } ================================================ FILE: man/assess_has_vignettes.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_has_vignettes.R \name{assess_has_vignettes} \alias{assess_has_vignettes} \title{Assess a package for the presence of Vignettes files} \usage{ assess_has_vignettes(x, ...) } \arguments{ \item{x}{a \code{pkg_ref} package reference object} \item{...}{additional arguments passed on to S3 methods, rarely used} } \value{ a \code{pkg_metric} containing an integer value indicating the number of discovered vignettes files } \description{ Assess a package for the presence of Vignettes files } \examples{ \dontrun{ assess_has_vignettes(pkg_ref("riskmetric")) } } \seealso{ \code{\link{metric_score.pkg_metric_has_vignettes}} } ================================================ FILE: man/assess_has_website.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_has_website.R \name{assess_has_website} \alias{assess_has_website} \title{Assess a package for an associated website url} \usage{ assess_has_website(x, ...) } \arguments{ \item{x}{a \code{pkg_ref} package reference object} \item{...}{additional arguments passed on to S3 methods, rarely used} } \value{ a \code{pkg_metric} containing a character vector of website urls associated with the package } \description{ Assess a package for an associated website url } \examples{ \dontrun{ assess_has_website(pkg_ref("riskmetric")) } } \seealso{ \code{\link{metric_score.pkg_metric_has_website}} } ================================================ FILE: man/assess_last_30_bugs_status.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_last_30_bugs_status.R \name{assess_last_30_bugs_status} \alias{assess_last_30_bugs_status} \title{Assess how many recent BugReports have been closed} \usage{ assess_last_30_bugs_status(x, ...) } \arguments{ \item{x}{a \code{pkg_ref} package reference object} \item{...}{additional arguments passed on to S3 methods, rarely used} } \value{ a \code{pkg_metric} containing a logical vector indicating whether a recent BugReport was closed } \description{ Assess how many recent BugReports have been closed } \examples{ \dontrun{ assess_last_30_bugs_status(pkg_ref("riskmetric")) } } \seealso{ \code{\link{metric_score.pkg_metric_last_30_bugs_status}} } ================================================ FILE: man/assess_license.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_license.R \name{assess_license} \alias{assess_license} \title{Assess a package for an acceptable license} \usage{ assess_license(x, ...) } \arguments{ \item{x}{a \code{pkg_ref} package reference object} \item{...}{additional arguments passed on to S3 methods, rarely used} } \value{ a \code{pkg_metric} containing a string indicating the license under which the package is released } \description{ Assess a package for an acceptable license } \examples{ \dontrun{ assess_license(pkg_ref("riskmetric")) } } \seealso{ \code{\link{metric_score.pkg_metric_license}} } ================================================ FILE: man/assess_news_current.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_news_current.R \name{assess_news_current} \alias{assess_news_current} \title{Assess a package for an up-to-date NEWS file} \usage{ assess_news_current(x, ...) } \arguments{ \item{x}{a \code{pkg_ref} package reference object} \item{...}{additional arguments passed on to S3 methods, rarely used} } \value{ a \code{pkg_metric} containing a logical vector indicating whether each discovered NEWS file is up-to-date } \description{ Assess a package for an up-to-date NEWS file } \examples{ \dontrun{ assess_news_current(pkg_ref("riskmetric")) } } \seealso{ \code{\link{metric_score.pkg_metric_news_current}} } ================================================ FILE: man/assess_r_cmd_check.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_r_cmd_check.R \name{assess_r_cmd_check} \alias{assess_r_cmd_check} \title{Assess a package's results from running R CMD check} \usage{ assess_r_cmd_check(x, ...) } \arguments{ \item{x}{a \code{pkg_ref} package reference object} \item{...}{additional arguments passed on to S3 methods, rarely used} } \value{ a \code{pkg_metric} containing Tally of errors, warnings and notes from running R CMD check locally } \description{ Assess a package's results from running R CMD check } \examples{ \dontrun{ assess_r_cmd_check(pkg_ref("riskmetric")) } } \seealso{ \code{\link{metric_score.pkg_metric_r_cmd_check}} } ================================================ FILE: man/assess_remote_checks.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_remote_checks.R \name{assess_remote_checks} \alias{assess_remote_checks} \title{Assess package checks from CRAN/Bioc or R CMD check} \usage{ assess_remote_checks(x, ...) } \arguments{ \item{x}{a \code{pkg_ref} package reference object} \item{...}{additional arguments passed on to S3 methods, rarely used} } \value{ a \code{pkg_metric} containing Tally of R CMD check results run on differnt OS flavors by BioC or CRAN } \description{ Assess package checks from CRAN/Bioc or R CMD check } \examples{ \dontrun{ assess_remote_checks(pkg_ref("riskmetric")) } } \seealso{ \code{\link{metric_score.pkg_metric_remote_checks}} } ================================================ FILE: man/assess_reverse_dependencies.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_reverse_dependencies.R \name{assess_reverse_dependencies} \alias{assess_reverse_dependencies} \title{Generate list of Reverse Dependencies for a package} \usage{ assess_reverse_dependencies(x, ...) } \arguments{ \item{x}{a \code{pkg_ref} package reference object} \item{...}{additional arguments passed on to S3 methods, rarely used} } \value{ a \code{pkg_metric} containing A character vector of reverse dependencies } \description{ Generate list of Reverse Dependencies for a package } \details{ The more packages that depend on a package the more chance for errors/bugs to be found } \examples{ \dontrun{ assess_reverse_dependencies(pkg_ref("riskmetric")) } } \seealso{ \code{\link{metric_score.pkg_metric_reverse_dependencies}} } ================================================ FILE: man/assess_size_codebase.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_size_codebase.R \name{assess_size_codebase} \alias{assess_size_codebase} \title{Assess a package for size of code base} \usage{ assess_size_codebase(x, ...) } \arguments{ \item{x}{a \code{pkg_ref} package reference object} \item{...}{additional arguments passed on to S3 methods, rarely used} } \value{ a \code{pkg_metric} containing a numeric value for number of lines of code base for a package } \description{ Assess a package for size of code base } \examples{ \dontrun{ assess_size_codebase(pkg_ref("riskmetric")) } } \seealso{ \code{\link{metric_score.pkg_metric_size_codebase}} } ================================================ FILE: man/assessment_error_as_warning.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_metric_error.R \name{assessment_error_as_warning} \alias{assessment_error_as_warning} \title{Error handler for assessments to deescalate errors to warnings} \usage{ assessment_error_as_warning(e, name, assessment) } \arguments{ \item{e}{an error raised during a package reference assessment} \item{name}{the name of the package whose package reference assessment raised the error} \item{assessment}{the name of the assessment function which raised the error} } \value{ a pkg_metric object of pkg_metric_error subclass } \description{ Error handler for assessments to deescalate errors to warnings } \seealso{ Other assessment error handlers: \code{\link{assessment_error_empty}()}, \code{\link{assessment_error_throw}()} } \concept{assessment error handlers} ================================================ FILE: man/assessment_error_empty.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_metric_error.R \name{assessment_error_empty} \alias{assessment_error_empty} \title{Error handler for assessments with safe fallback} \usage{ assessment_error_empty(e, ...) } \arguments{ \item{e}{an error raised during a package reference assessment} \item{...}{additional arguments unused} } \value{ a pkg_metric object of pkg_metric_error subclass } \description{ Error handler for assessments with safe fallback } \seealso{ Other assessment error handlers: \code{\link{assessment_error_as_warning}()}, \code{\link{assessment_error_throw}()} } \concept{assessment error handlers} ================================================ FILE: man/assessment_error_throw.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_metric_error.R \name{assessment_error_throw} \alias{assessment_error_throw} \title{Error handler for assessments to throw error immediately} \usage{ assessment_error_throw(e, name, assessment) } \arguments{ \item{e}{an error raised during a package reference assessment} \item{name}{the name of the package whose package reference assessment raised the error} \item{assessment}{the name of the assessment function which raised the error} } \value{ the error encountered during assessment } \description{ Error handler for assessments to throw error immediately } \seealso{ Other assessment error handlers: \code{\link{assessment_error_as_warning}()}, \code{\link{assessment_error_empty}()} } \concept{assessment error handlers} ================================================ FILE: man/available_pkg_ref_fields.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_ref_cache.R \name{available_pkg_ref_fields} \alias{available_pkg_ref_fields} \title{A helper function for retrieving a list of available fields, identified based on implementation of a pkg_ref_cache method for a given class.} \usage{ available_pkg_ref_fields(x) } \arguments{ \item{x}{a package reference object} } \value{ a list of available fields implemented with a pkg_ref_cache method } \description{ A helper function for retrieving a list of available fields, identified based on implementation of a pkg_ref_cache method for a given class. } \keyword{internal} ================================================ FILE: man/bare_env.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_ref_class_extract.R \name{bare_env} \alias{bare_env} \title{evaluate an expression with a pkg_ref object reclassed as a bare environment object, used to sidestep pkg_ref assignment guardrails} \usage{ bare_env(x, expr, envir = parent.frame()) } \arguments{ \item{x}{a \code{pkg_ref} object} \item{expr}{an expression to evaluate, avoiding \code{pkg_ref} extraction handlers} \item{envir}{an environment in which the expression is to be evaluated} } \value{ the result of \code{expr} } \description{ evaluate an expression with a pkg_ref object reclassed as a bare environment object, used to sidestep pkg_ref assignment guardrails } \keyword{internal} ================================================ FILE: man/bug_report_metadata.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_ref_cache_bug_reports.R \name{bug_report_metadata} \alias{bug_report_metadata} \title{Helper for structuring bug reports} \usage{ bug_report_metadata(bug_reports_data, x) } \arguments{ \item{bug_reports_data}{data to represent a bug report history - generally a return object from making a request to a repository's issues API} \item{x}{a \code{pkg_ref} object where a \code{bug_reports_host} field can be found} } \value{ a \code{bug_reports_host} field } \description{ Helper for structuring bug reports } \keyword{internal} ================================================ FILE: man/cache_behaviors.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_ref_cache_behaviors.R \docType{data} \name{cache_behaviors} \alias{cache_behaviors} \title{List of available caching behaviors with metadata, including default and annotations for building documentation} \format{ An object of class \code{list} of length 1. } \usage{ cache_behaviors } \value{ a list contain cache behaviros information } \description{ List of available caching behaviors with metadata, including default and annotations for building documentation } \keyword{internal} ================================================ FILE: man/capture_expr_output.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{capture_expr_output} \alias{capture_expr_output} \title{Capture side effects issued by an evaluated expression} \usage{ capture_expr_output(expr, split = FALSE, env = parent.frame(), quoted = FALSE) } \arguments{ \item{expr}{an expression to evaluate, capturing output events as they are issued} \item{split}{logical: if \code{TRUE}, output will be sent to the new sink and to the current output stream, like the Unix program \code{tee}.} \item{env}{the environment in which \code{expr} should be evaluated, defaulting to the calling environment.} \item{quoted}{whether \code{expr} is a quoted object and should be evaluated as is, or whether the expression should be captured from the function call. Defaults to \code{FALSE}, capturing the passed expression.} } \value{ an with_eval_recording object } \description{ All messaging condition side effects are captured in the order that they are issued. } \keyword{internal} ================================================ FILE: man/dec_mutations_count.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_ref_class_extract.R \name{dec_mutations_count} \alias{dec_mutations_count} \title{decrement the number of allowed mutations} \usage{ dec_mutations_count(x) } \arguments{ \item{x}{pkg_ref object to decrement mutation counter for} } \value{ pkg_ref object } \description{ decrement the number of allowed mutations } \keyword{internal} ================================================ FILE: man/determine_pkg_source.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_ref_class.R \name{determine_pkg_source} \alias{determine_pkg_source} \title{Determine the intended source of a new package} \usage{ determine_pkg_source(x, source, repos) } \arguments{ \item{x}{Package name or path to package} \item{source}{type of source passed in `pkg_ref`} } \value{ one of c('pkg_source', 'pkg_install', 'pkg_cran_remote', 'pkg_bioc_remote', 'pkg_missing') } \description{ Determine the intended source of a new package } \keyword{internal} ================================================ FILE: man/dot-tools.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{.tools} \alias{.tools} \title{Accessor for tools namespace} \usage{ .tools() } \value{ tools namespace } \description{ used internally for - tools:::.news_reader_default } \keyword{internal} ================================================ FILE: man/examples_from_dir.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_ref_cache_examples.R \name{examples_from_dir} \alias{examples_from_dir} \title{Build logical vector for Rd objects with example or usage fields discovered in a given directory} \usage{ examples_from_dir(path, pkg) } \arguments{ \item{path}{a package directory path expected to contain exported objects} } \value{ a numeric proportion of documentation files with examples } \description{ Build logical vector for Rd objects with example or usage fields discovered in a given directory } \keyword{internal} ================================================ FILE: man/examples_from_pkg.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_ref_cache_examples.R \name{examples_from_pkg} \alias{examples_from_pkg} \title{Build logical vector for Rd objects with example or usage fields discovered in a given package} \usage{ examples_from_pkg(pkg) } \arguments{ \item{pkg}{a package name expected to contain exported objects} } \value{ a numeric proportion of documentation files with examples } \description{ Build logical vector for Rd objects with example or usage fields discovered in a given package } \keyword{internal} ================================================ FILE: man/filter_rd_db.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_ref_cache_examples.R \name{filter_rd_db} \alias{filter_rd_db} \title{Filter a simple database of Rd objects in a package for files with example fields} \usage{ filter_rd_db(rddb) } \arguments{ \item{rddb}{a simple database of Rd object obtained via tools::Rd_db} } \value{ a vector of Rd file names that have example fields } \description{ Filter a simple database of Rd objects in a package for files with example fields } \keyword{internal} ================================================ FILE: man/firstS3method.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{firstS3method} \alias{firstS3method} \title{Find the S3 method that will be evaluated when an S3 generic is called by an object of class \code{classes}} \usage{ firstS3method(f, classes, envir = parent.frame()) } \arguments{ \item{f}{a character string giving the name of the generic.} \item{classes}{a character vector of classes used to search for the appropriate S3 method} \item{envir}{the \code{\link{environment}} in which the method and its generic are searched first.} } \value{ a S3 method } \description{ Find the S3 method that will be evaluated when an S3 generic is called by an object of class \code{classes} } \keyword{internal} ================================================ FILE: man/format_assessment_message.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_metric_error.R \name{format_assessment_message} \alias{format_assessment_message} \title{Assessment console printing formatter} \usage{ format_assessment_message(e, name, assessment) } \arguments{ \item{e}{an error raised during a package reference assessment} \item{name}{the name of the package whose package reference assessment raised the error} \item{assessment}{the name of the assessment function which raised the error} } \value{ a character string of formatted text to communicate the error } \description{ make the errors and warnings consistent with meaningful indication of what triggered the error, including the name of the package whose reference triggered the error while running which asesessment. } \keyword{internal} ================================================ FILE: man/get_assessment_columns.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_assess.R \name{get_assessment_columns} \alias{get_assessment_columns} \title{Helper for retrieving a list of columns which contain pkg_metric objects} \usage{ get_assessment_columns(tbl) } \arguments{ \item{tbl}{a \code{\link[tibble]{tibble}} to select columns among} } \value{ a logical vector of \code{pkg_metric} column indices } \description{ Helper for retrieving a list of columns which contain pkg_metric objects } \keyword{internal} ================================================ FILE: man/get_assessments.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_assess.R \name{get_assessments} \alias{get_assessments} \title{Get a specific set of assess_* functions for pkg_assess} \usage{ get_assessments(fxn_string = "") } \arguments{ \item{fxn_string}{vector of assess functions} } \value{ a list of specific assess_* functions exported from riskmetric } \description{ Get a specific set of assess_* functions for pkg_assess } ================================================ FILE: man/get_package_dependencies.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_dependencies.R \name{get_package_dependencies} \alias{get_package_dependencies} \title{Gets available packages from necessary repository and filters for package of interest} \usage{ get_package_dependencies(name, repo) } \arguments{ \item{name}{package name} \item{repo}{package repository (e.g. CRAN or Bioconductor)} } \value{ Returns a data frame with two columns 1) Package names, 2) type of dependency (LinkingTo, Imports, Depends) } \description{ Gets available packages from necessary repository and filters for package of interest } \keyword{internal} ================================================ FILE: man/get_pkg_ref_classes.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_ref_class.R \name{get_pkg_ref_classes} \alias{get_pkg_ref_classes} \title{Walk the pkg_ref class hierarchy to match a single subclass to a class path} \usage{ get_pkg_ref_classes(x, classes = pkg_ref_class_hierarchy) } \arguments{ \item{x}{(`character(1L)`) A subclass, among those known in pkg_ref subclasses} \item{classes}{(`list`) A class hierarchy, described using a named list. Defaults to `pkg_ref_class_hierarchy`.} } \value{ A `character(n)` class path from `pkg_ref` down to the specified subclass, or `FALSE` if no path is found. } \description{ Walk the pkg_ref class hierarchy to match a single subclass to a class path } \keyword{internal} ================================================ FILE: man/if_not_null_else.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{if_not_null_else} \alias{if_not_null_else} \alias{\%||\%} \title{If not NULL else} \usage{ lhs \%||\% rhs } \arguments{ \item{lhs}{Left-hand side} \item{rhs}{Right-hand side A shorthand for a common comparison} } \value{ an object same as \code{lhs} or \code{rhs} } \description{ If not NULL else } \keyword{internal} ================================================ FILE: man/inc_mutations_count.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_ref_class_extract.R \name{inc_mutations_count} \alias{inc_mutations_count} \title{increment the number of allowed mutations} \usage{ inc_mutations_count(x) } \arguments{ \item{x}{pkg_ref object to increment mutation counter for} } \value{ a pkg_ref object } \description{ increment the number of allowed mutations } \keyword{internal} ================================================ FILE: man/is_url_subpath_of.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{is_url_subpath_of} \alias{is_url_subpath_of} \title{check if a url originates from a list of repo urls} \usage{ is_url_subpath_of(url, urls) } \arguments{ \item{url}{a url which may stem from one of the provided base urls} \item{urls}{vector of base urls} } \value{ logical vector indicating which base urls have a sub url of \code{url} } \description{ check if a url originates from a list of repo urls } \keyword{internal} ================================================ FILE: man/memoise_bioc_mirrors.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils_memoised.R \name{memoise_bioc_mirrors} \alias{memoise_bioc_mirrors} \title{Fetch BioC Mirrors Info} \usage{ memoise_bioc_mirrors() } \value{ a data frame with mirror information } \description{ taken from utils::chooseBioCmirror } \keyword{internal} ================================================ FILE: man/memoise_cran_mirrors.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils_memoised.R \name{memoise_cran_mirrors} \alias{memoise_cran_mirrors} \title{Fetch CRAN Mirrors Info} \usage{ memoise_cran_mirrors(all = TRUE, ..., .local = getOption("riskmetric.tests")) } \arguments{ \item{all}{default \code{TRUE}, passed to \code{\link{utils}[getCRANmirrors]}} \item{...}{additional arguments passed to \code{\link{utils}[getCRANmirrors]}} \item{.local}{an optional local directory to source the CRAN package index from, defaulting to \code{getOption("riskmetric.tests")}, used for isolating repository requests during testing.} } \value{ a data frame with mirror information } \description{ Fetch CRAN Mirrors Info } \keyword{internal} ================================================ FILE: man/metric_score.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/metric_score.R \name{metric_score} \alias{metric_score} \title{Score a package metric} \usage{ metric_score(x, ...) } \arguments{ \item{x}{A \code{pkg_metric_*} class object to score} \item{...}{Additional arguments unused} } \value{ score of a package risk metric } \description{ Convert a package metric into a numeric value between 0 to 1 } ================================================ FILE: man/metric_score.pkg_metric_covr_coverage.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_covr_coverage.R \name{metric_score.pkg_metric_covr_coverage} \alias{metric_score.pkg_metric_covr_coverage} \title{Score a package for unit test coverage} \usage{ \method{metric_score}{pkg_metric_covr_coverage}(x, ...) } \arguments{ \item{x}{a \code{pkg_metric_covr_coverage} packge metric object} \item{...}{additional arguments unused} } \value{ A \code{numeric} } \description{ Returns the overall test coverage from a covr coverage report } \examples{ \dontrun{metric_score(assess_covr_coverage(pkg_ref("riskmetric"))) } } ================================================ FILE: man/metric_score.pkg_metric_dependencies.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_dependencies.R \name{metric_score.pkg_metric_dependencies} \alias{metric_score.pkg_metric_dependencies} \title{Score a package for dependencies} \usage{ \method{metric_score}{pkg_metric_dependencies}(x, ...) } \arguments{ \item{x}{a \code{pkg_metric_dependencies} packge metric object} \item{...}{additional arguments unused} } \value{ numeric value between \code{0} (high number of dependencies) and \code{1} (low number of dependencies) } \description{ Calculates a regularized score based on the number of dependencies a package has. Convert the number of dependencies \code{NROW(x)} into a validation score [0,1] \deqn{ 1 - 1 / (1 + exp(-0.5 * (NROW(x) + 4))) } } \details{ The scoring function is the classic logistic curve \deqn{ / (1 + exp(-k(x-x[0])) } \eqn{x = NROW(x)}, sigmoid midpoint is 5 reverse dependencies, ie. \eqn{x[0] = 4}, and logistic growth rate of \eqn{k = 0.5}. \deqn{ 1 - 1 / (1 + exp(NROW(x)-4)) } } \examples{ \dontrun{metric_score(assess_dependencies(pkg_ref("riskmetric"))) } } ================================================ FILE: man/metric_score.pkg_metric_downloads_1yr.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_downloads.R \name{metric_score.pkg_metric_downloads_1yr} \alias{metric_score.pkg_metric_downloads_1yr} \title{Defining an Assessment Scoring Function} \usage{ \method{metric_score}{pkg_metric_downloads_1yr}(x, ...) } \arguments{ \item{x}{a \code{pkg_metric_downloads_1yr} packge metric object} \item{...}{additional arguments unused} } \value{ numeric value between \code{0} (low) and \code{1} (high download volume) converting the number of downloads. } \description{ Score a package for the number of downloads in the past year regularized Convert the number of downloads \code{x} in the past year into a validation score [0,1] \deqn{ 1 - 150,000 / (x + 150,000) } } \details{ The scoring function is a simplification of the classic logistic curve \deqn{ 1 / (1 + exp(-k(x-x[0])) } with a log scale for the number of downloads \eqn{x = log(x)}, sigmoid midpoint is 1000 downloads, ie. \eqn{x[0] = log(1,000)}, and logistic growth rate of \eqn{k = 0.5}. \deqn{ 1 - 1 / (1 + exp(log(x)-log(1.5e5))) = 1 - 150,000 / (x + 150,000) } } \examples{ \dontrun{metric_score(assess_downloads_1yr(pkg_ref("riskmetric"))) } } ================================================ FILE: man/metric_score.pkg_metric_export_help.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_export_help.R \name{metric_score.pkg_metric_export_help} \alias{metric_score.pkg_metric_export_help} \title{Score a package for availability of documentation for exported values} \usage{ \method{metric_score}{pkg_metric_export_help}(x, ...) } \arguments{ \item{x}{a \code{pkg_metric_export_help} packge metric object} \item{...}{additional arguments unused} } \value{ \code{1} if any NEWS files are found, otherwise \code{0} } \description{ Coerce a logical vector indicating availability of export documentation } \examples{ \dontrun{metric_score(assess_export_help(pkg_ref("riskmetric"))) } } ================================================ FILE: man/metric_score.pkg_metric_exported_namespace.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_exported_namespace.R \name{metric_score.pkg_metric_exported_namespace} \alias{metric_score.pkg_metric_exported_namespace} \title{Score a package for the number of exported objects} \usage{ \method{metric_score}{pkg_metric_exported_namespace}(x, ...) } \arguments{ \item{x}{a \code{pkg_metric_exported_namespace} packge metric object} \item{...}{additional arguments unused} } \value{ numeric value between \code{0} (high number of exported objects) and \code{1} (low number of exported objects) } \description{ Score a package for the number of exported objects it has; regularized Convert the number of exported objects \code{length(x)} into a validation score [0,1] \deqn{ 1 / (1 + exp(-0.5 * (sqrt(length(x)) + sqrt(5)))) } } \details{ The scoring function is the classic logistic curve \deqn{ 1 / (1 + exp(-k(x-x[0])) } with a square root scale for the number of exported objects \eqn{x = sqrt(length(x))}, sigmoid midpoint is 25 exported objects, ie. \eqn{x[0] = sqrt(5)}, and logistic growth rate of \eqn{k = 0.25}. \deqn{ 1 / (1 + exp(-0.25 * sqrt(length(x))-sqrt(25))) } } \examples{ \dontrun{metric_score(assess_exported_namespace(pkg_ref("riskmetric"))) } } ================================================ FILE: man/metric_score.pkg_metric_has_bug_reports_url.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_has_bug_reports_url.R \name{metric_score.pkg_metric_has_bug_reports_url} \alias{metric_score.pkg_metric_has_bug_reports_url} \title{Score a package for the presence of a bug report url} \usage{ \method{metric_score}{pkg_metric_has_bug_reports_url}(x, ...) } \arguments{ \item{x}{a \code{pkg_metric_has_bug_reports_url} packge metric object} \item{...}{additional arguments unused} } \value{ A logical value indicating whether the package has a BugReports field filled in } \description{ Score a package for the presence of a bug report url } \examples{ \dontrun{metric_score(assess_has_bug_reports_url(pkg_ref("riskmetric"))) } } ================================================ FILE: man/metric_score.pkg_metric_has_examples.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_has_examples.R \name{metric_score.pkg_metric_has_examples} \alias{metric_score.pkg_metric_has_examples} \title{Score a package for the presence of a example or usage fields} \usage{ \method{metric_score}{pkg_metric_has_examples}(x, ...) } \arguments{ \item{x}{a \code{pkg_metric_has_examples} packge metric object} \item{...}{additional arguments unused} } \value{ \code{1} if any example or usage fields are found, otherwise \code{0} } \description{ Coerce a logical vector indicating availability of example or usage documentation } \examples{ \dontrun{metric_score(assess_has_examples(pkg_ref("riskmetric"))) } } ================================================ FILE: man/metric_score.pkg_metric_has_maintainer.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_has_maintainer.R \name{metric_score.pkg_metric_has_maintainer} \alias{metric_score.pkg_metric_has_maintainer} \title{Score a package for inclusion of an associated maintainer} \usage{ \method{metric_score}{pkg_metric_has_maintainer}(x, ...) } \arguments{ \item{x}{a \code{pkg_metric_has_maintainer} packge metric object} \item{...}{additional arguments unused} } \value{ \code{1} if any maintainer is provided, otherwise \code{0} } \description{ Coerce a list of maintainers into a numeric value indicating whether the number of listed maintainers is greater than 0. } \examples{ \dontrun{metric_score(assess_has_maintainer(pkg_ref("riskmetric"))) } } ================================================ FILE: man/metric_score.pkg_metric_has_news.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_has_news.R \name{metric_score.pkg_metric_has_news} \alias{metric_score.pkg_metric_has_news} \title{Score a package for the presence of a NEWS file} \usage{ \method{metric_score}{pkg_metric_has_news}(x, ...) } \arguments{ \item{x}{a \code{pkg_metric_has_news} packge metric object} \item{...}{additional arguments unused} } \value{ \code{1} if any NEWS files are found, otherwise \code{0} } \description{ Coerce the number of news files to binary indication of valid NEWS files } \examples{ \dontrun{metric_score(assess_has_news(pkg_ref("riskmetric"))) } } ================================================ FILE: man/metric_score.pkg_metric_has_source_control.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_has_source_control.R \name{metric_score.pkg_metric_has_source_control} \alias{metric_score.pkg_metric_has_source_control} \title{Score a package for inclusion of an associated source control url} \usage{ \method{metric_score}{pkg_metric_has_source_control}(x, ...) } \arguments{ \item{x}{a \code{pkg_metric_has_source_control} packge metric object} \item{...}{additional arguments unused} } \value{ \code{1} if any source control url is provided, otherwise \code{0} } \description{ Coerce a list of source control urls into a numeric value indicating whether the number of listed urls is greater than 0. } \examples{ \dontrun{metric_score(assess_has_source_control(pkg_ref("riskmetric"))) } } ================================================ FILE: man/metric_score.pkg_metric_has_vignettes.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_has_vignettes.R \name{metric_score.pkg_metric_has_vignettes} \alias{metric_score.pkg_metric_has_vignettes} \title{Score a package for the presence of a Vignettes file} \usage{ \method{metric_score}{pkg_metric_has_vignettes}(x, ...) } \arguments{ \item{x}{a \code{pkg_metric_has_vignettes} packge metric object} \item{...}{additional arguments unused} } \value{ \code{1} if any Vignettes files are found, otherwise \code{0} } \description{ Coerce the number of vignettes files to binary indication of valid Vignettes } \examples{ \dontrun{metric_score(assess_has_vignettes(pkg_ref("riskmetric"))) } } ================================================ FILE: man/metric_score.pkg_metric_has_website.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_has_website.R \name{metric_score.pkg_metric_has_website} \alias{metric_score.pkg_metric_has_website} \title{Score a package for inclusion of an associated website url} \usage{ \method{metric_score}{pkg_metric_has_website}(x, ...) } \arguments{ \item{x}{a \code{pkg_metric_has_website} packge metric object} \item{...}{additional arguments unused} } \value{ \code{1} if any website url is provided, otherwise \code{0} } \description{ Coerce a list of website urls into a numeric value indicating whether the number of listed urls is greater than 0. } \examples{ \dontrun{metric_score(assess_has_website(pkg_ref("riskmetric"))) } } ================================================ FILE: man/metric_score.pkg_metric_last_30_bugs_status.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_last_30_bugs_status.R \name{metric_score.pkg_metric_last_30_bugs_status} \alias{metric_score.pkg_metric_last_30_bugs_status} \title{Score a package for number of recently opened BugReports that are now closed} \usage{ \method{metric_score}{pkg_metric_last_30_bugs_status}(x, ...) } \arguments{ \item{x}{a \code{pkg_metric_last_30_bugs_status} packge metric object} \item{...}{additional arguments unused} } \value{ a fractional value indicating percentage of last 30 bug reports that are now closed } \description{ Score a package for number of recently opened BugReports that are now closed } \examples{ \dontrun{metric_score(assess_last_30_bugs_status(pkg_ref("riskmetric"))) } } ================================================ FILE: man/metric_score.pkg_metric_license.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_license.R \name{metric_score.pkg_metric_license} \alias{metric_score.pkg_metric_license} \title{Score a package for acceptable license} \usage{ \method{metric_score}{pkg_metric_license}(x, ...) } \arguments{ \item{x}{a \code{pkg_metric_license} packge metric object} \item{...}{additional arguments unused} } \value{ score of metric license } \description{ Maps a license string to a score } \examples{ \dontrun{metric_score(assess_license(pkg_ref("riskmetric"))) } } ================================================ FILE: man/metric_score.pkg_metric_news_current.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_news_current.R \name{metric_score.pkg_metric_news_current} \alias{metric_score.pkg_metric_news_current} \title{Score a package for NEWS files updated to current version} \usage{ \method{metric_score}{pkg_metric_news_current}(x, ...) } \arguments{ \item{x}{a \code{pkg_metric_news_current} packge metric object} \item{...}{additional arguments unused} } \value{ \code{1} if any NEWS files are up-to-date, otherwise \code{0} } \description{ Coerce a logical vector of discovered up-to-date NEWS to a metric score } \examples{ \dontrun{metric_score(assess_news_current(pkg_ref("riskmetric"))) } } ================================================ FILE: man/metric_score.pkg_metric_r_cmd_check.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_r_cmd_check.R \name{metric_score.pkg_metric_r_cmd_check} \alias{metric_score.pkg_metric_r_cmd_check} \title{Score a package based on R CMD check results run locally} \usage{ \method{metric_score}{pkg_metric_r_cmd_check}(x, ...) } \arguments{ \item{x}{a \code{pkg_metric_r_cmd_check} packge metric object} \item{...}{additional arguments unused} } \value{ A weighted sum of errors and warnings of all tests preformed } \description{ The scoring function is the weighted sum of notes (0.1), errors (1) and warnings (0.25), with a maximum score of 1 (no errors, notes or warnings) and a minimum score of 0. Essentially, the metric will allow up to 10 notes, 1 error or 4 warnings before returning the lowest score of 0 } \examples{ \dontrun{metric_score(assess_r_cmd_check(pkg_ref("riskmetric"))) } } ================================================ FILE: man/metric_score.pkg_metric_remote_checks.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_remote_checks.R \name{metric_score.pkg_metric_remote_checks} \alias{metric_score.pkg_metric_remote_checks} \title{Score a package based on R CMD check results run by BioC or CRAN} \usage{ \method{metric_score}{pkg_metric_remote_checks}(x, ...) } \arguments{ \item{x}{a \code{pkg_metric_remote_checks} packge metric object} \item{...}{additional arguments unused} } \value{ a fractional value indicating percentage OS flavors that did not produce an error or warning from R CMD check } \description{ The scoring function is the number of OS flavors that passed with OK or NOTES + 0.5*the number of OS's that produced WARNINGS divided by the number of OS's checked } \examples{ \dontrun{metric_score(assess_remote_checks(pkg_ref("riskmetric"))) } } ================================================ FILE: man/metric_score.pkg_metric_reverse_dependencies.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_reverse_dependencies.R \name{metric_score.pkg_metric_reverse_dependencies} \alias{metric_score.pkg_metric_reverse_dependencies} \title{Scoring method for number of reverse dependencies a package has} \usage{ \method{metric_score}{pkg_metric_reverse_dependencies}(x, ...) } \arguments{ \item{x}{a \code{pkg_metric_reverse_dependencies} packge metric object} \item{...}{additional arguments unused} } \value{ numeric value between \code{1} (high number of reverse dependencies) and \code{0} (low number of reverse dependencies) } \description{ Score a package for the number of reverse dependencies it has; regularized Convert the number of reverse dependencies \code{length(x)} into a validation score [0,1] \deqn{ 1 / (1 + exp(-0.5 * (sqrt(length(x)) + sqrt(5)))) } } \details{ The scoring function is the classic logistic curve \deqn{ 1 / (1 + exp(-k(x-x[0])) } with a square root scale for the number of reverse dependencies \eqn{x = sqrt(length(x))}, sigmoid midpoint is 5 reverse dependencies, ie. \eqn{x[0] = sqrt(5)}, and logistic growth rate of \eqn{k = 0.5}. \deqn{ 1 / (1 + -0.5 * exp(sqrt(length(x)) - sqrt(5))) } } \examples{ \dontrun{metric_score(assess_reverse_dependencies(pkg_ref("riskmetric"))) } } ================================================ FILE: man/metric_score.pkg_metric_size_codebase.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_size_codebase.R \name{metric_score.pkg_metric_size_codebase} \alias{metric_score.pkg_metric_size_codebase} \title{Score a package for number of lines of code} \usage{ \method{metric_score}{pkg_metric_size_codebase}(x, ...) } \arguments{ \item{x}{a \code{pkg_metric_size_codebase} packge metric object} \item{...}{additional arguments unused} } \value{ numeric value between \code{0} (for large codebase) and \code{1} (for small codebase) } \description{ Scores packages based on its codebase size, as determined by number of lines of code. } \examples{ \dontrun{metric_score(assess_size_codebase(pkg_ref("riskmetric"))) } } ================================================ FILE: man/news_from_dir.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_ref_cache_news.R \name{news_from_dir} \alias{news_from_dir} \title{Build a list of NEWS files discovered within a given directory} \usage{ news_from_dir(path) } \arguments{ \item{path}{a package directory path expected to contain NEWS files} } \value{ a list of parsed NEWS files } \description{ Build a list of NEWS files discovered within a given directory } \keyword{internal} ================================================ FILE: man/parse_dcf_dependencies.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_dependencies.R \name{parse_dcf_dependencies} \alias{parse_dcf_dependencies} \title{Parse DCF of description file} \usage{ parse_dcf_dependencies(path) } \arguments{ \item{path}{pkg_ref path} } \description{ Parse DCF of description file } \keyword{internal} ================================================ FILE: man/pkg_assess.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_assess.R \name{pkg_assess} \alias{pkg_assess} \title{Apply assess_* family of functions to a package reference} \usage{ pkg_assess( x, assessments = all_assessments(), ..., error_handler = assessment_error_empty ) } \arguments{ \item{x}{A single \code{\link{pkg_ref}} object or \code{\link[tibble]{tibble}} of package references to assess} \item{assessments}{A list of assessment functions to apply to each package reference. By default, a list of all exported assess_* functions from the riskmetric package.} \item{...}{additional arguments unused} \item{error_handler}{A function, which accepts a single parameter expecting the raised error, which will be called if any errors occur when attempting to apply an assessment function.} } \value{ Either a \code{list_of_pkg_metric} object when a single \code{pkg_ref} object is passed as \code{x}, or a \code{\link[tibble]{tibble}} of metrics when a \code{list_of_pkg_ref} or \code{tibble} is passed as \code{x}. When a \code{\link[tibble]{tibble}} is returned, it has one row per package reference and a new column per assessment function, with cells of that column as package metric objects returned when the assessment was called with the associated pacakge reference. } \description{ By default, use all \code{assess_*} funtions in the \code{riskmetric} namespace and produce a \code{\link[tibble]{tibble}} with one column per assessment applied. } \section{Assessment function catalog}{ \describe{ \item{\code{\link{assess_covr_coverage}}}{Package unit test coverage} \item{\code{\link{assess_has_news}}}{number of discovered NEWS files} \item{\code{\link{assess_remote_checks}}}{Number of OS flavors that passed/warned/errored on R CMD check} \item{\code{\link{assess_news_current}}}{NEWS file contains entry for current version number} \item{\code{\link{assess_r_cmd_check}}}{Package check results} \item{\code{\link{assess_exported_namespace}}}{Objects exported by package} \item{\code{\link{assess_has_vignettes}}}{number of discovered vignettes files} \item{\code{\link{assess_export_help}}}{exported objects have documentation} \item{\code{\link{assess_has_website}}}{a vector of associated website urls} \item{\code{\link{assess_has_maintainer}}}{a vector of associated maintainers} \item{\code{\link{assess_last_30_bugs_status}}}{vector indicating whether BugReports status is closed} \item{\code{\link{assess_size_codebase}}}{number of lines of code base} \item{\code{\link{assess_has_source_control}}}{a vector of associated source control urls} \item{\code{\link{assess_has_bug_reports_url}}}{presence of a bug reports url in repository} \item{\code{\link{assess_downloads_1yr}}}{number of downloads in the past year} \item{\code{\link{assess_reverse_dependencies}}}{List of reverse dependencies a package has} \item{\code{\link{assess_has_examples}}}{proportion of discovered function files with examples} \item{\code{\link{assess_dependencies}}}{Package dependency footprint} \item{\code{\link{assess_license}}}{software is released with an acceptable license} } } ================================================ FILE: man/pkg_metric.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_metric.R \name{pkg_metric} \alias{pkg_metric} \title{A helper for structuring assessment return objects for dispatch with the score function} \usage{ pkg_metric(x = NA, ..., class = c()) } \arguments{ \item{x}{data to store as a \code{pkg_metric}} \item{...}{additional attributes to bind to the \code{pkg_metric} object} \item{class}{a subclass to differentiate the \code{pkg_metric} object} } \value{ a \code{pkg_metric} object } \description{ A helper for structuring assessment return objects for dispatch with the score function } ================================================ FILE: man/pkg_metric_eval.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_metric.R \name{pkg_metric_eval} \alias{pkg_metric_eval} \title{Evaluate a metric} \usage{ pkg_metric_eval(expr, ..., class = c(), env = parent.frame()) } \arguments{ \item{expr}{An expression to evaluate in order to calculate a \code{pkg_metric}} \item{...}{additional attributes to bind to the \code{pkg_metric} object} \item{class}{a subclass to differentiate the \code{pkg_metric} object} \item{env}{An environment in which \code{expr} is to be evaluated} } \value{ a \code{pkg_metric} object containing the result of \code{expr} } \description{ Evalute code relevant to a metric, capturing the evaluated code as well as any messages, warnings or errors that are thrown in the process. } \keyword{internal} ================================================ FILE: man/pkg_ref.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_ref_class.R \name{pkg_ref} \alias{pkg_ref} \alias{pkg_install} \alias{pkg_source} \alias{pkg_cran} \alias{pkg_bioc} \alias{pkg_missing} \alias{pkg_library} \alias{as_pkg_ref} \title{Create a package reference} \usage{ pkg_ref(x, ...) pkg_install(x, lib.loc = NULL) pkg_source(x) pkg_cran(x, repos = getOption("repos", "https://cran.rstudio.com")) pkg_bioc(x) pkg_missing(x) pkg_library(lib.loc) as_pkg_ref(x, ...) } \arguments{ \item{x}{A singular \code{character} value, \code{character vector} or \code{list} of \code{character} values of package names or source code directory paths.} \item{...}{Additional arguments passed to methods.} \item{lib.loc}{The path to the R library directory of the installed package.} \item{repos}{URL of CRAN repository to pull package metadata.} } \value{ When a single value is provided, a single \code{pkg_ref} object is returned, possibly with a subclass based on where the package was found. If a \code{vector} or \code{list} is provided, a \code{list_of_pkg_ref} object constructed with \code{\link[vctrs]{list_of}} is returned, which can be considered analogous to a \code{list}. See 'Details' for further information about \code{pkg_ref} subclasses. } \description{ Create a package reference from package name or filepath, producing an object in which package metadata will be collected as risk assessments are performed. Depending on where the package was found - whether it is found as source code, in a local library or from a remote host - an S3 subclass is given to allow for source-specific collection of metadata. See 'Details' for a breakdown of subclasses. Different sources can be specified by passing a subclass as an arguemnt named 'source', see details. } \details{ Package reference objects are used to collect metadata pertaining to a given package. As data is needed for assessing a package's risk, this metadata populates fields within the package reference object. The \code{pkg_ref} S3 subclasses are used extensively for divergent metadata collection behaviors dependent on where the package was discovered. Because of this, there is a rich hierarchy of subclasses to articulate the different ways package information can be found. A source argument can be passed using the `source` argument. This will override the logic that riskmetric does when determining a package source. This can be useful when you are scoring the most recent version present on a repository, or testing a specific library. \describe{ \item{\strong{\code{pkg_ref}}}{ A default class for general metadata collection.} \item{\strong{\code{pkg_source}}}{ A reference to a source code directory.} \item{\strong{\code{pkg_install}}}{ A reference to a package installation location in a package library. A specific library can be passed by passing the path to the library as the parameter `lib.loc`} \item{\strong{\code{pkg_remote}}}{ A reference to package metadata on a remote server. \describe{ \item{\strong{\code{pkg_cran_remote}}}{ A reference to package information pulled from the CRAN repository.} \item{\strong{\code{pkg_bioc_remote}}}{ A reference to package information pulled from the Bioconductor repository.} \item{\strong{\code{pkg_git_remote}}}{ A reference to a package source code git repository. (not yet implemented)} } } } } \section{Package Cohorts}{ *Experimental!* Package cohorts are structures to determine the risk of a set of packages. `pkg_library()` can be called to create a object containing the pkg_ref objects of all packages in a system library. } \examples{ \dontrun{ # riskmetric will check for installed packages by default ref_1 <- pkg_ref("utils") ref_1$source # returns 'pkg_install' # lib.loc can be used to specify a library for pkg_install ref_3 <- pkg_ref("utils", source = "pkg_install", lib.loc = .libPaths()[1]) # You can also override this behavior with a source argument ref_2 <- pkg_ref("utils", source = "pkg_cran_remote") ref_2$source # returns 'pkg_cran_remote' } } ================================================ FILE: man/pkg_ref_cache.bug_reports_host.default.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_ref_cache_bug_reports_host.R \name{pkg_ref_cache.bug_reports_host.default} \alias{pkg_ref_cache.bug_reports_host.default} \title{Get the host name of a BugReports url} \usage{ \method{pkg_ref_cache}{bug_reports_host.default}(x, ...) } \value{ a \code{pkg_ref} object } \description{ Get the host name of a BugReports url } \keyword{internal} ================================================ FILE: man/pkg_ref_cache.bug_reports_url.pkg_source.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_ref_cache_bug_reports_url.R \name{pkg_ref_cache.bug_reports_url.pkg_source} \alias{pkg_ref_cache.bug_reports_url.pkg_source} \title{Get the BugReports url} \usage{ \method{pkg_ref_cache}{bug_reports_url.pkg_source}(x, ...) } \value{ a \code{pkg_ref} object } \description{ Get the BugReports url } \keyword{internal} ================================================ FILE: man/pkg_ref_cache.covr_coverage.pkg_source.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_ref_cache_covr_coverage.R \name{pkg_ref_cache.covr_coverage.pkg_source} \alias{pkg_ref_cache.covr_coverage.pkg_source} \title{Retrieve output of covr::package_coverage} \usage{ \method{pkg_ref_cache}{covr_coverage.pkg_source}(x, ...) } \value{ a \code{pkg_ref} object } \description{ Retrieve output of covr::package_coverage } \keyword{internal} ================================================ FILE: man/pkg_ref_cache.expression_coverage.pkg_source.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_ref_cache_expr_coverage.R \name{pkg_ref_cache.expression_coverage.pkg_source} \alias{pkg_ref_cache.expression_coverage.pkg_source} \title{Retrieve output of covr::package_coverage, tallied by expression} \usage{ \method{pkg_ref_cache}{expression_coverage.pkg_source}(x, ...) } \value{ a \code{pkg_ref} object } \description{ Retrieve output of covr::package_coverage, tallied by expression } \keyword{internal} ================================================ FILE: man/pkg_ref_cache.help.pkg_install.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_ref_cache_help.R \name{pkg_ref_cache.help.pkg_install} \alias{pkg_ref_cache.help.pkg_install} \title{Cache a list of available help files as LaTeX objects} \usage{ \method{pkg_ref_cache}{help.pkg_install}(x, name, ...) } \value{ a \code{pkg_ref} object } \description{ Cache a list of available help files as LaTeX objects } \keyword{internal} ================================================ FILE: man/pkg_ref_cache.help.pkg_source.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_ref_cache_help.R \name{pkg_ref_cache.help.pkg_source} \alias{pkg_ref_cache.help.pkg_source} \title{Cache a list of available help files as LaTeX objects} \usage{ \method{pkg_ref_cache}{help.pkg_source}(x, name, ...) } \description{ Cache a list of available help files as LaTeX objects } \keyword{internal} ================================================ FILE: man/pkg_ref_cache.news.pkg_remote.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_ref_cache_news.R \name{pkg_ref_cache.news.pkg_remote} \alias{pkg_ref_cache.news.pkg_remote} \title{Cache a list of NEWS files from a package reference} \usage{ \method{pkg_ref_cache}{news.pkg_remote}(x, name, ...) } \value{ a \code{pkg_ref} object } \description{ Cache a list of NEWS files from a package reference } \keyword{internal} ================================================ FILE: man/pkg_ref_class_hierarchy.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_ref_class.R \docType{data} \name{pkg_ref_class_hierarchy} \alias{pkg_ref_class_hierarchy} \title{The `pkg_ref` subclass hierarchy, used for pkg_ref object creation with a specified subclass} \format{ An object of class \code{list} of length 1. } \usage{ pkg_ref_class_hierarchy } \description{ The `pkg_ref` subclass hierarchy, used for pkg_ref object creation with a specified subclass } \keyword{datasets} ================================================ FILE: man/pkg_ref_mutability_error.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_ref_class_extract.R \name{pkg_ref_mutability_error} \alias{pkg_ref_mutability_error} \title{pretty printing for a pkg_ref mutability error caused by trying to do assignment within the pkg_ref without permission} \usage{ pkg_ref_mutability_error(name) } \arguments{ \item{name}{name of field for which mutation was attempted} } \value{ a \code{simplError} with subclasses \code{pkg_ref_mutability_error}, \code{pkg_ref_error} } \description{ pretty printing for a pkg_ref mutability error caused by trying to do assignment within the pkg_ref without permission } \keyword{internal} ================================================ FILE: man/pkg_score.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_score.R \name{pkg_score} \alias{pkg_score} \title{Score a package assessment, collapsing results into a single numeric} \usage{ pkg_score(x, ..., error_handler = score_error_default) } \arguments{ \item{x}{A \code{pkg_metric} object, whose subclass is used to choose the appropriate scoring method for the atomic metric metadata. Optionally, a \code{\link[tibble]{tibble}} can be provided, in which cases all \code{pkg_metric} values will be scored.} \item{...}{Additional arguments passed to \code{summarize_scores} when an object of class \code{tbl_df} is provided, unused otherwise.} \item{error_handler}{Specify a function to be called if the class can't be identified. Most commonly this occurs for \code{pkg_metric} objects of subclass \code{pkg_metric_error}, which is produced when an error is encountered when calculating an associated assessment.} } \value{ A numeric value if a single \code{pkg_metric} is provided, or a \code{\link[tibble]{tibble}} with \code{pkg_metric} objects scored and returned as numeric values when a \code{\link[tibble]{tibble}} is provided. } \description{ pkg_score() calculates the risk involved with using a package. Risk ranges from 0 (low-risk) to 1 (high-risk). } \examples{ \dontrun{ # scoring a single assessment metric_score(assess_has_news(pkg_ref("riskmetric"))) # scoring many assessments as a tibble library(dplyr) pkg_score(pkg_assess(as_tibble(pkg_ref(c("riskmetric", "riskmetric"))))) } } \seealso{ score_error_default score_error_zero score_error_NA } ================================================ FILE: man/print.with_eval_recording.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{print.with_eval_recording} \alias{print.with_eval_recording} \title{Handle pretty printing of expression output} \usage{ \method{print}{with_eval_recording}(x, playback = FALSE, cr = TRUE, ..., sleep = 0) } \arguments{ \item{x}{expr_output to print} \item{playback}{a \code{logical} indicating whether evaluation output should be played back (\code{FALSE}), or whether the result value should be printed as is (\code{TRUE}, the default)} \item{cr}{a \code{logical} indicating whether carriage returns should be printed, possibly overwriting characters in the output.} \item{...}{additional arguments unused} \item{sleep}{an \code{numeric} indicating a time to sleep between printing each line to console. This can be helpful if the original output overwrites valuable information in the log that is eventually overwritten and you would like to watch it play out as it was formatted.} } \value{ a print message } \description{ Handle pretty printing of expression output } \keyword{internal} ================================================ FILE: man/remove_base_packages.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/assess_dependencies.R \name{remove_base_packages} \alias{remove_base_packages} \title{Helper function to remove base and recommended packages} \usage{ remove_base_packages(df) } \arguments{ \item{df}{Data frame of dependencies of a package.} } \description{ Helper function to remove base and recommended packages } \keyword{internal} ================================================ FILE: man/require_cache_behaviors.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_ref_cache_behaviors.R \name{require_cache_behaviors} \alias{require_cache_behaviors} \title{Stop if a function requires disabled behaviors} \usage{ require_cache_behaviors(behaviors) } \arguments{ \item{behaviors}{a character vector of behavior flags to assert as requirements for metadata caching. values must have an entry found in riskmetric:::cache_behaviors list} } \value{ a boolean value } \description{ Stop if a function requires disabled behaviors } \keyword{internal} ================================================ FILE: man/riskmetric.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/riskmetric-package.R \docType{package} \name{riskmetric} \alias{riskmetric-package} \alias{riskmetric} \title{riskmetric} \description{ Facilities for assessing R packages against a number of metrics to help quantify their robustness. } \seealso{ Useful links: \itemize{ \item \url{https://pharmar.github.io/riskmetric/} \item \url{https://github.com/pharmaR/riskmetric} \item Report bugs at \url{https://github.com/pharmaR/riskmetric/issues} } } \author{ \strong{Maintainer}: Eli Miller \email{eli.miller@atorusresearch.com} Authors: \itemize{ \item R Validation Hub \email{psi.aims.r.validation@gmail.com} \item Doug Kelkhoff \email{doug.kelkhoff@gmail.com} \item Marly Gotti \item Kevin K \item Yilong Zhang \item Eric Milliman \item Juliane Manitz } Other contributors: \itemize{ \item Mark Padgham [contributor] \item PSI special interest group Application and Implementation of Methodologies in Statistics [copyright holder] } } \keyword{internal} ================================================ FILE: man/riskmetric_metadata_caching.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_ref_cache.R \name{pkg_ref_cache} \alias{pkg_ref_cache} \title{S3 generic to calculate a `pkg_ref` field} \value{ a \code{pkg_ref} field } \description{ Reactively retrieve and cache `pkg_ref` metadata } \section{Caching Details}{ \subsection{\code{pkg_ref} class fields}{ The \code{pkg_ref} class structures an environment with special handling for indexing into the \code{pkg_ref} class using the \code{$} or \code{[[} operators. For all intents and purposes, the \code{pkg_ref} class is works conceptually similar to a lazy, immutable \code{list}, and uses the \code{pkg_ref_cache} function internally to lazily retrieve package reference fields. } \subsection{Lazy metadata caching}{ Laziness in a \code{pkg_ref} object refers to the delayed evaluation of the contents of its fields. Since some metadata is time or computationally intensive to retrieve, and unnessary for some assessments, we want to avoid that retrieval until it is needed. The first time that a field is accessed within a \code{pkg_ref} object \code{x}, a corresponding \code{pkg_ref_cache} S3 generic is called. For example, when \code{x$description} is first accessed, the \code{pkg_ref} object uses the function \code{pkg_ref_cache.description} to attempt to retrieve the contents of the corresponding \code{DESCRIPTION} file. Often, the way that this data is collected might be different depending on the subclass of the \code{pkg_ref}. In the case of the \code{description} metadata, a reference to a local install might be able to read in a local file directly, whereas a reference to a remote source of metadata might require first downloading the file. For this reason, many \code{pkg_ref_cache.*} functions are themselves S3 generics that dispatch on the class of the \code{pkg_ref} object, allowing for divergent behaviors for different source of package metadata. } \subsection{\code{pkg_ref} field immutability}{ Once a field has been calculated, its value is immutable. This behavior was chosen because of the long time frame over which package metadata changes, rendering it unnecessary to continually reevaluate fields each time they are accesssed. This means that within an assessment, a given field for a package will only ever be calculated once and preserved for downstream use. } } \examples{ \dontrun{ # implementing a new field called "first_letter" that is consistently derived # across all pkg_ref objects: pkg_ref_cache.first_letter <- function(x, name, ...) { substring(x$name, 1, 1) } x <- pkg_ref("riskmetric") x$first_letter # implementing a new field called "subclass_enum" that dispatches on # the subclass of the pkg_ref object: pkg_ref_cache.subclass_enum <- function(x, name, ...) { UseMethod("pkg_ref_cache.subclass_enum") } pkg_ref_cache.subclass_enum.pkg_ref <- function(x, name, ...) { 0 } pkg_ref_cache.subclass_enum.pkg_install <- function(x, name, ...) { 1 } x$subclass_enum } } ================================================ FILE: man/roxygen_assess_family.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_assess.R \name{roxygen_assess_family} \alias{roxygen_assess_family} \title{Helper for creating a roxygen header from template for assess_* functions} \usage{ roxygen_assess_family( name, return_type = "an atomic assessment result", dontrun = TRUE ) } \arguments{ \item{name}{the name of the assessment, assuming naming conventions are followed} \item{return_type}{an optional added commentary about the return type of the assessment function} \item{dontrun}{logical indicating whether examples should be wrapped in a dontrun block. This is particularly useful for assessments which may require an internet connection.} } \value{ roxygen section template for assess family functions } \description{ Helper for creating a roxygen header from template for assess_* functions } \examples{ \dontrun{ #' @eval roxygen_assess_family( #' "has_news", #' "an integer value indicating the number of discovered NEWS files") } } \keyword{internal} ================================================ FILE: man/roxygen_assess_family_catalog.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_assess.R \name{roxygen_assess_family_catalog} \alias{roxygen_assess_family_catalog} \title{Helper for creating a roxygen itemized list for assess_* functions} \usage{ roxygen_assess_family_catalog() } \value{ roxygen section template for assess family function catalog } \description{ Helper for creating a roxygen itemized list for assess_* functions } \examples{ \dontrun{ #' @eval assess_family_catalog_roxygen() } } \keyword{internal} ================================================ FILE: man/roxygen_cache_behaviors.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_ref_cache_behaviors.R \name{roxygen_cache_behaviors} \alias{roxygen_cache_behaviors} \title{Document both declare_cache_behavior parameters and options list} \usage{ roxygen_cache_behaviors( fmt = "\%s: \%s", name_fmt = "\%s", annotation_fmt = "\%s", wrap_fmt = "\%s", collapse = "\\n" ) } \arguments{ \item{fmt}{format of cache behavior entries} \item{name_fmt}{special formating for name (first) component} \item{annotation_fmt}{special formating for annotation (second) component} \item{wrap_fmt}{a wrapper for the entirety of the roxygen entries} \item{collapse}{passed to paste} } \value{ a string } \description{ Document both declare_cache_behavior parameters and options list } \keyword{internal} ================================================ FILE: man/roxygen_score_family.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_score.R \name{roxygen_score_family} \alias{roxygen_score_family} \title{Helper for creating a roxygen header from template for score.* functions} \usage{ roxygen_score_family(name, dontrun = TRUE) } \arguments{ \item{name}{the name of the scoring function, assuming naming conventions are followed} \item{dontrun}{logical indicating whether examples should be wrapped in a dontrun block. This is particularly useful for assessments which may require an internet connection.} } \value{ roxygen section template for score family functions } \description{ Helper for creating a roxygen header from template for score.* functions } \examples{ \dontrun{ #' @eval roxygen_score_family("has_news") } } \keyword{internal} ================================================ FILE: man/score_error_NA.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/metric_score.R \name{score_error_NA} \alias{score_error_NA} \title{Score error handler to silently return NA} \usage{ score_error_NA(...) } \arguments{ \item{...}{Additional arguments unused} } \value{ a value of package score } \description{ Score error handler to silently return NA } ================================================ FILE: man/score_error_default.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/metric_score.R \name{score_error_default} \alias{score_error_default} \title{Default score error handling, emitting a warning and returning 0} \usage{ score_error_default(x, ...) } \arguments{ \item{x}{A \code{pkg_metric_*} class object to score} \item{...}{Additional arguments unused} } \value{ a value of package score } \description{ Default score error handling, emitting a warning and returning 0 } ================================================ FILE: man/score_error_zero.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/metric_score.R \name{score_error_zero} \alias{score_error_zero} \title{Score error handler to silently return 0} \usage{ score_error_zero(...) } \arguments{ \item{...}{Additional arguments unused} } \value{ a value of package score } \description{ Score error handler to silently return 0 } ================================================ FILE: man/sub-sub-.pkg_ref.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_ref_class_extract.R \name{[[.pkg_ref} \alias{[[.pkg_ref} \title{Lazily instantiated, immutable metadata access} \usage{ \method{[[}{pkg_ref}(x, name, ...) } \arguments{ \item{x}{pkg_ref object to extract metadata from} \item{name}{name of metadata field to extract} \item{...}{additional arguments used to extract from internal environment} } \value{ a pkg_ref object } \description{ If errors are thrown upon instantiation, they are saved and rethrown any time the value is attempted to be accessed. These then propegate through assessment and scoring functions to affect any downstream metrics. } \keyword{internal} ================================================ FILE: man/summarize_scores.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/summarize_scores.R \name{summarize_scores} \alias{summarize_scores} \title{Summarize a default set of assessments into a single risk score} \usage{ summarize_scores(data, weights = NULL) } \arguments{ \item{data}{a \code{\link[tibble]{tibble}} of scored assessments whose column names match those provided by riskmetric's \code{\link{pkg_assess}} function.} \item{weights}{an optional vector of non-negative weights to be assigned to each assessment.} } \value{ a numeric vector of risk scores } \description{ This function serves as an example for how a risk score might be derived. Assuming all assessments provided by \code{riskmetric} are available in a dataset, this function can be used to calculate a vector of risks. } \examples{ \dontrun{ library(dplyr) summarize_scores(pkg_score(pkg_assess(as_tibble(pkg_ref("riskmetric"))))) library(dplyr) pkg_ref("riskmetric") \%>\% pkg_assess() \%>\% pkg_score() \%>\% summarize_scores() } } ================================================ FILE: man/suppressMatchingConditions.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{suppressMatchingConditions} \alias{suppressMatchingConditions} \title{Suppress messages and warnings based on one or more regex matches} \usage{ suppressMatchingConditions(expr, ..., .opts = list(), .envir = parent.frame()) } \arguments{ \item{expr}{An expression to evaluate} \item{...}{Named parameters, where the name indicates the class of conditions to capture and the value is a vector of regular expressions that, when matched against the respective condition message, should suppress that condition.} \item{.opts}{A named list of arguments to pass to \code{grepl}} \item{.envir}{The environment in which \code{expr} is to be evaluated} } \value{ a message printed on console } \description{ Suppress messages and warnings based on one or more regex matches } \keyword{internal} ================================================ FILE: man/use_assessments_column_names.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_assess.R \name{use_assessments_column_names} \alias{use_assessments_column_names} \title{reassign assignment list names with column_name attribute if available} \usage{ use_assessments_column_names(x) } \arguments{ \item{x}{list of columns for which to consider friendly column name attributes} } \value{ a vector of friendly column names if available } \description{ reassign assignment list names with column_name attribute if available } \keyword{internal} ================================================ FILE: man/verify_pkg_source.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_ref_class.R \name{verify_pkg_source} \alias{verify_pkg_source} \title{Verify a pkg_source when one is manually specified by the user} \usage{ verify_pkg_source(x, source, repos) } \value{ a string of package source } \description{ Verify a pkg_source when one is manually specified by the user } \keyword{internal} ================================================ FILE: man/vignettes_from_dir.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_ref_cache_vignettes.R \name{vignettes_from_dir} \alias{vignettes_from_dir} \title{Build a List of Vignettes Files Discovered Within a Given Directory} \usage{ vignettes_from_dir(path) } \arguments{ \item{path}{a package directory path expected to contain Vignettes files} } \value{ a vector of parsed Vignettes files } \description{ Build a List of Vignettes Files Discovered Within a Given Directory } \keyword{internal} ================================================ FILE: man/vignettes_from_html.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg_ref_cache_vignettes.R \name{vignettes_from_html} \alias{vignettes_from_html} \title{Build a List of Vignettes Files Discovered Within a Package Website} \usage{ vignettes_from_html(x) } \arguments{ \item{x}{a \code{pkg_ref} object} } \value{ a vector of Vignettes files } \description{ Build a List of Vignettes Files Discovered Within a Package Website } \keyword{internal} ================================================ FILE: man/with.pkg_ref.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{with.pkg_ref} \alias{with.pkg_ref} \title{Evaluate an expression in the context of a pkg_ref} \usage{ \method{with}{pkg_ref}(data, expr, ...) } \arguments{ \item{data}{data to use for constructing an environment. For the default \code{with} method this may be an environment, a list, a data frame, or an integer as in \code{sys.call}. For \code{within}, it can be a list or a data frame.} \item{expr}{expression to evaluate; particularly for \code{within()} often a \dQuote{compound} expression, i.e., of the form \preformatted{ { a <- somefun() b <- otherfun() ..... rm(unused1, temp) } }} \item{...}{arguments to be passed to (future) methods.} } \value{ the value of the evaluated expr. } \description{ \code{pkg_ref} objects are environments and can be passed to \code{with} in much the same way. This specialized function makes sure that any fields within the \code{pkg_ref} have been appropriately evaluated before trying to execute the expression. } \keyword{internal} ================================================ FILE: man/with_unclassed_to.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{with_unclassed_to} \alias{with_unclassed_to} \title{Evaluate an expression after first removing a range of S3 classes} \usage{ with_unclassed_to(x, .class = 1:length(class(x)), expr, envir = parent.frame()) } \arguments{ \item{x}{a structured S3-classed object} \item{.class}{the class to unclass the object to} \item{expr}{an expression to evaluate, avoiding parent classs dispatch} \item{envir}{an environment in which the expression is to be evaluated} } \value{ the result of \code{expr} } \description{ Evaluate an expression after first removing a range of S3 classes } \keyword{internal} ================================================ FILE: tests/testthat/setup_mock_web_requests.R ================================================ library(webmockr) library(httr) options(riskmetric.tests = test_path()) httr_mock() # helper function for generating cran-log style API results build_downloads <- function(downloads = NULL, pkg_name = "temp") { day = Sys.Date() - seq_along(downloads) downloads_obj <- list(list( downloads = rev(mapply( function(day, dls) list(list(day = day, downloads = dls)), day, downloads)), start = Sys.Date(), end = Sys.Date() - ifelse(is.null(downloads), 0L, length(downloads)), package = pkg_name )) as.character(jsonlite::toJSON(downloads_obj, auto_unbox = TRUE)) } ## CRAN (or mirror) package page # TODO: separate out good and bad examples stub_request("get", uri_regex = ".+/web/packages/[^/]*") %>% to_return( body = paste(collapse = "\n", readLines(file.path(test_path(), "test_webmocks", "data", "cran_package.html"))), headers = list( "Content-Type" = "text/html; charset=utf-8", "Content-Encoding" = "UTF-8")) ## CRAN (or mirror) package checks page # TODO: separate out good and bad examples stub_request("get", uri_regex = ".+/web/checks/[^/]*") %>% to_return( body = paste(collapse = "\n", readLines(file.path(test_path(), "test_webmocks", "data", "cran_package_checks.html"))), headers = list( "Content-Type" = "text/html; charset=utf-8", "Content-Encoding" = "UTF-8")) ## CRAN (or mirror) NEWS page # TODO: separate out good and bad examples stub_request("get", uri_regex = ".+/web/packages/[^/]*/news/news.html") %>% to_return( body = paste(collapse = "\n", readLines(file.path(test_path(), "test_webmocks", "data", "cran_news.html"))), headers = list( "Content-Type" = "text/html; charset=utf-8", "Content-Encoding" = "UTF-8")) ## CRAN (or mirror) package archive listing page # TODO: serpate out good and bad examples stub_request("get", uri_regex = ".+/src/contrib/Archive/[^/]*") %>% to_return( body = paste(collapse = "\n", readLines(file.path(test_path(), "test_webmocks", "data", "cran_package_archive.html"))), headers = list( "Content-Type" = "text/html; charset=utf-8", "Content-Encoding" = "UTF-8")) ## CRAN logs downloads # good stub_request("get", uri_regex = ".+/downloads/daily/[^/]*/pkgcranremotegood[^/]*") %>% to_return( # build a json return payload of ~2k daily downloads over past year body = mock_file( path = tempfile(), payload = build_downloads(pmax(round(rnorm(356, 2e3, 200)), 0))), headers = list( "Content-Type" = "application/json; charset=utf-8", "Content-Encoding" = "UTF-8")) # bad stub_request("get", uri_regex = ".+/downloads/daily/[^/]*/pkgcranremotebad[^/]*") %>% to_return( # build a json return payload of ~10 daily downloads over < 1 month body = mock_file( path = tempfile(), payload = build_downloads(pmax(round(rnorm(20, 10, 3)), 0))), headers = list( "Content-Type" = "application/json; charset=utf-8", "Content-Encoding" = "UTF-8")) # other stub_request("get", uri_regex = ".+/downloads/daily/.*") %>% to_return( body = mock_file(path = tempfile(), payload = build_downloads()), headers = list( "Content-Type" = "application/json; charset=utf-8", "Content-Encoding" = "UTF-8")) # github bugreports via github's repo issues api stub_request("get", uri_regex = "api\\.github\\.com/repos/[^/]+/[^/]+/issues") %>% wi_th(query = list(state = "all", per_page = "30")) %>% to_return( body = paste(collapse = "\n", readLines(file.path(test_path(), "test_webmocks", "data", "github_repo_issues_api_response.json"))), headers = list("Content-Type" = "application/json")) ================================================ FILE: tests/testthat/setup_test_packages.R ================================================ # create a series of reliably evaluated pkg reference objects that can be # reused throughout testthat unit tests # NOTE: score_error_zero is used for all scoring functions to suppress warnings # and explicitly return zeros whenever errors are captured throughout the # evaluation process. Errors are better tested in metric-specific unit tests. # create a temporary library for installed test packages options(repos = "fake-cran.fake-r-project.org") templib <- tempfile("riskmetric_test_lib_") oldLibs <- .libPaths() dir.create(templib) withr::with_libpaths(c(templib, oldLibs), { pkg_path <- file.path(test_path(), "test_packages", "pkgsourcegood") # build and install pkg_archive <- pkgbuild::build(pkg_path, quiet = TRUE) on.exit(file.remove(pkg_archive), add = TRUE) install.packages(pkg_archive, type = "source", repos = NULL, quiet = TRUE) # a representative "good" quality package from an installed package pkg_ref_install_good <- pkg_install("pkgsourcegood") assess_install_good <- pkg_assess(pkg_ref_install_good) score_install_good <- pkg_score( assess_install_good, error_handler = score_error_zero ) }) # a representative "good" quality package from source code pkg_ref_source_good <- pkg_source(pkg_path) assess_source_good <- pkg_assess(pkg_ref_source_good) score_source_good <- pkg_score( assess_source_good, error_handler = score_error_zero ) # a representative "bad" pkg_source package from source code pkg_ref_source_bad <- pkg_source(file.path( test_path(), "test_packages", "pkgsourcebad" )) assess_source_bad <- pkg_assess(pkg_ref_source_bad) score_source_bad <- pkg_score( assess_source_bad, error_handler = score_error_zero ) # another representative "bad" pkg_source package from source code pkg_ref_source_bad2 <- pkg_source(file.path( test_path(), "test_packages", "pkgsourcebad2" )) assess_source_bad2 <- pkg_assess(pkg_ref_source_bad2) score_source_bad2 <- pkg_score( assess_source_bad2, error_handler = score_error_zero ) # a representative package from an installed library pkg_ref_stdlib_install <- pkg_install("utils") assess_stdlib_install <- pkg_assess(pkg_ref_stdlib_install) score_stdlib_install <- pkg_score( assess_stdlib_install, error_handler = score_error_zero ) # a representative cohort of packages from an installed library pkg_ref_stdlibs_install <- pkg_ref(c("utils", "tools"), source = "pkg_install") assess_stdlibs_install <- pkg_assess(pkg_ref_stdlibs_install) score_stdlibs_install <- pkg_score( assess_stdlibs_install, error_handler = score_error_zero ) # a representative "good" quality package available on CRAN, but not installed pkg_ref_cran_remote_good <- pkg_cran( "pkgcranremotegood", repos = "fake-cran.fake-r-project.org" ) assess_cran_remote_good <- pkg_assess(pkg_ref_cran_remote_good) score_cran_remote_good <- pkg_score( assess_cran_remote_good, error_handler = score_error_zero ) # a representative "bad" quality package available on CRAN, but not installed pkg_ref_cran_remote_bad <- pkg_cran( "pkgcranremotebad", repos = "fake-cran.fake-r-project.org" ) assess_cran_remote_bad <- pkg_assess(pkg_ref_cran_remote_bad) score_cran_remote_bad <- pkg_score( assess_cran_remote_bad, error_handler = score_error_zero ) # a representative package without a discoverable reference pkg_ref_missing <- pkg_missing("pkgmissing") assess_pkg_missing <- pkg_assess(pkg_ref_missing) score_pkg_missing <- pkg_score( assess_pkg_missing, error_handler = score_error_zero ) withr::defer(unlink(templib, recursive = TRUE), teardown_env()) ================================================ FILE: tests/testthat/teardown_mock_web_requests.R ================================================ options(riskmetric.tests = NULL) stub_registry_clear() httr_mock(FALSE) rm(build_downloads) ================================================ FILE: tests/testthat/teardown_test_packages.R ================================================ rm( pkg_ref_source_good, assess_source_good, pkg_ref_source_bad, assess_source_bad, pkg_ref_source_bad2, assess_source_bad2, score_source_bad2, pkg_ref_stdlib_install, assess_stdlib_install, pkg_ref_stdlibs_install, assess_stdlibs_install, pkg_ref_cran_remote_good, assess_cran_remote_good, pkg_ref_cran_remote_bad, assess_cran_remote_bad ) ================================================ FILE: tests/testthat/test_assess.R ================================================ test_that("pkg_assess on a single pkg_ref returns a vctrs_list_of with one element per assessment", { expect_s3_class(assess_source_good, "vctrs_list_of") expect_s3_class(assess_source_good, "list") expect_length(assess_source_good, length(all_assessments())) }) test_that("pkg_assess on list of pkg_refs returns a tibble with one col per assessment", { expect_s3_class(assess_stdlibs_install, "tbl_df") expect_s3_class(assess_stdlibs_install, "data.frame") expect_length(assess_stdlibs_install, length(all_assessments()) + 3) }) test_that("assess returns the correct classes", { expect_s3_class( assess_source_good$downloads_1yr, c("pkg_metric_downloads_1yr", "pkg_metric", "numeric")) expect_s3_class( assess_source_good$license, c("pkg_metric_license", "pkg_metric", "character")) expect_s3_class( assess_source_good$bugs_status, c("pkg_metric_last_30_bugs_status", "pkg_metric", "logical")) expect_s3_class( assess_source_good$has_news, c("pkg_metric_has_news", "pkg_metric", "integer")) expect_s3_class( assess_source_good$has_vignettes, c("pkg_metric_has_vignettes", "pkg_metric", "integer")) expect_s3_class( assess_source_good$export_help, c("pkg_metric_export_help", "pkg_metric", "logical")) expect_s3_class( assess_source_good$news_current, c("pkg_metric_news_current", "pkg_metric", "logical")) expect_s3_class( assess_source_good$has_examples, c("pkg_metric_has_examples", "pkg_metric", "integer")) }) ================================================ FILE: tests/testthat/test_assess_dependencies.R ================================================ test_that("assess_dependencies returns the correct number of dependencies", { expect_s3_class( assess_source_good$dependencies, c("pkg_metric_dependencies", "pkg_metric", "data.frame")) expect_equal( nrow(assess_source_good$dependencies), 0) }) ================================================ FILE: tests/testthat/test_assess_export_help.R ================================================ # Commenting these out until this is implemented for sourced packages test_that("assess_export_help returns expected result for source packages", { expect_equal(unclass(unname(assess_source_good$export_help)), c(TRUE), ignore_attr = TRUE) expect_equal(unclass(unname(assess_install_good$export_help)), c(TRUE), ignore_attr = TRUE) expect_equal(unclass(unname(assess_source_bad$export_help)), c(FALSE, TRUE), ignore_attr = TRUE) expect_true(assess_stdlibs_install$export_help[[1]][[1]]) expect_equal(score_install_good$export_help[[1]], 1, ignore_attr = TRUE) expect_equal(score_source_bad$export_help[[1]], 0.5, ignore_attr = TRUE) expect_equal(score_source_good$export_help[[1]], 1, ignore_attr = TRUE) }) ================================================ FILE: tests/testthat/test_assess_has_bug_reports_url.R ================================================ test_that("assess_has_bug_reports_url returns the correct url", { expect_s3_class( assess_source_good$has_bug_reports_url, c("pkg_metric_has_bug_reports_url", "pkg_metric", "integer")) expect_equal(as.numeric(assess_source_good$has_bug_reports_url), 1) }) ================================================ FILE: tests/testthat/test_assess_has_examples.R ================================================ test_that("assess_has_examples returns expected result for source packages", { expect_equal(unclass(assess_source_good$has_examples[[1]]), TRUE) expect_equal(unclass(assess_source_bad$has_examples[[1]]), TRUE) }) test_that("check behavior of package that does not export any objects", { expect_length(pkg_ref_source_bad2$examples, 0) expect_length(assess_source_bad2$has_examples, 0) expect_equal(unclass(score_source_bad2$has_examples[[1]]), NA) }) ================================================ FILE: tests/testthat/test_assess_has_news.R ================================================ test_that("assess_has_news returns expected result for source packages", { # TODO: add other package types expect_equal(unclass(assess_source_good$has_news[[1]]), 1) expect_equal(unclass(assess_source_bad$has_news[[1]]), 0) }) ================================================ FILE: tests/testthat/test_assess_last_30_bugs_status.R ================================================ test_that("assess_last_30_bugs_status returns expected result for source package", { # using mocked github api response expect_s3_class( assess_source_good$bugs_status, c("pkg_metric_last_30_bugs_status", "pkg_metric", "logical")) expect_equal( as.vector(assess_source_good$bugs_status), c(FALSE, TRUE, FALSE)) }) ================================================ FILE: tests/testthat/test_assess_news_current.R ================================================ test_that("assess_news_current returns expected result for source packages", { # TODO: Add other pkg_ref types expect_true(assess_source_good$news_current[[1]]) expect_length(assess_source_bad$news_current, 0) }) ================================================ FILE: tests/testthat/test_metric_score_labels.R ================================================ test_that("scored assessments maintain labels which indicate what the score represents", { pkg_scores_objs <- list( # score_cran_remote_good, # score_cran_remote_bad, score_source_good # score_source_bad, # score_stdlib_install, # score_stdlibs_install, # score_pkg_missing ) for (pkg_scores_i in pkg_scores_objs) { expect_true({ scores_have_labels <- vapply( pkg_scores_i, function(i) !is.null(attr(i, "label")), logical(1L)) all(scores_have_labels) }) } }) ================================================ FILE: tests/testthat/test_metric_score_range.R ================================================ test_that("scored assessments are all between [0,1]", { pkg_scores_objs <- list( # score_cran_remote_good, # score_cran_remote_bad, score_source_good # score_source_bad, # score_stdlib_install, # score_stdlibs_install, # score_pkg_missing ) for (pkg_scores_i in pkg_scores_objs) { pkg_metric_idx <- which(sapply(pkg_scores_i, function(x) any(class(x) %in% "pkg_score"))) expect_true({ metrics_are_valid <- vapply(pkg_scores_i[pkg_metric_idx], function(i) all(na.omit(i)<=1 & na.omit(i) >= 0), logical(1L)) all(metrics_are_valid) }) } }) ================================================ FILE: tests/testthat/test_packages/pkgsourcebad/DESCRIPTION ================================================ Package: pkgsourcebad Title: A Test Package Used to Test Source Assessments Version: 0.0.0.9000 Authors@R: "Eli Miller [aut, cre]" Description: What the package does (one paragraph) Depends: R (>= 3.1.2) License: CC BY 2.0 LazyData: true Suggests: testthat Encoding: UTF-8 RoxygenNote: 7.1.1 ================================================ FILE: tests/testthat/test_packages/pkgsourcebad/NAMESPACE ================================================ # Generated by roxygen2: do not edit by hand export(hello_work_test_2) export(hello_world_test) ================================================ FILE: tests/testthat/test_packages/pkgsourcebad/R/hello_world_test.R ================================================ #' A Test function #' #' @param x An object to print #' #' @return Nothing #' @export hello_world_test <- function(x) { print(paste0("hello world", x)) } #' A Test Function with no help docs #' @export #' @noRd hello_work_test_2 <- function(x) { print(paste0("hello world", x)) } ================================================ FILE: tests/testthat/test_packages/pkgsourcebad/man/hello_world_test.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/hello_world_test.R \name{hello_world_test} \alias{hello_world_test} \title{A Test function} \usage{ hello_world_test(x) } \arguments{ \item{x}{An object to print} } \value{ Nothing } \description{ A Test function } ================================================ FILE: tests/testthat/test_packages/pkgsourcebad2/DESCRIPTION ================================================ Package: pkgsourcebad2 Title: A Test Package Used to Test Source Assessments Version: 0.0.0.9000 Authors@R: "Eli Miller [aut, cre]" Description: What the package does (one paragraph) Depends: R (>= 3.1.2) License: CC BY 2.0 LazyData: true Suggests: testthat Encoding: UTF-8 RoxygenNote: 7.1.1 ================================================ FILE: tests/testthat/test_packages/pkgsourcegood/DESCRIPTION ================================================ Package: pkgsourcegood Type: Package Title: A Test Package Used to Test Source Assessments Version: 0.1.0 Authors@R: "Eli Miller [aut, cre]" Description: What the package does (one paragraph) BugReports: https://github.com/pharmar/pkgsourcegood Depends: R (>= 3.1.2) License: MIT + file LICENSE LazyData: true Suggests: testthat Encoding: UTF-8 RoxygenNote: 7.1.1 ================================================ FILE: tests/testthat/test_packages/pkgsourcegood/NAMESPACE ================================================ # Generated by roxygen2: do not edit by hand export(hello_world_test) ================================================ FILE: tests/testthat/test_packages/pkgsourcegood/NEWS.md ================================================ # 0.1.0 There is no news. This is a test file to determine if the assessments are working. ================================================ FILE: tests/testthat/test_packages/pkgsourcegood/R/hello_world_test.R ================================================ #' A Test function #' #' @param x An object to print #' #' @return Nothing #' @export hello_world_test <- function(x) { print(paste0("hello world", x)) } ================================================ FILE: tests/testthat/test_packages/pkgsourcegood/man/hello_world_test.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/hello_world_test.R \name{hello_world_test} \alias{hello_world_test} \title{A Test function} \usage{ hello_world_test(x) } \arguments{ \item{x}{An object to print} } \value{ Nothing } \description{ A Test function } ================================================ FILE: tests/testthat/test_packages/secondLib/secondLibPkg/DESCRIPTION ================================================ Package: secondLibPkg Type: Package Title: A Test Package Used to Test Source Assessments Version: 0.1.0 Authors@R: "Eli Miller [aut, cre]" Description: What the package does (one paragraph) BugReports: https://github.com/pharmar/pkgsourcegood Depends: R (>= 3.1.2) License: MIT + file LICENSE LazyData: true Suggests: testthat Encoding: UTF-8 RoxygenNote: 7.1.1 ================================================ FILE: tests/testthat/test_packages/secondLib/secondLibPkg/NAMESPACE ================================================ # Generated by roxygen2: do not edit by hand export(hello_world_test) ================================================ FILE: tests/testthat/test_packages/secondLib/secondLibPkg/NEWS.md ================================================ # 0.1.0 There is no news. This is a test file to determine if the assessments are working. ================================================ FILE: tests/testthat/test_packages/secondLib/secondLibPkg/R/hello_world_test.R ================================================ #' A Test function #' #' @param x An object to print #' #' @return Nothing #' @export hello_world_test <- function(x) { print(paste0("hello world", x)) } ================================================ FILE: tests/testthat/test_packages/secondLib/secondLibPkg/man/hello_world_test.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/test.R \name{hello_world_test} \alias{hello_world_test} \title{A Test function} \usage{ hello_world_test(x) } \arguments{ \item{x}{An object to print} } \value{ Nothing } \description{ A Test function } ================================================ FILE: tests/testthat/test_pkg_ref.R ================================================ options(repos = "fake-cran.fake-r-project.org") test_that("get_pkg_ref_classes appropriately captures full class chain", { skip_on_cran() abstract_hierarchy <- list("a" = list("b", "c" = list("d"))) expect_equal(get_pkg_ref_classes("b", abstract_hierarchy), c("b", "a")) expect_equal(get_pkg_ref_classes("d", abstract_hierarchy), c("d", "c", "a")) expect_equal( get_pkg_ref_classes("pkg_cran_remote"), c("pkg_cran_remote", "pkg_remote", "pkg_ref") ) }) test_that("pkg_ref infers source when not explicitly provided", { skip_on_cran() # pkg_source ref4 <- pkg_ref_source_bad expect_s3_class(ref4, c("pkg_source", "pkg_ref", "environment"), exact = TRUE) expect_equal(ref4$source, "pkg_source") # pkg_install ref1 <- pkg_ref_install_good expect_s3_class(ref1, c("pkg_install", "pkg_ref", "environment"), exact = TRUE) expect_equal(ref1$source, "pkg_install") # pkg_cran_remote ref2 <- pkg_cran("pkgcranremotegood") expect_s3_class(ref2, c("pkg_cran_remote", "pkg_remote", "pkg_ref", "environment"), exact = TRUE) expect_equal(ref2$source, "pkg_cran_remote") # pkg_bioc_remote # TODO: add bioconductor available packages mock and replace with a stub package ref3 <- pkg_bioc("GenomicFeatures") expect_s3_class(ref3, c("pkg_bioc_remote", "pkg_remote", "pkg_ref", "environment"), exact = TRUE) expect_equal(ref3$source, "pkg_bioc_remote") }) test_that("pkg_ref can accept an argument of 'source'", { skip_on_cran() # argument pkg_cran_remote ref5 <- pkg_ref("pkgcranremotegood", source = "pkg_cran_remote") expect_s3_class(ref5, c("pkg_cran_remote", "pkg_remote", "pkg_ref", "environment"), exact = TRUE) expect_equal(ref5$source, "pkg_cran_remote") # Multiple pkg_refs ref6 <- pkg_ref(c("pkgcranremotegood", "pkgcranremotebad"), source = "pkg_cran_remote") expect_s3_class(ref6[[1L]], c("pkg_cran_remote", "pkg_remote", "pkg_ref", "environment"), exact = TRUE) expect_s3_class(ref6[[2L]], c("pkg_cran_remote", "pkg_remote", "pkg_ref", "environment"), exact = TRUE) expect_equal(sapply(ref6, `[[`, "source"), c("pkg_cran_remote", "pkg_cran_remote")) # Multiple pkg_refs with different sources ref7 <- pkg_ref(c("urltools", "pkgcranremotegood"), source = c("pkg_install", "pkg_cran_remote")) expect_s3_class(ref7[[1L]], c("pkg_install", "pkg_ref", "environment"), exact = TRUE) expect_s3_class(ref7[[2L]], c("pkg_cran_remote", "pkg_remote", "pkg_ref", "environment"), exact = TRUE) expect_equal(sapply(ref7, `[[`, "source"), c("pkg_install", "pkg_cran_remote")) }) test_that("pkg_ref throws nice warnings when you give bad 'source' arguments",{ skip_on_cran() expect_warning( p1 <- pkg_ref("UnknownCRANPkg", source = "pkg_cran_remote"), "Package: `UnknownCRANPkg` not found on CRAN, source is now 'pkg_missing'" ) expect_equal(p1$source, "pkg_missing") expect_warning( p2 <- pkg_ref("UnknownBiocPkg", source = "pkg_bioc_remote"), "Package: `UnknownBiocPkg` not found on bioconductor, source is now 'pkg_missing'" ) expect_equal(p2$source, "pkg_missing") expect_warning( p3 <- pkg_ref("./MissingPackage", source = "pkg_source"), "Package source: `./MissingPackage` does not exist, source is now 'pkg_missing'" ) expect_equal(p3$source, "pkg_missing") }) test_that("pkg_ref throws errors as expected", { skip_on_cran() expect_error( new_pkg_ref("dplyr", source = "pkg_cran_remote", version = "1.0.0", "someUnnamedArgument"), "pkg_ref ellipses arguments must be named" ) expect_error( pkg_ref(structure(list("abc"), class = "badClass")), "Don't know how to convert object class 'badClass' to class 'pkg_ref'" ) }) test_that("pkg_ref will return the same object if passed an object of class pkg_ref", { skip_on_cran() ref1 <- pkg_ref("tools") expect_equal(ref1, pkg_ref(ref1)) }) test_that("determine_pkg_source returns the expected values", { skip_on_cran() ## pkg_source expect_equal( determine_pkg_source(file.path(test_path(), "test_packages", "pkgsourcegood"), repos = getOption("repos")), "pkg_source" ) ## pkg_install expect_equal( determine_pkg_source("tools", repos = getOption("repos")), "pkg_install" ) ## pkg_cran_remote expect_equal( determine_pkg_source("pkgcranremotegood", repos = getOption("repos")), "pkg_cran_remote" ) }) ================================================ FILE: tests/testthat/test_snapshots.R ================================================ ### Actual Snapshots if (packageVersion("testthat") >= package_version("2.99")) { local_edition(3L) test_that("Test source packages", { skip("snapshots appear to be failing due to random order of pkg_ref fields") # Packages are embedded in directories in the testing folder and are created # in setup_test_packages.R expect_snapshot_output(pkg_ref_source_good) expect_snapshot_output(assess_source_good) expect_snapshot_output(pkg_ref_source_good) expect_snapshot_output(assess_source_bad) }) test_that("Test remote CRAN packages", { skip("snapshots appear to be failing due to random order of pkg_ref fields") # Packages were selected because they are not dependencies of this package # NOTE: pkg_refs fail because output of $web_html is a pointer memory # address which change with each run. tests disabled until we can # figure out a workaround. # expect_snapshot_output(pkg_ref_cran_remote_good) expect_snapshot_output(assess_cran_remote_good) # expect_snapshot_output(pkg_ref_cran_remote_bad) expect_snapshot_output(assess_cran_remote_bad) }) test_that("Test installed packages", { skip("snapshots appear to be failing due to random order of pkg_ref fields") # Packages were selected because they are dependencies of this package. expect_snapshot_output(pkg_ref_stdlib_install) expect_snapshot_output(assess_stdlib_install) }) } ================================================ FILE: tests/testthat/test_webmocks/data/cran_mirrors.csv ================================================ "","Name","Country","City","URL","Host","Maintainer","OK","CountryCode","Comment" "1","0-Cloud [https]","0-Cloud","0-Cloud","https://cloud.r-project.org/","Automatic redirection to servers worldwide, currently sponsored by Rstudio","cran # rstudio.com",1,"us","secure_mirror_from_master" ================================================ FILE: tests/testthat/test_webmocks/data/cran_news.html ================================================ NEWS

glmnet 4.0-2

  • Biggest change. Cindex and auc calculations now use the concordance function from package survival
  • Minor changes. Allow coefficient warm starts for glmnet.fit. The print method for glmnet now really prints %dDev rather than the fraction.

glmnet 4.0

Major revision with added functionality. Any GLM family can be used now with glmnet, not just the built-in families. By passing a “family” object as the family argument (rather than a character string), one gets access to all families supported by glm. This development was programmed by our newest member of the glmnet team, Kenneth Tay.

glmnet 3.0-3

Bug fixes

  • Intercept=FALSE with “Gaussian” is fixed. The dev.ratio comes out correctly now. The mortran code was changed directly in 4 places. look for “standard”. Thanks to Kenneth Tay.

glmnet 3.0-2

Bug fixes

  • confusion.glmnet was sometimes not returning a list because of apply collapsing structure
  • cv.mrelnet and cv.multnet dropping dimensions inappropriately
  • Fix to storePB to avoid segfault. Thanks Tomas Kalibera!
  • Changed the help for assess.glmnet and cousins to be more helpful!
  • Changed some logic in lambda.interp to avoid edge cases (thanks David Keplinger)

glmnet 3.0-1

Minor fix to correct Depends in the DESCRIPTION to R (>= 3.6.0)

glmnet 3.0

This is a major revision with much added functionality, listed roughly in order of importance. An additional vignette called relax is supplied to describe the usage.

  • relax argument added to glmnet. This causes the models in the path to be refit without regularization. The resulting object inherits from class glmnet, and has an additional component, itself a glmnet object, which is the relaxed fit.
  • relax argument to cv.glmnet. This allows selection from a mixture of the relaxed fit and the regular fit. The mixture is governed by an argument gamma with a default of 5 values between 0 and 1.
  • predict, coef and plot methods for relaxed and cv.relaxed objects.
  • print method for relaxed object, and new print methods for cv.glmnet and cv.relaxed objects.
  • A progress bar is provided via an additional argument trace.it=TRUE to glmnet and cv.glmnet. This can also be set for the session via glmnet.control.
  • Three new functions assess.glmnet, roc.glmnet and confusion.glmnet for displaying the performance of models.
  • makeX for building the x matrix for input to glmnet. Main functionality is one-hot-encoding of factor variables, treatment of NA and creating sparse inputs.
  • bigGlm for fitting the GLMs of glmnet unpenalized.

In addition to these new features, some of the code in glmnet has been tidied up, especially related to CV.

glmnet 2.0-20

  • Fixed a bug in internal function coxnet.deviance to do with input pred, as well as saturated loglike (missing) and weights
  • added a coxgrad function for computing the gradient

glmnet 2.0-19

  • Fixed a bug in coxnet to do with ties between death set and risk set

glmnet 2.0-18

  • Added an option alignment to cv.glmnet, for cases when wierd things happen

glmnet 2.0-17

  • Further fixes to mortran to get clean fortran; current mortran src is in inst/mortran

glmnet 2.0-16

  • Additional fixes to mortran; current mortran src is in inst/mortran
  • Mortran uses double precision, and variables are initialized to avoid -Wall warnings
  • cleaned up repeat code in CV by creating a utility function

glmnet 2.0-15

  • Fixed up the mortran so that generic fortran compiler can run without any configure

glmnet 2.0-13

  • Cleaned up some bugs to do with exact prediction
  • newoffset created problems all over - fixed these

glmnet 2.0-11

  • Added protection with exact=TRUE calls to coef and predict. See help file for more details

glmnet 2.0-10

  • Two iterations to fix to fix native fortran registration.

glmnet 2.0-8

  • included native registration of fortran

glmnet 2.0-7

  • constant y blows up elnet; error trap included
  • fixed lambda.interp which was returning NaN under degenerate circumstances.

glmnet 2.0-6

  • added some code to extract time and status gracefully from a Surv object

glmnet 2.0-3

  • changed the usage of predict and coef with exact=TRUE. The user is strongly encouraged to supply the original x and y values, as well as any other data such as weights that were used in the original fit.

glmnet 2.0-1

  • Major upgrade to CV; let each model use its own lambdas, then predict at original set.
  • fixed some minor bugs

glmnet 1.9-9

  • fixed subsetting bug in lognet when some weights are zero and x is sparse

glmnet 1.9-8

  • fixed bug in multivariate response model (uninitialized variable), leading to valgrind issues
  • fixed issue with multinomial response matrix and zeros
  • Added a link to a glmnet vignette

glmnet 1.9-6

  • fixed bug in predict.glmnet, predict.multnet and predict.coxnet, when s= argument is used with a vector of values. It was not doing the matrix multiply correctly
  • changed documentation of glmnet to explain logistic response matrix

glmnet 1.9-5

  • added parallel capabilities, and fixed some minor bugs

glmnet 1.9-3

  • added intercept option

glmnet 1.9-1

  • added upper and lower bounds for coefficients
  • added glmnet.control for setting systems parameters
  • fixed serious bug in coxnet

glmnet 1.8-5

  • added exact=TRUE option for prediction and coef functions

glmnet 1.8

  • Major new release
  • added mgaussian family for multivariate response
  • added grouped option for multinomial family

glmnet 1.7-4

  • nasty bug fixed in fortran - removed reference to dble
  • check class of newx and make dgCmatrix if sparse

glmnet 1.7-1

  • lognet added a classnames component to the object
  • predict.lognet(type="class") now returns a character vector/matrix

glmnet 1.6

  • predict.glmnet : fixed bug with type="nonzero"
  • glmnet: Now x can inherit from sparseMatrix rather than the very specific dgCMatrix, and this will trigger sparse mode for glmnet

glmnet 1.5

  • glmnet.Rd (lambda.min) : changed value to 0.01 if nobs < nvars, (lambda) added warnings to avoid single value, (lambda.min): renamed it lambda.min.ratio
  • glmnet (lambda.min) : changed value to 0.01 if nobs < nvars (HessianExact) : changed the sense (it was wrong), (lambda.min): renamed it lambda.min.ratio. This allows it to be called lambda.min in a call though
  • predict.cv.glmnet (new function) : makes predictions directly from the saved glmnet object on the cv object
  • coef.cv.glmnet (new function) : as above
  • predict.cv.glmnet.Rd : help functions for the above
  • cv.glmnet : insert drop(y) to avoid 1 column matrices; now include a glmnet.fit object for later predictions
  • nonzeroCoef : added a special case for a single variable in x; it was dying on this
  • deviance.glmnet : included
  • deviance.glmnet.Rd : included

glmnet 1.4

  • Note that this starts from version glmnet_1.4.
================================================ FILE: tests/testthat/test_webmocks/data/cran_package.html ================================================ CRAN - Package glmnet

glmnet: Lasso and Elastic-Net Regularized Generalized Linear Models

Extremely efficient procedures for fitting the entire lasso or elastic-net regularization path for linear regression, logistic and multinomial regression models, Poisson regression, Cox model, multiple-response Gaussian, and the grouped multinomial regression. There are two new and important additions. The family argument can be a GLM family object, which opens the door to any programmed family. This comes with a modest computational cost, so when the built-in families suffice, they should be used instead. The other novelty is the relax option, which refits each of the active sets in the path unpenalized. The algorithm uses cyclical coordinate descent in a path-wise fashion, as described in the papers listed in the URL below.

Version: 4.0-2
Depends: R (≥ 3.6.0), Matrix (≥ 1.0-6)
Imports: methods, utils, foreach, shape, survival
Suggests: knitr, lars, testthat
Published: 2020-06-16
Author: Jerome Friedman [aut], Trevor Hastie [aut, cre], Rob Tibshirani [aut], Balasubramanian Narasimhan [aut], Kenneth Tay [aut], Noah Simon [aut], Junyang Qian [ctb]
Maintainer: Trevor Hastie <hastie at stanford.edu>
License: GPL-2
URL: https://glmnet.stanford.edu, https://dx.doi.org/10.18637/jss.v033.i01, https://dx.doi.org/10.18637/jss.v039.i05
NeedsCompilation: yes
Citation: glmnet citation info
Materials: README NEWS
In views: MachineLearning, Survival
CRAN checks: glmnet results

Downloads:

Reference manual: glmnet.pdf
Vignettes: Coxnet: Regularized Cox Regression
An Introduction to glmnet
Glm family
Relaxed fits
Package source: glmnet_4.0-2.tar.gz
Windows binaries: r-devel: glmnet_4.0-2.zip, r-release: glmnet_4.0-2.zip, r-oldrel: glmnet_4.0-2.zip
macOS binaries: r-release: glmnet_4.0-2.tgz, r-oldrel: glmnet_4.0-2.tgz
Old sources: glmnet archive

Reverse dependencies:

Reverse depends: a4Base, a4Classif, a4Core, AdapEnetClass, adapt4pv, AHM, bapred, BigTSP, BioMark, CBPS, cosso, ctmle, DivMelt, DTRlearn2, elasso, ensr, EstHer, fcd, glmnetcr, glmSparseNet, glmtlp, glmvsd, Grace, GRridge, hdlm, HiCfeat, HSDiC, IGG, InvariantCausalPrediction, ipflasso, islasso, lassoscore, Lavash, mcen, metagenomeSeq, MetGen, MMMS, MNS, MRFcov, MTPS, MultiVarSel, PAS, personalized, PRIMsrc, prototest, roccv, RVtests, selectiveInference, SIMMS, SMLE, tmle, trena, TSGSIS
Reverse imports: AMARETTO, animalcules, anoint, ArCo, argo, armada, arulesCBA, aurelius, bastah, BeSS, bestglm, bgsmtr, BioMM, biospear, blin, blkbox, BloodCancerMultiOmics2017, BNrich, c060, categoryEncodings, CausalKinetiX, causalweight, CenBAR, changedetection, CISE, cmenet, coca, cocoreg, ComICS, cornet, CovSelHigh, cpt, creditmodel, customizedTraining, CytoDx, DALEXtra, DevTreatRules, dlbayes, DMRnet, dnr, doc2concrete, DWLasso, elasticIsing, enetLTS, eNetXplorer, EnMCB, EnsembleBase, EnsemblePenReg, ePCR, epiGWAS, eshrink, eventstream, expandFunctions, expose, FADA, fdm2id, FindIt, FLAME, fssemR, fuser, gamreg, GAprediction, gencve, gespeR, ggmix, glmnetUtils, GMDH2, GMSimpute, goffda, graphicalVAR, gren, GRPtests, GWLelast, hal9001, HCTR, HDCI, hdi, hdm, hdme, hdnom, hierGWAS, hierinf, hit, hmgm, HTLR, hybridEnsemble, IsingFit, joinet, knockoff, KOBT, kosel, LassoSIR, LEGIT, lilikoi, lime, lmmen, localModel, LPRelevance, LUCIDus, mase, maxnet, mcb, mdpeer, MEAT, MESS, MetabolicSurv, metafuse, mgm, milr, mimi, miRLAB, mplot, MRFA, msaenet, MTGS, mudfold, MWRidge, naivereg, natural, NCutYX, netDx, netgsa, nethet, nnfor, nnGarrote, nonet, NonProbEst, NormalBetaPrime, nproc, obliqueRSF, OHPL, OmicsMarkeR, omicwas, pact, palasso, parboost, partialCI, PathoStat, Patterns, PDN, pgraph, phd, PhylogeneticEM, Pi, plsmselect, politeness, polywog, postDoubleR, pre, predictoR, prioritylasso, PRISM.forecast, QTL.gCIMapping, QTL.gCIMapping.GUI, ramwas, rare, RCPmod, regnet, regressoR, regsplice, reinforcedPred, relgam, rexposome, RISCA, RIVER, rminer, RNAseqNet, roben, rolypoly, RPtests, rqt, rrpack, RSDA, RTextTools, sail, SAVER, scGPS, sdafilter, SelectBoost, SentimentAnalysis, sentometrics, SIAMCAT, SILM, sirus, SIS, SISIR, slimrec, smurf, SOIL, sparsenetgls, sparsereg, sparsevar, spinBayes, splitSelect, SPONGE, sprintr, StabilizedRegression, starnet, statVisual, stepPenal, STGS, stm, STOPES, StratifiedMedicine, SubgrpID, SuperPCA, TANDEM, TBSignatureProfiler, TCA, tensorsparse, TextForecast, tools4uplift, triplot, TULIP, varEst, XMRF, xrf, xtune, ZVCV
Reverse suggests: adaptMT, bamlss, bbl, bcaboot, bigstatsr, BiodiversityR, BOSSreg, broom, caretEnsemble, casebase, catdata, CBDA, ClassifyR, CMA, coefplot, CompareCausalNetworks, cydar, DriveML, EBglmnet, eclust, EHR, fbRanks, FeatureHashing, flexmix, forecastML, FRESA.CAD, fscaret, ggfortify, GWASinlps, heuristica, iml, imputeR, live, LSAmitR, MachineShop, MetNet, Mirsynergy, mlr, mlr3learners, mlr3pipelines, mlr3proba, ModelGood, modelplotr, nlpred, nscancor, ordinalNet, philr, plotmo, pmml, projpred, pulsar, r2pmml, rcellminer, regsem, RnBeads, s2net, sAIC, sgd, simputation, simulator, SLOPE, SplitReg, SPreFuGED, sqlscore, stabs, STPGA, SuperLearner, superml, TAPseq, text2vec, tramnet, varbvs, vimp, vip, WeightedROC
Reverse enhances: prediction

Linking:

Please use the canonical form https://CRAN.R-project.org/package=glmnet to link to this page.

================================================ FILE: tests/testthat/test_webmocks/data/cran_package_archive.html ================================================ Index of /src/contrib/Archive/glmnet

Index of /src/contrib/Archive/glmnet

      Name                    Last modified      Size  
Parent Directory - glmnet_1.1-1.tar.gz 2008-06-27 06:35 60K glmnet_1.1-2.tar.gz 2008-12-23 08:02 60K glmnet_1.1-3.tar.gz 2009-01-24 10:12 61K glmnet_1.1-4.tar.gz 2009-12-18 16:36 64K glmnet_1.1-5.tar.gz 2010-01-31 10:11 64K glmnet_1.1.tar.gz 2008-06-02 07:00 57K glmnet_1.2.tar.gz 2010-04-04 15:43 83K glmnet_1.3.tar.gz 2010-04-25 07:26 83K glmnet_1.4.tar.gz 2010-06-16 15:35 84K glmnet_1.5.1.tar.gz 2010-11-19 07:33 463K glmnet_1.5.2.tar.gz 2011-02-07 09:50 463K glmnet_1.5.3.tar.gz 2011-03-01 00:04 463K glmnet_1.5.tar.gz 2010-11-04 20:16 463K glmnet_1.6.tar.gz 2011-04-24 06:08 510K glmnet_1.7.1.tar.gz 2011-09-23 11:45 510K glmnet_1.7.3.tar.gz 2012-02-19 09:12 510K glmnet_1.7.4.tar.gz 2012-04-27 06:02 470K glmnet_1.7.tar.gz 2011-06-15 18:50 511K glmnet_1.8-2.tar.gz 2012-10-02 06:20 507K glmnet_1.8-4.tar.gz 2012-12-27 22:11 507K glmnet_1.8-5.tar.gz 2013-01-04 08:21 507K glmnet_1.8.tar.gz 2012-07-03 17:50 484K glmnet_1.9-1.tar.gz 2013-02-10 19:17 514K glmnet_1.9-3.tar.gz 2013-03-02 07:14 519K glmnet_1.9-5.tar.gz 2013-08-04 00:09 524K glmnet_1.9-8.tar.gz 2014-05-24 20:49 525K glmnet_2.0-1.tar.gz 2015-04-08 09:12 1.8M glmnet_2.0-10.tar.gz 2017-05-06 06:23 3.7M glmnet_2.0-12.tar.gz 2017-09-13 17:35 3.7M glmnet_2.0-13.tar.gz 2017-09-22 05:43 3.7M glmnet_2.0-16.tar.gz 2018-04-02 12:06 3.7M glmnet_2.0-18.tar.gz 2019-05-20 05:10 3.7M glmnet_2.0-2.tar.gz 2015-04-11 22:56 1.8M glmnet_2.0-3.tar.gz 2016-02-23 06:18 1.8M glmnet_2.0-4.tar.gz 2016-03-13 10:29 1.8M glmnet_2.0-5.tar.gz 2016-03-17 13:00 1.8M glmnet_2.0-8.tar.gz 2017-04-30 07:02 3.3M glmnet_2.0-9.tar.gz 2017-05-02 20:39 3.7M glmnet_3.0-1.tar.gz 2019-11-15 06:50 1.5M glmnet_3.0-2.tar.gz 2019-12-11 17:00 1.5M glmnet_3.0.tar.gz 2019-11-09 10:20 1.5M glmnet_4.0.tar.gz 2020-05-14 17:30 2.0M
Apache/2.4.39 (Unix) Server at cloud.r-project.org Port 80
================================================ FILE: tests/testthat/test_webmocks/data/cran_package_checks.html ================================================ CRAN Package Check Results for Package glmnet

CRAN Package Check Results for Package glmnet

Last updated on 2021-07-06 20:48:30 CEST.

Flavor Version Tinstall Tcheck Ttotal Status Flags
r-devel-linux-x86_64-debian-clang 4.1-2 OK
r-devel-linux-x86_64-debian-gcc 4.1-2 23.41 210.25 233.66 OK
r-devel-linux-x86_64-fedora-clang 4.1-2 377.96 OK
r-devel-linux-x86_64-fedora-gcc 4.1-2 390.59 OK
r-devel-windows-x86_64 4.1-2 40.00 274.00 314.00 OK
r-devel-windows-x86_64-gcc10-UCRT 4.1-2 OK
r-patched-linux-x86_64 4.1-2 30.61 273.29 303.90 OK
r-patched-solaris-x86 4.1-2 272.90 ERROR
r-release-linux-x86_64 4.1-2 31.34 275.73 307.07 OK
r-release-macos-arm64 4.1-2 OK
r-release-macos-x86_64 4.1-2 OK
r-release-windows-ix86+x86_64 4.1-2 47.00 426.00 473.00 OK
r-oldrel-macos-x86_64 4.1-2 OK
r-oldrel-windows-ix86+x86_64 4.1-2 68.00 367.00 435.00 OK

Check Details

Version: 4.1-2
Check: examples
Result: ERROR
    Running examples in ‘glmnet-Ex.R’ failed
    The error most likely occurred in:
    
    > ### Name: cox.path
    > ### Title: Fit a Cox regression model with elastic net regularization for a
    > ### path of lambda values
    > ### Aliases: cox.path
    >
    > ### ** Examples
    >
    > set.seed(2)
    > nobs <- 100; nvars <- 15
    > xvec <- rnorm(nobs * nvars)
    > xvec[sample.int(nobs * nvars, size = 0.4 * nobs * nvars)] <- 0
    > x <- matrix(xvec, nrow = nobs)
    > beta <- rnorm(nvars / 3)
    > fx <- x[, seq(nvars / 3)] %*% beta / 3
    > ty <- rexp(nobs, exp(fx))
    > tcens <- rbinom(n = nobs, prob = 0.3, size = 1)
    > jsurv <- survival::Surv(ty, tcens)
    > fit1 <- glmnet:::cox.path(x, jsurv)
    >
    > # works with sparse x matrix
    > x_sparse <- Matrix::Matrix(x, sparse = TRUE)
    > fit2 <- glmnet:::cox.path(x_sparse, jsurv)
    Error in elnet.fit(x, z, w, lambda, alpha, intercept = FALSE, thresh = thresh, :
     NA/NaN/Inf in foreign function call (arg 23)
    Calls: <Anonymous> -> cox.fit -> elnet.fit
    Execution halted
Flavor: r-patched-solaris-x86

Version: 4.1-2
Check: re-building of vignette outputs
Result: WARN
    Error(s) in re-building vignettes:
     ...
    --- re-building ‘Coxnet.Rmd’ using rmarkdown
    Warning in engine$weave(file, quiet = quiet, encoding = enc) :
     Pandoc (>= 1.12.3) not available. Falling back to R Markdown v1.
    
    (process:18845): Pango-WARNING **: failed to create cairo scaled font, expect ugly output. the offending font is 'Helvetica Medium 9'
    
    (process:18845): Pango-WARNING **: font_face status is: <unknown error status>
    
    (process:18845): Pango-WARNING **: scaled_font status is: invalid matrix (not invertible)
    
    (process:18845): Pango-WARNING **: shaping failure, expect ugly output. shape-engine='BasicEngineFc', font='Helvetica Medium 9', text='m'
    Quitting from lines 191-193 (Coxnet.Rmd)
    Error: processing vignette 'Coxnet.Rmd' failed with diagnostics:
    NA/NaN/Inf in foreign function call (arg 19)
    --- failed re-building ‘Coxnet.Rmd’
    
    --- re-building ‘glmnet.Rmd’ using rmarkdown
    Warning in engine$weave(file, quiet = quiet, encoding = enc) :
     Pandoc (>= 1.12.3) not available. Falling back to R Markdown v1.
    Loaded lars 1.2
    
    --- finished re-building ‘glmnet.Rmd’
    
    --- re-building ‘glmnetFamily.Rmd’ using rmarkdown
    Warning in engine$weave(file, quiet = quiet, encoding = enc) :
     Pandoc (>= 1.12.3) not available. Falling back to R Markdown v1.
    Loading required package: Matrix
    Loaded glmnet 4.1-2
    Quitting from lines 86-89 (glmnetFamily.Rmd)
    Error: processing vignette 'glmnetFamily.Rmd' failed with diagnostics:
    NA/NaN/Inf in foreign function call (arg 25)
    --- failed re-building ‘glmnetFamily.Rmd’
    
    --- re-building ‘relax.Rmd’ using rmarkdown
    Warning in engine$weave(file, quiet = quiet, encoding = enc) :
     Pandoc (>= 1.12.3) not available. Falling back to R Markdown v1.
    --- finished re-building ‘relax.Rmd’
    
    SUMMARY: processing the following files failed:
     ‘Coxnet.Rmd’ ‘glmnetFamily.Rmd’
    
    Error: Vignette re-building failed.
    Execution halted
Flavor: r-patched-solaris-x86

================================================ FILE: tests/testthat/test_webmocks/data/cran_packages.csv ================================================ "","Package","Version","Priority","Depends","Imports","LinkingTo","Suggests","Enhances","License","License_is_FOSS","License_restricts_use","OS_type","Archs","MD5sum","NeedsCompilation","Additional_repositories","Author","Authors@R","Biarch","BugReports","BuildKeepEmpty","BuildManual","BuildResaveData","BuildVignettes","Built","ByteCompile","Classification/ACM","Classification/ACM-2012","Classification/JEL","Classification/MSC","Classification/MSC-2010","Collate","Collate.unix","Collate.windows","Contact","Copyright","Date","Description","Encoding","KeepSource","Language","LazyData","LazyDataCompression","LazyLoad","MailingList","Maintainer","Note","Packaged","RdMacros","SysDataCompression","SystemRequirements","Title","Type","URL","VignetteBuilder","ZipData","Published","Path","X-CRAN-Comment","Reverse depends","Reverse imports","Reverse linking to","Reverse suggests","Reverse enhances" "5522","pkgcranremotegood","4.0-2",NA,"R (>= 3.6.0), Matrix (>= 1.0-6)","methods, utils, foreach, shape, survival",NA,"knitr, lars, testthat",NA,"GPL-2",NA,NA,NA,NA,"284a7d2ebab21bebbe70370de3c0cc81","yes",NA,"Jerome Friedman [aut], Trevor Hastie [aut, cre], Rob Tibshirani [aut], Balasubramanian Narasimhan [aut], Kenneth Tay [aut], Noah Simon [aut], Junyang Qian [ctb]","c(person(""Jerome"", ""Friedman"", role=c(""aut"")), person(""Trevor"", ""Hastie"", role=c(""aut"", ""cre""), email = ""hastie@stanford.edu""), person(""Rob"", ""Tibshirani"", role=c(""aut"")), person(""Balasubramanian"", ""Narasimhan"", role=c(""aut"")), person(""Kenneth"",""Tay"",role=c(""aut"")), person(""Noah"", ""Simon"", role=c(""aut"")), person(""Junyang"", ""Qian"", role=c(""ctb"")))",NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,"2020-6-13","Extremely efficient procedures for fitting the entire lasso or elastic-net regularization path for linear regression, logistic and multinomial regression models, Poisson regression, Cox model, multiple-response Gaussian, and the grouped multinomial regression. There are two new and important additions. The family argument can be a GLM family object, which opens the door to any programmed family. This comes with a modest computational cost, so when the built-in families suffice, they should be used instead. The other novelty is the relax option, which refits each of the active sets in the path unpenalized. The algorithm uses cyclical coordinate descent in a path-wise fashion, as described in the papers listed in the URL below.","UTF-8",NA,NA,NA,NA,NA,NA,"Trevor Hastie ",NA,"2020-06-14 23:21:58 UTC; hastie",NA,NA,NA,"Lasso and Elastic-Net Regularized Generalized Linear Models","Package","https://glmnet.stanford.edu, https://dx.doi.org/10.18637/jss.v033.i01, https://dx.doi.org/10.18637/jss.v039.i05","knitr",NA,"2020-06-16",NA,NA,"a4Base, a4Classif, a4Core, AdapEnetClass, adapt4pv, AHM, bapred, BigTSP, BioMark, CBPS, cosso, ctmle, DivMelt, DTRlearn2, elasso, ensr, EstHer, fcd, glmnetcr, glmSparseNet, glmtlp, glmvsd, Grace, GRridge, hdlm, HiCfeat, HSDiC, IGG, InvariantCausalPrediction, ipflasso, islasso, lassoscore, Lavash, mcen, metagenomeSeq, MetGen, MMMS, MNS, MRFcov, MTPS, MultiVarSel, PAS, personalized, prototest, roccv, RVtests, selectiveInference, SIMMS, SMLE, tmle, trena, TSGSIS","AMARETTO, animalcules, anoint, ArCo, argo, armada, arulesCBA, aurelius, bastah, BeSS, bestglm, bgsmtr, BioMM, biospear, blin, BloodCancerMultiOmics2017, BNrich, BWGS, c060, categoryEncodings, CausalKinetiX, causalweight, CenBAR, changedetection, CISE, cmenet, coca, ComICS, cornet, CovSelHigh, cpt, creditmodel, customizedTraining, CytoDx, DevTreatRules, dlbayes, DMRnet, dnr, doc2concrete, DWLasso, elasticIsing, enetLTS, EnMCB, EnsembleBase, EnsemblePenReg, ePCR, epiGWAS, ER, eshrink, eventstream, expandFunctions, expose, FADA, fdm2id, FindIt, FLAME, fssemR, fuser, gamreg, GAprediction, gencve, gespeR, ggmix, glmnetUtils, GMDH2, GMSimpute, goffda, graphicalVAR, gren, GRPtests, GWLelast, hal9001, HCTR, HDCI, hdi, hdm, hdme, hdnom, healthcareai, hierGWAS, hierinf, hit, hmgm, HTLR, hybridEnsemble, IsingFit, joinet, knockoff, KOBT, kosel, LassoSIR, LEGIT, lilikoi, lime, localModel, LPRelevance, LUCIDus, mase, maxnet, mcb, mdpeer, MEAT, MESS, metafuse, mgm, milr, mimi, miRLAB, mplot, MRFA, msaenet, mudfold, MWRidge, naivereg, natural, NCutYX, netDx, netgsa, nethet, nnfor, nnGarrote, nonet, NonProbEst, NormalBetaPrime, nproc, obliqueRSF, OHPL, OmicsMarkeR, omicwas, pact, palasso, parboost, partialCI, PathoStat, Patterns, PDN, pgraph, phd, PhylogeneticEM, Pi, plsmselect, PO.EN, politeness, polywog, postDoubleR, pre, predictoR, prioritylasso, PRISM.forecast, QTL.gCIMapping, QTL.gCIMapping.GUI, ramwas, rare, RCPmod, rdomains, regnet, regressoR, regsplice, relgam, rexposome, RISCA, RIVER, rminer, RNAseqNet, roben, RobMixReg, rolypoly, RPtests, rqt, rrpack, RSDA, RTextTools, sail, SAVER, scGPS, sdafilter, SelectBoost, SentimentAnalysis, sentometrics, SIAMCAT, SILM, sirus, SIS, SISIR, slimrec, smurf, SOIL, sparsenetgls, sparsereg, sparsevar, spinBayes, splitSelect, SPONGE, sprintr, StabilizedRegression, starnet, statVisual, stepPenal, STGS, stm, STOPES, StratifiedMedicine, SubgrpID, SuperPCA, TANDEM, TBSignatureProfiler, TCA, tensorsparse, TextForecast, tools4uplift, triplot, TULIP, varEst, XMRF, xrf, xtune, ZVCV",NA,"adaptMT, bamlss, bbl, bcaboot, bigstatsr, BiodiversityR, BOSSreg, broom, caretEnsemble, casebase, catdata, CBDA, ClassifyR, CMA, coefplot, CompareCausalNetworks, cydar, DriveML, EBglmnet, eclust, EHR, fbRanks, FeatureHashing, flexmix, forecastML, FRESA.CAD, fscaret, ggfortify, GWASinlps, heuristica, iml, imputeR, live, LSAmitR, MachineShop, medflex, MetNet, Mirsynergy, mlr, mlr3learners, mlr3pipelines, ModelGood, modelplotr, nlpred, nscancor, ordinalNet, philr, plotmo, pmml, projpred, pulsar, r2pmml, rcellminer, regsem, RnBeads, s2net, sAIC, sgd, simputation, simulator, SLOPE, SplitReg, SPreFuGED, sqlscore, stabs, STPGA, SuperLearner, superml, TAPseq, text2vec, tramnet, varbvs, vimp, vip, WeightedROC","prediction" "7226","pkgcranremotebad","1.2",NA,"R (>= 2.10)",NA,NA,NA,NA,"GPL-2",NA,NA,NA,NA,"2571bae325f6cba1ad0202ea61695b8c","yes",NA,"Trevor Hastie and Brad Efron ",NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,"2013-04-23","Efficient procedures for fitting an entire lasso sequence with the cost of a single least squares fit. Least angle regression and infinitesimal forward stagewise regression are related to the lasso, as described in the paper below.",NA,NA,NA,NA,NA,NA,NA,"Trevor Hastie ",NA,"2013-04-24 05:12:53 UTC; hastie",NA,NA,NA,"Least Angle Regression, Lasso and Forward Stagewise","Package","http://www-stat.stanford.edu/~hastie/Papers/#LARS",NA,NA,"2013-04-24",NA,NA,"AdapEnetClass, aml, changepointsVar, cumSeg, ebreg, elasticnet, FeaLect, gwrr, monomvn, mrMLM, mrMLM.GUI, multiPIM, OTRselect, PAGWAS, pfa, relaxo, rRAP, RXshrink, scalreg, sealasso, sisVIVE, spikeslab, TSMCP","bastah, Cascade, chemmodlab, chemometrics, FindIt, fsMTS, gencve, GGMselect, lassopv, mcb, NHMSAR, PACLasso, pathwayPCA, Patterns, plsRcox, RobMixReg, SelectBoost",NA,"ChemometricsWithR, COBRA, directlabels, dna, fscaret, glmnet, knockoff, lassoscore, Libra, MachineShop, scout, stabs",NA ================================================ FILE: tests/testthat/test_webmocks/data/github_repo_issues_api_response.json ================================================ [ { "url": "https://api.github.com/repos/pharmaR/riskmetric/issues/149", "repository_url": "https://api.github.com/repos/pharmaR/riskmetric", "labels_url": "https://api.github.com/repos/pharmaR/riskmetric/issues/149/labels{/name}", "comments_url": "https://api.github.com/repos/pharmaR/riskmetric/issues/149/comments", "events_url": "https://api.github.com/repos/pharmaR/riskmetric/issues/149/events", "html_url": "https://github.com/pharmaR/riskmetric/issues/149", "id": 682172736, "node_id": "MDU6SXNzdWU2ODIxNzI3MzY=", "number": 149, "title": "Improving error messages explanation", "user": { "login": "marlycormar", "id": 8559654, "node_id": "MDQ6VXNlcjg1NTk2NTQ=", "avatar_url": "https://avatars3.githubusercontent.com/u/8559654?v=4", "gravatar_id": "", "url": "https://api.github.com/users/marlycormar", "html_url": "https://github.com/marlycormar", "followers_url": "https://api.github.com/users/marlycormar/followers", "following_url": "https://api.github.com/users/marlycormar/following{/other_user}", "gists_url": "https://api.github.com/users/marlycormar/gists{/gist_id}", "starred_url": "https://api.github.com/users/marlycormar/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/marlycormar/subscriptions", "organizations_url": "https://api.github.com/users/marlycormar/orgs", "repos_url": "https://api.github.com/users/marlycormar/repos", "events_url": "https://api.github.com/users/marlycormar/events{/privacy}", "received_events_url": "https://api.github.com/users/marlycormar/received_events", "type": "User", "site_admin": false }, "labels": [ ], "state": "open", "locked": false, "assignee": null, "assignees": [ ], "milestone": { "url": "https://api.github.com/repos/pharmaR/riskmetric/milestones/10", "html_url": "https://github.com/pharmaR/riskmetric/milestone/10", "labels_url": "https://api.github.com/repos/pharmaR/riskmetric/milestones/10/labels", "id": 5736792, "node_id": "MDk6TWlsZXN0b25lNTczNjc5Mg==", "number": 10, "title": "2020.09", "description": "", "creator": { "login": "elong0527", "id": 465246, "node_id": "MDQ6VXNlcjQ2NTI0Ng==", "avatar_url": "https://avatars0.githubusercontent.com/u/465246?v=4", "gravatar_id": "", "url": "https://api.github.com/users/elong0527", "html_url": "https://github.com/elong0527", "followers_url": "https://api.github.com/users/elong0527/followers", "following_url": "https://api.github.com/users/elong0527/following{/other_user}", "gists_url": "https://api.github.com/users/elong0527/gists{/gist_id}", "starred_url": "https://api.github.com/users/elong0527/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/elong0527/subscriptions", "organizations_url": "https://api.github.com/users/elong0527/orgs", "repos_url": "https://api.github.com/users/elong0527/repos", "events_url": "https://api.github.com/users/elong0527/events{/privacy}", "received_events_url": "https://api.github.com/users/elong0527/received_events", "type": "User", "site_admin": false }, "open_issues": 8, "closed_issues": 2, "state": "open", "created_at": "2020-08-05T16:46:24Z", "updated_at": "2020-08-27T14:51:51Z", "due_on": null, "closed_at": null }, "comments": 0, "created_at": "2020-08-19T20:52:40Z", "updated_at": "2020-08-27T14:47:47Z", "closed_at": null, "author_association": "COLLABORATOR", "active_lock_reason": null, "body": "When a package doesn't contain some of the metrics we are assessing, e.g., NEWS, bug reports, etc., the user will get warnings/errors which do not state which metrics are missing but rather output a generic error message. Should we improve the warning/error messages shown?\r\n\r\nTo see this issue in action, we can use `riskmetric` on the `extratests` package. This package lacks some of the metrics we test. For example, the following code\r\n\r\n```r\r\nlibrary(dplyr)\r\nlibrary(riskmetric)\r\n\r\npkg_ref(\"extratests\") %>%\r\n pkg_assess() %>%\r\n pkg_score() \r\n```\r\n\r\nproduces this list of warnings\r\n\r\n```\r\nWarning messages:\r\n1: In error_handler(x, ...) :\r\n no available scoring algorithm for metric of class \"pkg_metric_error\", returning default score of 0.\r\n2: In error_handler(x, ...) :\r\n no available scoring algorithm for metric of class \"pkg_metric_error\", returning default score of 0.\r\n3: In error_handler(x, ...) :\r\n no available scoring algorithm for metric of class \"pkg_metric_error\", returning default score of 0.\r\n4: In error_handler(x, ...) :\r\n no available scoring algorithm for metric of class \"pkg_metric_error\", returning default score of 0.\r\n5: In error_handler(x, ...) :\r\n no available scoring algorithm for metric of class \"pkg_metric_error\", returning default score of 0.\r\n6: In error_handler(x, ...) :\r\n no available scoring algorithm for metric of class \"pkg_metric_error\", returning default score of 0.\r\n7: In error_handler(x, ...) :\r\n no available scoring algorithm for metric of class \"pkg_metric_error\", returning default score of 0.\r\n8: In error_handler(x, ...) :\r\n no available scoring algorithm for metric of class \"pkg_metric_error\", returning default score of 0.\r\n9: In error_handler(x, ...) :\r\n no available scoring algorithm for metric of class \"pkg_metric_error\", returning default score of 0.\r\n10: In error_handler(x, ...) :\r\n no available scoring algorithm for metric of class \"pkg_metric_error\", returning default score of 0.\r\n```\r\n\r\nfrom which you cannot conclude that the NEWS file for example is missing. ", "performed_via_github_app": null }, { "url": "https://api.github.com/repos/pharmaR/riskmetric/issues/148", "repository_url": "https://api.github.com/repos/pharmaR/riskmetric", "labels_url": "https://api.github.com/repos/pharmaR/riskmetric/issues/148/labels{/name}", "comments_url": "https://api.github.com/repos/pharmaR/riskmetric/issues/148/comments", "events_url": "https://api.github.com/repos/pharmaR/riskmetric/issues/148/events", "html_url": "https://github.com/pharmaR/riskmetric/pull/148", "id": 682165286, "node_id": "MDExOlB1bGxSZXF1ZXN0NDcwNDQzNzQ5", "number": 148, "title": "Update roxygen function description for pkg_score", "user": { "login": "marlycormar", "id": 8559654, "node_id": "MDQ6VXNlcjg1NTk2NTQ=", "avatar_url": "https://avatars3.githubusercontent.com/u/8559654?v=4", "gravatar_id": "", "url": "https://api.github.com/users/marlycormar", "html_url": "https://github.com/marlycormar", "followers_url": "https://api.github.com/users/marlycormar/followers", "following_url": "https://api.github.com/users/marlycormar/following{/other_user}", "gists_url": "https://api.github.com/users/marlycormar/gists{/gist_id}", "starred_url": "https://api.github.com/users/marlycormar/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/marlycormar/subscriptions", "organizations_url": "https://api.github.com/users/marlycormar/orgs", "repos_url": "https://api.github.com/users/marlycormar/repos", "events_url": "https://api.github.com/users/marlycormar/events{/privacy}", "received_events_url": "https://api.github.com/users/marlycormar/received_events", "type": "User", "site_admin": false }, "labels": [ ], "state": "closed", "locked": false, "assignee": null, "assignees": [ ], "milestone": null, "comments": 1, "created_at": "2020-08-19T20:39:55Z", "updated_at": "2020-09-01T18:20:53Z", "closed_at": "2020-09-01T18:20:52Z", "author_association": "COLLABORATOR", "active_lock_reason": null, "pull_request": { "url": "https://api.github.com/repos/pharmaR/riskmetric/pulls/148", "html_url": "https://github.com/pharmaR/riskmetric/pull/148", "diff_url": "https://github.com/pharmaR/riskmetric/pull/148.diff", "patch_url": "https://github.com/pharmaR/riskmetric/pull/148.patch" }, "body": "`pkg_score()` now calculates the risk of using a package (rather than its quality). This PR updates the roxygen comments for this function accordingly.", "performed_via_github_app": null }, { "url": "https://api.github.com/repos/pharmaR/riskmetric/issues/147", "repository_url": "https://api.github.com/repos/pharmaR/riskmetric", "labels_url": "https://api.github.com/repos/pharmaR/riskmetric/issues/147/labels{/name}", "comments_url": "https://api.github.com/repos/pharmaR/riskmetric/issues/147/comments", "events_url": "https://api.github.com/repos/pharmaR/riskmetric/issues/147/events", "html_url": "https://github.com/pharmaR/riskmetric/issues/147", "id": 681804685, "node_id": "MDU6SXNzdWU2ODE4MDQ2ODU=", "number": 147, "title": "Fix CRAN/CI Build", "user": { "login": "elimillera", "id": 24767886, "node_id": "MDQ6VXNlcjI0NzY3ODg2", "avatar_url": "https://avatars1.githubusercontent.com/u/24767886?v=4", "gravatar_id": "", "url": "https://api.github.com/users/elimillera", "html_url": "https://github.com/elimillera", "followers_url": "https://api.github.com/users/elimillera/followers", "following_url": "https://api.github.com/users/elimillera/following{/other_user}", "gists_url": "https://api.github.com/users/elimillera/gists{/gist_id}", "starred_url": "https://api.github.com/users/elimillera/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/elimillera/subscriptions", "organizations_url": "https://api.github.com/users/elimillera/orgs", "repos_url": "https://api.github.com/users/elimillera/repos", "events_url": "https://api.github.com/users/elimillera/events{/privacy}", "received_events_url": "https://api.github.com/users/elimillera/received_events", "type": "User", "site_admin": false }, "labels": [ ], "state": "open", "locked": false, "assignee": null, "assignees": [ ], "milestone": { "url": "https://api.github.com/repos/pharmaR/riskmetric/milestones/10", "html_url": "https://github.com/pharmaR/riskmetric/milestone/10", "labels_url": "https://api.github.com/repos/pharmaR/riskmetric/milestones/10/labels", "id": 5736792, "node_id": "MDk6TWlsZXN0b25lNTczNjc5Mg==", "number": 10, "title": "2020.09", "description": "", "creator": { "login": "elong0527", "id": 465246, "node_id": "MDQ6VXNlcjQ2NTI0Ng==", "avatar_url": "https://avatars0.githubusercontent.com/u/465246?v=4", "gravatar_id": "", "url": "https://api.github.com/users/elong0527", "html_url": "https://github.com/elong0527", "followers_url": "https://api.github.com/users/elong0527/followers", "following_url": "https://api.github.com/users/elong0527/following{/other_user}", "gists_url": "https://api.github.com/users/elong0527/gists{/gist_id}", "starred_url": "https://api.github.com/users/elong0527/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/elong0527/subscriptions", "organizations_url": "https://api.github.com/users/elong0527/orgs", "repos_url": "https://api.github.com/users/elong0527/repos", "events_url": "https://api.github.com/users/elong0527/events{/privacy}", "received_events_url": "https://api.github.com/users/elong0527/received_events", "type": "User", "site_admin": false }, "open_issues": 8, "closed_issues": 2, "state": "open", "created_at": "2020-08-05T16:46:24Z", "updated_at": "2020-08-27T14:51:51Z", "due_on": null, "closed_at": null }, "comments": 0, "created_at": "2020-08-19T12:35:07Z", "updated_at": "2020-08-27T14:47:34Z", "closed_at": null, "author_association": "COLLABORATOR", "active_lock_reason": null, "body": "The CI is failing due to a few changes to dependencies and documentation.", "performed_via_github_app": null } ] ================================================ FILE: tests/testthat/test_webmocks/rebuild_webmocks.R ================================================ repo <- getOption("repos", "https://cloud.r-project.org") # no judgement on either package, just using it to stub out something that # resembles the cran package db. sorry lars!! db <- available.packages() db <- db[db[, "Package"] %in% c("glmnet", "lars"),] db[,"Package"] <- c("pkgcranremotegood", "pkgcranremotebad") write.csv(db, "./tests/testthat/test_webmocks/data/cran_packages.csv") db <- getCRANmirrors() db <- db[grepl("cloud\\.r-project\\.org", db$URL),] write.csv(db, "./tests/testthat/test_webmocks/data/cran_mirrors.csv") download.file( sprintf("%s/web/packages/glmnet", repo), "./tests/testthat/test_webmocks/data/cran_package.html") download.file( sprintf("%s/web/checks/check_results_glmnet.html", repo), "./tests/testthat/test_webmocks/data/cran_package_checks.html") download.file( sprintf("%s/web/packages/glmnet/news/news.html", repo), "./tests/testthat/test_webmocks/data/cran_news.html") download.file( sprintf("%s/src/contrib/Archive/glmnet", repo), "./tests/testthat/test_webmocks/data/cran_package_archive.html") writeLines( httr::content(httr::GET("https://api.github.com/repos/pharmaR/riskmetric/issues?state=all&per_page=3"), as = "text"), "./tests/testthat/test_webmocks/good_example/github_repo_issues_api_response.json") ================================================ FILE: tests/testthat.R ================================================ library(testthat) library(riskmetric) options(repos = "fake-cran.fake-r-project.org") test_check("riskmetric")