Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@
^\.vscode$
^[.]?air[.]toml$
^scratch\.R$
^\.claude$
1 change: 1 addition & 0 deletions .claude/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
settings.local.json
60 changes: 60 additions & 0 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
## R package development

### Key commands

```
# To run code
Rscript -e "devtools::load_all(); code"

# To run all tests
Rscript -e "devtools::test()"

# To run tests for R/{name.R}
Rscript -e "devtools::test(filter = '{name}')"

# To document the package
Rscript -e "devtools::document()"

# To check pkgdown documentation
Rscript -e "pkgdown::check_pkgdown()"

# To format code
air format .
```

### Coding

* Always run `air format .` after generating code
* Use the base pipe operator (`|>`) not the magrittr pipe (`%>%`)
* Don't use `_$x` or `_$[["x"]]` since dbplyr must work on R 4.1.
* Use `\() ...` for single-line anonymous functions. For all other cases, use `function() {...}`

### Testing

- Tests for `R/{name}.R` go in `tests/testthat/test-{name}.R`.
- All new code should have an accompanying test.
- If there are existing tests, place new tests next to similar existing tests.
- Strive to keep your tests minimal with few comments.

### Documentation

- Every user-facing function should be exported and have roxygen2 documentation.
- Wrap roxygen comments at 80 characters.
- Internal functions should not have roxygen documentation.
- Whenever you add a new documentation topic, also add the topic to `_pkgdown.yml`.
- Use `pkgdown::check_pkgdown()` to check that all topics are included in the reference index.

### Writing

- Use sentence case for headings.
- Use US English.

### Proofreading

If the user asks you to proofread a file, act as an expert proofreader and editor with a deep understanding of clear, engaging, and well-structured writing.

Work paragraph by paragraph, always starting by making a TODO list that includes individual items for each top-level heading.

Fix spelling, grammar, and other minor problems without asking the user. Label any unclear, confusing, or ambiguous sentences with a FIXME comment.

Only report what you have changed.
21 changes: 21 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{

"permissions": {
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"defaultMode": "acceptEdits",
"allow": [
"Bash(find:*)",
"Bash(grep:*)",
"Bash(ls:*)",
"Bash(R:*)",
"Bash(Rscript:*)",
"Bash(rm:*)",
"Bash(air:*)",
"WebFetch(domain:github.com)"
],
"deny": [
"Read(.Renviron)",
"Read(.env)"
]
}
}
156 changes: 156 additions & 0 deletions .claude/skills/tidy-deprecate-function/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
---
name: tidy-deprecate-function
description: Guide for deprecating R functions/arguments. Use when a user asks to deprecate a function or parameter, including adding lifecycle warnings, updating documentation, adding NEWS entries, and updating tests.
---

# Deprecate functions and function arguments

Use this skill when deprecating functions or function parameters in dbplyr.

## Overview

This skill guides you through the complete process of deprecating a function or parameter, ensuring all necessary changes are made consistently:

1. Add deprecation warning using `lifecycle::deprecate_warn()`.
2. Silence deprecation warnings in existing tests.
3. Add lifecycle badge to documentation.
4. Add bullet point to NEWS.md.
5. Create test for deprecation warning.

## Workflow

### Step 1: Determine deprecation version

Read the current version from DESCRIPTION and calculate the deprecation version:

- Current version format: `MAJOR.MINOR.PATCH.9000` (development).
- Deprecation version: Next minor release `MAJOR.(MINOR+1).0`.
- Example: If current version is `2.5.1.9000`, deprecation version is `2.6.0`.

### Step 2: Add `lifecycle::deprecate_warn()` call

Add the deprecation warning to the function:

```r
# For a deprecated function:
function_name <- function(...) {
lifecycle::deprecate_warn("X.Y.0", "function_name()", "replacement_function()")
# rest of function
}

# For a deprecated parameter:
function_name <- function(param1, deprecated_param = deprecated()) {
if (lifecycle::is_present(deprecated_param)) {
lifecycle::deprecate_warn("X.Y.0", "function_name(deprecated_param)")
}
# rest of function
}
```

Key points:

- First argument is the deprecation version string (e.g., "2.6.0").
- Second argument describes what is deprecated (e.g., "function_name(param)").
- Optional third argument suggests replacement.
- Use `lifecycle::is_present()` to check if a deprecated parameter was supplied.

### Step 3: Update tests

Find all existing tests that use the deprecated function or parameter and silence lifecycle warnings. Add at the beginning of test blocks that use the deprecated feature:

```r
test_that("existing test with deprecated feature", {
withr::local_options(lifecycle_verbosity = "quiet")

# existing test code
})
```

Then add a new test to verify the deprecation message in the appropriate test file (usually `tests/testthat/test-{name}.R`):

```r
test_that("function_name(deprecated_param) is deprecated", {
expect_snapshot(. <- function_name(deprecated_param = value))
})
```

You'll need to supply any additional arguments to create a valid call.

Then run the tests and verify they pass.

### Step 4: Update documentation

For function deprecation, add to the description section:

```r
#' @description
#' `r lifecycle::badge("deprecated")`
#'
#' This function is deprecated. Please use [replacement_function()] instead.
```

If the documentation does not already contain `@description`, you will need to add it.

For argument deprecation, add to the appropriate `@param` tag:

```r
#' @param deprecated_param `r lifecycle::badge("deprecated")`
```

When deprecating a function or parameter in favor of a replacement, add old/new examples to the `@examples` section to help users migrate. These should relace all existing examples.

```r
#' @examples
#' # Old:
#' old_function(arg1, arg2)
#' # New:
#' replacement_function(arg1, arg2)
#'
#' # Old:
#' x <- "value"
#' old_function("prefix", x, "suffix")
#' # New:
#' replacement_function("prefix {x} suffix")
```

Key points:

- Use "# Old:" and "# New:" comments to clearly show the transition.
- Include 2-3 practical examples covering common use cases.
- Make examples runnable and self-contained.
- Show how the new syntax differs from the old.

Then re-document the package.

### Step 5: Add NEWS entry

Add a bullet point to the top of the "# dbplyr (development version)" section in NEWS.md:

```markdown
# dbplyr (development version)

* `function_name(parameter)` is deprecated and will be removed in a future
version.
* `function_name()` is deprecated. Use `replacement_function()` instead.
```

Place the entry:

- In the lifecycle subsection if it exists, otherwise at the top level under development version.
- Include the replacement if known.
- Keep entries concise and actionable.

## Implementation checklist

When deprecating a function or parameter, ensure you:

- [ ] Read DESCRIPTION to determine deprecation version.
- [ ] Add `lifecycle::deprecate_warn()` call in the function.
- [ ] Add `withr::local_options(lifecycle_verbosity = "quiet")` to existing tests.
- [ ] Create new test for deprecation warning using `expect_snapshot()`.
- [ ] Run tests to verify everything works.
- [ ] Add lifecycle badge to roxygen documentation.
- [ ] Add migration examples to `@examples` section (for function deprecation).
- [ ] Run `devtools::document()` to update documentation.
- [ ] Add bullet point to NEWS.md.
- [ ] Run `air format .` to format code.
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ export(use_ccby_license)
export(use_circleci)
export(use_circleci_badge)
export(use_citation)
export(use_claude_code)
export(use_code_of_conduct)
export(use_conflicted)
export(use_course)
Expand Down
51 changes: 51 additions & 0 deletions R/claude.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#' Configure a project to use Claude Code
#'
#' @description
#' This function sets up a project to use
#' [Claude Code](https://docs.anthropic.com/en/docs/claude-code).
#' Specifically, it:
#'
#' - Creates a `.claude/` directory with a `CLAUDE.md` file containing
#' project-specific instructions for R package development.
#'
#' - Creates a `.claude/settings.json` configuration file with recommended
#' permissions for R package development, including the ability to run R,
#' format with [Air](https://posit-dev.github.io/air/), and run common
#' development tools.
#'
#' - Creates a `.claude/skills/` directory containing various skills found
#' useful by the tidyverse team. All skills have a `tidy-` prefix to avoid
#' clashing with skills that you might provide.
#'
#' - Updates `.claude/.gitignore` to ignore `settings.local.json` (for
#' user-specific settings).
#'
#' @export
#' @examples
#' \dontrun{
#' use_claude_code()
#' }
use_claude_code <- function() {
use_directory(".claude", ignore = TRUE)
copy_claude_directory()
use_git_ignore("settings.local.json", directory = ".claude")

invisible(TRUE)
}

copy_claude_directory <- function() {
source_dir <- path_package("usethis", "claude")
dest_dir <- proj_path(".claude")

source_dirs <- dir_ls(source_dir, recurse = TRUE, type = "directory")
dir_create(path(dest_dir, path_rel(source_dirs, source_dir)))

source_files <- dir_ls(source_dir, recurse = TRUE, type = "file")
for (source_file in source_files) {
rel_path <- path_rel(source_file, source_dir)
dest_file <- path(dest_dir, rel_path)

ui_bullets(c("v" = "Creating {.path {pth(dest_file)}}."))
file_copy(source_file, dest_file, overwrite = TRUE)
}
}
1 change: 1 addition & 0 deletions _pkgdown.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ reference:
- use_standalone
- use_testthat
- use_air
- use_claude_code
- title: Package release
contents:
- use_cran_comments
Expand Down
60 changes: 60 additions & 0 deletions inst/claude/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
## R package development

### Key commands

```
# To run code
Rscript -e "devtools::load_all(); code"

# To run all tests
Rscript -e "devtools::test()"

# To run tests for R/{name.R}
Rscript -e "devtools::test(filter = '{name}')"

# To document the package
Rscript -e "devtools::document()"

# To check pkgdown documentation
Rscript -e "pkgdown::check_pkgdown()"

# To format code
air format .
```

### Coding

* Always run `air format .` after generating code
* Use the base pipe operator (`|>`) not the magrittr pipe (`%>%`)
* Don't use `_$x` or `_$[["x"]]` since dbplyr must work on R 4.1.
* Use `\() ...` for single-line anonymous functions. For all other cases, use `function() {...}`

### Testing

- Tests for `R/{name}.R` go in `tests/testthat/test-{name}.R`.
- All new code should have an accompanying test.
- If there are existing tests, place new tests next to similar existing tests.
- Strive to keep your tests minimal with few comments.

### Documentation

- Every user-facing function should be exported and have roxygen2 documentation.
- Wrap roxygen comments at 80 characters.
- Internal functions should not have roxygen documentation.
- Whenever you add a new documentation topic, also add the topic to `_pkgdown.yml`.
- Use `pkgdown::check_pkgdown()` to check that all topics are included in the reference index.

### Writing

- Use sentence case for headings.
- Use US English.

### Proofreading

If the user asks you to proofread a file, act as an expert proofreader and editor with a deep understanding of clear, engaging, and well-structured writing.

Work paragraph by paragraph, always starting by making a TODO list that includes individual items for each top-level heading.

Fix spelling, grammar, and other minor problems without asking the user. Label any unclear, confusing, or ambiguous sentences with a FIXME comment.

Only report what you have changed.
21 changes: 21 additions & 0 deletions inst/claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{

"permissions": {
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"defaultMode": "acceptEdits",
"allow": [
"Bash(find:*)",
"Bash(grep:*)",
"Bash(ls:*)",
"Bash(R:*)",
"Bash(Rscript:*)",
"Bash(rm:*)",
"Bash(air:*)",
"WebFetch(domain:github.com)"
],
"deny": [
"Read(.Renviron)",
"Read(.env)"
]
}
}
Loading
Loading