Skip to content

Commit 2a75a94

Browse files
authored
refactor(r/sedonafns): Ensure generated function names are snake case (#933)
1 parent f92c953 commit 2a75a94

2 files changed

Lines changed: 72 additions & 7 deletions

File tree

r/sedonafns/tests/testthat/test-sedonafns-package.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ test_that("basic usage with sedonadb integration works", {
1919
skip_if_not_installed("sedonadb")
2020

2121
df_out <- data.frame(x = 1, y = 2) |>
22-
sedonadb::sd_transmute(x, y, geom = sd_point(x, y) |> sd_astext())
22+
sedonadb::sd_transmute(x, y, geom = sd_point(x, y) |> sd_as_text())
2323

2424
expect_identical(
2525
as.data.frame(df_out),

r/sedonafns/tools/update-sd-funcs.R

Lines changed: 71 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,34 @@ type_to_param <- list(
3939
boolean = "b"
4040
)
4141

42+
#' Convert a CamelCase function name to snake_case with appropriate prefix
43+
#'
44+
#' @param title The function title (e.g., "ST_AsEWKB", "RS_Width")
45+
#' @returns Character snake_case name with appropriate prefix (e.g., "sd_as_ewkb", "rs_width")
46+
camel_to_snake <- function(title) {
47+
# Determine prefix based on original function type
48+
if (grepl("^RS_", title)) {
49+
prefix <- "rs_"
50+
} else {
51+
prefix <- "sd_"
52+
}
53+
54+
# Remove ST_, RS_, or S2_ prefix
55+
name <- sub("^(ST|RS|S2)_", "", title)
56+
57+
# Insert underscore before uppercase letters that follow lowercase letters
58+
# e.g., "AsEWKB" -> "As_EWKB"
59+
name <- gsub("([a-z])([A-Z])", "\\1_\\2", name)
60+
61+
# Insert underscore before uppercase letters followed by lowercase
62+
# (to handle acronyms like EWKB before lowercase)
63+
# e.g., "EWKBTest" -> "EWKB_Test"
64+
name <- gsub("([A-Z]+)([A-Z][a-z])", "\\1_\\2", name)
65+
66+
# Convert to lowercase and add appropriate prefix
67+
paste0(prefix, tolower(name))
68+
}
69+
4270
# Apache license header
4371
license_header <- "# Licensed to the Apache Software Foundation (ASF) under one
4472
# or more contributor license agreements. See the NOTICE file
@@ -425,7 +453,9 @@ generate_translation <- function(sd_name, fn_name, args, variadic = FALSE) {
425453
#' @param file_hash MD5 hash of source .qmd file
426454
#' @returns Character string with complete R file content
427455
generate_r_file <- function(fn_name, frontmatter, description, file_hash) {
428-
sd_name <- sub("^st_", "sd_", fn_name)
456+
# Use title from frontmatter to get proper snake_case naming
457+
title <- frontmatter$title %||% fn_name
458+
sd_name <- camel_to_snake(title)
429459
kernel_info <- parse_kernel_params(frontmatter$kernels, fn_name)
430460
title <- frontmatter$description %||% frontmatter$title
431461

@@ -472,7 +502,15 @@ generate_r_file <- function(fn_name, frontmatter, description, file_hash) {
472502
#' @returns List with status ("generated", "skipped", "failed") and error message if failed
473503
generate_from_qmd <- function(qmd_path, force = FALSE) {
474504
fn_name <- tools::file_path_sans_ext(basename(qmd_path))
475-
sd_name <- sub("^st_", "sd_", fn_name)
505+
506+
# Parse frontmatter early to get the proper sd_name from title
507+
frontmatter <- tryCatch(
508+
extract_frontmatter(qmd_path),
509+
510+
error = function(e) list(title = fn_name)
511+
)
512+
title <- frontmatter$title %||% fn_name
513+
sd_name <- camel_to_snake(title)
476514
output_path <- file.path(output_dir, paste0(sd_name, ".R"))
477515

478516
# Compute hash
@@ -493,7 +531,10 @@ generate_from_qmd <- function(qmd_path, force = FALSE) {
493531
{
494532
message("Generating ", sd_name, ".R from ", fn_name, ".qmd")
495533

496-
frontmatter <- extract_frontmatter(qmd_path)
534+
# Re-parse frontmatter if initial parse failed (used fallback)
535+
if (is.null(frontmatter$kernels)) {
536+
frontmatter <- extract_frontmatter(qmd_path)
537+
}
497538
description <- extract_description_section(qmd_path)
498539

499540
content <- generate_r_file(fn_name, frontmatter, description, file_hash)
@@ -516,7 +557,33 @@ generate_from_qmd <- function(qmd_path, force = FALSE) {
516557
#' @returns Invisible NULL
517558
update_sd_funcs <- function(files = NULL, force = TRUE) {
518559
if (is.null(files)) {
519-
qmd_files <- list.files(docs_dir, pattern = "^(st|rs)_.*\\.qmd$", full.names = TRUE)
560+
qmd_files <- list.files(
561+
docs_dir,
562+
pattern = "^(st|rs|s2)_.*\\.qmd$",
563+
full.names = TRUE
564+
)
565+
566+
# Clear old generated files when regenerating all
567+
message("Clearing old generated files...")
568+
569+
# Remove R/sd_*.R and R/rs_*.R files
570+
old_sd_files <- list.files(output_dir, pattern = "^sd_.*\\.R$", full.names = TRUE)
571+
old_rs_files <- list.files(output_dir, pattern = "^rs_.*\\.R$", full.names = TRUE)
572+
old_files <- c(old_sd_files, old_rs_files)
573+
if (length(old_files) > 0) {
574+
file.remove(old_files)
575+
message(" Removed ", length(old_files), " old R files")
576+
}
577+
578+
# Clear man/ directory
579+
man_dir <- here::here("man")
580+
if (dir.exists(man_dir)) {
581+
man_files <- list.files(man_dir, full.names = TRUE)
582+
if (length(man_files) > 0) {
583+
file.remove(man_files)
584+
message(" Removed ", length(man_files), " old man files")
585+
}
586+
}
520587
} else {
521588
qmd_files <- file.path(docs_dir, paste0(files, ".qmd"))
522589
missing <- !file.exists(qmd_files)
@@ -545,6 +612,4 @@ update_sd_funcs <- function(files = NULL, force = TRUE) {
545612
message(" - ", f$fn_name, ": ", f$error)
546613
}
547614
}
548-
549-
invisible(NULL)
550615
}

0 commit comments

Comments
 (0)