Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

render_site with Rmd parameters #903

Open
zeehio opened this issue Dec 4, 2016 · 10 comments
Open

render_site with Rmd parameters #903

zeehio opened this issue Dec 4, 2016 · 10 comments
Labels
theme: parametrized report related to parametes in Rmarkdown theme: site generator Related to website feature

Comments

@zeehio
Copy link
Contributor

zeehio commented Dec 4, 2016

I started to use the rmarkdown::render_site function. At some point I had an Rmd file with parameters, and I wanted to render it several times with different options.

To do that, I adapted a custom site generator. I would like to know if you are interested in a pull request or at least export some functions so I don't need to use :::.

render_site with rmarkdown parameters

I wrote a custom site generator named site_with_params based on default_site. The main changes with respect to the default_site are:

  • The yaml header of index.Rmd needs to have site: site_with_params to use this site generator.
  • An additional files section to _site.yml is required, that states how html files need to be rendered. An example of _site.yml is shown here (note the "files" section):
name: "my-website"
navbar:
  title: "My Website"
  left:
    - text: "Home"
      href: index.html
    - text: "Domestic results"
       href: domestic.html
    - text: "Foreign results"
      href: foreign.html
files:
  index.html:
    src: index.Rmd
  domestic.html:
    src: domestic_foreign.Rmd
    params:
      type_of_flight: "domestic"
  foreign.html:
    src: domestic_foreign.Rmd
    params:
      type_of_flight: "foreign"

Questions

  • Would you would be interested in a pull request offering this "site with parameters" feature?
  • In case you are not interested, would it be possible to export rmarkdown:::site_config, rmarkdown:::input_as_dir, rmarkdown:::knitr_files_dir, rmarkdown:::dir_exists, rmarkdown:::copy_site_resources and rmarkdown:::knitr_root_cache_dir so it is easy to provide custom site generators in other packages?

Actual code

The code I wrote is adapted from default_site and uses several non-exported rmarkdown functions.

This could be submitted as a PR if there is interest:

#' Generate site with Rmd files with parameters
#' To use this site generator set \code{site: rmarkdown::site_with_params}
#' in the yaml header of the \code{index.Rmd} file.
#' Then in the \code{_site.yml} file, add a files section:
#'
#' \code{
#' files:
#'   index.html:
#'     src: index.Rmd
#'     params:
#'       opt1: Val1
#'   about.html:
#'     src: about.Rmd
#'     params:
#'       opt1: Val2
#' }
#'
#' @param input
#'
#' @export
site_with_params <- function(input, encoding = getOption("encoding"), ...) {

  # get the site config
  config <- rmarkdown:::site_config(input, encoding)
  if (is.null(config))
    stop("No site configuration (_site.yml) file found.")

  input_dir <- rmarkdown:::input_as_dir(input)
  output_file_options <- config[["files"]]
  # config[["files"]] has:
  # files:
  #   index.html:
  #     src: index.Rmd
  #     params:
  #       opt1: Val1
  #   about.html:
  #     src: about.Rmd
  #     params:
  #       opt1: Val2

  # define render function (use ... to gracefully handle future args)
  render <- function(input_file,
                     output_format,
                     envir,
                     quiet,
                     encoding, ...) {

    # track outputs
    outputs <- c()

    # see if this is an incremental render
    incremental <- !is.null(input_file)


    if (incremental) {
      all_input_files <- vapply(output_file_options, function(x) x[["src"]], character(1))
      output_files <- names(output_file_options[all_input_files %in% input_file])
    } else {
      output_files <- names(output_file_options)
    }
    sapply(output_files, function(x) {
      # we suppress messages so that "Output created" isn't emitted
      # (which could result in RStudio previewing the wrong file)
      output <- suppressMessages(
        rmarkdown::render(file.path(input_dir, output_file_options[[x]][["src"]]),
                          output_format = output_format,
                          output_file = x,
                          params = output_file_options[[x]]$params,
                          output_options = list(lib_dir = "site_libs",
                                                self_contained = FALSE),
                          envir = envir,
                          quiet = quiet,
                          encoding = encoding)
      )

      # add to global list of outputs
      outputs <<- c(outputs, output)

      # check for files dir and add that as well
      sidecar_files_dir <- rmarkdown:::knitr_files_dir(output)
      files_dir_info <- file.info(sidecar_files_dir)
      if (isTRUE(files_dir_info$isdir))
        outputs <<- c(outputs, sidecar_files_dir)
    })

    # do we have a relative output directory? if so then remove,
    # recreate, and copy outputs to it (we don't however remove
    # it for incremental builds)
    if (config$output_dir != '.') {

      # remove and recreate output dir if necessary
      output_dir <- file.path(input, config$output_dir)
      if (file.exists(output_dir)) {
        if (!incremental) {
          unlink(output_dir, recursive = TRUE)
          dir.create(output_dir)
        }
      } else {
        dir.create(output_dir)
      }

      # move outputs
      for (output in outputs) {

        # don't move it if it's a _files dir that has a _cache dir
        if (grepl("^.*_files$", output)) {
          cache_dir <- gsub("_files$", "_cache", output)
          if (rmarkdown:::dir_exists(cache_dir))
            next;
        }

        output_dest <- file.path(output_dir, basename(output))
        if (rmarkdown:::dir_exists(output_dest))
          unlink(output_dest, recursive = TRUE)
        file.rename(output, output_dest)
      }

      # copy lib dir a directory at a time (allows it to work with incremental)
      lib_dir <- file.path(input, "site_libs")
      output_lib_dir <- file.path(output_dir, "site_libs")
      if (!file.exists(output_lib_dir))
        dir.create(output_lib_dir)
      libs <- list.files(lib_dir)
      for (lib in libs)
        file.copy(file.path(lib_dir, lib), output_lib_dir, recursive = TRUE)
      unlink(lib_dir, recursive = TRUE)

      # copy other files
      rmarkdown:::copy_site_resources(input, encoding)
    }

    # Print output created for rstudio preview
    if (!quiet) {
      # determine output file
      output_file <- ifelse(is.null(input_file),
                            "index.html",
                            output_files)
      if (config$output_dir != ".")
        output_file <- file.path(config$output_dir, output_file)
      message("\nOutput created: ", output_file)
    }
  }

  # define clean function
  clean <- function() {

    # build list of generated files
    generated <- c()


    # get html files
    html_files <- names(config[["files"]])

    # _files peers are always removed (they could be here due to
    # output_dir == "." or due to a _cache existing for the page)
    html_supporting <- paste0(rmarkdown:::knitr_files_dir(html_files), '/')
    generated <- c(generated, html_supporting)

    # _cache peers are always removed
    html_cache <- paste0(rmarkdown:::knitr_root_cache_dir(html_files), '/')
    generated <- c(generated, html_cache)

    # for rendering in the current directory we need to eliminate
    # output files for our inputs (including _files) and the lib dir
    if (config$output_dir == ".") {

      # .html peers
      generated <- c(generated, html_files)

      # site_libs dir
      generated <- c(generated, "site_libs/")

      # for an explicit output_dir just remove the directory
    } else {
      generated <- c(generated, paste0(config$output_dir, '/'))
    }

    # filter out by existence
    generated[file.exists(file.path(input, generated))]
  }

  # return site generator
  list(
    name = config$name,
    output_dir = config$output_dir,
    render = render,
    clean = clean
  )
}
@jjallaire
Copy link
Member

What if we just added support for a params argument for the render_site function? We could furthermore allow this parameter to be the path to a YAML file.

@zeehio
Copy link
Contributor Author

zeehio commented Dec 4, 2016

That would be great!

My only concern is that the same .Rmd file may be used as the input file for several output html files (with different parameters) and the params argument should be able to accept that use case.

Looking at the render function in the default_site, when it wants to do an "incremental" rendering, it is based on the input_file. This is fine if there is a single output file for each input file, but it gets a bit confusing in the case where we have a single Rmd file generating several html files.

To me it would make more sense to say "render this output file" and then it would render it using the corresponding Rmd input file and parameters... but maybe I am missing something or overcomplicating things...

Thanks for the quick reply on a Sunday... I wasn't expecting that 😮 👍

@jjallaire
Copy link
Member

jjallaire commented Dec 4, 2016 via email

zeehio added a commit to zeehio/rmarkdown that referenced this issue Dec 4, 2016
render_site understands the `output_files` field in the `_site.yml` file to
describe which source file should an html file use and give Rmd parameters
if needed. (closes rstudio#903)

The output_files feature can be used as well to set as source for an html file
a R script that will be spinned.

This commit also adds a `autospin` field in `_site.yml` that, if set to `true`
will render all the `R` scripts spinning them first (closes rstudio#892)
@zeehio
Copy link
Contributor Author

zeehio commented Dec 4, 2016

I thought it would be better to do a PR with the changes and if necessary do further discussion there.

We were lucky and I also fixed #892 with almost zero extra effort. Looking forward to the next release! 😃

@jjallaire
Copy link
Member

@zeehio I'm going to take this PR back up after the next CRAN release (2 or 3 weeks out).

@zeehio
Copy link
Contributor Author

zeehio commented Feb 1, 2017

@jjallaire this is just a kind reminder of the issue, given your previous comment of taking the PR back up after the 1.3 release 👍

@yihui yihui added this to the v1.8 milestone Oct 16, 2017
@yihui
Copy link
Member

yihui commented Oct 16, 2017

@zeehio Sorry for leaving this issue aside for so long. Bad news is we probably still don't have the bandwidth to review your PR. I have currently scheduled it for v1.8, the next version after 1.7 (no ETA yet; probably early next year). I'd appreciate it if you could resolve the GIT conflicts before then.

@zeehio
Copy link
Contributor Author

zeehio commented Oct 17, 2017

@yihui, I fully understand the bandwidth limitations. Like you, I am a bit full until mid January, but I will do my best to find the time to resolve conflicts. I appreciate a lot your kind reply, keep up doing your great work!

@yihui yihui modified the milestones: v1.8, v1.9 Nov 15, 2017
@yihui yihui modified the milestones: v1.9, v1.10 Mar 4, 2018
@yihui yihui modified the milestones: v1.10, v1.11 Jun 15, 2018
@yihui yihui removed this from the v1.11 milestone Jun 25, 2018
zeehio added a commit to zeehio/rmarkdown that referenced this issue Apr 4, 2019
render_site understands the `output_files` field in the `_site.yml` file to
describe which source file should an html file use and give Rmd parameters
if needed. (closes rstudio#903)

The output_files feature can be used as well to set as source for an html file
a R script that will be spinned.

This commit also adds a `autospin` field in `_site.yml` that, if set to `true`
will render all the `R` scripts spinning them first (closes rstudio#892)
zeehio added a commit to zeehio/rmarkdown that referenced this issue Apr 4, 2019
render_site understands the `output_files` field in the `_site.yml` file to
describe which source file should an html file use and give Rmd parameters
if needed. (closes rstudio#903)

The output_files feature can be used as well to set as source for an html file
a R script that will be spinned.

This commit also adds a `autospin` field in `_site.yml` that, if set to `true`
will render all the `R` scripts spinning them first (closes rstudio#892)
@jhelvy
Copy link

jhelvy commented Dec 30, 2021

Just came across this - any chance this was ever integrated? I could certainly use this functionality in some course sites I'm building.

@zeehio
Copy link
Contributor Author

zeehio commented Dec 30, 2021

As far as I know, the option of passing parameters to Rmd files through render_site() was not implemented.

I believe my pull request had some room for improvement and I discarded it. I think there was some interest by rmarkdown maintainers to keep render_site() functionality simple (or at least not more complex) and someone suggested that for such functionality maybe another package would be a better suit.

I have no idea and I don't need this functionality anymore, but I would check other packages (blogdown maybe?)

@cderv cderv added theme: site generator Related to website feature theme: parametrized report related to parametes in Rmarkdown labels Jan 10, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
theme: parametrized report related to parametes in Rmarkdown theme: site generator Related to website feature
Projects
None yet
Development

No branches or pull requests

5 participants