27  Code Style

Published

2024-11-22

Caution

The contents for section are being revised. Thank you for your patience.

I’ve created the shinypak R package In an effort to make each section accessible and easy to follow:

Install shinypak using pak (or remotes):

# install.packages('pak')
pak::pak('mjfrigaard/shinypak')

Review the chapters in each section:

library(shinypak)
list_apps(regex = '^21')
## # A tibble: 1 × 2
##   branch   last_updated       
##   <chr>    <dttm>             
## 1 21_rhino 2024-08-24 05:20:05

Launch an app with launch()

launch(app = "21_style")

Download an app with get_app()

get_app(app = "21_style")

We’re in the business of keystrokes and neurons

During development, it can be challenging to keep the code in your app-package clean and perfectly formatted. Fortunately, the R ecosystem has some excellent tools for making your code functional and easy to read.

27.1 Code style and formatting

The lintr and styler packages in R serve related but distinct purposes and have different focuses in their functionality. The primary difference between lintr and styler is that styler can automatically fix any stylistic issues it identifies in your code (rather than just reporting them).

Launch app with the shinypak package:

launch('21_style')

27.1.1 lintr

lintr is a static code analysis tool used to identify syntax errors, semantic issues, and violations of stylistic guidelines in your code. The package contains a list of ‘linters’ for various potential problems and can be customized according to your needs. lintr is designed to help improve your code’s quality and readability by generating reports in the ‘markers’ pane. Running lintr won’t automatically correct the identified issues (you’ll need to fix the linting issues it reports manually).

27.1.2 styler

On the other hand, the purpose of styler is to ensure consistency in the code formatting, which is crucial if you’re working in a team or contributing to open-source projects (like tidyverse packages). The styler package will change your code’s format according to specified style guidelines. These changes include indentation, spaces, and line breaks that adhere to your style guidelines.

While there is some overlap (both packages can help enforce coding style guidelines), lintr is a more general tool for code quality, spotting potential issues and bugs. At the same time, styler focuses on code formatting and can automatically apply fixes. Many developers find combining both can help catch potential issues and ensure a consistent, readable coding style.

27.2 Checking your code

I’ve previously mentioned running devtools::check() can be overkill for your app-package (especially if it’s not destined for CRAN). A nice alternative to check() is the goodpractice package..

goodpractice::gp() inspects your package and prints any areas that might need ‘good practice’ advice:

library(goodpractice)
pkg_checks <- gp(path = ".")
pkg_checks
Preparing: description
Preparing: lintr
  |====================================================================| 100%
Preparing: namespace
Preparing: rcmdcheck
── GP sap ───────────────────────────────────────────────────────

It is good practice to

  ✖ add a "URL" field to DESCRIPTION. It helps users find information about your
    package online. If your package does not have a homepage, add an URL to 
    GitHub, or the CRAN package package page.
  ✖ add a "BugReports" field to DESCRIPTION, and point it to a bug tracker. 
    Many online code hosting services provide bug trackers for free, 
    https://github.com, https://gitlab.com, etc.
  ✖ avoid long code lines, it is bad for readability. Also, many people prefer 
    editor windows that are about 80 characters wide. Try make your lines 
    shorter than 80 characters

    data-raw/tidy_movies.R:49:81
    R/data.R:4:81
    R/data.R:7:81
    R/data.R:17:81
    R/data.R:21:81
    ... and 13 more lines

  ✖ not import packages as a whole, as this can cause name clashes between the 
    imported packages. Instead, import only the specific functions you need.
  ✖ fix this R CMD check NOTE: display_type: no visible binding for global 
    variable
    ‘.rs.invokeShinyPaneViewer’ display_type: no visible binding for global 
    variable
    ‘.rs.invokeShinyWindowExternal’ display_type: no visible binding for global
    variable
    ‘.rs.invokeShinyWindowViewer’ mod_scatter_display_server : <anonymous>: no 
    visible binding for global
    variable ‘movies’ Undefined global functions or variables: 
    .rs.invokeShinyPaneViewer 
    .rs.invokeShinyWindowExternal 
    .rs.invokeShinyWindowViewer 
    movies

We can also check specific components of our package by looking up the available checks in all_checks():

grep("import", x = all_checks(), value = TRUE)
[1] "no_import_package_as_a_whole"                 
[2] "rcmdcheck_undeclared_imports"                 
[3] "rcmdcheck_imports_not_imported_from"          
[4] "rcmdcheck_depends_not_imported_from"          
[5] "rcmdcheck_triple_colon_imported_objects_exist"
[6] "rcmdcheck_unexported_base_objects_imported"   
[7] "rcmdcheck_unexported_objects_imported"        
[8] "rcmdcheck_empty_importfrom_in_namespace"  

All of the checks with the rcmdcheck_ prefix are part of the R CMD check diagnostic, but goodpractice comes with other checks that are good practices (even if you’re not submitting your package to CRAN).

For example, no_import_package_as_a_whole checks the practice we covered in managing imports. If we pass the no_import_package_as_a_whole check as a character vector to the checks argument:

gp(path = ".", checks = 'no_import_package_as_a_whole')

Only this check is performed:

── GP sap ───────────────────────────────────────────────────

It is good practice to

   not import packages as a whole, as this can cause name clashes between
    the imported packages. Instead, import only the specific functions you need.

───────────────────────────────────────────────────────────────────

27.3 Recap

This chapter covered an introduction to some tools and practices for improving and maintaining the quality of the code in your app-package. Maintaining code style and standards (lintr and styler) and performing thorough checks to adhere to best practices (goodpractice) will ensure efficient and reliable development and deployment for your app.

Code tools recap

This chapter covered:

Code Linting and Styling:

  • lintr is used for checking the code for potential errors and style issues

    • Can help in identifying syntactical and stylistic problems that might lead to code inefficiency or errors

    • Linters are instrumental in enforcing coding standards and ensuring consistency across the codebase

  • styler is used to automatically style the R code in your app-package

    • styler will format R code according to specified style guidelines, ensuring that the code is not only consistent in its look but also adheres to best practices

    • Automating styling can save time and reduces manual effort in code formatting

Managing Dependencies:

  • The attachment package helps in managing these dependencies by automatically listing and updating the packages used in your app-package

    • att_amend_desc scans the code and identifies all package dependencies

    • managing dependencies ensures reproducibility and ease of package installation

  • desc assists in handling the DESCRIPTION file in your app-package

    • provides tools for reading, writing, and modifying the DESCRIPTION files

Code Checks:

  • goodpractice is designed to help ensure best practices in R coding and package development are adhered to in your app-package

    • goodpractice::gp() performs a comprehensive analysis of the code (beyond devtools::check()) and checks for various potential issues like code complexity, redundancy, adherence to coding standards, etc

    • Offers suggestions for improvements, making it a helpful tool for quality assurance in package development