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"
## [[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!