Posting straight from Rmd to DEV.TO (for real this time)

Table of Contents

I’ve spent a little time fleshing out my open source R package to post from .Rmd straight to dev.to.

Update from V1

The biggest difference from the first version is that there is now a single function to move straight from an .Rmd on disk to a post on dev.to. In V1 the user still had to:

  1. Write an .Rmd
  2. knit to github_document
  3. post_new_article using my dev.to.ol package
  4. De-duplicate the title
  5. (Optionally) Add meta-data

With this new function the workflow is:

  1. Write an .Rmd
  2. post_new_article using my dev.to.ol package

🎉

#' @title Post a markdown file to dev.to
#' @description Create a new post from an .Rmd.
#' @param file The path to the file
#' @param key Your API key, Default: NA
#' @return The response
#' @details Will look for an api key in the `.REnviron` file. Seems to check if the body is identical to a previous article and error if so with `"Body markdown has already been taken"`.
#' The following YAML arguments are read from the file YAML frontmatter if present:
#' \describe{
#'   \item{title}{A character string}
#'   \item{series}{A character string}
#'   \item{published}{A boolean}
#'   \item{tags}{list of character strings: \code{["tag1", "tag2"]}}
#' }
#'
#' The default table output method renders a very large print code block.
#' The workaround is to use  \code{\link[knitr]{kable}}.
#'
#' @examples
#' \dontrun{
#' if(interactive()){
#'  post_new_article("./articles/my_article.Rmd")
#'  }
#' }
#' @seealso
#'  \code{\link[rmarkdown]{yaml_front_matter}},\code{\link[rmarkdown]{render}}
#'  \code{\link[readr]{read_file}}
#'  \code{\link[stringr]{str_remove}}
#'  \code{\link[glue]{glue}}
#'  \code{\link[httr]{POST}},\code{\link[httr]{add_headers}},\code{\link[httr]{content}}
#' @rdname post_new_article
#' @export
#' @importFrom rmarkdown yaml_front_matter render
#' @importFrom readr read_file
#' @importFrom stringr str_remove
#' @importFrom glue glue
#' @importFrom httr POST add_headers content

post_new_article <-
  function(file,
           key = NA) {
    check_file <- is_postable_Rmd(file)

    if (check_file) {
      file_frontmatter <- rmarkdown::yaml_front_matter(file)

      output_path <- rmarkdown::render('./data/test.Rmd',
                                       output_format = 'github_document',
                                       output_dir = getwd())

      file_string <- readr::read_file(output_path) %\u003e%
        stringr::str_remove(glue::glue("{title}\n================\n\n\n",
                                       title = file_frontmatter$title))

      response <- httr::POST(
        url = "https://dev.to/api/articles",
        httr::add_headers("api-key" = api_key(key = key)),
        body = list(
          article = list(
            title = file_frontmatter$title,
            series = file_frontmatter$series,
            published = file_frontmatter$published,
            tags = file_frontmatter$tags,
            body_markdown = file_string
          )
        ),
        encode = 'json'
      )
      httr::content(response)
    } else {
      message(attr(check_file, "msg"))
    }
  }

Notable changes

Extract YAML frontmatter and render from .Rmd with rmarkdown

The function will now accept an .Rmd file directly which it renders to the correct markdown output internally. This was the biggest part missing from V1, and was easy to do with rmarkdown::render. A great side-effect is that I can also access the YAML frontmatter of the .Rmd, which I can use to populate the meta-data such as the series the post is in, the tags the post is relevant to, and the published status.

### Check file is suitable with assertthat

Another benefit is that there is now a concrete object I can check for correctness. I’ve adopted the assertthat package to help me, and most of the work was done in this commit. The workhorse function is is_postable_Rmd:

is_postable_Rmd <- function(file) {
    assertthat::see_if(
    assertthat::is.readable(file),
    assertthat::has_extension(file, "Rmd")
    )
}

Using assertthat::see_if was really useful. It allowed me to check for multiple conditions, and if they were both passed, it returns TRUE. However, if they did not pass, it returns FALSE as well as a message attribute. This meant that I could very trivially plumb that back into the main function for user feedback as to what is the problem with the line message(attr(check_file, "msg")). Additionally, as I add criteria to the function I know they will all return human, well formatted error messages. Great work assertthat!

Deduplicate the title with glue and stringr

One of the issues with the original approach was the call to render will generate an .md with a title in the file, but the dev.to api wants the title as a separate part of the call. stringr and glue to the rescue! Using glue is basically a super powered paste. It does nice things to insert named string variables into a templated string. I use this to make the string that is what I want to remove from the body_markdown object, and then do the actual removal with str_remove.

Next steps

Images. The real power of Rmarkdown comes from turning code into notebooks populated with graphics representing your data science work. I have not yet found an api endpoint for images. The POST looks like it will allow a cover image to be included, but on the face of it I don’t think it will allow images to be inserted at arbitrary points in the article text. Still, I’ll start there and see where it goes!

Go to top

Read Next

I made my dev.to content into a website to find a new job

Read Previous

Posting from .Rmd to dev.to