├── .Rbuildignore ├── .github ├── .gitignore ├── CODE_OF_CONDUCT.md └── workflows │ ├── R-CMD-check.yaml │ ├── pkgdown.yaml │ ├── pr-commands.yaml │ └── test-coverage.yaml ├── .gitignore ├── DESCRIPTION ├── LICENSE ├── LICENSE.md ├── MAINTENANCE.md ├── NAMESPACE ├── NEWS.md ├── R ├── available-package.R ├── available.R ├── bad_words.R ├── bioc.R ├── cran.R ├── dist.R ├── github.R ├── name_to_search_terms.R ├── namr.R ├── sentiment.R ├── utils.R ├── valid_name.R └── wiki.R ├── README.Rmd ├── README.md ├── _pkgdown.yml ├── available.Rproj ├── codecov.yml ├── man ├── available-package.Rd ├── available.Rd ├── available_on_cran.Rd ├── available_on_github.Rd ├── common_suffixes.Rd ├── create.Rd ├── find_acronym.Rd ├── get_bad_words.Rd ├── get_sentiment.Rd ├── get_wikipedia.Rd ├── make_spelling_rlike.Rd ├── namr.Rd ├── pick_word_from_title.Rd ├── pkg_name_dist.Rd ├── suggest.Rd └── valid_package_name.Rd └── tests ├── testthat.R └── testthat ├── test-badwords.R ├── test-bioc.R ├── test-cran.R ├── test-github.R ├── test-name_to_search_terms.R └── test-namr.R /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^CRAN-RELEASE$ 2 | ^.*\.Rproj$ 3 | ^\.Rproj\.user$ 4 | ^\.travis\.yml$ 5 | ^README\.Rmd$ 6 | ^README\.html$ 7 | ^README-.*\.png$ 8 | ^cran-comments\.md$ 9 | ^script\.R$ 10 | ^MAINTENANCE\.md$ 11 | ^codecov\.yml$ 12 | ^\.github$ 13 | ^_pkgdown\.yml$ 14 | ^docs$ 15 | ^pkgdown$ 16 | ^LICENSE\.md$ 17 | -------------------------------------------------------------------------------- /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, caste, color, religion, or sexual 10 | identity and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the overall 26 | community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or advances of 31 | any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email address, 35 | without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at codeofconduct@posit.co. 63 | All complaints will be reviewed and investigated promptly and fairly. 64 | 65 | All community leaders are obligated to respect the privacy and security of the 66 | reporter of any incident. 67 | 68 | ## Enforcement Guidelines 69 | 70 | Community leaders will follow these Community Impact Guidelines in determining 71 | the consequences for any action they deem in violation of this Code of Conduct: 72 | 73 | ### 1. Correction 74 | 75 | **Community Impact**: Use of inappropriate language or other behavior deemed 76 | unprofessional or unwelcome in the community. 77 | 78 | **Consequence**: A private, written warning from community leaders, providing 79 | clarity around the nature of the violation and an explanation of why the 80 | behavior was inappropriate. A public apology may be requested. 81 | 82 | ### 2. Warning 83 | 84 | **Community Impact**: A violation through a single incident or series of 85 | actions. 86 | 87 | **Consequence**: A warning with consequences for continued behavior. No 88 | interaction with the people involved, including unsolicited interaction with 89 | those enforcing the Code of Conduct, for a specified period of time. This 90 | includes avoiding interactions in community spaces as well as external channels 91 | like social media. Violating these terms may lead to a temporary or permanent 92 | ban. 93 | 94 | ### 3. Temporary Ban 95 | 96 | **Community Impact**: A serious violation of community standards, including 97 | sustained inappropriate behavior. 98 | 99 | **Consequence**: A temporary ban from any sort of interaction or public 100 | communication with the community for a specified period of time. No public or 101 | private interaction with the people involved, including unsolicited interaction 102 | with those enforcing the Code of Conduct, is allowed during this period. 103 | Violating these terms may lead to a permanent ban. 104 | 105 | ### 4. Permanent Ban 106 | 107 | **Community Impact**: Demonstrating a pattern of violation of community 108 | standards, including sustained inappropriate behavior, harassment of an 109 | individual, or aggression toward or disparagement of classes of individuals. 110 | 111 | **Consequence**: A permanent ban from any sort of public interaction within the 112 | community. 113 | 114 | ## Attribution 115 | 116 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 117 | version 2.1, available at 118 | . 119 | 120 | Community Impact Guidelines were inspired by 121 | [Mozilla's code of conduct enforcement ladder][https://github.com/mozilla/inclusion]. 122 | 123 | For answers to common questions about this code of conduct, see the FAQ at 124 | . Translations are available at . 125 | 126 | [homepage]: https://www.contributor-covenant.org 127 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | # 4 | # NOTE: This workflow is overkill for most R packages and 5 | # check-standard.yaml is likely a better choice. 6 | # usethis::use_github_action("check-standard") will install it. 7 | on: 8 | push: 9 | branches: [main, master] 10 | pull_request: 11 | branches: [main, master] 12 | 13 | name: R-CMD-check.yaml 14 | 15 | permissions: read-all 16 | 17 | jobs: 18 | R-CMD-check: 19 | runs-on: ${{ matrix.config.os }} 20 | 21 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 22 | 23 | strategy: 24 | fail-fast: false 25 | matrix: 26 | config: 27 | - {os: macos-latest, r: 'release'} 28 | 29 | - {os: windows-latest, r: 'release'} 30 | # use 4.0 or 4.1 to check with rtools40's older compiler 31 | - {os: windows-latest, r: 'oldrel-4'} 32 | 33 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} 34 | - {os: ubuntu-latest, r: 'release'} 35 | - {os: ubuntu-latest, r: 'oldrel-1'} 36 | - {os: ubuntu-latest, r: 'oldrel-2'} 37 | - {os: ubuntu-latest, r: 'oldrel-3'} 38 | - {os: ubuntu-latest, r: 'oldrel-4'} 39 | 40 | env: 41 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 42 | R_KEEP_PKG_SOURCE: yes 43 | 44 | steps: 45 | - uses: actions/checkout@v4 46 | 47 | - uses: r-lib/actions/setup-pandoc@v2 48 | 49 | - uses: r-lib/actions/setup-r@v2 50 | with: 51 | r-version: ${{ matrix.config.r }} 52 | http-user-agent: ${{ matrix.config.http-user-agent }} 53 | use-public-rspm: true 54 | 55 | - uses: r-lib/actions/setup-r-dependencies@v2 56 | with: 57 | extra-packages: any::rcmdcheck 58 | needs: check 59 | 60 | - uses: r-lib/actions/check-r-package@v2 61 | with: 62 | upload-snapshots: true 63 | build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' 64 | -------------------------------------------------------------------------------- /.github/workflows/pkgdown.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | release: 9 | types: [published] 10 | workflow_dispatch: 11 | 12 | name: pkgdown.yaml 13 | 14 | permissions: read-all 15 | 16 | jobs: 17 | pkgdown: 18 | runs-on: ubuntu-latest 19 | # Only restrict concurrency for non-PR jobs 20 | concurrency: 21 | group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }} 22 | env: 23 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 24 | permissions: 25 | contents: write 26 | steps: 27 | - uses: actions/checkout@v4 28 | 29 | - uses: r-lib/actions/setup-pandoc@v2 30 | 31 | - uses: r-lib/actions/setup-r@v2 32 | with: 33 | use-public-rspm: true 34 | 35 | - uses: r-lib/actions/setup-r-dependencies@v2 36 | with: 37 | extra-packages: any::pkgdown, local::. 38 | needs: website 39 | 40 | - name: Build site 41 | run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) 42 | shell: Rscript {0} 43 | 44 | - name: Deploy to GitHub pages 🚀 45 | if: github.event_name != 'pull_request' 46 | uses: JamesIves/github-pages-deploy-action@v4.5.0 47 | with: 48 | clean: false 49 | branch: gh-pages 50 | folder: docs 51 | -------------------------------------------------------------------------------- /.github/workflows/pr-commands.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | issue_comment: 5 | types: [created] 6 | 7 | name: pr-commands.yaml 8 | 9 | permissions: read-all 10 | 11 | jobs: 12 | document: 13 | if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/document') }} 14 | name: document 15 | runs-on: ubuntu-latest 16 | env: 17 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 18 | permissions: 19 | contents: write 20 | steps: 21 | - uses: actions/checkout@v4 22 | 23 | - uses: r-lib/actions/pr-fetch@v2 24 | with: 25 | repo-token: ${{ secrets.GITHUB_TOKEN }} 26 | 27 | - uses: r-lib/actions/setup-r@v2 28 | with: 29 | use-public-rspm: true 30 | 31 | - uses: r-lib/actions/setup-r-dependencies@v2 32 | with: 33 | extra-packages: any::roxygen2 34 | needs: pr-document 35 | 36 | - name: Document 37 | run: roxygen2::roxygenise() 38 | shell: Rscript {0} 39 | 40 | - name: commit 41 | run: | 42 | git config --local user.name "$GITHUB_ACTOR" 43 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 44 | git add man/\* NAMESPACE 45 | git commit -m 'Document' 46 | 47 | - uses: r-lib/actions/pr-push@v2 48 | with: 49 | repo-token: ${{ secrets.GITHUB_TOKEN }} 50 | 51 | style: 52 | if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/style') }} 53 | name: style 54 | runs-on: ubuntu-latest 55 | env: 56 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 57 | permissions: 58 | contents: write 59 | steps: 60 | - uses: actions/checkout@v4 61 | 62 | - uses: r-lib/actions/pr-fetch@v2 63 | with: 64 | repo-token: ${{ secrets.GITHUB_TOKEN }} 65 | 66 | - uses: r-lib/actions/setup-r@v2 67 | 68 | - name: Install dependencies 69 | run: install.packages("styler") 70 | shell: Rscript {0} 71 | 72 | - name: Style 73 | run: styler::style_pkg() 74 | shell: Rscript {0} 75 | 76 | - name: commit 77 | run: | 78 | git config --local user.name "$GITHUB_ACTOR" 79 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 80 | git add \*.R 81 | git commit -m 'Style' 82 | 83 | - uses: r-lib/actions/pr-push@v2 84 | with: 85 | repo-token: ${{ secrets.GITHUB_TOKEN }} 86 | -------------------------------------------------------------------------------- /.github/workflows/test-coverage.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | 9 | name: test-coverage.yaml 10 | 11 | permissions: read-all 12 | 13 | jobs: 14 | test-coverage: 15 | runs-on: ubuntu-latest 16 | env: 17 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 18 | 19 | steps: 20 | - uses: actions/checkout@v4 21 | 22 | - uses: r-lib/actions/setup-r@v2 23 | with: 24 | use-public-rspm: true 25 | 26 | - uses: r-lib/actions/setup-r-dependencies@v2 27 | with: 28 | extra-packages: any::covr, any::xml2 29 | needs: coverage 30 | 31 | - name: Test coverage 32 | run: | 33 | cov <- covr::package_coverage( 34 | quiet = FALSE, 35 | clean = FALSE, 36 | install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package") 37 | ) 38 | covr::to_cobertura(cov) 39 | shell: Rscript {0} 40 | 41 | - uses: codecov/codecov-action@v4 42 | with: 43 | fail_ci_if_error: ${{ github.event_name != 'pull_request' && true || false }} 44 | file: ./cobertura.xml 45 | plugin: noop 46 | disable_search: true 47 | token: ${{ secrets.CODECOV_TOKEN }} 48 | 49 | - name: Show testthat output 50 | if: always() 51 | run: | 52 | ## -------------------------------------------------------------------- 53 | find '${{ runner.temp }}/package' -name 'testthat.Rout*' -exec cat '{}' \; || true 54 | shell: bash 55 | 56 | - name: Upload test results 57 | if: failure() 58 | uses: actions/upload-artifact@v4 59 | with: 60 | name: coverage-test-failures 61 | path: ${{ runner.temp }}/package 62 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | README.html 5 | script.R 6 | docs 7 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: available 2 | Title: Check if the Title of a Package is Available, Appropriate and 3 | Interesting 4 | Version: 1.1.0.9000 5 | Authors@R: c( 6 | person("Carl", "Ganz", role = "aut"), 7 | person("Gábor", "Csárdi", , "csardi.gabor@gmail.com", role = c("aut", "cre")), 8 | person("Jim", "Hester", role = "aut"), 9 | person("Molly", "Lewis", role = "aut"), 10 | person("Rachael", "Tatman", role = "aut"), 11 | person("Posit Software, PBC", role = c("cph", "fnd")) 12 | ) 13 | Description: Check if a given package name is available to use. It checks 14 | the name's validity. Checks if it is used on 'GitHub', 'CRAN' and 15 | 'Bioconductor'. Checks for unintended meanings by querying 16 | 'Wiktionary' and Wikipedia. 17 | License: MIT + file LICENSE 18 | URL: https://github.com/r-lib/available, 19 | https://r-lib.github.io/available/ 20 | BugReports: https://github.com/r-lib/available/issues 21 | Depends: 22 | R (>= 3.6) 23 | Imports: 24 | cli, 25 | desc, 26 | glue, 27 | jsonlite, 28 | memoise, 29 | rlang, 30 | SnowballC, 31 | stringdist, 32 | tidytext 33 | Suggests: 34 | BiocManager, 35 | testthat (>= 3.0.0), 36 | usethis 37 | Config/Needs/website: tidyverse/tidytemplate 38 | Config/testthat/edition: 3 39 | Encoding: UTF-8 40 | Roxygen: list(markdown = TRUE) 41 | RoxygenNote: 7.3.2 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2023 2 | COPYRIGHT HOLDER: available authors 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2023 available authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MAINTENANCE.md: -------------------------------------------------------------------------------- 1 | ## Current state 2 | 3 | Available is very stable, lightly used, don't need much maintenance. 4 | 5 | ## Known outstanding issues 6 | 7 | One case that comes up periodically is the service at http://rpkg-api.gepuro.net/ goes down. It is possible we could add some code to improve that, or look for GitHub projects in a different way, maybe using GitHub search directly. 8 | 9 | ## Future directions 10 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method(format,available_bad_words) 4 | S3method(format,available_bioc) 5 | S3method(format,available_cran) 6 | S3method(format,available_github) 7 | S3method(format,available_sentiment) 8 | S3method(format,available_valid_name) 9 | S3method(format,available_wikipedia) 10 | S3method(format,available_wiktionary) 11 | S3method(print,available_abbreviation) 12 | S3method(print,available_bad_words) 13 | S3method(print,available_bioc) 14 | S3method(print,available_cran) 15 | S3method(print,available_github) 16 | S3method(print,available_query) 17 | S3method(print,available_sentiment) 18 | S3method(print,available_valid_name) 19 | S3method(print,available_wikipedia) 20 | S3method(print,available_wiktionary) 21 | S3method(testthat::compare,glue) 22 | export(available) 23 | export(available_on_bioc) 24 | export(available_on_cran) 25 | export(available_on_github) 26 | export(get_bad_words) 27 | export(get_sentiment) 28 | export(get_wikipedia) 29 | export(get_wiktionary) 30 | export(suggest) 31 | export(valid_package_name) 32 | importFrom(jsonlite,fromJSON) 33 | importFrom(memoise,memoise) 34 | importFrom(stats,na.omit) 35 | importFrom(stringdist,stringdist) 36 | importFrom(utils,browseURL) 37 | importFrom(utils,head) 38 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # available (development version) 2 | 3 | # available 1.1.0 4 | 5 | * Removed Urban Dictionary support, as the udapi package is set to be 6 | archived. 7 | 8 | # available 1.0.5 9 | 10 | * Gábor Csárdi is now the maintainer. 11 | 12 | * `get_wikipidia()` renamed to `get_wikipedia()`. 13 | 14 | * `valid_package_name()` now correctly checks a package name according to CRAN policy (@KevCaz, #61). 15 | 16 | # available 1.0.4 17 | 18 | * Tests requiring network access are now skipped on CRAN. 19 | 20 | # available 1.0.3 21 | 22 | * `BiocManager` is now preferred to `BiocInstaller` if both are installed (#44, @luciorq). 23 | 24 | * `create()` now uses `usethis::create_package()` rather than the deprecated `devtools::create()`. 25 | 26 | # available 1.0.2 27 | 28 | * Add dialog when run interactively asking if urban dictionary results should 29 | be included, as they can potentially contain offensive results (#41). 30 | * Use BiocManager for compatibility with future versions of R. 31 | 32 | # available 1.0.1 33 | 34 | * Filter own repo from GitHub results (#21). 35 | * `get_urban_data()` is now exported (#34). 36 | * No longer trimming r or R when proceeded by a vowel from search terms, as originally intended (#35). 37 | * Support for upcoming glue 1.3.0 release 38 | 39 | # available 1.0.0 40 | 41 | * Initial release 42 | -------------------------------------------------------------------------------- /R/available-package.R: -------------------------------------------------------------------------------- 1 | #' @keywords internal 2 | "_PACKAGE" 3 | 4 | ## usethis namespace: start 5 | #' @importFrom utils browseURL 6 | #' @importFrom stats na.omit 7 | ## usethis namespace: end 8 | NULL 9 | -------------------------------------------------------------------------------- /R/available.R: -------------------------------------------------------------------------------- 1 | #' See if a name is available 2 | #' 3 | #' Searches performed 4 | #' - Valid package name 5 | #' - Already taken on CRAN 6 | #' - Positive or negative sentiment 7 | #' @param name Name of package to search 8 | #' @param browse Whether browser should be opened for all web links, 9 | #' default = TRUE. Default can be changed by setting 10 | #' \code{available.browse} in \code{.Rprofile}. See \link[base]{Startup} 11 | #' for more details. 12 | #' @param ... Additional arguments passed to [utils::available.packages()]. 13 | #' @importFrom memoise memoise 14 | #' @examples 15 | #' \dontrun{ 16 | #' # Check if the available package is available 17 | #' available("available") 18 | #' 19 | #' # You can disable opening of browser windows with browse = FALSE 20 | #' available("survival", browse = FALSE) 21 | #' 22 | #' # Or by setting a global option 23 | #' options(available.browse = FALSE) 24 | #' available("survival") 25 | #' 26 | #' # Test if a name is available in a non-default CRAN repository by setting 27 | #' # the `repos` argument 28 | #' available_on_cran("semaforos") 29 | #' 30 | #' available_on_cran("semaforos", repos = "https://bisaloo.r-universe.dev") 31 | #' } 32 | #' @export 33 | available <- function(name, browse = getOption("available.browse", TRUE), ...) { 34 | res <- list( 35 | valid_package_name(name), 36 | available_on_cran(name, ...), 37 | available_on_bioc(name, ...), 38 | available_on_github(name) 39 | ) 40 | terms <- name_to_search_terms(name) 41 | 42 | res <- c( 43 | res, 44 | unlist( 45 | recursive = FALSE, 46 | lapply(terms, check_online_terms) 47 | ) 48 | ) 49 | structure(res, 50 | class = "available_query", packagename = name, 51 | browse = browse 52 | ) 53 | } 54 | 55 | #' @export 56 | print.available_query <- function(x, ...) { 57 | if (!attr(x, "browse")) { 58 | base_browser <- getOption("browser") 59 | options(browser = "false") 60 | on.exit(options(browser = base_browser)) 61 | } 62 | cat(cli::rule(attr(x, "packagename")), "\n", sep = "") 63 | for (i in x) { 64 | print(i) 65 | } 66 | invisible(x) 67 | } 68 | 69 | #' Check a new package name and possibly create it 70 | #' 71 | #' @inheritParams available 72 | #' @param ... Additional arguments passed to [usethis::create_package()]. 73 | create <- function(name, ...) { 74 | print(available(name)) 75 | 76 | ans <- utils::menu( 77 | choices = c("Yes", "No"), 78 | title = cli::format_inline("Create package {.pkg {name}}?") 79 | ) 80 | if (ans == 1) { 81 | rlang::check_installed("usethis", "to create a package.") 82 | usethis::create_package(name, ...) 83 | } 84 | } 85 | 86 | #' Suggest a package name based on a development package title or description 87 | #' 88 | #' If the package you are using already has a title, simply pass the path to 89 | #' the package root in `path`. Otherwise use `title` to specify a potential 90 | #' title. 91 | #' @param path Path to a existing package to extract the title from. 92 | #' @param field one of "Title" or "Description" 93 | #' @param text text string to search. 94 | #' @export 95 | #' @examples 96 | #' \dontrun{ 97 | #' # Default will use the title from the current path. 98 | #' suggest() 99 | #' 100 | #' # Can also suggest based on the description 101 | #' suggest(field = "Description") 102 | #' } 103 | #' 104 | #' # Or by explicitly using the text argument 105 | #' suggest( 106 | #' text = 107 | #' "A Package for Displaying Visual Scenes as They May Appear to an Animal with Lower Acuity" 108 | #' ) 109 | suggest <- function(path = ".", field = c("Title", "Description"), text = NULL) { 110 | if (is.null(text)) { 111 | if (file.exists(path)) { 112 | field <- match.arg(field) 113 | text <- tryCatch( 114 | error = function(e) NA, 115 | unname(desc::desc(path)$get(field)) 116 | ) 117 | } else { 118 | text <- path 119 | } 120 | if (is.na(text)) { 121 | cli::cli_abort("No text found, please specify one with `text`.", call = NULL) 122 | } 123 | } 124 | 125 | namr(text) 126 | } 127 | 128 | 129 | 130 | check_online_terms <- function(term) { 131 | compact(list( 132 | get_bad_words(term), 133 | get_abbreviation(term), 134 | get_wikipedia(term), 135 | get_wiktionary(term), 136 | get_sentiment(term) 137 | )) 138 | } 139 | -------------------------------------------------------------------------------- /R/bad_words.R: -------------------------------------------------------------------------------- 1 | bad_words <- memoise::memoise(function() { 2 | url <- "https://raw.githubusercontent.com/web-mech/badwords/master/lib/lang.json" 3 | 4 | words <- jsonlite::fromJSON(url)[[1]] 5 | # remove non-alphanumerics 6 | words <- gsub("[^[:alnum:]]", "", words) 7 | # remove blank and short strings 8 | words <- words[nchar(words) > 2] 9 | 10 | unique(words) 11 | }) 12 | 13 | #' Check for bad words in name 14 | #' 15 | #' @inheritParams available 16 | #' @export 17 | #' @seealso See \url{https://github.com/web-mech/badwords} 18 | get_bad_words <- function(name) { 19 | # check each bad word to see if in package name 20 | bad <- grepl(glue_collapse(bad_words(), "|"), name) 21 | 22 | structure(name[bad], class = "available_bad_words") 23 | } 24 | 25 | #' @export 26 | 27 | format.available_bad_words <- function(x, ...) { 28 | good <- cli::col_green 29 | bad <- cli::combine_ansi_styles(cli::bg_red, cli::col_white) 30 | cli::cat_line( 31 | cli::style_bold("Bad Words: "), 32 | if (length(x) == 0) { 33 | good(cli::symbol$tick) 34 | } else { 35 | bad(cli::ansi_collapse(x)) 36 | }, 37 | ) 38 | } 39 | 40 | #' @export 41 | 42 | print.available_bad_words <- function(x, ...) { 43 | cat(format(x, ...)) 44 | invisible(x) 45 | } 46 | 47 | mark_bad_words <- function(text, marker = NULL) { 48 | if (is.null(marker)) { 49 | marker <- cli::combine_ansi_styles(cli::col_white, cli::bg_red) 50 | } 51 | 52 | vapply( 53 | tolower(text), 54 | mark_bad_words1, 55 | character(1), 56 | marker = marker, 57 | USE.NAMES = FALSE 58 | ) 59 | } 60 | 61 | mark_bad_words1 <- function(text1, marker) { 62 | word_pos <- gregexpr("\\b\\w+\\b", text1) 63 | if (length(word_pos[[1]]) == 1 && word_pos == -1) { 64 | return(text1) 65 | } 66 | start <- c(word_pos[[1]]) 67 | end <- start + attr(word_pos[[1]], "match.length") - 1 68 | words <- substring(text1, start, end) 69 | stemmed <- SnowballC::wordStem(words, language = "english") 70 | 71 | bad <- words %in% bad_words() | stemmed %in% bad_words() 72 | regmatches(text1, word_pos) <- list(ifelse( 73 | bad, marker(words), words 74 | )) 75 | text1 76 | } 77 | -------------------------------------------------------------------------------- /R/bioc.R: -------------------------------------------------------------------------------- 1 | #' @rdname available_on_cran 2 | #' @export 3 | available_on_bioc <- function(name, repos = NULL, ...) { 4 | if (is.null(repos)) { 5 | if (requireNamespace("BiocManager", quietly = TRUE)) { 6 | repos <- BiocManager::repositories() 7 | } else { 8 | # Search on latest bioc release 9 | repos <- c( 10 | BioCsoft = "https://bioconductor.org/packages/release/bioc", 11 | BioCann = "https://bioconductor.org/packages/release/data/annotation", 12 | BioCexp = "https://bioconductor.org/packages/release/data/experiment" 13 | ) 14 | } 15 | } 16 | bioc_names <- rownames(available_packages(repos = repos, ...)) 17 | on_bioc <- tolower(name) %in% tolower(bioc_names) 18 | structure(!on_bioc, class = "available_bioc") 19 | } 20 | 21 | #' @export 22 | 23 | format.available_bioc <- function(x, ...) { 24 | paste0(cli::style_bold("Available on Bioconductor: "), yes_no(x[[1]]), "\n") 25 | } 26 | 27 | #' @export 28 | 29 | print.available_bioc <- function(x, ...) { 30 | cat(format(x, ...)) 31 | invisible(x) 32 | } 33 | -------------------------------------------------------------------------------- /R/cran.R: -------------------------------------------------------------------------------- 1 | #' See if a name is available on CRAN 2 | #' 3 | #' @param name Name of package to search 4 | #' @param ... Additional arguments passed to [utils::available.packages()]. 5 | #' @inheritParams utils::available.packages 6 | #' 7 | #' @examples 8 | #' available_on_cran("semaforos") 9 | #' 10 | #' # Test if this name is available in a non-default CRAN repository 11 | #' \dontrun{ 12 | #' available_on_cran("semaforos", repos = "https://bisaloo.r-universe.dev") 13 | #' } 14 | #' @export 15 | available_on_cran <- function(name, repos = default_cran_repos, ...) { 16 | cran_names <- rownames(available_packages(repos = repos, ...)) 17 | archive_names <- names(archive_packages()) 18 | 19 | on_cran <- tolower(name) %in% tolower(cran_names) 20 | 21 | on_cran_archive <- tolower(name) %in% tolower(archive_names) 22 | 23 | # TODO use adist to return close names 24 | structure(!on_cran && !on_cran_archive, class = "available_cran") 25 | } 26 | 27 | archive_packages <- memoise::memoise(function() { 28 | ("tools" %:::% "CRAN_archive_db")() 29 | }) 30 | 31 | available_packages <- memoise::memoise(available.packages) 32 | 33 | #' @export 34 | format.available_cran <- function(x, ...) { 35 | cat(cli::style_bold("Available on CRAN: "), yes_no(x[[1]]), "\n") 36 | } 37 | 38 | #' @export 39 | print.available_cran <- function(x, ...) { 40 | cat(format(x, ...)) 41 | invisible(x) 42 | } 43 | 44 | default_cran_repos <- c( 45 | CRAN = "https://cloud.r-project.org", 46 | CRANextra = "https://www.stats.ox.ac.uk/pub/RWin" 47 | ) 48 | -------------------------------------------------------------------------------- /R/dist.R: -------------------------------------------------------------------------------- 1 | #' Find five closest package names in terms of string distance 2 | #' 3 | #' @param name name of package 4 | #' @param pkgs packages to compare with 5 | #' @importFrom stringdist stringdist 6 | #' @importFrom utils head 7 | #' 8 | pkg_name_dist <- function(name, pkgs) { 9 | if (is.data.frame(pkgs)) { 10 | distances <- stringdist::stringdist(name, pkgs[["pkg_name"]]) 11 | 12 | cbind( 13 | utils::head(pkgs[order(distances), ]), 14 | distance = utils::head(distances[order(distances)]) 15 | ) 16 | } else { 17 | distances <- stringdist::stringdist(name, pkgs) 18 | 19 | data.frame( 20 | pkgs = utils::head(pkgs[order(distances)]), 21 | distance = utils::head(distances[order(distances)]), 22 | stringsAsFactors = FALSE 23 | ) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /R/github.R: -------------------------------------------------------------------------------- 1 | #' See if a name is available on github 2 | #' 3 | #' @param name Name of package to search 4 | #' @importFrom jsonlite fromJSON 5 | #' @export 6 | available_on_github <- function(name) { 7 | github_names <- gh_pkg(name) 8 | my_links <- github_link_location() 9 | github_names <- github_names[!github_names[["pkg_location"]] %in% my_links, ] 10 | 11 | same <- tolower(name) == tolower(github_names[["pkg_name"]]) 12 | if (any(same)) { 13 | return( 14 | structure( 15 | list( 16 | available = FALSE, 17 | close = list(pkg_name_dist(name, github_names)) 18 | ), 19 | class = "available_github" 20 | ) 21 | ) 22 | } 23 | 24 | structure( 25 | list( 26 | available = TRUE, 27 | close = list() 28 | ), 29 | class = "available_github" 30 | ) 31 | } 32 | 33 | gh_pkg <- memoise::memoise(function(pkg) { 34 | res <- jsonlite::fromJSON(paste0("http://rpkg-api.gepuro.net/rpkg?q=", pkg)) 35 | if (length(res) == 0) { 36 | return(data.frame(pkg_name = character(), pkg_location = character(), pkg_org = character())) 37 | } 38 | res$pkg_location <- res$pkg_name 39 | res$pkg_org <- vapply(strsplit(res$pkg_location, "/"), `[[`, character(1), 1) 40 | res$pkg_name <- vapply(strsplit(res$pkg_location, "/"), `[[`, character(1), 2) 41 | res[!(res$pkg_org == "cran" | res$pkg_org == "Bioconductor-mirror"), ] 42 | }) 43 | 44 | #' @export 45 | 46 | format.available_github <- function(x, ...) { 47 | paste0(cli::style_bold("Available on GitHub: ", yes_no(x[[1]]), "\n")) 48 | } 49 | 50 | #' @export 51 | 52 | print.available_github <- function(x, ...) { 53 | cat(format(x, ...)) 54 | invisible(x) 55 | } 56 | 57 | github_link_location <- function() { 58 | tryCatch( 59 | { 60 | desc <- desc::desc() 61 | urls <- desc$get_urls() 62 | gh_links <- grep("^https?://github.com/", urls, value = TRUE) 63 | 64 | gsub("https?://github.com/(.*)/?$", "\\1", gh_links) 65 | }, 66 | error = function(e) character(0) 67 | ) 68 | } 69 | -------------------------------------------------------------------------------- /R/name_to_search_terms.R: -------------------------------------------------------------------------------- 1 | # Function that takes a name and generates search terms based on it 2 | name_to_search_terms <- function(name) { 3 | if (name == toupper(name)) { 4 | return(name) 5 | } 6 | # generate test strings by splitting on & removing periods & digits 7 | output <- strsplit(x = name, split = "[[:digit:][:punct:]]") 8 | 9 | # generate test strings by splitting on and removing uppercase letters 10 | removedUppers <- strsplit(x = output[[1]], split = "[[:upper:]]") 11 | # split on but do not remove uppercase characters 12 | # assume uppercase first letter in word 13 | uppersFirst <- strsplit(gsub("([[:upper:]])", " \\1", output[[1]]), split = " ") 14 | # assume uppercase last letter in word (it happens!) 15 | uppersLast <- strsplit(gsub("([[:upper:]])", "\\1 ", output[[1]]), split = " ") 16 | 17 | # combine list of search terms generated based on different assumptions about use of capitals 18 | search_terms <- unique(unlist(c(uppersFirst, uppersLast, removedUppers))) 19 | 20 | # generate test strings by splitting on/removing r 21 | # searchTermsWithR <- unique(c(searchTerms, unlist(strsplit(x = searchTerms, split = "R")))) 22 | 23 | # remove elements that are just "r", "R" or empty & add name 24 | search_terms <- c(setdiff(search_terms, c("r", "R", "", name)), name) 25 | 26 | # remove trailing r that is not preceded by a vowel 27 | search_terms <- sub("(? 15 characters) 40 | # remove very short words (< 5 characters) 41 | word_vector <- word_vector[nchar(word_vector) < 15 | nchar(word_vector) > 5] 42 | 43 | package_name <- character() 44 | 45 | # pick the first verb (if the user has requested a verb) or the longest edge word (< 15 characters) 46 | if (length(word_vector) > 1) { 47 | # get the part of speech for every word left in our vector 48 | POS <- apply( 49 | as.matrix(word_vector), 1, 50 | function(x) { 51 | tidytext::parts_of_speech$pos[tidytext::parts_of_speech$word == x][1] 52 | } 53 | ) 54 | first <- word_vector[1] 55 | last <- word_vector[length(word_vector)] 56 | if (sum(grepl("Verb", POS)) > 0 && verb) { 57 | package_name <- word_vector[grepl("Verb", POS)][1] 58 | } else if (nchar(last) > nchar(first)) { 59 | package_name <- last 60 | } else { 61 | package_name <- first 62 | } 63 | } 64 | 65 | # make sure we always return a package name 66 | if (length(word_vector) == 1) { 67 | package_name <- word_vector[1] 68 | } 69 | 70 | # remove punctuation 71 | package_name <- gsub("[[:punct:]]", "", package_name) 72 | 73 | # make sure 74 | if (length(package_name) == 0) { 75 | cli::cli_abort(c( 76 | x = "Sorry, we couldn't make a good name from your tile.", 77 | i = "Try using more specific words in your description." 78 | )) 79 | } 80 | 81 | package_name 82 | } 83 | 84 | #' Spelling transformations 85 | #' 86 | #' This function takes in a single word and applies spelling transformations to 87 | #' make it more "r-like" and easier to Google 88 | #' 89 | #' @param word a single word to make more rlike 90 | #' @return a single word with a spelling transformation 91 | #' @keywords internal 92 | make_spelling_rlike <- function(word) { 93 | # convert string into vector of lowercase characters 94 | chars <- unlist(strsplit(tolower(word), "")) 95 | 96 | # list of vowels 97 | vowels <- c("a", "e", "i", "o", "u") 98 | 99 | # set a variable that tells us whether we've made a spelling transformation 100 | spelling_changed <- FALSE 101 | 102 | # remove second to last letter if it's a vowel and the last letter is r 103 | if (chars[length(chars) - 1] %in% vowels & chars[length(chars)] == "r") { 104 | chars <- chars[-(length(chars) - 1)] 105 | spelling_changed <- TRUE 106 | } 107 | 108 | # if the first letter is a vowel and the second letter is an r, remove the first letter 109 | if (chars[1] %in% vowels & chars[2] == "r" & spelling_changed == FALSE) { 110 | chars <- chars[-1] 111 | spelling_changed <- TRUE 112 | } 113 | 114 | # if the word ends with an r but there isn't a vowel in front of it, add an r to the beginning 115 | if (chars[length(chars) - 1] %in% vowels == FALSE & chars[length(chars)] == "r" & spelling_changed == FALSE) { 116 | chars <- c("r", chars) 117 | spelling_changed <- TRUE 118 | } 119 | 120 | # if there hasn't been a spelling change, add an "r" to the end 121 | if (spelling_changed == FALSE) { 122 | chars <- c(chars, "r") 123 | } 124 | 125 | glue_collapse(unlist(chars)) 126 | } 127 | 128 | #' function to add common, informative suffixes 129 | #' 130 | #' Search a title for common terms (plot, vis..., viz..., markdown) and apply 131 | #' appropriate affixes to a given word as applicable. 132 | #' 133 | #' @param title the package title or description 134 | #' @param name the single word that will be appended to 135 | #' 136 | #' @return a single word with affix, if applicable 137 | #' @keywords internal 138 | common_suffixes <- function(title, name) { 139 | # add "plot", "viz" or "vis" to the end of the package name if that appears in the title 140 | if (grepl("\\ "1.2.0") { 19 | utils::getFromNamespace("glue_collapse", "glue")(...) 20 | } else { 21 | utils::getFromNamespace("collapse", "glue")(...) 22 | } 23 | } 24 | 25 | #' @exportS3Method testthat::compare 26 | compare.glue <- function(x, y) { 27 | if (identical(class(y), "character")) { 28 | class(x) <- NULL 29 | } 30 | NextMethod("compare") 31 | } 32 | 33 | compact <- function(x) { 34 | len <- lengths(x) 35 | x[!len == 0] 36 | } 37 | -------------------------------------------------------------------------------- /R/valid_name.R: -------------------------------------------------------------------------------- 1 | #' Is a package name valid 2 | #' @inheritParams available 3 | #' @export 4 | valid_package_name <- function(name) { 5 | # This is the algorithm used in R CMD check at 6 | # https://github.com/wch/r-source/blob/a3a73a730962fa214b4af0ded55b497fb5688b8b/src/library/tools/R/QC.R#L3214 7 | valid <- TRUE 8 | 9 | # check for a package called 'R' 10 | res <- if (tolower(name) == "r") { 11 | FALSE 12 | } else { 13 | grepl(glue::glue("^{valid_package_name_regexp}$"), name) 14 | } 15 | structure(res, class = "available_valid_name") 16 | } 17 | 18 | #' @export 19 | 20 | format.available_valid_name <- function(x, ...) { 21 | paste0(cli::style_bold("Name valid: "), yes_no(x[[1]]), "\n") 22 | } 23 | 24 | #' @export 25 | 26 | print.available_valid_name <- function(x, ...) { 27 | cat(format(x, ...)) 28 | invisible(x) 29 | } 30 | 31 | valid_package_name_regexp <- 32 | .standard_regexps()$valid_package_name 33 | -------------------------------------------------------------------------------- /R/wiki.R: -------------------------------------------------------------------------------- 1 | #' Open wikipedia page and abbreviations.com page 2 | #' 3 | #' @inheritParams available 4 | #' @export 5 | get_wikipedia <- function(name) { 6 | # TODO: handle case when we can't open browser 7 | url <- paste0("https://en.wikipedia.org/wiki/", name) 8 | 9 | structure(url, class = "available_wikipedia") 10 | } 11 | 12 | #' @export 13 | format.available_wikipedia <- function(x, ...) { 14 | browseURL(x[[1]]) 15 | paste0(cli::style_bold("Wikipedia: "), cli::format_inline("{.url {x[[1L]]}}"), "\n") 16 | } 17 | 18 | #' @export 19 | print.available_wikipedia <- function(x, ...) { 20 | cat(format(x, ...)) 21 | invisible(x) 22 | } 23 | 24 | #' @rdname get_wikipedia 25 | #' @export 26 | get_wiktionary <- function(name) { 27 | # TODO: handle case when we can't open browser 28 | url <- paste0("https://en.wiktionary.org/wiki/", name) 29 | 30 | structure(url, class = "available_wiktionary") 31 | } 32 | 33 | #' @export 34 | format.available_wiktionary <- function(x, ...) { 35 | browseURL(x[[1]]) 36 | paste0(cli::style_bold("Wiktionary: "), cli::format_inline("{.url {x[[1L]]}}"), "\n") 37 | } 38 | 39 | #' @export 40 | print.available_wiktionary <- function(x, ...) { 41 | cat(format(x)) 42 | invisible(x) 43 | } 44 | 45 | get_abbreviation <- function(name) { 46 | # TODO: handle case when we can't open browser 47 | url <- paste0("https://www.abbreviations.com/", name) 48 | 49 | structure(url, class = "available_abbreviation") 50 | } 51 | 52 | #' @export 53 | print.available_abbreviation <- function(x, ...) { 54 | browseURL(x[[1]]) 55 | cat(cli::style_bold("Abbreviations: "), cli::format_inline("{.url {x[[1L]]}}"), "\n", sep = "") 56 | 57 | invisible(x) 58 | } 59 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: github_document 3 | --- 4 | 5 | 6 | 7 | ```{r, echo = FALSE} 8 | knitr::opts_chunk$set( 9 | collapse = TRUE, 10 | comment = "#>", 11 | fig.path = "README-" 12 | ) 13 | ``` 14 | 15 | 16 | [![CRAN status](https://www.r-pkg.org/badges/version/available)](https://CRAN.R-project.org/package=available) 17 | [![R-CMD-check](https://github.com/r-lib/available/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/r-lib/available/actions/workflows/R-CMD-check.yaml) 18 | [![Codecov test coverage](https://codecov.io/gh/r-lib/available/graph/badge.svg)](https://app.codecov.io/gh/r-lib/available) 19 | 20 | 21 |

22 | xzibit 23 |

24 | \ 25 | 26 | # available 27 | 28 | > available helps you name your R package. 29 | 30 | - Checks for validity 31 | - Checks not already available on GitHub, CRAN and Bioconductor 32 | - Can suggest possible names based on text in the package title or description. 33 | 34 | ## Installation 35 | 36 | You can install available from CRAN with: 37 | 38 | ```{r CRAN-installation, eval = FALSE} 39 | install.packages("available") 40 | ``` 41 | 42 | Or the development version from GitHub with: 43 | 44 | ```{r gh-installation, eval = FALSE} 45 | pak::pak("r-lib/available") 46 | ``` 47 | 48 | ## Examples 49 | 50 |

51 | 52 |

53 | \ 54 | 55 | ## Generate new package names from titles 56 | ```{r} 57 | library(available) 58 | suggest(text = "Client for New York Times APIs") 59 | 60 | suggest(text = "An R Interface to SciDB") 61 | ``` 62 | 63 | ### Rstudio Support 64 | RStudio versions 1.1 and later support color in the terminal. 65 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | [![CRAN 6 | status](https://www.r-pkg.org/badges/version/available)](https://CRAN.R-project.org/package=available) 7 | [![R-CMD-check](https://github.com/r-lib/available/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/r-lib/available/actions/workflows/R-CMD-check.yaml) 8 | [![Codecov test 9 | coverage](https://codecov.io/gh/r-lib/available/graph/badge.svg)](https://app.codecov.io/gh/r-lib/available) 10 | 11 | 12 |

13 | xzibit 14 |

15 | 16 |   17 | 18 | # available 19 | 20 | > available helps you name your R package. 21 | 22 | - Checks for validity 23 | - Checks not already available on GitHub, CRAN and Bioconductor 24 | - Can suggest possible names based on text in the package title or 25 | description. 26 | 27 | ## Installation 28 | 29 | You can install available from CRAN with: 30 | 31 | ``` r 32 | install.packages("available") 33 | ``` 34 | 35 | Or the development version from GitHub with: 36 | 37 | ``` r 38 | pak::pak("r-lib/available") 39 | ``` 40 | 41 | ## Examples 42 | 43 |

44 | 45 |

46 | 47 |   48 | 49 | ## Generate new package names from titles 50 | 51 | ``` r 52 | library(available) 53 | suggest(text = "Client for New York Times APIs") 54 | #> timesr 55 | 56 | suggest(text = "An R Interface to SciDB") 57 | #> scidbr 58 | ``` 59 | 60 | ### Rstudio Support 61 | 62 | RStudio versions 1.1 and later support color in the terminal. 63 | -------------------------------------------------------------------------------- /_pkgdown.yml: -------------------------------------------------------------------------------- 1 | url: https://github.r-lib.io/available 2 | template: 3 | bootstrap: 5 4 | 5 | includes: 6 | in_header: | 7 | 8 | 9 | development: 10 | mode: auto 11 | -------------------------------------------------------------------------------- /available.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: No 4 | SaveWorkspace: No 5 | AlwaysSaveHistory: Default 6 | QuitChildProcessesOnExit: Default 7 | 8 | EnableCodeIndexing: Yes 9 | UseSpacesForTab: Yes 10 | NumSpacesForTab: 2 11 | Encoding: UTF-8 12 | 13 | RnwWeave: Sweave 14 | LaTeX: pdfLaTeX 15 | 16 | AutoAppendNewline: Yes 17 | StripTrailingWhitespace: Yes 18 | 19 | BuildType: Package 20 | PackageUseDevtools: Yes 21 | PackageInstallArgs: --no-multiarch --with-keep.source 22 | PackageRoxygenize: rd,collate,namespace 23 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: false 2 | 3 | coverage: 4 | status: 5 | project: 6 | default: 7 | target: auto 8 | threshold: 1% 9 | informational: true 10 | patch: 11 | default: 12 | target: auto 13 | threshold: 1% 14 | informational: true 15 | -------------------------------------------------------------------------------- /man/available-package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/available-package.R 3 | \docType{package} 4 | \name{available-package} 5 | \alias{available-package} 6 | \title{available: Check if the Title of a Package is Available, Appropriate and Interesting} 7 | \description{ 8 | Check if a given package name is available to use. It checks the name's validity. Checks if it is used on 'GitHub', 'CRAN' and 'Bioconductor'. Checks for unintended meanings by querying 'Wiktionary' and Wikipedia. 9 | } 10 | \seealso{ 11 | Useful links: 12 | \itemize{ 13 | \item \url{https://github.com/r-lib/available} 14 | \item \url{https://r-lib.github.io/available/} 15 | \item Report bugs at \url{https://github.com/r-lib/available/issues} 16 | } 17 | 18 | } 19 | \author{ 20 | \strong{Maintainer}: Gábor Csárdi \email{csardi.gabor@gmail.com} 21 | 22 | Authors: 23 | \itemize{ 24 | \item Carl Ganz 25 | \item Jim Hester 26 | \item Molly Lewis 27 | \item Rachael Tatman 28 | } 29 | 30 | Other contributors: 31 | \itemize{ 32 | \item Posit Software, PBC [copyright holder, funder] 33 | } 34 | 35 | } 36 | \keyword{internal} 37 | -------------------------------------------------------------------------------- /man/available.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/available.R 3 | \name{available} 4 | \alias{available} 5 | \title{See if a name is available} 6 | \usage{ 7 | available(name, browse = getOption("available.browse", TRUE), ...) 8 | } 9 | \arguments{ 10 | \item{name}{Name of package to search} 11 | 12 | \item{browse}{Whether browser should be opened for all web links, 13 | default = TRUE. Default can be changed by setting 14 | \code{available.browse} in \code{.Rprofile}. See \link[base]{Startup} 15 | for more details.} 16 | 17 | \item{...}{Additional arguments passed to \code{\link[utils:available.packages]{utils::available.packages()}}.} 18 | } 19 | \description{ 20 | Searches performed 21 | \itemize{ 22 | \item Valid package name 23 | \item Already taken on CRAN 24 | \item Positive or negative sentiment 25 | } 26 | } 27 | \examples{ 28 | \dontrun{ 29 | # Check if the available package is available 30 | available("available") 31 | 32 | # You can disable opening of browser windows with browse = FALSE 33 | available("survival", browse = FALSE) 34 | 35 | # Or by setting a global option 36 | options(available.browse = FALSE) 37 | available("survival") 38 | 39 | # Test if a name is available in a non-default CRAN repository by setting 40 | # the `repos` argument 41 | available_on_cran("semaforos") 42 | 43 | available_on_cran("semaforos", repos = "https://bisaloo.r-universe.dev") 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /man/available_on_cran.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/bioc.R, R/cran.R 3 | \name{available_on_bioc} 4 | \alias{available_on_bioc} 5 | \alias{available_on_cran} 6 | \title{See if a name is available on CRAN} 7 | \usage{ 8 | available_on_bioc(name, repos = NULL, ...) 9 | 10 | available_on_cran(name, repos = default_cran_repos, ...) 11 | } 12 | \arguments{ 13 | \item{name}{Name of package to search} 14 | 15 | \item{repos}{ 16 | character vector, the base URL(s) of the repositories to use. 17 | } 18 | 19 | \item{...}{Additional arguments passed to \code{\link[utils:available.packages]{utils::available.packages()}}.} 20 | } 21 | \description{ 22 | See if a name is available on CRAN 23 | } 24 | \examples{ 25 | available_on_cran("semaforos") 26 | 27 | # Test if this name is available in a non-default CRAN repository 28 | \dontrun{ 29 | available_on_cran("semaforos", repos = "https://bisaloo.r-universe.dev") 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /man/available_on_github.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/github.R 3 | \name{available_on_github} 4 | \alias{available_on_github} 5 | \title{See if a name is available on github} 6 | \usage{ 7 | available_on_github(name) 8 | } 9 | \arguments{ 10 | \item{name}{Name of package to search} 11 | } 12 | \description{ 13 | See if a name is available on github 14 | } 15 | -------------------------------------------------------------------------------- /man/common_suffixes.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/namr.R 3 | \name{common_suffixes} 4 | \alias{common_suffixes} 5 | \title{function to add common, informative suffixes} 6 | \usage{ 7 | common_suffixes(title, name) 8 | } 9 | \arguments{ 10 | \item{title}{the package title or description} 11 | 12 | \item{name}{the single word that will be appended to} 13 | } 14 | \value{ 15 | a single word with affix, if applicable 16 | } 17 | \description{ 18 | Search a title for common terms (plot, vis..., viz..., markdown) and apply 19 | appropriate affixes to a given word as applicable. 20 | } 21 | \keyword{internal} 22 | -------------------------------------------------------------------------------- /man/create.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/available.R 3 | \name{create} 4 | \alias{create} 5 | \title{Check a new package name and possibly create it} 6 | \usage{ 7 | create(name, ...) 8 | } 9 | \arguments{ 10 | \item{name}{Name of package to search} 11 | 12 | \item{...}{Additional arguments passed to \code{\link[usethis:create_package]{usethis::create_package()}}.} 13 | } 14 | \description{ 15 | Check a new package name and possibly create it 16 | } 17 | -------------------------------------------------------------------------------- /man/find_acronym.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/namr.R 3 | \name{find_acronym} 4 | \alias{find_acronym} 5 | \title{Function that finds and returns the first acronym (all caps) in a text string} 6 | \usage{ 7 | find_acronym(title) 8 | } 9 | \arguments{ 10 | \item{title}{package title or description} 11 | } 12 | \value{ 13 | a single acronym, if present 14 | } 15 | \description{ 16 | Function that finds and returns the first acronym (all caps) in a text string 17 | } 18 | \keyword{internal} 19 | -------------------------------------------------------------------------------- /man/get_bad_words.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/bad_words.R 3 | \name{get_bad_words} 4 | \alias{get_bad_words} 5 | \title{Check for bad words in name} 6 | \usage{ 7 | get_bad_words(name) 8 | } 9 | \arguments{ 10 | \item{name}{Name of package to search} 11 | } 12 | \description{ 13 | Check for bad words in name 14 | } 15 | \seealso{ 16 | See \url{https://github.com/web-mech/badwords} 17 | } 18 | -------------------------------------------------------------------------------- /man/get_sentiment.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/sentiment.R 3 | \name{get_sentiment} 4 | \alias{get_sentiment} 5 | \title{Get sentiment of name} 6 | \usage{ 7 | get_sentiment(name) 8 | } 9 | \arguments{ 10 | \item{name}{Name of package to search} 11 | } 12 | \description{ 13 | Get sentiment of name 14 | } 15 | -------------------------------------------------------------------------------- /man/get_wikipedia.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/wiki.R 3 | \name{get_wikipedia} 4 | \alias{get_wikipedia} 5 | \alias{get_wiktionary} 6 | \title{Open wikipedia page and abbreviations.com page} 7 | \usage{ 8 | get_wikipedia(name) 9 | 10 | get_wiktionary(name) 11 | } 12 | \arguments{ 13 | \item{name}{Name of package to search} 14 | } 15 | \description{ 16 | Open wikipedia page and abbreviations.com page 17 | } 18 | -------------------------------------------------------------------------------- /man/make_spelling_rlike.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/namr.R 3 | \name{make_spelling_rlike} 4 | \alias{make_spelling_rlike} 5 | \title{Spelling transformations} 6 | \usage{ 7 | make_spelling_rlike(word) 8 | } 9 | \arguments{ 10 | \item{word}{a single word to make more rlike} 11 | } 12 | \value{ 13 | a single word with a spelling transformation 14 | } 15 | \description{ 16 | This function takes in a single word and applies spelling transformations to 17 | make it more "r-like" and easier to Google 18 | } 19 | \keyword{internal} 20 | -------------------------------------------------------------------------------- /man/namr.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/namr.R 3 | \name{namr} 4 | \alias{namr} 5 | \title{Suggest package name} 6 | \usage{ 7 | namr(title, acronym = FALSE, verb = FALSE, ...) 8 | } 9 | \arguments{ 10 | \item{title}{the package title or description} 11 | 12 | \item{acronym}{whether to include an acronym (if there is one) in the title} 13 | 14 | \item{verb}{whether to prioritize using a verb in the package title} 15 | } 16 | \value{ 17 | a single word to use as a package title 18 | } 19 | \description{ 20 | Suggests a package name based on the package title or description. 21 | } 22 | \keyword{internal} 23 | -------------------------------------------------------------------------------- /man/pick_word_from_title.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/namr.R 3 | \name{pick_word_from_title} 4 | \alias{pick_word_from_title} 5 | \title{Pick word from title} 6 | \usage{ 7 | pick_word_from_title(title, verb = FALSE) 8 | } 9 | \arguments{ 10 | \item{title}{text string to pick word from. Package title or description.} 11 | 12 | \item{verb}{whether you would like to prioritize returning a verb} 13 | } 14 | \value{ 15 | a single word from the title 16 | } 17 | \description{ 18 | picks a single (hopefully informative) word from the provided title or 19 | package description 20 | } 21 | \keyword{internal} 22 | -------------------------------------------------------------------------------- /man/pkg_name_dist.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/dist.R 3 | \name{pkg_name_dist} 4 | \alias{pkg_name_dist} 5 | \title{Find five closest package names in terms of string distance} 6 | \usage{ 7 | pkg_name_dist(name, pkgs) 8 | } 9 | \arguments{ 10 | \item{name}{name of package} 11 | 12 | \item{pkgs}{packages to compare with} 13 | } 14 | \description{ 15 | Find five closest package names in terms of string distance 16 | } 17 | -------------------------------------------------------------------------------- /man/suggest.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/available.R 3 | \name{suggest} 4 | \alias{suggest} 5 | \title{Suggest a package name based on a development package title or description} 6 | \usage{ 7 | suggest(path = ".", field = c("Title", "Description"), text = NULL) 8 | } 9 | \arguments{ 10 | \item{path}{Path to a existing package to extract the title from.} 11 | 12 | \item{field}{one of "Title" or "Description"} 13 | 14 | \item{text}{text string to search.} 15 | } 16 | \description{ 17 | If the package you are using already has a title, simply pass the path to 18 | the package root in \code{path}. Otherwise use \code{title} to specify a potential 19 | title. 20 | } 21 | \examples{ 22 | \dontrun{ 23 | # Default will use the title from the current path. 24 | suggest() 25 | 26 | # Can also suggest based on the description 27 | suggest(field = "Description") 28 | } 29 | 30 | # Or by explicitly using the text argument 31 | suggest( 32 | text = 33 | "A Package for Displaying Visual Scenes as They May Appear to an Animal with Lower Acuity" 34 | ) 35 | } 36 | -------------------------------------------------------------------------------- /man/valid_package_name.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/valid_name.R 3 | \name{valid_package_name} 4 | \alias{valid_package_name} 5 | \title{Is a package name valid} 6 | \usage{ 7 | valid_package_name(name) 8 | } 9 | \arguments{ 10 | \item{name}{Name of package to search} 11 | } 12 | \description{ 13 | Is a package name valid 14 | } 15 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | # This file is part of the standard setup for testthat. 2 | # It is recommended that you do not modify it. 3 | # 4 | # Where should you do additional test configuration? 5 | # Learn more about the roles of various files in: 6 | # * https://r-pkgs.org/testing-design.html#sec-tests-files-overview 7 | # * https://testthat.r-lib.org/articles/special-files.html 8 | 9 | library(testthat) 10 | library(available) 11 | 12 | test_check("available") 13 | -------------------------------------------------------------------------------- /tests/testthat/test-badwords.R: -------------------------------------------------------------------------------- 1 | test_that("Catches bad word", { 2 | skip_on_cran() 3 | 4 | expect_identical("hell", get_bad_words("hell")[[1L]]) 5 | }) 6 | 7 | test_that("Passes safe word", { 8 | skip_on_cran() 9 | 10 | expect_true(length(get_bad_words("happy")) == 0) 11 | }) 12 | -------------------------------------------------------------------------------- /tests/testthat/test-bioc.R: -------------------------------------------------------------------------------- 1 | test_that("Can find dplyr", { 2 | skip_on_cran() 3 | 4 | expect_false(available_on_bioc("ACME")) 5 | }) 6 | 7 | test_that("Can't find made up package", { 8 | skip_on_cran() 9 | 10 | expect_true(available_on_bioc("This_is_not_a_pkg")) 11 | }) 12 | -------------------------------------------------------------------------------- /tests/testthat/test-cran.R: -------------------------------------------------------------------------------- 1 | test_that("Can find dplyr", { 2 | skip_on_cran() 3 | 4 | expect_false(available_on_cran("dplyr")) 5 | }) 6 | 7 | test_that("Can't find made up package", { 8 | skip_on_cran() 9 | 10 | expect_true(available_on_cran("This_is_not_a_pkg")) 11 | }) 12 | -------------------------------------------------------------------------------- /tests/testthat/test-github.R: -------------------------------------------------------------------------------- 1 | test_that("Can find ", { 2 | skip_on_cran() 3 | 4 | expect_false(available_on_github("svrepmisc")$available) 5 | }) 6 | 7 | test_that("Can't find made up package", { 8 | skip_on_cran() 9 | 10 | expect_true(available_on_github("This_is_not_a_pkg")$available) 11 | }) 12 | -------------------------------------------------------------------------------- /tests/testthat/test-name_to_search_terms.R: -------------------------------------------------------------------------------- 1 | describe("name_to_search_terms", { 2 | it("does not remove trailing R when proceeded by a vowel", { 3 | expect_equal(name_to_search_terms("linear"), "linear") 4 | expect_equal(name_to_search_terms("loder"), "loder") 5 | }) 6 | it("does remove trailing R when not proceeded by a vowel", { 7 | expect_equal(name_to_search_terms("covr"), "cov") 8 | expect_equal(name_to_search_terms("knitr"), "knit") 9 | }) 10 | }) 11 | -------------------------------------------------------------------------------- /tests/testthat/test-namr.R: -------------------------------------------------------------------------------- 1 | describe("pick_word_from_title", { 2 | it("ignores common words", { 3 | expect_equal(pick_word_from_title("wrapper for the intro.js library"), "introjs") 4 | }) 5 | }) 6 | 7 | describe("make_spelling_rlike", { 8 | it("adds r suffixes", { 9 | expect_equal(make_spelling_rlike("tidy"), "tidyr") 10 | }) 11 | it("shortens R prefixes", { 12 | expect_equal(make_spelling_rlike("archive"), "rchive") 13 | }) 14 | it("removes trailing vowels", { 15 | expect_equal(make_spelling_rlike("reader"), "readr") 16 | }) 17 | it("adds leading rs", { 18 | make_spelling_rlike("rinstr") 19 | }) 20 | }) 21 | 22 | describe("common_suffixes", { 23 | it("adds a prefix for common suffixes", { 24 | expect_equal(common_suffixes("package for plotting things", "my"), "myplot") 25 | expect_equal(common_suffixes("visualizer 2000 the reboot", "my"), "myvis") 26 | }) 27 | }) 28 | 29 | describe("namr", { 30 | it("works on real examples", { 31 | expect_equal(namr("A Package for Displaying Visual Scenes as They May Appear to an Animal with Lower Acuity"), "displayingrvis") 32 | expect_equal(namr("Analysis of Ecological Data : Exploratory and Euclidean Methods in Environmental Sciences"), "ecologicalr") 33 | expect_equal(namr("Population Assignment using Genetic, Non-Genetic or Integrated Data in a Machine Learning Framework"), "populationr") 34 | }) 35 | }) 36 | --------------------------------------------------------------------------------