Mejores paquetes

Qué es buen código

La única forma de escribir buen código es escribir primero toneladas de código de mierda. Sentir vergüenza por el código malo te impide llegar al código bueno.

Traducción de una entrada en Mastodon de Hadley Wickham

Taller de hoy

Presentaré una colección de cosas muy útiles que he aprendido en los últimos años.

Después de cada sección haré un resumen y te pediré que comentes.

Después, elige una cosa para mejorar en tu paquete.

Interfaz

Mensajes agradables

Conoce al paquete cli

variable <- 42
cli::cli_alert_info("Set {.field parameter} to {.val {variable}}")
#>  Set parameter to 42

Viñeta para migrar de las funciones usethis::ui a cli

Viñeta sobre “clases” de formatos

Bonitos mensajes

¿Cómo controlar la verbosidad?

cli_alert_info <- function(...) {
  if (!getOption("usethis.quiet", default = FALSE)) {
    cli::cli_alert_info(...)
  }
}

Bonitos mensajes

Para leer más: https://ropensci.org/blog/2024/02/06/verbosity-control-packages/

🧰 ¿Hay mensajes en tu paquete que podrías mejorar?

Mensajes de error

cli::cli_abort(
  c(
    "Can't find good error message.",
    i = "Read the tidyverse style guide."
  )
)
#> Error:
#> ! Can't find good error message.
#>  Read the tidyverse style guide.

Mensajes de error

🧰 Revisa los mensajes de error de tu paquete (busca stop() y equivalentes). ¿Podrían mejorarse algunos de ellos aplicando la guía de la tidyverse?

Tal vez con AI: https://simonpcouch.github.io/chores/reference/cli_helper.html

Comprobación de argumentos

  • Documenta el tipo de argumento.

  • Comprueba los argumentos. rlang::arg_match() por ejemplo.

Más información: Comprobar las entradas de tus funciones en R por Hugo Gruson, Sam Abbott, Carl Pearson.

Comprobación de argumentos

🧰 ¿Tu paquete documenta y valida los argumentos? Mejora esto en una sola función o más.

Interfaz
🎤 stop() 🎤

  • Bonitos mensajes con {cli}.
  • Mensajes de error con {cli}, guía de tidyverse.
  • Comprobación de argumentos con {rlang}, entrada del blog R-hub.

Por favor, escribi en el chat

  • ¡Algo que te haya parecido interesante!
  • ¡Algo con lo que no estabas de acuerdo!
  • ¿Una experiencia buena/mala?

Menos código o menos dolores de cabeza

Sopesa tus dependencias

¿Esta dependencia provoca alegría? 😉

  • Una dependencia es un código que otra persona ha elaborado y probado cuidadosamente.
  • Una dependencia es un punto de fallo.

Más información: Dependencias: Mentalidad y antecedentes en el libro R Packages de Hadley Wickham y Jenny Bryan.

Sopesa tus dependencias

En la Guía de desarrollo de rOpenSci

  • curl, httr2, crul, httr. No RCurl. Para un nuevo paquete, httr2 en vez de httr.

  • jsonlite. No rjson ni RJSONIO.

  • xml2. No XML

  • sf, suites espaciales desarrolladas por las comunidades r-spatial y rspatial. No sp, rgdal, maptools, rgeos.

Sopesa tus dependencias

Evaluar la actividad en GitHub de los colaboradores

https://ropensci.org/es/blog/2022/07/01/evaluating-github-activity-for-contributors/

Sopesa tus dependencias

🧰 ¿Hay dependencias que podrías añadir, sustituir o eliminar en tu paquete?

¿Menos código? Más allá del uso de dependencias

Feature creep: “ampliación o adición excesiva y continua de nuevas funciones en un producto” https://en.wikipedia.org/wiki/Feature_creep

Está bien dividir el paquete.

Está bien decir no a las peticiones de funciones. Ejemplo

Menos código

🧰 ¿Hay peticiones de funciones a las que te gustaría decir que no? Guardar respuesta como Respuesta GitHub?

Menos código
🎤 stop() 🎤

  • Elegir dependencias.
  • Dependencias a evitar.
  • Definir el ámbito del paquete.

Por favor, escribi en el chat

  • ¡Algo que te haya parecido interesante!
  • ¡Algo con lo que no estabas de acuerdo!
  • ¿Una experiencia buena/mala?

Código de tests

HUMEDO / SECO

“DAMP (descriptive and meaningful phrases)”

“DRY (don’t repeat yourself)”

¡Un intercambio!

Código de tests: diferente de otro código

El código está cubierto por el código de tests, ¡así que podemos asumir más riesgos!

Tests ideales

Ejemplos

{saperlipopette} https://github.com/ropensci-training/saperlipopette/blob/main/tests/testthat/test-exo-blame.R

{babelquarto} https://github.com/ropensci-review-tools/babelquarto/blob/main/tests/testthat/test-render.R

🧰 ¿Algunas de tus tests tienen código de nivel superior? ¿Puedes crear archivos y funciones de ayuda, y repetir la creación de objetos en cada prueba?

Mocking

Mi código

is_internet_down <- function() {
  !curl::has_internet()
}

my_complicated_code <- function() {
  if (is_internet_down()) {
    message("No internet! Le sigh")
  }
  # blablablabla
}

¿Cómo comprobar el mensaje?

Mocking

En el test,

test_that("my_complicated_code() notes the absence of internet", {
  local_mocked_bindings(is_internet_down = function(...) TRUE)
  expect_message(my_complicated_code(), "No internet")
})

Mocking

Para leer más: https://www.tidyverse.org/blog/2023/10/testthat-3-2-0/#mocking

y https://testthat.r-lib.org/dev/articles/challenging-tests.html

Mocking

🧰 ¿tienes una situación de este tipo para probar?

Tests
🎤 stop() 🎤

  • DAMP/DRY
  • Código de prueba diferente de código
  • Pruebas ideales (autónomas, pueden ejecutarse interactivamente, sin fugas)
  • Mocking

Por favor, publica en el chat

  • ¡Algo que te haya parecido interesante!
  • ¡Algo con lo que no estabas de acuerdo!
  • ¿Una experiencia buena/mala?

Cada cosa en su sitio

Secretos

Llaves de API, etc.

.Renviron {keyring}!

{keyring}

# Usuari@, 1 vez para cada ordenador
keyring::key_set("deepl")

# Usuari@, cada vez que usa tu paquete
Sys.setenv(DEEPL_API_KEY = keyring::key_get("deepl"))

# Tu paquete
Sys.getenv("DEEPL_API_KEY")

Secretos

🧰 ¿Podés aconsejar keyring en la documentación de tu paquete?

https://blog.r-hub.io/2024/02/28/key-advantages-of-using-keyring/

Datos

tools::R_user_dir()

¿Cómo buscar ejemplos?

https://github.com/search/advanced

tools::R_user_dir()

Ejemplo 1 https://github.com/matt-dray/tamRgo + https://www.rostrum.blog/posts/2022-11-13-tamrgo/

Ejemplo 2 https://github.com/search?q=repo%3Aropensci%2Fcomtradr%20R_user_dir&type=code

🧰 ¿Tiene tu paquete algo que guardar entre las sesiones?

Resultados repetidos

En la misma sesión, las mismas computaciones… caching?!

{memoise}

.a <- function() {
  Sys.sleep(5)
  "Hola"
}
a <- memoise::memoise(.a)
a()
a()

Resultados repetidos

https://blog.r-hub.io/2021/07/30/cache/

🧰 ¿Podría usar caching tu paquete?

Cada cosa en su sitio
🎤 stop() 🎤

  • {keyring} para secretos
  • Carpetas de usuarias (user directories)
  • Caching

Por favor, publica en el chat

  • ¡Algo que te haya parecido interesante!
  • ¡Algo con lo que no estabas de acuerdo!
  • ¿Una experiencia buena/mala?

Elige tu propia aventura

…¡con tu propio paquete! En salas de Zoom.

Nos reuniremos en XX minutos como grupo para debatir.

De vuelta de la aventura

¿Comentarios? ¿Preguntas?

Muchas gracias.

Nos vemos en el #package-maintenance canal? 😉