r-lib / ymlthis

write YAML for R Markdown, bookdown, blogdown, and more
https://ymlthis.r-lib.org/
Other
164 stars 10 forks source link

Feature request: piping to assemble documents. #53

Open Maschette opened 4 years ago

Maschette commented 4 years ago

Just an Idea and not sure how feasible it would be.

My suggestion is have another function called body_text() with the idea that you can pipe all your normal YAML like this:

Year<-2019
YAML<-yml() %>% 
    yml_title(title=paste(Year,"report")) %>% 
    yml_output(html_document(), pdf_document()) 

but then have it that you could build the rest of the document by continuing to pipe eg:

report<-YAML %>% 
body_text("#Introduction
           Welcome to the 2019 final report...") %>% 
    code_chunk({plot(x=1:10, y=1:10) },
                 chunk_name = "plot_1",     
                 chunk_args=list(fig.width=10, fig.height=10)) %>% 
    body_text("now that we can see some dots lets write the rest of the report")

use_rmarkdown(Report, path = "report.rmd")

I a just not entirely sure how you would tell it which bit goes into the YAML and which in the code chunk/ body parts of options if I understand how it would work

Maschette commented 4 years ago

I have been playing and upon discovering asis_yaml_output() I almost get something that works with

YAML<-yml() %>% 
    yml_title(title=paste(Year,"report")) %>% 
    yml_output(html_document(), pdf_document()) %>%
        asis_yaml_output()

intro<-"#Introduction \n 
           Welcome to the 2019 final report..."

plot_chunk<- code_chunk({plot(x=1:10, y=1:10) },
                 chunk_name = "plot_1",     
                 chunk_args=list(fig.width=10, fig.height=10))

cat(YAML, intro, plot_chunk, sep= "\n \n", file="report.rmd")

and it seems to work ok, having the ability to pipe it would be cool but I guess not super urgent.

malcolmbarrett commented 4 years ago

Yes, I agree that the existing API is not as fluid as it should be. I think something like you suggest in your first comment is in the right arena, although I need to think about it more. Thank you for the suggestion!

Regarding your second comment, I don't think that is producing what you want. asis_yaml_output() writes a YAML knitr chunk rather than actual metadata for your document, and it does so in a manner that tells knitr to print it exactly. I would also prefer use_rmarkdown() over the cat() approach, as the former is specifically designed to assemble these types of documents.

Maschette commented 4 years ago

Yep i agree, I wasn't sure the best way to capture the YAML out of the list format. I ended up looking at this which works quite well

YAML<-yml() %>% 
    yml_title(title=paste(Year,"Mackeral Icefish Assessment")) %>% 
            yml_output(html_document(), pdf_document())

yaml<-ymlthis:::capture_yml(YAML)
yaml<-glue::glue_collapse(yaml, "\n")

and then using glue_collapse() and use_rmarkdown() to join ad print them all.

eg:

doc<-glue::glue_collapse(c(yaml,intro,plot_chunk), sep = c("\n \n \n"))
use_rmarkdown(doc, path = "yml3.rmd")
Maschette commented 4 years ago

Just as an idea, I am attending the OzUnconf this week and this may be something that people are interested in working on if you are happy for us to explore option?

ethanbass commented 2 years ago

This seems like a really great idea. I wrote a little prototype of how I think it could work using some new functions I wrote to string together the yml object with the different body elements as suggested by @Maschette.

# add text block to markdown body
add_text <- function(x, text){
  if (!class(x) %in% c("character", "markdown_doc", "yml")){
    stop("!")
  }
  if (!inherits(text, "character")){
    stop("!")
  }
  if (inherits(x, "yml")){
    x1 <- list(yml = x, body = text)
    class(x1) <- "markdown_doc"
    x1
  } else if (inherits(x, "character")){
    c(x, text)
  } else if (inherits(x, "markdown_doc")){
    x$body <- c(x$body, text)
    x
  }
}

# add code chunk to markdown body
add_chunk <- function(x, ...){
  if (!class(x) %in% c("character", "markdown_doc", "yml")){
    stop("!")
  }
  if (inherits(x, "yml")){
    x1 <- list(yml = x, body = code_chunk(...))
    class(x1) <- "markdown_doc"
    x1
  } else if (inherits(x, "character")){
    c(x, code_chunk(...))
  } else if (inherits(x, "markdown_doc")){
    x$body <- c(x$body, code_chunk(...))
    x
  }
}

# write yml and body to markdown
write_markdown <- function(x, sep = "\n \n", path, knit = TRUE, ...){
  if (!(class(x) %in% c("markdown_doc", "character"))){
    stop("!")
  }
  if (inherits(x, "markdown_doc")){
    yml <- x$yml
    body <- glue::glue_collapse(x$body, sep = sep)
  } else{
    yml <- yml()
    body <- x
  }
  use_rmarkdown(.yml = yml, body = body, path = path, ...)
  if (knit){
    rmarkdown::render(path)
  }
}

example pipeline to reconstruct something like the Bookdown demo introduction:

# example pipeline
yml <- yml() %>% yml_title("") %>%
  yml_bookdown_site() %>%
  yml_latex_opts(
    documentclass = "book",
    bibliography = c("book.bib", "packages.bib"),
    biblio_style = "apalike"
  ) %>%
  yml_citations(
    link_citations = TRUE
  ) %>%
  yml_description("This is a minimal example of using
  the bookdown package to write a book.")

yml %>%
  add_text("# Introduction {#intro} \n") %>%
  add_chunk(chunk_name = "plot_1",
                    chunk_args=list(fig.width=5, fig.height=5),
                    {
                      par(mar = c(4, 4, .1, .1))
                      plot(pressure, type = 'b', pch = 19)
                    }) %>%
  add_text("Reference a figure by its code chunk label with the `fig:` prefix, e.g., see Figure ref(fig:nice-fig).
           Similarly, you can reference tables generated from `knitr::kable()`, e.g., see Table ref(tab:nice-tab).") %>%
  add_chunk(chunk_name = "nice-tab", chunk_args = list(tidy=FALSE),
            {knitr::kable(
              head(iris, 20), caption = 'Here is a nice table!',
              booktabs = TRUE)}) %>%
  add_text("You can write citations, too. For example, we are using the **bookdown** package [@R-bookdown] in this sample book, which was built on top of R Markdown and **knitr** [@xie2015].") %>%
  add_text("# References") %>%
  write_markdown(path = "Intro.Rmd", overwrite = TRUE, open_doc = FALSE)