diff --git a/.claude/.gitignore b/.claude/.gitignore new file mode 100644 index 000000000..93c0f73fa --- /dev/null +++ b/.claude/.gitignore @@ -0,0 +1 @@ +settings.local.json diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md index 32006a401..2e4fbaa6e 100644 --- a/.claude/CLAUDE.md +++ b/.claude/CLAUDE.md @@ -1,99 +1,60 @@ -# CLAUDE.md +## R package development -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. +### Key commands -## About This Project +``` +# To run code +Rscript -e "devtools::load_all(); code" -testthat is R's most popular unit testing framework, used by thousands of CRAN packages. It provides functions to make testing R code as fun and addictive as possible, with clear expectations, visual progress indicators, and seamless integration with R package development workflows. +# To run all tests +Rscript -e "devtools::test()" -## Key development commands +# 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 -General advice: -* When running R from the console, always run it with `--quiet --vanilla` * 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 -- Use `devtools::test()` to run all tests -- Use `devtools::test_file("tests/testthat/test-filename.R")` to run tests in a specific file -- DO NOT USE `devtools::test_active_file()` -- All testing functions automatically load code; you don't needs to. - -- All new code should have an accompanying test. - 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 -- Run `devtools::document()` after changing any roxygen2 docs. -- Every user facing function should be exported and have roxygen2 documentation. -- Whenever you add a new documentation file, make sure to also add the topic name to `_pkgdown.yml`. -- Run `pkgdown::check_pkgdown()` to check that all topics are included in the reference index. -- Use sentence case for all headings - -## Core Architecture - -### Main Components - -1. **Core Testing Functions** (`R/test-that.R`, `R/test-package.R`): - - `test_that()` - The fundamental testing function - - `test_local()`, `test_package()`, `test_check()` - Different ways to run test suites - -2. **Expectations** (`R/expect-*.R`): - - Modular expectation functions (equality, conditions, types, etc.) - - Each expectation type has its own file following the pattern `expect-[type].R` - -3. **Reporters** (`R/reporter*.R`): - - Different output formats for test results - - Object-oriented design with base `Reporter` class - - Includes check, debug, progress, summary, JUnit, TAP formats - -4. **Snapshot Testing** (`R/snapshot*.R`): - - Value snapshots, file snapshots, output snapshots - - Automatic management and comparison of expected outputs - -5. **Parallel Testing** (`R/parallel*.R`): - - Multi-core test execution - - Configuration via `Config/testthat/parallel: true` in DESCRIPTION - -6. **Mocking** (`R/mock*.R`, `R/mock2*.R`): - - Function mocking capabilities - - Both legacy (`mock.R`) and modern (`mock2*.R`) implementations - -### Key Design Patterns - -- **Editions**: testthat has different "editions" with varying behavior, controlled by `Config/testthat/edition` -- **Reporters**: Extensible reporting system using R6 classes -- **Lazy Evaluation**: Expectations use substitute() and lazy evaluation for better error messages -- **C++ Integration**: Core functionality implemented in C++ for performance - -### File Organization - -- `R/` - All R source code, organized by functionality -- `src/` - C++ source code and Makevars -- `inst/include/testthat/` - C++ headers for other packages to use -- `tests/testthat/` - Package's own comprehensive test suite -- `vignettes/` - Documentation on testing concepts and workflows +- 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. -### Important Configuration +### Writing -The package uses several DESCRIPTION fields for configuration: -- `Config/testthat/edition: 3` - Sets testthat edition -- `Config/testthat/parallel: true` - Enables parallel testing -- `Config/testthat/start-first` - Tests to run first in parallel mode +- Use sentence case for headings. +- Use US English. -### C++ Testing Infrastructure +### Proofreading -testthat provides C++ testing capabilities via Catch framework: -- Headers in `inst/include/testthat/` -- Test runner infrastructure in `src/test-runner.cpp` -- Integration with R's testing system +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. -### Snapshot Testing Workflow +Work paragraph by paragraph, always starting by making a TODO list that includes individual items for each top-level heading. -- Snapshots stored in `tests/testthat/_snaps/` -- Different snapshot types: values, files, output -- Version-specific snapshots for different R versions -- Use `testthat::snapshot_accept()` to update snapshots +Fix spelling, grammar, and other minor problems without asking the user. Label any unclear, confusing, or ambiguous sentences with a FIXME comment. -This codebase prioritizes backward compatibility, comprehensive testing, and clear, descriptive error messages to help R developers write better tests. +Only report what you have changed. diff --git a/.claude/settings.json b/.claude/settings.json index cf2f0abb8..e1f575192 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -1,13 +1,21 @@ { - "$schema": "https://json.schemastore.org/claude-code-settings.json", + "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 format:*)", - "Edit(**)", + "Bash(air:*)", + "WebFetch(domain:github.com)" ], - "deny": [] + "deny": [ + "Read(.Renviron)", + "Read(.env)" + ] } } diff --git a/.claude/skills/tidy-deprecate-function/SKILL.md b/.claude/skills/tidy-deprecate-function/SKILL.md new file mode 100644 index 000000000..a9427f5ca --- /dev/null +++ b/.claude/skills/tidy-deprecate-function/SKILL.md @@ -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. diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index bb7e4c2d5..1a9098c91 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -53,7 +53,7 @@ jobs: actions: read # Optional: Specify model (defaults to Claude Sonnet 4, uncomment for Claude Opus 4.1) - # model: "claude-opus-4-1-20250805" + model: "claude-opus-4-5-20251101" # Optional: Customize the trigger phrase (default: @claude) # trigger_phrase: "/claude"