Skip to content

Commit

Permalink
Add min_version to use_package()
Browse files Browse the repository at this point in the history
Includes refactoring internal use_dependency() to use it instead of more general dependency specification.

Fixes #398. Fixes #484. Fixes #498.
  • Loading branch information
hadley committed Nov 27, 2018
1 parent 7407192 commit b9b373c
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 46 deletions.
5 changes: 5 additions & 0 deletions NEWS.md
@@ -1,5 +1,10 @@
# usethis (development version)

* `use_package()` gains a `min_version` argument so you can specify a minimum
required version (#498). Set to `TRUE` to use the currently installed
version (#386). This is used by `use_tidy()` in order to require 0.1.2
or greater (#484).

* `code_block()` now strips ascii escapes before copying code to clipboard
(#447).

Expand Down
59 changes: 31 additions & 28 deletions R/helpers.R
Expand Up @@ -19,7 +19,7 @@ use_description_field <- function(name,
invisible()
}

use_dependency <- function(package, type, version = "*") {
use_dependency <- function(package, type, min_version = NULL) {
stopifnot(is_string(package))
stopifnot(is_string(type))

Expand All @@ -30,6 +30,11 @@ use_dependency <- function(package, type, version = "*") {
)
}

if (isTRUE(min_version)) {
min_version <- utils::packageVersion(package)
}
version <- if (is.null(min_version)) "*" else paste0(">= ", min_version)

types <- c("Depends", "Imports", "Suggests", "Enhances", "LinkingTo")
names(types) <- tolower(types)
type <- types[[match.arg(tolower(type), names(types))]]
Expand All @@ -38,55 +43,53 @@ use_dependency <- function(package, type, version = "*") {

existing_dep <- deps$package == package
existing_type <- deps$type[existing_dep]
existing_ver <- deps$version[existing_dep]
is_linking_to <- (existing_type != "LinkingTo" && type == "LinkingTo") ||
(existing_type == "LinkingTo" && type != "LinkingTo")

if (
!any(existing_dep) ||
(existing_type != "LinkingTo" && type == "LinkingTo")
) {
# No existing dependecy, so can simply add
if (!any(existing_dep) || is_linking_to) {
done("Adding {value(package)} to {field(type)} field in DESCRIPTION")
desc::desc_set_dep(package, type, version = version, file = proj_get())
return(invisible())
}

## no downgrades
if (match(existing_type, types) < match(type, types)) {
delta <- sign(match(existing_type, types) - match(type, types))
if (delta < 0) {
# don't downgrade
warning_glue(
"Package {value(package)} is already listed in ",
"{value(existing_type)} in DESCRIPTION, no change made."
)
return(invisible())
}

if (match(existing_type, types) > match(type, types)) {
if (existing_type != "LinkingTo") {
## prepare for an upgrade
} else if (delta == 0 && !is.null(min_version)) {
# change version
upgrade <- existing_ver == "*" || numeric_version(min_version) > version_spec(existing_ver)
if (upgrade) {
done(
"Removing {value(package)} from {field(existing_type)}",
" field in DESCRIPTION"
"Increasing {value(package)} version to {value(version)} in DESCRIPTION"
)
desc::desc_del_dep(package, existing_type, file = proj_get())
desc::desc_set_dep(package, type, version = version, file = proj_get())
}
} else {
## maybe change version?
to_version <- any(existing_dep & deps$version != version)
if (to_version) {
} else if (delta > 0) {
# upgrade
if (existing_type != "LinkingTo") {
done(
"Setting {value(package)} version to {value(version)} in DESCRIPTION"
"Moving {value(package)} from {field(existing_type)} to {field(type)}",
" field in DESCRIPTION"
)
desc::desc_del_dep(package, existing_type, file = proj_get())
desc::desc_set_dep(package, type, version = version, file = proj_get())
}
return(invisible())
}

done(
"Adding {value(package)} to {field(type)} field in DESCRIPTION",
if (version != "*") ", with version {value(version)}" else ""
)
desc::desc_set_dep(package, type, version = version, file = proj_get())

invisible()
}

version_spec <- function(x) {
x <- gsub("(<=|<|>=|>|==)\\s*", "", x)
numeric_version(x)
}

view_url <- function(..., open = interactive()) {
url <- paste(..., sep = "/")
if (open) {
Expand Down
8 changes: 5 additions & 3 deletions R/package.R
Expand Up @@ -9,6 +9,8 @@
#' @param type Type of dependency: must be one of "Imports", "Depends",
#' "Suggests", "Enhances", or "LinkingTo" (or unique abbreviation). Matching
#' is case insensitive.
#' @param min_version Optionally, supply a minimum version for the package.
#' Set to `TRUE` to use the currently installed version.
#' @seealso The [dependencies
#' section](http://r-pkgs.had.co.nz/description.html#dependencies) of [R
#' Packages](http://r-pkgs.had.co.nz).
Expand All @@ -19,12 +21,12 @@
#' use_package("dplyr", "suggests")
#' use_dev_package("glue")
#' }
use_package <- function(package, type = "Imports") {
use_package <- function(package, type = "Imports", min_version = NULL) {
types <- tolower(c("Imports", "Depends", "Suggests", "Enhances", "LinkingTo"))
type <- match.arg(tolower(type), types)
refuse_package(package, verboten = "tidyverse")

use_dependency(package, type)
use_dependency(package, type, min_version = min_version)

switch(tolower(type),
imports = todo("Refer to functions with {code(package, '::fun()')}"),
Expand Down Expand Up @@ -70,7 +72,7 @@ use_dev_package <- function(package, type = "Imports") {
}

package_remote <- package_remote(package)
use_dependency(package, type = type, version = dep_version(package))
use_dependency(package, type = type, min_version = TRUE)
remotes <- desc::desc_get_remotes(proj_get())
if (package_remote %in% remotes) {
return(invisible())
Expand Down
4 changes: 2 additions & 2 deletions R/tidyverse.R
Expand Up @@ -70,7 +70,7 @@ use_tidy_ci <- function(browse = interactive()) {
)
use_template("codecov.yml", ignore = TRUE)

use_dependency("R", "Depends", ">= 3.1")
use_dependency("R", "Depends", min_version = "3.1")
use_dependency("covr", "Suggests")
use_covr_ignore(c("R/deprec-*.R", "R/compat-*.R"))

Expand Down Expand Up @@ -126,7 +126,7 @@ use_tidy_eval <- function() {
check_is_package("use_tidy_eval()")

use_dependency("roxygen2", "Suggests")
use_dependency("rlang", "Imports", ">= 0.1.2")
use_dependency("rlang", "Imports", min_version = "0.1.2")
new <- use_template("tidy-eval.R", "R/utils-tidy-eval.R")

todo("Run {code('devtools::document()')}")
Expand Down
5 changes: 4 additions & 1 deletion man/use_package.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

51 changes: 39 additions & 12 deletions tests/testthat/test-use-dependency.R
@@ -1,9 +1,8 @@
context("use_dependency")

test_that("we message for new type and are silent for same type", {
scoped_temporary_package()

withr::local_options(list(usethis.quiet = FALSE))
scoped_temporary_package()

expect_output(
use_dependency("crayon", "Imports"),
Expand All @@ -17,45 +16,73 @@ test_that("we message for new type and are silent for same type", {
})

test_that("we message for version change and are silent for same version", {
scoped_temporary_package()

withr::local_options(list(usethis.quiet = FALSE))
scoped_temporary_package()

expect_output(use_dependency("crayon", "Imports"))
expect_output(
use_dependency("crayon", "Imports"),
"Adding 'crayon"
)

expect_output(
use_dependency("crayon", "Imports", "> 1.0"),
"Setting 'crayon'"
use_dependency("crayon", "Imports", min_version = "1.0.0"),
"Increasing 'crayon'"
)

expect_output(
use_dependency("crayon", "Imports", "> 1.0"),
use_dependency("crayon", "Imports", min_version = "1.0.0"),
NA
)

expect_output(
use_dependency("crayon", "Imports", min_version = "2.0.0"),
"Increasing 'crayon'"
)

expect_output(
use_dependency("crayon", "Imports", min_version = "1.0.0"),
NA
)
})

## https://github.com/r-lib/usethis/issues/99
test_that("use_dependency() upgrades a dependency", {
withr::local_options(list(usethis.quiet = FALSE))
scoped_temporary_package()

use_dependency("usethis", "Suggests")
expect_output(use_dependency("usethis", "Suggests"))
expect_match(desc::desc_get("Suggests", proj_get()), "usethis")

withr::local_options(list(usethis.quiet = FALSE))
expect_output(use_dependency("usethis", "Imports"), "Removing.*Adding")
expect_output(use_dependency("usethis", "Imports"), "Moving 'usethis'")
expect_match(desc::desc_get("Imports", proj_get()), "usethis")
expect_false(grepl("usethis", desc::desc_get("Suggests", proj_get())))
})

## https://github.com/r-lib/usethis/issues/99
test_that("use_dependency() declines to downgrade a dependency", {
withr::local_options(list(usethis.quiet = FALSE))
scoped_temporary_package()

use_dependency("usethis", "Imports")
expect_output(use_dependency("usethis", "Imports"))
expect_match(desc::desc_get("Imports", proj_get()), "usethis")

expect_warning(use_dependency("usethis", "Suggests"), "no change")
expect_match(desc::desc_get("Imports", proj_get()), "usethis")
expect_false(grepl("usethis", desc::desc_get("Suggests", proj_get())))
})

test_that("can add LinkingTo dependency if other dependency already exists", {
withr::local_options(list(usethis.quiet = FALSE))
scoped_temporary_package()

expect_output(use_dependency("Rcpp", "Imports"))
expect_output(use_dependency("Rcpp", "LinkingTo"), "Adding 'Rcpp'")
})

test_that("can add any dependency if LinkingTo dependency already exists", {
withr::local_options(list(usethis.quiet = FALSE))
scoped_temporary_package()

expect_output(use_dependency("Rcpp", "LinkingTo"))
expect_output(use_dependency("Rcpp", "Import"), "Adding 'Rcpp'")
})

0 comments on commit b9b373c

Please sign in to comment.