Basics

Consider this simple R script:




birth_date_to_current_age <- function(birth_date) {
  stopifnot(inherits(birth_date, "Date"))

  # @codedoc_comment_block current_age
  # current_age values are formed in function birth_date_to_current_age by
  # counting the number of days between today and the birth day.
  # @codedoc_comment_block current_age

  current_age_in_days <- as.numeric(Sys.Date() - birth_date)

  # @codedoc_comment_block current_age
  # the days are transformed into fractional years by dividing by 365.242199.
  # @codedoc_comment_block current_age

  current_age_in_days / 365.242199
}


age_group_breaks <- function() {
  c(0, 20, 40, 65, 80, Inf)
}
age_to_age_group <- function(age) {

  # comment blocks can be nested
  # @codedoc_comment_block age_group_definitions
  #
  # **below is taken from age_group comment block**
  #
  # @codedoc_comment_block age_group
  # age_group values are formed in function **age_to_age_group**. The function
  # creates 5 age groups from age values by splitting
  # the age values at breaks `{0, 20, 40, 60, 80, Inf}`
  # @codedoc_comment_block age_group
  # @codedoc_comment_block age_group_definitions

  cut(age, breaks = age_group_breaks(), labels = FALSE, right = FALSE)
}


age_to_pension_age_indicator <- function(age) {

  # comment blocks can be nested
  # @codedoc_comment_block age_group_definitions
  #
  # **below is taken from pension_age_indicator comment block**
  #
  # @codedoc_comment_block pension_age_indicator
  # pension_age_indicator is created in function age_to_pension_age_indicator
  # by detecting whether age at least `r pension_age()`.
  # ```{r, results = "asis"}
  # # I wrote this here only for demonstration purposes.
  # knitr::kable(data.frame(
  #   pension_age_indicator = c(FALSE, TRUE),
  #   text = c("Is too young to be pensioner", "Is of pension age")
  # ))
  # ```
  # @codedoc_comment_block pension_age_indicator
  # @codedoc_comment_block age_group_definitions

  age >= pension_age()
}

pension_age <- function() {
  # @codedoc_comment_block pension_age
  # the pension age is set to `r pension_age()` in function pension_age().
  # @codedoc_comment_block pension_age
  65L
}

age_group_to_pension_age_indicator <- function(age_groups) {

  # you can insert other comment blocks into another
  #
  # @codedoc_comment_block age_group_to_pension_age_indicator
  #
  # function age_group_to_pension_age_indicator() converts age groups into
  # boolean indicators of whether pension
  # age has been reached. age groups are assumed to be defined using
  # age_to_age_group(), where age groups are defined as follows:
  #
  # @codedoc_insert_comment_block age_group_definitions
  #
  # the current pension age is retrieved using pension_age():
  #
  # @codedoc_insert_comment_block pension_age
  #
  # @codedoc_comment_block age_group_to_pension_age_indicator

  age_group_breaks()[age_groups] >= pension_age()
}

Note the comment blocks starting with @codedoc_comment_block current_age and @codedoc_comment_block age_group. These specify comment blocks that this package can identify as code documentation for the purpose of compiling a single aggregated document (html, pdf, etc. using rmarkdown::render).

Any text after the @codedoc_comment_block identifier is called the key of the comment block. Keys are used to identify comment blocks when compiling the documentation. There must be an even number of lines with @codedoc_comment_block my_key for any key. Otherwise an error is raised.

You can extract the comment blocks based on this information using codedoc::extract_keyed_comment_blocks. Here we refer to an example text file stored into this package.

block_df <- codedoc::extract_keyed_comment_blocks(
  text_file_paths = codedoc::example_text_file_path("r_script.R")
)
print(block_df$key)
## [1] "current_age"                        "current_age"                       
## [3] "age_group_definitions"              "age_group"                         
## [5] "age_group_definitions"              "pension_age_indicator"             
## [7] "pension_age"                        "age_group_to_pension_age_indicator"
print(block_df$comment_block)
## [[1]]
## [1] "current_age values are formed in function birth_date_to_current_age by"
## [2] "counting the number of days between today and the birth day."          
## 
## [[2]]
## [1] "the days are transformed into fractional years by dividing by 365.242199."
## 
## [[3]]
## [1] ""                                                                          
## [2] "**below is taken from age_group comment block**"                           
## [3] ""                                                                          
## [4] "age_group values are formed in function **age_to_age_group**. The function"
## [5] "creates 5 age groups from age values by splitting"                         
## [6] "the age values at breaks `{0, 20, 40, 60, 80, Inf}`"                       
## 
## [[4]]
## [1] "age_group values are formed in function **age_to_age_group**. The function"
## [2] "creates 5 age groups from age values by splitting"                         
## [3] "the age values at breaks `{0, 20, 40, 60, 80, Inf}`"                       
## 
## [[5]]
##  [1] ""                                                                         
##  [2] "**below is taken from pension_age_indicator comment block**"              
##  [3] ""                                                                         
##  [4] "pension_age_indicator is created in function age_to_pension_age_indicator"
##  [5] "by detecting whether age at least `r pension_age()`."                     
##  [6] "```{r, results = \"asis\"}"                                               
##  [7] "# I wrote this here only for demonstration purposes."                     
##  [8] "knitr::kable(data.frame("                                                 
##  [9] "  pension_age_indicator = c(FALSE, TRUE),"                                
## [10] "  text = c(\"Is too young to be pensioner\", \"Is of pension age\")"      
## [11] "))"                                                                       
## [12] "```"                                                                      
## 
## [[6]]
## [1] "pension_age_indicator is created in function age_to_pension_age_indicator"
## [2] "by detecting whether age at least `r pension_age()`."                     
## [3] "```{r, results = \"asis\"}"                                               
## [4] "# I wrote this here only for demonstration purposes."                     
## [5] "knitr::kable(data.frame("                                                 
## [6] "  pension_age_indicator = c(FALSE, TRUE),"                                
## [7] "  text = c(\"Is too young to be pensioner\", \"Is of pension age\")"      
## [8] "))"                                                                       
## [9] "```"                                                                      
## 
## [[7]]
## [1] "the pension age is set to `r pension_age()` in function pension_age()."
## 
## [[8]]
##  [1] ""                                                                          
##  [2] "function age_group_to_pension_age_indicator() converts age groups into"    
##  [3] "boolean indicators of whether pension"                                     
##  [4] "age has been reached. age groups are assumed to be defined using"          
##  [5] "age_to_age_group(), where age groups are defined as follows:"              
##  [6] ""                                                                          
##  [7] ""                                                                          
##  [8] "**below is taken from age_group comment block**"                           
##  [9] ""                                                                          
## [10] "age_group values are formed in function **age_to_age_group**. The function"
## [11] "creates 5 age groups from age values by splitting"                         
## [12] "the age values at breaks `{0, 20, 40, 60, 80, Inf}`"                       
## [13] ""                                                                          
## [14] "**below is taken from pension_age_indicator comment block**"               
## [15] ""                                                                          
## [16] "pension_age_indicator is created in function age_to_pension_age_indicator" 
## [17] "by detecting whether age at least `r pension_age()`."                      
## [18] "```{r, results = \"asis\"}"                                                
## [19] "# I wrote this here only for demonstration purposes."                      
## [20] "knitr::kable(data.frame("                                                  
## [21] "  pension_age_indicator = c(FALSE, TRUE),"                                 
## [22] "  text = c(\"Is too young to be pensioner\", \"Is of pension age\")"       
## [23] "))"                                                                        
## [24] "```"                                                                       
## [25] ""                                                                          
## [26] "the current pension age is retrieved using pension_age():"                 
## [27] ""                                                                          
## [28] "the pension age is set to `r pension_age()` in function pension_age()."    
## [29] ""

You would usually not use this result directly, but rather render a documentation file using it. codedoc::render_codedoc is designed to do just that. You may use a template .rmd to define the structure of your document, and it’s contents will be taken from block_df (hence originally from your code). Here’s an example template:

---
title: "Documentation for processing ages"
---

# Example of manually created sections using functions

## Age

`r codedoc_text("current_age")`


## Age group

`r codedoc_text("age_group")`

# Example of manually created sections using keys

## Age

@codedoc_lines current_age

## Age group

@codedoc_lines age_group

## Pension age

@codedoc_lines pension_age_indicator

# Example of generated sections

```{r, results = "asis", echo = TRUE}
invisible(lapply(unique(block_df[["key"]]), function(key) {
  
  cat(c(
    "",
    paste0("## ", key),
    "",
    codedoc_lines(key),
    ""
  ), sep = "\n")
  
}))
Let's now render the document using the above template and the `block_df` 
we created.


``` r
# we use a temp file just for the purpose of creating this vignette. in your
# case that probably won't be necessary.
tmp_dir_path <- tempdir()
tmp_md_file_path <- tempfile(fileext = ".md")
template_file_path <- codedoc::example_template_file_path("r_script_template.rmd")
fun_env <- new.env()
source(codedoc::example_text_file_path("r_script.R"), local = fun_env)
codedoc::render_codedoc(
  block_df = block_df, 
  template_file_path = template_file_path,
  render_arg_list = list(output_dir = tmp_dir_path,
                         output_file = tmp_md_file_path,
                         envir = fun_env)
)

This is what the rendered .md looks like:

## # Example of manually created sections using functions
## 
## ## Age
## 
## current\_age values are formed in function birth\_date\_to\_current\_age
## by counting the number of days between today and the birth day. the days
## are transformed into fractional years by dividing by 365.242199.
## 
## ## Age group
## 
## age\_group values are formed in function **age\_to\_age\_group**. The
## function creates 5 age groups from age values by splitting the age
## values at breaks `{0, 20, 40, 60, 80, Inf}`
## 
## # Example of manually created sections using keys
## 
## ## Age
## 
## current\_age values are formed in function birth\_date\_to\_current\_age
## by counting the number of days between today and the birth day.
## 
## the days are transformed into fractional years by dividing by
## 365.242199.
## 
## ## Age group
## 
## age\_group values are formed in function **age\_to\_age\_group**. The
## function creates 5 age groups from age values by splitting the age
## values at breaks `{0, 20, 40, 60, 80, Inf}`
## 
## ## Pension age
## 
## pension\_age\_indicator is created in function
## age\_to\_pension\_age\_indicator by detecting whether age at least 65.
## 
##     # I wrote this here only for demonstration purposes.
##     knitr::kable(data.frame(
##       pension_age_indicator = c(FALSE, TRUE),
##       text = c("Is too young to be pensioner", "Is of pension age")
##     ))
## 
## <table>
## <thead>
## <tr>
## <th style="text-align: left;">pension_age_indicator</th>
## <th style="text-align: left;">text</th>
## </tr>
## </thead>
## <tbody>
## <tr>
## <td style="text-align: left;">FALSE</td>
## <td style="text-align: left;">Is too young to be pensioner</td>
## </tr>
## <tr>
## <td style="text-align: left;">TRUE</td>
## <td style="text-align: left;">Is of pension age</td>
## </tr>
## </tbody>
## </table>
## 
## # Example of generated sections
## 
##     invisible(lapply(unique(block_df[["key"]]), function(key) {
##       
##       cat(c(
##         "",
##         paste0("## ", key),
##         "",
##         codedoc_lines(key),
##         ""
##       ), sep = "\n")
##       
##     }))
## 
## ## current\_age
## 
## current\_age values are formed in function birth\_date\_to\_current\_age
## by counting the number of days between today and the birth day.
## 
## the days are transformed into fractional years by dividing by
## 365.242199.
## 
## ## age\_group\_definitions
## 
## **below is taken from age\_group comment block**
## 
## age\_group values are formed in function **age\_to\_age\_group**. The
## function creates 5 age groups from age values by splitting the age
## values at breaks `{0, 20, 40, 60, 80, Inf}`
## 
## **below is taken from pension\_age\_indicator comment block**
## 
## pension\_age\_indicator is created in function
## age\_to\_pension\_age\_indicator by detecting whether age at least
## `r pension_age()`.
## `{r, results = "asis"} # I wrote this here only for demonstration purposes. knitr::kable(data.frame(   pension_age_indicator = c(FALSE, TRUE),   text = c("Is too young to be pensioner", "Is of pension age") ))`
## 
## ## age\_group
## 
## age\_group values are formed in function **age\_to\_age\_group**. The
## function creates 5 age groups from age values by splitting the age
## values at breaks `{0, 20, 40, 60, 80, Inf}`
## 
## ## pension\_age\_indicator
## 
## pension\_age\_indicator is created in function
## age\_to\_pension\_age\_indicator by detecting whether age at least
## `r pension_age()`.
## `{r, results = "asis"} # I wrote this here only for demonstration purposes. knitr::kable(data.frame(   pension_age_indicator = c(FALSE, TRUE),   text = c("Is too young to be pensioner", "Is of pension age") ))`
## 
## ## pension\_age
## 
## the pension age is set to `r pension_age()` in function pension\_age().
## 
## ## age\_group\_to\_pension\_age\_indicator
## 
## function age\_group\_to\_pension\_age\_indicator() converts age groups
## into boolean indicators of whether pension age has been reached. age
## groups are assumed to be defined using age\_to\_age\_group(), where age
## groups are defined as follows:
## 
## **below is taken from age\_group comment block**
## 
## age\_group values are formed in function **age\_to\_age\_group**. The
## function creates 5 age groups from age values by splitting the age
## values at breaks `{0, 20, 40, 60, 80, Inf}`
## 
## **below is taken from pension\_age\_indicator comment block**
## 
## pension\_age\_indicator is created in function
## age\_to\_pension\_age\_indicator by detecting whether age at least
## `r pension_age()`.
## `{r, results = "asis"} # I wrote this here only for demonstration purposes. knitr::kable(data.frame(   pension_age_indicator = c(FALSE, TRUE),   text = c("Is too young to be pensioner", "Is of pension age") ))`
## 
## the current pension age is retrieved using pension\_age():
## 
## the pension age is set to `r pension_age()` in function pension\_age().

As you can see, it was populated by the contents of block_df.

This concludes the basics of using this package. Happy documenting!